From 90ed698e83be186ea9c534553dc07ff116c9f600 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 9 Oct 2017 15:24:15 -0700 Subject: [PATCH] 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. --- .../filetests/regalloc/unreachable_code.cton | 45 +++++++++++++++++++ lib/cretonne/src/context.rs | 11 +++++ lib/cretonne/src/ir/layout.rs | 26 +++++++++++ lib/cretonne/src/lib.rs | 1 + lib/cretonne/src/unreachable_code.rs | 43 ++++++++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 cranelift/filetests/regalloc/unreachable_code.cton create mode 100644 lib/cretonne/src/unreachable_code.rs 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); + } +}