@@ -190,29 +190,51 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
190190 for ( src, candidates) in candidates. c . into_iter ( ) {
191191 trace ! ( ?src, ?candidates) ;
192192
193- let Some ( src) = relevant. find ( src) else { continue } ;
194- let Some ( src_live_ranges) = & live. row ( src) else { continue } ;
195- trace ! ( ?src, ?src_live_ranges) ;
196-
197- let dst = candidates. into_iter ( ) . find_map ( |dst| {
198- let dst = relevant. find ( dst) ?;
199- let dst_live_ranges = & live. row ( dst) ?;
193+ for dst in candidates {
194+ // We call `relevant.find(src)` inside the loop, as a previous iteration may have
195+ // renamed `src` to one of the locals in `dst`.
196+ let Some ( mut src) = relevant. find ( src) else { continue } ;
197+ let Some ( src_live_ranges) = live. row ( src) else { continue } ;
198+ trace ! ( ?src, ?src_live_ranges) ;
199+
200+ let Some ( mut dst) = relevant. find ( dst) else { continue } ;
201+ let Some ( dst_live_ranges) = live. row ( dst) else { continue } ;
200202 trace ! ( ?dst, ?dst_live_ranges) ;
201203
202- let disjoint = src_live_ranges. disjoint ( dst_live_ranges) ;
203- disjoint. then_some ( dst)
204- } ) ;
205- let Some ( dst) = dst else { continue } ;
204+ if src_live_ranges. disjoint ( dst_live_ranges) {
205+ // We want to replace `src` by `dst`.
206+ let mut orig_src = relevant. original [ src] ;
207+ let mut orig_dst = relevant. original [ dst] ;
208+
209+ // The return place and function arguments are required and cannot be renamed.
210+ // This check cannot be made during candidate collection, as we may want to
211+ // unify the same non-required local with several required locals.
212+ match ( is_local_required ( orig_src, body) , is_local_required ( orig_dst, body) ) {
213+ // Renaming `src` is ok.
214+ ( false , _) => { }
215+ // Renaming `src` is wrong, but renaming `dst` is ok.
216+ ( true , false ) => {
217+ std:: mem:: swap ( & mut src, & mut dst) ;
218+ std:: mem:: swap ( & mut orig_src, & mut orig_dst) ;
219+ }
220+ // Neither local can be renamed, so skip this case.
221+ ( true , true ) => continue ,
222+ }
206223
207- merged_locals. insert ( relevant. original [ src] ) ;
208- merged_locals. insert ( relevant. original [ dst] ) ;
224+ trace ! ( ?src, ?dst, "merge" ) ;
225+ merged_locals. insert ( orig_src) ;
226+ merged_locals. insert ( orig_dst) ;
209227
210- relevant. union ( src, dst) ;
211- live. union_rows ( src, dst) ;
228+ // Replace `src` by `dst`.
229+ relevant. union ( src, dst) ;
230+ live. union_rows ( /* read */ src, /* write */ dst) ;
231+ }
232+ }
212233 }
213234 trace ! ( ?merged_locals) ;
214235
215236 relevant. make_idempotent ( ) ;
237+ trace ! ( ?relevant. renames) ;
216238
217239 if merged_locals. is_empty ( ) {
218240 return ;
@@ -402,41 +424,6 @@ impl Candidates {
402424 }
403425}
404426
405- /// If the pair of places is being considered for merging, returns the candidate which would be
406- /// merged in order to accomplish this.
407- ///
408- /// The contract here is in one direction - there is a guarantee that merging the locals that are
409- /// outputted by this function would result in an assignment between the inputs becoming a
410- /// self-assignment. However, there is no guarantee that the returned pair is actually suitable for
411- /// merging - candidate collection must still check this independently.
412- ///
413- /// This output is unique for each unordered pair of input places.
414- fn places_to_candidate_pair < ' tcx > (
415- a : Place < ' tcx > ,
416- b : Place < ' tcx > ,
417- body : & Body < ' tcx > ,
418- ) -> Option < ( Local , Local ) > {
419- let ( mut a, mut b) = if a. projection . len ( ) == 0 && b. projection . len ( ) == 0 {
420- ( a. local , b. local )
421- } else {
422- return None ;
423- } ;
424-
425- // By sorting, we make sure we're input order independent
426- if a > b {
427- std:: mem:: swap ( & mut a, & mut b) ;
428- }
429-
430- // We could now return `(a, b)`, but then we miss some candidates in the case where `a` can't be
431- // used as a `src`.
432- if is_local_required ( a, body) {
433- std:: mem:: swap ( & mut a, & mut b) ;
434- }
435- // We could check `is_local_required` again here, but there's no need - after all, we make no
436- // promise that the candidate pair is actually valid
437- Some ( ( a, b) )
438- }
439-
440427struct FindAssignments < ' a , ' tcx > {
441428 body : & ' a Body < ' tcx > ,
442429 candidates : FxIndexMap < Local , Vec < Local > > ,
@@ -449,11 +436,9 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> {
449436 lhs,
450437 Rvalue :: CopyForDeref ( rhs) | Rvalue :: Use ( Operand :: Copy ( rhs) | Operand :: Move ( rhs) ) ,
451438 ) ) = & statement. kind
439+ && let Some ( src) = lhs. as_local ( )
440+ && let Some ( dest) = rhs. as_local ( )
452441 {
453- let Some ( ( src, dest) ) = places_to_candidate_pair ( * lhs, * rhs, self . body ) else {
454- return ;
455- } ;
456-
457442 // As described at the top of the file, we do not go near things that have
458443 // their address taken.
459444 if self . borrowed . contains ( src) || self . borrowed . contains ( dest) {
@@ -470,11 +455,6 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> {
470455 return ;
471456 }
472457
473- // Also, we need to make sure that MIR actually allows the `src` to be removed
474- if is_local_required ( src, self . body ) {
475- return ;
476- }
477-
478458 // We may insert duplicates here, but that's fine
479459 self . candidates . entry ( src) . or_default ( ) . push ( dest) ;
480460 }
0 commit comments