5
5
//! As a consequence of rule 2, we consider that borrowed locals are not SSA, even if they are
6
6
//! `Freeze`, as we do not track that the assignment dominates all uses of the borrow.
7
7
8
- use either:: Either ;
9
8
use rustc_data_structures:: graph:: dominators:: Dominators ;
10
9
use rustc_index:: bit_set:: BitSet ;
11
10
use rustc_index:: { IndexSlice , IndexVec } ;
@@ -27,6 +26,12 @@ pub struct SsaLocals {
27
26
direct_uses : IndexVec < Local , u32 > ,
28
27
}
29
28
29
+ pub enum AssignedValue < ' a , ' tcx > {
30
+ Arg ,
31
+ Rvalue ( & ' a mut Rvalue < ' tcx > ) ,
32
+ Terminator ( & ' a mut TerminatorKind < ' tcx > ) ,
33
+ }
34
+
30
35
impl SsaLocals {
31
36
pub fn new < ' tcx > ( body : & Body < ' tcx > ) -> SsaLocals {
32
37
let assignment_order = Vec :: with_capacity ( body. local_decls . len ( ) ) ;
@@ -39,6 +44,7 @@ impl SsaLocals {
39
44
40
45
for local in body. args_iter ( ) {
41
46
visitor. assignments [ local] = Set1 :: One ( DefLocation :: Argument ) ;
47
+ visitor. assignment_order . push ( local) ;
42
48
}
43
49
44
50
// For SSA assignments, a RPO visit will see the assignment before it sees any use.
@@ -105,8 +111,8 @@ impl SsaLocals {
105
111
) -> impl Iterator < Item = ( Local , & ' a Rvalue < ' tcx > , Location ) > + ' a {
106
112
self . assignment_order . iter ( ) . filter_map ( |& local| {
107
113
if let Set1 :: One ( DefLocation :: Body ( loc) ) = self . assignments [ local] {
114
+ let stmt = body. stmt_at ( loc) . left ( ) ?;
108
115
// `loc` must point to a direct assignment to `local`.
109
- let Either :: Left ( stmt) = body. stmt_at ( loc) else { bug ! ( ) } ;
110
116
let Some ( ( target, rvalue) ) = stmt. kind . as_assign ( ) else { bug ! ( ) } ;
111
117
assert_eq ! ( target. as_local( ) , Some ( local) ) ;
112
118
Some ( ( local, rvalue, loc) )
@@ -118,18 +124,33 @@ impl SsaLocals {
118
124
119
125
pub fn for_each_assignment_mut < ' tcx > (
120
126
& self ,
121
- basic_blocks : & mut BasicBlocks < ' tcx > ,
122
- mut f : impl FnMut ( Local , & mut Rvalue < ' tcx > , Location ) ,
127
+ basic_blocks : & mut IndexSlice < BasicBlock , BasicBlockData < ' tcx > > ,
128
+ mut f : impl FnMut ( Local , AssignedValue < ' _ , ' tcx > , Location ) ,
123
129
) {
124
130
for & local in & self . assignment_order {
125
- if let Set1 :: One ( DefLocation :: Body ( loc) ) = self . assignments [ local] {
126
- // `loc` must point to a direct assignment to `local`.
127
- let bbs = basic_blocks. as_mut_preserves_cfg ( ) ;
128
- let bb = & mut bbs[ loc. block ] ;
129
- let stmt = & mut bb. statements [ loc. statement_index ] ;
130
- let StatementKind :: Assign ( box ( target, ref mut rvalue) ) = stmt. kind else { bug ! ( ) } ;
131
- assert_eq ! ( target. as_local( ) , Some ( local) ) ;
132
- f ( local, rvalue, loc)
131
+ match self . assignments [ local] {
132
+ Set1 :: One ( DefLocation :: Argument ) => f (
133
+ local,
134
+ AssignedValue :: Arg ,
135
+ Location { block : START_BLOCK , statement_index : 0 } ,
136
+ ) ,
137
+ Set1 :: One ( DefLocation :: Body ( loc) ) => {
138
+ let bb = & mut basic_blocks[ loc. block ] ;
139
+ let value = if loc. statement_index < bb. statements . len ( ) {
140
+ // `loc` must point to a direct assignment to `local`.
141
+ let stmt = & mut bb. statements [ loc. statement_index ] ;
142
+ let StatementKind :: Assign ( box ( target, ref mut rvalue) ) = stmt. kind else {
143
+ bug ! ( )
144
+ } ;
145
+ assert_eq ! ( target. as_local( ) , Some ( local) ) ;
146
+ AssignedValue :: Rvalue ( rvalue)
147
+ } else {
148
+ let term = bb. terminator_mut ( ) ;
149
+ AssignedValue :: Terminator ( & mut term. kind )
150
+ } ;
151
+ f ( local, value, loc)
152
+ }
153
+ _ => { }
133
154
}
134
155
}
135
156
}
@@ -228,34 +249,34 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> {
228
249
}
229
250
230
251
fn visit_place ( & mut self , place : & Place < ' tcx > , ctxt : PlaceContext , loc : Location ) {
231
- if place. projection . first ( ) == Some ( & PlaceElem :: Deref ) {
232
- // Do not do anything for storage statements and debuginfo.
252
+ let location = match ctxt {
253
+ PlaceContext :: MutatingUse (
254
+ MutatingUseContext :: Store | MutatingUseContext :: Call | MutatingUseContext :: Yield ,
255
+ ) => Some ( DefLocation :: Body ( loc) ) ,
256
+ _ => None ,
257
+ } ;
258
+ if let Some ( location) = location
259
+ && let Some ( local) = place. as_local ( )
260
+ {
261
+ self . assignments [ local] . insert ( location) ;
262
+ if let Set1 :: One ( _) = self . assignments [ local] {
263
+ // Only record if SSA-like, to avoid growing the vector needlessly.
264
+ self . assignment_order . push ( local) ;
265
+ }
266
+ } else if place. projection . first ( ) == Some ( & PlaceElem :: Deref ) {
267
+ // Do not do anything for debuginfo.
233
268
if ctxt. is_use ( ) {
234
269
// Only change the context if it is a real use, not a "use" in debuginfo.
235
270
let new_ctxt = PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
236
271
237
272
self . visit_projection ( place. as_ref ( ) , new_ctxt, loc) ;
238
273
self . check_dominates ( place. local , loc) ;
239
274
}
240
- return ;
241
275
} else {
242
276
self . visit_projection ( place. as_ref ( ) , ctxt, loc) ;
243
277
self . visit_local ( place. local , ctxt, loc) ;
244
278
}
245
279
}
246
-
247
- fn visit_assign ( & mut self , place : & Place < ' tcx > , rvalue : & Rvalue < ' tcx > , loc : Location ) {
248
- if let Some ( local) = place. as_local ( ) {
249
- self . assignments [ local] . insert ( DefLocation :: Body ( loc) ) ;
250
- if let Set1 :: One ( _) = self . assignments [ local] {
251
- // Only record if SSA-like, to avoid growing the vector needlessly.
252
- self . assignment_order . push ( local) ;
253
- }
254
- } else {
255
- self . visit_place ( place, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) , loc) ;
256
- }
257
- self . visit_rvalue ( rvalue, loc) ;
258
- }
259
280
}
260
281
261
282
#[ instrument( level = "trace" , skip( ssa, body) ) ]
0 commit comments