Skip to content

Commit e0fdf7c

Browse files
Refactor LocalAnalyzer to use visit_projection_elem
1 parent 3e74727 commit e0fdf7c

File tree

1 file changed

+126
-121
lines changed

1 file changed

+126
-121
lines changed

src/librustc_codegen_ssa/mir/analyze.rs

Lines changed: 126 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ use rustc_data_structures::graph::dominators::Dominators;
77
use rustc_index::bit_set::BitSet;
88
use rustc_index::vec::IndexVec;
99
use rustc_middle::mir::traversal;
10-
use rustc_middle::mir::visit::{
11-
MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor,
12-
};
10+
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
1311
use rustc_middle::mir::{self, Location, TerminatorKind};
1412
use rustc_middle::ty;
1513
use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
@@ -21,7 +19,13 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
2119
let mir = fx.mir;
2220
let mut analyzer = LocalAnalyzer::new(fx);
2321

24-
analyzer.visit_body(&mir);
22+
for (block, data) in traversal::reverse_postorder(mir) {
23+
analyzer.visit_basic_block_data(block, data);
24+
}
25+
26+
for debuginfo in mir.var_debug_info.iter() {
27+
analyzer.visit_var_debug_info(debuginfo);
28+
}
2529

2630
for (local, decl) in mir.local_decls.iter_enumerated() {
2731
let ty = fx.monomorphize(&decl.ty);
@@ -36,13 +40,21 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
3640
analyzer.non_ssa_locals
3741
}
3842

43+
#[derive(Default, PartialEq, Eq)]
44+
struct PlaceInfo {
45+
has_disqualifying_projection: bool,
46+
has_deref_projection: bool,
47+
}
48+
3949
struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
4050
fx: &'mir FunctionCx<'a, 'tcx, Bx>,
4151
dominators: Dominators<mir::BasicBlock>,
4252
non_ssa_locals: BitSet<mir::Local>,
4353

4454
/// The location of the first visited direct assignment to each local.
4555
first_assignment: IndexVec<mir::Local, Option<Location>>,
56+
57+
place_info: PlaceInfo,
4658
}
4759

4860
impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
@@ -53,6 +65,7 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
5365
dominators,
5466
non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
5567
first_assignment: IndexVec::from_elem(None, &fx.mir.local_decls),
68+
place_info: PlaceInfo::default(),
5669
};
5770

