@@ -274,6 +274,8 @@ struct ConstPropagator<'mir, 'tcx> {
274
274
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
275
275
// the last known `SourceInfo` here and just keep revisiting it.
276
276
source_info : Option < SourceInfo > ,
277
+ // Locals we need to forget at the end of the current block
278
+ locals_of_current_block : BitSet < Local > ,
277
279
}
278
280
279
281
impl < ' mir , ' tcx > LayoutOf for ConstPropagator < ' mir , ' tcx > {
@@ -343,6 +345,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
343
345
//FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
344
346
local_decls : body. local_decls . clone ( ) ,
345
347
source_info : None ,
348
+ locals_of_current_block : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
346
349
}
347
350
}
348
351
@@ -357,8 +360,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
357
360
}
358
361
}
359
362
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] =
362
367
LocalState { value : LocalValue :: Uninitialized , layout : Cell :: new ( None ) } ;
363
368
}
364
369
@@ -389,6 +394,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
389
394
}
390
395
}
391
396
397
+ /// Returns the value, if any, of evaluating `c`.
392
398
fn eval_constant ( & mut self , c : & Constant < ' tcx > , source_info : SourceInfo ) -> Option < OpTy < ' tcx > > {
393
399
// FIXME we need to revisit this for #67176
394
400
if c. needs_subst ( ) {
@@ -429,11 +435,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
429
435
}
430
436
}
431
437
438
+ /// Returns the value, if any, of evaluating `place`.
432
439
fn eval_place ( & mut self , place : Place < ' tcx > ) -> Option < OpTy < ' tcx > > {
433
440
trace ! ( "eval_place(place={:?})" , place) ;
434
441
self . use_ecx ( |this| this. ecx . eval_place_to_op ( place, None ) )
435
442
}
436
443
444
+ /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
445
+ /// or `eval_place`, depending on the variant of `Operand` used.
437
446
fn eval_operand ( & mut self , op : & Operand < ' tcx > , source_info : SourceInfo ) -> Option < OpTy < ' tcx > > {
438
447
match * op {
439
448
Operand :: Constant ( ref c) => self . eval_constant ( c, source_info) ,
@@ -592,6 +601,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
592
601
} )
593
602
}
594
603
604
+ /// Creates a new `Operand::Constant` from a `Scalar` value
595
605
fn operand_from_scalar ( & self , scalar : Scalar , ty : Ty < ' tcx > , span : Span ) -> Operand < ' tcx > {
596
606
Operand :: Constant ( Box :: new ( Constant {
597
607
span,
@@ -637,6 +647,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
637
647
// Found a value represented as a pair. For now only do cont-prop if type of
638
648
// Rvalue is also a pair with two scalars. The more general case is more
639
649
// complicated to implement so we'll do it later.
650
+ // FIXME: implement the general case stated above ^.
640
651
let ty = & value. layout . ty . kind ;
641
652
// Only do it for tuples
642
653
if let ty:: Tuple ( substs) = ty {
@@ -673,6 +684,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
673
684
}
674
685
}
675
686
687
+ /// Returns `true` if and only if this `op` should be const-propagated into.
676
688
fn should_const_prop ( & mut self , op : OpTy < ' tcx > ) -> bool {
677
689
let mir_opt_level = self . tcx . sess . opts . debugging_opts . mir_opt_level ;
678
690
@@ -704,6 +716,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
704
716
enum ConstPropMode {
705
717
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
706
718
FullConstProp ,
719
+ /// The `Local` can only be propagated into and from its own block.
720
+ OnlyInsideOwnBlock ,
707
721
/// The `Local` can be propagated into but reads cannot be propagated.
708
722
OnlyPropagateInto ,
709
723
/// No propagation is allowed at all.
@@ -712,28 +726,41 @@ enum ConstPropMode {
712
726
713
727
struct CanConstProp {
714
728
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.
716
730
found_assignment : BitSet < Local > ,
731
+ // Cache of locals' information
732
+ local_kinds : IndexVec < Local , LocalKind > ,
717
733
}
718
734
719
735
impl CanConstProp {
720
- /// returns true if `local` can be propagated
736
+ /// Returns true if `local` can be propagated
721
737
fn check ( body : & Body < ' _ > ) -> IndexVec < Local , ConstPropMode > {
722
738
let mut cpv = CanConstProp {
723
739
can_const_prop : IndexVec :: from_elem ( ConstPropMode :: FullConstProp , & body. local_decls ) ,
724
740
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
+ ) ,
725
745
} ;
726
746
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
729
749
// lint for x != y
730
750
// FIXME(oli-obk): lint variables until they are used in a condition
731
751
// 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 {
735
753
* 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
+ ) ;
737
764
}
738
765
}
739
766
cpv. visit_body ( & body) ;
@@ -759,8 +786,12 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
759
786
| NonMutatingUse ( NonMutatingUseContext :: Move )
760
787
| NonMutatingUse ( NonMutatingUseContext :: Inspect )
761
788
| NonMutatingUse ( NonMutatingUseContext :: Projection )
762
- | MutatingUse ( MutatingUseContext :: Projection )
763
789
| NonUse ( _) => { }
790
+ MutatingUse ( MutatingUseContext :: Projection ) => {
791
+ if self . local_kinds [ local] != LocalKind :: Temp {
792
+ self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
793
+ }
794
+ }
764
795
_ => {
765
796
trace ! ( "local {:?} can't be propagaged because it's used: {:?}" , local, context) ;
766
797
self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
@@ -797,25 +828,35 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
797
828
if let Some ( local) = place. as_local ( ) {
798
829
let can_const_prop = self . can_const_prop [ local] ;
799
830
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.
803
834
if let Some ( value) = self . get_const ( local) {
804
835
if self . should_const_prop ( value) {
805
836
trace ! ( "replacing {:?} with {:?}" , rval, value) ;
806
837
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
+ {
809
841
trace ! ( "propagated into {:?}" , local) ;
810
842
}
811
843
}
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
+ }
812
851
}
813
852
}
814
853
}
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
+ {
816
857
trace ! ( "can't propagate into {:?}" , local) ;
817
858
if local != RETURN_PLACE {
818
- self . remove_const ( local) ;
859
+ Self :: remove_const ( & mut self . ecx , local) ;
819
860
}
820
861
}
821
862
}
@@ -850,11 +891,11 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
850
891
let expected = ScalarMaybeUndef :: from ( Scalar :: from_bool ( * expected) ) ;
851
892
let value_const = self . ecx . read_scalar ( value) . unwrap ( ) ;
852
893
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
854
895
// doesn't use the invalid value
855
896
match cond {
856
897
Operand :: Move ( ref place) | Operand :: Copy ( ref place) => {
857
- self . remove_const ( place. local ) ;
898
+ Self :: remove_const ( & mut self . ecx , place. local ) ;
858
899
}
859
900
Operand :: Constant ( _) => { }
860
901
}
@@ -916,7 +957,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
916
957
}
917
958
}
918
959
}
919
- //none of these have Operands to const-propagate
960
+ // None of these have Operands to const-propagate
920
961
TerminatorKind :: Goto { .. }
921
962
| TerminatorKind :: Resume
922
963
| TerminatorKind :: Abort
@@ -931,5 +972,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
931
972
//FIXME(wesleywiser) Call does have Operands that could be const-propagated
932
973
TerminatorKind :: Call { .. } => { }
933
974
}
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 ( ) ;
934
983
}
935
984
}
0 commit comments