diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 03ae91fefb925..a6052f9aa75a1 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -144,6 +144,40 @@ impl<'tcx> Mir<'tcx> { pub fn predecessors_for(&self, bb: BasicBlock) -> Ref> { Ref::map(self.predecessors(), |p| &p[bb]) } + + /// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order) + /// to their index in the whole list of locals. This is useful if you + /// want to treat all locals the same instead of repeating yourself. + pub fn local_index(&self, lvalue: &Lvalue<'tcx>) -> Option { + let idx = match *lvalue { + Lvalue::Arg(arg) => arg.index(), + Lvalue::Var(var) => { + self.arg_decls.len() + + var.index() + } + Lvalue::Temp(temp) => { + self.arg_decls.len() + + self.var_decls.len() + + temp.index() + } + Lvalue::ReturnPointer => { + self.arg_decls.len() + + self.var_decls.len() + + self.temp_decls.len() + } + Lvalue::Static(_) | + Lvalue::Projection(_) => return None + }; + Some(Local::new(idx)) + } + + /// Counts the number of locals, such that that local_index + /// will always return an index smaller than this count. + pub fn count_locals(&self) -> usize { + self.arg_decls.len() + + self.var_decls.len() + + self.temp_decls.len() + 1 + } } impl<'tcx> Index for Mir<'tcx> { @@ -663,6 +697,7 @@ impl<'tcx> Debug for Statement<'tcx> { newtype_index!(Var, "var"); newtype_index!(Temp, "tmp"); newtype_index!(Arg, "arg"); +newtype_index!(Local, "local"); /// A path to a value; something that can be evaluated without /// changing or disturbing program state. diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index e2a9a95412669..7c69618068a6b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -492,6 +492,13 @@ impl<'tcx> FnOutput<'tcx> { ty::FnDiverging => def } } + + pub fn maybe_converging(self) -> Option> { + match self { + ty::FnConverging(t) => Some(t), + ty::FnDiverging => None + } + } } pub type PolyFnOutput<'tcx> = Binder>; diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 65581d43f8820..dac7afab6e38b 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! An analysis to determine which temporaries require allocas and +//! An analysis to determine which locals require allocas and //! which do not. use rustc_data_structures::bitvec::BitVector; @@ -21,16 +21,20 @@ use common::{self, Block, BlockAndBuilder}; use glue; use super::rvalue; -pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, - mir: &mir::Mir<'tcx>) -> BitVector { +pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>, + mir: &mir::Mir<'tcx>) -> BitVector { let bcx = bcx.build(); - let mut analyzer = TempAnalyzer::new(mir, &bcx, mir.temp_decls.len()); + let mut analyzer = LocalAnalyzer::new(mir, &bcx); analyzer.visit_mir(mir); - for (index, temp_decl) in mir.temp_decls.iter().enumerate() { - let ty = bcx.monomorphize(&temp_decl.ty); - debug!("temp {:?} has type {:?}", index, ty); + let local_types = mir.arg_decls.iter().map(|a| a.ty) + .chain(mir.var_decls.iter().map(|v| v.ty)) + .chain(mir.temp_decls.iter().map(|t| t.ty)) + .chain(mir.return_ty.maybe_converging()); + for (index, ty) in local_types.enumerate() { + let ty = bcx.monomorphize(&ty); + debug!("local {} has type {:?}", index, ty); if ty.is_scalar() || ty.is_unique() || ty.is_region_ptr() || @@ -50,66 +54,87 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, // (e.g. structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). - analyzer.mark_as_lvalue(index); + analyzer.mark_as_lvalue(mir::Local::new(index)); } } - analyzer.lvalue_temps + analyzer.lvalue_locals } -struct TempAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> { +struct LocalAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> { mir: &'mir mir::Mir<'tcx>, bcx: &'mir BlockAndBuilder<'bcx, 'tcx>, - lvalue_temps: BitVector, + lvalue_locals: BitVector, seen_assigned: BitVector } -impl<'mir, 'bcx, 'tcx> TempAnalyzer<'mir, 'bcx, 'tcx> { +impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> { fn new(mir: &'mir mir::Mir<'tcx>, - bcx: &'mir BlockAndBuilder<'bcx, 'tcx>, - temp_count: usize) -> TempAnalyzer<'mir, 'bcx, 'tcx> { - TempAnalyzer { + bcx: &'mir BlockAndBuilder<'bcx, 'tcx>) + -> LocalAnalyzer<'mir, 'bcx, 'tcx> { + let local_count = mir.count_locals(); + LocalAnalyzer { mir: mir, bcx: bcx, - lvalue_temps: BitVector::new(temp_count), - seen_assigned: BitVector::new(temp_count) + lvalue_locals: BitVector::new(local_count), + seen_assigned: BitVector::new(local_count) } } - fn mark_as_lvalue(&mut self, temp: usize) { - debug!("marking temp {} as lvalue", temp); - self.lvalue_temps.insert(temp); + fn mark_as_lvalue(&mut self, local: mir::Local) { + debug!("marking {:?} as lvalue", local); + self.lvalue_locals.insert(local.index()); } - fn mark_assigned(&mut self, temp: usize) { - if !self.seen_assigned.insert(temp) { - self.mark_as_lvalue(temp); + fn mark_assigned(&mut self, local: mir::Local) { + if !self.seen_assigned.insert(local.index()) { + self.mark_as_lvalue(local); } } } -impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { +impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { fn visit_assign(&mut self, block: mir::BasicBlock, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) { debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue); - match *lvalue { - mir::Lvalue::Temp(temp) => { - self.mark_assigned(temp.index()); - if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) { - self.mark_as_lvalue(temp.index()); - } - } - _ => { - self.visit_lvalue(lvalue, LvalueContext::Store); + if let Some(index) = self.mir.local_index(lvalue) { + self.mark_assigned(index); + if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) { + self.mark_as_lvalue(index); } + } else { + self.visit_lvalue(lvalue, LvalueContext::Store); } self.visit_rvalue(rvalue); } + fn visit_terminator_kind(&mut self, + block: mir::BasicBlock, + kind: &mir::TerminatorKind<'tcx>) { + match *kind { + mir::TerminatorKind::Call { + func: mir::Operand::Constant(mir::Constant { + literal: mir::Literal::Item { def_id, .. }, .. + }), + ref args, .. + } if Some(def_id) == self.bcx.tcx().lang_items.box_free_fn() => { + // box_free(x) shares with `drop x` the property that it + // is not guaranteed to be statically dominated by the + // definition of x, so x must always be in an alloca. + if let mir::Operand::Consume(ref lvalue) = args[0] { + self.visit_lvalue(lvalue, LvalueContext::Drop); + } + } + _ => {} + } + + self.super_terminator_kind(block, kind); + } + fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { @@ -117,9 +142,9 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { // Allow uses of projections of immediate pair fields. if let mir::Lvalue::Projection(ref proj) = *lvalue { - if let mir::Lvalue::Temp(temp) = proj.base { - let ty = self.mir.temp_decls[temp].ty; - let ty = self.bcx.monomorphize(&ty); + if self.mir.local_index(&proj.base).is_some() { + let ty = self.mir.lvalue_ty(self.bcx.tcx(), &proj.base); + let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); if common::type_is_imm_pair(self.bcx.ccx(), ty) { if let mir::ProjectionElem::Field(..) = proj.elem { if let LvalueContext::Consume = context { @@ -130,34 +155,30 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { } } - match *lvalue { - mir::Lvalue::Temp(temp) => { - match context { - LvalueContext::Call => { - self.mark_assigned(temp.index()); - } - LvalueContext::Consume => { - } - LvalueContext::Store | - LvalueContext::Inspect | - LvalueContext::Borrow { .. } | - LvalueContext::Slice { .. } | - LvalueContext::Projection => { - self.mark_as_lvalue(temp.index()); - } - LvalueContext::Drop => { - let ty = self.mir.temp_decls[index as usize].ty; - let ty = self.bcx.monomorphize(&ty); + if let Some(index) = self.mir.local_index(lvalue) { + match context { + LvalueContext::Call => { + self.mark_assigned(index); + } + LvalueContext::Consume => { + } + LvalueContext::Store | + LvalueContext::Inspect | + LvalueContext::Borrow { .. } | + LvalueContext::Slice { .. } | + LvalueContext::Projection => { + self.mark_as_lvalue(index); + } + LvalueContext::Drop => { + let ty = self.mir.lvalue_ty(self.bcx.tcx(), lvalue); + let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); - // Only need the lvalue if we're actually dropping it. - if glue::type_needs_drop(self.bcx.tcx(), ty) { - self.mark_as_lvalue(index as usize); - } + // Only need the lvalue if we're actually dropping it. + if glue::type_needs_drop(self.bcx.tcx(), ty) { + self.mark_as_lvalue(index); } } } - _ => { - } } // A deref projection only reads the pointer, never needs the lvalue. diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 4591fa2857811..7a7f1901736c5 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -32,7 +32,7 @@ use type_::Type; use rustc_data_structures::fnv::FnvHashMap; use syntax::parse::token; -use super::{MirContext, TempRef}; +use super::{MirContext, LocalRef}; use super::analyze::CleanupKind; use super::constant::Const; use super::lvalue::{LvalueRef, load_fat_ptr}; @@ -186,9 +186,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::TerminatorKind::Return => { - bcx.with_block(|bcx| { - self.fcx.build_return_block(bcx, debug_loc); - }) + let ret = bcx.fcx().fn_ty.ret; + if ret.is_ignore() || ret.is_indirect() { + bcx.ret_void(); + return; + } + + let llval = if let Some(cast_ty) = ret.cast { + let index = mir.local_index(&mir::Lvalue::ReturnPointer).unwrap(); + let op = match self.locals[index] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Lvalue(tr_lvalue) => { + OperandRef { + val: Ref(tr_lvalue.llval), + ty: tr_lvalue.ty.to_ty(bcx.tcx()) + } + } + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let llscratch = build::AllocaFcx(bcx.fcx(), ret.original_ty, "ret"); + self.store_operand(&bcx, llscratch, op); + llscratch + } + Ref(llval) => llval + }; + let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to())); + let llalign = llalign_of_min(bcx.ccx(), ret.ty); + unsafe { + llvm::LLVMSetAlignment(load, llalign); + } + load + } else { + let op = self.trans_consume(&bcx, &mir::Lvalue::ReturnPointer); + op.pack_if_pair(&bcx).immediate() + }; + bcx.ret(llval); } mir::TerminatorKind::Unreachable => { @@ -537,7 +571,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { fn trans_argument(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, - mut op: OperandRef<'tcx>, + op: OperandRef<'tcx>, llargs: &mut Vec, fn_ty: &FnType, next_idx: &mut usize, @@ -565,8 +599,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, callee); return; } - - op = op.pack_if_pair(bcx); } let arg = &fn_ty.args[*next_idx]; @@ -583,14 +615,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Force by-ref if we have to load through a cast pointer. let (mut llval, by_ref) = match op.val { - Immediate(llval) if arg.is_indirect() || arg.cast.is_some() => { - let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg"); - bcx.store(llval, llscratch); - (llscratch, true) + Immediate(_) | Pair(..) => { + if arg.is_indirect() || arg.cast.is_some() { + let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg"); + self.store_operand(bcx, llscratch, op); + (llscratch, true) + } else { + (op.pack_if_pair(bcx).immediate(), false) + } } - Immediate(llval) => (llval, false), - Ref(llval) => (llval, true), - Pair(..) => bug!("pairs handled above") + Ref(llval) => (llval, true) }; if by_ref && !arg.is_indirect() { @@ -776,40 +810,39 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { if fn_ret_ty.is_ignore() { return ReturnDest::Nothing; } - let dest = match *dest { - mir::Lvalue::Temp(idx) => { - let ret_ty = self.lvalue_ty(dest); - match self.temps[idx] { - TempRef::Lvalue(dest) => dest, - TempRef::Operand(None) => { - // Handle temporary lvalues, specifically Operand ones, as - // they don't have allocas - return if fn_ret_ty.is_indirect() { - // Odd, but possible, case, we have an operand temporary, - // but the calling convention has an indirect return. - let tmp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, ret_ty, "tmp_ret") - }); - llargs.push(tmp); - ReturnDest::IndirectOperand(tmp, idx) - } else if is_intrinsic { - // Currently, intrinsics always need a location to store - // the result. so we create a temporary alloca for the - // result - let tmp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, ret_ty, "tmp_ret") - }); - ReturnDest::IndirectOperand(tmp, idx) - } else { - ReturnDest::DirectOperand(idx) - }; - } - TempRef::Operand(Some(_)) => { - bug!("lvalue temp already assigned to"); - } + let dest = if let Some(index) = self.mir.local_index(dest) { + let ret_ty = self.lvalue_ty(dest); + match self.locals[index] { + LocalRef::Lvalue(dest) => dest, + LocalRef::Operand(None) => { + // Handle temporary lvalues, specifically Operand ones, as + // they don't have allocas + return if fn_ret_ty.is_indirect() { + // Odd, but possible, case, we have an operand temporary, + // but the calling convention has an indirect return. + let tmp = bcx.with_block(|bcx| { + base::alloc_ty(bcx, ret_ty, "tmp_ret") + }); + llargs.push(tmp); + ReturnDest::IndirectOperand(tmp, index) + } else if is_intrinsic { + // Currently, intrinsics always need a location to store + // the result. so we create a temporary alloca for the + // result + let tmp = bcx.with_block(|bcx| { + base::alloc_ty(bcx, ret_ty, "tmp_ret") + }); + ReturnDest::IndirectOperand(tmp, index) + } else { + ReturnDest::DirectOperand(index) + }; + } + LocalRef::Operand(Some(_)) => { + bug!("lvalue local already assigned to"); } } - _ => self.trans_lvalue(bcx, dest) + } else { + self.trans_lvalue(bcx, dest) }; if fn_ret_ty.is_indirect() { llargs.push(dest.llval); @@ -853,11 +886,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match dest { Nothing => (), Store(dst) => ret_ty.store(bcx, op.immediate(), dst), - IndirectOperand(tmp, idx) => { + IndirectOperand(tmp, index) => { let op = self.trans_load(bcx, tmp, op.ty); - self.temps[idx] = TempRef::Operand(Some(op)); + self.locals[index] = LocalRef::Operand(Some(op)); } - DirectOperand(idx) => { + DirectOperand(index) => { // If there is a cast, we have to store and reload. let op = if ret_ty.cast.is_some() { let tmp = bcx.with_block(|bcx| { @@ -868,7 +901,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } else { op.unpack_if_pair(bcx) }; - self.temps[idx] = TempRef::Operand(Some(op)); + self.locals[index] = LocalRef::Operand(Some(op)); } } } @@ -879,8 +912,8 @@ enum ReturnDest { Nothing, // Store the return value to the pointer Store(ValueRef), - // Stores an indirect return value to an operand temporary lvalue - IndirectOperand(ValueRef, mir::Temp), - // Stores a direct return value to an operand temporary lvalue - DirectOperand(mir::Temp) + // Stores an indirect return value to an operand local lvalue + IndirectOperand(ValueRef, mir::Local), + // Stores a direct return value to an operand local lvalue + DirectOperand(mir::Local) } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 316c7341ef142..30be4a9737270 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -203,17 +203,8 @@ struct MirConstContext<'a, 'tcx: 'a> { /// Type parameters for const fn and associated constants. substs: &'tcx Substs<'tcx>, - /// Arguments passed to a const fn. - args: IndexVec>, - - /// Variable values - specifically, argument bindings of a const fn. - vars: IndexVec>>, - - /// Temp values. - temps: IndexVec>>, - - /// Value assigned to Return, which is the resulting constant. - return_value: Option> + /// Values of locals in a constant or const fn. + locals: IndexVec>> } @@ -223,15 +214,17 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, args: IndexVec>) -> MirConstContext<'a, 'tcx> { - MirConstContext { + let mut context = MirConstContext { ccx: ccx, mir: mir, substs: substs, - args: args, - vars: IndexVec::from_elem(None, &mir.var_decls), - temps: IndexVec::from_elem(None, &mir.temp_decls), - return_value: None + locals: (0..mir.count_locals()).map(|_| None).collect(), + }; + for (i, arg) in args.into_iter().enumerate() { + let index = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(i))).unwrap(); + context.locals[index] = Some(arg); } + context } fn trans_def(ccx: &'a CrateContext<'a, 'tcx>, @@ -302,9 +295,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::TerminatorKind::Goto { target } => target, mir::TerminatorKind::Return => { failure?; - return Ok(self.return_value.unwrap_or_else(|| { + let index = self.mir.local_index(&mir::Lvalue::ReturnPointer).unwrap(); + return Ok(self.locals[index].unwrap_or_else(|| { span_bug!(span, "no returned value in constant"); - })) + })); } mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => { @@ -366,30 +360,28 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) { - let dest = match *dest { - mir::Lvalue::Var(var) => &mut self.vars[var], - mir::Lvalue::Temp(temp) => &mut self.temps[temp], - mir::Lvalue::ReturnPointer => &mut self.return_value, - _ => span_bug!(span, "assignment to {:?} in constant", dest) - }; - *dest = Some(value); + if let Some(index) = self.mir.local_index(dest) { + self.locals[index] = Some(value); + } else { + span_bug!(span, "assignment to {:?} in constant", dest); + } } fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span) -> Result, ConstEvalFailure> { let tcx = self.ccx.tcx(); + + if let Some(index) = self.mir.local_index(lvalue) { + return Ok(self.locals[index].unwrap_or_else(|| { + span_bug!(span, "{:?} not initialized", lvalue) + }).as_lvalue()); + } + let lvalue = match *lvalue { - mir::Lvalue::Var(var) => { - self.vars[var].unwrap_or_else(|| { - span_bug!(span, "{:?} not initialized", var) - }).as_lvalue() - } - mir::Lvalue::Temp(temp) => { - self.temps[temp].unwrap_or_else(|| { - span_bug!(span, "{:?} not initialized", temp) - }).as_lvalue() - } - mir::Lvalue::Arg(arg) => self.args[arg].as_lvalue(), + mir::Lvalue::Var(_) | + mir::Lvalue::Temp(_) | + mir::Lvalue::Arg(_) | + mir::Lvalue::ReturnPointer => bug!(), // handled above mir::Lvalue::Static(def_id) => { ConstLvalue { base: Base::Static(consts::get_static(self.ccx, def_id).val), @@ -397,9 +389,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { ty: self.mir.lvalue_ty(tcx, lvalue).to_ty(tcx) } } - mir::Lvalue::ReturnPointer => { - span_bug!(span, "accessing Lvalue::ReturnPointer in constant") - } mir::Lvalue::Projection(ref projection) => { let tr_base = self.const_lvalue(&projection.base, span)?; let projected_ty = LvalueTy::Ty { ty: tr_base.ty } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index f7e159797479c..ceaba2a40ca55 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -26,7 +26,7 @@ use Disr; use std::ptr; -use super::{MirContext, TempRef}; +use super::{MirContext, LocalRef}; use super::operand::OperandValue; #[derive(Copy, Clone, Debug)] @@ -88,40 +88,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { -> LvalueRef<'tcx> { debug!("trans_lvalue(lvalue={:?})", lvalue); - let fcx = bcx.fcx(); let ccx = bcx.ccx(); let tcx = bcx.tcx(); + + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Lvalue(lvalue) => { + return lvalue; + } + LocalRef::Operand(..) => { + bug!("using operand local {:?} as lvalue", lvalue); + } + } + } + let result = match *lvalue { - mir::Lvalue::Var(var) => self.vars[var], - mir::Lvalue::Temp(temp) => match self.temps[temp] { - TempRef::Lvalue(lvalue) => - lvalue, - TempRef::Operand(..) => - bug!("using operand temp {:?} as lvalue", lvalue), - }, - mir::Lvalue::Arg(arg) => self.args[arg], + mir::Lvalue::Var(_) | + mir::Lvalue::Temp(_) | + mir::Lvalue::Arg(_) | + mir::Lvalue::ReturnPointer => bug!(), // handled above mir::Lvalue::Static(def_id) => { let const_ty = self.lvalue_ty(lvalue); LvalueRef::new_sized(consts::get_static(ccx, def_id).val, LvalueTy::from_ty(const_ty)) }, - mir::Lvalue::ReturnPointer => { - let llval = if !fcx.fn_ty.ret.is_ignore() { - bcx.with_block(|bcx| { - fcx.get_ret_slot(bcx, "") - }) - } else { - // This is a void return; that is, there’s no place to store the value and - // there cannot really be one (or storing into it doesn’t make sense, anyway). - // Ergo, we return an undef ValueRef, so we do not have to special-case every - // place using lvalues, and could use it the same way you use a regular - // ReturnPointer LValue (i.e. store into it, load from it etc). - C_undef(fcx.fn_ty.ret.original_ty.ptr_to()) - }; - let fn_return_ty = bcx.monomorphize(&self.mir.return_ty); - let return_ty = fn_return_ty.unwrap(); - LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty)) - }, mir::Lvalue::Projection(box mir::Projection { ref base, elem: mir::ProjectionElem::Deref @@ -240,44 +230,41 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } // Perform an action using the given Lvalue. - // If the Lvalue is an empty TempRef::Operand, then a temporary stack slot + // If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot // is created first, then used as an operand to update the Lvalue. pub fn with_lvalue_ref(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, lvalue: &mir::Lvalue<'tcx>, f: F) -> U where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U { - match *lvalue { - mir::Lvalue::Temp(temp) => { - match self.temps[temp] { - TempRef::Lvalue(lvalue) => f(self, lvalue), - TempRef::Operand(None) => { - let lvalue_ty = self.lvalue_ty(lvalue); - let lvalue = LvalueRef::alloca(bcx, - lvalue_ty, - "lvalue_temp"); - let ret = f(self, lvalue); - let op = self.trans_load(bcx, lvalue.llval, lvalue_ty); - self.temps[temp] = TempRef::Operand(Some(op)); - ret - } - TempRef::Operand(Some(_)) => { - // See comments in TempRef::new_operand as to why - // we always have Some in a ZST TempRef::Operand. - let ty = self.lvalue_ty(lvalue); - if common::type_is_zero_size(bcx.ccx(), ty) { - // Pass an undef pointer as no stores can actually occur. - let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to()); - f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty))) - } else { - bug!("Lvalue temp already set"); - } + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Lvalue(lvalue) => f(self, lvalue), + LocalRef::Operand(None) => { + let lvalue_ty = self.lvalue_ty(lvalue); + let lvalue = LvalueRef::alloca(bcx, + lvalue_ty, + "lvalue_temp"); + let ret = f(self, lvalue); + let op = self.trans_load(bcx, lvalue.llval, lvalue_ty); + self.locals[index] = LocalRef::Operand(Some(op)); + ret + } + LocalRef::Operand(Some(_)) => { + // See comments in LocalRef::new_operand as to why + // we always have Some in a ZST LocalRef::Operand. + let ty = self.lvalue_ty(lvalue); + if common::type_is_zero_size(bcx.ccx(), ty) { + // Pass an undef pointer as no stores can actually occur. + let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to()); + f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty))) + } else { + bug!("Lvalue local already set"); } } } - _ => { - let lvalue = self.trans_lvalue(bcx, lvalue); - f(self, lvalue) - } + } else { + let lvalue = self.trans_lvalue(bcx, lvalue); + f(self, lvalue) } } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 1932a2023fac8..0db5d3ae4d131 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -84,16 +84,13 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// Cached unreachable block unreachable_block: Option>, - /// An LLVM alloca for each MIR `VarDecl` - vars: IndexVec>, - - /// The location where each MIR `TempDecl` is stored. This is + /// The location where each MIR arg/var/tmp/ret 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 type of the local 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` @@ -102,12 +99,7 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// /// Avoiding allocs can also be important for certain intrinsics, /// notably `expect`. - temps: IndexVec>, - - /// The arguments to the function; as args are lvalues, these are - /// always indirect, though we try to avoid creating an alloca - /// when we can (and just reuse the pointer the caller provided). - args: IndexVec>, + locals: IndexVec>, /// Debug information for MIR scopes. scopes: IndexVec @@ -119,14 +111,14 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> { } } -enum TempRef<'tcx> { +enum LocalRef<'tcx> { Lvalue(LvalueRef<'tcx>), Operand(Option>), } -impl<'tcx> TempRef<'tcx> { +impl<'tcx> LocalRef<'tcx> { fn new_operand<'bcx>(ccx: &CrateContext<'bcx, 'tcx>, - ty: ty::Ty<'tcx>) -> TempRef<'tcx> { + ty: ty::Ty<'tcx>) -> LocalRef<'tcx> { if common::type_is_zero_size(ccx, ty) { // Zero-size temporaries aren't always initialized, which // doesn't matter because they don't contain data, but @@ -142,9 +134,9 @@ impl<'tcx> TempRef<'tcx> { val: val, ty: ty }; - TempRef::Operand(Some(op)) + LocalRef::Operand(Some(op)) } else { - TempRef::Operand(None) + LocalRef::Operand(None) } } } @@ -157,8 +149,8 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { // Analyze the temps to determine which must be lvalues // FIXME - let (lvalue_temps, cleanup_kinds) = bcx.with_block(|bcx| { - (analyze::lvalue_temps(bcx, &mir), + let (lvalue_locals, cleanup_kinds) = bcx.with_block(|bcx| { + (analyze::lvalue_locals(bcx, &mir), analyze::cleanup_kinds(bcx, &mir)) }); @@ -166,37 +158,49 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let scopes = debuginfo::create_mir_scopes(fcx); // Allocate variable and temp allocas - let args = arg_value_refs(&bcx, &mir, &scopes); - let vars = mir.var_decls.iter() - .map(|decl| (bcx.monomorphize(&decl.ty), decl)) - .map(|(mty, decl)| { - let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str()); - - let scope = scopes[decl.source_info.scope]; - if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo { - bcx.with_block(|bcx| { - declare_local(bcx, decl.name, mty, scope, - VariableAccess::DirectVariable { alloca: lvalue.llval }, - VariableKind::LocalVariable, decl.source_info.span); - }); - } + let locals = { + let args = arg_local_refs(&bcx, &mir, &scopes, &lvalue_locals); + let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| { + let ty = bcx.monomorphize(&decl.ty); + let scope = scopes[decl.source_info.scope]; + let dbg = !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo; + + let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap(); + if !lvalue_locals.contains(local.index()) && !dbg { + return LocalRef::new_operand(bcx.ccx(), ty); + } - lvalue - }).collect(); - let temps = mir.temp_decls.iter() - .map(|decl| bcx.monomorphize(&decl.ty)) - .enumerate() - .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::new_operand(bcx.ccx(), mty) - }) - .collect(); + let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str()); + if dbg { + bcx.with_block(|bcx| { + declare_local(bcx, decl.name, ty, scope, + VariableAccess::DirectVariable { alloca: lvalue.llval }, + VariableKind::LocalVariable, decl.source_info.span); + }); + } + LocalRef::Lvalue(lvalue) + }); + + let locals = mir.temp_decls.iter().enumerate().map(|(i, decl)| { + (mir::Lvalue::Temp(mir::Temp::new(i)), decl.ty) + }).chain(mir.return_ty.maybe_converging().map(|ty| (mir::Lvalue::ReturnPointer, ty))); + + args.into_iter().chain(vars).chain(locals.map(|(lvalue, ty)| { + let ty = bcx.monomorphize(&ty); + let local = mir.local_index(&lvalue).unwrap(); + if lvalue == mir::Lvalue::ReturnPointer && fcx.fn_ty.ret.is_indirect() { + let llretptr = llvm::get_param(fcx.llfn, 0); + LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty))) + } else if lvalue_locals.contains(local.index()) { + LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", lvalue))) + } else { + // If this is an immediate local, we do not create an + // alloca in advance. Instead we wait until we see the + // definition and update the operand there. + LocalRef::new_operand(bcx.ccx(), ty) + } + })).collect() + }; // Allocate a `Block` for every basic block let block_bcxs: IndexVec> = @@ -225,9 +229,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { unreachable_block: None, cleanup_kinds: cleanup_kinds, landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), - vars: vars, - temps: temps, - args: args, + locals: locals, scopes: scopes }; @@ -266,10 +268,11 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { /// Produce, for each argument, a `ValueRef` pointing at the /// argument's value. As arguments are lvalues, these are always /// indirect. -fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, +fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir: &mir::Mir<'tcx>, - scopes: &IndexVec) - -> IndexVec> { + scopes: &IndexVec, + lvalue_locals: &BitVector) + -> Vec> { let fcx = bcx.fcx(); let tcx = bcx.tcx(); let mut idx = 0; @@ -285,6 +288,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| { let arg_ty = bcx.monomorphize(&arg_decl.ty); + let local = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(arg_index))).unwrap(); if arg_decl.spread { // This argument (e.g. the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have @@ -305,8 +309,8 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, let arg = &fcx.fn_ty.args[idx]; idx += 1; if common::type_is_fat_ptr(tcx, tupled_arg_ty) { - // We pass fat pointers as two words, but inside the tuple - // they are the two sub-fields of a single aggregate field. + // We pass fat pointers as two words, but inside the tuple + // they are the two sub-fields of a single aggregate field. let meta = &fcx.fn_ty.args[idx]; idx += 1; arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, dst)); @@ -335,7 +339,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx.fcx().span.unwrap_or(DUMMY_SP)); })); } - return LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty)); + return LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty))); } let arg = &fcx.fn_ty.args[idx]; @@ -345,9 +349,42 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, // already put it in a temporary alloca and gave it up, unless // we emit extra-debug-info, which requires local allocas :(. // FIXME: lifetimes + if arg.pad.is_some() { + llarg_idx += 1; + } let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); llarg_idx += 1; llarg + } else if !lvalue_locals.contains(local.index()) && + !arg.is_indirect() && arg.cast.is_none() && + arg_scope.is_none() { + if arg.is_ignore() { + return LocalRef::new_operand(bcx.ccx(), arg_ty); + } + + // We don't have to cast or keep the argument in the alloca. + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + if arg.pad.is_some() { + llarg_idx += 1; + } + let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + llarg_idx += 1; + let val = if common::type_is_fat_ptr(tcx, arg_ty) { + let meta = &fcx.fn_ty.args[idx]; + idx += 1; + assert_eq!((meta.cast, meta.pad), (None, None)); + let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + llarg_idx += 1; + OperandValue::Pair(llarg, llmeta) + } else { + OperandValue::Immediate(llarg) + }; + let operand = OperandRef { + val: val, + ty: arg_ty + }; + return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); } else { let lltemp = bcx.with_block(|bcx| { base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) @@ -441,7 +478,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx.fcx().span.unwrap_or(DUMMY_SP)); } })); - LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)) + LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))) }).collect() } diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 980db76d632c1..446ac91b1f580 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -21,7 +21,7 @@ use type_::Type; use std::fmt; -use super::{MirContext, TempRef}; +use super::{MirContext, LocalRef}; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -112,6 +112,8 @@ impl<'bcx, 'tcx> OperandRef<'tcx> { if let OperandValue::Immediate(llval) = self.val { // Deconstruct the immediate aggregate. if common::type_is_imm_pair(bcx.ccx(), self.ty) { + debug!("Operand::unpack_if_pair: unpacking {:?}", self); + let mut a = bcx.extract_value(llval, 0); let mut b = bcx.extract_value(llval, 1); @@ -171,17 +173,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { { debug!("trans_consume(lvalue={:?})", lvalue); - // watch out for temporaries that do not have an + // watch out for locals that do not have an // alloca; they are handled somewhat differently - if let &mir::Lvalue::Temp(index) = lvalue { - match self.temps[index] { - TempRef::Operand(Some(o)) => { + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Operand(Some(o)) => { return o; } - TempRef::Operand(None) => { + LocalRef::Operand(None) => { bug!("use of {:?} before def", lvalue); } - TempRef::Lvalue(..) => { + LocalRef::Lvalue(..) => { // use path below } } @@ -189,9 +191,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Moves out of pair fields are trivial. if let &mir::Lvalue::Projection(ref proj) = lvalue { - if let mir::Lvalue::Temp(index) = proj.base { - let temp_ref = &self.temps[index]; - if let &TempRef::Operand(Some(o)) = temp_ref { + if let Some(index) = self.mir.local_index(&proj.base) { + if let LocalRef::Operand(Some(o)) = self.locals[index] { match (o.val, &proj.elem) { (OperandValue::Pair(a, b), &mir::ProjectionElem::Field(ref f, ty)) => { diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index d592f5ee1b936..55efa75b17336 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -13,7 +13,7 @@ use rustc::mir::repr as mir; use common::{self, BlockAndBuilder}; use super::MirContext; -use super::TempRef; +use super::LocalRef; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_statement(&mut self, @@ -27,37 +27,34 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug_loc.apply(bcx.fcx()); match statement.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - match *lvalue { - mir::Lvalue::Temp(index) => { - match self.temps[index] { - TempRef::Lvalue(tr_dest) => { - self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) - } - TempRef::Operand(None) => { - let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue, - debug_loc); - self.temps[index] = TempRef::Operand(Some(operand)); - bcx - } - TempRef::Operand(Some(_)) => { - let ty = self.lvalue_ty(lvalue); + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Lvalue(tr_dest) => { + self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) + } + LocalRef::Operand(None) => { + let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue, + debug_loc); + self.locals[index] = LocalRef::Operand(Some(operand)); + bcx + } + LocalRef::Operand(Some(_)) => { + let ty = self.lvalue_ty(lvalue); - if !common::type_is_zero_size(bcx.ccx(), ty) { - span_bug!(statement.source_info.span, - "operand {:?} already assigned", - rvalue); - } else { - // If the type is zero-sized, it's already been set here, - // but we still need to make sure we translate the operand - self.trans_rvalue_operand(bcx, rvalue, debug_loc).0 - } + if !common::type_is_zero_size(bcx.ccx(), ty) { + span_bug!(statement.source_info.span, + "operand {:?} already assigned", + rvalue); + } else { + // If the type is zero-sized, it's already been set here, + // but we still need to make sure we translate the operand + self.trans_rvalue_operand(bcx, rvalue, debug_loc).0 } } } - _ => { - let tr_dest = self.trans_lvalue(&bcx, lvalue); - self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) - } + } else { + let tr_dest = self.trans_lvalue(&bcx, lvalue); + self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) } } } diff --git a/src/test/codegen/loads.rs b/src/test/codegen/loads.rs index 21f23b6ea1861..a65a3e1bb66fe 100644 --- a/src/test/codegen/loads.rs +++ b/src/test/codegen/loads.rs @@ -11,6 +11,7 @@ // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] +#![feature(rustc_attrs)] pub struct Bytes { a: u8, @@ -21,6 +22,7 @@ pub struct Bytes { // CHECK-LABEL: @borrow #[no_mangle] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. pub fn borrow(x: &i32) -> &i32 { // CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull x @@ -28,6 +30,7 @@ pub fn borrow(x: &i32) -> &i32 { // CHECK-LABEL: @_box #[no_mangle] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. pub fn _box(x: Box) -> i32 { // CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull *x diff --git a/src/test/codegen/naked-functions.rs b/src/test/codegen/naked-functions.rs index 0a600f4acad1f..199f7f0201877 100644 --- a/src/test/codegen/naked-functions.rs +++ b/src/test/codegen/naked-functions.rs @@ -13,7 +13,7 @@ // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] -#![feature(naked_functions)] +#![feature(naked_functions, rustc_attrs)] // CHECK: Function Attrs: naked uwtable // CHECK-NEXT: define internal void @naked_empty() @@ -26,6 +26,7 @@ fn naked_empty() { // CHECK: Function Attrs: naked uwtable #[no_mangle] #[naked] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. // CHECK-NEXT: define internal void @naked_with_args(i{{[0-9]+}}) fn naked_with_args(a: isize) { // CHECK: %a = alloca i{{[0-9]+}} @@ -45,6 +46,7 @@ fn naked_with_return() -> isize { // CHECK-NEXT: define internal i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}}) #[no_mangle] #[naked] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. fn naked_with_args_and_return(a: isize) -> isize { // CHECK: %a = alloca i{{[0-9]+}} // CHECK: ret i{{[0-9]+}} %{{[0-9]+}} diff --git a/src/test/compile-fail/issue-26548.rs b/src/test/compile-fail/issue-26548.rs index 2919b0b3caca6..b6d5e5458ff08 100644 --- a/src/test/compile-fail/issue-26548.rs +++ b/src/test/compile-fail/issue-26548.rs @@ -10,10 +10,13 @@ // error-pattern: overflow representing the type `S` +#![feature(rustc_attrs)] + trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } struct S(Option<::It>); +#[rustc_no_mir] // FIXME #27840 MIR tries to represent `std::option::Option` first. fn main() { let _s = S(None); }