@@ -9,17 +9,14 @@ use rustc_const_eval::interpret::{
9
9
use rustc_data_structures:: fx:: FxHashSet ;
10
10
use rustc_hir:: def:: DefKind ;
11
11
use rustc_hir:: HirId ;
12
- use rustc_index:: bit_set:: BitSet ;
13
- use rustc_index:: { Idx , IndexVec } ;
14
- use rustc_middle:: mir:: visit:: Visitor ;
12
+ use rustc_index:: { bit_set:: BitSet , Idx , IndexVec } ;
13
+ use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
15
14
use rustc_middle:: mir:: * ;
16
15
use rustc_middle:: ty:: layout:: { LayoutError , LayoutOf , LayoutOfHelpers , TyAndLayout } ;
17
16
use rustc_middle:: ty:: { self , ConstInt , ParamEnv , ScalarInt , Ty , TyCtxt , TypeVisitableExt } ;
18
17
use rustc_span:: Span ;
19
18
use rustc_target:: abi:: { Abi , FieldIdx , HasDataLayout , Size , TargetDataLayout , VariantIdx } ;
20
19
21
- use crate :: const_prop:: CanConstProp ;
22
- use crate :: const_prop:: ConstPropMode ;
23
20
use crate :: dataflow_const_prop:: DummyMachine ;
24
21
use crate :: errors:: { AssertLint , AssertLintKind } ;
25
22
use crate :: MirLint ;
@@ -849,3 +846,128 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
849
846
}
850
847
}
851
848
}
849
+
850
+ /// The maximum number of bytes that we'll allocate space for a local or the return value.
851
+ /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
852
+ /// Severely regress performance.
853
+ const MAX_ALLOC_LIMIT : u64 = 1024 ;
854
+
855
+ /// The mode that `ConstProp` is allowed to run in for a given `Local`.
856
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
857
+ pub enum ConstPropMode {
858
+ /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
859
+ FullConstProp ,
860
+ /// The `Local` can only be propagated into and from its own block.
861
+ OnlyInsideOwnBlock ,
862
+ /// The `Local` cannot be part of propagation at all. Any statement
863
+ /// referencing it either for reading or writing will not get propagated.
864
+ NoPropagation ,
865
+ }
866
+
867
+ pub struct CanConstProp {
868
+ can_const_prop : IndexVec < Local , ConstPropMode > ,
869
+ // False at the beginning. Once set, no more assignments are allowed to that local.
870
+ found_assignment : BitSet < Local > ,
871
+ }
872
+
873
+ impl CanConstProp {
874
+ /// Returns true if `local` can be propagated
875
+ pub fn check < ' tcx > (
876
+ tcx : TyCtxt < ' tcx > ,
877
+ param_env : ParamEnv < ' tcx > ,
878
+ body : & Body < ' tcx > ,
879
+ ) -> IndexVec < Local , ConstPropMode > {
880
+ let mut cpv = CanConstProp {
881
+ can_const_prop : IndexVec :: from_elem ( ConstPropMode :: FullConstProp , & body. local_decls ) ,
882
+ found_assignment : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
883
+ } ;
884
+ for ( local, val) in cpv. can_const_prop . iter_enumerated_mut ( ) {
885
+ let ty = body. local_decls [ local] . ty ;
886
+ match tcx. layout_of ( param_env. and ( ty) ) {
887
+ Ok ( layout) if layout. size < Size :: from_bytes ( MAX_ALLOC_LIMIT ) => { }
888
+ // Either the layout fails to compute, then we can't use this local anyway
889
+ // or the local is too large, then we don't want to.
890
+ _ => {
891
+ * val = ConstPropMode :: NoPropagation ;
892
+ continue ;
893
+ }
894
+ }
895
+ }
896
+ // Consider that arguments are assigned on entry.
897
+ for arg in body. args_iter ( ) {
898
+ cpv. found_assignment . insert ( arg) ;
899
+ }
900
+ cpv. visit_body ( body) ;
901
+ cpv. can_const_prop
902
+ }
903
+ }
904
+
905
+ impl < ' tcx > Visitor < ' tcx > for CanConstProp {
906
+ fn visit_place ( & mut self , place : & Place < ' tcx > , mut context : PlaceContext , loc : Location ) {
907
+ use rustc_middle:: mir:: visit:: PlaceContext :: * ;
908
+
909
+ // Dereferencing just read the addess of `place.local`.
910
+ if place. projection . first ( ) == Some ( & PlaceElem :: Deref ) {
911
+ context = NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
912
+ }
913
+
914
+ self . visit_local ( place. local , context, loc) ;
915
+ self . visit_projection ( place. as_ref ( ) , context, loc) ;
916
+ }
917
+
918
+ fn visit_local ( & mut self , local : Local , context : PlaceContext , _: Location ) {
919
+ use rustc_middle:: mir:: visit:: PlaceContext :: * ;
920
+ match context {
921
+ // These are just stores, where the storing is not propagatable, but there may be later
922
+ // mutations of the same local via `Store`
923
+ | MutatingUse ( MutatingUseContext :: Call )
924
+ | MutatingUse ( MutatingUseContext :: AsmOutput )
925
+ | MutatingUse ( MutatingUseContext :: Deinit )
926
+ // Actual store that can possibly even propagate a value
927
+ | MutatingUse ( MutatingUseContext :: Store )
928
+ | MutatingUse ( MutatingUseContext :: SetDiscriminant ) => {
929
+ if !self . found_assignment . insert ( local) {
930
+ match & mut self . can_const_prop [ local] {
931
+ // If the local can only get propagated in its own block, then we don't have
932
+ // to worry about multiple assignments, as we'll nuke the const state at the
933
+ // end of the block anyway, and inside the block we overwrite previous
934
+ // states as applicable.
935
+ ConstPropMode :: OnlyInsideOwnBlock => { }
936
+ ConstPropMode :: NoPropagation => { }
937
+ other @ ConstPropMode :: FullConstProp => {
938
+ trace ! (
939
+ "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}" ,
940
+ local, other,
941
+ ) ;
942
+ * other = ConstPropMode :: OnlyInsideOwnBlock ;
943
+ }
944
+ }
945
+ }
946
+ }
947
+ // Reading constants is allowed an arbitrary number of times
948
+ NonMutatingUse ( NonMutatingUseContext :: Copy )
949
+ | NonMutatingUse ( NonMutatingUseContext :: Move )
950
+ | NonMutatingUse ( NonMutatingUseContext :: Inspect )
951
+ | NonMutatingUse ( NonMutatingUseContext :: PlaceMention )
952
+ | NonUse ( _) => { }
953
+
954
+ // These could be propagated with a smarter analysis or just some careful thinking about
955
+ // whether they'd be fine right now.
956
+ MutatingUse ( MutatingUseContext :: Yield )
957
+ | MutatingUse ( MutatingUseContext :: Drop )
958
+ | MutatingUse ( MutatingUseContext :: Retag )
959
+ // These can't ever be propagated under any scheme, as we can't reason about indirect
960
+ // mutation.
961
+ | NonMutatingUse ( NonMutatingUseContext :: SharedBorrow )
962
+ | NonMutatingUse ( NonMutatingUseContext :: FakeBorrow )
963
+ | NonMutatingUse ( NonMutatingUseContext :: AddressOf )
964
+ | MutatingUse ( MutatingUseContext :: Borrow )
965
+ | MutatingUse ( MutatingUseContext :: AddressOf ) => {
966
+ trace ! ( "local {:?} can't be propagated because it's used: {:?}" , local, context) ;
967
+ self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
968
+ }
969
+ MutatingUse ( MutatingUseContext :: Projection )
970
+ | NonMutatingUse ( NonMutatingUseContext :: Projection ) => bug ! ( "visit_place should not pass {context:?} for {local:?}" ) ,
971
+ }
972
+ }
973
+ }
0 commit comments