Skip to content

Commit 7027d07

Browse files
committed
Prune unreachable HIR blocks after optimization
git-bug: 33e89c8b9a2ef0be93c7c89416384ccc68541d9a33c15965864fa408bb3eeabe
1 parent 3e4846c commit 7027d07

2 files changed

Lines changed: 70 additions & 0 deletions

File tree

‎crates/compiler/src/cfg_simplify.rs‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,40 @@ pub struct CfgSimplifyStats {
3737
/// Empty blocks whose predecessors now branch straight to their
3838
/// target.
3939
pub threaded: usize,
40+
/// Blocks with no path from the function entry.
41+
pub unreachable_removed: usize,
42+
}
43+
44+
/// Drop blocks left disconnected by CFG rewrites, and their stale phi edges.
45+
/// Reachability follows terminators, which are the edges codegen executes.
46+
pub fn prune_unreachable(func: &mut HirFunction) -> usize {
47+
let mut reachable = HashSet::new();
48+
let mut pending = vec![func.entry_block];
49+
while let Some(id) = pending.pop() {
50+
if !reachable.insert(id) {
51+
continue;
52+
}
53+
if let Some(block) = func.blocks.get(&id) {
54+
pending.extend(block.terminator.targets());
55+
}
56+
}
57+
let before = func.blocks.len();
58+
if before == reachable.len() {
59+
return 0;
60+
}
61+
func.blocks.retain(|id, _| reachable.contains(id));
62+
for block in func.blocks.values_mut() {
63+
block.predecessors.retain(|id| reachable.contains(id));
64+
block.successors.retain(|id| reachable.contains(id));
65+
for phi in &mut block.phis {
66+
phi.incoming.retain(|(_, pred)| reachable.contains(pred));
67+
}
68+
}
69+
before - func.blocks.len()
70+
}
71+
72+
pub fn prune_unreachable_module(module: &mut HirModule) -> usize {
73+
module.functions_to_optimize().map(prune_unreachable).sum()
4074
}
4175

4276
/// Run on one function. Iterates until no merge fires (covers chains
@@ -410,4 +444,37 @@ mod tests {
410444
let stats = run(&mut f);
411445
assert_eq!(stats.merged, 0);
412446
}
447+
448+
#[test]
449+
fn prune_unreachable_removes_orphan_phi_edges() {
450+
let mut f = mk_func();
451+
let entry = f.entry_block;
452+
let join = HirId::new();
453+
let orphan = HirId::new();
454+
let live_value = add_const(&mut f, HirType::I64, HirConstant::I64(7));
455+
let dead_value = add_const(&mut f, HirType::I64, HirConstant::I64(8));
456+
let result = HirId::new();
457+
f.blocks.insert(entry, HirBlock::new(entry));
458+
f.blocks.insert(join, HirBlock::new(join));
459+
f.blocks.insert(orphan, HirBlock::new(orphan));
460+
f.blocks.get_mut(&entry).unwrap().terminator = HirTerminator::Branch { target: join };
461+
f.blocks.get_mut(&entry).unwrap().successors = vec![join];
462+
f.blocks.get_mut(&orphan).unwrap().terminator = HirTerminator::Branch { target: join };
463+
f.blocks.get_mut(&orphan).unwrap().successors = vec![join];
464+
let join_block = f.blocks.get_mut(&join).unwrap();
465+
join_block.predecessors = vec![entry, orphan];
466+
join_block.phis.push(crate::hir::HirPhi {
467+
result,
468+
ty: HirType::I64,
469+
incoming: vec![(live_value, entry), (dead_value, orphan)],
470+
});
471+
join_block.terminator = HirTerminator::Return {
472+
values: vec![result],
473+
};
474+
475+
assert_eq!(prune_unreachable(&mut f), 1);
476+
assert!(!f.blocks.contains_key(&orphan));
477+
assert_eq!(f.blocks[&join].predecessors, vec![entry]);
478+
assert_eq!(f.blocks[&join].phis[0].incoming, vec![(live_value, entry)]);
479+
}
413480
}

‎crates/compiler/src/lib.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,9 @@ fn run_interp_safe_opts_with(module: &mut HirModule, expand_box_reads: bool) ->
23242324
timed("licm+cse", &mut at);
23252325
}
23262326

2327+
stats.cfg_simplify.unreachable_removed += cfg_simplify::prune_unreachable_module(module);
2328+
timed("prune_unreachable", &mut at);
2329+
23272330
stats
23282331
}
23292332

0 commit comments

Comments
 (0)