@@ -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.
@@ -155,14 +157,19 @@ struct ConstPropMachine<'mir, 'tcx> {
155157 written_only_inside_own_block_locals : FxHashSet < Local > ,
156158 /// Locals that need to be cleared after every block terminates.
157159 only_propagate_inside_block_locals : BitSet < Local > ,
160+ can_const_prop : IndexVec < Local , ConstPropMode > ,
158161}
159162
160163impl < ' mir , ' tcx > ConstPropMachine < ' mir , ' tcx > {
161- fn new ( only_propagate_inside_block_locals : BitSet < Local > ) -> Self {
164+ fn new (
165+ only_propagate_inside_block_locals : BitSet < Local > ,
166+ can_const_prop : IndexVec < Local , ConstPropMode > ,
167+ ) -> Self {
162168 Self {
163169 stack : Vec :: new ( ) ,
164170 written_only_inside_own_block_locals : Default :: default ( ) ,
165171 only_propagate_inside_block_locals,
172+ can_const_prop,
166173 }
167174 }
168175}
@@ -241,6 +248,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
241248 local : Local ,
242249 ) -> InterpResult < ' tcx , Result < & ' a mut LocalValue < Self :: PointerTag > , MemPlace < Self :: PointerTag > > >
243250 {
251+ if ecx. machine . can_const_prop [ local] == ConstPropMode :: NoPropagation {
252+ throw_machine_stop_str ! ( "tried to write to a local that is marked as not propagatable" )
253+ }
244254 if frame == 0 && ecx. machine . only_propagate_inside_block_locals . contains ( local) {
245255 ecx. machine . written_only_inside_own_block_locals . insert ( local) ;
246256 }
@@ -285,7 +295,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
285295struct ConstPropagator < ' mir , ' tcx > {
286296 ecx : InterpCx < ' mir , ' tcx , ConstPropMachine < ' mir , ' tcx > > ,
287297 tcx : TyCtxt < ' tcx > ,
288- can_const_prop : IndexVec < Local , ConstPropMode > ,
289298 param_env : ParamEnv < ' tcx > ,
290299 // FIXME(eddyb) avoid cloning these two fields more than once,
291300 // by accessing them through `ecx` instead.
@@ -331,7 +340,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
331340 let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
332341
333342 let span = tcx. def_span ( def_id) ;
334- let can_const_prop = CanConstProp :: check ( body) ;
343+ // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
344+ // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
345+ // `layout_of` query invocations.
346+ let can_const_prop = CanConstProp :: check ( tcx, param_env, body) ;
335347 let mut only_propagate_inside_block_locals = BitSet :: new_empty ( can_const_prop. len ( ) ) ;
336348 for ( l, mode) in can_const_prop. iter_enumerated ( ) {
337349 if * mode == ConstPropMode :: OnlyInsideOwnBlock {
@@ -342,7 +354,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
342354 tcx,
343355 span,
344356 param_env,
345- ConstPropMachine :: new ( only_propagate_inside_block_locals) ,
357+ ConstPropMachine :: new ( only_propagate_inside_block_locals, can_const_prop ) ,
346358 ( ) ,
347359 ) ;
348360
@@ -368,7 +380,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
368380 ecx,
369381 tcx,
370382 param_env,
371- can_const_prop,
372383 // FIXME(eddyb) avoid cloning these two fields more than once,
373384 // by accessing them through `ecx` instead.
374385 source_scopes : body. source_scopes . clone ( ) ,
@@ -612,15 +623,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
612623 fn const_prop (
613624 & mut self ,
614625 rvalue : & Rvalue < ' tcx > ,
615- place_layout : TyAndLayout < ' tcx > ,
616626 source_info : SourceInfo ,
617627 place : Place < ' tcx > ,
618628 ) -> 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-
624629 // Perform any special handling for specific Rvalue types.
625630 // Generally, checks here fall into one of two categories:
626631 // 1. Additional checking to provide useful lints to the user
@@ -893,7 +898,11 @@ struct CanConstProp {
893898
894899impl CanConstProp {
895900 /// Returns true if `local` can be propagated
896- fn check ( body : & Body < ' _ > ) -> IndexVec < Local , ConstPropMode > {
901+ fn check (
902+ tcx : TyCtxt < ' tcx > ,
903+ param_env : ParamEnv < ' tcx > ,
904+ body : & Body < ' tcx > ,
905+ ) -> IndexVec < Local , ConstPropMode > {
897906 let mut cpv = CanConstProp {
898907 can_const_prop : IndexVec :: from_elem ( ConstPropMode :: FullConstProp , & body. local_decls ) ,
899908 found_assignment : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
@@ -903,6 +912,16 @@ impl CanConstProp {
903912 ) ,
904913 } ;
905914 for ( local, val) in cpv. can_const_prop . iter_enumerated_mut ( ) {
915+ let ty = body. local_decls [ local] . ty ;
916+ match tcx. layout_of ( param_env. and ( ty) ) {
917+ Ok ( layout) if layout. size < Size :: from_bytes ( MAX_ALLOC_LIMIT ) => { }
918+ // Either the layout fails to compute, then we can't use this local anyway
919+ // or the local is too large, then we don't want to.
920+ _ => {
921+ * val = ConstPropMode :: NoPropagation ;
922+ continue ;
923+ }
924+ }
906925 // Cannot use args at all
907926 // Cannot use locals because if x < y { y - x } else { x - y } would
908927 // lint for x != y
@@ -1018,61 +1037,52 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
10181037 let source_info = statement. source_info ;
10191038 self . source_info = Some ( source_info) ;
10201039 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- }
1040+ let can_const_prop = self . ecx . machine . can_const_prop [ place. local ] ;
1041+ if let Some ( ( ) ) = self . const_prop ( rval, source_info, place) {
1042+ // This will return None if the above `const_prop` invocation only "wrote" a
1043+ // type whose creation requires no write. E.g. a generator whose initial state
1044+ // consists solely of uninitialized memory (so it doesn't capture any locals).
1045+ if let Some ( value) = self . get_const ( place) {
1046+ if self . should_const_prop ( value) {
1047+ trace ! ( "replacing {:?} with {:?}" , rval, value) ;
1048+ self . replace_with_const ( rval, value, source_info) ;
1049+ if can_const_prop == ConstPropMode :: FullConstProp
1050+ || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
1051+ {
1052+ trace ! ( "propagated into {:?}" , place) ;
10371053 }
10381054 }
1039- match can_const_prop {
1040- ConstPropMode :: OnlyInsideOwnBlock => {
1041- trace ! (
1042- "found local restricted to its block. \
1055+ }
1056+ match can_const_prop {
1057+ ConstPropMode :: OnlyInsideOwnBlock => {
1058+ trace ! (
1059+ "found local restricted to its block. \
10431060 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- }
1061+ place. local
1062+ ) ;
1063+ }
1064+ ConstPropMode :: OnlyPropagateInto | ConstPropMode :: NoPropagation => {
1065+ trace ! ( "can't propagate into {:?}" , place) ;
1066+ if place. local != RETURN_PLACE {
1067+ Self :: remove_const ( & mut self . ecx , place. local ) ;
10521068 }
1053- ConstPropMode :: FullConstProp => { }
10541069 }
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 ) ;
1070+ ConstPropMode :: FullConstProp => { }
10721071 }
10731072 } else {
1073+ // Const prop failed, so erase the destination, ensuring that whatever happens
1074+ // from here on, does not know about the previous value.
1075+ // This is important in case we have
1076+ // ```rust
1077+ // let mut x = 42;
1078+ // x = SOME_MUTABLE_STATIC;
1079+ // // x must now be undefined
1080+ // ```
1081+ // FIXME: we overzealously erase the entire local, because that's easier to
1082+ // implement.
10741083 trace ! (
1075- "cannot propagate into {:?}, because the type of the local is generic." ,
1084+ "propagation into {:?} failed.
1085+ Nuking the entire site from orbit, it's the only way to be sure" ,
10761086 place,
10771087 ) ;
10781088 Self :: remove_const ( & mut self . ecx , place. local ) ;
0 commit comments