Skip to content

Commit 9b90541

Browse files
Rollup merge of #119699 - cjgillot:simplify-unreachable, r=oli-obk
Merge dead bb pruning and unreachable bb deduplication. Both routines share the same basic structure: iterate on all bbs to identify work, and then renumber bbs. We can do both at once.
2 parents 72fdaf5 + 4071572 commit 9b90541

19 files changed

+124
-156
lines changed

compiler/rustc_middle/src/mir/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,7 @@ impl<'tcx> BasicBlockData<'tcx> {
13161316
}
13171317

13181318
/// Does the block have no statements and an unreachable terminator?
1319+
#[inline]
13191320
pub fn is_empty_unreachable(&self) -> bool {
13201321
self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable)
13211322
}

compiler/rustc_mir_transform/src/const_goto.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for ConstGoto {
5151
// if we applied optimizations, we potentially have some cfg to cleanup to
5252
// make it easier for further passes
5353
if should_simplify {
54-
simplify_cfg(tcx, body);
54+
simplify_cfg(body);
5555
simplify_locals(body, tcx);
5656
}
5757
}

compiler/rustc_mir_transform/src/deduplicate_blocks.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl<'tcx> MirPass<'tcx> for DeduplicateBlocks {
2525
if has_opts_to_apply {
2626
let mut opt_applier = OptApplier { tcx, duplicates };
2727
opt_applier.visit_body(body);
28-
simplify_cfg(tcx, body);
28+
simplify_cfg(body);
2929
}
3030
}
3131
}

compiler/rustc_mir_transform/src/early_otherwise_branch.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
212212
// Since this optimization adds new basic blocks and invalidates others,
213213
// clean up the cfg to make it nicer for other passes
214214
if should_cleanup {
215-
simplify_cfg(tcx, body);
215+
simplify_cfg(body);
216216
}
217217
}
218218
}

compiler/rustc_mir_transform/src/inline.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_target::abi::FieldIdx;
1515
use rustc_target::spec::abi::Abi;
1616

1717
use crate::cost_checker::CostChecker;
18-
use crate::simplify::{remove_dead_blocks, CfgSimplifier};
18+
use crate::simplify::simplify_cfg;
1919
use crate::util;
2020
use std::iter;
2121
use std::ops::{Range, RangeFrom};
@@ -56,8 +56,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
5656
let _guard = span.enter();
5757
if inline(tcx, body) {
5858
debug!("running simplify cfg on {:?}", body.source);
59-
CfgSimplifier::new(body).simplify();
60-
remove_dead_blocks(body);
59+
simplify_cfg(body);
6160
deref_finder(tcx, body);
6261
}
6362
}

compiler/rustc_mir_transform/src/match_branches.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
174174
}
175175

176176
if should_cleanup {
177-
simplify_cfg(tcx, body);
177+
simplify_cfg(body);
178178
}
179179
}
180180
}

compiler/rustc_mir_transform/src/remove_unneeded_drops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
3838
// if we applied optimizations, we potentially have some cfg to cleanup to
3939
// make it easier for further passes
4040
if should_simplify {
41-
simplify_cfg(tcx, body);
41+
simplify_cfg(body);
4242
}
4343
}
4444
}

compiler/rustc_mir_transform/src/separate_const_switch.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
5050
sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
5151
}
5252

53-
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
53+
fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
5454
// If execution did something, applying a simplification layer
5555
// helps later passes optimize the copy away.
5656
if separate_const_switch(body) > 0 {
57-
super::simplify::simplify_cfg(tcx, body);
57+
super::simplify::simplify_cfg(body);
5858
}
5959
}
6060
}

compiler/rustc_mir_transform/src/simplify.rs

+37-55
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
//! naively generate still contains the `_a = ()` write in the unreachable block "after" the
2828
//! return.
2929
30-
use rustc_data_structures::fx::FxIndexSet;
3130
use rustc_index::{Idx, IndexSlice, IndexVec};
3231
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
3332
use rustc_middle::mir::*;
@@ -62,9 +61,8 @@ impl SimplifyCfg {
6261
}
6362
}
6463

65-
pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
64+
pub(crate) fn simplify_cfg(body: &mut Body<'_>) {
6665
CfgSimplifier::new(body).simplify();
67-
remove_duplicate_unreachable_blocks(tcx, body);
6866
remove_dead_blocks(body);
6967

7068
// FIXME: Should probably be moved into some kind of pass manager
@@ -76,9 +74,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
7674
self.name()
7775
}
7876

79-
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
77+
fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
8078
debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source);
81-
simplify_cfg(tcx, body);
79+
simplify_cfg(body);
8280
}
8381
}
8482

@@ -289,55 +287,25 @@ pub fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) {
289287
}
290288
}
291289

