@@ -3,6 +3,7 @@ use rustc_index::vec::IndexVec;
33use rustc_middle:: mir:: visit:: * ;
44use rustc_middle:: mir:: * ;
55use rustc_middle:: ty:: TyCtxt ;
6+ use rustc_mir_dataflow:: impls:: borrowed_locals;
67
78use crate :: ssa:: SsaLocals ;
89use crate :: MirPass ;
@@ -33,7 +34,8 @@ impl<'tcx> MirPass<'tcx> for CopyProp {
3334
3435fn propagate_ssa < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
3536 let param_env = tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) ;
36- let ssa = SsaLocals :: new ( tcx, param_env, body) ;
37+ let borrowed_locals = borrowed_locals ( body) ;
38+ let ssa = SsaLocals :: new ( tcx, param_env, body, & borrowed_locals) ;
3739
3840 let fully_moved = fully_moved_locals ( & ssa, body) ;
3941 debug ! ( ?fully_moved) ;
@@ -42,14 +44,19 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
4244 for ( local, & head) in ssa. copy_classes ( ) . iter_enumerated ( ) {
4345 if local != head {
4446 storage_to_remove. insert ( head) ;
45- storage_to_remove. insert ( local) ;
4647 }
4748 }
4849
4950 let any_replacement = ssa. copy_classes ( ) . iter_enumerated ( ) . any ( |( l, & h) | l != h) ;
5051
51- Replacer { tcx, copy_classes : & ssa. copy_classes ( ) , fully_moved, storage_to_remove }
52- . visit_body_preserves_cfg ( body) ;
52+ Replacer {
53+ tcx,
54+ copy_classes : & ssa. copy_classes ( ) ,
55+ fully_moved,
56+ borrowed_locals,
57+ storage_to_remove,
58+ }
59+ . visit_body_preserves_cfg ( body) ;
5360
5461 if any_replacement {
5562 crate :: simplify:: remove_unused_definitions ( body) ;
@@ -94,6 +101,7 @@ struct Replacer<'a, 'tcx> {
94101 tcx : TyCtxt < ' tcx > ,
95102 fully_moved : BitSet < Local > ,
96103 storage_to_remove : BitSet < Local > ,
104+ borrowed_locals : BitSet < Local > ,
97105 copy_classes : & ' a IndexVec < Local , Local > ,
98106}
99107
@@ -102,8 +110,45 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
102110 self . tcx
103111 }
104112
105- fn visit_local ( & mut self , local : & mut Local , _: PlaceContext , _: Location ) {
106- * local = self . copy_classes [ * local] ;
113+ fn visit_local ( & mut self , local : & mut Local , ctxt : PlaceContext , _: Location ) {
114+ let new_local = self . copy_classes [ * local] ;
115+ match ctxt {
116+ // Do not modify the local in storage statements.
117+ PlaceContext :: NonUse ( NonUseContext :: StorageLive | NonUseContext :: StorageDead ) => { }
118+ // The local should have been marked as non-SSA.
119+ PlaceContext :: MutatingUse ( _) => assert_eq ! ( * local, new_local) ,
120+ // We access the value.
121+ _ => * local = new_local,
122+ }
123+ }
124+
125+ fn visit_place ( & mut self , place : & mut Place < ' tcx > , ctxt : PlaceContext , loc : Location ) {
126+ if let Some ( new_projection) = self . process_projection ( & place. projection , loc) {
127+ place. projection = self . tcx ( ) . intern_place_elems ( & new_projection) ;
128+ }
129+
130+ let observes_address = match ctxt {
131+ PlaceContext :: NonMutatingUse (
132+ NonMutatingUseContext :: SharedBorrow
133+ | NonMutatingUseContext :: ShallowBorrow
134+ | NonMutatingUseContext :: UniqueBorrow
135+ | NonMutatingUseContext :: AddressOf ,
136+ ) => true ,
137+ // For debuginfo, merging locals is ok.
138+ PlaceContext :: NonUse ( NonUseContext :: VarDebugInfo ) => {
139+ self . borrowed_locals . contains ( place. local )
140+ }
141+ _ => false ,
142+ } ;
143+ if observes_address && !place. is_indirect ( ) {
144+ // We observe the address of `place.local`. Do not replace it.
145+ } else {
146+ self . visit_local (
147+ & mut place. local ,
148+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ,
149+ loc,
150+ )
151+ }
107152 }
108153
109154 fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , loc : Location ) {
@@ -117,17 +162,17 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
117162 }
118163
119164 fn visit_statement ( & mut self , stmt : & mut Statement < ' tcx > , loc : Location ) {
120- if let StatementKind :: StorageLive ( l ) | StatementKind :: StorageDead ( l) = stmt. kind
165+ if let StatementKind :: StorageDead ( l) = stmt. kind
121166 && self . storage_to_remove . contains ( l)
122167 {
123168 stmt. make_nop ( ) ;
124- }
125- if let StatementKind :: Assign ( box ( ref place, _) ) = stmt. kind
126- && let Some ( l) = place. as_local ( )
127- && self . copy_classes [ l] != l
169+ } else if let StatementKind :: Assign ( box ( ref place, ref mut rvalue) ) = stmt. kind
170+ && place. as_local ( ) . is_some ( )
128171 {
129- stmt. make_nop ( ) ;
172+ // Do not replace assignments.
173+ self . visit_rvalue ( rvalue, loc)
174+ } else {
175+ self . super_statement ( stmt, loc) ;
130176 }
131- self . super_statement ( stmt, loc) ;
132177 }
133178}
0 commit comments