132132//! [attempt 3]: https://github.com/rust-lang/rust/pull/72632
133133
134134use crate :: MirPass ;
135- use rustc_data_structures:: fx:: { FxIndexMap , IndexEntry , IndexOccupiedEntry } ;
135+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap , IndexEntry , IndexOccupiedEntry } ;
136136use rustc_index:: bit_set:: BitSet ;
137137use rustc_index:: interval:: SparseIntervalMatrix ;
138138use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext , Visitor } ;
@@ -159,7 +159,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
159159 // 2. Despite being an overall perf improvement, this still causes a 30% regression in
160160 // keccak. We can temporarily fix this by bounding function size, but in the long term
161161 // we should fix this by being smarter about invalidating analysis results.
162- sess. mir_opt_level ( ) >= 3
162+ sess. mir_opt_level ( ) >= 2
163163 }
164164
165165 fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
@@ -216,12 +216,14 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
216216
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 ( ) ;
219+ let mut remove_writes = FxHashMap :: default ( ) ;
219220
220- for ( src, candidates) in candidates. c . iter ( ) {
221- if merged_locals. contains ( * src) {
221+ for ( src, candidates) in candidates. c . drain ( .. ) {
222+ if merged_locals. contains ( src) {
222223 continue ;
223224 }
224- let Some ( dest) = candidates. iter ( ) . find ( |dest| !merged_locals. contains ( * * dest) )
225+ let Some ( dest) =
226+ candidates. into_iter ( ) . find ( |( dest, _) | !merged_locals. contains ( * dest) )
225227 else {
226228 continue ;
227229 } ;
@@ -231,14 +233,17 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
231233 break ;
232234 }
233235
234- // Replace `src` by `dest` everywhere.
235- merges. insert ( * src, * dest) ;
236- merged_locals. insert ( * src) ;
237- merged_locals. insert ( * dest) ;
238-
239236 // Update liveness information based on the merge we just performed.
240237 // Every location where `src` was live, `dest` will be live.
241- live. union_rows ( * src, * dest) ;
238+ live. union_rows ( src, dest. 0 ) ;
239+
240+ // Replace `src` by `dest` everywhere.
241+ merged_locals. insert ( src) ;
242+ merged_locals. insert ( dest. 0 ) ;
243+ merges. insert ( src, dest. 0 ) ;
244+ if !dest. 1 . is_empty ( ) {
245+ remove_writes. insert ( dest. 0 , dest. 1 ) ;
246+ }
242247 }
243248 trace ! ( merging = ?merges) ;
244249
@@ -247,7 +252,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
247252 }
248253 round_count += 1 ;
249254
250- apply_merges ( body, tcx, & merges, & merged_locals) ;
255+ apply_merges ( body, tcx, & merges, & remove_writes , & merged_locals) ;
251256 }
252257
253258 trace ! ( round_count) ;
@@ -260,7 +265,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
260265/// frequently. Everything with a `&'alloc` lifetime points into here.
261266#[ derive( Default ) ]
262267struct Allocations {
263- candidates : FxIndexMap < Local , Vec < Local > > ,
268+ candidates : FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
264269 candidates_reverse : FxIndexMap < Local , Vec < Local > > ,
265270 write_info : WriteInfo ,
266271 // PERF: Do this for `MaybeLiveLocals` allocations too.
@@ -282,7 +287,7 @@ struct Candidates<'alloc> {
282287 ///
283288 /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
284289 /// remove that assignment.
285- c : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
290+ c : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
286291 /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
287292 /// then this contains `b => a`.
288293 // PERF: Possibly these should be `SmallVec`s?
@@ -298,18 +303,29 @@ fn apply_merges<'tcx>(
298303 body : & mut Body < ' tcx > ,
299304 tcx : TyCtxt < ' tcx > ,
300305 merges : & FxIndexMap < Local , Local > ,
306+ remove_writes : & FxHashMap < Local , Vec < Location > > ,
301307 merged_locals : & BitSet < Local > ,
302308) {
303- let mut merger = Merger { tcx, merges, merged_locals } ;
309+ let mut merger = Merger { tcx, merges, remove_writes , merged_locals } ;
304310 merger. visit_body_preserves_cfg ( body) ;
305311}
306312
307313struct Merger < ' a , ' tcx > {
308314 tcx : TyCtxt < ' tcx > ,
309315 merges : & ' a FxIndexMap < Local , Local > ,
316+ remove_writes : & ' a FxHashMap < Local , Vec < Location > > ,
310317 merged_locals : & ' a BitSet < Local > ,
311318}
312319
320+ impl < ' a , ' tcx > Merger < ' a , ' tcx > {
321+ fn should_remove_write_at ( & self , local : Local , location : Location ) -> bool {
322+ let Some ( to_remove) = self . remove_writes . get ( & local) else {
323+ return false ;
324+ } ;
325+ to_remove. contains ( & location)
326+ }
327+ }
328+
313329impl < ' a , ' tcx > MutVisitor < ' tcx > for Merger < ' a , ' tcx > {
314330 fn tcx ( & self ) -> TyCtxt < ' tcx > {
315331 self . tcx
@@ -348,10 +364,27 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
348364 _ => { }
349365 }
350366 }
367+ StatementKind :: Deinit ( place) => {
368+ if self . should_remove_write_at ( place. local , location) {
369+ statement. make_nop ( ) ;
370+ }
371+ }
351372
352373 _ => { }
353374 }
354375 }
376+
377+ fn visit_operand ( & mut self , op : & mut Operand < ' tcx > , location : Location ) {
378+ self . super_operand ( op, location) ;
379+ match op {
380+ Operand :: Move ( place) => {
381+ if self . should_remove_write_at ( place. local , location) {
382+ * op = Operand :: Copy ( * place) ;
383+ }
384+ }
385+ _ => ( ) ,
386+ }
387+ }
355388}
356389
357390//////////////////////////////////////////////////////////
@@ -373,30 +406,35 @@ struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
373406// through these methods, and not directly.
374407impl < ' alloc > Candidates < ' alloc > {
375408 /// Just `Vec::retain`, but the condition is inverted and we add debugging output
376- fn vec_filter_candidates (
409+ fn vec_modify_candidates (
377410 src : Local ,
378- v : & mut Vec < Local > ,
379- mut f : impl FnMut ( Local ) -> CandidateFilter ,
411+ v : & mut Vec < ( Local , Vec < Location > ) > ,
412+ mut f : impl FnMut ( Local ) -> CandidateModification ,
380413 at : Location ,
381414 ) {
382- v. retain ( |dest| {
383- let remove = f ( * dest) ;
384- if remove == CandidateFilter :: Remove {
415+ v. retain_mut ( |( dest, remove_writes) | match f ( * dest) {
416+ CandidateModification :: Remove => {
385417 trace ! ( "eliminating {:?} => {:?} due to conflict at {:?}" , src, dest, at) ;
418+ false
419+ }
420+ CandidateModification :: RemoveWrite => {
421+ trace ! ( "marking write for {:?} => {:?} as needing removing at {:?}" , src, dest, at) ;
422+ remove_writes. push ( at) ;
423+ true
386424 }
387- remove == CandidateFilter :: Keep
425+ CandidateModification :: Keep => true ,
388426 } ) ;
389427 }
390428
391429 /// `vec_filter_candidates` but for an `Entry`
392430 fn entry_filter_candidates (
393- mut entry : IndexOccupiedEntry < ' _ , Local , Vec < Local > > ,
431+ mut entry : IndexOccupiedEntry < ' _ , Local , Vec < ( Local , Vec < Location > ) > > ,
394432 p : Local ,
395- f : impl FnMut ( Local ) -> CandidateFilter ,
433+ f : impl FnMut ( Local ) -> CandidateModification ,
396434 at : Location ,
397435 ) {
398436 let candidates = entry. get_mut ( ) ;
399- Self :: vec_filter_candidates ( p, candidates, f, at) ;
437+ Self :: vec_modify_candidates ( p, candidates, f, at) ;
400438 if candidates. len ( ) == 0 {
401439 entry. remove ( ) ;
402440 }
@@ -406,7 +444,7 @@ impl<'alloc> Candidates<'alloc> {
406444 fn filter_candidates_by (
407445 & mut self ,
408446 p : Local ,
409- mut f : impl FnMut ( Local ) -> CandidateFilter ,
447+ mut f : impl FnMut ( Local ) -> CandidateModification ,
410448 at : Location ,
411449 ) {
412450 // Cover the cases where `p` appears as a `src`
@@ -420,7 +458,8 @@ impl<'alloc> Candidates<'alloc> {
420458 // We use `retain` here to remove the elements from the reverse set if we've removed the
421459 // matching candidate in the forward set.
422460 srcs. retain ( |src| {
423- if f ( * src) == CandidateFilter :: Keep {
461+ let modification = f ( * src) ;
462+ if modification == CandidateModification :: Keep {
424463 return true ;
425464 }
426465 let IndexEntry :: Occupied ( entry) = self . c . entry ( * src) else {
@@ -430,18 +469,20 @@ impl<'alloc> Candidates<'alloc> {
430469 entry,
431470 * src,
432471 |dest| {
433- if dest == p { CandidateFilter :: Remove } else { CandidateFilter :: Keep }
472+ if dest == p { modification } else { CandidateModification :: Keep }
434473 } ,
435474 at,
436475 ) ;
437- false
476+ // Remove the src from the reverse set if we removed the candidate pair
477+ modification == CandidateModification :: RemoveWrite
438478 } ) ;
439479 }
440480}
441481
442482#[ derive( Copy , Clone , PartialEq , Eq ) ]
443- enum CandidateFilter {
483+ enum CandidateModification {
444484 Keep ,
485+ RemoveWrite ,
445486 Remove ,
446487}
447488
@@ -500,32 +541,37 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
500541
501542 fn apply_conflicts ( & mut self ) {
502543 let writes = & self . write_info . writes ;
503- for p in writes {
544+ for & ( p, is_removable) in writes {
545+ let modification = if is_removable {
546+ CandidateModification :: RemoveWrite
547+ } else {
548+ CandidateModification :: Remove
549+ } ;
504550 let other_skip = self . write_info . skip_pair . and_then ( |( a, b) | {
505- if a == * p {
551+ if a == p {
506552 Some ( b)
507- } else if b == * p {
553+ } else if b == p {
508554 Some ( a)
509555 } else {
510556 None
511557 }
512558 } ) ;
513559 let at = self . points . point_from_location ( self . at ) ;
514560 self . candidates . filter_candidates_by (
515- * p,
561+ p,
516562 |q| {
517563 if Some ( q) == other_skip {
518- return CandidateFilter :: Keep ;
564+ return CandidateModification :: Keep ;
519565 }
520566 // It is possible that a local may be live for less than the
521567 // duration of a statement This happens in the case of function
522568 // calls or inline asm. Because of this, we also mark locals as
523569 // conflicting when both of them are written to in the same
524570 // statement.
525- if self . live . contains ( q, at) || writes. contains ( & q) {
526- CandidateFilter :: Remove
571+ if self . live . contains ( q, at) || writes. iter ( ) . any ( | & ( x , _ ) | x == q) {
572+ modification
527573 } else {
528- CandidateFilter :: Keep
574+ CandidateModification :: Keep
529575 }
530576 } ,
531577 self . at ,
@@ -537,7 +583,9 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
537583/// Describes where a statement/terminator writes to
538584#[ derive( Default , Debug ) ]
539585struct WriteInfo {
540- writes : Vec < Local > ,
586+ /// Which locals are written to. The `bool` is true if the write is "removable," ie if it comes
587+ /// from a `Operand::Move` or `Deinit`.
588+ writes : Vec < ( Local , bool ) > ,
541589 /// If this pair of locals is a candidate pair, completely skip processing it during this
542590 /// statement. All other candidates are unaffected.
543591 skip_pair : Option < ( Local , Local ) > ,
@@ -581,10 +629,11 @@ impl WriteInfo {
581629 | Rvalue :: CopyForDeref ( _) => ( ) ,
582630 }
583631 }
632+ StatementKind :: Deinit ( p) => {
633+ self . writes . push ( ( p. local , true ) ) ;
634+ }
584635 // Retags are technically also reads, but reporting them as a write suffices
585- StatementKind :: SetDiscriminant { place, .. }
586- | StatementKind :: Deinit ( place)
587- | StatementKind :: Retag ( _, place) => {
636+ StatementKind :: SetDiscriminant { place, .. } | StatementKind :: Retag ( _, place) => {
588637 self . add_place ( * * place) ;
589638 }
590639 StatementKind :: Intrinsic ( _)
@@ -669,16 +718,12 @@ impl WriteInfo {
669718 }
670719
671720 fn add_place ( & mut self , place : Place < ' _ > ) {
672- self . writes . push ( place. local ) ;
721+ self . writes . push ( ( place. local , false ) ) ;
673722 }
674723
675724 fn add_operand < ' tcx > ( & mut self , op : & Operand < ' tcx > ) {
676725 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) ,
726+ Operand :: Move ( p) => self . writes . push ( ( p. local , true ) ) ,
682727 Operand :: Copy ( _) | Operand :: Constant ( _) => ( ) ,
683728 }
684729 }
@@ -733,7 +778,7 @@ fn places_to_candidate_pair<'tcx>(
733778fn find_candidates < ' alloc , ' tcx > (
734779 body : & Body < ' tcx > ,
735780 borrowed : & BitSet < Local > ,
736- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
781+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
737782 candidates_reverse : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
738783) -> Candidates < ' alloc > {
739784 candidates. clear ( ) ;
@@ -747,16 +792,16 @@ fn find_candidates<'alloc, 'tcx>(
747792 }
748793 // Generate the reverse map
749794 for ( src, cands) in candidates. iter ( ) {
750- for dest in cands. iter ( ) . copied ( ) {
751- candidates_reverse. entry ( dest) . or_default ( ) . push ( * src) ;
795+ for ( dest, _ ) in cands. iter ( ) {
796+ candidates_reverse. entry ( * dest) . or_default ( ) . push ( * src) ;
752797 }
753798 }
754799 Candidates { c : candidates, reverse : candidates_reverse }
755800}
756801
757802struct FindAssignments < ' a , ' alloc , ' tcx > {
758803 body : & ' a Body < ' tcx > ,
759- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
804+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
760805 borrowed : & ' a BitSet < Local > ,
761806}
762807
@@ -793,7 +838,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
793838 }
794839
795840 // We may insert duplicates here, but that's fine
796- self . candidates . entry ( src) . or_default ( ) . push ( dest) ;
841+ self . candidates . entry ( src) . or_default ( ) . push ( ( dest, Vec :: new ( ) ) ) ;
797842 }
798843 }
799844}
0 commit comments