@@ -16,7 +16,7 @@ use crate::ssa::SsaLocals;
1616/// _d = move? _c
1717/// where each of the locals is only assigned once.
1818///
19- /// We want to replace all those locals by `_a`, either copied or moved .
19+ /// We want to replace all those locals by `copy _a`.
2020pub ( super ) struct CopyProp ;
2121
2222impl < ' tcx > crate :: MirPass < ' tcx > for CopyProp {
@@ -34,23 +34,21 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
3434 debug ! ( copy_classes = ?ssa. copy_classes( ) ) ;
3535
3636 let mut any_replacement = false ;
37- let mut storage_to_remove = DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ;
37+ // Locals that participate in copy propagation either as a source or a destination.
38+ let mut unified = DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ;
3839 for ( local, & head) in ssa. copy_classes ( ) . iter_enumerated ( ) {
3940 if local != head {
4041 any_replacement = true ;
41- storage_to_remove. insert ( head) ;
42+ unified. insert ( head) ;
43+ unified. insert ( local) ;
4244 }
4345 }
4446
4547 if !any_replacement {
4648 return ;
4749 }
4850
49- let fully_moved = fully_moved_locals ( & ssa, body) ;
50- debug ! ( ?fully_moved) ;
51-
52- Replacer { tcx, copy_classes : ssa. copy_classes ( ) , fully_moved, storage_to_remove }
53- . visit_body_preserves_cfg ( body) ;
51+ Replacer { tcx, copy_classes : ssa. copy_classes ( ) , unified } . visit_body_preserves_cfg ( body) ;
5452
5553 crate :: simplify:: remove_unused_definitions ( body) ;
5654 }
@@ -60,44 +58,10 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
6058 }
6159}
6260
63- /// `SsaLocals` computed equivalence classes between locals considering copy/move assignments.
64- ///
65- /// This function also returns whether all the `move?` in the pattern are `move` and not copies.
66- /// A local which is in the bitset can be replaced by `move _a`. Otherwise, it must be
67- /// replaced by `copy _a`, as we cannot move multiple times from `_a`.
68- ///
69- /// If an operand copies `_c`, it must happen before the assignment `_d = _c`, otherwise it is UB.
70- /// This means that replacing it by a copy of `_a` if ok, since this copy happens before `_c` is
71- /// moved, and therefore that `_d` is moved.
72- #[ instrument( level = "trace" , skip( ssa, body) ) ]
73- fn fully_moved_locals ( ssa : & SsaLocals , body : & Body < ' _ > ) -> DenseBitSet < Local > {
74- let mut fully_moved = DenseBitSet :: new_filled ( body. local_decls . len ( ) ) ;
75-
76- for ( _, rvalue, _) in ssa. assignments ( body) {
77- let Rvalue :: Use ( Operand :: Copy ( place) | Operand :: Move ( place) ) = rvalue else {
78- continue ;
79- } ;
80-
81- let Some ( rhs) = place. as_local ( ) else { continue } ;
82- if !ssa. is_ssa ( rhs) {
83- continue ;
84- }
85-
86- if let Rvalue :: Use ( Operand :: Copy ( _) ) = rvalue {
87- fully_moved. remove ( rhs) ;
88- }
89- }
90-
91- ssa. meet_copy_equivalence ( & mut fully_moved) ;
92-
93- fully_moved
94- }
95-
9661/// Utility to help performing substitution of `*pattern` by `target`.
9762struct Replacer < ' a , ' tcx > {
9863 tcx : TyCtxt < ' tcx > ,
99- fully_moved : DenseBitSet < Local > ,
100- storage_to_remove : DenseBitSet < Local > ,
64+ unified : DenseBitSet < Local > ,
10165 copy_classes : & ' a IndexSlice < Local , Local > ,
10266}
10367
@@ -108,13 +72,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
10872
10973 #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
11074 fn visit_local ( & mut self , local : & mut Local , ctxt : PlaceContext , _: Location ) {
111- let new_local = self . copy_classes [ * local] ;
112- match ctxt {
113- // Do not modify the local in storage statements.
114- PlaceContext :: NonUse ( NonUseContext :: StorageLive | NonUseContext :: StorageDead ) => { }
115- // We access the value.
116- _ => * local = new_local,
117- }
75+ * local = self . copy_classes [ * local] ;
11876 }
11977
12078 #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
@@ -123,7 +81,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
12381 // A move out of a projection of a copy is equivalent to a copy of the original
12482 // projection.
12583 && !place. is_indirect_first_projection ( )
126- && ! self . fully_moved . contains ( place. local )
84+ && self . unified . contains ( place. local )
12785 {
12886 * operand = Operand :: Copy ( place) ;
12987 }
@@ -134,10 +92,9 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
13492 fn visit_statement ( & mut self , stmt : & mut Statement < ' tcx > , loc : Location ) {
13593 // When removing storage statements, we need to remove both (#107511).
13694 if let StatementKind :: StorageLive ( l) | StatementKind :: StorageDead ( l) = stmt. kind
137- && self . storage_to_remove . contains ( l)
95+ && self . unified . contains ( l)
13896 {
13997 stmt. make_nop ( true ) ;
140- return ;
14198 }
14299
143100 self . super_statement ( stmt, loc) ;
0 commit comments