@@ -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.
@@ -331,7 +333,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
331
333
let param_env = tcx. param_env ( def_id) . with_reveal_all ( ) ;
332
334
333
335
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) ;
335
340
let mut only_propagate_inside_block_locals = BitSet :: new_empty ( can_const_prop. len ( ) ) ;
336
341
for ( l, mode) in can_const_prop. iter_enumerated ( ) {
337
342
if * mode == ConstPropMode :: OnlyInsideOwnBlock {
@@ -612,15 +617,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
612
617
fn const_prop (
613
618
& mut self ,
614
619
rvalue : & Rvalue < ' tcx > ,
615
- place_layout : TyAndLayout < ' tcx > ,
616
620
source_info : SourceInfo ,
617
621
place : Place < ' tcx > ,
618
622
) -> 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
623
// Perform any special handling for specific Rvalue types.
625
624
// Generally, checks here fall into one of two categories:
626
625
// 1. Additional checking to provide useful lints to the user
@@ -893,7 +892,11 @@ struct CanConstProp {
893
892
894
893
impl CanConstProp {
895
894
/// 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 > {
897
900
let mut cpv = CanConstProp {
898
901
can_const_prop : IndexVec :: from_elem ( ConstPropMode :: FullConstProp , & body. local_decls ) ,
899
902
found_assignment : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
@@ -903,6 +906,16 @@ impl CanConstProp {
903
906
) ,
904
907
} ;
905
908
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
+ }
906
919
// Cannot use args at all
907
920
// Cannot use locals because if x < y { y - x } else { x - y } would
908
921
// lint for x != y
@@ -1018,61 +1031,52 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
1018
1031
let source_info = statement. source_info ;
1019
1032
self . source_info = Some ( source_info) ;
1020
1033
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) ;
1037
1047
}
1038
1048
}
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. \
1043
1054
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 ) ;
1052
1062
}
1053
- ConstPropMode :: FullConstProp => { }
1054
1063
}
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 => { }
1072
1065
}
1073
1066
} 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.
1074
1077
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" ,
1076
1080
place,
1077
1081
) ;
1078
1082
Self :: remove_const ( & mut self . ecx , place. local ) ;
0 commit comments