Plumbing to omit allocas for temps when possible (currently unused)

This commit is contained in:
Niko Matsakis 2015-11-02 09:39:59 -05:00
parent 02017b30eb
commit e84829d51d
5 changed files with 166 additions and 44 deletions

View file

@ -51,6 +51,7 @@ extern crate graphviz;
extern crate libc;
extern crate rustc;
extern crate rustc_back;
extern crate rustc_data_structures;
extern crate rustc_front;
extern crate rustc_llvm as llvm;
extern crate rustc_mir;

View file

@ -20,7 +20,7 @@ use trans::debuginfo::DebugLoc;
use trans::machine;
use trans::tvec;
use super::MirContext;
use super::{MirContext, TempRef};
#[derive(Copy, Clone)]
pub struct LvalueRef<'tcx> {
@ -58,7 +58,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let tcx = bcx.tcx();
match *lvalue {
mir::Lvalue::Var(index) => self.vars[index as usize],
mir::Lvalue::Temp(index) => self.temps[index as usize],
mir::Lvalue::Temp(index) => match self.temps[index as usize] {
TempRef::Lvalue(lvalue) =>
lvalue,
TempRef::Operand(..) =>
tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)),
},
mir::Lvalue::Arg(index) => self.args[index as usize],
mir::Lvalue::Static(_def_id) => unimplemented!(),
mir::Lvalue::ReturnPointer => {

View file

@ -10,9 +10,9 @@
use libc::c_uint;
use llvm::{self, ValueRef};
use rustc_data_structures::fnv::FnvHashSet;
use rustc_mir::repr as mir;
use rustc_mir::tcx::LvalueTy;
use std::cell::Cell;
use trans::base;
use trans::build;
use trans::common::{self, Block};
@ -21,6 +21,7 @@ use trans::expr;
use trans::type_of;
use self::lvalue::LvalueRef;
use self::operand::OperandRef;
// FIXME DebugLoc is always None right now
@ -43,8 +44,19 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
/// An LLVM alloca for each MIR `VarDecl`
vars: Vec<LvalueRef<'tcx>>,
/// An LLVM alloca for each MIR `TempDecl`
temps: Vec<LvalueRef<'tcx>>,
/// The location where each MIR `TempDecl` is stored. This is
/// usually an `LvalueRef` representing an alloca, but not always:
/// sometimes we can skip the alloca and just store the value
/// directly using an `OperandRef`, which makes for tighter LLVM
/// IR. The conditions for using an `OperandRef` are as follows:
///
/// - the type of the temporary must be judged "immediate" by `type_is_immediate`
/// - the operand must never be referenced indirectly
/// - we should not take its address using the `&` operator
/// - nor should it appear in an lvalue path like `tmp.a`
/// - the operand must be defined by an rvalue that can generate immediate
/// values
temps: Vec<TempRef<'tcx>>,
/// The arguments to the function; as args are lvalues, these are
/// always indirect, though we try to avoid creating an alloca
@ -52,6 +64,11 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
args: Vec<LvalueRef<'tcx>>,
}
enum TempRef<'tcx> {
Lvalue(LvalueRef<'tcx>),
Operand(Option<OperandRef<'tcx>>),
}
///////////////////////////////////////////////////////////////////////////
pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
@ -60,6 +77,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
let mir_blocks = bcx.mir().all_basic_blocks();
// Analyze the temps to determine which must be lvalues
// FIXME
let lvalue_temps: FnvHashSet<usize> = (0..mir.temp_decls.len()).collect();
// Allocate variable and temp allocas
let vars = mir.var_decls.iter()
.map(|decl| (bcx.monomorphize(&decl.ty), decl.name))
@ -68,7 +89,16 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
let temps = mir.temp_decls.iter()
.map(|decl| bcx.monomorphize(&decl.ty))
.enumerate()
.map(|(i, mty)| LvalueRef::alloca(bcx, mty, &format!("temp{:?}", i)))
.map(|(i, mty)| if lvalue_temps.contains(&i) {
TempRef::Lvalue(LvalueRef::alloca(bcx,
mty,
&format!("temp{:?}", i)))
} else {
// If this is an immediate temp, we do not create an
// alloca in advance. Instead we wait until we see the
// definition and update the operand there.
TempRef::Operand(None)
})
.collect();
let args = arg_value_refs(bcx, mir);

View file

@ -53,12 +53,87 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
unimplemented!()
}
mir::Rvalue::Aggregate(_, ref operands) => {
for (i, operand) in operands.iter().enumerate() {
let lldest_i = build::GEPi(bcx, lldest, &[0, i]);
self.trans_operand_into(bcx, lldest_i, operand);
}
bcx
}
mir::Rvalue::Slice { ref input, from_start, from_end } => {
let ccx = bcx.ccx();
let input = self.trans_lvalue(bcx, input);
let (llbase, lllen) = tvec::get_base_and_len(bcx,
input.llval,
input.ty.to_ty(bcx.tcx()));
let llbase1 = build::GEPi(bcx, llbase, &[from_start]);
let adj = common::C_uint(ccx, from_start + from_end);
let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None);
build::Store(bcx, llbase1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_ADDR]));
build::Store(bcx, lllen1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA]));
bcx
}
mir::Rvalue::InlineAsm(inline_asm) => {
asm::trans_inline_asm(bcx, inline_asm)
}
_ => {
assert!(self.rvalue_creates_operand(rvalue));
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
build::Store(bcx, temp.llval, lldest);
bcx
}
}
}
pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool {
match *rvalue {
mir::Rvalue::Use(..) | // (*)
mir::Rvalue::Ref(..) |
mir::Rvalue::Len(..) |
mir::Rvalue::Cast(..) | // (*)
mir::Rvalue::BinaryOp(..) |
mir::Rvalue::UnaryOp(..) |
mir::Rvalue::Box(..) =>
true,
mir::Rvalue::Repeat(..) |
mir::Rvalue::Aggregate(..) |
mir::Rvalue::Slice { .. } |
mir::Rvalue::InlineAsm(..) =>
false,
}
// (*) this is only true if the type is suitable
}
pub fn trans_rvalue_operand(&mut self,
bcx: Block<'bcx, 'tcx>,
rvalue: &mir::Rvalue<'tcx>)
-> (Block<'bcx, 'tcx>, OperandRef<'tcx>)
{
assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
match *rvalue {
mir::Rvalue::Use(ref operand) => {
let operand = self.trans_operand(bcx, operand);
(bcx, operand)
}
mir::Rvalue::Cast(..) => {
unimplemented!()
}
mir::Rvalue::Ref(_, _, ref lvalue) => {
let tr_lvalue = self.trans_lvalue(bcx, lvalue);
// Note: lvalues are indirect, so storing the `llval` into the
// destination effectively creates a reference.
build::Store(bcx, tr_lvalue.llval, lldest);
bcx
(bcx, OperandRef {
llval: tr_lvalue.llval,
ty: tr_lvalue.ty.to_ty(bcx.tcx()),
})
}
mir::Rvalue::Len(ref lvalue) => {
@ -66,8 +141,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let (_, lllen) = tvec::get_base_and_len(bcx,
tr_lvalue.llval,
tr_lvalue.ty.to_ty(bcx.tcx()));
build::Store(bcx, lllen, lldest);
bcx
(bcx, OperandRef {
llval: lllen,
ty: bcx.tcx().types.usize,
})
}
mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
@ -170,8 +247,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
hir::BiGt, binop_debug_loc),
};
build::Store(bcx, llval, lldest);
bcx
(bcx, OperandRef {
llval: llval,
ty: lhs.ty,
})
}
mir::Rvalue::UnaryOp(op, ref operand) => {
@ -186,12 +265,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
build::Neg(bcx, operand.llval, debug_loc)
}
};
build::Store(bcx, llval, lldest);
bcx
(bcx, OperandRef {
llval: llval,
ty: operand.ty,
})
}
mir::Rvalue::Box(content_ty) => {
let content_ty: Ty<'tcx> = content_ty;
let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty);
let llty = type_of::type_of(bcx.ccx(), content_ty);
let llsize = machine::llsize_of(bcx.ccx(), llty);
let align = type_of::align_of(bcx.ccx(), content_ty);
@ -204,34 +285,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
llsize,
llalign,
DebugLoc::None);
build::Store(bcx, llval, lldest);
bcx
(bcx, OperandRef {
llval: llval,
ty: box_ty,
})
}
mir::Rvalue::Aggregate(_, ref operands) => {
for (i, operand) in operands.iter().enumerate() {
let lldest_i = build::GEPi(bcx, lldest, &[0, i]);
self.trans_operand_into(bcx, lldest_i, operand);
}
bcx
}
mir::Rvalue::Slice { ref input, from_start, from_end } => {
let ccx = bcx.ccx();
let input = self.trans_lvalue(bcx, input);
let (llbase, lllen) = tvec::get_base_and_len(bcx,
input.llval,
input.ty.to_ty(bcx.tcx()));
let llbase1 = build::GEPi(bcx, llbase, &[from_start]);
let adj = common::C_uint(ccx, from_start + from_end);
let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None);
build::Store(bcx, llbase1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_ADDR]));
build::Store(bcx, lllen1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA]));
bcx
}
mir::Rvalue::InlineAsm(inline_asm) => {
asm::trans_inline_asm(bcx, inline_asm)
mir::Rvalue::Repeat(..) |
mir::Rvalue::Aggregate(..) |
mir::Rvalue::Slice { .. } |
mir::Rvalue::InlineAsm(..) => {
bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue));
}
}
}

View file

@ -15,6 +15,7 @@ use trans::debuginfo::DebugLoc;
use trans::glue;
use super::MirContext;
use super::TempRef;
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn trans_statement(&mut self,
@ -25,10 +26,31 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
match statement.kind {
mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
let tr_dest = self.trans_lvalue(bcx, lvalue);
self.trans_rvalue(bcx, tr_dest.llval, rvalue);
match *lvalue {
mir::Lvalue::Temp(index) => {
let index = index as usize;
match self.temps[index as usize] {
TempRef::Lvalue(tr_dest) => {
self.trans_rvalue(bcx, tr_dest.llval, rvalue)
}
TempRef::Operand(None) => {
let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue);
self.temps[index] = TempRef::Operand(Some(operand));
bcx
}
TempRef::Operand(Some(_)) => {
bcx.tcx().sess.span_bug(
statement.span,
&format!("operand {:?} already assigned", rvalue));
}
}
}
_ => {
let tr_dest = self.trans_lvalue(bcx, lvalue);
self.trans_rvalue(bcx, tr_dest.llval, rvalue)
}
}
}
mir::StatementKind::Drop(mir::DropKind::Deep, ref lvalue) => {
let tr_lvalue = self.trans_lvalue(bcx, lvalue);