292-
pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
293-
struct OptApplier<'tcx> {
294-
tcx: TyCtxt<'tcx>,
295-
duplicates: FxIndexSet<BasicBlock>,
296-
}
297-
298-
impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> {
299-
fn tcx(&self) -> TyCtxt<'tcx> {
300-
self.tcx
301-
}
302-
303-
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
304-
for target in terminator.successors_mut() {
305-
// We don't have to check whether `target` is a cleanup block, because have
306-
// entirely excluded cleanup blocks in building the set of duplicates.
307-
if self.duplicates.contains(target) {
308-
*target = self.duplicates[0];
309-
}
310-
}
311-
312-
simplify_duplicate_switch_targets(terminator);
313-
314-
self.super_terminator(terminator, location);
315-
}
316-
}
290+
pub(crate) fn remove_dead_blocks(body: &mut Body<'_>) {
291+
let should_deduplicate_unreachable = |bbdata: &BasicBlockData<'_>| {
292+
// CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
293+
// terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
294+
// before then so we need to handle missing terminators.
295+
// We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
296+
// don't emit empty unreachable cleanup blocks, so this simple check suffices.
297+
bbdata.terminator.is_some() && bbdata.is_empty_unreachable() && !bbdata.is_cleanup
298+
};
317299

318-
let unreachable_blocks = body
300+
let reachable = traversal::reachable_as_bitset(body);
301+
let empty_unreachable_blocks = body
319302
.basic_blocks
320303
.iter_enumerated()
321-
.filter(|(_, bb)| {
322-
// CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
323-
// terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
324-
// before then so we need to handle missing terminators.
325-
// We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
326-
// don't emit empty unreachable cleanup blocks, so this simple check suffices.
327-
bb.terminator.is_some() && bb.is_empty_unreachable() && !bb.is_cleanup
328-
})
329-
.map(|(block, _)| block)
330-
.collect::<FxIndexSet<_>>();
331-
332-
if unreachable_blocks.len() > 1 {
333-
OptApplier { tcx, duplicates: unreachable_blocks }.visit_body(body);
334-
}
335-
}
304+
.filter(|(bb, bbdata)| should_deduplicate_unreachable(bbdata) && reachable.contains(*bb))
305+
.count();
336306

337-
pub fn remove_dead_blocks(body: &mut Body<'_>) {
338-
let reachable = traversal::reachable_as_bitset(body);
339307
let num_blocks = body.basic_blocks.len();
340-
if num_blocks == reachable.count() {
308+
if num_blocks == reachable.count() && empty_unreachable_blocks <= 1 {
341309
return;
342310
}
343311

@@ -346,14 +314,28 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
346314
let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
347315
let mut orig_index = 0;
348316
let mut used_index = 0;
349-
basic_blocks.raw.retain(|_| {
350-
let keep = reachable.contains(BasicBlock::new(orig_index));
351-
if keep {
352-
replacements[orig_index] = BasicBlock::new(used_index);
353-
used_index += 1;
317+
let mut kept_unreachable = None;
318+
basic_blocks.raw.retain(|bbdata| {
319+
let orig_bb = BasicBlock::new(orig_index);
320+
if !reachable.contains(orig_bb) {
321+
orig_index += 1;
322+
return false;
323+
}
324+
325+
let used_bb = BasicBlock::new(used_index);
326+
if should_deduplicate_unreachable(bbdata) {
327+
let kept_unreachable = *kept_unreachable.get_or_insert(used_bb);
328+
if kept_unreachable != used_bb {
329+
replacements[orig_index] = kept_unreachable;
330+
orig_index += 1;
331+
return false;
332+
}
354333
}
334+
335+
replacements[orig_index] = used_bb;
336+
used_index += 1;
355337
orig_index += 1;
356-
keep
338+
true
357339
});
358340

359341
for block in basic_blocks {

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
108108

109109
bb0: {
110110
_39 = discriminant((*(_1.0: &mut {async fn body@$DIR/async_await.rs:15:18: 18:2})));
111-
switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb30];
111+
switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb9];
112112
}
113113

114114
bb1: {
@@ -345,8 +345,4 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
345345
bb29: {
346346
assert(const false, "`async fn` resumed after completion") -> [success: bb29, unwind unreachable];
347347
}
348-
349-
bb30: {
350-
unreachable;
351-
}
352348
}

tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff

+3-7
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
StorageLive(_11);
5757
StorageLive(_12);
5858
_10 = discriminant(_4);
59-
switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7];
59+
switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2];
6060
}
6161

6262
bb1: {
@@ -114,20 +114,16 @@
114114
_3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _13);
115115
StorageDead(_13);
116116
- goto -> bb5;
117-
+ goto -> bb9;
117+
+ goto -> bb8;
118118
}
119119

