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 ;
@@ -177,12 +177,11 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
177
177
return ;
178
178
}
179
179
180
- let live =
181
- MaybeTwoStepLiveLocals . iterate_to_fixpoint ( tcx, body, Some ( "MaybeLiveLocals-DestProp" ) ) ;
180
+ let live = MaybeLiveLocals . iterate_to_fixpoint ( tcx, body, Some ( "MaybeLiveLocals-DestProp" ) ) ;
182
181
183
182
let points = DenseLocationMap :: new ( body) ;
184
183
let mut relevant = RelevantLocals :: compute ( & candidates, body. local_decls . len ( ) ) ;
185
- let mut live = save_as_intervals ( & points, body, & relevant. original , live. results ) ;
184
+ let mut live = save_as_intervals ( & points, body, & relevant, live. results ) ;
186
185
187
186
dest_prop_mir_dump ( tcx, body, & points, & live, & relevant) ;
188
187
@@ -527,8 +526,6 @@ fn dest_prop_mir_dump<'tcx>(
527
526
}
528
527
}
529
528
530
- struct MaybeTwoStepLiveLocals ;
531
-
532
529
#[ derive( Copy , Clone , Debug ) ]
533
530
enum Effect {
534
531
Before ,
@@ -579,137 +576,29 @@ where
579
576
}
580
577
}
581
578
582
- impl < ' tcx > Analysis < ' tcx > for MaybeTwoStepLiveLocals {
583
- type Domain = DenseBitSet < Local > ;
584
- type Direction = Backward ;
585
-
586
- const NAME : & ' static str = "transitive liveness" ;
587
-
588
- fn bottom_value ( & self , body : & Body < ' tcx > ) -> DenseBitSet < Local > {
589
- // bottom = not live
590
- DenseBitSet :: new_empty ( body. local_decls . len ( ) )
591
- }
592
-
593
- fn initialize_start_block ( & self , _: & Body < ' tcx > , _: & mut DenseBitSet < Local > ) {
594
- // No variables are live until we observe a use
595
- }
596
-
597
- // This happens between the previous statement and this one.
598
- #[ tracing:: instrument( level = "trace" , skip( self , statement) ) ]
599
- fn apply_primary_statement_effect (
600
- & mut self ,
601
- state : & mut DenseBitSet < Local > ,
602
- statement : & Statement < ' tcx > ,
603
- location : Location ,
604
- ) {
605
- VisitPlacesWith ( |place, ctxt| match DefUse :: for_place ( place, ctxt) {
606
- DefUse :: Def => {
607
- state. remove ( place. local ) ;
608
- }
609
- DefUse :: Use => {
610
- state. insert ( place. local ) ;
611
- }
612
- DefUse :: PartialWrite | DefUse :: NonUse => { }
613
- } )
614
- . visit_statement ( statement, location) ;
615
- }
616
-
617
- // This happens between this statement and the next one.
618
- #[ tracing:: instrument( level = "trace" , skip( self , statement) ) ]
619
- fn apply_early_statement_effect (
620
- & mut self ,
621
- state : & mut DenseBitSet < Local > ,
622
- statement : & Statement < ' tcx > ,
623
- location : Location ,
624
- ) {
625
- // We need to ensure we have a non-zero live range even for dead stores. This is done by
626
- // marking all the writes locals as live in the second half of the statement.
627
- VisitPlacesWith ( |place : Place < ' tcx > , ctxt| match DefUse :: for_place ( place, ctxt) {
628
- DefUse :: Def | DefUse :: PartialWrite => {
629
- state. insert ( place. local ) ;
630
- }
631
- // We already perform the reads in the first part of the statement. As statements are
632
- // not splittable, we do not need to re-read the same values.
633
- DefUse :: Use | DefUse :: NonUse => { }
634
- } )
635
- . visit_statement ( statement, location) ;
636
- }
637
-
638
- // We model terminator as a special case in this two-step analysis. Consider the terminator
639
- // `destination = func(arg0...)`.
640
- //
641
- // -- state at (location, Effect::Before)
642
- // read(arg0)...
643
- // write(destination)
644
- // -- state at (location, Effect::After)
645
- // read(arg0)...
646
-
647
- // This happens between the last statement and the terminator.
648
- #[ tracing:: instrument( level = "trace" , skip( self , terminator) ) ]
649
- fn apply_primary_terminator_effect < ' mir > (
650
- & mut self ,
651
- state : & mut DenseBitSet < Local > ,
652
- terminator : & ' mir Terminator < ' tcx > ,
653
- location : Location ,
654
- ) -> TerminatorEdges < ' mir , ' tcx > {
655
- // Consider that all writes in this terminator happen at the start of the execution of the
656
- // terminator. For instance if we pass a return-pointer to a `Call` terminator.
657
- VisitPlacesWith ( |place : Place < ' tcx > , ctxt| match DefUse :: for_place ( place, ctxt) {
658
- DefUse :: Def => {
659
- state. remove ( place. local ) ;
660
- }
661
- DefUse :: Use => {
662
- state. insert ( place. local ) ;
663
- }
664
- DefUse :: PartialWrite | DefUse :: NonUse => { }
665
- } )
666
- . visit_terminator ( terminator, location) ;
667
- terminator. edges ( )
668
- }
669
-
670
- // This happens between the terminator and the end of the block.
671
- #[ tracing:: instrument( level = "trace" , skip( self , terminator) ) ]
672
- fn apply_early_terminator_effect < ' mir > (
673
- & mut self ,
674
- state : & mut DenseBitSet < Local > ,
675
- terminator : & ' mir Terminator < ' tcx > ,
676
- location : Location ,
677
- ) {
678
- // Consider that all reads in this terminator happen at the end of the execution of the
679
- // terminator, even after it may have written to the destination local. For instance if we
680
- // pass arguments as pointers to a `Call` terminator.
681
- VisitPlacesWith ( |place : Place < ' tcx > , ctxt| match DefUse :: for_place ( place, ctxt) {
682
- DefUse :: Def | DefUse :: Use | DefUse :: PartialWrite => {
683
- state. insert ( place. local ) ;
684
- }
685
- DefUse :: NonUse => { }
686
- } )
687
- . visit_terminator ( terminator, location) ;
688
- }
689
- }
690
-
691
579
/// Add points depending on the result of the given dataflow analysis.
692
580
fn save_as_intervals < ' tcx > (
693
581
elements : & DenseLocationMap ,
694
582
body : & Body < ' tcx > ,
695
- relevant : & IndexSlice < RelevantLocal , Local > ,
583
+ relevant : & RelevantLocals ,
696
584
results : Results < DenseBitSet < Local > > ,
697
585
) -> SparseIntervalMatrix < RelevantLocal , TwoStepIndex > {
698
586
let mut values = SparseIntervalMatrix :: new ( 2 * elements. num_points ( ) ) ;
699
- let mut state = MaybeTwoStepLiveLocals . bottom_value ( body) ;
587
+ let mut state = MaybeLiveLocals . bottom_value ( body) ;
700
588
let reachable_blocks = traversal:: reachable_as_bitset ( body) ;
701
589
702
590
let two_step_loc = |location, effect| {
703
591
let point = elements. point_from_location ( location) ;
704
592
TwoStepIndex :: new ( point, effect)
705
593
} ;
706
- let mut prepend_at = |state : & mut DenseBitSet < Local > , twostep| {
707
- for ( relevant, & original) in relevant. iter_enumerated ( ) {
708
- if state. contains ( original) {
709
- values. prepend ( relevant, twostep) ;
594
+ let prepend_at =
595
+ |values : & mut SparseIntervalMatrix < _ , _ > , state : & DenseBitSet < Local > , twostep| {
596
+ for ( relevant, & original) in relevant. original . iter_enumerated ( ) {
597
+ if state. contains ( original) {
598
+ values. prepend ( relevant, twostep) ;
599
+ }
710
600
}
711
- }
712
- } ;
601
+ } ;
713
602
714
603
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
715
604
// allows to use the more efficient `prepend` method to interval sets.
@@ -725,25 +614,51 @@ fn save_as_intervals<'tcx>(
725
614
726
615
let term = block_data. terminator ( ) ;
727
616
let mut twostep = two_step_loc ( loc, Effect :: After ) ;
728
- MaybeTwoStepLiveLocals . apply_early_terminator_effect ( & mut state, term, loc) ;
729
- prepend_at ( & mut state, twostep) ;
617
+ prepend_at ( & mut values, & state, twostep) ;
618
+ // Ensure we have a non-zero live range even for dead stores. This is done by marking all
619
+ // the written-to locals as live in the second half of the statement.
620
+ // We also ensure that operands read by terminators conflict with writes by that terminator.
621
+ // For instance a function call may read args after having written to the destination.
622
+ VisitPlacesWith ( |place, ctxt| match DefUse :: for_place ( place, ctxt) {
623
+ DefUse :: Def | DefUse :: Use | DefUse :: PartialWrite => {
624
+ if let Some ( relevant) = relevant. shrink [ place. local ] {
625
+ values. insert ( relevant, twostep) ;
626
+ }
627
+ }
628
+ DefUse :: NonUse => { }
629
+ } )
630
+ . visit_terminator ( term, loc) ;
730
631
731
632
twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
732
633
debug_assert_eq ! ( twostep, two_step_loc( loc, Effect :: Before ) ) ;
733
- MaybeTwoStepLiveLocals . apply_primary_terminator_effect ( & mut state, term, loc) ;
734
- prepend_at ( & mut state, twostep) ;
634
+ MaybeLiveLocals . apply_early_terminator_effect ( & mut state, term, loc) ;
635
+ MaybeLiveLocals . apply_primary_terminator_effect ( & mut state, term, loc) ;
636
+ prepend_at ( & mut values, & state, twostep) ;
735
637
736
638
for ( statement_index, stmt) in block_data. statements . iter ( ) . enumerate ( ) . rev ( ) {
737
639
let loc = Location { block, statement_index } ;
738
640
twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
739
641
debug_assert_eq ! ( twostep, two_step_loc( loc, Effect :: After ) ) ;
740
- MaybeTwoStepLiveLocals . apply_early_statement_effect ( & mut state, stmt, loc) ;
741
- prepend_at ( & mut state, twostep) ;
642
+ prepend_at ( & mut values, & state, twostep) ;
643
+ // Ensure we have a non-zero live range even for dead stores. This is done by marking
644
+ // all the written-to locals as live in the second half of the statement.
645
+ VisitPlacesWith ( |place, ctxt| match DefUse :: for_place ( place, ctxt) {
646
+ DefUse :: Def | DefUse :: PartialWrite => {
647
+ if let Some ( relevant) = relevant. shrink [ place. local ] {
648
+ values. insert ( relevant, twostep) ;
649
+ }
650
+ }
651
+ DefUse :: Use | DefUse :: NonUse => { }
652
+ } )
653
+ . visit_statement ( stmt, loc) ;
742
654
743
655
twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
744
656
debug_assert_eq ! ( twostep, two_step_loc( loc, Effect :: Before ) ) ;
745
- MaybeTwoStepLiveLocals . apply_primary_statement_effect ( & mut state, stmt, loc) ;
746
- prepend_at ( & mut state, twostep) ;
657
+ MaybeLiveLocals . apply_early_statement_effect ( & mut state, stmt, loc) ;
658
+ MaybeLiveLocals . apply_primary_statement_effect ( & mut state, stmt, loc) ;
659
+ // ... but reads from operands are marked as live here so they do not conflict with
660
+ // the all the writes we manually marked as live in the second half of the statement.
661
+ prepend_at ( & mut values, & state, twostep) ;
747
662
}
748
663
}
749
664
0 commit comments