55//! As a consequence of rule 2, we consider that borrowed locals are not SSA, even if they are
66//! `Freeze`, as we do not track that the assignment dominates all uses of the borrow.
77
8- use either:: Either ;
98use rustc_data_structures:: graph:: dominators:: Dominators ;
109use rustc_index:: bit_set:: BitSet ;
1110use rustc_index:: { IndexSlice , IndexVec } ;
@@ -27,6 +26,12 @@ pub struct SsaLocals {
2726 direct_uses : IndexVec < Local , u32 > ,
2827}
2928
29+ pub enum AssignedValue < ' a , ' tcx > {
30+ Arg ,
31+ Rvalue ( & ' a mut Rvalue < ' tcx > ) ,
32+ Terminator ( & ' a mut TerminatorKind < ' tcx > ) ,
33+ }
34+
3035impl SsaLocals {
3136 pub fn new < ' tcx > ( body : & Body < ' tcx > ) -> SsaLocals {
3237 let assignment_order = Vec :: with_capacity ( body. local_decls . len ( ) ) ;
@@ -39,6 +44,7 @@ impl SsaLocals {
3944
4045 for local in body. args_iter ( ) {
4146 visitor. assignments [ local] = Set1 :: One ( DefLocation :: Argument ) ;
47+ visitor. assignment_order . push ( local) ;
4248 }
4349
4450 // For SSA assignments, a RPO visit will see the assignment before it sees any use.
@@ -105,8 +111,8 @@ impl SsaLocals {
105111 ) -> impl Iterator < Item = ( Local , & ' a Rvalue < ' tcx > , Location ) > + ' a {
106112 self . assignment_order . iter ( ) . filter_map ( |& local| {
107113 if let Set1 :: One ( DefLocation :: Body ( loc) ) = self . assignments [ local] {
114+ let stmt = body. stmt_at ( loc) . left ( ) ?;
108115 // `loc` must point to a direct assignment to `local`.
109- let Either :: Left ( stmt) = body. stmt_at ( loc) else { bug ! ( ) } ;
110116 let Some ( ( target, rvalue) ) = stmt. kind . as_assign ( ) else { bug ! ( ) } ;
111117 assert_eq ! ( target. as_local( ) , Some ( local) ) ;
112118 Some ( ( local, rvalue, loc) )
@@ -118,18 +124,33 @@ impl SsaLocals {
118124
119125 pub fn for_each_assignment_mut < ' tcx > (
120126 & 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 ) ,
123129 ) {
124130 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+ _ => { }
133154 }
134155 }
135156 }
@@ -228,34 +249,34 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> {
228249 }
229250
230251 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.
233268 if ctxt. is_use ( ) {
234269 // Only change the context if it is a real use, not a "use" in debuginfo.
235270 let new_ctxt = PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
236271
237272 self . visit_projection ( place. as_ref ( ) , new_ctxt, loc) ;
238273 self . check_dominates ( place. local , loc) ;
239274 }
240- return ;
241275 } else {
242276 self . visit_projection ( place. as_ref ( ) , ctxt, loc) ;
243277 self . visit_local ( place. local , ctxt, loc) ;
244278 }
245279 }
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- }
259280}
260281
261282#[ instrument( level = "trace" , skip( ssa, body) ) ]
0 commit comments