@@ -274,6 +274,8 @@ struct ConstPropagator<'mir, 'tcx> {
274274 // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
275275 // the last known `SourceInfo` here and just keep revisiting it.
276276 source_info : Option < SourceInfo > ,
277+ // Locals we need to forget at the end of the current block
278+ locals_of_current_block : BitSet < Local > ,
277279}
278280
279281impl < ' mir , ' tcx > LayoutOf for ConstPropagator < ' mir , ' tcx > {
@@ -343,6 +345,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
343345 //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
344346 local_decls : body. local_decls . clone ( ) ,
345347 source_info : None ,
348+ locals_of_current_block : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
346349 }
347350 }
348351
@@ -357,8 +360,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
357360 }
358361 }
359362
360- fn remove_const ( & mut self , local : Local ) {
361- self . ecx . frame_mut ( ) . locals [ local] =
363+ /// Remove `local` from the pool of `Locals`. Allows writing to them,
364+ /// but not reading from them anymore.
365+ fn remove_const ( ecx : & mut InterpCx < ' mir , ' tcx , ConstPropMachine < ' mir , ' tcx > > , local : Local ) {
366+ ecx. frame_mut ( ) . locals [ local] =
362367 LocalState { value : LocalValue :: Uninitialized , layout : Cell :: new ( None ) } ;
363368 }
364369
@@ -389,6 +394,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
389394 }
390395 }
391396
397+ /// Returns the value, if any, of evaluating `c`.
392398 fn eval_constant ( & mut self , c : & Constant < ' tcx > , source_info : SourceInfo ) -> Option < OpTy < ' tcx > > {
393399 // FIXME we need to revisit this for #67176
394400 if c. needs_subst ( ) {
@@ -429,11 +435,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
429435 }
430436 }
431437
438+ /// Returns the value, if any, of evaluating `place`.
432439 fn eval_place ( & mut self , place : Place < ' tcx > ) -> Option < OpTy < ' tcx > > {
433440 trace ! ( "eval_place(place={:?})" , place) ;
434441 self . use_ecx ( |this| this. ecx . eval_place_to_op ( place, None ) )
435442 }
436443
444+ /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
445+ /// or `eval_place`, depending on the variant of `Operand` used.
437446 fn eval_operand ( & mut self , op : & Operand < ' tcx > , source_info : SourceInfo ) -> Option < OpTy < ' tcx > > {
438447 match * op {
439448 Operand :: Constant ( ref c) => self . eval_constant ( c, source_info) ,
@@ -592,6 +601,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
592601 } )
593602 }
594603
604+ /// Creates a new `Operand::Constant` from a `Scalar` value
595605 fn operand_from_scalar ( & self , scalar : Scalar , ty : Ty < ' tcx > , span : Span ) -> Operand < ' tcx > {
596606 Operand :: Constant ( Box :: new ( Constant {
597607 span,
@@ -637,6 +647,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
637647 // Found a value represented as a pair. For now only do cont-prop if type of
638648 // Rvalue is also a pair with two scalars. The more general case is more
639649 // complicated to implement so we'll do it later.
650+ // FIXME: implement the general case stated above ^.
640651 let ty = & value. layout . ty . kind ;
641652 // Only do it for tuples
642653 if let ty:: Tuple ( substs) = ty {
@@ -673,6 +684,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
673684 }
674685 }
675686
687+ /// Returns `true` if and only if this `op` should be const-propagated into.
676688 fn should_const_prop ( & mut self , op : OpTy < ' tcx > ) -> bool {
677689 let mir_opt_level = self . tcx . sess . opts . debugging_opts . mir_opt_level ;
678690
@@ -704,6 +716,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
704716enum ConstPropMode {
705717 /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
706718 FullConstProp ,
719+ /// The `Local` can only be propagated into and from its own block.
720+ OnlyInsideOwnBlock ,
707721 /// The `Local` can be propagated into but reads cannot be propagated.
708722 OnlyPropagateInto ,
709723 /// No propagation is allowed at all.
@@ -712,28 +726,41 @@ enum ConstPropMode {
712726
713727struct CanConstProp {
714728 can_const_prop : IndexVec < Local , ConstPropMode > ,
715- // false at the beginning, once set, there are not allowed to be any more assignments
729+ // False at the beginning. Once set, no more assignments are allowed to that local.
716730 found_assignment : BitSet < Local > ,
731+ // Cache of locals' information
732+ local_kinds : IndexVec < Local , LocalKind > ,
717733}
718734
719735impl CanConstProp {
720- /// returns true if `local` can be propagated
736+ /// Returns true if `local` can be propagated
721737 fn check ( body : & Body < ' _ > ) -> IndexVec < Local , ConstPropMode > {
722738 let mut cpv = CanConstProp {
723739 can_const_prop : IndexVec :: from_elem ( ConstPropMode :: FullConstProp , & body. local_decls ) ,
724740 found_assignment : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
741+ local_kinds : IndexVec :: from_fn_n (
742+ |local| body. local_kind ( local) ,
743+ body. local_decls . len ( ) ,
744+ ) ,
725745 } ;
726746 for ( local, val) in cpv. can_const_prop . iter_enumerated_mut ( ) {
727- // cannot use args at all
728- // cannot use locals because if x < y { y - x } else { x - y } would
747+ // Cannot use args at all
748+ // Cannot use locals because if x < y { y - x } else { x - y } would
729749 // lint for x != y
730750 // FIXME(oli-obk): lint variables until they are used in a condition
731751 // FIXME(oli-obk): lint if return value is constant
732- let local_kind = body. local_kind ( local) ;
733-
734- if local_kind == LocalKind :: Arg || local_kind == LocalKind :: Var {
752+ if cpv. local_kinds [ local] == LocalKind :: Arg {
735753 * val = ConstPropMode :: OnlyPropagateInto ;
736- trace ! ( "local {:?} can't be const propagated because it's not a temporary" , local) ;
754+ trace ! (
755+ "local {:?} can't be const propagated because it's a function argument" ,
756+ local
757+ ) ;
758+ } else if cpv. local_kinds [ local] == LocalKind :: Var {
759+ * val = ConstPropMode :: OnlyInsideOwnBlock ;
760+ trace ! (
761+ "local {:?} will only be propagated inside its block, because it's a user variable" ,
762+ local
763+ ) ;
737764 }
738765 }
739766 cpv. visit_body ( & body) ;
@@ -759,8 +786,12 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
759786 | NonMutatingUse ( NonMutatingUseContext :: Move )
760787 | NonMutatingUse ( NonMutatingUseContext :: Inspect )
761788 | NonMutatingUse ( NonMutatingUseContext :: Projection )
762- | MutatingUse ( MutatingUseContext :: Projection )
763789 | NonUse ( _) => { }
790+ MutatingUse ( MutatingUseContext :: Projection ) => {
791+ if self . local_kinds [ local] != LocalKind :: Temp {
792+ self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
793+ }
794+ }
764795 _ => {
765796 trace ! ( "local {:?} can't be propagaged because it's used: {:?}" , local, context) ;
766797 self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
@@ -797,25 +828,35 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
797828 if let Some ( local) = place. as_local ( ) {
798829 let can_const_prop = self . can_const_prop [ local] ;
799830 if let Some ( ( ) ) = self . const_prop ( rval, place_layout, source_info, place) {
800- if can_const_prop == ConstPropMode :: FullConstProp
801- || can_const_prop == ConstPropMode :: OnlyPropagateInto
802- {
831+ if can_const_prop != ConstPropMode :: NoPropagation {
832+ // This will return None for Locals that are from other blocks,
833+ // so it should be okay to propagate from here on down.
803834 if let Some ( value) = self . get_const ( local) {
804835 if self . should_const_prop ( value) {
805836 trace ! ( "replacing {:?} with {:?}" , rval, value) ;
806837 self . replace_with_const ( rval, value, statement. source_info ) ;
807-
808- if can_const_prop == ConstPropMode :: FullConstProp {
838+ if can_const_prop == ConstPropMode :: FullConstProp
839+ || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
840+ {
809841 trace ! ( "propagated into {:?}" , local) ;
810842 }
811843 }
844+ if can_const_prop == ConstPropMode :: OnlyInsideOwnBlock {
845+ trace ! (
846+ "found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}" ,
847+ local
848+ ) ;
849+ self . locals_of_current_block . insert ( local) ;
850+ }
812851 }
813852 }
814853 }
815- if self . can_const_prop [ local] != ConstPropMode :: FullConstProp {
854+ if self . can_const_prop [ local] == ConstPropMode :: OnlyPropagateInto
855+ || self . can_const_prop [ local] == ConstPropMode :: NoPropagation
856+ {
816857 trace ! ( "can't propagate into {:?}" , local) ;
817858 if local != RETURN_PLACE {
818- self . remove_const ( local) ;
859+ Self :: remove_const ( & mut self . ecx , local) ;
819860 }
820861 }
821862 }
@@ -850,11 +891,11 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
850891 let expected = ScalarMaybeUndef :: from ( Scalar :: from_bool ( * expected) ) ;
851892 let value_const = self . ecx . read_scalar ( value) . unwrap ( ) ;
852893 if expected != value_const {
853- // poison all places this operand references so that further code
894+ // Poison all places this operand references so that further code
854895 // doesn't use the invalid value
855896 match cond {
856897 Operand :: Move ( ref place) | Operand :: Copy ( ref place) => {
857- self . remove_const ( place. local ) ;
898+ Self :: remove_const ( & mut self . ecx , place. local ) ;
858899 }
859900 Operand :: Constant ( _) => { }
860901 }
@@ -916,7 +957,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
916957 }
917958 }
918959 }
919- //none of these have Operands to const-propagate
960+ // None of these have Operands to const-propagate
920961 TerminatorKind :: Goto { .. }
921962 | TerminatorKind :: Resume
922963 | TerminatorKind :: Abort
@@ -931,5 +972,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
931972 //FIXME(wesleywiser) Call does have Operands that could be const-propagated
932973 TerminatorKind :: Call { .. } => { }
933974 }
975+ // We remove all Locals which are restricted in propagation to their containing blocks.
976+ // We wouldn't need to clone, but the borrow checker can't see that we're not aliasing
977+ // the locals_of_current_block field, so we need to clone it first.
978+ // let ecx = &mut self.ecx;
979+ for local in self . locals_of_current_block . iter ( ) {
980+ Self :: remove_const ( & mut self . ecx , local) ;
981+ }
982+ self . locals_of_current_block . clear ( ) ;
934983 }
935984}
0 commit comments