@@ -34,7 +34,9 @@ use crate::interpret::{
3434} ;
3535use crate :: transform:: { MirPass , MirSource } ;
3636
37- /// The maximum number of bytes that we'll allocate space for a return value.
37+ /// The maximum number of bytes that we'll allocate space for a local or the return value.
38+ /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
39+ /// Severely regress performance.
3840const MAX_ALLOC_LIMIT : u64 = 1024 ;
3941
4042/// Macro for machine-specific `InterpError` without allocation.
@@ -331,7 +333,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
331333 let param_env = tcx. param_env ( def_id) . with_reveal_all ( ) ;
332334
333335 let span = tcx. def_span ( def_id) ;
334- let can_const_prop = CanConstProp :: check ( body) ;
336+ // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
337+ // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
338+ // `layout_of` query invocations.
339+ let can_const_prop = CanConstProp :: check ( tcx, param_env, body) ;
335340 let mut only_propagate_inside_block_locals = BitSet :: new_empty ( can_const_prop. len ( ) ) ;
336341 for ( l, mode) in can_const_prop. iter_enumerated ( ) {
337342 if * mode == ConstPropMode :: OnlyInsideOwnBlock {
@@ -612,15 +617,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
612617 fn const_prop (
613618 & mut self ,
614619 rvalue : & Rvalue < ' tcx > ,
615- place_layout : TyAndLayout < ' tcx > ,
616620 source_info : SourceInfo ,
617621 place : Place < ' tcx > ,
618622 ) -> Option < ( ) > {
619- // #66397: Don't try to eval into large places as that can cause an OOM
620- if place_layout. size >= Size :: from_bytes ( MAX_ALLOC_LIMIT ) {
621- return None ;
622- }
623-
624623 // Perform any special handling for specific Rvalue types.
625624 // Generally, checks here fall into one of two categories:
626625 // 1. Additional checking to provide useful lints to the user
@@ -893,7 +892,11 @@ struct CanConstProp {
893892
894893impl CanConstProp {
895894 /// Returns true if `local` can be propagated
896- fn check ( body : & Body < ' _ > ) -> IndexVec < Local , ConstPropMode > {
895+ fn check (
896+ tcx : TyCtxt < ' tcx > ,
897+ param_env : ParamEnv < ' tcx > ,
898+ body : & Body < ' tcx > ,
899+ ) -> IndexVec < Local , ConstPropMode > {
897900 let mut cpv = CanConstProp {
898901 can_const_prop : IndexVec :: from_elem ( ConstPropMode :: FullConstProp , & body. local_decls ) ,
899902 found_assignment : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
@@ -903,6 +906,16 @@ impl CanConstProp {
903906 ) ,
904907 } ;
905908 for ( local, val) in cpv. can_const_prop . iter_enumerated_mut ( ) {
909+ let ty = body. local_decls [ local] . ty ;
910+ match tcx. layout_of ( param_env. and ( ty) ) {
911+ Ok ( layout) if layout. size < Size :: from_bytes ( MAX_ALLOC_LIMIT ) => { }
912+ // Either the layout fails to compute, then we can't use this local anyway
913+ // or the local is too large, then we don't want to.
914+ _ => {
915+ * val = ConstPropMode :: NoPropagation ;
916+ continue ;
917+ }
918+ }
906919 // Cannot use args at all
907920 // Cannot use locals because if x < y { y - x } else { x - y } would
908921 // lint for x != y
@@ -1018,61 +1031,52 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
10181031 let source_info = statement. source_info ;
10191032 self . source_info = Some ( source_info) ;
10201033 if let StatementKind :: Assign ( box ( place, ref mut rval) ) = statement. kind {
1021- let place_ty: Ty < ' tcx > = place. ty ( & self . local_decls , self . tcx ) . ty ;
1022- if let Ok ( place_layout) = self . tcx . layout_of ( self . param_env . and ( place_ty) ) {
1023- let can_const_prop = self . can_const_prop [ place. local ] ;
1024- if let Some ( ( ) ) = self . const_prop ( rval, place_layout, source_info, place) {
1025- // This will return None if the above `const_prop` invocation only "wrote" a
1026- // type whose creation requires no write. E.g. a generator whose initial state
1027- // consists solely of uninitialized memory (so it doesn't capture any locals).
1028- if let Some ( value) = self . get_const ( place) {
1029- if self . should_const_prop ( value) {
1030- trace ! ( "replacing {:?} with {:?}" , rval, value) ;
1031- self . replace_with_const ( rval, value, source_info) ;
1032- if can_const_prop == ConstPropMode :: FullConstProp
1033- || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
1034- {
1035- trace ! ( "propagated into {:?}" , place) ;
1036- }
1034+ let can_const_prop = self . can_const_prop [ place. local ] ;
1035+ if let Some ( ( ) ) = self . const_prop ( rval, source_info, place) {
1036+ // This will return None if the above `const_prop` invocation only "wrote" a
1037+ // type whose creation requires no write. E.g. a generator whose initial state
1038+ // consists solely of uninitialized memory (so it doesn't capture any locals).
1039+ if let Some ( value) = self . get_const ( place) {
1040+ if self . should_const_prop ( value) {
1041+ trace ! ( "replacing {:?} with {:?}" , rval, value) ;
1042+ self . replace_with_const ( rval, value, source_info) ;
1043+ if can_const_prop == ConstPropMode :: FullConstProp
1044+ || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
1045+ {
1046+ trace ! ( "propagated into {:?}" , place) ;
10371047 }
10381048 }
1039- match can_const_prop {
1040- ConstPropMode :: OnlyInsideOwnBlock => {
1041- trace ! (
1042- "found local restricted to its block. \
1049+ }
1050+ match can_const_prop {
1051+ ConstPropMode :: OnlyInsideOwnBlock => {
1052+ trace ! (
1053+ "found local restricted to its block. \
10431054 Will remove it from const-prop after block is finished. Local: {:?}",
1044- place. local
1045- ) ;
1046- }
1047- ConstPropMode :: OnlyPropagateInto | ConstPropMode :: NoPropagation => {
1048- trace ! ( "can't propagate into {:?}" , place) ;
1049- if place. local != RETURN_PLACE {
1050- Self :: remove_const ( & mut self . ecx , place. local ) ;
1051- }
1055+ place. local
1056+ ) ;
1057+ }
1058+ ConstPropMode :: OnlyPropagateInto | ConstPropMode :: NoPropagation => {
1059+ trace ! ( "can't propagate into {:?}" , place) ;
1060+ if place. local != RETURN_PLACE {
1061+ Self :: remove_const ( & mut self . ecx , place. local ) ;
10521062 }
1053- ConstPropMode :: FullConstProp => { }
10541063 }
1055- } else {
1056- // Const prop failed, so erase the destination, ensuring that whatever happens
1057- // from here on, does not know about the previous value.
1058- // This is important in case we have
1059- // ```rust
1060- // let mut x = 42;
1061- // x = SOME_MUTABLE_STATIC;
1062- // // x must now be undefined
1063- // ```
1064- // FIXME: we overzealously erase the entire local, because that's easier to
1065- // implement.
1066- trace ! (
1067- "propagation into {:?} failed.
1068- Nuking the entire site from orbit, it's the only way to be sure" ,
1069- place,
1070- ) ;
1071- Self :: remove_const ( & mut self . ecx , place. local ) ;
1064+ ConstPropMode :: FullConstProp => { }
10721065 }
10731066 } else {
1067+ // Const prop failed, so erase the destination, ensuring that whatever happens
1068+ // from here on, does not know about the previous value.
1069+ // This is important in case we have
1070+ // ```rust
1071+ // let mut x = 42;
1072+ // x = SOME_MUTABLE_STATIC;
1073+ // // x must now be undefined
1074+ // ```
1075+ // FIXME: we overzealously erase the entire local, because that's easier to
1076+ // implement.
10741077 trace ! (
1075- "cannot propagate into {:?}, because the type of the local is generic." ,
1078+ "propagation into {:?} failed.
1079+ Nuking the entire site from orbit, it's the only way to be sure" ,
10761080 place,
10771081 ) ;
10781082 Self :: remove_const ( & mut self . ecx , place. local ) ;
0 commit comments