@@ -217,11 +217,12 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
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
219
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) {
222
222
continue ;
223
223
}
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) )
225
226
else {
226
227
continue ;
227
228
} ;
@@ -231,14 +232,15 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
231
232
break ;
232
233
}
233
234
234
- // Replace `src` by `dest` everywhere.
235
- merges. insert ( * src, * dest) ;
236
- merged_locals. insert ( * src) ;
237
- merged_locals. insert ( * dest) ;
238
-
239
235
// Update liveness information based on the merge we just performed.
240
236
// 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) ;
242
244
}
243
245
trace ! ( merging = ?merges) ;
244
246
@@ -260,7 +262,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
260
262
/// frequently. Everything with a `&'alloc` lifetime points into here.
261
263
#[ derive( Default ) ]
262
264
struct Allocations {
263
- candidates : FxIndexMap < Local , Vec < Local > > ,
265
+ candidates : FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
264
266
candidates_reverse : FxIndexMap < Local , Vec < Local > > ,
265
267
write_info : WriteInfo ,
266
268
// PERF: Do this for `MaybeLiveLocals` allocations too.
@@ -282,7 +284,7 @@ struct Candidates<'alloc> {
282
284
///
283
285
/// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
284
286
/// remove that assignment.
285
- c : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
287
+ c : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
286
288
/// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
287
289
/// then this contains `b => a`.
288
290
// PERF: Possibly these should be `SmallVec`s?
@@ -297,7 +299,7 @@ struct Candidates<'alloc> {
297
299
fn apply_merges < ' tcx > (
298
300
body : & mut Body < ' tcx > ,
299
301
tcx : TyCtxt < ' tcx > ,
300
- merges : & FxIndexMap < Local , Local > ,
302
+ merges : & FxIndexMap < Local , ( Local , Vec < Location > ) > ,
301
303
merged_locals : & BitSet < Local > ,
302
304
) {
303
305
let mut merger = Merger { tcx, merges, merged_locals } ;
@@ -306,18 +308,27 @@ fn apply_merges<'tcx>(
306
308
307
309
struct Merger < ' a , ' tcx > {
308
310
tcx : TyCtxt < ' tcx > ,
309
- merges : & ' a FxIndexMap < Local , Local > ,
311
+ merges : & ' a FxIndexMap < Local , ( Local , Vec < Location > ) > ,
310
312
merged_locals : & ' a BitSet < Local > ,
311
313
}
312
314
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
+
313
324
impl < ' a , ' tcx > MutVisitor < ' tcx > for Merger < ' a , ' tcx > {
314
325
fn tcx ( & self ) -> TyCtxt < ' tcx > {
315
326
self . tcx
316
327
}
317
328
318
329
fn visit_local ( & mut self , local : & mut Local , _: PlaceContext , _location : Location ) {
319
330
if let Some ( dest) = self . merges . get ( local) {
320
- * local = * dest;
331
+ * local = dest. 0 ;
321
332
}
322
333
}
323
334
@@ -348,10 +359,27 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
348
359
_ => { }
349
360
}
350
361
}
362
+ StatementKind :: Deinit ( place) => {
363
+ if self . should_remove_write_at ( place. local , location) {
364
+ statement. make_nop ( ) ;
365
+ }
366
+ }
351
367
352
368
_ => { }
353
369
}
354
370
}
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
+ }
355
383
}
356
384
357
385
//////////////////////////////////////////////////////////
@@ -373,30 +401,35 @@ struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
373
401
// through these methods, and not directly.
374
402
impl < ' alloc > Candidates < ' alloc > {
375
403
/// Just `Vec::retain`, but the condition is inverted and we add debugging output
376
- fn vec_filter_candidates (
404
+ fn vec_modify_candidates (
377
405
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 ,
380
408
at : Location ,
381
409
) {
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 => {
385
412
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
386
419
}
387
- remove == CandidateFilter :: Keep
420
+ CandidateModification :: Keep => true ,
388
421
} ) ;
389
422
}
390
423
391
424
/// `vec_filter_candidates` but for an `Entry`
392
425
fn entry_filter_candidates (
393
- mut entry : IndexOccupiedEntry < ' _ , Local , Vec < Local > > ,
426
+ mut entry : IndexOccupiedEntry < ' _ , Local , Vec < ( Local , Vec < Location > ) > > ,
394
427
p : Local ,
395
- f : impl FnMut ( Local ) -> CandidateFilter ,
428
+ f : impl FnMut ( Local ) -> CandidateModification ,
396
429
at : Location ,
397
430
) {
398
431
let candidates = entry. get_mut ( ) ;
399
- Self :: vec_filter_candidates ( p, candidates, f, at) ;
432
+ Self :: vec_modify_candidates ( p, candidates, f, at) ;
400
433
if candidates. len ( ) == 0 {
401
434
entry. remove ( ) ;
402
435
}
@@ -406,7 +439,7 @@ impl<'alloc> Candidates<'alloc> {
406
439
fn filter_candidates_by (
407
440
& mut self ,
408
441
p : Local ,
409
- mut f : impl FnMut ( Local ) -> CandidateFilter ,
442
+ mut f : impl FnMut ( Local ) -> CandidateModification ,
410
443
at : Location ,
411
444
) {
412
445
// Cover the cases where `p` appears as a `src`
@@ -420,7 +453,8 @@ impl<'alloc> Candidates<'alloc> {
420
453
// We use `retain` here to remove the elements from the reverse set if we've removed the
421
454
// matching candidate in the forward set.
422
455
srcs. retain ( |src| {
423
- if f ( * src) == CandidateFilter :: Keep {
456
+ let modification = f ( * src) ;
457
+ if modification == CandidateModification :: Keep {
424
458
return true ;
425
459
}
426
460
let IndexEntry :: Occupied ( entry) = self . c . entry ( * src) else {
@@ -430,18 +464,20 @@ impl<'alloc> Candidates<'alloc> {
430
464
entry,
431
465
* src,
432
466
|dest| {
433
- if dest == p { CandidateFilter :: Remove } else { CandidateFilter :: Keep }
467
+ if dest == p { modification } else { CandidateModification :: Keep }
434
468
} ,
435
469
at,
436
470
) ;
437
- false
471
+ // Remove the src from the reverse set if we removed the candidate pair
472
+ modification == CandidateModification :: RemoveWrite
438
473
} ) ;
439
474
}
440
475
}
441
476
442
477
#[ derive( Copy , Clone , PartialEq , Eq ) ]
443
- enum CandidateFilter {
478
+ enum CandidateModification {
444
479
Keep ,
480
+ RemoveWrite ,
445
481
Remove ,
446
482
}
447
483
@@ -500,32 +536,37 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
500
536
501
537
fn apply_conflicts ( & mut self ) {
502
538
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
+ } ;
504
545
let other_skip = self . write_info . skip_pair . and_then ( |( a, b) | {
505
- if a == * p {
546
+ if a == p {
506
547
Some ( b)
507
- } else if b == * p {
548
+ } else if b == p {
508
549
Some ( a)
509
550
} else {
510
551
None
511
552
}
512
553
} ) ;
513
554
let at = self . points . point_from_location ( self . at ) ;
514
555
self . candidates . filter_candidates_by (
515
- * p,
556
+ p,
516
557
|q| {
517
558
if Some ( q) == other_skip {
518
- return CandidateFilter :: Keep ;
559
+ return CandidateModification :: Keep ;
519
560
}
520
561
// It is possible that a local may be live for less than the
521
562
// duration of a statement This happens in the case of function
522
563
// calls or inline asm. Because of this, we also mark locals as
523
564
// conflicting when both of them are written to in the same
524
565
// 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
527
568
} else {
528
- CandidateFilter :: Keep
569
+ CandidateModification :: Keep
529
570
}
530
571
} ,
531
572
self . at ,
@@ -537,7 +578,9 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
537
578
/// Describes where a statement/terminator writes to
538
579
#[ derive( Default , Debug ) ]
539
580
struct 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 ) > ,
541
584
/// If this pair of locals is a candidate pair, completely skip processing it during this
542
585
/// statement. All other candidates are unaffected.
543
586
skip_pair : Option < ( Local , Local ) > ,
@@ -581,10 +624,11 @@ impl WriteInfo {
581
624
| Rvalue :: CopyForDeref ( _) => ( ) ,
582
625
}
583
626
}
627
+ StatementKind :: Deinit ( p) => {
628
+ self . writes . push ( ( p. local , true ) ) ;
629
+ }
584
630
// 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) => {
588
632
self . add_place ( * * place) ;
589
633
}
590
634
StatementKind :: Intrinsic ( _)
@@ -669,16 +713,12 @@ impl WriteInfo {
669
713
}
670
714
671
715
fn add_place ( & mut self , place : Place < ' _ > ) {
672
- self . writes . push ( place. local ) ;
716
+ self . writes . push ( ( place. local , false ) ) ;
673
717
}
674
718
675
719
fn add_operand < ' tcx > ( & mut self , op : & Operand < ' tcx > ) {
676
720
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 ) ) ,
682
722
Operand :: Copy ( _) | Operand :: Constant ( _) => ( ) ,
683
723
}
684
724
}
@@ -733,7 +773,7 @@ fn places_to_candidate_pair<'tcx>(
733
773
fn find_candidates < ' alloc , ' tcx > (
734
774
body : & Body < ' tcx > ,
735
775
borrowed : & BitSet < Local > ,
736
- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
776
+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
737
777
candidates_reverse : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
738
778
) -> Candidates < ' alloc > {
739
779
candidates. clear ( ) ;
@@ -747,16 +787,16 @@ fn find_candidates<'alloc, 'tcx>(
747
787
}
748
788
// Generate the reverse map
749
789
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) ;
752
792
}
753
793
}
754
794
Candidates { c : candidates, reverse : candidates_reverse }
755
795
}
756
796
757
797
struct FindAssignments < ' a , ' alloc , ' tcx > {
758
798
body : & ' a Body < ' tcx > ,
759
- candidates : & ' alloc mut FxIndexMap < Local , Vec < Local > > ,
799
+ candidates : & ' alloc mut FxIndexMap < Local , Vec < ( Local , Vec < Location > ) > > ,
760
800
borrowed : & ' a BitSet < Local > ,
761
801
}
762
802
@@ -793,7 +833,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
793
833
}
794
834
795
835
// 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 ( ) ) ) ;
797
837
}
798
838
}
799
839
}
0 commit comments