@@ -208,22 +208,23 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
208
208
// This is the set of merges we will apply this round. It is a subset of the candidates.
209
209
let mut merges = FxHashMap :: default ( ) ;
210
210
211
- for ( src, candidates) in candidates. c . iter ( ) {
212
- if merged_locals. contains ( * src) {
211
+ for ( src, candidates) in candidates. c . drain ( ) {
212
+ if merged_locals. contains ( src) {
213
213
continue ;
214
214
}
215
215
let Some ( dest) =
216
- candidates. iter ( ) . find ( |dest| !merged_locals. contains ( * * dest) ) else {
216
+ candidates. into_iter ( ) . find ( |( dest, _ ) | !merged_locals. contains ( * dest) ) else {
217
217
continue ;
218
218
} ;
219
219
if !tcx. consider_optimizing ( || {
220
220
format ! ( "{} round {}" , tcx. def_path_str( def_id) , round_count)
221
221
} ) {
222
222
break ;
223
223
}
224
- merges. insert ( * src, * dest) ;
225
- merged_locals. insert ( * src) ;
226
- merged_locals. insert ( * dest) ;
224
+ merged_locals. insert ( src) ;
225
+ merged_locals. insert ( dest. 0 ) ;
226
+ merges. insert ( src, dest. clone ( ) ) ;
227
+ merges. insert ( dest. 0 , dest) ;
227
228
}
228
229
trace ! ( merging = ?merges) ;
229
230
@@ -245,7 +246,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
245
246
/// frequently. Everything with a `&'alloc` lifetime points into here.
246
247
#[ derive( Default ) ]
247
248
struct Allocations {
248
- candidates : FxHashMap < Local , Vec < Local > > ,
249
+ candidates : FxHashMap < Local , Vec < ( Local , Vec < Location > ) > > ,
249
250
candidates_reverse : FxHashMap < Local , Vec < Local > > ,
250
251
write_info : WriteInfo ,
251
252
// PERF: Do this for `MaybeLiveLocals` allocations too.
@@ -267,7 +268,11 @@ struct Candidates<'alloc> {
267
268
///
268
269
/// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
269
270
/// remove that assignment.
270
- c : & ' alloc mut FxHashMap < Local , Vec < Local > > ,
271
+ ///
272
+ /// Each candidate pair is associated with a `Vec<Location>`. If the candidate pair is accepted,
273
+ /// all writes to either local at these locations must be removed. The writes will always be
274
+ /// removable.
275
+ c : & ' alloc mut FxHashMap < Local , Vec < ( Local , Vec < Location > ) > > ,
271
276
/// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
272
277
/// then this contains `b => a`.
273
278
// PERF: Possibly these should be `SmallVec`s?
@@ -282,7 +287,7 @@ struct Candidates<'alloc> {
282
287
fn apply_merges < ' tcx > (
283
288
body : & mut Body < ' tcx > ,
284
289
tcx : TyCtxt < ' tcx > ,
285
- merges : & FxHashMap < Local , Local > ,
290
+ merges : & FxHashMap < Local , ( Local , Vec < Location > ) > ,
286
291
merged_locals : & BitSet < Local > ,
287
292
) {
288
293
let mut merger = Merger { tcx, merges, merged_locals } ;
@@ -291,18 +296,27 @@ fn apply_merges<'tcx>(
291
296
292
297
struct Merger < ' a , ' tcx > {
293
298
tcx : TyCtxt < ' tcx > ,
294
- merges : & ' a FxHashMap < Local , Local > ,
299
+ merges : & ' a FxHashMap < Local , ( Local , Vec < Location > ) > ,
295
300
merged_locals : & ' a BitSet < Local > ,
296
301
}
297
302
303
+ impl < ' a , ' tcx > Merger < ' a , ' tcx > {
304
+ fn should_remove_write_at ( & self , local : Local , location : Location ) -> bool {
305
+ let Some ( ( _, to_remove) ) = self . merges . get ( & local) else {
306
+ return false ;
307
+ } ;
308
+ to_remove. contains ( & location)
309
+ }
310
+ }
311
+
298
312
impl < ' a , ' tcx > MutVisitor < ' tcx > for Merger < ' a , ' tcx > {
299
313
fn tcx ( & self ) -> TyCtxt < ' tcx > {
300
314
self . tcx
301
315
}
302
316
303
317
fn visit_local ( & mut self , local : & mut Local , _: PlaceContext , _location : Location ) {
304
318
if let Some ( dest) = self . merges . get ( local) {
305
- * local = * dest;
319
+ * local = dest. 0 ;
306
320
}
307
321
}
308
322
@@ -332,10 +346,27 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
332
346
_ => { }
333
347
}
334
348
}
349
+ StatementKind :: Deinit ( place) => {
350
+ if self . should_remove_write_at ( place. local , location) {
351
+ statement. make_nop ( ) ;
352
+ }
353
+ }
335
354
336
355
_ => { }
337
356
}
338
357
}
358
+
359
+ fn visit_operand ( & mut self , op : & mut Operand < ' tcx > , location : Location ) {
360
+ self . super_operand ( op, location) ;
361
+ match op {
362
+ Operand :: Move ( place) => {
363
+ if self . should_remove_write_at ( place. local , location) {
364
+ * op = Operand :: Copy ( * place) ;
365
+ }
366
+ }
367
+ _ => ( ) ,
368
+ }
369
+ }
339
370
}
340
371
341
372
//////////////////////////////////////////////////////////
@@ -356,30 +387,35 @@ struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
356
387
// through these methods, and not directly.
357
388
impl < ' alloc > Candidates < ' alloc > {
358
389
/// Just `Vec::retain`, but the condition is inverted and we add debugging output
359
- fn vec_filter_candidates (
390
+ fn vec_modify_candidates (
360
391
src : Local ,
361
- v : & mut Vec < Local > ,
362
- mut f : impl FnMut ( Local ) -> CandidateFilter ,
392
+ v : & mut Vec < ( Local , Vec < Location > ) > ,
393
+ mut f : impl FnMut ( Local ) -> CandidateModification ,
363
394
at : Location ,
364
395
) {
365
- v. retain ( |dest| {
366
- let remove = f ( * dest) ;
367
- if remove == CandidateFilter :: Remove {
396
+ v. retain_mut ( |( dest, remove_writes) | match f ( * dest) {
397
+ CandidateModification :: Remove => {
368
398
trace ! ( "eliminating {:?} => {:?} due to conflict at {:?}" , src, dest, at) ;
399
+ false
400
+ }
401
+ CandidateModification :: RemoveWrite => {
402
+ trace ! ( "marking write for {:?} => {:?} as needing removing at {:?}" , src, dest, at) ;
403
+ remove_writes. push ( at) ;
404
+ true
369
405
}
370
- remove == CandidateFilter :: Keep
406
+ CandidateModification :: Keep => true ,
371
407
} ) ;
372
408
}
373
409
374
410
/// `vec_filter_candidates` but for an `Entry`
375
411
fn entry_filter_candidates (
376
- mut entry : OccupiedEntry < ' _ , Local , Vec < Local > > ,
412
+ mut entry : OccupiedEntry < ' _ , Local , Vec < ( Local , Vec < Location > ) > > ,
377
413
p : Local ,
378
- f : impl FnMut ( Local ) -> CandidateFilter ,
414
+ f : impl FnMut ( Local ) -> CandidateModification ,
379
415
at : Location ,
380
416
) {
381
417
let candidates = entry. get_mut ( ) ;
382
- Self :: vec_filter_candidates ( p, candidates, f, at) ;
418
+ Self :: vec_modify_candidates ( p, candidates, f, at) ;
383
419
if candidates. len ( ) == 0 {
384
420
entry. remove ( ) ;
385
421
}
@@ -389,7 +425,7 @@ impl<'alloc> Candidates<'alloc> {
389
425
fn filter_candidates_by (
390
426
& mut self ,
391
427
p : Local ,
392
- mut f : impl FnMut ( Local ) -> CandidateFilter ,
428
+ mut f : impl FnMut ( Local ) -> CandidateModification ,
393
429
at : Location ,
394
430
) {
395
431
// Cover the cases where `p` appears as a `src`
@@ -403,7 +439,8 @@ impl<'alloc> Candidates<'alloc> {
403
439
// We use `retain` here to remove the elements from the reverse set if we've removed the
404
440
// matching candidate in the forward set.
405
441
srcs. retain ( |src| {
406
- if f ( * src) == CandidateFilter :: Keep {
442
+ let modification = f ( * src) ;
443
+ if modification == CandidateModification :: Keep {
407
444
return true ;
408
445
}
409
446
let Entry :: Occupied ( entry) = self . c . entry ( * src) else {
@@ -413,18 +450,20 @@ impl<'alloc> Candidates<'alloc> {
413
450
entry,
414
451
* src,
415
452
|dest| {
416
- if dest == p { CandidateFilter :: Remove } else { CandidateFilter :: Keep }
453
+ if dest == p { modification } else { CandidateModification :: Keep }
417
454
} ,
418
455
at,
419
456
) ;
420
- false
457
+ // Remove the src from the reverse set if we removed the candidate pair
458
+ modification == CandidateModification :: RemoveWrite
421
459
} ) ;
422
460
}
423
461
}
424
462
425
463
#[ derive( Copy , Clone , PartialEq , Eq ) ]
426
- enum CandidateFilter {
464
+ enum CandidateModification {
427
465
Keep ,
466
+ RemoveWrite ,
428
467
Remove ,
429
468
}
430
469
@@ -483,31 +522,36 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
483
522
484
523
fn apply_conflicts ( & mut self ) {
485
524
let writes = & self . write_info . writes ;
486
- for p in writes {
525
+ for & ( p, is_removable) in writes {
526
+ let modification = if is_removable {
527
+ CandidateModification :: RemoveWrite
528
+ } else {
529
+ CandidateModification :: Remove
530
+ } ;
487
531
let other_skip = self . write_info . skip_pair . and_then ( |( a, b) | {
488
- if a == * p {
532
+ if a == p {
489
533
Some ( b)
490
- } else if b == * p {
534
+ } else if b == p {
491
535
Some ( a)
492
536
} else {
493
537
None
494
538
}
495
539
} ) ;
496
540
self . candidates . filter_candidates_by (
497
- * p,
541
+ p,
498
542
|q| {
499
543
if Some ( q) == other_skip {
500
- return CandidateFilter :: Keep ;
544
+ return CandidateModification :: Keep ;
501
545
}
502
546
// It is possible that a local may be live for less than the
503
547
// duration of a statement This happens in the case of function
504
548
// calls or inline asm. Because of this, we also mark locals as
505
549
// conflicting when both of them are written to in the same
506
550
// statement.
507
- if self . live . contains ( q) || writes. contains ( & q) {
508
- CandidateFilter :: Remove
551
+ if self . live . contains ( q) || writes. iter ( ) . any ( | & ( x , _ ) | x == q) {
552
+ modification
509
553
} else {
510
- CandidateFilter :: Keep
554
+ CandidateModification :: Keep
511
555
}
512
556
} ,
513
557
self . at ,
@@ -519,7 +563,9 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
519
563
/// Describes where a statement/terminator writes to
520
564
#[ derive( Default , Debug ) ]
521
565
struct WriteInfo {
522
- writes : Vec < Local > ,
566
+ /// Which locals are written to. The `bool` is true if the write is "removable," ie if it comes
567
+ /// from a `Operand::Move` or `Deinit`.
568
+ writes : Vec < ( Local , bool ) > ,
523
569
/// If this pair of locals is a candidate pair, completely skip processing it during this
524
570
/// statement. All other candidates are unaffected.
525
571
skip_pair : Option < ( Local , Local ) > ,
@@ -563,10 +609,11 @@ impl WriteInfo {
563
609
| Rvalue :: CopyForDeref ( _) => ( ) ,
564
610
}
565
611
}
612
+ StatementKind :: Deinit ( p) => {
613
+ self . writes . push ( ( p. local , true ) ) ;
614
+ }
566
615
// Retags are technically also reads, but reporting them as a write suffices
567
- StatementKind :: SetDiscriminant { place, .. }
568
- | StatementKind :: Deinit ( place)
569
- | StatementKind :: Retag ( _, place) => {
616
+ StatementKind :: SetDiscriminant { place, .. } | StatementKind :: Retag ( _, place) => {
570
617
self . add_place ( * * place) ;
571
618
}
572
619
StatementKind :: Intrinsic ( _)
@@ -652,16 +699,12 @@ impl WriteInfo {
652
699
}
653
700
654
701
fn add_place < ' tcx > ( & mut self , place : Place < ' tcx > ) {
655
- self . writes . push ( place. local ) ;
702
+ self . writes . push ( ( place. local , false ) ) ;
656
703
}
657
704
658
705
fn add_operand < ' tcx > ( & mut self , op : & Operand < ' tcx > ) {
659
706
match op {
660
- // FIXME(JakobDegen): In a previous version, the `Move` case was incorrectly treated as
661
- // being a read only. This was unsound, however we cannot add a regression test because
662
- // it is not possible to set this off with current MIR. Once we have that ability, a
663
- // regression test should be added.
664
- Operand :: Move ( p) => self . add_place ( * p) ,
707
+ Operand :: Move ( p) => self . writes . push ( ( p. local , true ) ) ,
665
708
Operand :: Copy ( _) | Operand :: Constant ( _) => ( ) ,
666
709
}
667
710
}
@@ -716,7 +759,7 @@ fn places_to_candidate_pair<'tcx>(
716
759
fn find_candidates < ' alloc , ' tcx > (
717
760
body : & Body < ' tcx > ,
718
761
borrowed : & BitSet < Local > ,
719
- candidates : & ' alloc mut FxHashMap < Local , Vec < Local > > ,
762
+ candidates : & ' alloc mut FxHashMap < Local , Vec < ( Local , Vec < Location > ) > > ,
720
763
candidates_reverse : & ' alloc mut FxHashMap < Local , Vec < Local > > ,
721
764
) -> Candidates < ' alloc > {
722
765
candidates. clear ( ) ;
@@ -730,16 +773,16 @@ fn find_candidates<'alloc, 'tcx>(
730
773
}
731
774
// Generate the reverse map
732
775
for ( src, cands) in candidates. iter ( ) {
733
- for dest in cands. iter ( ) . copied ( ) {
734
- candidates_reverse. entry ( dest) . or_default ( ) . push ( * src) ;
776
+ for ( dest, _ ) in cands. iter ( ) {
777
+ candidates_reverse. entry ( * dest) . or_default ( ) . push ( * src) ;
735
778
}
736
779
}
737
780
Candidates { c : candidates, reverse : candidates_reverse }
738
781
}
739
782
740
783
struct FindAssignments < ' a , ' alloc , ' tcx > {
741
784
body : & ' a Body < ' tcx > ,
742
- candidates : & ' alloc mut FxHashMap < Local , Vec < Local > > ,
785
+ candidates : & ' alloc mut FxHashMap < Local , Vec < ( Local , Vec < Location > ) > > ,
743
786
borrowed : & ' a BitSet < Local > ,
744
787
}
745
788
@@ -766,7 +809,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
766
809
}
767
810
768
811
// We may insert duplicates here, but that's fine
769
- self . candidates . entry ( src) . or_default ( ) . push ( dest) ;
812
+ self . candidates . entry ( src) . or_default ( ) . push ( ( dest, Vec :: new ( ) ) ) ;
770
813
}
771
814
}
772
815
}
0 commit comments