Skip to content

Commit 0b86972

Browse files
committed
rustc_trans: be more relaxed with non-lvalue consumes, especially ZSTs.
1 parent c4d9ada commit 0b86972

File tree

2 files changed

+59
-24
lines changed

2 files changed

+59
-24
lines changed

src/librustc_trans/mir/analyze.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,19 +136,29 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
136136
context: LvalueContext<'tcx>,
137137
location: Location) {
138138
debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
139+
let ccx = self.cx.ccx;
139140

140141
if let mir::Lvalue::Projection(ref proj) = *lvalue {
141-
// Allow uses of projections of immediate pair fields.
142+
// Allow uses of projections that are ZSTs or from immediate scalar fields.
142143
if let LvalueContext::Consume = context {
143-
if let mir::Lvalue::Local(_) = proj.base {
144-
if let mir::ProjectionElem::Field(..) = proj.elem {
145-
let ty = proj.base.ty(self.cx.mir, self.cx.ccx.tcx());
146-
147-
let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx()));
148-
let layout = self.cx.ccx.layout_of(ty);
149-
if layout.is_llvm_scalar_pair() {
150-
return;
151-
}
144+
let base_ty = proj.base.ty(self.cx.mir, ccx.tcx());
145+
let base_ty = self.cx.monomorphize(&base_ty);
146+
147+
// ZSTs don't require any actual memory access.
148+
let elem_ty = base_ty.projection_ty(ccx.tcx(), &proj.elem).to_ty(ccx.tcx());
149+
let elem_ty = self.cx.monomorphize(&elem_ty);
150+
if ccx.layout_of(elem_ty).is_zst() {
151+
return;
152+
}
153+
154+
if let mir::ProjectionElem::Field(..) = proj.elem {
155+
let layout = ccx.layout_of(base_ty.to_ty(ccx.tcx()));
156+
if layout.is_llvm_scalar_pair() {
157+
// Recurse as a `Consume` instead of `Projection`,
158+
// potentially stopping at non-operand projections,
159+
// which would trigger `mark_as_lvalue` on locals.
160+
self.visit_lvalue(&proj.base, LvalueContext::Consume, location);
161+
return;
152162
}
153163
}
154164
}

src/librustc_trans/mir/operand.rs

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -179,19 +179,19 @@ impl<'a, 'tcx> OperandValue {
179179
}
180180

181181
impl<'a, 'tcx> MirContext<'a, 'tcx> {
182-
pub fn trans_consume(&mut self,
183-
bcx: &Builder<'a, 'tcx>,
184-
lvalue: &mir::Lvalue<'tcx>)
185-
-> OperandRef<'tcx>
182+
fn maybe_trans_consume_direct(&mut self,
183+
bcx: &Builder<'a, 'tcx>,
184+
lvalue: &mir::Lvalue<'tcx>)
185+
-> Option<OperandRef<'tcx>>
186186
{
187-
debug!("trans_consume(lvalue={:?})", lvalue);
187+
debug!("maybe_trans_consume_direct(lvalue={:?})", lvalue);
188188

189189
// watch out for locals that do not have an
190190
// alloca; they are handled somewhat differently
191191
if let mir::Lvalue::Local(index) = *lvalue {
192192
match self.locals[index] {
193193
LocalRef::Operand(Some(o)) => {
194-
return o;
194+
return Some(o);
195195
}
196196
LocalRef::Operand(None) => {
197197
bug!("use of {:?} before def", lvalue);
@@ -204,28 +204,53 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
204204

205205
// Moves out of pair fields are trivial.
206206
if let &mir::Lvalue::Projection(ref proj) = lvalue {
207-
if let mir::Lvalue::Local(index) = proj.base {
208-
if let LocalRef::Operand(Some(o)) = self.locals[index] {
209-
match (o.val, &proj.elem) {
210-
(OperandValue::Pair(a, b),
211-
&mir::ProjectionElem::Field(ref f, ty)) => {
212-
let layout = bcx.ccx.layout_of(self.monomorphize(&ty));
207+
if let mir::ProjectionElem::Field(ref f, _) = proj.elem {
208+
if let Some(o) = self.maybe_trans_consume_direct(bcx, &proj.base) {
209+
let layout = o.layout.field(bcx.ccx, f.index());
210+
211+
// Handled in `trans_consume`.
212+
assert!(!layout.is_zst());
213+
214+
match o.val {
215+
OperandValue::Pair(a, b) => {
213216
let llval = [a, b][f.index()];
214217
// HACK(eddyb) have to bitcast pointers
215218
// until LLVM removes pointee types.
216219
let llval = bcx.bitcast(llval,
217220
layout.immediate_llvm_type(bcx.ccx));
218-
return OperandRef {
221+
return Some(OperandRef {
219222
val: OperandValue::Immediate(llval),
220223
layout
221-
};
224+
});
222225
}
223226
_ => {}
224227
}
225228
}
226229
}
227230
}
228231

232+
None
233+
}
234+
235+
pub fn trans_consume(&mut self,
236+
bcx: &Builder<'a, 'tcx>,
237+
lvalue: &mir::Lvalue<'tcx>)
238+
-> OperandRef<'tcx>
239+
{
240+
debug!("trans_consume(lvalue={:?})", lvalue);
241+
242+
let ty = self.monomorphized_lvalue_ty(lvalue);
243+
let layout = bcx.ccx.layout_of(ty);
244+
245+
// ZSTs don't require any actual memory access.
246+
if layout.is_zst() {
247+
return OperandRef::new_zst(bcx.ccx, layout);
248+
}
249+
250+
if let Some(o) = self.maybe_trans_consume_direct(bcx, lvalue) {
251+
return o;
252+
}
253+
229254
// for most lvalues, to consume them we just load them
230255
// out from their home
231256
self.trans_lvalue(bcx, lvalue).load(bcx)

0 commit comments

Comments
 (0)