Do not thread through loop headers.
This commit is contained in:
parent
751a079413
commit
0d0a536777
3 changed files with 48 additions and 35 deletions
|
@ -26,11 +26,14 @@
|
||||||
//! - bound the maximum depth by a constant `MAX_BACKTRACK`;
|
//! - bound the maximum depth by a constant `MAX_BACKTRACK`;
|
||||||
//! - we only traverse `Goto` terminators.
|
//! - we only traverse `Goto` terminators.
|
||||||
//!
|
//!
|
||||||
|
//! We try to avoid creating irreducible control-flow by not threading through a loop header.
|
||||||
|
//!
|
||||||
//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
|
//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
|
||||||
//! cost by `MAX_COST`.
|
//! cost by `MAX_COST`.
|
||||||
|
|
||||||
use rustc_arena::DroplessArena;
|
use rustc_arena::DroplessArena;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_index::IndexVec;
|
use rustc_index::IndexVec;
|
||||||
use rustc_middle::mir::visit::Visitor;
|
use rustc_middle::mir::visit::Visitor;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
|
@ -58,14 +61,22 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
|
||||||
|
|
||||||
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
||||||
let map = Map::new(tcx, body, Some(MAX_PLACES));
|
let map = Map::new(tcx, body, Some(MAX_PLACES));
|
||||||
|
let loop_headers = loop_headers(body);
|
||||||
|
|
||||||
let arena = DroplessArena::default();
|
let arena = DroplessArena::default();
|
||||||
let mut finder =
|
let mut finder = TOFinder {
|
||||||
TOFinder { tcx, param_env, body, arena: &arena, map: &map, opportunities: Vec::new() };
|
tcx,
|
||||||
|
param_env,
|
||||||
|
body,
|
||||||
|
arena: &arena,
|
||||||
|
map: &map,
|
||||||
|
loop_headers: &loop_headers,
|
||||||
|
opportunities: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
for (bb, bbdata) in body.basic_blocks.iter_enumerated() {
|
for (bb, bbdata) in body.basic_blocks.iter_enumerated() {
|
||||||
debug!(?bb, term = ?bbdata.terminator());
|
debug!(?bb, term = ?bbdata.terminator());
|
||||||
if bbdata.is_cleanup {
|
if bbdata.is_cleanup || loop_headers.contains(bb) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { continue };
|
let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { continue };
|
||||||
|
@ -108,6 +119,10 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that we do not thread through a loop header.
|
||||||
|
for to in opportunities.iter() {
|
||||||
|
assert!(to.chain.iter().all(|&block| !loop_headers.contains(block)));
|
||||||
|
}
|
||||||
OpportunitySet::new(body, opportunities).apply(body);
|
OpportunitySet::new(body, opportunities).apply(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,6 +140,7 @@ struct TOFinder<'tcx, 'a> {
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
body: &'a Body<'tcx>,
|
body: &'a Body<'tcx>,
|
||||||
map: &'a Map,
|
map: &'a Map,
|
||||||
|
loop_headers: &'a BitSet<BasicBlock>,
|
||||||
/// We use an arena to avoid cloning the slices when cloning `state`.
|
/// We use an arena to avoid cloning the slices when cloning `state`.
|
||||||
arena: &'a DroplessArena,
|
arena: &'a DroplessArena,
|
||||||
opportunities: Vec<ThreadingOpportunity>,
|
opportunities: Vec<ThreadingOpportunity>,
|
||||||
|
@ -180,6 +196,11 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
|
||||||
mut cost: CostChecker<'_, 'tcx>,
|
mut cost: CostChecker<'_, 'tcx>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) {
|
) {
|
||||||
|
// Do not thread through loop headers.
|
||||||
|
if self.loop_headers.contains(bb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
debug!(cost = ?cost.cost());
|
debug!(cost = ?cost.cost());
|
||||||
for (statement_index, stmt) in
|
for (statement_index, stmt) in
|
||||||
self.body.basic_blocks[bb].statements.iter().enumerate().rev()
|
self.body.basic_blocks[bb].statements.iter().enumerate().rev()
|
||||||
|
@ -636,3 +657,21 @@ enum Update {
|
||||||
Incr,
|
Incr,
|
||||||
Decr,
|
Decr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the set of loop headers in the given body. We define a loop header as a block which has
|
||||||
|
/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs.
|
||||||
|
/// But if the CFG is already irreducible, there is no point in trying much harder.
|
||||||
|
/// is already irreducibl
|
||||||
|
fn loop_headers(body: &Body<'_>) -> BitSet<BasicBlock> {
|
||||||
|
let mut loop_headers = BitSet::new_empty(body.basic_blocks.len());
|
||||||
|
let dominators = body.basic_blocks.dominators();
|
||||||
|
// Only visit reachable blocks.
|
||||||
|
for (bb, bbdata) in traversal::preorder(body) {
|
||||||
|
for succ in bbdata.terminator().successors() {
|
||||||
|
if dominators.dominates(succ, bb) {
|
||||||
|
loop_headers.insert(bb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop_headers
|
||||||
|
}
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
_4 = discriminant(_1);
|
_4 = discriminant(_1);
|
||||||
- switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
|
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
|
||||||
+ goto -> bb2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
bb2: {
|
||||||
|
@ -46,8 +45,7 @@
|
||||||
_1 = move _5;
|
_1 = move _5;
|
||||||
_3 = const ();
|
_3 = const ();
|
||||||
StorageDead(_5);
|
StorageDead(_5);
|
||||||
- goto -> bb1;
|
goto -> bb1;
|
||||||
+ goto -> bb8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb5: {
|
bb5: {
|
||||||
|
@ -56,8 +54,7 @@
|
||||||
_1 = move _6;
|
_1 = move _6;
|
||||||
_3 = const ();
|
_3 = const ();
|
||||||
StorageDead(_6);
|
StorageDead(_6);
|
||||||
- goto -> bb1;
|
goto -> bb1;
|
||||||
+ goto -> bb9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb6: {
|
bb6: {
|
||||||
|
@ -72,16 +69,6 @@
|
||||||
+ bb7: {
|
+ bb7: {
|
||||||
+ _4 = discriminant(_1);
|
+ _4 = discriminant(_1);
|
||||||
+ goto -> bb4;
|
+ goto -> bb4;
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ bb8: {
|
|
||||||
+ _4 = discriminant(_1);
|
|
||||||
+ goto -> bb5;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ bb9: {
|
|
||||||
+ _4 = discriminant(_1);
|
|
||||||
+ goto -> bb6;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
_4 = discriminant(_1);
|
_4 = discriminant(_1);
|
||||||
- switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
|
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
|
||||||
+ goto -> bb2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
bb2: {
|
||||||
|
@ -46,8 +45,7 @@
|
||||||
_1 = move _5;
|
_1 = move _5;
|
||||||
_3 = const ();
|
_3 = const ();
|
||||||
StorageDead(_5);
|
StorageDead(_5);
|
||||||
- goto -> bb1;
|
goto -> bb1;
|
||||||
+ goto -> bb8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb5: {
|
bb5: {
|
||||||
|
@ -56,8 +54,7 @@
|
||||||
_1 = move _6;
|
_1 = move _6;
|
||||||
_3 = const ();
|
_3 = const ();
|
||||||
StorageDead(_6);
|
StorageDead(_6);
|
||||||
- goto -> bb1;
|
goto -> bb1;
|
||||||
+ goto -> bb9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb6: {
|
bb6: {
|
||||||
|
@ -72,16 +69,6 @@
|
||||||
+ bb7: {
|
+ bb7: {
|
||||||
+ _4 = discriminant(_1);
|
+ _4 = discriminant(_1);
|
||||||
+ goto -> bb4;
|
+ goto -> bb4;
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ bb8: {
|
|
||||||
+ _4 = discriminant(_1);
|
|
||||||
+ goto -> bb5;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ bb9: {
|
|
||||||
+ _4 = discriminant(_1);
|
|
||||||
+ goto -> bb6;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue