132
132
//! [attempt 3]: https://github.com/rust-lang/rust/pull/72632
133
133
134
134
use crate :: MirPass ;
135
- use rustc_data_structures:: fx:: { FxIndexMap , IndexEntry , IndexOccupiedEntry } ;
135
+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap , IndexEntry , IndexOccupiedEntry } ;
136
136
use rustc_index:: bit_set:: BitSet ;
137
137
use rustc_index:: interval:: SparseIntervalMatrix ;
138
138
use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext , Visitor } ;
@@ -159,7 +159,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
159
159
// 2. Despite being an overall perf improvement, this still causes a 30% regression in
160
160
// keccak. We can temporarily fix this by bounding function size, but in the long term
161
161
// we should fix this by being smarter about invalidating analysis results.
162
- sess. mir_opt_level ( ) >= 3
162
+ sess. mir_opt_level ( ) >= 2
163
163
}
164
164
165
165
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
@@ -216,12 +216,14 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
216
216
217
217
// This is the set of merges we will apply this round. It is a subset of the candidates.
218
218
let mut merges = FxIndexMap :: default ( ) ;
219
+ let mut remove_writes = FxHashMap :: default ( ) ;
219
220
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) {
222
223
continue ;
223
224
}
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) )
225
227
else {
226
228
continue ;
227
229
} ;
@@ -231,14 +233,17 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
231
233
break ;
232
234
}
233
235
234
- // Replace `src` by `dest` everywhere.
235
- merges. insert ( * src, * dest) ;
236
- merged_locals. insert ( * src) ;
237
- merged_locals. insert ( * dest) ;
238
-
239
236
// Update liveness information based on the merge we just performed.
240
237
// 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
+ }
242
247
}
243
248
trace ! ( merging = ?merges) ;
244
249
@@ -247,7 +252,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
247
252
}
248
253
round_count += 1 ;
249
254
250
- apply_merges ( body, tcx, & merges, & merged_locals) ;
255
+ apply_merges ( body, tcx, & merges, & remove_writes , & merged_locals) ;
251
256
}
252
257
253
258
trace ! ( round_count) ;
@@ -260,7 +265,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
260
265
/// frequently. Everything with a `&'alloc` lifetime points into here.
261
266
#[ derive( Default ) ]
262
267
struct Allocations {
263
- candidates : FxIndexMap < Local , Vec < Local > > ,
268
+ candidates : FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
264
269
candidates_reverse : FxIndexMap < Local , Vec < Local > > ,
265
270
write_info : WriteInfo ,
266
271
// PERF: Do this for `MaybeLiveLocals` allocations too.
@@ -282,7 +287,7 @@ struct Candidates<'alloc> {
282
287
///
283
288
/// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
284
289
/// remove that assignment.
285
- c : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
290
+ c : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
286
291
/// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
287
292
/// then this contains `b => a`.
288
293
// PERF: Possibly these should be `SmallVec`s?
@@ -298,18 +303,29 @@ fn apply_merges<'tcx>(
298
303
body : & mut Body < ' tcx > ,
299
304
tcx : TyCtxt < ' tcx > ,
300
305
merges : & FxIndexMap < Local , Local > ,
306
+ remove_writes : & FxHashMap < Local , Vec < Location > > ,
301
307
merged_locals : & BitSet < Local > ,
302
308
) {
303
- let mut merger = Merger { tcx, merges, merged_locals } ;
309
+ let mut merger = Merger { tcx, merges, remove_writes , merged_locals } ;
304
310
merger. visit_body_preserves_cfg ( body) ;
305
311
}
306
312
307
313
struct Merger < ' a , ' tcx > {
308
314
tcx : TyCtxt < ' tcx > ,
309
315
merges : & ' a FxIndexMap < Local , Local > ,
316
+ remove_writes : & ' a FxHashMap < Local , Vec < Location > > ,
310
317
merged_locals : & ' a BitSet < Local > ,
311
318
}
312
319
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
+
313
329
impl < ' a , ' tcx > MutVisitor < ' tcx > for Merger < ' a , ' tcx > {
314
330
fn tcx ( & self ) -> TyCtxt < ' tcx > {
315
331
self . tcx
@@ -348,10 +364,27 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
348
364
_ => { }
349
365
}
350
366
}
367
+ StatementKind :: Deinit ( place) => {
368
+ if self . should_remove_write_at ( place. local , location) {
369
+ statement. make_nop ( ) ;
370
+ }
371
+ }
351
372
352
373
_ => { }
353
374
}
354
375
}
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
+ }
355
388
}
356
389
357
390
//////////////////////////////////////////////////////////
@@ -373,30 +406,35 @@ struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
373
406
// through these methods, and not directly.
374
407
impl < ' alloc > Candidates < ' alloc > {
375
408
/// Just `Vec::retain`, but the condition is inverted and we add debugging output
376
- fn vec_filter_candidates (
409
+ fn vec_modify_candidates (
377
410
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 ,
380
413
at : Location ,
381
414
) {
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 => {
385
417
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
386
424
}
387
- remove == CandidateFilter :: Keep
425
+ CandidateModification :: Keep => true ,
388
426
} ) ;
389
427
}
390
428
391
429
/// `vec_filter_candidates` but for an `Entry`
392
430
fn entry_filter_candidates (
393
- mut entry : IndexOccupiedEntry < ' _ , Local , Vec < Local > > ,
431
+ mut entry : IndexOccupiedEntry < ' _ , Local , Vec < ( Local , Vec < Location > ) > > ,
394
432
p : Local ,
395
- f : impl FnMut ( Local ) -> CandidateFilter ,
433
+ f : impl FnMut ( Local ) -> CandidateModification ,
396
434
at : Location ,
397
435
) {
398
436
let candidates = entry. get_mut ( ) ;
399
- Self :: vec_filter_candidates ( p, candidates, f, at) ;
437
+ Self :: vec_modify_candidates ( p, candidates, f, at) ;
400
438
if candidates. len ( ) == 0 {
401
439
entry. remove ( ) ;
402
440
}
@@ -406,7 +444,7 @@ impl<'alloc> Candidates<'alloc> {
406
444
fn filter_candidates_by (
407
445
& mut self ,
408
446
p : Local ,
409
- mut f : impl FnMut ( Local ) -> CandidateFilter ,
447
+ mut f : impl FnMut ( Local ) -> CandidateModification ,
410
448
at : Location ,
411
449
) {
412
450
// Cover the cases where `p` appears as a `src`
@@ -420,7 +458,8 @@ impl<'alloc> Candidates<'alloc> {
420
458
// We use `retain` here to remove the elements from the reverse set if we've removed the
421
459
// matching candidate in the forward set.
422
460
srcs. retain ( |src| {
423
- if f ( * src) == CandidateFilter :: Keep {
461
+ let modification = f ( * src) ;
462
+ if modification == CandidateModification :: Keep {
424
463
return true ;
425
464
}
426
465
let IndexEntry :: Occupied ( entry) = self . c . entry ( * src) else {
@@ -430,18 +469,20 @@ impl<'alloc> Candidates<'alloc> {
430
469
entry,
431
470
* src,
432
471
|dest| {
433
- if dest == p { CandidateFilter :: Remove } else { CandidateFilter :: Keep }
472
+ if dest == p { modification } else { CandidateModification :: Keep }
434
473
} ,
435
474
at,
436
475
) ;
437
- false
476
+ // Remove the src from the reverse set if we removed the candidate pair
477
+ modification == CandidateModification :: RemoveWrite
438
478
} ) ;
439
479
}
440
480
}
441
481
442
482
#[ derive( Copy , Clone , PartialEq , Eq ) ]
443
- enum CandidateFilter {
483
+ enum CandidateModification {
444
484
Keep ,
485
+ RemoveWrite ,
445
486
Remove ,
446
487
}
447
488
@@ -500,32 +541,37 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
500
541
501
542
fn apply_conflicts ( & mut self ) {
502
543
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
+ } ;
504
550
let other_skip = self . write_info . skip_pair . and_then ( |( a, b) | {
505
- if a == * p {
551
+ if a == p {
506
552
Some ( b)
507
- } else if b == * p {
553
+ } else if b == p {
508
554
Some ( a)
509
555
} else {
510
556
None
511
557
}
512
558
} ) ;
513
559
let at = self . points . point_from_location ( self . at ) ;
514
560
self . candidates . filter_candidates_by (
515
- * p,
561
+ p,
516
562
|q| {
517
563
if Some ( q) == other_skip {
518
- return CandidateFilter :: Keep ;
564
+ return CandidateModification :: Keep ;
519
565
}
520
566
// It is possible that a local may be live for less than the
521
567
// duration of a statement This happens in the case of function
522
568
// calls or inline asm. Because of this, we also mark locals as
523
569
// conflicting when both of them are written to in the same
524
570
// 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
527
573
} else {
528
- CandidateFilter :: Keep
574
+ CandidateModification :: Keep
529
575
}
530
576
} ,
531
577
self . at ,
@@ -537,7 +583,9 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
537
583
/// Describes where a statement/terminator writes to
538
584
#[ derive( Default , Debug ) ]
539
585
struct 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 ) > ,
541
589
/// If this pair of locals is a candidate pair, completely skip processing it during this
542
590
/// statement. All other candidates are unaffected.
543
591
skip_pair : Option < ( Local , Local ) > ,
@@ -581,10 +629,11 @@ impl WriteInfo {
581
629
| Rvalue :: CopyForDeref ( _) => ( ) ,
582
630
}
583
631
}
632
+ StatementKind :: Deinit ( p) => {
633
+ self . writes . push ( ( p. local , true ) ) ;
634
+ }
584
635
// 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) => {
588
637
self . add_place ( * * place) ;
589
638
}
590
639
StatementKind :: Intrinsic ( _)
@@ -669,16 +718,12 @@ impl WriteInfo {
669
718
}
670
719
671
720
fn add_place ( & mut self , place : Place < ' _ > ) {
672
- self . writes . push ( place. local ) ;
721
+ self . writes . push ( ( place. local , false ) ) ;
673
722
}
674
723
675
724
fn add_operand < ' tcx > ( & mut self , op : & Operand < ' tcx > ) {
676
725
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 ) ) ,
682
727
Operand :: Copy ( _) | Operand :: Constant ( _) => ( ) ,
683
728
}
684
729
}
@@ -733,7 +778,7 @@ fn places_to_candidate_pair<'tcx>(
733
778
fn find_candidates < ' alloc , ' tcx > (
734
779
body : & Body < ' tcx > ,
735
780
borrowed : & BitSet < Local > ,
736
- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
781
+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
737
782
candidates_reverse : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
738
783
) -> Candidates < ' alloc > {
739
784
candidates. clear ( ) ;
@@ -747,16 +792,16 @@ fn find_candidates<'alloc, 'tcx>(
747
792
}
748
793
// Generate the reverse map
749
794
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) ;
752
797
}
753
798
}
754
799
Candidates { c : candidates, reverse : candidates_reverse }
755
800
}
756
801
757
802
struct FindAssignments < ' a , ' alloc , ' tcx > {
758
803
body : & ' a Body < ' tcx > ,
759
- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
804
+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
760
805
borrowed : & ' a BitSet < Local > ,
761
806
}
762
807
@@ -793,7 +838,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
793
838
}
794
839
795
840
// 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 ( ) ) ) ;
797
842
}
798
843
}
799
844
}
0 commit comments