@@ -182,29 +182,51 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
182182 for ( src, candidates) in candidates. c . into_iter ( ) {
183183 trace ! ( ?src, ?candidates) ;
184184
185- let Some ( src) = relevant. find ( src) else { continue } ;
186- let Some ( src_live_ranges) = & live. row ( src) else { continue } ;
187- trace ! ( ?src, ?src_live_ranges) ;
188-
189- let dst = candidates. into_iter ( ) . find_map ( |dst| {
190- let dst = relevant. find ( dst) ?;
191- let dst_live_ranges = & live. row ( dst) ?;
185+ for dst in candidates {
186+ // We call `relevant.find(src)` inside the loop, as a previous iteration may have
187+ // renamed `src` to one of the locals in `dst`.
188+ let Some ( mut src) = relevant. find ( src) else { continue } ;
189+ let Some ( src_live_ranges) = live. row ( src) else { continue } ;
190+ trace ! ( ?src, ?src_live_ranges) ;
191+
192+ let Some ( mut dst) = relevant. find ( dst) else { continue } ;
193+ let Some ( dst_live_ranges) = live. row ( dst) else { continue } ;
192194 trace ! ( ?dst, ?dst_live_ranges) ;
193195
194- let disjoint = src_live_ranges. disjoint ( dst_live_ranges) ;
195- disjoint. then_some ( dst)
196- } ) ;
197- let Some ( dst) = dst else { continue } ;
196+ if src_live_ranges. disjoint ( dst_live_ranges) {
197+ // We want to replace `src` by `dst`.
198+ let mut orig_src = relevant. original [ src] ;
199+ let mut orig_dst = relevant. original [ dst] ;
200+
201+ // The return place and function arguments are required and cannot be renamed.
202+ // This check cannot be made during candidate collection, as we may want to
203+ // unify the same non-required local with several required locals.
204+ match ( is_local_required ( orig_src, body) , is_local_required ( orig_dst, body) ) {
205+ // Renaming `src` is ok.
206+ ( false , _) => { }
207+ // Renaming `src` is wrong, but renaming `dst` is ok.
208+ ( true , false ) => {
209+ std:: mem:: swap ( & mut src, & mut dst) ;
210+ std:: mem:: swap ( & mut orig_src, & mut orig_dst) ;
211+ }
212+ // Neither local can be renamed, so skip this case.
213+ ( true , true ) => continue ,
214+ }
198215
199- merged_locals. insert ( relevant. original [ src] ) ;
200- merged_locals. insert ( relevant. original [ dst] ) ;
216+ trace ! ( ?src, ?dst, "merge" ) ;
217+ merged_locals. insert ( orig_src) ;
218+ merged_locals. insert ( orig_dst) ;
201219
202- relevant. union ( src, dst) ;
203- live. union_rows ( src, dst) ;
220+ // Replace `src` by `dst`.
221+ relevant. union ( src, dst) ;
222+ live. union_rows ( /* read */ src, /* write */ dst) ;
223+ }
224+ }
204225 }
205226 trace ! ( ?merged_locals) ;
206227
207228 relevant. make_idempotent ( ) ;
229+ trace ! ( ?relevant. renames) ;
208230
209231 if merged_locals. is_empty ( ) {
210232 return ;
@@ -394,41 +416,6 @@ impl Candidates {
394416 }
395417}
396418
397- /// If the pair of places is being considered for merging, returns the candidate which would be
398- /// merged in order to accomplish this.
399- ///
400- /// The contract here is in one direction - there is a guarantee that merging the locals that are
401- /// outputted by this function would result in an assignment between the inputs becoming a
402- /// self-assignment. However, there is no guarantee that the returned pair is actually suitable for
403- /// merging - candidate collection must still check this independently.
404- ///
405- /// This output is unique for each unordered pair of input places.
406- fn places_to_candidate_pair < ' tcx > (
407- a : Place < ' tcx > ,
408- b : Place < ' tcx > ,
409- body : & Body < ' tcx > ,
410- ) -> Option < ( Local , Local ) > {
411- let ( mut a, mut b) = if a. projection . len ( ) == 0 && b. projection . len ( ) == 0 {
412- ( a. local , b. local )
413- } else {
414- return None ;
415- } ;
416-
417- // By sorting, we make sure we're input order independent
418- if a > b {
419- std:: mem:: swap ( & mut a, & mut b) ;
420- }
421-
422- // We could now return `(a, b)`, but then we miss some candidates in the case where `a` can't be
423- // used as a `src`.
424- if is_local_required ( a, body) {
425- std:: mem:: swap ( & mut a, & mut b) ;
426- }
427- // We could check `is_local_required` again here, but there's no need - after all, we make no
428- // promise that the candidate pair is actually valid
429- Some ( ( a, b) )
430- }
431-
432419struct FindAssignments < ' a , ' tcx > {
433420 body : & ' a Body < ' tcx > ,
434421 candidates : FxIndexMap < Local , Vec < Local > > ,
@@ -441,11 +428,9 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> {
441428 lhs,
442429 Rvalue :: CopyForDeref ( rhs) | Rvalue :: Use ( Operand :: Copy ( rhs) | Operand :: Move ( rhs) ) ,
443430 ) ) = & statement. kind
431+ && let Some ( src) = lhs. as_local ( )
432+ && let Some ( dest) = rhs. as_local ( )
444433 {
445- let Some ( ( src, dest) ) = places_to_candidate_pair ( * lhs, * rhs, self . body ) else {
446- return ;
447- } ;
448-
449434 // As described at the top of the file, we do not go near things that have
450435 // their address taken.
451436 if self . borrowed . contains ( src) || self . borrowed . contains ( dest) {
@@ -462,11 +447,6 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> {
462447 return ;
463448 }
464449
465- // Also, we need to make sure that MIR actually allows the `src` to be removed
466- if is_local_required ( src, self . body ) {
467- return ;
468- }
469-
470450 // We may insert duplicates here, but that's fine
471451 self . candidates . entry ( src) . or_default ( ) . push ( dest) ;
472452 }
0 commit comments