Skip to content

Commit c3454c0

Browse files
committed
Check whether locals are too large instead of whether accesses into them are too large
1 parent 8611e52 commit c3454c0

File tree

1 file changed

+60
-56
lines changed

1 file changed

+60
-56
lines changed

src/librustc_mir/transform/const_prop.rs

Lines changed: 60 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ use crate::interpret::{
3434
};
3535
use 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.
3840
const 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

894893
impl 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

Comments
 (0)