140
140
use rustc_data_structures:: fx:: FxIndexMap ;
141
141
use rustc_index:: bit_set:: DenseBitSet ;
142
142
use rustc_index:: interval:: SparseIntervalMatrix ;
143
- use rustc_index:: { IndexSlice , IndexVec , newtype_index} ;
143
+ use rustc_index:: { IndexVec , newtype_index} ;
144
144
use rustc_middle:: mir:: visit:: { MutVisitor , NonMutatingUseContext , PlaceContext , Visitor } ;
145
145
use rustc_middle:: mir:: * ;
146
146
use rustc_middle:: ty:: TyCtxt ;
147
- use rustc_mir_dataflow:: impls:: DefUse ;
147
+ use rustc_mir_dataflow:: impls:: { DefUse , MaybeLiveLocals } ;
148
148
use rustc_mir_dataflow:: points:: { DenseLocationMap , PointIndex } ;
149
- use rustc_mir_dataflow:: { Analysis , Backward , Results } ;
149
+ use rustc_mir_dataflow:: { Analysis , Results } ;
150
150
use tracing:: { debug, trace} ;
151
151
152
152
pub ( super ) struct DestinationPropagation ;
@@ -169,12 +169,11 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
169
169
return ;
170
170
}
171
171
172
- let live =
173
- MaybeTwoStepLiveLocals . iterate_to_fixpoint ( tcx, body, Some ( "MaybeLiveLocals-DestProp" ) ) ;
172
+ let live = MaybeLiveLocals . iterate_to_fixpoint ( tcx, body, Some ( "MaybeLiveLocals-DestProp" ) ) ;
174
173
175
174
let points = DenseLocationMap :: new ( body) ;
176
175
let mut relevant = RelevantLocals :: compute ( & candidates, body. local_decls . len ( ) ) ;
177
- let mut live = save_as_intervals ( & points, body, & relevant. original , live. results ) ;
176
+ let mut live = save_as_intervals ( & points, body, & relevant, live. results ) ;
178
177
179
178
dest_prop_mir_dump ( tcx, body, & points, & live, & relevant) ;
180
179
@@ -515,8 +514,6 @@ fn dest_prop_mir_dump<'tcx>(
515
514
} ) ;
516
515
}
517
516
518
- struct MaybeTwoStepLiveLocals ;
519
-
520
517
#[ derive( Copy , Clone , Debug ) ]
521
518
enum Effect {
522
519
Before ,
@@ -567,137 +564,29 @@ where
567
564
}
568
565
}
569
566
570
- impl < ' tcx > Analysis < ' tcx > for MaybeTwoStepLiveLocals {
571
- type Domain = DenseBitSet < Local > ;
572
- type Direction = Backward ;
573
-
574
- const NAME : & ' static str = "transitive liveness" ;
575
-
576
- fn bottom_value ( & self , body : & Body < ' tcx > ) -> DenseBitSet < Local > {
577
- // bottom = not live
578
- DenseBitSet :: new_empty ( body. local_decls . len ( ) )
579
- }
580
-
581
- fn initialize_start_block ( & self , _: & Body < ' tcx > , _: & mut DenseBitSet < Local > ) {
582
- // No variables are live until we observe a use
583
- }
584
-
585
- // This happens between the previous statement and this one.
586
- #[ tracing:: instrument( level = "trace" , skip( self , statement) ) ]
587
- fn apply_primary_statement_effect (
588
- & mut self ,
589
- state : & mut DenseBitSet < Local > ,
590
- statement : & Statement < ' tcx > ,
591
- location : Location ,
592
- ) {
593
- VisitPlacesWith ( |place, ctxt| match DefUse :: for_place ( place, ctxt) {
594
- DefUse :: Def => {
595
- state. remove ( place. local ) ;
596
- }
597
- DefUse :: Use => {
598
- state. insert ( place. local ) ;
599
- }
600
- DefUse :: PartialWrite | DefUse :: NonUse => { }
601
- } )
602
- . visit_statement ( statement, location) ;
603
- }
604
-
605
- // This happens between this statement and the next one.
606
- #[ tracing:: instrument( level = "trace" , skip( self , statement) ) ]
607
- fn apply_early_statement_effect (
608
- & mut self ,
609
- state : & mut DenseBitSet < Local > ,
610
- statement : & Statement < ' tcx > ,
611
- location : Location ,
612
- ) {
613
- // We need to ensure we have a non-zero live range even for dead stores. This is done by
614
- // marking all the writes locals as live in the second half of the statement.
615
- VisitPlacesWith ( |place : Place < ' tcx > , ctxt| match DefUse :: for_place ( place, ctxt) {
616
- DefUse :: Def | DefUse :: PartialWrite => {
617
- state. insert ( place. local ) ;
618
- }
619
- // We already perform the reads in the first part of the statement. As statements are
620
- // not splittable, we do not need to re-read the same values.
621
- DefUse :: Use | DefUse :: NonUse => { }
622
- } )
623
- . visit_statement ( statement, location) ;
624
- }
625
-
626
- // We model terminator as a special case in this two-step analysis. Consider the terminator
627
- // `destination = func(arg0...)`.
628
- //
629
- // -- state at (location, Effect::Before)
630
- // read(arg0)...
631
- // write(destination)
632
- // -- state at (location, Effect::After)
633
- // read(arg0)...
634
-
635
- // This happens between the last statement and the terminator.
636
- #[ tracing:: instrument( level = "trace" , skip( self , terminator) ) ]
637
- fn apply_primary_terminator_effect < ' mir > (
638
- & mut self ,
639
- state : & mut DenseBitSet < Local > ,
640
- terminator : & ' mir Terminator < ' tcx > ,
641
- location : Location ,
642
- ) -> TerminatorEdges < ' mir , ' tcx > {
643
- // Consider that all writes in this terminator happen at the start of the execution of the
644
- // terminator. For instance if we pass a return-pointer to a `Call` terminator.
645
- VisitPlacesWith ( |place : Place < ' tcx > , ctxt| match DefUse :: for_place ( place, ctxt) {
646
- DefUse :: Def => {
647
- state. remove ( place. local ) ;
648
- }
649
- DefUse :: Use => {
650
- state. insert ( place. local ) ;
651
- }
652
- DefUse :: PartialWrite | DefUse :: NonUse => { }
653
- } )
654
- . visit_terminator ( terminator, location) ;
655
- terminator. edges ( )
656
- }
657
-
658
- // This happens between the terminator and the end of the block.
659
- #[ tracing:: instrument( level = "trace" , skip( self , terminator) ) ]
660
- fn apply_early_terminator_effect < ' mir > (
661
- & mut self ,
662
- state : & mut DenseBitSet < Local > ,
663
- terminator : & ' mir Terminator < ' tcx > ,
664
- location : Location ,
665
- ) {
666
- // Consider that all reads in this terminator happen at the end of the execution of the
667
- // terminator, even after it may have written to the destination local. For instance if we
668
- // pass arguments as pointers to a `Call` terminator.
669
- VisitPlacesWith ( |place : Place < ' tcx > , ctxt| match DefUse :: for_place ( place, ctxt) {
670
- DefUse :: Def | DefUse :: Use | DefUse :: PartialWrite => {
671
- state. insert ( place. local ) ;
672
- }
673
- DefUse :: NonUse => { }
674
- } )
675
- . visit_terminator ( terminator, location) ;
676
- }
677
- }
678
-
679
567
/// Add points depending on the result of the given dataflow analysis.
680
568
fn save_as_intervals < ' tcx > (
681
569
elements : & DenseLocationMap ,
682
570
body : & Body < ' tcx > ,
683
- relevant : & IndexSlice < RelevantLocal , Local > ,
571
+ relevant : & RelevantLocals ,
684
572
results : Results < DenseBitSet < Local > > ,
685
573
) -> SparseIntervalMatrix < RelevantLocal , TwoStepIndex > {
686
574
let mut values = SparseIntervalMatrix :: new ( 2 * elements. num_points ( ) ) ;
687
- let mut state = MaybeTwoStepLiveLocals . bottom_value ( body) ;
575
+ let mut state = MaybeLiveLocals . bottom_value ( body) ;
688
576
let reachable_blocks = traversal:: reachable_as_bitset ( body) ;
689
577
690
578
let two_step_loc = |location, effect| {
691
579
let point = elements. point_from_location ( location) ;
692
580
TwoStepIndex :: new ( point, effect)
693
581
} ;
694
- let mut prepend_at = |state : & mut DenseBitSet < Local > , twostep| {
695
- for ( relevant, & original) in relevant. iter_enumerated ( ) {
696
- if state. contains ( original) {
697
- values. prepend ( relevant, twostep) ;
582
+ let prepend_at =
583
+ |values : & mut SparseIntervalMatrix < _ , _ > , state : & DenseBitSet < Local > , twostep| {
584
+ for ( relevant, & original) in relevant. original . iter_enumerated ( ) {
585
+ if state. contains ( original) {
586
+ values. prepend ( relevant, twostep) ;
587
+ }
698
588
}
699
- }
700
- } ;
589
+ } ;
701
590
702
591
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
703
592
// allows to use the more efficient `prepend` method to interval sets.
@@ -713,25 +602,51 @@ fn save_as_intervals<'tcx>(
713
602
714
603
let term = block_data. terminator ( ) ;
715
604
let mut twostep = two_step_loc ( loc, Effect :: After ) ;
716
- MaybeTwoStepLiveLocals . apply_early_terminator_effect ( & mut state, term, loc) ;
717
- prepend_at ( & mut state, twostep) ;
605
+ prepend_at ( & mut values, & state, twostep) ;
606
+ // Ensure we have a non-zero live range even for dead stores. This is done by marking all
607
+ // the writes locals as live in the second half of the statement.
608
+ // We also ensure that operands read by terminators conflict with reads by that terminator.
609
+ // For instance a function call may read args after having written to the destination.
610
+ VisitPlacesWith ( |place, ctxt| match DefUse :: for_place ( place, ctxt) {
611
+ DefUse :: Def | DefUse :: Use | DefUse :: PartialWrite => {
612
+ if let Some ( relevant) = relevant. shrink [ place. local ] {
613
+ values. insert ( relevant, twostep) ;
614
+ }
615
+ }
616
+ DefUse :: NonUse => { }
617
+ } )
618
+ . visit_terminator ( term, loc) ;
718
619
719
620
twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
720
621
debug_assert_eq ! ( twostep, two_step_loc( loc, Effect :: Before ) ) ;
721
- MaybeTwoStepLiveLocals . apply_primary_terminator_effect ( & mut state, term, loc) ;
722
- prepend_at ( & mut state, twostep) ;
622
+ MaybeLiveLocals . apply_early_terminator_effect ( & mut state, term, loc) ;
623
+ MaybeLiveLocals . apply_primary_terminator_effect ( & mut state, term, loc) ;
624
+ prepend_at ( & mut values, & state, twostep) ;
723
625
724
626
for ( statement_index, stmt) in block_data. statements . iter ( ) . enumerate ( ) . rev ( ) {
725
627
let loc = Location { block, statement_index } ;
726
628
twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
727
629
debug_assert_eq ! ( twostep, two_step_loc( loc, Effect :: After ) ) ;
728
- MaybeTwoStepLiveLocals . apply_early_statement_effect ( & mut state, stmt, loc) ;
729
- prepend_at ( & mut state, twostep) ;
630
+ prepend_at ( & mut values, & state, twostep) ;
631
+ // Ensure we have a non-zero live range even for dead stores. This is done by marking
632
+ // all the writes locals as live in the second half of the statement.
633
+ VisitPlacesWith ( |place, ctxt| match DefUse :: for_place ( place, ctxt) {
634
+ DefUse :: Def | DefUse :: PartialWrite => {
635
+ if let Some ( relevant) = relevant. shrink [ place. local ] {
636
+ values. insert ( relevant, twostep) ;
637
+ }
638
+ }
639
+ DefUse :: Use | DefUse :: NonUse => { }
640
+ } )
641
+ . visit_statement ( stmt, loc) ;
730
642
731
643
twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
732
644
debug_assert_eq ! ( twostep, two_step_loc( loc, Effect :: Before ) ) ;
733
- MaybeTwoStepLiveLocals . apply_primary_statement_effect ( & mut state, stmt, loc) ;
734
- prepend_at ( & mut state, twostep) ;
645
+ MaybeLiveLocals . apply_early_statement_effect ( & mut state, stmt, loc) ;
646
+ MaybeLiveLocals . apply_primary_statement_effect ( & mut state, stmt, loc) ;
647
+ // ... but reads from operands are marked as live here so they do not conflict with
648
+ // the all the writes locals as live in the second half of the statement.
649
+ prepend_at ( & mut values, & state, twostep) ;
735
650
}
736
651
}
737
652
0 commit comments