5871
// Arguments get assigned to by means of the function being called
@@ -79,118 +92,6 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
7992
self.first_assignment[local] = Some(location);
8093
}
8194
}
82-
83-
fn process_place(
84-
&mut self,
85-
place_ref: &mir::PlaceRef<'tcx>,
86-
context: PlaceContext,
87-
location: Location,
88-
) {
89-
let cx = self.fx.cx;
90-
91-
if let &[ref proj_base @ .., elem] = place_ref.projection {
92-
let mut base_context = if context.is_mutating_use() {
93-
PlaceContext::MutatingUse(MutatingUseContext::Projection)
94-
} else {
95-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
96-
};
97-
98-
// Allow uses of projections that are ZSTs or from scalar fields.
99-
let is_consume = match context {
100-
PlaceContext::NonMutatingUse(
101-
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
102-
) => true,
103-
_ => false,
104-
};
105-
if is_consume {
106-
let base_ty =
107-
mir::Place::ty_from(place_ref.local, proj_base, self.fx.mir, cx.tcx());
108-
let base_ty = self.fx.monomorphize(&base_ty);
109-
110-
// ZSTs don't require any actual memory access.
111-
let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(&elem)).ty;
112-
let span = self.fx.mir.local_decls[place_ref.local].source_info.span;
113-
if cx.spanned_layout_of(elem_ty, span).is_zst() {
114-
return;
115-
}
116-
117-
if let mir::ProjectionElem::Field(..) = elem {
118-
let layout = cx.spanned_layout_of(base_ty.ty, span);
119-
if self.ty_requires_alloca(layout) {
120-
// Recurse with the same context, instead of `Projection`,
121-
// potentially stopping at non-operand projections,
122-
// which would trigger `not_ssa` on locals.
123-
base_context = context;
124-
}
125-
}
126-
}
127-
128-
if let mir::ProjectionElem::Deref = elem {
129-
// Deref projections typically only read the pointer.
130-
// (the exception being `VarDebugInfo` contexts, handled below)
131-
base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
132-
133-
// Indirect debuginfo requires going through memory, that only
134-
// the debugger accesses, following our emitted DWARF pointer ops.
135-
//
136-
// FIXME(eddyb) Investigate the possibility of relaxing this, but
137-
// note that `llvm.dbg.declare` *must* be used for indirect places,
138-
// even if we start using `llvm.dbg.value` for all other cases,
139-
// as we don't necessarily know when the value changes, but only
140-
// where it lives in memory.
141-
//
142-
// It's possible `llvm.dbg.declare` could support starting from
143-
// a pointer that doesn't point to an `alloca`, but this would
144-
// only be useful if we know the pointer being `Deref`'d comes
145-
// from an immutable place, and if `llvm.dbg.declare` calls
146-
// must be at the very start of the function, then only function
147-
// arguments could contain such pointers.
148-
if context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
149-
// We use `NonUseContext::VarDebugInfo` for the base,
150-
// which might not force the base local to memory,
151-
// so we have to do it manually.
152-
self.visit_local(&place_ref.local, context, location);
153-
}
154-
}
155-
156-
// `NonUseContext::VarDebugInfo` needs to flow all the
157-
// way down to the base local (see `visit_local`).
158-
if context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
159-
base_context = context;
160-
}
161-
162-
self.process_place(
163-
&mir::PlaceRef { local: place_ref.local, projection: proj_base },
164-
base_context,
165-
location,
166-
);
167-
// HACK(eddyb) this emulates the old `visit_projection_elem`, this
168-
// entire `visit_place`-like `process_place` method should be rewritten,
169-
// now that we have moved to the "slice of projections" representation.
170-
if let mir::ProjectionElem::Index(local) = elem {
171-
self.visit_local(
172-
&local,
173-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
174-
location,
175-
);
176-
}
177-
} else {
178-
// FIXME this is super_place code, is repeated here to avoid cloning place or changing
179-
// visit_place API
180-
let mut context = context;
181-
182-
if !place_ref.projection.is_empty() {
183-
context = if context.is_mutating_use() {
184-
PlaceContext::MutatingUse(MutatingUseContext::Projection)
185-
} else {
186-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
187-
};
188-
}
189-
190-
self.visit_local(&place_ref.local, context, location);
191-
self.visit_projection(place_ref.local, place_ref.projection, context, location);
192-
}
193-
}
19495
}
19596

19697
impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
@@ -247,7 +148,45 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
247148

248149
fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
249150
debug!("visit_place(place={:?}, context={:?})", place, context);
250-
self.process_place(&place.as_ref(), context, location);
151+
152+
// Except for `VarDebugInfo`, non-uses do not force locals onto the stack.
153+
//
154+
// `VarDebugInfo` is handled in `visit_var_debug_info`.
155+
if !context.is_use() {
156+
return;
157+
}
158+
159+
let mir::Place { local, projection } = *place;
160+
161+
// Reads from ZSTs do not require memory accesses.
162+
if is_consume(context) {
163+
let ty = place.ty(self.fx.mir, self.fx.cx.tcx()).ty;
164+
let ty = self.fx.monomorphize(&ty);
165+
let span = self.fx.mir.local_decls[local].source_info.span;
166+
if self.fx.cx.spanned_layout_of(ty, span).is_zst() {
167+
return;
168+
}
169+
}
170+
171+
assert!(self.place_info == PlaceInfo::default(), "`visit_place` should not recurse");
172+
self.visit_projection(local, projection, context, location);
173+
174+
let PlaceInfo { has_disqualifying_projection, has_deref_projection } =
175+
std::mem::take(&mut self.place_info);
176+
177+
if has_disqualifying_projection {
178+
self.not_ssa(local);
179+
return;
180+
}
181+
182+
// Treat a `Deref` of a local as a `Copy` of that local.
183+
let context = if has_deref_projection {
184+
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy)
185+
} else {
186+
context
187+
};
188+
189+
self.visit_local(&local, context, location);
251190
}
252191