120120
bb7: {
121-
unreachable;
122-
}
123-
124-
bb8: {
125121
_11 = move ((_4 as Ok).0: i32);
126122
_3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _11);
127123
goto -> bb5;
128124
+ }
129125
+
130-
+ bb9: {
126+
+ bb8: {
131127
+ StorageDead(_12);
132128
+ StorageDead(_11);
133129
+ StorageDead(_10);

tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff

+3-7
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
StorageLive(_11);
5757
StorageLive(_12);
5858
_10 = discriminant(_4);
59-
switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7];
59+
switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2];
6060
}
6161

6262
bb1: {
@@ -114,20 +114,16 @@
114114
_3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _13);
115115
StorageDead(_13);
116116
- goto -> bb5;
117-
+ goto -> bb9;
117+
+ goto -> bb8;
118118
}
119119

120120
bb7: {
121-
unreachable;
122-
}
123-
124-
bb8: {
125121
_11 = move ((_4 as Ok).0: i32);
126122
_3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _11);
127123
goto -> bb5;
128124
+ }
129125
+
130-
+ bb9: {
126+
+ bb8: {
131127
+ StorageDead(_12);
132128
+ StorageDead(_11);
133129
+ StorageDead(_10);

tests/mir-opt/jump_threading.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
5050
// CHECK-LABEL: fn identity(
5151
// CHECK: bb0: {
5252
// CHECK: [[x:_.*]] = _1;
53-
// CHECK: switchInt(move {{_.*}}) -> [0: bb8, 1: bb6, otherwise: bb7];
53+
// CHECK: switchInt(move {{_.*}}) -> [0: bb7, 1: bb6, otherwise: bb2];
5454
// CHECK: bb1: {
5555
// CHECK: {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32);
5656
// CHECK: _0 = Result::<i32, i32>::Ok(
@@ -68,14 +68,12 @@ fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
6868
// CHECK: bb6: {
6969
// CHECK: {{_.*}} = move (([[x]] as Err).0: i32);
7070
// CHECK: [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Break(
71-
// CHECK: goto -> bb9;
71+
// CHECK: goto -> bb8;
7272
// CHECK: bb7: {
73-
// CHECK: unreachable;
74-
// CHECK: bb8: {
7573
// CHECK: {{_.*}} = move (([[x]] as Ok).0: i32);
7674
// CHECK: [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Continue(
7775
// CHECK: goto -> bb5;
78-
// CHECK: bb9: {
76+
// CHECK: bb8: {
7977
// CHECK: goto -> bb3;
8078
Ok(x?)
8179
}

tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff

+12-12
Original file line numberDiff line numberDiff line change
@@ -56,20 +56,16 @@
5656
+ _2 = const Option::<Layout>::None;
5757
StorageLive(_10);
5858
- _10 = discriminant(_2);
59-
- switchInt(move _10) -> [0: bb1, 1: bb3, otherwise: bb2];
59+
- switchInt(move _10) -> [0: bb1, 1: bb2, otherwise: bb6];
6060
+ _10 = const 0_isize;
61-
+ switchInt(const 0_isize) -> [0: bb1, 1: bb3, otherwise: bb2];
61+
+ switchInt(const 0_isize) -> [0: bb1, 1: bb2, otherwise: bb6];
6262
}
6363

6464
bb1: {
6565
_11 = core::panicking::panic(const "called `Option::unwrap()` on a `None` value") -> unwind unreachable;
6666
}
6767

6868
bb2: {
69-
unreachable;
70-
}
71-
72-
bb3: {
7369
- _1 = move ((_2 as Some).0: std::alloc::Layout);
7470
+ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }};
7571
StorageDead(_10);
@@ -79,18 +75,18 @@
7975
StorageLive(_5);
8076
StorageLive(_6);
8177
_9 = const _;
82-
- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind unreachable];
83-
+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb4, unwind unreachable];
78+
- _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb3, unwind unreachable];
79+
+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb3, unwind unreachable];
8480
}
8581

86-
bb4: {
82+
bb3: {
8783
StorageLive(_12);
8884
StorageLive(_15);
8985
_12 = discriminant(_6);
90-
switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb2];
86+
switchInt(move _12) -> [0: bb5, 1: bb4, otherwise: bb6];
9187
}
9288

93-
bb5: {
89+
bb4: {
9490
_15 = const "called `Result::unwrap()` on an `Err` value";
9591
StorageLive(_16);
9692
StorageLive(_17);
@@ -100,7 +96,7 @@
10096
_14 = result::unwrap_failed(move _15, move _16) -> unwind unreachable;
10197
}
10298

103-
bb6: {
99+
bb5: {
104100
_5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>);
105101
StorageDead(_15);
106102
StorageDead(_12);
@@ -115,6 +111,10 @@
115111
StorageDead(_3);
116112
return;
117113
}
114+
115+
bb6: {
116+
unreachable;
117+
}
118118
}
119119
+
120120
+ ALLOC0 (size: 8, align: 4) {

0 commit comments

Comments
 (0)