@@ -7,9 +7,7 @@ use rustc_data_structures::graph::dominators::Dominators;
7
7
use rustc_index:: bit_set:: BitSet ;
8
8
use rustc_index:: vec:: IndexVec ;
9
9
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 } ;
13
11
use rustc_middle:: mir:: { self , Location , TerminatorKind } ;
14
12
use rustc_middle:: ty;
15
13
use rustc_middle:: ty:: layout:: { HasTyCtxt , TyAndLayout } ;
@@ -21,7 +19,13 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
21
19
let mir = fx. mir ;
22
20
let mut analyzer = LocalAnalyzer :: new ( fx) ;
23
21
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
+ }
25
29
26
30
for ( local, decl) in mir. local_decls . iter_enumerated ( ) {
27
31
let ty = fx. monomorphize ( & decl. ty ) ;
@@ -36,13 +40,21 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
36
40
analyzer. non_ssa_locals
37
41
}
38
42
43
+ #[ derive( Default , PartialEq , Eq ) ]
44
+ struct PlaceInfo {
45
+ has_disqualifying_projection : bool ,
46
+ has_deref_projection : bool ,
47
+ }
48
+
39
49
struct LocalAnalyzer < ' mir , ' a , ' tcx , Bx : BuilderMethods < ' a , ' tcx > > {
40
50
fx : & ' mir FunctionCx < ' a , ' tcx , Bx > ,
41
51
dominators : Dominators < mir:: BasicBlock > ,
42
52
non_ssa_locals : BitSet < mir:: Local > ,
43
53
44
54
/// The location of the first visited direct assignment to each local.
45
55
first_assignment : IndexVec < mir:: Local , Option < Location > > ,
56
+
57
+ place_info : PlaceInfo ,
46
58
}
47
59
48
60
impl < Bx : BuilderMethods < ' a , ' tcx > > LocalAnalyzer < ' mir , ' a , ' tcx , Bx > {
@@ -53,6 +65,7 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
53
65
dominators,
54
66
non_ssa_locals : BitSet :: new_empty ( fx. mir . local_decls . len ( ) ) ,
55
67
first_assignment : IndexVec :: from_elem ( None , & fx. mir . local_decls ) ,
68
+ place_info : PlaceInfo :: default ( ) ,
56
69
} ;
57
70
58
71
// 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> {
79
92
self . first_assignment [ local] = Some ( location) ;
80
93
}
81
94
}
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
- }
194
95
}
195
96
196
97
impl < ' mir , ' a , ' tcx , Bx : BuilderMethods < ' a , ' tcx > > Visitor < ' tcx >
@@ -247,7 +148,45 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
247
148
248
149
fn visit_place ( & mut self , place : & mir:: Place < ' tcx > , context : PlaceContext , location : Location ) {
249
150
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) ;
251
190
}
252
191
253
192
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>
277
216
}
278
217
}
279
218
219
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Projection )
220
+ | PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Projection ) => {
221
+ unreachable ! ( "We always use the original context from `visit_place`" )
222
+ }
223
+
280
224
PlaceContext :: MutatingUse (
281
225
MutatingUseContext :: Store
282
226
| MutatingUseContext :: AsmOutput
283
227
| MutatingUseContext :: Borrow
284
- | MutatingUseContext :: AddressOf
285
- | MutatingUseContext :: Projection ,
228
+ | MutatingUseContext :: AddressOf ,
286
229
)
287
230
| PlaceContext :: NonMutatingUse (
288
231
NonMutatingUseContext :: Inspect
289
232
| NonMutatingUseContext :: SharedBorrow
290
233
| NonMutatingUseContext :: UniqueBorrow
291
234
| NonMutatingUseContext :: ShallowBorrow
292
- | NonMutatingUseContext :: AddressOf
293
- | NonMutatingUseContext :: Projection ,
235
+ | NonMutatingUseContext :: AddressOf ,
294
236
) => {
295
237
self . not_ssa ( local) ;
296
238
}
@@ -306,6 +248,62 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
306
248
}
307
249
}
308
250
}
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
+ }
309
307
}
310
308
311
309
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -441,3 +439,10 @@ fn ty_requires_alloca<'a, 'tcx>(
441
439
// (gep, extractvalue, etc.).
442
440
!fx. cx . is_backend_immediate ( ty) && !fx. cx . is_backend_scalar_pair ( ty)
443
441
}
442
+
443
+ fn is_consume ( context : PlaceContext ) -> bool {
444
+ matches ! (
445
+ context,
446
+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy | NonMutatingUseContext :: Move )
447
+ )
448
+ }
0 commit comments