Skip to content

Commit 2b77d79

Browse files
Auto merge of #147654 - dianqk:simplify-const-condition, r=<try>
Simplify trivial constants in SimplifyConstCondition
2 parents 4b94758 + dcd9af0 commit 2b77d79

File tree

8 files changed

+275
-3
lines changed

8 files changed

+275
-3
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1924,7 +1924,9 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19241924
// Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
19251925
let safe_to_preserve_derefs = matches!(
19261926
terminator.kind,
1927-
TerminatorKind::SwitchInt { .. } | TerminatorKind::Goto { .. }
1927+
TerminatorKind::SwitchInt { .. }
1928+
| TerminatorKind::Goto { .. }
1929+
| TerminatorKind::Unreachable
19281930
);
19291931
if !safe_to_preserve_derefs {
19301932
self.invalidate_derefs();

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ declare_passes! {
189189
Final
190190
};
191191
mod simplify_branches : SimplifyConstCondition {
192+
AfterInstSimplify,
192193
AfterConstProp,
193194
Final
194195
};
@@ -708,6 +709,7 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
708709
// optimizations. This invalidates CFG caches, so avoid putting between
709710
// `ReferencePropagation` and `GVN` which both use the dominator tree.
710711
&instsimplify::InstSimplify::AfterSimplifyCfg,
712+
&o1(simplify_branches::SimplifyConstCondition::AfterInstSimplify),
711713
&ref_prop::ReferencePropagation,
712714
&sroa::ScalarReplacementOfAggregates,
713715
&simplify::SimplifyLocals::BeforeConstProp,

compiler/rustc_mir_transform/src/simplify_branches.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use tracing::trace;
55
use crate::patch::MirPatch;
66

77
pub(super) enum SimplifyConstCondition {
8+
AfterInstSimplify,
89
AfterConstProp,
910
Final,
1011
}
@@ -13,6 +14,9 @@ pub(super) enum SimplifyConstCondition {
1314
impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition {
1415
fn name(&self) -> &'static str {
1516
match self {
17+
SimplifyConstCondition::AfterInstSimplify => {
18+
"SimplifyConstCondition-after-inst-simplify"
19+
}
1620
SimplifyConstCondition::AfterConstProp => "SimplifyConstCondition-after-const-prop",
1721
SimplifyConstCondition::Final => "SimplifyConstCondition-final",
1822
}
@@ -24,19 +28,39 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition {
2428
let mut patch = MirPatch::new(body);
2529

2630
'blocks: for (bb, block) in body.basic_blocks.iter_enumerated() {
31+
let mut pre_local_const: Option<(Local, &'_ ConstOperand<'_>)> = None;
32+
2733
for (statement_index, stmt) in block.statements.iter().enumerate() {
34+
let has_local_const = pre_local_const.take();
2835
// Simplify `assume` of a known value: either a NOP or unreachable.
2936
if let StatementKind::Intrinsic(box ref intrinsic) = stmt.kind
3037
&& let NonDivergingIntrinsic::Assume(discr) = intrinsic
31-
&& let Operand::Constant(c) = discr
32-
&& let Some(constant) = c.const_.try_eval_bool(tcx, typing_env)
3338
{
39+
let c = if let Operand::Constant(c) = discr {
40+
c
41+
} else if let Some((local, c)) = has_local_const
42+
&& let Some(assume_local) = discr.place().and_then(|p| p.as_local())
43+
&& local == assume_local
44+
{
45+
c
46+
} else {
47+
continue;
48+
};
49+
let Some(constant) = c.const_.try_eval_bool(tcx, typing_env) else {
50+
continue;
51+
};
3452
if constant {
3553
patch.nop_statement(Location { block: bb, statement_index });
3654
} else {
3755
patch.patch_terminator(bb, TerminatorKind::Unreachable);
3856
continue 'blocks;
3957
}
58+
} else if let StatementKind::Assign(box (ref lhs, ref rvalue)) = stmt.kind
59+
&& let Some(local) = lhs.as_local()
60+
&& let Rvalue::Use(Operand::Constant(c)) = rvalue
61+
&& c.const_.ty().is_bool()
62+
{
63+
pre_local_const = Some((local, c));
4064
}
4165
}
4266

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ test-mir-pass: SimplifyConstCondition-after-inst-simplify
2+
//@ compile-flags: -Zmir-enable-passes=+InstSimplify-after-simplifycfg -Zub_checks=false -Zinline-mir
3+
4+
#![crate_type = "lib"]
5+
6+
// EMIT_MIR trivial_const.unwrap_unchecked.SimplifyConstCondition-after-inst-simplify.diff
7+
pub fn unwrap_unchecked(v: &Option<i32>) -> i32 {
8+
// CHECK-LABEL: fn unwrap_unchecked(
9+
// CHECK: bb0: {
10+
// CHECK: switchInt({{.*}}) -> [0: [[AssumeFalseBB:bb.*]], 1:
11+
// CHECK: [[AssumeFalseBB]]: {
12+
// CHECK-NEXT: unreachable;
13+
let v = unsafe { v.unwrap_unchecked() };
14+
v
15+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
- // MIR for `unwrap_unchecked` before SimplifyConstCondition-after-inst-simplify
2+
+ // MIR for `unwrap_unchecked` after SimplifyConstCondition-after-inst-simplify
3+
4+
fn unwrap_unchecked(_1: &Option<i32>) -> i32 {
5+
debug v => _1;
6+
let mut _0: i32;
7+
let _2: i32;
8+
let mut _3: std::option::Option<i32>;
9+
scope 1 {
10+
debug v => _2;
11+
}
12+
scope 2 (inlined #[track_caller] Option::<i32>::unwrap_unchecked) {
13+
let mut _4: isize;
14+
scope 3 {
15+
}
16+
scope 4 (inlined #[track_caller] unreachable_unchecked) {
17+
let _5: ();
18+
scope 5 (inlined core::ub_checks::check_language_ub) {
19+
let mut _6: bool;
20+
scope 6 (inlined core::ub_checks::check_language_ub::runtime) {
21+
}
22+
}
23+
}
24+
}
25+
26+
bb0: {
27+
StorageLive(_2);
28+
StorageLive(_3);
29+
_3 = copy (*_1);
30+
StorageLive(_4);
31+
StorageLive(_5);
32+
_4 = discriminant(_3);
33+
switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb1];
34+
}
35+
36+
bb1: {
37+
unreachable;
38+
}
39+
40+
bb2: {
41+
- StorageLive(_6);
42+
- _6 = const false;
43+
- assume(copy _6);
44+
- _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable];
45+
+ unreachable;
46+
}
47+
48+
bb3: {
49+
_2 = move ((_3 as Some).0: i32);
50+
StorageDead(_5);
51+
StorageDead(_4);
52+
StorageDead(_3);
53+
_0 = copy _2;
54+
StorageDead(_2);
55+
return;
56+
}
57+
}
58+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ compile-flags: -O
2+
3+
#![crate_type = "lib"]
4+
5+
// EMIT_MIR two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff
6+
// EMIT_MIR two_unwrap_unchecked.two_unwrap_unchecked.PreCodegen.after.mir
7+
pub fn two_unwrap_unchecked(v: &Option<i32>) -> i32 {
8+
// CHECK-LABEL: fn two_unwrap_unchecked(
9+
// CHECK: [[DEREF_V:_.*]] = copy (*_1);
10+
// CHECK: [[V1V2:_.*]] = copy (([[DEREF_V]] as Some).0: i32);
11+
// CHECK: _0 = Add(copy [[V1V2]], copy [[V1V2]]);
12+
let v1 = unsafe { v.unwrap_unchecked() };
13+
let v2 = unsafe { v.unwrap_unchecked() };
14+
v1 + v2
15+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
- // MIR for `two_unwrap_unchecked` before GVN
2+
+ // MIR for `two_unwrap_unchecked` after GVN
3+
4+
fn two_unwrap_unchecked(_1: &Option<i32>) -> i32 {
5+
debug v => _1;
6+
let mut _0: i32;
7+
let _2: i32;
8+
let mut _3: std::option::Option<i32>;
9+
let mut _5: std::option::Option<i32>;
10+
let mut _6: i32;
11+
let mut _7: i32;
12+
scope 1 {
13+
debug v1 => _2;
14+
let _4: i32;
15+
scope 2 {
16+
debug v2 => _4;
17+
}
18+
scope 8 (inlined #[track_caller] Option::<i32>::unwrap_unchecked) {
19+
let mut _9: isize;
20+
scope 9 {
21+
}
22+
scope 10 (inlined #[track_caller] unreachable_unchecked) {
23+
scope 11 (inlined core::ub_checks::check_language_ub) {
24+
scope 12 (inlined core::ub_checks::check_language_ub::runtime) {
25+
}
26+
}
27+
}
28+
}
29+
}
30+
scope 3 (inlined #[track_caller] Option::<i32>::unwrap_unchecked) {
31+
let mut _8: isize;
32+
scope 4 {
33+
}
34+
scope 5 (inlined #[track_caller] unreachable_unchecked) {
35+
scope 6 (inlined core::ub_checks::check_language_ub) {
36+
scope 7 (inlined core::ub_checks::check_language_ub::runtime) {
37+
}
38+
}
39+
}
40+
}
41+
42+
bb0: {
43+
- StorageLive(_2);
44+
- StorageLive(_3);
45+
+ nop;
46+
+ nop;
47+
_3 = copy (*_1);
48+
- StorageLive(_8);
49+
+ nop;
50+
_8 = discriminant(_3);
51+
- switchInt(move _8) -> [0: bb2, 1: bb3, otherwise: bb1];
52+
+ switchInt(copy _8) -> [0: bb2, 1: bb3, otherwise: bb1];
53+
}
54+
55+
bb1: {
56+
unreachable;
57+
}
58+
59+
bb2: {
60+
unreachable;
61+
}
62+
63+
bb3: {
64+
- _2 = move ((_3 as Some).0: i32);
65+
- StorageDead(_8);
66+
- StorageDead(_3);
67+
+ _2 = copy ((_3 as Some).0: i32);
68+
+ nop;
69+
+ nop;
70+
StorageLive(_4);
71+
StorageLive(_5);
72+
- _5 = copy (*_1);
73+
+ _5 = copy _3;
74+
StorageLive(_9);
75+
- _9 = discriminant(_5);
76+
- switchInt(move _9) -> [0: bb4, 1: bb5, otherwise: bb1];
77+
+ _9 = copy _8;
78+
+ switchInt(copy _8) -> [0: bb4, 1: bb5, otherwise: bb1];
79+
}
80+
81+
bb4: {
82+
unreachable;
83+
}
84+
85+
bb5: {
86+
- _4 = move ((_5 as Some).0: i32);
87+
+ _4 = copy _2;
88+
StorageDead(_9);
89+
StorageDead(_5);
90+
StorageLive(_6);
91+
_6 = copy _2;
92+
StorageLive(_7);
93+
- _7 = copy _4;
94+
- _0 = Add(move _6, move _7);
95+
+ _7 = copy _2;
96+
+ _0 = Add(copy _2, copy _2);
97+
StorageDead(_7);
98+
StorageDead(_6);
99+
StorageDead(_4);
100+
- StorageDead(_2);
101+
+ nop;
102+
return;
103+
}
104+
}
105+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// MIR for `two_unwrap_unchecked` after PreCodegen
2+
3+
fn two_unwrap_unchecked(_1: &Option<i32>) -> i32 {
4+
debug v => _1;
5+
let mut _0: i32;
6+
let mut _2: std::option::Option<i32>;
7+
let _4: i32;
8+
scope 1 {
9+
debug v1 => _4;
10+
scope 2 {
11+
debug v2 => _4;
12+
}
13+
scope 8 (inlined #[track_caller] Option::<i32>::unwrap_unchecked) {
14+
scope 9 {
15+
}
16+
scope 10 (inlined #[track_caller] unreachable_unchecked) {
17+
scope 11 (inlined core::ub_checks::check_language_ub) {
18+
scope 12 (inlined core::ub_checks::check_language_ub::runtime) {
19+
}
20+
}
21+
}
22+
}
23+
}
24+
scope 3 (inlined #[track_caller] Option::<i32>::unwrap_unchecked) {
25+
let mut _3: isize;
26+
scope 4 {
27+
}
28+
scope 5 (inlined #[track_caller] unreachable_unchecked) {
29+
scope 6 (inlined core::ub_checks::check_language_ub) {
30+
scope 7 (inlined core::ub_checks::check_language_ub::runtime) {
31+
}
32+
}
33+
}
34+
}
35+
36+
bb0: {
37+
_2 = copy (*_1);
38+
_3 = discriminant(_2);
39+
switchInt(copy _3) -> [0: bb2, 1: bb1, otherwise: bb2];
40+
}
41+
42+
bb1: {
43+
_4 = copy ((_2 as Some).0: i32);
44+
_0 = Add(copy _4, copy _4);
45+
return;
46+
}
47+
48+
bb2: {
49+
unreachable;
50+
}
51+
}

0 commit comments

Comments
 (0)