Skip to content

Commit 41eda69

Browse files
committed
Remove duplicate unreachable blocks
1 parent 511364e commit 41eda69

10 files changed

+148
-75
lines changed

compiler/rustc_mir_transform/src/simplify.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
//! return.
2929
3030
use crate::MirPass;
31-
use rustc_data_structures::fx::FxHashSet;
31+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
3232
use rustc_index::vec::{Idx, IndexVec};
3333
use rustc_middle::mir::coverage::*;
3434
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@@ -48,6 +48,7 @@ impl SimplifyCfg {
4848

4949
pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
5050
CfgSimplifier::new(body).simplify();
51+
remove_duplicate_unreachable_blocks(tcx, body);
5152
remove_dead_blocks(tcx, body);
5253

5354
// FIXME: Should probably be moved into some kind of pass manager
@@ -259,6 +260,47 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
259260
}
260261
}
261262

263+
pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
264+
struct OptApplier<'tcx> {
265+
tcx: TyCtxt<'tcx>,
266+
duplicates: FxIndexSet<BasicBlock>,
267+
}
268+
269+
impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> {
270+
fn tcx(&self) -> TyCtxt<'tcx> {
271+
self.tcx
272+
}
273+
274+
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
275+
for target in terminator.successors_mut() {
276+
if self.duplicates.contains(target) {
277+
*target = self.duplicates[0];
278+
}
279+
}
280+
281+
self.super_terminator(terminator, location);
282+
}
283+
}
284+
285+
let unreachable_blocks = body
286+
.basic_blocks
287+
.iter_enumerated()
288+
.filter(|(_, bb)| {
289+
// CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
290+
// terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
291+
// before then so we need to handle missing terminators.
292+
// We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
293+
// don't emit empty unreachable cleanup blocks, so this simple check suffices.
294+
bb.terminator.is_some() && bb.is_empty_unreachable() && !bb.is_cleanup
295+
})
296+
.map(|(block, _)| block)
297+
.collect::<FxIndexSet<_>>();
298+
299+
if unreachable_blocks.len() > 1 {
300+
OptApplier { tcx, duplicates: unreachable_blocks }.visit_body(body);
301+
}
302+
}
303+
262304
pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
263305
let reachable = traversal::reachable_as_bitset(body);
264306
let num_blocks = body.basic_blocks.len();

tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir

+15-19
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fn b::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:14:18: 17:2]>,
9090

9191
bb0: {
9292
_39 = discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
93-
switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb30]; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
93+
switchInt(move _39) -> [0: bb1, 1: bb28, 3: bb26, 4: bb27, otherwise: bb29]; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
9494
}
9595

9696
bb1: {
@@ -263,7 +263,7 @@ fn b::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:14:18: 17:2]>,
263263
StorageDead(_29); // scope 5 at $DIR/async_await.rs:+2:13: +2:14
264264
StorageDead(_26); // scope 5 at $DIR/async_await.rs:+2:13: +2:14
265265
_32 = discriminant(_25); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
266-
switchInt(move _32) -> [0: bb22, 1: bb20, otherwise: bb21]; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
266+
switchInt(move _32) -> [0: bb21, 1: bb20, otherwise: bb9]; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
267267
}
268268

269269
bb20: {
@@ -281,10 +281,6 @@ fn b::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:14:18: 17:2]>,
281281
}
282282

283283
bb21: {
284-
unreachable; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
285-
}
286-
287-
bb22: {
288284
StorageLive(_33); // scope 4 at $DIR/async_await.rs:+2:5: +2:14
289285
_33 = ((_25 as Ready).0: ()); // scope 4 at $DIR/async_await.rs:+2:5: +2:14
290286
_37 = _33; // scope 6 at $DIR/async_await.rs:+2:5: +2:14
@@ -293,34 +289,34 @@ fn b::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:14:18: 17:2]>,
293289
StorageDead(_28); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
294290
StorageDead(_25); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
295291
StorageDead(_24); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
296-
goto -> bb24; // scope 0 at $DIR/async_await.rs:+2:13: +2:14
292+
goto -> bb23; // scope 0 at $DIR/async_await.rs:+2:13: +2:14
297293
}
298294

