@@ -9,17 +9,14 @@ use rustc_const_eval::interpret::{
99use rustc_data_structures:: fx:: FxHashSet ;
1010use rustc_hir:: def:: DefKind ;
1111use 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 } ;
1514use rustc_middle:: mir:: * ;
1615use rustc_middle:: ty:: layout:: { LayoutError , LayoutOf , LayoutOfHelpers , TyAndLayout } ;
1716use rustc_middle:: ty:: { self , ConstInt , ParamEnv , ScalarInt , Ty , TyCtxt , TypeVisitableExt } ;
1817use rustc_span:: Span ;
1918use rustc_target:: abi:: { Abi , FieldIdx , HasDataLayout , Size , TargetDataLayout , VariantIdx } ;
2019
21- use crate :: const_prop:: CanConstProp ;
22- use crate :: const_prop:: ConstPropMode ;
2320use crate :: dataflow_const_prop:: DummyMachine ;
2421use crate :: errors:: { AssertLint , AssertLintKind } ;
2522use crate :: MirLint ;
@@ -849,3 +846,128 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
849846 }
850847 }
851848}
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