@@ -3,6 +3,7 @@ use rustc_index::vec::IndexVec;
3
3
use rustc_middle:: mir:: visit:: * ;
4
4
use rustc_middle:: mir:: * ;
5
5
use rustc_middle:: ty:: TyCtxt ;
6
+ use rustc_mir_dataflow:: impls:: borrowed_locals;
6
7
7
8
use crate :: ssa:: SsaLocals ;
8
9
use crate :: MirPass ;
@@ -33,7 +34,8 @@ impl<'tcx> MirPass<'tcx> for CopyProp {
33
34
34
35
fn propagate_ssa < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
35
36
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) ;
37
39
38
40
let fully_moved = fully_moved_locals ( & ssa, body) ;
39
41
debug ! ( ?fully_moved) ;
@@ -42,14 +44,19 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
42
44
for ( local, & head) in ssa. copy_classes ( ) . iter_enumerated ( ) {
43
45
if local != head {
44
46
storage_to_remove. insert ( head) ;
45
- storage_to_remove. insert ( local) ;
46
47
}
47
48
}
48
49
49
50
let any_replacement = ssa. copy_classes ( ) . iter_enumerated ( ) . any ( |( l, & h) | l != h) ;
50
51
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) ;
53
60
54
61
if any_replacement {
55
62
crate :: simplify:: remove_unused_definitions ( body) ;
@@ -94,6 +101,7 @@ struct Replacer<'a, 'tcx> {
94
101
tcx : TyCtxt < ' tcx > ,
95
102
fully_moved : BitSet < Local > ,
96
103
storage_to_remove : BitSet < Local > ,
104
+ borrowed_locals : BitSet < Local > ,
97
105
copy_classes : & ' a IndexVec < Local , Local > ,
98
106
}
99
107
@@ -102,8 +110,45 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
102
110
self . tcx
103
111
}
104
112
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
+ }
107
152
}
108
153
109
154
fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , loc : Location ) {
@@ -117,17 +162,17 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
117
162
}
118
163
119
164
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
121
166
&& self . storage_to_remove . contains ( l)
122
167
{
123
168
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 ( )
128
171
{
129
- stmt. make_nop ( ) ;
172
+ // Do not replace assignments.
173
+ self . visit_rvalue ( rvalue, loc)
174
+ } else {
175
+ self . super_statement ( stmt, loc) ;
130
176
}
131
- self . super_statement ( stmt, loc) ;
132
177
}
133
178
}
0 commit comments