Skip to content

Commit 5da457a

Browse files
committed
Do not thread through loop headers.
1 parent d9c439e commit 5da457a

File tree

3 files changed

+48
-35
lines changed

3 files changed

+48
-35
lines changed

compiler/rustc_mir_transform/src/jump_threading.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626
//! - bound the maximum depth by a constant `MAX_BACKTRACK`;
2727
//! - we only traverse `Goto` terminators.
2828
//!
29+
//! We try to avoid creating irreducible control-flow by not threading through a loop header.
30+
//!
2931
//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
3032
//! cost by `MAX_COST`.
3133
3234
use rustc_arena::DroplessArena;
3335
use rustc_data_structures::fx::FxHashSet;
36+
use rustc_index::bit_set::BitSet;
3437
use rustc_index::IndexVec;
3538
use rustc_middle::mir::visit::Visitor;
3639
use rustc_middle::mir::*;
@@ -58,14 +61,22 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
5861

5962
let param_env = tcx.param_env_reveal_all_normalized(def_id);
6063
let map = Map::new(tcx, body, Some(MAX_PLACES));
64+
let loop_headers = loop_headers(body);
6165

6266
let arena = DroplessArena::default();
63-
let mut finder =
64-
TOFinder { tcx, param_env, body, arena: &arena, map: &map, opportunities: Vec::new() };
67+
let mut finder = TOFinder {
68+
tcx,
69+
param_env,
70+
body,
71+
arena: &arena,
72+
map: &map,
73+
loop_headers: &loop_headers,
74+
opportunities: Vec::new(),
75+
};
6576

6677
for (bb, bbdata) in body.basic_blocks.iter_enumerated() {
6778
debug!(?bb, term = ?bbdata.terminator());
68-
if bbdata.is_cleanup {
79+
if bbdata.is_cleanup || loop_headers.contains(bb) {
6980
continue;
7081
}
7182
let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { continue };
@@ -108,6 +119,10 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
108119
return;
109120
}
110121

122+
// Verify that we do not thread through a loop header.
123+
for to in opportunities.iter() {
124+
assert!(to.chain.iter().all(|&block| !loop_headers.contains(block)));
125+
}
111126
OpportunitySet::new(body, opportunities).apply(body);
112127
}
113128
}
@@ -125,6 +140,7 @@ struct TOFinder<'tcx, 'a> {
125140
param_env: ty::ParamEnv<'tcx>,
126141
body: &'a Body<'tcx>,
127142
map: &'a Map,
143+
loop_headers: &'a BitSet<BasicBlock>,
128144
/// We use an arena to avoid cloning the slices when cloning `state`.
129145
arena: &'a DroplessArena,
130146
opportunities: Vec<ThreadingOpportunity>,
@@ -180,6 +196,11 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
180196
mut cost: CostChecker<'_, 'tcx>,
181197
depth: usize,
182198
) {
199+
// Do not thread through loop headers.
200+
if self.loop_headers.contains(bb) {
201+
return;
202+
}
203+
183204
debug!(cost = ?cost.cost());
184205
for (statement_index, stmt) in
185206
self.body.basic_blocks[bb].statements.iter().enumerate().rev()
@@ -636,3 +657,21 @@ enum Update {
636657
Incr,
637658
Decr,
638659
}
660+
661+
/// Compute the set of loop headers in the given body. We define a loop header as a block which has
662+
/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs.
663+
/// But if the CFG is already irreducible, there is no point in trying much harder.
664+
/// is already irreducibl
665+
fn loop_headers(body: &Body<'_>) -> BitSet<BasicBlock> {
666+
let mut loop_headers = BitSet::new_empty(body.basic_blocks.len());
667+
let dominators = body.basic_blocks.dominators();
668+
// Only visit reachable blocks.
669+
for (bb, bbdata) in traversal::preorder(body) {
670+
for succ in bbdata.terminator().successors() {
671+
if dominators.dominates(succ, bb) {
672+
loop_headers.insert(bb);
673+
}
674+
}
675+
}
676+
loop_headers
677+
}

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

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@
2525

2626
bb1: {
2727
_4 = discriminant(_1);
28-
- switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
29-
+ goto -> bb2;
28+
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
3029
}
3130

3231
bb2: {
@@ -46,8 +45,7 @@
4645
_1 = move _5;
4746
_3 = const ();
4847
StorageDead(_5);
49-
- goto -> bb1;
50-
+ goto -> bb8;
48+
goto -> bb1;
5149
}
5250

5351
bb5: {
@@ -56,8 +54,7 @@
5654
_1 = move _6;
5755
_3 = const ();
5856
StorageDead(_6);
59-
- goto -> bb1;
60-
+ goto -> bb9;
57+
goto -> bb1;
6158
}
6259

6360
bb6: {
@@ -72,16 +69,6 @@
7269
+ bb7: {
7370
+ _4 = discriminant(_1);
7471
+ goto -> bb4;
75-
+ }
76-
+
77-
+ bb8: {
78-
+ _4 = discriminant(_1);
79-
+ goto -> bb5;
80-
+ }
81-
+
82-
+ bb9: {
83-
+ _4 = discriminant(_1);
84-
+ goto -> bb6;
8572
}
8673
}
8774

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

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@
2525

2626
bb1: {
2727
_4 = discriminant(_1);
28-
- switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
29-
+ goto -> bb2;
28+
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
3029
}
3130

3231
bb2: {
@@ -46,8 +45,7 @@
4645
_1 = move _5;
4746
_3 = const ();
4847
StorageDead(_5);
49-
- goto -> bb1;
50-
+ goto -> bb8;
48+
goto -> bb1;
5149
}
5250

5351
bb5: {
@@ -56,8 +54,7 @@
5654
_1 = move _6;
5755
_3 = const ();
5856
StorageDead(_6);
59-
- goto -> bb1;
60-
+ goto -> bb9;
57+
goto -> bb1;
6158
}
6259

6360
bb6: {
@@ -72,16 +69,6 @@
7269
+ bb7: {
7370
+ _4 = discriminant(_1);
7471
+ goto -> bb4;
75-
+ }
76-
+
77-
+ bb8: {
78-
+ _4 = discriminant(_1);
79-
+ goto -> bb5;
80-
+ }
81-
+
82-
+ bb9: {
83-
+ _4 = discriminant(_1);
84-
+ goto -> bb6;
8572
}
8673
}
8774

0 commit comments

Comments
 (0)