Skip to content

Commit b277c7e

Browse files
committed
Auto merge of rust-lang#139639 - compiler-errors:do-not-optimize-switchint-2, r=<try>
[Experiment] Make RemoveRedundantSwitch into a separate pass r? `@ghost`
2 parents 69b3959 + c758fc1 commit b277c7e

17 files changed

+254
-72
lines changed

compiler/rustc_mir_transform/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ declare_passes! {
189189
BeforeConstProp,
190190
AfterGVN,
191191
Final
192-
};
192+
},
193+
RemoveRedundantSwitch;
193194
mod simplify_branches : SimplifyConstCondition {
194195
AfterConstProp,
195196
Final
@@ -675,6 +676,8 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
675676
&check_null::CheckNull,
676677
// Before inlining: trim down MIR with passes to reduce inlining work.
677678

679+
// Remove some redundant `SwitchInt` terminators.
680+
&simplify::RemoveRedundantSwitch,
678681
// Has to be done before inlining, otherwise actual call will be almost always inlined.
679682
// Also simple, so can just do first.
680683
&lower_slice_len::LowerSliceLenCalls,

compiler/rustc_mir_transform/src/simplify.rs

+46-27
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
//! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we
2727
//! naively generate still contains the `_a = ()` write in the unreachable block "after" the
2828
//! return.
29+
//!
30+
//! **WARNING**: `SimplifyCfg` is one of the few optimizations that runs on built and analysis
31+
//! MIR, and so its effects may affect the type-checking, borrow-checking, and other analysis.
32+
//! We must be extremely careful to only apply optimizations that preserve UB and all
33+
//! non-determinism, since changes here can affect which programs compile in an insta-stable way.
34+
//! The normal logic that a program with UB can be changed to do anything does not apply to
35+
//! pre-"runtime" MIR!
2936
3037
use rustc_index::{Idx, IndexSlice, IndexVec};
3138
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@@ -144,7 +151,6 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
144151
merged_blocks.clear();
145152
while inner_changed {
146153
inner_changed = false;
147-
inner_changed |= self.simplify_branch(&mut terminator);
148154
inner_changed |= self.merge_successor(&mut merged_blocks, &mut terminator);
149155
changed |= inner_changed;
150156
}
@@ -251,32 +257,6 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
251257
true
252258
}
253259

