diff --git a/cranelift/filetests/regalloc/unreachable_code.cton b/cranelift/filetests/regalloc/unreachable_code.cton new file mode 100644 index 0000000000..efb1271105 --- /dev/null +++ b/cranelift/filetests/regalloc/unreachable_code.cton @@ -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 +} + diff --git a/lib/cretonne/src/context.rs b/lib/cretonne/src/context.rs index 4b62721fff..6c04a7f213 100644 --- a/lib/cretonne/src/context.rs +++ b/lib/cretonne/src/context.rs @@ -19,6 +19,7 @@ use legalize_function; use regalloc; use result::{CtonError, CtonResult}; use settings::{FlagsOrIsa, OptLevel}; +use unreachable_code::eliminate_unreachable_code; use verifier; use simple_gvn::do_simple_gvn; use licm::do_licm; @@ -75,6 +76,7 @@ impl Context { self.simple_gvn(isa)?; } self.compute_domtree(); + self.eliminate_unreachable_code(isa)?; self.regalloc(isa)?; self.prologue_epilogue(isa)?; self.relax_branches(isa) @@ -159,6 +161,15 @@ impl Context { self.verify_if(fisa) } + /// Perform unreachable code elimination. + pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CtonResult + where + FOI: Into>, + { + eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree); + self.verify_if(fisa) + } + /// Run the register allocator. pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult { self.regalloc.run( diff --git a/lib/cretonne/src/ir/layout.rs b/lib/cretonne/src/ir/layout.rs index 2b8b5b6099..1664a3ba4d 100644 --- a/lib/cretonne/src/ir/layout.rs +++ b/lib/cretonne/src/ir/layout.rs @@ -360,6 +360,32 @@ impl Layout { 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. pub fn ebbs<'f>(&'f self) -> Ebbs<'f> { Ebbs { diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index 8fb1c393d9..6fbc651218 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -42,4 +42,5 @@ mod scoped_hash_map; mod simple_gvn; mod stack_layout; mod topo_order; +mod unreachable_code; mod write; diff --git a/lib/cretonne/src/unreachable_code.rs b/lib/cretonne/src/unreachable_code.rs new file mode 100644 index 0000000000..f5d49c77f9 --- /dev/null +++ b/lib/cretonne/src/unreachable_code.rs @@ -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); + } +}