@@ -49,12 +49,13 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
4949
5050 let any_replacement = ssa. copy_classes ( ) . iter_enumerated ( ) . any ( |( l, & h) | l != h) ;
5151
52- let storage_to_remove = if any_replacement {
53- // The replacer will remove tautological moves from the head to the local,
54- // so we remove them before running `MaybeUninitializedPlaces`.
55- TautologicalMoveAssignmentRemover {
52+ if any_replacement {
53+ let storage_to_remove = DenseBitSet :: new_empty ( fully_moved . domain_size ( ) ) ;
54+
55+ Replacer {
5656 tcx,
5757 copy_classes : ssa. copy_classes ( ) ,
58+ fully_moved,
5859 borrowed_locals : ssa. borrowed_locals ( ) ,
5960 }
6061 . visit_body_preserves_cfg ( body) ;
@@ -65,29 +66,18 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
6566 . iterate_to_fixpoint ( tcx, body, Some ( "mir_opt::copy_prop" ) )
6667 . into_results_cursor ( body) ;
6768
68- let mut storage_checker = StorageChecker {
69- copy_classes : ssa. copy_classes ( ) ,
70- maybe_uninit,
71- head_storage_to_check,
72- storage_to_remove : DenseBitSet :: new_empty ( fully_moved. domain_size ( ) ) ,
73- } ;
69+ debug ! ( ?head_storage_to_check) ;
7470
75- storage_checker. visit_body ( body) ;
71+ let storage_to_remove = {
72+ let mut storage_checker =
73+ StorageChecker { maybe_uninit, head_storage_to_check, storage_to_remove } ;
7674
77- storage_checker. storage_to_remove
78- } else {
79- // Will be empty anyway.
80- head_storage_to_check
81- } ;
75+ storage_checker. visit_body ( body) ;
8276
83- Replacer {
84- tcx,
85- copy_classes : ssa. copy_classes ( ) ,
86- fully_moved,
87- borrowed_locals : ssa. borrowed_locals ( ) ,
88- storage_to_remove,
77+ storage_checker. storage_to_remove
78+ } ;
79+ StorageRemover { tcx, storage_to_remove } . visit_body_preserves_cfg ( body) ;
8980 }
90- . visit_body_preserves_cfg ( body) ;
9181
9282 if any_replacement {
9383 crate :: simplify:: remove_unused_definitions ( body) ;
@@ -134,46 +124,10 @@ fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> DenseBitSet<Local> {
134124 fully_moved
135125}
136126
137- // We don't want to consider tautological moves in `MaybeUninitializedPlaces`, so we remove them before running it.
138- struct TautologicalMoveAssignmentRemover < ' a , ' tcx > {
139- tcx : TyCtxt < ' tcx > ,
140- copy_classes : & ' a IndexSlice < Local , Local > ,
141- borrowed_locals : & ' a DenseBitSet < Local > ,
142- }
143-
144- impl < ' tcx > MutVisitor < ' tcx > for TautologicalMoveAssignmentRemover < ' _ , ' tcx > {
145- fn tcx ( & self ) -> TyCtxt < ' tcx > {
146- self . tcx
147- }
148-
149- fn visit_statement ( & mut self , stmt : & mut Statement < ' tcx > , loc : Location ) {
150- self . super_statement ( stmt, loc) ;
151-
152- // Similar to `Replacer::visit_statement`, but only handle moves.
153- if let StatementKind :: Assign ( box ( lhs, ref rhs) ) = stmt. kind
154- && let Rvalue :: Use ( Operand :: Move ( rhs) ) = * rhs
155- {
156- if self . borrowed_locals . contains ( lhs. local ) || self . borrowed_locals . contains ( rhs. local )
157- {
158- return ;
159- }
160-
161- let new_lhs_local = self . copy_classes [ lhs. local ] ;
162- let new_rhs_local = self . copy_classes [ rhs. local ] ;
163-
164- if new_lhs_local == new_rhs_local && lhs. projection == rhs. projection {
165- debug ! ( ?loc, ?lhs, ?rhs, "removing a to-be-tautological assignment" ) ;
166- stmt. make_nop ( ) ;
167- }
168- }
169- }
170- }
171-
172127/// Utility to help performing substitution of `*pattern` by `target`.
173128struct Replacer < ' a , ' tcx > {
174129 tcx : TyCtxt < ' tcx > ,
175130 fully_moved : DenseBitSet < Local > ,
176- storage_to_remove : DenseBitSet < Local > ,
177131 borrowed_locals : & ' a DenseBitSet < Local > ,
178132 copy_classes : & ' a IndexSlice < Local , Local > ,
179133}
@@ -225,14 +179,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
225179 }
226180
227181 fn visit_statement ( & mut self , stmt : & mut Statement < ' tcx > , loc : Location ) {
228- // When removing storage statements, we need to remove both (#107511).
229- if let StatementKind :: StorageLive ( l) | StatementKind :: StorageDead ( l) = stmt. kind
230- && self . storage_to_remove . contains ( l)
231- {
232- stmt. make_nop ( ) ;
233- return ;
234- }
235-
236182 self . super_statement ( stmt, loc) ;
237183
238184 // Do not leave tautological assignments around.
@@ -241,6 +187,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
241187 * rhs
242188 && lhs == rhs
243189 {
190+ debug ! ( ?stmt, ?loc, ?lhs, ?rhs, "removing tautological assignment" ) ;
244191 stmt. make_nop ( ) ;
245192 }
246193 }
@@ -250,42 +197,63 @@ struct StorageChecker<'a, 'tcx> {
250197 storage_to_remove : DenseBitSet < Local > ,
251198 head_storage_to_check : DenseBitSet < Local > ,
252199 maybe_uninit : ResultsCursor < ' a , ' tcx , MaybeUninitializedPlaces < ' a , ' tcx > > ,
253- copy_classes : & ' a IndexSlice < Local , Local > ,
254200}
255201
256202impl < ' a , ' tcx > Visitor < ' tcx > for StorageChecker < ' a , ' tcx > {
257203 fn visit_local ( & mut self , local : Local , context : PlaceContext , loc : Location ) {
258- if !context. is_use ( ) {
259- return ;
204+ // We don't need to check storage statements and stores (for which the local doesn't need to be initialized).
205+ match context {
206+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) | PlaceContext :: NonUse ( _) => {
207+ return ;
208+ }
209+ _ => { }
210+ } ;
211+
212+ // The head must be initialized at the location of the local, otherwise we must remove it's storage statements.
213+ if self . head_storage_to_check . contains ( local) {
214+ if let Some ( move_idx) =
215+ self . maybe_uninit . analysis ( ) . move_data ( ) . rev_lookup . find_local ( local)
216+ {
217+ self . maybe_uninit . seek_before_primary_effect ( loc) ;
218+
219+ if self . maybe_uninit . get ( ) . contains ( move_idx) {
220+ debug ! (
221+ ?loc,
222+ ?context,
223+ ?local,
224+ ?move_idx,
225+ "found a head at a location in which it is maybe uninit, marking head for storage statement removal"
226+ ) ;
227+ self . storage_to_remove . insert ( local) ;
228+
229+ // Once we found a use of the head that is maybe uninit, we do not need to check it again.
230+ self . head_storage_to_check . remove ( local) ;
231+ }
232+ }
260233 }
234+ }
235+ }
236+
237+ /// Utility to help performing substitution of `*pattern` by `target`.
238+ struct StorageRemover < ' tcx > {
239+ tcx : TyCtxt < ' tcx > ,
240+ storage_to_remove : DenseBitSet < Local > ,
241+ }
261242
262- let head = self . copy_classes [ local] ;
243+ impl < ' tcx > MutVisitor < ' tcx > for StorageRemover < ' tcx > {
244+ fn tcx ( & self ) -> TyCtxt < ' tcx > {
245+ self . tcx
246+ }
263247
264- if local == head {
265- // Every original use of the head is known to be initialized, so we don't need to check it.
248+ fn visit_statement ( & mut self , stmt : & mut Statement < ' tcx > , loc : Location ) {
249+ // When removing storage statements, we need to remove both (#107511).
250+ if let StatementKind :: StorageLive ( l) | StatementKind :: StorageDead ( l) = stmt. kind
251+ && self . storage_to_remove . contains ( l)
252+ {
253+ stmt. make_nop ( ) ;
266254 return ;
267255 }
268256
269- // The head must be initialized at the location of the local, otherwise we must remove it's storage statements.
270- if self . head_storage_to_check . contains ( head)
271- && let Some ( move_idx) =
272- self . maybe_uninit . analysis ( ) . move_data ( ) . rev_lookup . find_local ( head)
273- {
274- self . maybe_uninit . seek_after_primary_effect ( loc) ;
275-
276- if self . maybe_uninit . get ( ) . contains ( move_idx) {
277- debug ! (
278- ?loc,
279- ?local,
280- ?head,
281- ?move_idx,
282- "found use of local with head at a location in which it is maybe uninit, marking head for storage statement removal"
283- ) ;
284- self . storage_to_remove . insert ( head) ;
285-
286- // Once we found a use of the head that is maybe uninit, we do not need to check it again.
287- self . head_storage_to_check . remove ( head) ;
288- }
289- }
257+ self . super_statement ( stmt, loc) ;
290258 }
291259}
0 commit comments