@@ -7,9 +7,7 @@ use rustc_data_structures::graph::dominators::Dominators;
77use rustc_index:: bit_set:: BitSet ;
88use rustc_index:: vec:: IndexVec ;
99use 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 } ;
1311use rustc_middle:: mir:: { self , Location , TerminatorKind } ;
1412use rustc_middle:: ty;
1513use 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+
3949struct 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
4860impl < 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
19697impl < ' 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