rustc_trans: be more relaxed with non-lvalue consumes, especially ZSTs.

This commit is contained in:
Eduard-Mihai Burtescu 2017-10-09 00:38:10 +03:00
parent c4d9ada701
commit 0b8697241f
2 changed files with 58 additions and 23 deletions

View File

@ -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;
}
}
}

View File

@ -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<OperandRef<'tcx>>
{
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)