Skip to content

Commit 3fe4dd2

Browse files
committed
Auto merge of #71953 - oli-obk:const_prop_deaggregates, r=wesleywiser
Const prop aggregates even if partially or fully modified r? @wesleywiser cc @rust-lang/wg-mir-opt I'm moderately scared of this change, but I'm confident in having reviewed all the cases.
2 parents aeb4738 + a1ebb94 commit 3fe4dd2

File tree

9 files changed

+342
-42
lines changed

9 files changed

+342
-42
lines changed

src/librustc_mir/transform/const_prop.rs

+58-42
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
349349
}
350350
}
351351

352-
fn get_const(&self, local: Local) -> Option<OpTy<'tcx>> {
353-
let op = self.ecx.access_local(self.ecx.frame(), local, None).ok();
352+
fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
353+
let op = self.ecx.eval_place_to_op(place, None).ok();
354354

355355
// Try to read the local as an immediate so that if it is representable as a scalar, we can
356356
// handle it as such, but otherwise, just return the value as is.
@@ -772,13 +772,25 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
772772
fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
773773
use rustc_middle::mir::visit::PlaceContext::*;
774774
match context {
775-
// Constants must have at most one write
776-
// FIXME(oli-obk): we could be more powerful here, if the multiple writes
777-
// only occur in independent execution paths
778-
MutatingUse(MutatingUseContext::Store) => {
775+
// Projections are fine, because `&mut foo.x` will be caught by
776+
// `MutatingUseContext::Borrow` elsewhere.
777+
MutatingUse(MutatingUseContext::Projection)
778+
| MutatingUse(MutatingUseContext::Store) => {
779779
if !self.found_assignment.insert(local) {
780-
trace!("local {:?} can't be propagated because of multiple assignments", local);
781-
self.can_const_prop[local] = ConstPropMode::NoPropagation;
780+
match &mut self.can_const_prop[local] {
781+
// If the local can only get propagated in its own block, then we don't have
782+
// to worry about multiple assignments, as we'll nuke the const state at the
783+
// end of the block anyway, and inside the block we overwrite previous
784+
// states as applicable.
785+
ConstPropMode::OnlyInsideOwnBlock => {}
786+
other => {
787+
trace!(
788+
"local {:?} can't be propagated because of multiple assignments",
789+
local,
790+
);
791+
*other = ConstPropMode::NoPropagation;
792+
}
793+
}
782794
}
783795
}
784796
// Reading constants is allowed an arbitrary number of times
@@ -787,12 +799,6 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
787799
| NonMutatingUse(NonMutatingUseContext::Inspect)
788800
| NonMutatingUse(NonMutatingUseContext::Projection)
789801
| NonUse(_) => {}
790-
// FIXME(felix91gr): explain the reasoning behind this
791-
MutatingUse(MutatingUseContext::Projection) => {
792-
if self.local_kinds[local] != LocalKind::Temp {
793-
self.can_const_prop[local] = ConstPropMode::NoPropagation;
794-
}
795-
}
796802
_ => {
797803
trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
798804
self.can_const_prop[local] = ConstPropMode::NoPropagation;
@@ -826,40 +832,50 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
826832
if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind {
827833
let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
828834
if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
829-
if let Some(local) = place.as_local() {
830-
let can_const_prop = self.can_const_prop[local];
831-
if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
832-
if can_const_prop != ConstPropMode::NoPropagation {
833-
// This will return None for Locals that are from other blocks,
834-
// so it should be okay to propagate from here on down.
835-
if let Some(value) = self.get_const(local) {
836-
if self.should_const_prop(value) {
837-
trace!("replacing {:?} with {:?}", rval, value);
838-
self.replace_with_const(rval, value, statement.source_info);
839-
if can_const_prop == ConstPropMode::FullConstProp
840-
|| can_const_prop == ConstPropMode::OnlyInsideOwnBlock
841-
{
842-
trace!("propagated into {:?}", local);
843-
}
844-
}
845-
if can_const_prop == ConstPropMode::OnlyInsideOwnBlock {
846-
trace!(
847-
"found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}",
848-
local
849-
);
850-
self.locals_of_current_block.insert(local);
835+
let can_const_prop = self.can_const_prop[place.local];
836+
if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
837+
if can_const_prop != ConstPropMode::NoPropagation {
838+
// This will return None for variables that are from other blocks,
839+
// so it should be okay to propagate from here on down.
840+
if let Some(value) = self.get_const(place) {
841+
if self.should_const_prop(value) {
842+
trace!("replacing {:?} with {:?}", rval, value);
843+
self.replace_with_const(rval, value, statement.source_info);
844+
if can_const_prop == ConstPropMode::FullConstProp
845+
|| can_const_prop == ConstPropMode::OnlyInsideOwnBlock
846+
{
847+
trace!("propagated into {:?}", place);
851848
}
852849
}
850+
if can_const_prop == ConstPropMode::OnlyInsideOwnBlock {
851+
trace!(
852+
"found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}",
853+
place.local
854+
);
855+
self.locals_of_current_block.insert(place.local);
856+
}
853857
}
854858
}
855-
if self.can_const_prop[local] == ConstPropMode::OnlyPropagateInto
856-
|| self.can_const_prop[local] == ConstPropMode::NoPropagation
859+
if can_const_prop == ConstPropMode::OnlyPropagateInto
860+
|| can_const_prop == ConstPropMode::NoPropagation
857861
{
858-
trace!("can't propagate into {:?}", local);
859-
if local != RETURN_PLACE {
860-
Self::remove_const(&mut self.ecx, local);
862+
trace!("can't propagate into {:?}", place);
863+
if place.local != RETURN_PLACE {
864+
Self::remove_const(&mut self.ecx, place.local);
861865
}
862866
}
867+
} else {
868+
// Const prop failed, so erase the destination, ensuring that whatever happens
869+
// from here on, does not know about the previous value.
870+
// This is important in case we have
871+
// ```rust
872+
// let mut x = 42;
873+
// x = SOME_MUTABLE_STATIC;
874+
// // x must now be undefined
875+
// ```
876+
// FIXME: we overzealously erase the entire local, because that's easier to
877+
// implement.
878+
Self::remove_const(&mut self.ecx, place.local);
863879
}
864880
}
865881
} else {
@@ -993,7 +1009,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
9931009
arguments are of the variant `Operand::Copy`. This allows us to
9941010
simplify our handling of `Operands` in this case.
9951011
*/
996-
if let Some(l) = opr.place().and_then(|p| p.as_local()) {
1012+
if let Some(l) = opr.place() {
9971013
if let Some(value) = self.get_const(l) {
9981014
if self.should_const_prop(value) {
9991015
// FIXME(felix91gr): this code only handles `Scalar` cases.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: -O
2+
3+
// EMIT_MIR rustc.main.ConstProp.diff
4+
fn main() {
5+
let mut x = 42;
6+
x = 99;
7+
let y = x;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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/mutable_variable.rs:4:11: 4:11
6+
let mut _1: i32; // in scope 0 at $DIR/mutable_variable.rs:5:9: 5:14
7+
scope 1 {
8+
debug x => _1; // in scope 1 at $DIR/mutable_variable.rs:5:9: 5:14
9+
let _2: i32; // in scope 1 at $DIR/mutable_variable.rs:7:9: 7:10
10+
scope 2 {
11+
debug y => _2; // in scope 2 at $DIR/mutable_variable.rs:7:9: 7:10
12+
}
13+
}
14+
15+
bb0: {
16+
StorageLive(_1); // scope 0 at $DIR/mutable_variable.rs:5:9: 5:14
17+
_1 = const 42i32; // scope 0 at $DIR/mutable_variable.rs:5:17: 5:19
18+
// ty::Const
19+
// + ty: i32
20+
// + val: Value(Scalar(0x0000002a))
21+
// mir::Constant
22+
// + span: $DIR/mutable_variable.rs:5:17: 5:19
23+
// + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) }
24+
_1 = const 99i32; // scope 1 at $DIR/mutable_variable.rs:6:5: 6:11
25+
// ty::Const
26+
// + ty: i32
27+
// + val: Value(Scalar(0x00000063))
28+
// mir::Constant
29+
- // + span: $DIR/mutable_variable.rs:6:9: 6:11
30+
+ // + span: $DIR/mutable_variable.rs:6:5: 6:11
31+
// + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) }
32+
StorageLive(_2); // scope 1 at $DIR/mutable_variable.rs:7:9: 7:10
33+
- _2 = _1; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14
34+
+ _2 = const 99i32; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14
35+
+ // ty::Const
36+
+ // + ty: i32
37+
+ // + val: Value(Scalar(0x00000063))
38+
+ // mir::Constant
39+
+ // + span: $DIR/mutable_variable.rs:7:13: 7:14
40+
+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) }
41+
_0 = const (); // scope 0 at $DIR/mutable_variable.rs:4:11: 8:2
42+
// ty::Const
43+
// + ty: ()
44+
// + val: Value(Scalar(<ZST>))
45+
// mir::Constant
46+
// + span: $DIR/mutable_variable.rs:4:11: 8:2
47+
// + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
48+
StorageDead(_2); // scope 1 at $DIR/mutable_variable.rs:8:1: 8:2
49+
StorageDead(_1); // scope 0 at $DIR/mutable_variable.rs:8:1: 8:2
50+
return; // scope 0 at $DIR/mutable_variable.rs:8:2: 8:2
51+
}
52+
}
53+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: -O
2+
3+
// EMIT_MIR rustc.main.ConstProp.diff
4+
fn main() {
5+
let mut x = (42, 43);
6+
x.1 = 99;
7+
let y = x;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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/mutable_variable_aggregate.rs:4:11: 4:11
6+
let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14
7+
scope 1 {
8+
debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14
9+
let _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10
10+
scope 2 {
11+
debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10
12+
}
13+
}
14+
15+
bb0: {
16+
StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14
17+
_1 = (const 42i32, const 43i32); // scope 0 at $DIR/mutable_variable_aggregate.rs:5:17: 5:25
18+
// ty::Const
19+
// + ty: i32
20+
// + val: Value(Scalar(0x0000002a))
21+
// mir::Constant
22+
- // + span: $DIR/mutable_variable_aggregate.rs:5:18: 5:20
23+
+ // + span: $DIR/mutable_variable_aggregate.rs:5:17: 5:25
24+
// + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) }
25+
// ty::Const
26+
// + ty: i32
27+
// + val: Value(Scalar(0x0000002b))
28+
// mir::Constant
29+
- // + span: $DIR/mutable_variable_aggregate.rs:5:22: 5:24
30+
+ // + span: $DIR/mutable_variable_aggregate.rs:5:17: 5:25
31+
// + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) }
32+
(_1.1: i32) = const 99i32; // scope 1 at $DIR/mutable_variable_aggregate.rs:6:5: 6:13
33+
// ty::Const
34+
// + ty: i32
35+
// + val: Value(Scalar(0x00000063))
36+
// mir::Constant
37+
- // + span: $DIR/mutable_variable_aggregate.rs:6:11: 6:13
38+
+ // + span: $DIR/mutable_variable_aggregate.rs:6:5: 6:13
39+
// + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) }
40+
StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10
41+
- _2 = _1; // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14
42+
+ _2 = (const 42i32, const 99i32); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14
43+
+ // ty::Const
44+
+ // + ty: i32
45+
+ // + val: Value(Scalar(0x0000002a))
46+
+ // mir::Constant
47+
+ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14
48+
+ // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) }
49+
+ // ty::Const
50+
+ // + ty: i32
51+
+ // + val: Value(Scalar(0x00000063))
52+
+ // mir::Constant
53+
+ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14
54+
+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) }
55+
_0 = const (); // scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 8:2
56+
// ty::Const
57+
// + ty: ()
58+
// + val: Value(Scalar(<ZST>))
59+
// mir::Constant
60+
// + span: $DIR/mutable_variable_aggregate.rs:4:11: 8:2
61+
// + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
62+
StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2
63+
StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2
64+
return; // scope 0 at $DIR/mutable_variable_aggregate.rs:8:2: 8:2
65+
}
66+
}
67+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// compile-flags: -O
2+
3+
// EMIT_MIR rustc.main.ConstProp.diff
4+
fn main() {
5+
let mut x = (42, 43);
6+
let z = &mut x;
7+
z.1 = 99;
8+
let y = x;
9+
}

0 commit comments

Comments
 (0)