140140use rustc_data_structures:: fx:: FxIndexMap ;
141141use rustc_index:: bit_set:: DenseBitSet ;
142142use rustc_index:: interval:: SparseIntervalMatrix ;
143- use rustc_index:: { IndexSlice , IndexVec , newtype_index} ;
143+ use rustc_index:: { IndexVec , newtype_index} ;
144144use rustc_middle:: mir:: visit:: { MutVisitor , NonMutatingUseContext , PlaceContext , Visitor } ;
145145use rustc_middle:: mir:: * ;
146146use rustc_middle:: ty:: TyCtxt ;
147- use rustc_mir_dataflow:: impls:: DefUse ;
147+ use rustc_mir_dataflow:: impls:: { DefUse , MaybeLiveLocals } ;
148148use rustc_mir_dataflow:: points:: { DenseLocationMap , PointIndex } ;
149- use rustc_mir_dataflow:: { Analysis , Backward , Results } ;
149+ use rustc_mir_dataflow:: { Analysis , Results } ;
150150use tracing:: { debug, trace} ;
151151
152152pub ( super ) struct DestinationPropagation ;
@@ -169,12 +169,11 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
169169 return ;
170170 }
171171
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" ) ) ;
174173
175174 let points = DenseLocationMap :: new ( body) ;
176175 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 ) ;
178177
179178 dest_prop_mir_dump ( tcx, body, & points, & live, & relevant) ;
180179
@@ -515,8 +514,6 @@ fn dest_prop_mir_dump<'tcx>(
515514 } ) ;
516515}
517516
518- struct MaybeTwoStepLiveLocals ;
519-
520517#[ derive( Copy , Clone , Debug ) ]
521518enum Effect {
522519 Before ,
@@ -567,137 +564,29 @@ where
567564 }
568565}
569566
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-
679567/// Add points depending on the result of the given dataflow analysis.
680568fn save_as_intervals < ' tcx > (
681569 elements : & DenseLocationMap ,
682570 body : & Body < ' tcx > ,
683- relevant : & IndexSlice < RelevantLocal , Local > ,
571+ relevant : & RelevantLocals ,
684572 results : Results < DenseBitSet < Local > > ,
685573) -> SparseIntervalMatrix < RelevantLocal , TwoStepIndex > {
686574 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) ;
688576 let reachable_blocks = traversal:: reachable_as_bitset ( body) ;
689577
690578 let two_step_loc = |location, effect| {
691579 let point = elements. point_from_location ( location) ;
692580 TwoStepIndex :: new ( point, effect)
693581 } ;
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+ }
698588 }
699- }
700- } ;
589+ } ;
701590
702591 // Iterate blocks in decreasing order, to visit locations in decreasing order. This
703592 // allows to use the more efficient `prepend` method to interval sets.
@@ -713,25 +602,51 @@ fn save_as_intervals<'tcx>(
713602
714603 let term = block_data. terminator ( ) ;
715604 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 written-to locals as live in the second half of the statement.
608+ // We also ensure that operands read by terminators conflict with writes 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) ;
718619
719620 twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
720621 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) ;
723625
724626 for ( statement_index, stmt) in block_data. statements . iter ( ) . enumerate ( ) . rev ( ) {
725627 let loc = Location { block, statement_index } ;
726628 twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
727629 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 written-to 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) ;
730642
731643 twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
732644 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 we manually marked as live in the second half of the statement.
649+ prepend_at ( & mut values, & state, twostep) ;
735650 }
736651 }
737652
0 commit comments