1- use either:: Either ;
2- use rustc_data_structures:: graph:: dominators:: Dominators ;
31use rustc_index:: bit_set:: BitSet ;
42use rustc_index:: vec:: IndexVec ;
5- use rustc_middle:: middle:: resolve_lifetime:: Set1 ;
63use rustc_middle:: mir:: visit:: * ;
74use rustc_middle:: mir:: * ;
8- use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
9- use rustc_mir_dataflow:: impls:: borrowed_locals;
5+ use rustc_middle:: ty:: TyCtxt ;
106
7+ use crate :: ssa:: SsaLocals ;
118use crate :: MirPass ;
129
1310/// Unify locals that copy each other.
@@ -38,123 +35,28 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
3835 let param_env = tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) ;
3936 let ssa = SsaLocals :: new ( tcx, param_env, body) ;
4037
41- let ( copy_classes , fully_moved) = compute_copy_classes ( & ssa, body) ;
42- debug ! ( ?copy_classes ) ;
38+ let fully_moved = fully_moved_locals ( & ssa, body) ;
39+ debug ! ( ?fully_moved ) ;
4340
4441 let mut storage_to_remove = BitSet :: new_empty ( fully_moved. domain_size ( ) ) ;
45- for ( local, & head) in copy_classes. iter_enumerated ( ) {
42+ for ( local, & head) in ssa . copy_classes ( ) . iter_enumerated ( ) {
4643 if local != head {
4744 storage_to_remove. insert ( head) ;
4845 storage_to_remove. insert ( local) ;
4946 }
5047 }
5148
52- let any_replacement = copy_classes. iter_enumerated ( ) . any ( |( l, & h) | l != h) ;
49+ let any_replacement = ssa . copy_classes ( ) . iter_enumerated ( ) . any ( |( l, & h) | l != h) ;
5350
54- Replacer { tcx, copy_classes, fully_moved, storage_to_remove } . visit_body_preserves_cfg ( body) ;
51+ Replacer { tcx, copy_classes : & ssa. copy_classes ( ) , fully_moved, storage_to_remove }
52+ . visit_body_preserves_cfg ( body) ;
5553
5654 if any_replacement {
5755 crate :: simplify:: remove_unused_definitions ( body) ;
5856 }
5957}
6058
61- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
62- enum LocationExtended {
63- Plain ( Location ) ,
64- Arg ,
65- }
66-
67- #[ derive( Debug ) ]
68- struct SsaLocals {
69- dominators : Dominators < BasicBlock > ,
70- /// Assignments to each local. This defines whether the local is SSA.
71- assignments : IndexVec < Local , Set1 < LocationExtended > > ,
72- /// We visit the body in reverse postorder, to ensure each local is assigned before it is used.
73- /// We remember the order in which we saw the assignments to compute the SSA values in a single
74- /// pass.
75- assignment_order : Vec < Local > ,
76- }
77-
78- impl SsaLocals {
79- fn new < ' tcx > ( tcx : TyCtxt < ' tcx > , param_env : ParamEnv < ' tcx > , body : & Body < ' tcx > ) -> SsaLocals {
80- let assignment_order = Vec :: new ( ) ;
81-
82- let assignments = IndexVec :: from_elem ( Set1 :: Empty , & body. local_decls ) ;
83- let dominators = body. basic_blocks . dominators ( ) ;
84- let mut this = SsaLocals { assignments, assignment_order, dominators } ;
85-
86- let borrowed = borrowed_locals ( body) ;
87- for ( local, decl) in body. local_decls . iter_enumerated ( ) {
88- if matches ! ( body. local_kind( local) , LocalKind :: Arg ) {
89- this. assignments [ local] = Set1 :: One ( LocationExtended :: Arg ) ;
90- }
91- if borrowed. contains ( local) && !decl. ty . is_freeze ( tcx, param_env) {
92- this. assignments [ local] = Set1 :: Many ;
93- }
94- }
95-
96- for ( bb, data) in traversal:: reverse_postorder ( body) {
97- this. visit_basic_block_data ( bb, data) ;
98- }
99-
100- for var_debug_info in & body. var_debug_info {
101- this. visit_var_debug_info ( var_debug_info) ;
102- }
103-
104- debug ! ( ?this. assignments) ;
105-
106- this. assignment_order . retain ( |& local| matches ! ( this. assignments[ local] , Set1 :: One ( _) ) ) ;
107- debug ! ( ?this. assignment_order) ;
108-
109- this
110- }
111- }
112-
113- impl < ' tcx > Visitor < ' tcx > for SsaLocals {
114- fn visit_local ( & mut self , local : Local , ctxt : PlaceContext , loc : Location ) {
115- match ctxt {
116- PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) => {
117- self . assignments [ local] . insert ( LocationExtended :: Plain ( loc) ) ;
118- self . assignment_order . push ( local) ;
119- }
120- // Anything can happen with raw pointers, so remove them.
121- PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: AddressOf )
122- | PlaceContext :: MutatingUse ( _) => self . assignments [ local] = Set1 :: Many ,
123- // Immutable borrows are taken into account in `SsaLocals::new` by
124- // removing non-freeze locals.
125- PlaceContext :: NonMutatingUse ( _) => {
126- let set = & mut self . assignments [ local] ;
127- let assign_dominates = match * set {
128- Set1 :: Empty | Set1 :: Many => false ,
129- Set1 :: One ( LocationExtended :: Arg ) => true ,
130- Set1 :: One ( LocationExtended :: Plain ( assign) ) => {
131- assign. dominates ( loc, & self . dominators )
132- }
133- } ;
134- // We are visiting a use that is not dominated by an assignment.
135- // Either there is a cycle involved, or we are reading for uninitialized local.
136- // Bail out.
137- if !assign_dominates {
138- * set = Set1 :: Many ;
139- }
140- }
141- PlaceContext :: NonUse ( _) => { }
142- }
143- }
144- }
145-
146- /// Compute the equivalence classes for locals, based on copy statements.
147- ///
148- /// The returned vector maps each local to the one it copies. In the following case:
149- /// _a = &mut _0
150- /// _b = move? _a
151- /// _c = move? _a
152- /// _d = move? _c
153- /// We return the mapping
154- /// _a => _a // not a copy so, represented by itself
155- /// _b => _a
156- /// _c => _a
157- /// _d => _a // transitively through _c
59+ /// `SsaLocals` computed equivalence classes between locals considering copy/move assignments.
15860///
15961/// This function also returns whether all the `move?` in the pattern are `move` and not copies.
16062/// A local which is in the bitset can be replaced by `move _a`. Otherwise, it must be
@@ -164,95 +66,38 @@ impl<'tcx> Visitor<'tcx> for SsaLocals {
16466/// This means that replacing it by a copy of `_a` if ok, since this copy happens before `_c` is
16567/// moved, and therefore that `_d` is moved.
16668#[ instrument( level = "trace" , skip( ssa, body) ) ]
167- fn compute_copy_classes (
168- ssa : & SsaLocals ,
169- body : & Body < ' _ > ,
170- ) -> ( IndexVec < Local , Local > , BitSet < Local > ) {
171- let mut copies = IndexVec :: from_fn_n ( |l| l, body. local_decls . len ( ) ) ;
172- let mut fully_moved = BitSet :: new_filled ( copies. len ( ) ) ;
173-
174- for & local in & ssa. assignment_order {
175- debug ! ( ?local) ;
176-
177- if local == RETURN_PLACE {
178- // `_0` is special, we cannot rename it.
179- continue ;
180- }
181-
182- // This is not SSA: mark that we don't know the value.
183- debug ! ( assignments = ?ssa. assignments[ local] ) ;
184- let Set1 :: One ( LocationExtended :: Plain ( loc) ) = ssa. assignments [ local] else { continue } ;
185-
186- // `loc` must point to a direct assignment to `local`.
187- let Either :: Left ( stmt) = body. stmt_at ( loc) else { bug ! ( ) } ;
188- let Some ( ( _target, rvalue) ) = stmt. kind . as_assign ( ) else { bug ! ( ) } ;
189- assert_eq ! ( _target. as_local( ) , Some ( local) ) ;
69+ fn fully_moved_locals ( ssa : & SsaLocals , body : & Body < ' _ > ) -> BitSet < Local > {
70+ let mut fully_moved = BitSet :: new_filled ( body. local_decls . len ( ) ) ;
19071
72+ for ( _, rvalue) in ssa. assignments ( body) {
19173 let ( Rvalue :: Use ( Operand :: Copy ( place) | Operand :: Move ( place) ) | Rvalue :: CopyForDeref ( place) )
19274 = rvalue
19375 else { continue } ;
19476
19577 let Some ( rhs) = place. as_local ( ) else { continue } ;
196- let Set1 :: One ( _) = ssa. assignments [ rhs] else { continue } ;
197-
198- // We visit in `assignment_order`, ie. reverse post-order, so `rhs` has been
199- // visited before `local`, and we just have to copy the representing local.
200- copies[ local] = copies[ rhs] ;
78+ if !ssa. is_ssa ( rhs) {
79+ continue ;
80+ }
20181
20282 if let Rvalue :: Use ( Operand :: Copy ( _) ) | Rvalue :: CopyForDeref ( _) = rvalue {
20383 fully_moved. remove ( rhs) ;
20484 }
20585 }
20686
207- debug ! ( ?copies ) ;
87+ ssa . meet_copy_equivalence ( & mut fully_moved ) ;
20888
209- // Invariant: `copies` must point to the head of an equivalence class.
210- #[ cfg( debug_assertions) ]
211- for & head in copies. iter ( ) {
212- assert_eq ! ( copies[ head] , head) ;
213- }
214-
215- meet_copy_equivalence ( & copies, & mut fully_moved) ;
216-
217- ( copies, fully_moved)
218- }
219-
220- /// Make a property uniform on a copy equivalence class by removing elements.
221- fn meet_copy_equivalence ( copies : & IndexVec < Local , Local > , property : & mut BitSet < Local > ) {
222- // Consolidate to have a local iff all its copies are.
223- //
224- // `copies` defines equivalence classes between locals. The `local`s that recursively
225- // move/copy the same local all have the same `head`.
226- for ( local, & head) in copies. iter_enumerated ( ) {
227- // If any copy does not have `property`, then the head is not.
228- if !property. contains ( local) {
229- property. remove ( head) ;
230- }
231- }
232- for ( local, & head) in copies. iter_enumerated ( ) {
233- // If any copy does not have `property`, then the head doesn't either,
234- // then no copy has `property`.
235- if !property. contains ( head) {
236- property. remove ( local) ;
237- }
238- }
239-
240- // Verify that we correctly computed equivalence classes.
241- #[ cfg( debug_assertions) ]
242- for ( local, & head) in copies. iter_enumerated ( ) {
243- assert_eq ! ( property. contains( local) , property. contains( head) ) ;
244- }
89+ fully_moved
24590}
24691
24792/// Utility to help performing subtitution of `*pattern` by `target`.
248- struct Replacer < ' tcx > {
93+ struct Replacer < ' a , ' tcx > {
24994 tcx : TyCtxt < ' tcx > ,
25095 fully_moved : BitSet < Local > ,
25196 storage_to_remove : BitSet < Local > ,
252- copy_classes : IndexVec < Local , Local > ,
97+ copy_classes : & ' a IndexVec < Local , Local > ,
25398}
25499
255- impl < ' tcx > MutVisitor < ' tcx > for Replacer < ' tcx > {
100+ impl < ' tcx > MutVisitor < ' tcx > for Replacer < ' _ , ' tcx > {
256101 fn tcx ( & self ) -> TyCtxt < ' tcx > {
257102 self . tcx
258103 }
0 commit comments