@@ -217,11 +217,12 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
217217 // This is the set of merges we will apply this round. It is a subset of the candidates.
218218 let mut merges = FxIndexMap :: default ( ) ;
219219
220- for ( src, candidates) in candidates. c . iter ( ) {
221- if merged_locals. contains ( * src) {
220+ for ( src, candidates) in candidates. c . drain ( .. ) {
221+ if merged_locals. contains ( src) {
222222 continue ;
223223 }
224- let Some ( dest) = candidates. iter ( ) . find ( |dest| !merged_locals. contains ( * * dest) )
224+ let Some ( dest) =
225+ candidates. into_iter ( ) . find ( |( dest, _) | !merged_locals. contains ( * dest) )
225226 else {
226227 continue ;
227228 } ;
@@ -231,14 +232,15 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
231232 break ;
232233 }
233234
234- // Replace `src` by `dest` everywhere.
235- merges. insert ( * src, * dest) ;
236- merged_locals. insert ( * src) ;
237- merged_locals. insert ( * dest) ;
238-
239235 // Update liveness information based on the merge we just performed.
240236 // Every location where `src` was live, `dest` will be live.
241- live. union_rows ( * src, * dest) ;
237+ live. union_rows ( src, dest. 0 ) ;
238+
239+ // Replace `src` by `dest` everywhere.
240+ merged_locals. insert ( src) ;
241+ merged_locals. insert ( dest. 0 ) ;
242+ merges. insert ( src, dest. clone ( ) ) ;
243+ merges. insert ( dest. 0 , dest) ;
242244 }
243245 trace ! ( merging = ?merges) ;
244246
@@ -260,7 +262,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
260262/// frequently. Everything with a `&'alloc` lifetime points into here.
261263#[ derive( Default ) ]
262264struct Allocations {
263- candidates : FxIndexMap < Local , Vec < Local > > ,
265+ candidates : FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
264266 candidates_reverse : FxIndexMap < Local , Vec < Local > > ,
265267 write_info : WriteInfo ,
266268 // PERF: Do this for `MaybeLiveLocals` allocations too.
@@ -282,7 +284,7 @@ struct Candidates<'alloc> {
282284 ///
283285 /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
284286 /// remove that assignment.
285- c : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
287+ c : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
286288 /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
287289 /// then this contains `b => a`.
288290 // PERF: Possibly these should be `SmallVec`s?
@@ -297,7 +299,7 @@ struct Candidates<'alloc> {
297299fn apply_merges < ' tcx > (
298300 body : & mut Body < ' tcx > ,
299301 tcx : TyCtxt < ' tcx > ,
300- merges : & FxIndexMap < Local , Local > ,
302+ merges : & FxIndexMap < Local , ( Local , Vec < Location > ) > ,
301303 merged_locals : & BitSet < Local > ,
302304) {
303305 let mut merger = Merger { tcx, merges, merged_locals } ;
@@ -306,18 +308,27 @@ fn apply_merges<'tcx>(
306308
307309struct Merger < ' a , ' tcx > {
308310 tcx : TyCtxt < ' tcx > ,
309- merges : & ' a FxIndexMap < Local , Local > ,
311+ merges : & ' a FxIndexMap < Local , ( Local , Vec < Location > ) > ,
310312 merged_locals : & ' a BitSet < Local > ,
311313}
312314
315+ impl < ' a , ' tcx > Merger < ' a , ' tcx > {
316+ fn should_remove_write_at ( & self , local : Local , location : Location ) -> bool {
317+ let Some ( ( _, to_remove) ) = self . merges . get ( & local) else {
318+ return false ;
319+ } ;
320+ to_remove. contains ( & location)
321+ }
322+ }
323+
313324impl < ' a , ' tcx > MutVisitor < ' tcx > for Merger < ' a , ' tcx > {
314325 fn tcx ( & self ) -> TyCtxt < ' tcx > {
315326 self . tcx
316327 }
317328
318329 fn visit_local ( & mut self , local : & mut Local , _: PlaceContext , _location : Location ) {
319330 if let Some ( dest) = self . merges . get ( local) {
320- * local = * dest;
331+ * local = dest. 0 ;
321332 }
322333 }
323334
@@ -348,10 +359,27 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
348359 _ => { }
349360 }
350361 }
362+ StatementKind :: Deinit ( place) => {
363+ if self . should_remove_write_at ( place. local , location) {
364+ statement. make_nop ( ) ;
365+ }
366+ }
351367
352368 _ => { }
353369 }
354370 }
371+
372+ fn visit_operand ( & mut self , op : & mut Operand < ' tcx > , location : Location ) {
373+ self . super_operand ( op, location) ;
374+ match op {
375+ Operand :: Move ( place) => {
376+ if self . should_remove_write_at ( place. local , location) {
377+ * op = Operand :: Copy ( * place) ;
378+ }
379+ }
380+ _ => ( ) ,
381+ }
382+ }
355383}
356384
357385//////////////////////////////////////////////////////////
@@ -373,30 +401,35 @@ struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
373401// through these methods, and not directly.
374402impl < ' alloc > Candidates < ' alloc > {
375403 /// Just `Vec::retain`, but the condition is inverted and we add debugging output
376- fn vec_filter_candidates (
404+ fn vec_modify_candidates (
377405 src : Local ,
378- v : & mut Vec < Local > ,
379- mut f : impl FnMut ( Local ) -> CandidateFilter ,
406+ v : & mut Vec < ( Local , Vec < Location > ) > ,
407+ mut f : impl FnMut ( Local ) -> CandidateModification ,
380408 at : Location ,
381409 ) {
382- v. retain ( |dest| {
383- let remove = f ( * dest) ;
384- if remove == CandidateFilter :: Remove {
410+ v. retain_mut ( |( dest, remove_writes) | match f ( * dest) {
411+ CandidateModification :: Remove => {
385412 trace ! ( "eliminating {:?} => {:?} due to conflict at {:?}" , src, dest, at) ;
413+ false
414+ }
415+ CandidateModification :: RemoveWrite => {
416+ trace ! ( "marking write for {:?} => {:?} as needing removing at {:?}" , src, dest, at) ;
417+ remove_writes. push ( at) ;
418+ true
386419 }
387- remove == CandidateFilter :: Keep
420+ CandidateModification :: Keep => true ,
388421 } ) ;
389422 }
390423
391424 /// `vec_filter_candidates` but for an `Entry`
392425 fn entry_filter_candidates (
393- mut entry : IndexOccupiedEntry < ' _ , Local , Vec < Local > > ,
426+ mut entry : IndexOccupiedEntry < ' _ , Local , Vec < ( Local , Vec < Location > ) > > ,
394427 p : Local ,
395- f : impl FnMut ( Local ) -> CandidateFilter ,
428+ f : impl FnMut ( Local ) -> CandidateModification ,
396429 at : Location ,
397430 ) {
398431 let candidates = entry. get_mut ( ) ;
399- Self :: vec_filter_candidates ( p, candidates, f, at) ;
432+ Self :: vec_modify_candidates ( p, candidates, f, at) ;
400433 if candidates. len ( ) == 0 {
401434 entry. remove ( ) ;
402435 }
@@ -406,7 +439,7 @@ impl<'alloc> Candidates<'alloc> {
406439 fn filter_candidates_by (
407440 & mut self ,
408441 p : Local ,
409- mut f : impl FnMut ( Local ) -> CandidateFilter ,
442+ mut f : impl FnMut ( Local ) -> CandidateModification ,
410443 at : Location ,
411444 ) {
412445 // Cover the cases where `p` appears as a `src`
@@ -420,7 +453,8 @@ impl<'alloc> Candidates<'alloc> {
420453 // We use `retain` here to remove the elements from the reverse set if we've removed the
421454 // matching candidate in the forward set.
422455 srcs. retain ( |src| {
423- if f ( * src) == CandidateFilter :: Keep {
456+ let modification = f ( * src) ;
457+ if modification == CandidateModification :: Keep {
424458 return true ;
425459 }
426460 let IndexEntry :: Occupied ( entry) = self . c . entry ( * src) else {
@@ -430,18 +464,20 @@ impl<'alloc> Candidates<'alloc> {
430464 entry,
431465 * src,
432466 |dest| {
433- if dest == p { CandidateFilter :: Remove } else { CandidateFilter :: Keep }
467+ if dest == p { modification } else { CandidateModification :: Keep }
434468 } ,
435469 at,
436470 ) ;
437- false
471+ // Remove the src from the reverse set if we removed the candidate pair
472+ modification == CandidateModification :: RemoveWrite
438473 } ) ;
439474 }
440475}
441476
442477#[ derive( Copy , Clone , PartialEq , Eq ) ]
443- enum CandidateFilter {
478+ enum CandidateModification {
444479 Keep ,
480+ RemoveWrite ,
445481 Remove ,
446482}
447483
@@ -500,32 +536,37 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
500536
501537 fn apply_conflicts ( & mut self ) {
502538 let writes = & self . write_info . writes ;
503- for p in writes {
539+ for & ( p, is_removable) in writes {
540+ let modification = if is_removable {
541+ CandidateModification :: RemoveWrite
542+ } else {
543+ CandidateModification :: Remove
544+ } ;
504545 let other_skip = self . write_info . skip_pair . and_then ( |( a, b) | {
505- if a == * p {
546+ if a == p {
506547 Some ( b)
507- } else if b == * p {
548+ } else if b == p {
508549 Some ( a)
509550 } else {
510551 None
511552 }
512553 } ) ;
513554 let at = self . points . point_from_location ( self . at ) ;
514555 self . candidates . filter_candidates_by (
515- * p,
556+ p,
516557 |q| {
517558 if Some ( q) == other_skip {
518- return CandidateFilter :: Keep ;
559+ return CandidateModification :: Keep ;
519560 }
520561 // It is possible that a local may be live for less than the
521562 // duration of a statement This happens in the case of function
522563 // calls or inline asm. Because of this, we also mark locals as
523564 // conflicting when both of them are written to in the same
524565 // statement.
525- if self . live . contains ( q, at) || writes. contains ( & q) {
526- CandidateFilter :: Remove
566+ if self . live . contains ( q, at) || writes. iter ( ) . any ( | & ( x , _ ) | x == q) {
567+ modification
527568 } else {
528- CandidateFilter :: Keep
569+ CandidateModification :: Keep
529570 }
530571 } ,
531572 self . at ,
@@ -537,7 +578,9 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
537578/// Describes where a statement/terminator writes to
538579#[ derive( Default , Debug ) ]
539580struct WriteInfo {
540- writes : Vec < Local > ,
581+ /// Which locals are written to. The `bool` is true if the write is "removable," ie if it comes
582+ /// from a `Operand::Move` or `Deinit`.
583+ writes : Vec < ( Local , bool ) > ,
541584 /// If this pair of locals is a candidate pair, completely skip processing it during this
542585 /// statement. All other candidates are unaffected.
543586 skip_pair : Option < ( Local , Local ) > ,
@@ -581,10 +624,11 @@ impl WriteInfo {
581624 | Rvalue :: CopyForDeref ( _) => ( ) ,
582625 }
583626 }
627+ StatementKind :: Deinit ( p) => {
628+ self . writes . push ( ( p. local , true ) ) ;
629+ }
584630 // Retags are technically also reads, but reporting them as a write suffices
585- StatementKind :: SetDiscriminant { place, .. }
586- | StatementKind :: Deinit ( place)
587- | StatementKind :: Retag ( _, place) => {
631+ StatementKind :: SetDiscriminant { place, .. } | StatementKind :: Retag ( _, place) => {
588632 self . add_place ( * * place) ;
589633 }
590634 StatementKind :: Intrinsic ( _)
@@ -669,16 +713,12 @@ impl WriteInfo {
669713 }
670714
671715 fn add_place ( & mut self , place : Place < ' _ > ) {
672- self . writes . push ( place. local ) ;
716+ self . writes . push ( ( place. local , false ) ) ;
673717 }
674718
675719 fn add_operand < ' tcx > ( & mut self , op : & Operand < ' tcx > ) {
676720 match op {
677- // FIXME(JakobDegen): In a previous version, the `Move` case was incorrectly treated as
678- // being a read only. This was unsound, however we cannot add a regression test because
679- // it is not possible to set this off with current MIR. Once we have that ability, a
680- // regression test should be added.
681- Operand :: Move ( p) => self . add_place ( * p) ,
721+ Operand :: Move ( p) => self . writes . push ( ( p. local , true ) ) ,
682722 Operand :: Copy ( _) | Operand :: Constant ( _) => ( ) ,
683723 }
684724 }
@@ -733,7 +773,7 @@ fn places_to_candidate_pair<'tcx>(
733773fn find_candidates < ' alloc , ' tcx > (
734774 body : & Body < ' tcx > ,
735775 borrowed : & BitSet < Local > ,
736- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
776+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
737777 candidates_reverse : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
738778) -> Candidates < ' alloc > {
739779 candidates. clear ( ) ;
@@ -747,16 +787,16 @@ fn find_candidates<'alloc, 'tcx>(
747787 }
748788 // Generate the reverse map
749789 for ( src, cands) in candidates. iter ( ) {
750- for dest in cands. iter ( ) . copied ( ) {
751- candidates_reverse. entry ( dest) . or_default ( ) . push ( * src) ;
790+ for ( dest, _ ) in cands. iter ( ) {
791+ candidates_reverse. entry ( * dest) . or_default ( ) . push ( * src) ;
752792 }
753793 }
754794 Candidates { c : candidates, reverse : candidates_reverse }
755795}
756796
757797struct FindAssignments < ' a , ' alloc , ' tcx > {
758798 body : & ' a Body < ' tcx > ,
759- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
799+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
760800 borrowed : & ' a BitSet < Local > ,
761801}
762802
@@ -793,7 +833,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
793833 }
794834
795835 // We may insert duplicates here, but that's fine
796- self . candidates . entry ( src) . or_default ( ) . push ( dest) ;
836+ self . candidates . entry ( src) . or_default ( ) . push ( ( dest, Vec :: new ( ) ) ) ;
797837 }
798838 }
799839}
0 commit comments