From 0b8697241f3988bfa55b9f17d60732dab0e3d75d Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 9 Oct 2017 00:38:10 +0300 Subject: [PATCH] rustc_trans: be more relaxed with non-lvalue consumes, especially ZSTs. --- src/librustc_trans/mir/analyze.rs | 28 ++++++++++------ src/librustc_trans/mir/operand.rs | 53 +++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 3129ed028d4..3f3c5ac0a62 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -136,19 +136,29 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { context: LvalueContext<'tcx>, location: Location) { debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context); + let ccx = self.cx.ccx; if let mir::Lvalue::Projection(ref proj) = *lvalue { - // Allow uses of projections of immediate pair fields. + // Allow uses of projections that are ZSTs or from immediate scalar fields. if let LvalueContext::Consume = context { - if let mir::Lvalue::Local(_) = proj.base { - if let mir::ProjectionElem::Field(..) = proj.elem { - let ty = proj.base.ty(self.cx.mir, self.cx.ccx.tcx()); + let base_ty = proj.base.ty(self.cx.mir, ccx.tcx()); + let base_ty = self.cx.monomorphize(&base_ty); - let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx())); - let layout = self.cx.ccx.layout_of(ty); - if layout.is_llvm_scalar_pair() { - return; - } + // ZSTs don't require any actual memory access. + let elem_ty = base_ty.projection_ty(ccx.tcx(), &proj.elem).to_ty(ccx.tcx()); + let elem_ty = self.cx.monomorphize(&elem_ty); + if ccx.layout_of(elem_ty).is_zst() { + return; + } + + if let mir::ProjectionElem::Field(..) = proj.elem { + let layout = ccx.layout_of(base_ty.to_ty(ccx.tcx())); + if layout.is_llvm_scalar_pair() { + // Recurse as a `Consume` instead of `Projection`, + // potentially stopping at non-operand projections, + // which would trigger `mark_as_lvalue` on locals. + self.visit_lvalue(&proj.base, LvalueContext::Consume, location); + return; } } } diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index b9d4148acf6..97e7dda31aa 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -179,19 +179,19 @@ impl<'a, 'tcx> OperandValue { } impl<'a, 'tcx> MirContext<'a, 'tcx> { - pub fn trans_consume(&mut self, - bcx: &Builder<'a, 'tcx>, - lvalue: &mir::Lvalue<'tcx>) - -> OperandRef<'tcx> + fn maybe_trans_consume_direct(&mut self, + bcx: &Builder<'a, 'tcx>, + lvalue: &mir::Lvalue<'tcx>) + -> Option> { - debug!("trans_consume(lvalue={:?})", lvalue); + debug!("maybe_trans_consume_direct(lvalue={:?})", lvalue); // watch out for locals that do not have an // alloca; they are handled somewhat differently if let mir::Lvalue::Local(index) = *lvalue { match self.locals[index] { LocalRef::Operand(Some(o)) => { - return o; + return Some(o); } LocalRef::Operand(None) => { bug!("use of {:?} before def", lvalue); @@ -204,21 +204,24 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Moves out of pair fields are trivial. if let &mir::Lvalue::Projection(ref proj) = lvalue { - if let mir::Lvalue::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)) => { - let layout = bcx.ccx.layout_of(self.monomorphize(&ty)); + if let mir::ProjectionElem::Field(ref f, _) = proj.elem { + if let Some(o) = self.maybe_trans_consume_direct(bcx, &proj.base) { + let layout = o.layout.field(bcx.ccx, f.index()); + + // Handled in `trans_consume`. + assert!(!layout.is_zst()); + + match o.val { + OperandValue::Pair(a, b) => { let llval = [a, b][f.index()]; // HACK(eddyb) have to bitcast pointers // until LLVM removes pointee types. let llval = bcx.bitcast(llval, layout.immediate_llvm_type(bcx.ccx)); - return OperandRef { + return Some(OperandRef { val: OperandValue::Immediate(llval), layout - }; + }); } _ => {} } @@ -226,6 +229,28 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } } + None + } + + pub fn trans_consume(&mut self, + bcx: &Builder<'a, 'tcx>, + lvalue: &mir::Lvalue<'tcx>) + -> OperandRef<'tcx> + { + debug!("trans_consume(lvalue={:?})", lvalue); + + let ty = self.monomorphized_lvalue_ty(lvalue); + let layout = bcx.ccx.layout_of(ty); + + // ZSTs don't require any actual memory access. + if layout.is_zst() { + return OperandRef::new_zst(bcx.ccx, layout); + } + + if let Some(o) = self.maybe_trans_consume_direct(bcx, lvalue) { + return o; + } + // for most lvalues, to consume them we just load them // out from their home self.trans_lvalue(bcx, lvalue).load(bcx)