131131//! [attempt 2]: https://github.com/rust-lang/rust/pull/71003
132132//! [attempt 3]: https://github.com/rust-lang/rust/pull/72632
133133
134- use rustc_data_structures:: fx:: { FxIndexMap , IndexEntry , IndexOccupiedEntry } ;
135- use rustc_index:: bit_set:: DenseBitSet ;
134+ use rustc_data_structures:: fx:: FxIndexMap ;
135+ use rustc_index:: bit_set:: { BitMatrix , DenseBitSet } ;
136136use rustc_index:: interval:: SparseIntervalMatrix ;
137137use rustc_middle:: bug;
138138use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext , Visitor } ;
@@ -153,11 +153,12 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
153153 sess. mir_opt_level ( ) >= 2
154154 }
155155
156+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) ) ]
156157 fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
157158 let def_id = body. source . def_id ( ) ;
158159 let mut candidates = Candidates :: default ( ) ;
159160 let mut write_info = WriteInfo :: default ( ) ;
160- trace ! ( func = ?tcx . def_path_str ( def_id) ) ;
161+ trace ! ( ? def_id) ;
161162
162163 let borrowed = rustc_mir_dataflow:: impls:: borrowed_locals ( body) ;
163164
@@ -185,40 +186,33 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
185186 trace ! ( ?candidates) ;
186187 dest_prop_mir_dump ( tcx, body, & points, & live, round_count) ;
187188
188- FilterInformation :: filter_liveness (
189- & mut candidates,
190- & points,
191- & live,
192- & mut write_info,
193- body,
194- ) ;
195-
196- // Because we only filter once per round, it is unsound to use a local for more than
197- // one merge operation within a single round of optimizations. We store here which ones
198- // we have already used.
199- let mut merged_locals: DenseBitSet < Local > =
200- DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ;
189+ let mut filter =
190+ FilterInformation :: filter_liveness ( & points, & live, & mut write_info, body) ;
201191
202192 // This is the set of merges we will apply this round. It is a subset of the candidates.
203193 let mut merges = FxIndexMap :: default ( ) ;
194+ // Record which locals have been merged to handle storage statements.
195+ let mut merged_locals: DenseBitSet < Local > =
196+ DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ;
204197
205- for ( src, candidates) in candidates. c . iter ( ) {
206- if merged_locals. contains ( * src) {
207- continue ;
208- }
209- let Some ( dest) = candidates. iter ( ) . find ( |dest| !merged_locals. contains ( * * dest) )
210- else {
211- continue ;
212- } ;
198+ for ( & src, candidates) in candidates. c . iter ( ) {
199+ let Some ( dest) = filter. find_non_conflicting ( src, candidates) else { continue } ;
200+ // `dest` may already be replaced with another destination. This is not an issue.
201+ // This just appears as locals changing names between rounds. This will be resolved
202+ // in a next round.
213203
214204 // Replace `src` by `dest` everywhere.
215- merges. insert ( * src, * dest) ;
216- merged_locals. insert ( * src) ;
217- merged_locals. insert ( * dest) ;
205+ merges. insert ( src, dest) ;
206+ merged_locals. insert ( src) ;
207+ merged_locals. insert ( dest) ;
218208
219209 // Update liveness information based on the merge we just performed.
220210 // Every location where `src` was live, `dest` will be live.
221- live. union_rows ( * src, * dest) ;
211+ live. union_rows ( src, dest) ;
212+
213+ // Update conflict information based on the merge we just performed.
214+ // Everything that conflicted with `src` now conflicts with `dest`.
215+ filter. record_merge ( src, dest) ;
222216 }
223217 trace ! ( merging = ?merges) ;
224218
@@ -255,11 +249,6 @@ struct Candidates {
255249 /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
256250 /// remove that assignment.
257251 c : FxIndexMap < Local , Vec < Local > > ,
258-
259- /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
260- /// then this contains `b => a`.
261- // PERF: Possibly these should be `SmallVec`s?
262- reverse : FxIndexMap < Local , Vec < Local > > ,
263252}
264253
265254//////////////////////////////////////////////////////////
@@ -335,8 +324,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> {
335324struct FilterInformation < ' a , ' tcx > {
336325 body : & ' a Body < ' tcx > ,
337326 points : & ' a DenseLocationMap ,
338- live : & ' a SparseIntervalMatrix < Local , PointIndex > ,
339- candidates : & ' a mut Candidates ,
327+ conflicts : BitMatrix < Local , Local > ,
340328 write_info : & ' a mut WriteInfo ,
341329 at : Location ,
342330}
@@ -350,94 +338,14 @@ impl Candidates {
350338 /// This is responsible for enforcing the first and third bullet point.
351339 fn reset_and_find < ' tcx > ( & mut self , body : & Body < ' tcx > , borrowed : & DenseBitSet < Local > ) {
352340 self . c . clear ( ) ;
353- self . reverse . clear ( ) ;
354341 let mut visitor = FindAssignments { body, candidates : & mut self . c , borrowed } ;
355342 visitor. visit_body ( body) ;
356343 // Deduplicate candidates.
357344 for ( _, cands) in self . c . iter_mut ( ) {
358345 cands. sort ( ) ;
359346 cands. dedup ( ) ;
360347 }
361- // Generate the reverse map.
362- for ( src, cands) in self . c . iter ( ) {
363- for dest in cands. iter ( ) . copied ( ) {
364- self . reverse . entry ( dest) . or_default ( ) . push ( * src) ;
365- }
366- }
367348 }
368-
369- /// Just `Vec::retain`, but the condition is inverted and we add debugging output
370- fn vec_filter_candidates (
371- src : Local ,
372- v : & mut Vec < Local > ,
373- mut f : impl FnMut ( Local ) -> CandidateFilter ,
374- at : Location ,
375- ) {
376- v. retain ( |dest| {
377- let remove = f ( * dest) ;
378- if remove == CandidateFilter :: Remove {
379- trace ! ( "eliminating {:?} => {:?} due to conflict at {:?}" , src, dest, at) ;
380- }
381- remove == CandidateFilter :: Keep
382- } ) ;
383- }
384-
385- /// `vec_filter_candidates` but for an `Entry`
386- fn entry_filter_candidates (
387- mut entry : IndexOccupiedEntry < ' _ , Local , Vec < Local > > ,
388- p : Local ,
389- f : impl FnMut ( Local ) -> CandidateFilter ,
390- at : Location ,
391- ) {
392- let candidates = entry. get_mut ( ) ;
393- Self :: vec_filter_candidates ( p, candidates, f, at) ;
394- if candidates. len ( ) == 0 {
395- // FIXME(#120456) - is `swap_remove` correct?
396- entry. swap_remove ( ) ;
397- }
398- }
399-
400- /// For all candidates `(p, q)` or `(q, p)` removes the candidate if `f(q)` says to do so
401- fn filter_candidates_by (
402- & mut self ,
403- p : Local ,
404- mut f : impl FnMut ( Local ) -> CandidateFilter ,
405- at : Location ,
406- ) {
407- // Cover the cases where `p` appears as a `src`
408- if let IndexEntry :: Occupied ( entry) = self . c . entry ( p) {
409- Self :: entry_filter_candidates ( entry, p, & mut f, at) ;
410- }
411- // And the cases where `p` appears as a `dest`
412- let Some ( srcs) = self . reverse . get_mut ( & p) else {
413- return ;
414- } ;
415- // We use `retain` here to remove the elements from the reverse set if we've removed the
416- // matching candidate in the forward set.
417- srcs. retain ( |src| {
418- if f ( * src) == CandidateFilter :: Keep {
419- return true ;
420- }
421- let IndexEntry :: Occupied ( entry) = self . c . entry ( * src) else {
422- return false ;
423- } ;
424- Self :: entry_filter_candidates (
425- entry,
426- * src,
427- |dest| {
428- if dest == p { CandidateFilter :: Remove } else { CandidateFilter :: Keep }
429- } ,
430- at,
431- ) ;
432- false
433- } ) ;
434- }
435- }
436-
437- #[ derive( Copy , Clone , PartialEq , Eq ) ]
438- enum CandidateFilter {
439- Keep ,
440- Remove ,
441349}
442350
443351impl < ' a , ' tcx > FilterInformation < ' a , ' tcx > {
@@ -459,74 +367,80 @@ impl<'a, 'tcx> FilterInformation<'a, 'tcx> {
459367 /// statement/terminator to be live. We are additionally conservative by treating all written to
460368 /// locals as also being read from.
461369 fn filter_liveness (
462- candidates : & mut Candidates ,
463- points : & DenseLocationMap ,
370+ points : & ' a DenseLocationMap ,
464371 live : & SparseIntervalMatrix < Local , PointIndex > ,
465- write_info : & mut WriteInfo ,
466- body : & Body < ' tcx > ,
467- ) {
372+ write_info : & ' a mut WriteInfo ,
373+ body : & ' a Body < ' tcx > ,
374+ ) -> Self {
375+ let num_locals = body. local_decls . len ( ) ;
468376 let mut this = FilterInformation {
469377 body,
470378 points,
471- live,
472- candidates,
379+ conflicts : BitMatrix :: new ( num_locals, num_locals) ,
473380 // We don't actually store anything at this scope, we just keep things here to be able
474381 // to reuse the allocation.
475382 write_info,
476383 // Doesn't matter what we put here, will be overwritten before being used
477384 at : Location :: START ,
478385 } ;
479- this. internal_filter_liveness ( ) ;
386+ this. internal_filter_liveness ( live) ;
387+ this
480388 }
481389
482- fn internal_filter_liveness ( & mut self ) {
390+ fn internal_filter_liveness ( & mut self , live : & SparseIntervalMatrix < Local , PointIndex > ) {
483391 for ( block, data) in traversal:: preorder ( self . body ) {
484392 self . at = Location { block, statement_index : data. statements . len ( ) } ;
485393 self . write_info . for_terminator ( & data. terminator ( ) . kind ) ;
486- self . apply_conflicts ( ) ;
394+ self . record_conflicts ( live ) ;
487395
488396 for ( i, statement) in data. statements . iter ( ) . enumerate ( ) . rev ( ) {
489397 self . at = Location { block, statement_index : i } ;
490398 self . write_info . for_statement ( & statement. kind , self . body ) ;
491- self . apply_conflicts ( ) ;
399+ self . record_conflicts ( live ) ;
492400 }
493401 }
402+
403+ trace ! ( ?self . conflicts, "initial conflicts" ) ;
494404 }
495405
496- fn apply_conflicts ( & mut self ) {
406+ fn record_conflicts ( & mut self , live : & SparseIntervalMatrix < Local , PointIndex > ) {
497407 let writes = & self . write_info . writes ;
498- for p in writes {
499- let other_skip = self . write_info . skip_pair . and_then ( |( a, b) | {
500- if a == * p {
501- Some ( b)
502- } else if b == * p {
503- Some ( a)
504- } else {
505- None
408+ let skip_pair = self . write_info . skip_pair ;
409+ let at = self . points . point_from_location ( self . at ) ;
410+
411+ for & p in writes {
412+ for & q in writes {
413+ if p != q && skip_pair != Some ( ( p, q) ) && skip_pair != Some ( ( q, p) ) {
414+ self . conflicts . insert ( p, q) ;
506415 }
507- } ) ;
508- let at = self . points . point_from_location ( self . at ) ;
509- self . candidates . filter_candidates_by (
510- * p,
511- |q| {
512- if Some ( q) == other_skip {
513- return CandidateFilter :: Keep ;
514- }
515- // It is possible that a local may be live for less than the
516- // duration of a statement This happens in the case of function
517- // calls or inline asm. Because of this, we also mark locals as
518- // conflicting when both of them are written to in the same
519- // statement.
520- if self . live . contains ( q, at) || writes. contains ( & q) {
521- CandidateFilter :: Remove
522- } else {
523- CandidateFilter :: Keep
524- }
525- } ,
526- self . at ,
527- ) ;
416+ }
417+
418+ for q in live. rows ( ) {
419+ if p != q
420+ && skip_pair != Some ( ( p, q) )
421+ && skip_pair != Some ( ( q, p) )
422+ && live. contains ( q, at)
423+ {
424+ self . conflicts . insert ( p, q) ;
425+ }
426+ }
528427 }
529428 }
429+
430+ #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
431+ fn record_merge ( & mut self , src : Local , dest : Local ) {
432+ trace ! ( ?self . conflicts, "pre" ) ;
433+ self . conflicts . union_rows ( src, dest) ;
434+ self . conflicts . union_cols ( src, dest) ;
435+ trace ! ( ?self . conflicts, "post" ) ;
436+ }
437+
438+ #[ tracing:: instrument( level = "trace" , skip( self ) , ret) ]
439+ fn find_non_conflicting ( & self , src : Local , candidates : & Vec < Local > ) -> Option < Local > {
440+ candidates. iter ( ) . copied ( ) . find ( |& dest| {
441+ !self . conflicts . contains ( src, dest) && !self . conflicts . contains ( dest, src)
442+ } )
443+ }
530444}
531445
532446/// Describes where a statement/terminator writes to
0 commit comments