@@ -109,8 +109,8 @@ use rustc_index::{
109
109
use rustc_middle:: mir:: tcx:: PlaceTy ;
110
110
use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext , Visitor } ;
111
111
use rustc_middle:: mir:: {
112
- traversal, Body , Local , LocalKind , Location , Operand , Place , PlaceElem , Rvalue , Statement ,
113
- StatementKind , Terminator , TerminatorKind ,
112
+ traversal, Body , InlineAsmOperand , Local , LocalKind , Location , Operand , Place , PlaceElem ,
113
+ Rvalue , Statement , StatementKind , Terminator , TerminatorKind ,
114
114
} ;
115
115
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
116
116
@@ -397,7 +397,9 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
397
397
}
398
398
}
399
399
400
- struct Conflicts {
400
+ struct Conflicts < ' a > {
401
+ relevant_locals : & ' a BitSet < Local > ,
402
+
401
403
/// The conflict matrix. It is always symmetric and the adjacency matrix of the corresponding
402
404
/// conflict graph.
403
405
matrix : BitMatrix < Local , Local > ,
@@ -406,30 +408,21 @@ struct Conflicts {
406
408
unify_cache : BitSet < Local > ,
407
409
}
408
410
409
- impl Conflicts {
411
+ impl Conflicts < ' a > {
410
412
fn build < ' tcx > (
411
413
tcx : TyCtxt < ' tcx > ,
412
414
body : & ' _ Body < ' tcx > ,
413
415
source : MirSource < ' tcx > ,
414
- relevant_locals : & BitSet < Local > ,
416
+ relevant_locals : & ' a BitSet < Local > ,
415
417
) -> Self {
416
418
// We don't have to look out for locals that have their address taken, since
417
419
// `find_candidates` already takes care of that.
418
420
419
- let mut conflicts = BitMatrix :: from_row_n (
421
+ let conflicts = BitMatrix :: from_row_n (
420
422
& BitSet :: new_empty ( body. local_decls . len ( ) ) ,
421
423
body. local_decls . len ( ) ,
422
424
) ;
423
425
424
- let mut record_conflicts = |new_conflicts : & mut BitSet < _ > | {
425
- // Remove all locals that are not candidates.
426
- new_conflicts. intersect ( relevant_locals) ;
427
-
428
- for local in new_conflicts. iter ( ) {
429
- conflicts. union_row_with ( & new_conflicts, local) ;
430
- }
431
- } ;
432
-
433
426
let def_id = source. def_id ( ) ;
434
427
let mut init = MaybeInitializedLocals
435
428
. into_engine ( tcx, body, def_id)
@@ -480,6 +473,12 @@ impl Conflicts {
480
473
} ,
481
474
) ;
482
475
476
+ let mut this = Self {
477
+ relevant_locals,
478
+ matrix : conflicts,
479
+ unify_cache : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
480
+ } ;
481
+
483
482
let mut live_and_init_locals = Vec :: new ( ) ;
484
483
485
484
// Visit only reachable basic blocks. The exact order is not important.
@@ -497,14 +496,22 @@ impl Conflicts {
497
496
BitSet :: new_empty ( body. local_decls . len ( ) )
498
497
} ) ;
499
498
500
- // First, go forwards for `MaybeInitializedLocals`.
501
- for statement_index in 0 ..=data. statements . len ( ) {
502
- let loc = Location { block, statement_index } ;
499
+ // First, go forwards for `MaybeInitializedLocals` and apply intra-statement/terminator
500
+ // conflicts.
501
+ for ( i, statement) in data. statements . iter ( ) . enumerate ( ) {
502
+ this. record_statement_conflicts ( statement) ;
503
+
504
+ let loc = Location { block, statement_index : i } ;
503
505
init. seek_before_primary_effect ( loc) ;
504
506
505
- live_and_init_locals[ statement_index ] . overwrite ( init. get ( ) ) ;
507
+ live_and_init_locals[ i ] . overwrite ( init. get ( ) ) ;
506
508
}
507
509
510
+ this. record_terminator_conflicts ( data. terminator ( ) ) ;
511
+ let term_loc = Location { block, statement_index : data. statements . len ( ) } ;
512
+ init. seek_before_primary_effect ( term_loc) ;
513
+ live_and_init_locals[ term_loc. statement_index ] . overwrite ( init. get ( ) ) ;
514
+
508
515
// Now, go backwards and union with the liveness results.
509
516
for statement_index in ( 0 ..=data. statements . len ( ) ) . rev ( ) {
510
517
let loc = Location { block, statement_index } ;
@@ -514,7 +521,7 @@ impl Conflicts {
514
521
515
522
trace ! ( "record conflicts at {:?}" , loc) ;
516
523
517
- record_conflicts ( & mut live_and_init_locals[ statement_index] ) ;
524
+ this . record_conflicts ( & mut live_and_init_locals[ statement_index] ) ;
518
525
}
519
526
520
527
init. seek_to_block_end ( block) ;
@@ -523,10 +530,187 @@ impl Conflicts {
523
530
conflicts. intersect ( live. get ( ) ) ;
524
531
trace ! ( "record conflicts at end of {:?}" , block) ;
525
532
526
- record_conflicts ( & mut conflicts) ;
533
+ this. record_conflicts ( & mut conflicts) ;
534
+ }
535
+
536
+ this
537
+ }
538
+
539
+ fn record_conflicts ( & mut self , new_conflicts : & mut BitSet < Local > ) {
540
+ // Remove all locals that are not candidates.
541
+ new_conflicts. intersect ( self . relevant_locals ) ;
542
+
543
+ for local in new_conflicts. iter ( ) {
544
+ self . matrix . union_row_with ( & new_conflicts, local) ;
545
+ }
546
+ }
547
+
548
+ /// Records locals that must not overlap during the evaluation of `stmt`. These locals conflict
549
+ /// and must not be merged.
550
+ fn record_statement_conflicts ( & mut self , stmt : & Statement < ' _ > ) {
551
+ match & stmt. kind {
552
+ // While the left and right sides of an assignment must not overlap, we do not mark
553
+ // conflicts here as that would make this optimization useless. When we optimize, we
554
+ // eliminate the resulting self-assignments automatically.
555
+ StatementKind :: Assign ( _) => { }
556
+
557
+ StatementKind :: LlvmInlineAsm ( asm) => {
558
+ // Inputs and outputs must not overlap.
559
+ for ( _, input) in & * asm. inputs {
560
+ if let Some ( in_place) = input. place ( ) {
561
+ if !in_place. is_indirect ( ) {
562
+ for out_place in & * asm. outputs {
563
+ if !out_place. is_indirect ( ) && !in_place. is_indirect ( ) {
564
+ self . matrix . insert ( in_place. local , out_place. local ) ;
565
+ self . matrix . insert ( out_place. local , in_place. local ) ;
566
+ }
567
+ }
568
+ }
569
+ }
570
+ }
571
+ }
572
+
573
+ StatementKind :: SetDiscriminant { .. }
574
+ | StatementKind :: StorageLive ( _)
575
+ | StatementKind :: StorageDead ( _)
576
+ | StatementKind :: Retag ( _, _)
577
+ | StatementKind :: FakeRead ( _, _)
578
+ | StatementKind :: AscribeUserType ( _, _)
579
+ | StatementKind :: Nop => { }
527
580
}
581
+ }
528
582
529
- Self { matrix : conflicts, unify_cache : BitSet :: new_empty ( body. local_decls . len ( ) ) }
583
+ fn record_terminator_conflicts ( & mut self , term : & Terminator < ' _ > ) {
584
+ match & term. kind {
585
+ TerminatorKind :: DropAndReplace { location, value, target : _, unwind : _ } => {
586
+ if let Some ( place) = value. place ( ) {
587
+ if !place. is_indirect ( ) && !location. is_indirect ( ) {
588
+ self . matrix . insert ( place. local , location. local ) ;
589
+ self . matrix . insert ( location. local , place. local ) ;
590
+ }
591
+ }
592
+ }
593
+ TerminatorKind :: Yield { value, resume : _, resume_arg, drop : _ } => {
594
+ if let Some ( place) = value. place ( ) {
595
+ if !place. is_indirect ( ) && !resume_arg. is_indirect ( ) {
596
+ self . matrix . insert ( place. local , resume_arg. local ) ;
597
+ self . matrix . insert ( resume_arg. local , place. local ) ;
598
+ }
599
+ }
600
+ }
601
+ TerminatorKind :: Call {
602
+ func,
603
+ args,
604
+ destination : Some ( ( dest_place, _) ) ,
605
+ cleanup : _,
606
+ from_hir_call : _,
607
+ } => {
608
+ // No arguments may overlap with the destination.
609
+ for arg in args. iter ( ) . chain ( Some ( func) ) {
610
+ if let Some ( place) = arg. place ( ) {
611
+ if !place. is_indirect ( ) && !dest_place. is_indirect ( ) {
612
+ self . matrix . insert ( dest_place. local , place. local ) ;
613
+ self . matrix . insert ( place. local , dest_place. local ) ;
614
+ }
615
+ }
616
+ }
617
+ }
618
+ TerminatorKind :: InlineAsm {
619
+ template : _,
620
+ operands,
621
+ options : _,
622
+ line_spans : _,
623
+ destination : _,
624
+ } => {
625
+ // The intended semantics here aren't documented, we just assume that nothing that
626
+ // could be written to by the assembly may overlap with any other operands.
627
+ for op in operands {
628
+ match op {
629
+ InlineAsmOperand :: Out { reg : _, late : _, place : Some ( dest_place) }
630
+ | InlineAsmOperand :: InOut {
631
+ reg : _,
632
+ late : _,
633
+ in_value : _,
634
+ out_place : Some ( dest_place) ,
635
+ } => {
636
+ // For output place `place`, add all places accessed by the inline asm.
637
+ for op in operands {
638
+ match op {
639
+ InlineAsmOperand :: In { reg : _, value } => {
640
+ if let Some ( p) = value. place ( ) {
641
+ if !p. is_indirect ( ) && !dest_place. is_indirect ( ) {
642
+ self . matrix . insert ( p. local , dest_place. local ) ;
643
+ self . matrix . insert ( dest_place. local , p. local ) ;
644
+ }
645
+ }
646
+ }
647
+ InlineAsmOperand :: Out {
648
+ reg : _,
649
+ late : _,
650
+ place : Some ( place) ,
651
+ } => {
652
+ if !place. is_indirect ( ) && !dest_place. is_indirect ( ) {
653
+ self . matrix . insert ( place. local , dest_place. local ) ;
654
+ self . matrix . insert ( dest_place. local , place. local ) ;
655
+ }
656
+ }
657
+ InlineAsmOperand :: InOut {
658
+ reg : _,
659
+ late : _,
660
+ in_value,
661
+ out_place,
662
+ } => {
663
+ if let Some ( place) = in_value. place ( ) {
664
+ if !place. is_indirect ( ) && !dest_place. is_indirect ( ) {
665
+ self . matrix . insert ( place. local , dest_place. local ) ;
666
+ self . matrix . insert ( dest_place. local , place. local ) ;
667
+ }
668
+ }
669
+
670
+ if let Some ( place) = out_place {
671
+ if !place. is_indirect ( ) && !dest_place. is_indirect ( ) {
672
+ self . matrix . insert ( place. local , dest_place. local ) ;
673
+ self . matrix . insert ( dest_place. local , place. local ) ;
674
+ }
675
+ }
676
+ }
677
+ InlineAsmOperand :: Out { reg : _, late : _, place : None }
678
+ | InlineAsmOperand :: Const { value : _ }
679
+ | InlineAsmOperand :: SymFn { value : _ }
680
+ | InlineAsmOperand :: SymStatic { value : _ } => { }
681
+ }
682
+ }
683
+ }
684
+ InlineAsmOperand :: Const { value } => {
685
+ assert ! ( value. place( ) . is_none( ) ) ;
686
+ }
687
+ InlineAsmOperand :: InOut {
688
+ reg : _,
689
+ late : _,
690
+ in_value : _,
691
+ out_place : None ,
692
+ }
693
+ | InlineAsmOperand :: In { reg : _, value : _ }
694
+ | InlineAsmOperand :: Out { reg : _, late : _, place : None }
695
+ | InlineAsmOperand :: SymFn { value : _ }
696
+ | InlineAsmOperand :: SymStatic { value : _ } => { }
697
+ }
698
+ }
699
+ }
700
+
701
+ TerminatorKind :: Goto { .. }
702
+ | TerminatorKind :: Call { destination : None , .. }
703
+ | TerminatorKind :: SwitchInt { .. }
704
+ | TerminatorKind :: Resume
705
+ | TerminatorKind :: Abort
706
+ | TerminatorKind :: Return
707
+ | TerminatorKind :: Unreachable
708
+ | TerminatorKind :: Drop { .. }
709
+ | TerminatorKind :: Assert { .. }
710
+ | TerminatorKind :: GeneratorDrop
711
+ | TerminatorKind :: FalseEdges { .. }
712
+ | TerminatorKind :: FalseUnwind { .. } => { }
713
+ }
530
714
}
531
715
532
716
fn contains ( & self , a : Local , b : Local ) -> bool {
0 commit comments