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 ;
@@ -177,12 +177,11 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
177177 return ;
178178 }
179179
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" ) ) ;
182181
183182 let points = DenseLocationMap :: new ( body) ;
184183 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 ) ;
186185
187186 dest_prop_mir_dump ( tcx, body, & points, & live, & relevant) ;
188187
@@ -527,8 +526,6 @@ fn dest_prop_mir_dump<'tcx>(
527526 }
528527}
529528
530- struct MaybeTwoStepLiveLocals ;
531-
532529#[ derive( Copy , Clone , Debug ) ]
533530enum Effect {
534531 Before ,
@@ -579,137 +576,29 @@ where
579576 }
580577}
581578
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-
691579/// Add points depending on the result of the given dataflow analysis.
692580fn save_as_intervals < ' tcx > (
693581 elements : & DenseLocationMap ,
694582 body : & Body < ' tcx > ,
695- relevant : & IndexSlice < RelevantLocal , Local > ,
583+ relevant : & RelevantLocals ,
696584 results : Results < DenseBitSet < Local > > ,
697585) -> SparseIntervalMatrix < RelevantLocal , TwoStepIndex > {
698586 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) ;
700588 let reachable_blocks = traversal:: reachable_as_bitset ( body) ;
701589
702590 let two_step_loc = |location, effect| {
703591 let point = elements. point_from_location ( location) ;
704592 TwoStepIndex :: new ( point, effect)
705593 } ;
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+ }
710600 }
711- }
712- } ;
601+ } ;
713602
714603 // Iterate blocks in decreasing order, to visit locations in decreasing order. This
715604 // allows to use the more efficient `prepend` method to interval sets.
@@ -725,25 +614,51 @@ fn save_as_intervals<'tcx>(
725614
726615 let term = block_data. terminator ( ) ;
727616 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) ;
730631
731632 twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
732633 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) ;
735637
736638 for ( statement_index, stmt) in block_data. statements . iter ( ) . enumerate ( ) . rev ( ) {
737639 let loc = Location { block, statement_index } ;
738640 twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
739641 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) ;
742654
743655 twostep = TwoStepIndex :: from_u32 ( twostep. as_u32 ( ) - 1 ) ;
744656 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) ;
747662 }
748663 }
749664
0 commit comments