131
131
//! [attempt 2]: https://github.com/rust-lang/rust/pull/71003
132
132
//! [attempt 3]: https://github.com/rust-lang/rust/pull/72632
133
133
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 } ;
136
136
use rustc_index:: interval:: SparseIntervalMatrix ;
137
137
use rustc_middle:: bug;
138
138
use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext , Visitor } ;
@@ -153,11 +153,12 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
153
153
sess. mir_opt_level ( ) >= 2
154
154
}
155
155
156
+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) ) ]
156
157
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
157
158
let def_id = body. source . def_id ( ) ;
158
159
let mut candidates = Candidates :: default ( ) ;
159
160
let mut write_info = WriteInfo :: default ( ) ;
160
- trace ! ( func = ?tcx . def_path_str ( def_id) ) ;
161
+ trace ! ( ? def_id) ;
161
162
162
163
let borrowed = rustc_mir_dataflow:: impls:: borrowed_locals ( body) ;
163
164
@@ -185,40 +186,33 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
185
186
trace ! ( ?candidates) ;
186
187
dest_prop_mir_dump ( tcx, body, & points, & live, round_count) ;
187
188
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) ;
201
191
202
192
// This is the set of merges we will apply this round. It is a subset of the candidates.
203
193
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 ( ) ) ;
204
197
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.
213
203
214
204
// 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) ;
218
208
219
209
// Update liveness information based on the merge we just performed.
220
210
// 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) ;
222
216
}
223
217
trace ! ( merging = ?merges) ;
224
218
@@ -255,11 +249,6 @@ struct Candidates {
255
249
/// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
256
250
/// remove that assignment.
257
251
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 > > ,
263
252
}
264
253
265
254
//////////////////////////////////////////////////////////
@@ -335,8 +324,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> {
335
324
struct FilterInformation < ' a , ' tcx > {
336
325
body : & ' a Body < ' tcx > ,
337
326
points : & ' a DenseLocationMap ,
338
- live : & ' a SparseIntervalMatrix < Local , PointIndex > ,
339
- candidates : & ' a mut Candidates ,
327
+ conflicts : BitMatrix < Local , Local > ,
340
328
write_info : & ' a mut WriteInfo ,
341
329
at : Location ,
342
330
}
@@ -350,94 +338,14 @@ impl Candidates {
350
338
/// This is responsible for enforcing the first and third bullet point.
351
339
fn reset_and_find < ' tcx > ( & mut self , body : & Body < ' tcx > , borrowed : & DenseBitSet < Local > ) {
352
340
self . c . clear ( ) ;
353
- self . reverse . clear ( ) ;
354
341
let mut visitor = FindAssignments { body, candidates : & mut self . c , borrowed } ;
355
342
visitor. visit_body ( body) ;
356
343
// Deduplicate candidates.
357
344
for ( _, cands) in self . c . iter_mut ( ) {
358
345
cands. sort ( ) ;
359
346
cands. dedup ( ) ;
360
347
}
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
- }
367
348
}
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 ,
441
349
}
442
350
443
351
impl < ' a , ' tcx > FilterInformation < ' a , ' tcx > {
@@ -459,74 +367,80 @@ impl<'a, 'tcx> FilterInformation<'a, 'tcx> {
459
367
/// statement/terminator to be live. We are additionally conservative by treating all written to
460
368
/// locals as also being read from.
461
369
fn filter_liveness (
462
- candidates : & mut Candidates ,
463
- points : & DenseLocationMap ,
370
+ points : & ' a DenseLocationMap ,
464
371
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 ( ) ;
468
376
let mut this = FilterInformation {
469
377
body,
470
378
points,
471
- live,
472
- candidates,
379
+ conflicts : BitMatrix :: new ( num_locals, num_locals) ,
473
380
// We don't actually store anything at this scope, we just keep things here to be able
474
381
// to reuse the allocation.
475
382
write_info,
476
383
// Doesn't matter what we put here, will be overwritten before being used
477
384
at : Location :: START ,
478
385
} ;
479
- this. internal_filter_liveness ( ) ;
386
+ this. internal_filter_liveness ( live) ;
387
+ this
480
388
}
481
389
482
- fn internal_filter_liveness ( & mut self ) {
390
+ fn internal_filter_liveness ( & mut self , live : & SparseIntervalMatrix < Local , PointIndex > ) {
483
391
for ( block, data) in traversal:: preorder ( self . body ) {
484
392
self . at = Location { block, statement_index : data. statements . len ( ) } ;
485
393
self . write_info . for_terminator ( & data. terminator ( ) . kind ) ;
486
- self . apply_conflicts ( ) ;
394
+ self . record_conflicts ( live ) ;
487
395
488
396
for ( i, statement) in data. statements . iter ( ) . enumerate ( ) . rev ( ) {
489
397
self . at = Location { block, statement_index : i } ;
490
398
self . write_info . for_statement ( & statement. kind , self . body ) ;
491
- self . apply_conflicts ( ) ;
399
+ self . record_conflicts ( live ) ;
492
400
}
493
401
}
402
+
403
+ trace ! ( ?self . conflicts, "initial conflicts" ) ;
494
404
}
495
405
496
- fn apply_conflicts ( & mut self ) {
406
+ fn record_conflicts ( & mut self , live : & SparseIntervalMatrix < Local , PointIndex > ) {
497
407
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) ;
506
415
}
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
+ }
528
427
}
529
428
}
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
+ }
530
444
}
531
445
532
446
/// Describes where a statement/terminator writes to
0 commit comments