299-
bb23: {
295+
bb22: {
300296
StorageDead(_36); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
301297
_38 = move _35; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
302298
StorageDead(_35); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
303299
_7 = const (); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
304300
goto -> bb16; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
305301
}
306302

307-
bb24: {
303+
bb23: {
308304
nop; // scope 0 at $DIR/async_await.rs:+2:13: +2:14
309-
goto -> bb25; // scope 0 at $DIR/async_await.rs:+3:1: +3:2
305+
goto -> bb24; // scope 0 at $DIR/async_await.rs:+3:1: +3:2
310306
}
311307

312-
bb25: {
308+
bb24: {
313309
StorageDead(_21); // scope 0 at $DIR/async_await.rs:+3:1: +3:2
314-
goto -> bb26; // scope 0 at $DIR/async_await.rs:+3:1: +3:2
310+
goto -> bb25; // scope 0 at $DIR/async_await.rs:+3:1: +3:2
315311
}
316312

317-
bb26: {
313+
bb25: {
318314
_0 = Poll::<()>::Ready(move _37); // scope 0 at $DIR/async_await.rs:+3:2: +3:2
319315
discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))) = 1; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
320316
return; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
321317
}
322318

323-
bb27: {
319+
bb26: {
324320
StorageLive(_3); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
325321
StorageLive(_4); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
326322
StorageLive(_19); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
@@ -329,19 +325,19 @@ fn b::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:14:18: 17:2]>,
329325
goto -> bb11; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
330326
}
331327

332-
bb28: {
328+
bb27: {
333329
StorageLive(_21); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
334330
StorageLive(_35); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
335331
StorageLive(_36); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
336332
_35 = move _2; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
337-
goto -> bb23; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
333+
goto -> bb22; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
338334
}
339335

340-
bb29: {
341-
assert(const false, "`async fn` resumed after completion") -> bb29; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
336+
bb28: {
337+
assert(const false, "`async fn` resumed after completion") -> bb28; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
342338
}
343339

344-
bb30: {
340+
bb29: {
345341
unreachable; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
346342
}
347343
}

tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff

+4-8
Original file line numberDiff line numberDiff line change
@@ -61,33 +61,29 @@
6161

6262
bb4: {
6363
_8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
64-
switchInt(move _8) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
64+
switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
6565
}
6666

6767
bb5: {
6868
StorageLive(_11); // scope 0 at $DIR/separate_const_switch.rs:+12:28: +12:29
6969
_11 = ((_2 as Break).0: usize); // scope 0 at $DIR/separate_const_switch.rs:+12:28: +12:29
7070
_0 = Option::<i32>::None; // scope 4 at $DIR/separate_const_switch.rs:+12:34: +12:38
7171
StorageDead(_11); // scope 0 at $DIR/separate_const_switch.rs:+12:37: +12:38
72-
goto -> bb8; // scope 0 at $DIR/separate_const_switch.rs:+12:37: +12:38
72+
goto -> bb7; // scope 0 at $DIR/separate_const_switch.rs:+12:37: +12:38
7373
}
7474

7575
bb6: {
76-
unreachable; // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
77-
}
78-
79-
bb7: {
8076
StorageLive(_9); // scope 0 at $DIR/separate_const_switch.rs:+11:31: +11:32
8177
_9 = ((_2 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:+11:31: +11:32
8278
StorageLive(_10); // scope 3 at $DIR/separate_const_switch.rs:+11:42: +11:43
8379
_10 = _9; // scope 3 at $DIR/separate_const_switch.rs:+11:42: +11:43
8480
_0 = Option::<i32>::Some(move _10); // scope 3 at $DIR/separate_const_switch.rs:+11:37: +11:44
8581
StorageDead(_10); // scope 3 at $DIR/separate_const_switch.rs:+11:43: +11:44
8682
StorageDead(_9); // scope 0 at $DIR/separate_const_switch.rs:+11:43: +11:44
87-
goto -> bb8; // scope 0 at $DIR/separate_const_switch.rs:+11:43: +11:44
83+
goto -> bb7; // scope 0 at $DIR/separate_const_switch.rs:+11:43: +11:44
8884
}
8985

90-
bb8: {
86+
bb7: {
9187
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:+14:1: +14:2
9288
return; // scope 0 at $DIR/separate_const_switch.rs:+14:2: +14:2
9389
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
- // MIR for `assert_nonzero_nonmax` before SimplifyCfg-after-uninhabited-enum-branching
2+
+ // MIR for `assert_nonzero_nonmax` after SimplifyCfg-after-uninhabited-enum-branching
3+
4+
fn assert_nonzero_nonmax(_1: u8) -> u8 {
5+
let mut _0: u8; // return place in scope 0 at $DIR/simplify_duplicate_unreachable_blocks.rs:+0:47: +0:49
6+
7+
bb0: {
8+
- switchInt(_1) -> [0: bb1, 255: bb2, otherwise: bb3]; // scope 0 at $DIR/simplify_duplicate_unreachable_blocks.rs:+3:13: +7:14
9+
+ switchInt(_1) -> [0: bb1, 255: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_duplicate_unreachable_blocks.rs:+3:13: +7:14
10+
}
11+
12+
bb1: {
13+
unreachable; // scope 0 at $DIR/simplify_duplicate_unreachable_blocks.rs:+10:13: +10:26
14+
}
15+
16+
bb2: {
17+
- unreachable; // scope 0 at $DIR/simplify_duplicate_unreachable_blocks.rs:+13:13: +13:26
18+
- }
19+
-
20+
- bb3: {
21+
_0 = _1; // scope 0 at $DIR/simplify_duplicate_unreachable_blocks.rs:+16:13: +16:20
22+
return; // scope 0 at $DIR/simplify_duplicate_unreachable_blocks.rs:+17:13: +17:21
23+
}
24+
}
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![feature(custom_mir, core_intrinsics)]
2+
#![crate_type = "lib"]
3+
4+
use std::intrinsics::mir::*;
5+
6+
// unit-test: SimplifyCfg-after-uninhabited-enum-branching
7+
8+
// EMIT_MIR simplify_duplicate_unreachable_blocks.assert_nonzero_nonmax.SimplifyCfg-after-uninhabited-enum-branching.diff
9+
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
10+
pub unsafe fn assert_nonzero_nonmax(x: u8) -> u8 {
11+
mir!(
12+
{
13+
match x {
14+
0 => unreachable1,
15+
u8::MAX => unreachable2,
16+
_ => retblock,
17+
}
18+
}
19+
unreachable1 = {
20+
Unreachable()
21+
}
22+
unreachable2 = {
23+
Unreachable()
24+
}
25+
retblock = {
26+
RET = x;
27+
Return()
28+
}
29+
)
30+
}

tests/mir-opt/try_identity_e2e.new.PreCodegen.after.mir

+10-10
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,37 @@ fn new(_1: Result<T, E>) -> Result<T, E> {
2626
bb0: {
2727
StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
2828
_3 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+3:19: +3:20
29-
switchInt(move _3) -> [0: bb2, 1: bb1, otherwise: bb5]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20
29+
switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20
3030
}
3131

3232
bb1: {
3333
_5 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
3434
_2 = ControlFlow::<E, T>::Break(move _5); // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
35-
goto -> bb3; // scope 0 at $DIR/try_identity_e2e.rs:+5:47: +5:48
35+
goto -> bb4; // scope 0 at $DIR/try_identity_e2e.rs:+5:47: +5:48
3636
}
3737

3838
bb2: {
39+
unreachable; // scope 0 at $DIR/try_identity_e2e.rs:+3:19: +3:20
40+
}
41+
42+
bb3: {
3943
_4 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
4044
_2 = ControlFlow::<E, T>::Continue(move _4); // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
41-
goto -> bb3; // scope 0 at $DIR/try_identity_e2e.rs:+4:49: +4:50
45+
goto -> bb4; // scope 0 at $DIR/try_identity_e2e.rs:+4:49: +4:50
4246
}
4347

44-
bb3: {
48+
bb4: {
4549
_6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
46-
switchInt(move _6) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
50+
switchInt(move _6) -> [0: bb6, 1: bb5, otherwise: bb2]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
4751
}
4852

49-
bb4: {
53+
bb5: {
5054
_8 = move ((_2 as Break).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
5155
_0 = Result::<T, E>::Err(move _8); // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
5256
StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
5357
return; // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
5458
}
5559

56-
bb5: {
57-
unreachable; // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
58-
}
59-
6060
bb6: {
6161
_7 = move ((_2 as Continue).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
6262
_0 = Result::<T, E>::Ok(move _7); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6

tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir

+4-8
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fn main() -> () {
3434
StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
3535
_7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
3636
_8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
37-
switchInt(move _8) -> [4: bb5, 5: bb3, otherwise: bb4]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
37+
switchInt(move _8) -> [4: bb4, 5: bb3, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
3838
}
3939

4040
bb2: {
@@ -49,22 +49,18 @@ fn main() -> () {
4949
// + literal: Const { ty: &str, val: Value(Slice(..)) }
5050
_6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:21: +9:24
5151
StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:23: +9:24
52-
goto -> bb6; // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:23: +9:24
52+
goto -> bb5; // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:23: +9:24
5353
}
5454

5555
bb4: {
56-
unreachable; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
57-
}
58-
59-
bb5: {
6056
_6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:+8:21: +8:24
6157
// mir::Constant
6258
// + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24
6359
// + literal: Const { ty: &str, val: Value(Slice(..)) }
64-
goto -> bb6; // scope 0 at $DIR/uninhabited_enum_branching.rs:+8:21: +8:24
60+
goto -> bb5; // scope 0 at $DIR/uninhabited_enum_branching.rs:+8:21: +8:24
6561
}
6662

67-
bb6: {
63+
bb5: {
6864
StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+10:6: +10:7
6965
StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:+10:6: +10:7
7066
_0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:+0:11: +11:2

tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff

+4-8
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
6464
_7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
6565
_8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
66-
switchInt(move _8) -> [4: bb8, 5: bb6, otherwise: bb7]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
66+
switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
6767
}
6868

6969
bb6: {
@@ -74,22 +74,18 @@
7474
// + literal: Const { ty: &str, val: Value(Slice(..)) }
7575
_6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:21: +9:24
7676
StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:23: +9:24
77-
goto -> bb9; // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:23: +9:24
77+
goto -> bb8; // scope 0 at $DIR/uninhabited_enum_branching.rs:+9:23: +9:24
7878
}
7979

8080
bb7: {
81-
unreachable; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
82-
}
83-
84-
bb8: {
8581
_6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:+8:21: +8:24
8682
// mir::Constant
8783
// + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24
8884
// + literal: Const { ty: &str, val: Value(Slice(..)) }
89-
goto -> bb9; // scope 0 at $DIR/uninhabited_enum_branching.rs:+8:21: +8:24
85+
goto -> bb8; // scope 0 at $DIR/uninhabited_enum_branching.rs:+8:21: +8:24
9086
}
9187

92-
bb9: {
88+
bb8: {
9389
StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+10:6: +10:7
9490
StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:+10:6: +10:7
9591
_0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:+0:11: +11:2

0 commit comments

Comments
 (0)