From e84829d51d9aa8e2623dabd4dcd4935d22008cb5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 2 Nov 2015 09:39:59 -0500 Subject: [PATCH] Plumbing to omit allocas for temps when possible (currently unused) --- src/librustc_trans/lib.rs | 1 + src/librustc_trans/trans/mir/lvalue.rs | 9 +- src/librustc_trans/trans/mir/mod.rs | 38 +++++- src/librustc_trans/trans/mir/rvalue.rs | 134 ++++++++++++++++------ src/librustc_trans/trans/mir/statement.rs | 28 ++++- 5 files changed, 166 insertions(+), 44 deletions(-) diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index bc5b6f6b9d7..84ce458ed14 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -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; diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index 282c6a7e6de..1ce7b55a9c6 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -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 => { diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 6ed839d1a44..760018b4313 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -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>, - /// An LLVM alloca for each MIR `TempDecl` - temps: Vec>, + /// 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>, /// 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>, } +enum TempRef<'tcx> { + Lvalue(LvalueRef<'tcx>), + Operand(Option>), +} + /////////////////////////////////////////////////////////////////////////// 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 = (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); diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 416aa061276..c82726fd0e5 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -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)); } } } diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs index 17a20fb817c..95ff049836e 100644 --- a/src/librustc_trans/trans/mir/statement.rs +++ b/src/librustc_trans/trans/mir/statement.rs @@ -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,9 +26,30 @@ 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); - bcx + 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) => {