Skip to content

Commit 4d43423

Browse files
committed
Auto merge of #74821 - oli-obk:const_eval_read_uninit_fast_path, r=wesleywiser
Check whether locals are too large instead of whether accesses into them are too large Essentially this stops const prop from attempting to optimize ```rust let mut x = [0_u8; 5000]; x[42] = 3; ``` I don't expect this to be a perf improvement without #73656 (which is also where the lack of this PR will be a perf regression). r? @wesleywiser
2 parents 64f99b4 + 1864a97 commit 4d43423

8 files changed

+266
-90
lines changed

src/librustc_mir/transform/const_prop.rs

+70-60
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.
@@ -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

160163
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 {
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>
285295
struct 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

894899
impl 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);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
- // MIR for `main` before ConstProp
2+
+ // MIR for `main` after ConstProp
3+
4+
fn main() -> () {
5+
let mut _0: (); // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11
6+
let _1: u8; // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10
7+
let mut _2: [u8; 5000]; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29
8+
let _3: usize; // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31
9+
let mut _4: usize; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
10+
let mut _5: bool; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
11+
scope 1 {
12+
debug x => _1; // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10
13+
}
14+
15+
bb0: {
16+
StorageLive(_1); // scope 0 at $DIR/large_array_index.rs:6:9: 6:10
17+
StorageLive(_2); // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
18+
_2 = [const 0_u8; 5000]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
19+
// ty::Const
20+
// + ty: u8
21+
// + val: Value(Scalar(0x00))
22+
// mir::Constant
23+
// + span: $DIR/large_array_index.rs:6:18: 6:22
24+
// + literal: Const { ty: u8, val: Value(Scalar(0x00)) }
25+
StorageLive(_3); // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
26+
_3 = const 2_usize; // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
27+
// ty::Const
28+
// + ty: usize
29+
// + val: Value(Scalar(0x00000002))
30+
// mir::Constant
31+
// + span: $DIR/large_array_index.rs:6:30: 6:31
32+
// + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
33+
_4 = const 5000_usize; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
34+
// ty::Const
35+
// + ty: usize
36+
// + val: Value(Scalar(0x00001388))
37+
// mir::Constant
38+
// + span: $DIR/large_array_index.rs:6:17: 6:32
39+
// + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
40+
- _5 = Lt(_3, _4); // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
41+
- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
42+
+ _5 = const true; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
43+
+ // ty::Const
44+
+ // + ty: bool
45+
+ // + val: Value(Scalar(0x01))
46+
+ // mir::Constant
47+
+ // + span: $DIR/large_array_index.rs:6:17: 6:32
48+
+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
49+
+ assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
50+
+ // ty::Const
51+
+ // + ty: bool
52+
+ // + val: Value(Scalar(0x01))
53+
+ // mir::Constant
54+
+ // + span: $DIR/large_array_index.rs:6:17: 6:32
55+
+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
56+
+ // ty::Const
57+
+ // + ty: usize
58+
+ // + val: Value(Scalar(0x00001388))
59+
+ // mir::Constant
60+
+ // + span: $DIR/large_array_index.rs:6:17: 6:32
61+
+ // + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
62+
+ // ty::Const
63+
+ // + ty: usize
64+
+ // + val: Value(Scalar(0x00000002))
65+
+ // mir::Constant
66+
+ // + span: $DIR/large_array_index.rs:6:17: 6:32
67+
+ // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
68+
}
69+
70+
bb1: {
71+
_1 = _2[_3]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
72+
StorageDead(_3); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
73+
StorageDead(_2); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
74+
_0 = const (); // scope 0 at $DIR/large_array_index.rs:4:11: 7:2
75+
// ty::Const
76+
// + ty: ()
77+
// + val: Value(Scalar(<ZST>))
78+
// mir::Constant
79+
// + span: $DIR/large_array_index.rs:4:11: 7:2
80+
// + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
81+
StorageDead(_1); // scope 0 at $DIR/large_array_index.rs:7:1: 7:2
82+
return; // scope 0 at $DIR/large_array_index.rs:7:2: 7:2
83+
}
84+
}
85+

0 commit comments

Comments
 (0)