@@ -49,9 +49,13 @@ impl SmallDominators<'_> {
49
49
let assign_dominates = match * set {
50
50
Set1 :: Empty | Set1 :: Many => false ,
51
51
Set1 :: One ( LocationExtended :: Arg ) => true ,
52
- Set1 :: One ( LocationExtended :: Plain ( assign) ) => {
52
+ Set1 :: One ( LocationExtended :: Assign ( assign) ) => {
53
53
self . dominates ( assign. successor_within_block ( ) , loc)
54
54
}
55
+ Set1 :: One ( LocationExtended :: Terminator ( _, effect_bb) ) => {
56
+ let effect_loc = Location { block : effect_bb, statement_index : 0 } ;
57
+ self . dominates ( effect_loc, loc)
58
+ }
55
59
} ;
56
60
// We are visiting a use that is not dominated by an assignment.
57
61
// Either there is a cycle involved, or we are reading for uninitialized local.
@@ -62,6 +66,12 @@ impl SmallDominators<'_> {
62
66
}
63
67
}
64
68
69
+ pub enum AssignedValue < ' a , ' tcx > {
70
+ Arg ,
71
+ Rvalue ( & ' a mut Rvalue < ' tcx > ) ,
72
+ Terminator ( & ' a mut TerminatorKind < ' tcx > ) ,
73
+ }
74
+
65
75
impl SsaLocals {
66
76
pub fn new < ' tcx > ( body : & Body < ' tcx > ) -> SsaLocals {
67
77
let assignment_order = Vec :: with_capacity ( body. local_decls . len ( ) ) ;
@@ -72,10 +82,12 @@ impl SsaLocals {
72
82
let dominators = SmallDominators { inner : dominators } ;
73
83
74
84
let direct_uses = IndexVec :: from_elem ( 0 , & body. local_decls ) ;
75
- let mut visitor = SsaVisitor { assignments, assignment_order, dominators, direct_uses } ;
85
+ let mut visitor =
86
+ SsaVisitor { body, assignments, assignment_order, dominators, direct_uses } ;
76
87
77
88
for local in body. args_iter ( ) {
78
89
visitor. assignments [ local] = Set1 :: One ( LocationExtended :: Arg ) ;
90
+ visitor. assignment_order . push ( local) ;
79
91
}
80
92
81
93
if body. basic_blocks . len ( ) > 2 {
@@ -136,13 +148,16 @@ impl SsaLocals {
136
148
) -> bool {
137
149
match self . assignments [ local] {
138
150
Set1 :: One ( LocationExtended :: Arg ) => true ,
139
- Set1 :: One ( LocationExtended :: Plain ( ass) ) => {
151
+ Set1 :: One ( LocationExtended :: Assign ( ass) ) => {
140
152
if ass. block == location. block {
141
153
ass. statement_index < location. statement_index
142
154
} else {
143
155
dominators. dominates ( ass. block , location. block )
144
156
}
145
157
}
158
+ Set1 :: One ( LocationExtended :: Terminator ( _, effect_bb) ) => {
159
+ dominators. dominates ( effect_bb, location. block )
160
+ }
146
161
_ => false ,
147
162
}
148
163
}
@@ -152,7 +167,7 @@ impl SsaLocals {
152
167
body : & ' a Body < ' tcx > ,
153
168
) -> impl Iterator < Item = ( Local , & ' a Rvalue < ' tcx > , Location ) > + ' a {
154
169
self . assignment_order . iter ( ) . filter_map ( |& local| {
155
- if let Set1 :: One ( LocationExtended :: Plain ( loc) ) = self . assignments [ local] {
170
+ if let Set1 :: One ( LocationExtended :: Assign ( loc) ) = self . assignments [ local] {
156
171
// `loc` must point to a direct assignment to `local`.
157
172
let Either :: Left ( stmt) = body. stmt_at ( loc) else { bug ! ( ) } ;
158
173
let Some ( ( target, rvalue) ) = stmt. kind . as_assign ( ) else { bug ! ( ) } ;
@@ -166,18 +181,33 @@ impl SsaLocals {
166
181
167
182
pub fn for_each_assignment_mut < ' tcx > (
168
183
& self ,
169
- basic_blocks : & mut BasicBlocks < ' tcx > ,
170
- mut f : impl FnMut ( Local , & mut Rvalue < ' tcx > , Location ) ,
184
+ basic_blocks : & mut IndexSlice < BasicBlock , BasicBlockData < ' tcx > > ,
185
+ mut f : impl FnMut ( Local , AssignedValue < ' _ , ' tcx > , Location ) ,
171
186
) {
172
187
for & local in & self . assignment_order {
173
- if let Set1 :: One ( LocationExtended :: Plain ( loc) ) = self . assignments [ local] {
174
- // `loc` must point to a direct assignment to `local`.
175
- let bbs = basic_blocks. as_mut_preserves_cfg ( ) ;
176
- let bb = & mut bbs[ loc. block ] ;
177
- let stmt = & mut bb. statements [ loc. statement_index ] ;
178
- let StatementKind :: Assign ( box ( target, ref mut rvalue) ) = stmt. kind else { bug ! ( ) } ;
179
- assert_eq ! ( target. as_local( ) , Some ( local) ) ;
180
- f ( local, rvalue, loc)
188
+ match self . assignments [ local] {
189
+ Set1 :: One ( LocationExtended :: Arg ) => f (
190
+ local,
191
+ AssignedValue :: Arg ,
192
+ Location { block : START_BLOCK , statement_index : 0 } ,
193
+ ) ,
194
+ Set1 :: One ( LocationExtended :: Assign ( loc) ) => {
195
+ // `loc` must point to a direct assignment to `local`.
196
+ let bb = & mut basic_blocks[ loc. block ] ;
197
+ let stmt = & mut bb. statements [ loc. statement_index ] ;
198
+ let StatementKind :: Assign ( box ( target, ref mut rvalue) ) = stmt. kind else {
199
+ bug ! ( )
200
+ } ;
201
+ assert_eq ! ( target. as_local( ) , Some ( local) ) ;
202
+ f ( local, AssignedValue :: Rvalue ( rvalue) , loc)
203
+ }
204
+ Set1 :: One ( LocationExtended :: Terminator ( block, _) ) => {
205
+ let loc =
206
+ Location { block, statement_index : basic_blocks[ block] . statements . len ( ) } ;
207
+ let term = basic_blocks[ block] . terminator_mut ( ) ;
208
+ f ( local, AssignedValue :: Terminator ( & mut term. kind ) , loc)
209
+ }
210
+ _ => { }
181
211
}
182
212
}
183
213
}
@@ -230,18 +260,24 @@ impl SsaLocals {
230
260
231
261
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
232
262
enum LocationExtended {
233
- Plain ( Location ) ,
263
+ /// This local was assigned as a function parameter, before any execution.
234
264
Arg ,
265
+ /// `Location` points to the `Assign` statement.
266
+ Assign ( Location ) ,
267
+ /// The blocks are respectively the bb which contains the assignment,
268
+ /// and the bb in which the assignment effect is complete.
269
+ Terminator ( BasicBlock , BasicBlock ) ,
235
270
}
236
271
237
- struct SsaVisitor < ' a > {
272
+ struct SsaVisitor < ' a , ' tcx > {
273
+ body : & ' a Body < ' tcx > ,
238
274
dominators : SmallDominators < ' a > ,
239
275
assignments : IndexVec < Local , Set1 < LocationExtended > > ,
240
276
assignment_order : Vec < Local > ,
241
277
direct_uses : IndexVec < Local , u32 > ,
242
278
}
243
279
244
- impl < ' tcx > Visitor < ' tcx > for SsaVisitor < ' _ > {
280
+ impl < ' tcx > Visitor < ' tcx > for SsaVisitor < ' _ , ' tcx > {
245
281
fn visit_local ( & mut self , local : Local , ctxt : PlaceContext , loc : Location ) {
246
282
match ctxt {
247
283
PlaceContext :: MutatingUse ( MutatingUseContext :: Projection )
@@ -266,34 +302,46 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> {
266
302
}
267
303
268
304
fn visit_place ( & mut self , place : & Place < ' tcx > , ctxt : PlaceContext , loc : Location ) {
269
- if place. projection . first ( ) == Some ( & PlaceElem :: Deref ) {
270
- // Do not do anything for storage statements and debuginfo.
305
+ let location = match ctxt {
306
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) => {
307
+ Some ( LocationExtended :: Assign ( loc) )
308
+ }
309
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Call | MutatingUseContext :: Yield ) => {
310
+ // The assignment happens on the `loc -> target` edge. We need to ensure that
311
+ // this *edge* dominates all uses of the local. This is true if
312
+ // `loc` dominates `target` *and* `target` dominates all uses.
313
+ if let Some ( target) = self . body . basic_blocks [ loc. block ] . terminator ( ) . successors ( ) . next ( )
314
+ && self . dominators . dominates ( loc, Location { block : target, statement_index : 0 } )
315
+ {
316
+ Some ( LocationExtended :: Terminator ( loc. block , target) )
317
+ } else {
318
+ None
319
+ }
320
+ }
321
+ _ => None ,
322
+ } ;
323
+ if let Some ( location) = location
324
+ && let Some ( local) = place. as_local ( )
325
+ {
326
+ self . assignments [ local] . insert ( location) ;
327
+ if let Set1 :: One ( _) = self . assignments [ local] {
328
+ // Only record if SSA-like, to avoid growing the vector needlessly.
329
+ self . assignment_order . push ( local) ;
330
+ }
331
+ } else if place. projection . first ( ) == Some ( & PlaceElem :: Deref ) {
332
+ // Do not do anything for debuginfo.
271
333
if ctxt. is_use ( ) {
272
334
// Only change the context if it is a real use, not a "use" in debuginfo.
273
335
let new_ctxt = PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
274
336
275
337
self . visit_projection ( place. as_ref ( ) , new_ctxt, loc) ;
276
338
self . dominators . check_dominates ( & mut self . assignments [ place. local ] , loc) ;
277
339
}
278
- return ;
279
340
} else {
280
341
self . visit_projection ( place. as_ref ( ) , ctxt, loc) ;
281
342
self . visit_local ( place. local , ctxt, loc) ;
282
343
}
283
344
}
284
-
285
- fn visit_assign ( & mut self , place : & Place < ' tcx > , rvalue : & Rvalue < ' tcx > , loc : Location ) {
286
- if let Some ( local) = place. as_local ( ) {
287
- self . assignments [ local] . insert ( LocationExtended :: Plain ( loc) ) ;
288
- if let Set1 :: One ( _) = self . assignments [ local] {
289
- // Only record if SSA-like, to avoid growing the vector needlessly.
290
- self . assignment_order . push ( local) ;
291
- }
292
- } else {
293
- self . visit_place ( place, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) , loc) ;
294
- }
295
- self . visit_rvalue ( rvalue, loc) ;
296
- }
297
345
}
298
346
299
347
#[ instrument( level = "trace" , skip( ssa, body) ) ]
@@ -376,7 +424,7 @@ impl StorageLiveLocals {
376
424
for ( statement_index, statement) in bbdata. statements . iter ( ) . enumerate ( ) {
377
425
if let StatementKind :: StorageLive ( local) = statement. kind {
378
426
storage_live[ local]
379
- . insert ( LocationExtended :: Plain ( Location { block, statement_index } ) ) ;
427
+ . insert ( LocationExtended :: Assign ( Location { block, statement_index } ) ) ;
380
428
}
381
429
}
382
430
}
0 commit comments