254-
// turn a branch with all successors identical to a goto
255-
fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool {
256-
match terminator.kind {
257-
TerminatorKind::SwitchInt { .. } => {}
258-
_ => return false,
259-
};
260-
261-
let first_succ = {
262-
if let Some(first_succ) = terminator.successors().next() {
263-
if terminator.successors().all(|s| s == first_succ) {
264-
let count = terminator.successors().count();
265-
self.pred_count[first_succ] -= (count - 1) as u32;
266-
first_succ
267-
} else {
268-
return false;
269-
}
270-
} else {
271-
return false;
272-
}
273-
};
274-
275-
debug!("simplifying branch {:?}", terminator);
276-
terminator.kind = TerminatorKind::Goto { target: first_succ };
277-
true
278-
}
279-
280260
fn strip_nops(&mut self) {
281261
for blk in self.basic_blocks.iter_mut() {
282262
blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop))
@@ -615,3 +595,42 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
615595
*l = self.map[*l].unwrap();
616596
}
617597
}
598+
599+
pub struct RemoveRedundantSwitch;
600+
601+
impl<'tcx> crate::MirPass<'tcx> for RemoveRedundantSwitch {
602+
fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
603+
loop {
604+
let mut should_simplify = false;
605+
606+
for block in body.basic_blocks_mut() {
607+
let TerminatorKind::SwitchInt { discr: _, targets } = &block.terminator().kind
608+
else {
609+
continue;
610+
};
611+
let Some((first_succ, rest)) = targets.all_targets().split_first() else {
612+
continue;
613+
};
614+
if !rest.iter().all(|succ| succ == first_succ) {
615+
continue;
616+
}
617+
block.terminator_mut().kind = TerminatorKind::Goto { target: *first_succ };
618+
should_simplify = true;
619+
}
620+
621+
if should_simplify {
622+
simplify_cfg(body);
623+
} else {
624+
break;
625+
}
626+
}
627+
}
628+
629+
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
630+
sess.mir_opt_level() >= 1
631+
}
632+
633+
fn is_required(&self) -> bool {
634+
false
635+
}
636+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Ensure that we don't optimize out `SwitchInt` reads even if that terminator
2+
// branches to the same basic block on every target, since the operand may have
3+
// side-effects that affect analysis of the MIR.
4+
//
5+
// See <https://github.com/rust-lang/miri/issues/4237>.
6+
7+
use std::mem::MaybeUninit;
8+
9+
fn main() {
10+
let uninit: MaybeUninit<i32> = MaybeUninit::uninit();
11+
let bad_ref: &i32 = unsafe { uninit.assume_init_ref() };
12+
let &(0 | _) = bad_ref;
13+
//~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
2+
--> tests/fail/read_from_trivial_switch.rs:LL:CC
3+
|
4+
LL | let &(0 | _) = bad_ref;
5+
| ^^^^^^^^ using uninitialized data, but this operation requires initialized memory
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: BACKTRACE:
10+
= note: inside `main` at tests/fail/read_from_trivial_switch.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to 1 previous error
15+

tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir

+19-15
Original file line numberDiff line numberDiff line change
@@ -24,43 +24,47 @@ fn match_tuple(_1: (u32, bool, Option<i32>, u32)) -> u32 {
2424

2525
bb1: {
2626
_0 = const 0_u32;
27-
goto -> bb10;
27+
goto -> bb11;
2828
}
2929

3030
bb2: {
31-
_2 = discriminant((_1.2: std::option::Option<i32>));
32-
switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb1];
31+
switchInt(copy (_1.1: bool)) -> [0: bb3, otherwise: bb3];
3332
}
3433

3534
bb3: {
36-
switchInt(copy (((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1: bb4, 8: bb4, otherwise: bb1];
35+
_2 = discriminant((_1.2: std::option::Option<i32>));
36+
switchInt(move _2) -> [0: bb5, 1: bb4, otherwise: bb1];
3737
}
3838

3939
bb4: {
40-
_5 = Le(const 6_u32, copy (_1.3: u32));
41-
switchInt(move _5) -> [0: bb5, otherwise: bb7];
40+
switchInt(copy (((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1: bb5, 8: bb5, otherwise: bb1];
4241
}
4342

4443
bb5: {
45-
_3 = Le(const 13_u32, copy (_1.3: u32));
46-
switchInt(move _3) -> [0: bb1, otherwise: bb6];
44+
_5 = Le(const 6_u32, copy (_1.3: u32));
45+
switchInt(move _5) -> [0: bb6, otherwise: bb8];
4746
}
4847

4948
bb6: {
50-
_4 = Le(copy (_1.3: u32), const 16_u32);
51-
switchInt(move _4) -> [0: bb1, otherwise: bb8];
49+
_3 = Le(const 13_u32, copy (_1.3: u32));
50+
switchInt(move _3) -> [0: bb1, otherwise: bb7];
5251
}
5352

5453
bb7: {
55-
_6 = Le(copy (_1.3: u32), const 9_u32);
56-
switchInt(move _6) -> [0: bb5, otherwise: bb8];
54+
_4 = Le(copy (_1.3: u32), const 16_u32);
55+
switchInt(move _4) -> [0: bb1, otherwise: bb9];
5756
}
5857

5958
bb8: {
60-
falseEdge -> [real: bb9, imaginary: bb1];
59+
_6 = Le(copy (_1.3: u32), const 9_u32);
60+
switchInt(move _6) -> [0: bb6, otherwise: bb9];
6161
}
6262

6363
bb9: {
64+
falseEdge -> [real: bb10, imaginary: bb1];
65+
}
66+
67+
bb10: {
6468
StorageLive(_7);
6569
_7 = copy (_1.0: u32);
6670
StorageLive(_8);
@@ -74,10 +78,10 @@ fn match_tuple(_1: (u32, bool, Option<i32>, u32)) -> u32 {
7478
StorageDead(_9);
7579
StorageDead(_8);
7680
StorageDead(_7);
77-
goto -> bb10;
81+
goto -> bb11;
7882
}
7983

80-
bb10: {
84+
bb11: {
8185
return;
8286
}
8387
}

tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
let mut _7: bool;
1313
let mut _8: bool;
1414
let mut _9: isize;
15+
let mut _10: isize;
1516
scope 1 {
1617
debug _trailers => _5;
1718
}
@@ -99,12 +100,13 @@
99100
}
100101

101102
bb15: {
102-
goto -> bb14;
103+
_9 = discriminant(((((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>) as Ok).0: std::option::Option<std::vec::Vec<u8>>));
104+
switchInt(move _9) -> [1: bb14, otherwise: bb14];
103105
}
104106

105107
bb16: {
106-
_9 = discriminant(((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>));
107-
switchInt(move _9) -> [0: bb13, otherwise: bb12];
108+
_10 = discriminant(((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>));
109+
switchInt(move _10) -> [0: bb13, otherwise: bb12];
108110
}
109111

110112
bb17: {
@@ -116,7 +118,7 @@
116118
}
117119

118120
bb19 (cleanup): {
119-
goto -> bb9;
121+
switchInt(copy _2) -> [1: bb9, otherwise: bb9];
120122
}
121123

122124
bb20 (cleanup): {

tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
let mut _6: bool;
1212
let mut _7: bool;
1313
let mut _8: isize;
14+
let mut _9: isize;
1415
scope 1 {
1516
debug _v => _5;
1617
}
@@ -92,12 +93,13 @@
9293
}
9394

9495
bb15: {
95-
goto -> bb14;
96+
_8 = discriminant(((((_1 as Some).0: std::option::Option<std::option::Option<T>>) as Some).0: std::option::Option<T>));
97+
switchInt(move _8) -> [1: bb14, otherwise: bb14];
9698
}
9799

98100
bb16: {
99-
_8 = discriminant(((_1 as Some).0: std::option::Option<std::option::Option<T>>));
100-
switchInt(move _8) -> [1: bb13, otherwise: bb12];
101+
_9 = discriminant(((_1 as Some).0: std::option::Option<std::option::Option<T>>));
102+
switchInt(move _9) -> [1: bb13, otherwise: bb12];
101103
}
102104

103105
bb17: {
@@ -109,7 +111,7 @@
109111
}
110112

111113
bb19 (cleanup): {
112-
goto -> bb9;
114+
switchInt(copy _2) -> [1: bb9, otherwise: bb9];
113115
}
114116

115117
bb20 (cleanup): {

tests/mir-opt/gvn_copy_aggregate.remove_storage_dead.GVN.diff

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
+ _2 = copy _5;
4040
+ nop;
4141
_7 = discriminant(_3);
42+
switchInt(move _7) -> bb2;
43+
}
44+
45+
bb2: {
4246
- StorageDead(_3);
4347
+ nop;
4448
StorageLive(_6);

tests/mir-opt/multiple_return_terminators.test.MultipleReturnTerminators.diff

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
let mut _0: ();
77

88
bb0: {
9+
switchInt(copy _1) -> bb1;
10+
}
11+
12+
bb1: {
913
return;
1014
}
1115
}

tests/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-pre-optimizations.after.panic-abort.mir

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ fn unwrap(_1: Option<T>) -> T {
3333
_0 = move _3;
3434
StorageDead(_3);
3535
_5 = discriminant(_1);
36+
switchInt(move _5) -> [1: bb4, otherwise: bb4];
37+
}
38+
39+
bb4: {
3640
return;
3741
}
3842
}

tests/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-pre-optimizations.after.panic-unwind.mir

+11-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn unwrap(_1: Option<T>) -> T {
2424

2525
bb2: {
2626
StorageLive(_4);
27-
_4 = begin_panic::<&str>(const "explicit panic") -> bb4;
27+
_4 = begin_panic::<&str>(const "explicit panic") -> bb6;
2828
}
2929

3030
bb3: {
@@ -33,11 +33,19 @@ fn unwrap(_1: Option<T>) -> T {
3333
_0 = move _3;
3434
StorageDead(_3);
3535
_5 = discriminant(_1);
36-
return;
36+
switchInt(move _5) -> [1: bb5, otherwise: bb5];
3737
}
3838

3939
bb4 (cleanup): {
40-
_7 = discriminant(_1);
4140
resume;
4241
}
42+
43+
bb5: {
44+
return;
45+
}
46+
47+
bb6 (cleanup): {
48+
_7 = discriminant(_1);
49+
switchInt(move _7) -> [1: bb4, otherwise: bb4];
50+
}
4351
}

0 commit comments

Comments
 (0)