Simplify the reverse_postorder_ebbs implementation.

This commit is contained in:
Morgan Phillips
2016-07-28 17:49:25 -07:00
parent e94d7c2a99
commit 82ff64820c
2 changed files with 64 additions and 100 deletions

View File

@@ -24,9 +24,9 @@
use ir::Function; use ir::Function;
use ir::entities::{Inst, Ebb}; use ir::entities::{Inst, Ebb};
use ir::instructions::InstructionData; use ir::instructions::BranchInfo;
use entity_map::EntityMap; use entity_map::EntityMap;
use std::collections::{HashMap, BTreeMap}; use std::collections::{HashSet, BTreeMap};
/// A basic block denoted by its enclosing Ebb and last instruction. /// A basic block denoted by its enclosing Ebb and last instruction.
pub type BasicBlock = (Ebb, Inst); pub type BasicBlock = (Ebb, Inst);
@@ -74,16 +74,18 @@ impl ControlFlowGraph {
for ebb in &func.layout { for ebb in &func.layout {
for inst in func.layout.ebb_insts(ebb) { for inst in func.layout.ebb_insts(ebb) {
match func.dfg[inst] { match func.dfg[inst].analyze_branch() {
InstructionData::Branch { ty: _, opcode: _, ref data } => { BranchInfo::SingleDest(dest, _) => {
cfg.add_successor(ebb, data.destination); cfg.add_successor(ebb, dest);
cfg.add_predecessor(data.destination, (ebb, inst)); cfg.add_predecessor(dest, (ebb, inst));
} }
InstructionData::Jump { ty: _, opcode: _, ref data } => { BranchInfo::Table(jt) => {
cfg.add_successor(ebb, data.destination); for (_, dest) in func.jump_tables[jt].entries() {
cfg.add_predecessor(data.destination, (ebb, inst)); cfg.add_successor(ebb, dest);
cfg.add_predecessor(dest, (ebb, inst));
}
} }
_ => (), BranchInfo::NotABranch => {}
} }
} }
} }
@@ -119,8 +121,33 @@ impl ControlFlowGraph {
} }
Some(eb) => eb, Some(eb) => eb,
}; };
let mut postorder = CFGPostorderWalker::new(&self, entry_block).walk();
let mut grey = HashSet::new();
let mut black = HashSet::new();
let mut stack = vec![entry_block.clone()];
let mut postorder = Vec::new();
while !stack.is_empty() {
let node = stack.pop().unwrap();
if !grey.contains(&node) {
// This is a white node. Mark it as gray.
grey.insert(node);
stack.push(node);
// Get any children weve never seen before.
for child in self.get_successors(node) {
if !grey.contains(child) {
stack.push(child.clone());
}
}
} else if !black.contains(&node) {
// This is a gray node, now becoming black.
// We dont need to mark it since we wont see it again.
postorder.push(node.clone());
black.insert(node.clone());
}
}
postorder.reverse(); postorder.reverse();
let mut result = BTreeMap::new(); let mut result = BTreeMap::new();
for (offset, ebb) in postorder.iter().enumerate() { for (offset, ebb) in postorder.iter().enumerate() {
let i = postorder.len() - offset; let i = postorder.len() - offset;
@@ -141,91 +168,6 @@ impl ControlFlowGraph {
} }
} }
/// A helper for iteratively walking a CFG in postorder.
struct CFGPostorderWalker<'a> {
cfg: &'a ControlFlowGraph,
start: Ebb,
// Ebbs are mapped to a tuple of booleans where the first bool
// is true if the node has been visited, and the second is
// true if its children have been visited.
visited: HashMap<Ebb, (bool, bool)>,
levels: Vec<Vec<Ebb>>,
// Our node index within the current level.
level_index: usize,
}
impl<'a> CFGPostorderWalker<'a> {
fn new(cfg: &ControlFlowGraph, start: Ebb) -> CFGPostorderWalker {
CFGPostorderWalker {
cfg: cfg,
start: start,
visited: HashMap::new(),
levels: Vec::new(),
level_index: 0,
}
}
fn visited(&self, ebb: Ebb) -> bool {
match self.visited.get(&ebb) {
Some(b) => b.0,
None => false,
}
}
fn children_visited(&self, ebb: Ebb) -> bool {
match self.visited.get(&ebb) {
Some(b) => b.1,
None => false,
}
}
fn mark_visited(&mut self, ebb: Ebb) {
let status = self.visited.entry(ebb).or_insert((false, false)).1;
self.visited.insert(ebb, (true, status));
}
fn mark_children_visited(&mut self, ebb: Ebb) {
let status = self.visited.entry(ebb).or_insert((false, false)).0;
self.visited.insert(ebb, (status, true));
}
fn walk(&mut self) -> Vec<Ebb> {
let mut postorder = Vec::new();
self.levels.push(vec![self.start.clone()]);
while self.levels.len() > 0 {
let level_len = self.levels[self.levels.len() - 1].len();
if self.level_index >= level_len {
self.levels.pop();
self.level_index = 0;
continue;
}
let node = self.levels[self.levels.len() - 1][self.level_index].clone();
if !self.visited(node) {
if self.children_visited(node) {
self.mark_visited(node);
postorder.push(node.clone());
self.level_index += 1;
} else {
let edges = self.cfg.get_successors(node);
let outgoing = edges.iter()
.filter(|e| !self.children_visited(**e))
.cloned()
.collect::<Vec<Ebb>>();
if outgoing.len() > 0 {
self.levels.push(outgoing);
}
self.mark_children_visited(node);
}
} else {
self.level_index += 1;
}
}
postorder
}
}
/// Iterate through every mapping of ebb to predecessors in the CFG /// Iterate through every mapping of ebb to predecessors in the CFG
pub struct CFGPredecessorsIter<'a> { pub struct CFGPredecessorsIter<'a> {
cfg: &'a ControlFlowGraph, cfg: &'a ControlFlowGraph,

View File

@@ -44,7 +44,7 @@ fn simple_traversal() {
ebb5: ebb5:
trap trap
} }
", vec![0, 2, 5, 4, 1, 3]); ", vec![0, 2, 1, 3, 4, 5]);
} }
#[test] #[test]
@@ -85,7 +85,7 @@ fn loops_two() {
brz v0, ebb4 brz v0, ebb4
return return
} }
", vec![0, 2, 1, 3, 4, 5]); ", vec![0, 1, 2, 5, 4, 3]);
} }
#[test] #[test]
@@ -104,8 +104,8 @@ fn loops_three() {
jump ebb4 jump ebb4
ebb4: ebb4:
brz v0, ebb3 brz v0, ebb3
brnz v0, ebb5
jump ebb6 jump ebb6
brz v0, ebb5
ebb5: ebb5:
brz v0, ebb4 brz v0, ebb4
trap trap
@@ -114,5 +114,27 @@ fn loops_three() {
ebb7: ebb7:
return return
} }
", vec![0, 2, 1, 3, 4, 5, 6, 7]); ", vec![0, 1, 2, 5, 4, 3, 6, 7]);
}
#[test]
fn back_edge_one() {
test_reverse_postorder_traversal("
function test(i32) {
ebb0(v0: i32):
brz v0, ebb1
jump ebb2
ebb1:
jump ebb3
ebb2:
brz v0, ebb0
jump ebb4
ebb3:
brz v0, ebb2
brnz v0, ebb0
return
ebb4:
trap
}
", vec![0, 1, 3, 2, 4]);
} }