@@ -34,7 +34,9 @@ use crate::interpret::{
34
34
} ;
35
35
use crate :: transform:: { MirPass , MirSource } ;
36
36
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.
38
40
const MAX_ALLOC_LIMIT : u64 = 1024 ;
39
41
40
42
/// Macro for machine-specific `InterpError` without allocation.
@@ -155,14 +157,19 @@ struct ConstPropMachine<'mir, 'tcx> {
155
157
written_only_inside_own_block_locals : FxHashSet < Local > ,
156
158
/// Locals that need to be cleared after every block terminates.
157
159
only_propagate_inside_block_locals : BitSet < Local > ,
160
+ can_const_prop : IndexVec < Local , ConstPropMode > ,
158
161
}
159
162
160
163
impl < ' 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 {
162
168
Self {
163
169
stack : Vec :: new ( ) ,
164
170
written_only_inside_own_block_locals : Default :: default ( ) ,
165
171
only_propagate_inside_block_locals,
172
+ can_const_prop,
166
173
}
167
174
}
168
175
}
@@ -241,6 +248,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
241
248
local : Local ,
242
249
) -> InterpResult < ' tcx , Result < & ' a mut LocalValue < Self :: PointerTag > , MemPlace < Self :: PointerTag > > >
243
250
{
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
+ }
244
254
if frame == 0 && ecx. machine . only_propagate_inside_block_locals . contains ( local) {
245
255
ecx. machine . written_only_inside_own_block_locals . insert ( local) ;
246
256
}
@@ -285,7 +295,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
285
295
struct ConstPropagator < ' mir , ' tcx > {
286
296
ecx : InterpCx < ' mir , ' tcx , ConstPropMachine < ' mir , ' tcx > > ,
287
297
tcx : TyCtxt < ' tcx > ,
288
- can_const_prop : IndexVec < Local , ConstPropMode > ,
289
298
param_env : ParamEnv < ' tcx > ,
290
299
// FIXME(eddyb) avoid cloning these two fields more than once,
291
300
// by accessing them through `ecx` instead.
@@ -331,7 +340,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
331
340
let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
332
341
333
342
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) ;
335
347
let mut only_propagate_inside_block_locals = BitSet :: new_empty ( can_const_prop. len ( ) ) ;
336
348
for ( l, mode) in can_const_prop. iter_enumerated ( ) {
337
349
if * mode == ConstPropMode :: OnlyInsideOwnBlock {
@@ -342,7 +354,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
342
354
tcx,
343
355
span,
344
356
param_env,
345
- ConstPropMachine :: new ( only_propagate_inside_block_locals) ,
357
+ ConstPropMachine :: new ( only_propagate_inside_block_locals, can_const_prop ) ,
346
358
( ) ,
347
359
) ;
348
360
@@ -368,7 +380,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
368
380
ecx,
369
381
tcx,
370
382
param_env,
371
- can_const_prop,
372
383
// FIXME(eddyb) avoid cloning these two fields more than once,
373
384
// by accessing them through `ecx` instead.
374
385
source_scopes : body. source_scopes . clone ( ) ,
@@ -612,15 +623,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
612
623
fn const_prop (
613
624
& mut self ,
614
625
rvalue : & Rvalue < ' tcx > ,
615
- place_layout : TyAndLayout < ' tcx > ,
616
626
source_info : SourceInfo ,
617
627
place : Place < ' tcx > ,
618
628
) -> 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
-
624
629
// Perform any special handling for specific Rvalue types.
625
630
// Generally, checks here fall into one of two categories:
626
631
// 1. Additional checking to provide useful lints to the user
@@ -893,7 +898,11 @@ struct CanConstProp {
893
898
894
899
impl CanConstProp {
895
900
/// 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 > {
897
906
let mut cpv = CanConstProp {
898
907
can_const_prop : IndexVec :: from_elem ( ConstPropMode :: FullConstProp , & body. local_decls ) ,
899
908
found_assignment : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
@@ -903,6 +912,16 @@ impl CanConstProp {
903
912
) ,
904
913
} ;
905
914
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
+ }
906
925
// Cannot use args at all
907
926
// Cannot use locals because if x < y { y - x } else { x - y } would
908
927
// lint for x != y
@@ -1018,61 +1037,52 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
1018
1037
let source_info = statement. source_info ;
1019
1038
self . source_info = Some ( source_info) ;
1020
1039
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) ;
1037
1053
}
1038
1054
}
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. \
1043
1060
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 ) ;
1052
1068
}
1053
- ConstPropMode :: FullConstProp => { }
1054
1069
}
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 => { }
1072
1071
}
1073
1072
} 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.
1074
1083
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" ,
1076
1086
place,
1077
1087
) ;
1078
1088
Self :: remove_const ( & mut self . ecx , place. local ) ;
0 commit comments