253192
fn visit_local(&mut self, &local: &mir::Local, context: PlaceContext, location: Location) {
@@ -277,20 +216,23 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
277216
}
278217
}
279218

219+
PlaceContext::MutatingUse(MutatingUseContext::Projection)
220+
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
221+
unreachable!("We always use the original context from `visit_place`")
222+
}
223+
280224
PlaceContext::MutatingUse(
281225
MutatingUseContext::Store
282226
| MutatingUseContext::AsmOutput
283227
| MutatingUseContext::Borrow
284-
| MutatingUseContext::AddressOf
285-
| MutatingUseContext::Projection,
228+
| MutatingUseContext::AddressOf,
286229
)
287230
| PlaceContext::NonMutatingUse(
288231
NonMutatingUseContext::Inspect
289232
| NonMutatingUseContext::SharedBorrow
290233
| NonMutatingUseContext::UniqueBorrow
291234
| NonMutatingUseContext::ShallowBorrow
292-
| NonMutatingUseContext::AddressOf
293-
| NonMutatingUseContext::Projection,
235+
| NonMutatingUseContext::AddressOf,
294236
) => {
295237
self.not_ssa(local);
296238
}
@@ -306,6 +248,62 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
306248
}
307249
}
308250
}
251+
252+
fn visit_projection_elem(
253+
&mut self,
254+
local: mir::Local,
255+
proj_base: &[mir::PlaceElem<'tcx>],
256+
elem: mir::PlaceElem<'tcx>,
257+
context: PlaceContext,
258+
location: Location,
259+
) {
260+
self.super_projection_elem(local, proj_base, elem, context, location);
261+
262+
// Projections like `(*x)[12]` are allowed but not `*(x[12])`.
263+
if let mir::PlaceElem::Deref = elem {
264+
self.place_info.has_disqualifying_projection = false;
265+
self.place_info.has_deref_projection = true;
266+
return;
267+
}
268+
269+
if !is_consume(context) {
270+
self.place_info.has_disqualifying_projection = true;
271+
return;
272+
}
273+
274+
if let mir::ProjectionElem::Field(..) = elem {
275+
let base_ty = mir::Place::ty_from(local, proj_base, self.fx.mir, self.fx.cx.tcx());
276+
let base_ty = self.fx.monomorphize(&base_ty);
277+
let span = self.fx.mir.local_decls[local].source_info.span;
278+
let layout = self.fx.cx.spanned_layout_of(base_ty.ty, span);
279+
if !ty_requires_alloca(self.fx, layout) {
280+
return;
281+
}
282+
}
283+
284+
self.place_info.has_disqualifying_projection = true;
285+
}
286+
287+
fn visit_var_debug_info(&mut self, var_debug_info: &mir::VarDebugInfo<'tcx>) {
288+
// Indirect debuginfo requires going through memory, that only
289+
// the debugger accesses, following our emitted DWARF pointer ops.
290+
//
291+
// FIXME(eddyb) Investigate the possibility of relaxing this, but
292+
// note that `llvm.dbg.declare` *must* be used for indirect places,
293+
// even if we start using `llvm.dbg.value` for all other cases,
294+
// as we don't necessarily know when the value changes, but only
295+
// where it lives in memory.
296+
//
297+
// It's possible `llvm.dbg.declare` could support starting from
298+
// a pointer that doesn't point to an `alloca`, but this would
299+
// only be useful if we know the pointer being `Deref`'d comes
300+
// from an immutable place, and if `llvm.dbg.declare` calls
301+
// must be at the very start of the function, then only function
302+
// arguments could contain such pointers.
303+
if var_debug_info.place.is_indirect() {
304+
self.not_ssa(var_debug_info.place.local);
305+
}
306+
}
309307
}
310308

311309
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -441,3 +439,10 @@ fn ty_requires_alloca<'a, 'tcx>(
441439
// (gep, extractvalue, etc.).
442440
!fx.cx.is_backend_immediate(ty) && !fx.cx.is_backend_scalar_pair(ty)
443441
}
442+
443+
fn is_consume(context: PlaceContext) -> bool {
444+
matches!(
445+
context,
446+
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy | NonMutatingUseContext::Move)
447+
)
448+
}

0 commit comments

Comments
 (0)