Add an unreachable code elimination pass.
The register allocator doesn't even try to compile unreachable EBBs, so any values defined in such blocks won't be assigned registers. Since the dominator tree already has determined which EBBs are reachable, we should just eliminate any unreachable blocks instead o trying to do something with the dead code. Not that this is not a "dead code elimination" pass which would also remove individual instructions whose results are not used.
This commit is contained in:
45
cranelift/filetests/regalloc/unreachable_code.cton
Normal file
45
cranelift/filetests/regalloc/unreachable_code.cton
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
test compile
|
||||||
|
set is_64bit
|
||||||
|
isa intel haswell
|
||||||
|
|
||||||
|
; This function contains unreachable blocks which trip up the register
|
||||||
|
; allocator if they don't get cleared out.
|
||||||
|
function %unreachable_blocks(i64 vmctx) -> i32 spiderwasm {
|
||||||
|
ebb0(v0: i64):
|
||||||
|
v1 = iconst.i32 0
|
||||||
|
v2 = iconst.i32 0
|
||||||
|
jump ebb2
|
||||||
|
|
||||||
|
ebb2:
|
||||||
|
jump ebb4
|
||||||
|
|
||||||
|
ebb4:
|
||||||
|
jump ebb2
|
||||||
|
|
||||||
|
; Everything below this point is unreachable.
|
||||||
|
|
||||||
|
ebb3(v3: i32):
|
||||||
|
v5 = iadd.i32 v2, v3
|
||||||
|
jump ebb6
|
||||||
|
|
||||||
|
ebb6:
|
||||||
|
jump ebb6
|
||||||
|
|
||||||
|
ebb7(v6: i32):
|
||||||
|
v7 = iadd.i32 v5, v6
|
||||||
|
jump ebb8
|
||||||
|
|
||||||
|
ebb8:
|
||||||
|
jump ebb10
|
||||||
|
|
||||||
|
ebb10:
|
||||||
|
jump ebb8
|
||||||
|
|
||||||
|
ebb9(v8: i32):
|
||||||
|
v10 = iadd.i32 v7, v8
|
||||||
|
jump ebb1(v10)
|
||||||
|
|
||||||
|
ebb1(v11: i32):
|
||||||
|
return v11
|
||||||
|
}
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ use legalize_function;
|
|||||||
use regalloc;
|
use regalloc;
|
||||||
use result::{CtonError, CtonResult};
|
use result::{CtonError, CtonResult};
|
||||||
use settings::{FlagsOrIsa, OptLevel};
|
use settings::{FlagsOrIsa, OptLevel};
|
||||||
|
use unreachable_code::eliminate_unreachable_code;
|
||||||
use verifier;
|
use verifier;
|
||||||
use simple_gvn::do_simple_gvn;
|
use simple_gvn::do_simple_gvn;
|
||||||
use licm::do_licm;
|
use licm::do_licm;
|
||||||
@@ -75,6 +76,7 @@ impl Context {
|
|||||||
self.simple_gvn(isa)?;
|
self.simple_gvn(isa)?;
|
||||||
}
|
}
|
||||||
self.compute_domtree();
|
self.compute_domtree();
|
||||||
|
self.eliminate_unreachable_code(isa)?;
|
||||||
self.regalloc(isa)?;
|
self.regalloc(isa)?;
|
||||||
self.prologue_epilogue(isa)?;
|
self.prologue_epilogue(isa)?;
|
||||||
self.relax_branches(isa)
|
self.relax_branches(isa)
|
||||||
@@ -159,6 +161,15 @@ impl Context {
|
|||||||
self.verify_if(fisa)
|
self.verify_if(fisa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform unreachable code elimination.
|
||||||
|
pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CtonResult
|
||||||
|
where
|
||||||
|
FOI: Into<FlagsOrIsa<'a>>,
|
||||||
|
{
|
||||||
|
eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
|
||||||
|
self.verify_if(fisa)
|
||||||
|
}
|
||||||
|
|
||||||
/// Run the register allocator.
|
/// Run the register allocator.
|
||||||
pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
|
pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
|
||||||
self.regalloc.run(
|
self.regalloc.run(
|
||||||
|
|||||||
@@ -360,6 +360,32 @@ impl Layout {
|
|||||||
self.assign_ebb_seq(ebb);
|
self.assign_ebb_seq(ebb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove `ebb` from the layout.
|
||||||
|
pub fn remove_ebb(&mut self, ebb: Ebb) {
|
||||||
|
assert!(self.is_ebb_inserted(ebb), "EBB not in the layout");
|
||||||
|
assert!(self.first_inst(ebb).is_none(), "EBB must be empty.");
|
||||||
|
|
||||||
|
// Clear the `ebb` node and extract links.
|
||||||
|
let prev;
|
||||||
|
let next;
|
||||||
|
{
|
||||||
|
let n = &mut self.ebbs[ebb];
|
||||||
|
prev = n.prev;
|
||||||
|
next = n.next;
|
||||||
|
n.prev = None.into();
|
||||||
|
n.next = None.into();
|
||||||
|
}
|
||||||
|
// Fix up links to `ebb`.
|
||||||
|
match prev.expand() {
|
||||||
|
None => self.first_ebb = next.expand(),
|
||||||
|
Some(p) => self.ebbs[p].next = next,
|
||||||
|
}
|
||||||
|
match next.expand() {
|
||||||
|
None => self.last_ebb = prev.expand(),
|
||||||
|
Some(n) => self.ebbs[n].prev = prev,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return an iterator over all EBBs in layout order.
|
/// Return an iterator over all EBBs in layout order.
|
||||||
pub fn ebbs<'f>(&'f self) -> Ebbs<'f> {
|
pub fn ebbs<'f>(&'f self) -> Ebbs<'f> {
|
||||||
Ebbs {
|
Ebbs {
|
||||||
|
|||||||
@@ -42,4 +42,5 @@ mod scoped_hash_map;
|
|||||||
mod simple_gvn;
|
mod simple_gvn;
|
||||||
mod stack_layout;
|
mod stack_layout;
|
||||||
mod topo_order;
|
mod topo_order;
|
||||||
|
mod unreachable_code;
|
||||||
mod write;
|
mod write;
|
||||||
|
|||||||
43
lib/cretonne/src/unreachable_code.rs
Normal file
43
lib/cretonne/src/unreachable_code.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//! Unreachable code elimination.
|
||||||
|
|
||||||
|
use cursor::{Cursor, FuncCursor};
|
||||||
|
use dominator_tree::DominatorTree;
|
||||||
|
use flowgraph::ControlFlowGraph;
|
||||||
|
use ir;
|
||||||
|
|
||||||
|
/// Eliminate unreachable code.
|
||||||
|
///
|
||||||
|
/// This pass deletes whole EBBs that can't be reached from the entry block. It does not delete
|
||||||
|
/// individual instructions whose results are unused.
|
||||||
|
///
|
||||||
|
/// The reachability analysis is performed by the dominator tree analysis.
|
||||||
|
pub fn eliminate_unreachable_code(
|
||||||
|
func: &mut ir::Function,
|
||||||
|
cfg: &mut ControlFlowGraph,
|
||||||
|
domtree: &DominatorTree,
|
||||||
|
) {
|
||||||
|
let mut pos = FuncCursor::new(func);
|
||||||
|
while let Some(ebb) = pos.next_ebb() {
|
||||||
|
if domtree.is_reachable(ebb) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!("Eliminating unreachable {}", ebb);
|
||||||
|
// Move the cursor out of the way and make sure the next lop iteration goes to the right
|
||||||
|
// EBB.
|
||||||
|
pos.prev_ebb();
|
||||||
|
|
||||||
|
// Remove all instructions from `ebb`.
|
||||||
|
while let Some(inst) = pos.func.layout.first_inst(ebb) {
|
||||||
|
dbg!(" - {}", pos.func.dfg.display_inst(inst, None));
|
||||||
|
pos.func.layout.remove_inst(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the EBB is completely empty, we can update the CFG which removes it from any
|
||||||
|
// predecessor lists.
|
||||||
|
cfg.recompute_ebb(pos.func, ebb);
|
||||||
|
|
||||||
|
// Finally, remove the EBB from the layout.
|
||||||
|
pos.func.layout.remove_ebb(ebb);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user