Skip to content

Commit 277d4ca

Browse files
Rewrite projection loop using notes from review
1 parent f74e65c commit 277d4ca

File tree

1 file changed

+43
-29
lines changed

1 file changed

+43
-29
lines changed

src/librustc_codegen_ssa/mir/analyze.rs

+43-29
Original file line numberDiff line numberDiff line change
@@ -137,18 +137,20 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
137137
mut context: PlaceContext,
138138
location: Location,
139139
) {
140-
debug!("visit_place(place={:?}, context={:?})", place, context);
140+
let mir::Place { local, projection } = *place;
141+
142+
self.super_projection(local, projection, context, location);
141143

142144
// Non-uses do not force locals onto the stack.
143145
if !context.is_use() {
144146
return;
145147
}
146148

147-
let mir::Place { local, projection } = *place;
149+
let is_consume = is_consume(context);
148150

149151
// Reads from ZSTs do not require memory accesses and do not count when determining what
150152
// needs to live on the stack.
151-
if is_consume(context) {
153+
if is_consume {
152154
let ty = place.ty(self.fx.mir, self.fx.cx.tcx()).ty;
153155
let ty = self.fx.monomorphize(&ty);
154156
let span = self.fx.mir.local_decls[local].source_info.span;
@@ -157,41 +159,53 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
157159
}
158160
}
159161

160-
let mut has_disqualifying_projection = false;
161-
162-
let mut projection: &[_] = projection.as_ref();
163-
while let [ref proj_base @ .., elem] = *projection {
164-
projection = proj_base;
165-
self.super_projection_elem(local, proj_base, elem, context, location);
166-
167-
// Projections like `(*x)[12]` are allowed but not `*(x[12])`, since a `Deref` of a
168-
// local acts like a `Copy` of that local.
169-
if let mir::PlaceElem::Deref = elem {
170-
has_disqualifying_projection = false;
171-
context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
172-
continue;
173-
}
162+
let is_indirect = place.is_indirect();
163+
if is_indirect {
164+
context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
165+
}
174166

175-
// Ignoring `Deref`s, the only allowed projections are reads of scalar fields.
176-
if is_consume(context) && matches!(elem, mir::ProjectionElem::Field(..)) {
177-
let base_ty = mir::Place::ty_from(local, proj_base, self.fx.mir, self.fx.cx.tcx());
178-
let base_ty = self.fx.monomorphize(&base_ty);
179-
let span = self.fx.mir.local_decls[local].source_info.span;
180-
let layout = self.fx.cx.spanned_layout_of(base_ty.ty, span);
167+
self.visit_local(&local, context, location);
181168

182-
if !ty_requires_alloca(self.fx, layout) {
183-
continue;
184-
}
169+
// In any context besides a simple read or pointer deref, any projections whatsoever force
170+
// a value onto the stack.
171+
if !is_consume && !is_indirect {
172+
if !projection.is_empty() {
173+
self.not_ssa(local);
185174
}
186175

187-
has_disqualifying_projection = true;
176+
return;
188177
}
189178

190-
if has_disqualifying_projection {
179+
// Only projections inside a `Deref` can disqualify a local from being placed on the stack.
180+
// In other words, `(*x)[idx]` does not disqualify `x` but `*(x[idx])` does.
181+
let first_deref = projection.iter().position(|elem| matches!(elem, mir::PlaceElem::Deref));
182+
let projections_on_base_local = &projection[..first_deref.unwrap_or(projection.len())];
183+
184+
// Only field projections are allowed. We check this before checking the layout of each
185+
// projection below since computing layouts is relatively expensive.
186+
if !projections_on_base_local.iter().all(|elem| matches!(elem, mir::PlaceElem::Field(..))) {
191187
self.not_ssa(local);
188+
return;
192189
}
193190

194-
self.visit_local(&local, context, location);
191+
// Ensure that each field being projected through is handled correctly.
192+
for (i, elem) in projections_on_base_local.iter().enumerate() {
193+
assert!(matches!(elem, mir::PlaceElem::Field(..)));
194+
195+
// The inclusive range here means we check every projection prefix but the empty one.
196+
// This is okay since the type of each local is checked in `non_ssa_locals`.
197+
let base = &projection[..=i];
198+
199+
let base_ty = mir::Place::ty_from(local, base, self.fx.mir, self.fx.cx.tcx());
200+
let base_ty = self.fx.monomorphize(&base_ty);
201+
let span = self.fx.mir.local_decls[local].source_info.span;
202+
let layout = self.fx.cx.spanned_layout_of(base_ty.ty, span);
203+
204+
if ty_requires_alloca(self.fx, layout) {
205+
self.not_ssa(local);
206+
return;
207+
}
208+
}
195209
}
196210

197211
fn visit_local(&mut self, &local: &mir::Local, context: PlaceContext, location: Location) {

0 commit comments

Comments
 (0)