Dominator tree pre-order.
Add a DominatorTreePreorder data structure which can be initialized for a DominatorTree and used for queries involving a pre-order of the dominator tree. Print out the pre-order and send it through filecheck in "test domtree" file tests.
This commit is contained in:
@@ -16,3 +16,10 @@ function %test(i32) {
|
||||
; sameln: ebb3
|
||||
; sameln: ebb1
|
||||
; sameln: ebb0
|
||||
|
||||
; check: domtree_preorder {
|
||||
; nextln: ebb0: ebb1
|
||||
; nextln: ebb1: ebb3 ebb2
|
||||
; nextln: ebb3:
|
||||
; nextln: ebb2:
|
||||
; nextln: }
|
||||
|
||||
@@ -50,6 +50,15 @@ function %test(i32) {
|
||||
; sameln: ebb1
|
||||
; sameln: ebb0
|
||||
|
||||
; check: domtree_preorder {
|
||||
; nextln: ebb0: ebb1 ebb2 ebb4 ebb3 ebb5
|
||||
; nextln: ebb1:
|
||||
; nextln: ebb2:
|
||||
; nextln: ebb4:
|
||||
; nextln: ebb3:
|
||||
; nextln: ebb5:
|
||||
; nextln: }
|
||||
|
||||
function %loop2(i32) native {
|
||||
ebb0(v0: i32):
|
||||
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
||||
@@ -82,3 +91,14 @@ function %loop2(i32) native {
|
||||
; sameln: ebb2
|
||||
; sameln: ebb1
|
||||
; sameln: ebb0
|
||||
|
||||
; check: domtree_preorder {
|
||||
; nextln: ebb0: ebb1 ebb2 ebb4 ebb3 ebb5
|
||||
; nextln: ebb1:
|
||||
; nextln: ebb2:
|
||||
; nextln: ebb4: ebb6
|
||||
; nextln: ebb6: ebb7
|
||||
; nextln: ebb7:
|
||||
; nextln: ebb3:
|
||||
; nextln: ebb5:
|
||||
; nextln: }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
test domtree
|
||||
|
||||
function %test(i32) {
|
||||
function %loop1(i32) {
|
||||
ebb0(v0: i32):
|
||||
brz v0, ebb1 ; dominates: ebb1 ebb6
|
||||
brnz v0, ebb2 ; dominates: ebb2 ebb9
|
||||
@@ -30,7 +30,20 @@ function %test(i32) {
|
||||
return
|
||||
}
|
||||
|
||||
function %test(i32) native {
|
||||
; check: domtree_preorder {
|
||||
; nextln: ebb0: ebb1 ebb2 ebb6 ebb3 ebb9
|
||||
; nextln: ebb1:
|
||||
; nextln: ebb2: ebb4 ebb5 ebb7 ebb8
|
||||
; nextln: ebb4:
|
||||
; nextln: ebb5:
|
||||
; nextln: ebb7:
|
||||
; nextln: ebb8:
|
||||
; nextln: ebb6:
|
||||
; nextln: ebb3:
|
||||
; nextln: ebb9:
|
||||
; nextln: }
|
||||
|
||||
function %loop2(i32) native {
|
||||
ebb0(v0: i32):
|
||||
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
||||
jump ebb2 ; dominates: ebb2
|
||||
@@ -55,3 +68,12 @@ function %test(i32) native {
|
||||
; sameln: ebb2
|
||||
; sameln: ebb1
|
||||
; sameln: ebb0
|
||||
|
||||
; check: domtree_preorder {
|
||||
; nextln: ebb0: ebb1 ebb2 ebb4 ebb3 ebb5
|
||||
; nextln: ebb1:
|
||||
; nextln: ebb2:
|
||||
; nextln: ebb4:
|
||||
; nextln: ebb3:
|
||||
; nextln: ebb5:
|
||||
; nextln: }
|
||||
|
||||
@@ -31,3 +31,18 @@ function %test(i32) {
|
||||
ebb11:
|
||||
return
|
||||
}
|
||||
|
||||
; check: domtree_preorder {
|
||||
; nextln: ebb0: ebb1 ebb2 ebb3 ebb5
|
||||
; nextln: ebb1: ebb4
|
||||
; nextln: ebb4: ebb6 ebb7 ebb10
|
||||
; nextln: ebb6: ebb8 ebb9 ebb11
|
||||
; nextln: ebb8:
|
||||
; nextln: ebb9:
|
||||
; nextln: ebb11:
|
||||
; nextln: ebb7:
|
||||
; nextln: ebb10:
|
||||
; nextln: ebb2:
|
||||
; nextln: ebb3:
|
||||
; nextln: ebb5:
|
||||
; nextln: }
|
||||
|
||||
@@ -39,3 +39,20 @@ function %test(i32) {
|
||||
ebb13:
|
||||
return
|
||||
}
|
||||
|
||||
; check: domtree_preorder {
|
||||
; nextln: ebb0: ebb13 ebb1
|
||||
; nextln: ebb13:
|
||||
; nextln: ebb1: ebb2 ebb3 ebb4 ebb5 ebb6 ebb7
|
||||
; nextln: ebb2:
|
||||
; nextln: ebb3:
|
||||
; nextln: ebb4:
|
||||
; nextln: ebb5:
|
||||
; nextln: ebb6:
|
||||
; nextln: ebb7: ebb8 ebb9 ebb10 ebb12 ebb11
|
||||
; nextln: ebb8:
|
||||
; nextln: ebb9:
|
||||
; nextln: ebb10:
|
||||
; nextln: ebb12:
|
||||
; nextln: ebb11:
|
||||
; nextln: }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! We verify that the dominator tree annotations are complete and correct.
|
||||
//!
|
||||
|
||||
use cretonne::dominator_tree::DominatorTree;
|
||||
use cretonne::dominator_tree::{DominatorTree, DominatorTreePreorder};
|
||||
use cretonne::flowgraph::ControlFlowGraph;
|
||||
use cretonne::ir::Function;
|
||||
use cretonne::ir::entities::AnyEntity;
|
||||
@@ -110,13 +110,13 @@ impl SubTest for TestDomtree {
|
||||
}
|
||||
}
|
||||
|
||||
let text = filecheck_text(&domtree).expect("formatting error");
|
||||
let text = filecheck_text(func, &domtree).expect("formatting error");
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate some output for filecheck testing
|
||||
fn filecheck_text(domtree: &DominatorTree) -> result::Result<String, fmt::Error> {
|
||||
fn filecheck_text(func: &Function, domtree: &DominatorTree) -> result::Result<String, fmt::Error> {
|
||||
let mut s = String::new();
|
||||
|
||||
write!(s, "cfg_postorder:")?;
|
||||
@@ -125,5 +125,24 @@ fn filecheck_text(domtree: &DominatorTree) -> result::Result<String, fmt::Error>
|
||||
}
|
||||
writeln!(s, "")?;
|
||||
|
||||
// Compute and print out a pre-order of the dominator tree.
|
||||
writeln!(s, "domtree_preorder {{")?;
|
||||
let mut dtpo = DominatorTreePreorder::new();
|
||||
dtpo.compute(domtree, &func.layout);
|
||||
let mut stack = Vec::new();
|
||||
stack.extend(func.layout.entry_block());
|
||||
while let Some(ebb) = stack.pop() {
|
||||
write!(s, " {}:", ebb)?;
|
||||
let i = stack.len();
|
||||
for ch in dtpo.children(ebb) {
|
||||
write!(s, " {}", ch)?;
|
||||
stack.push(ch);
|
||||
}
|
||||
writeln!(s, "")?;
|
||||
// Reverse the children we just pushed so we'll pop them in order.
|
||||
stack[i..].reverse();
|
||||
}
|
||||
writeln!(s, "}}")?;
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ use flowgraph::{ControlFlowGraph, BasicBlock};
|
||||
use ir::{Ebb, Inst, Function, Layout, ProgramOrder, ExpandedProgramPoint};
|
||||
use ir::instructions::BranchInfo;
|
||||
use packed_option::PackedOption;
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use timing;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
@@ -83,7 +85,6 @@ impl DominatorTree {
|
||||
|
||||
/// Compare two EBBs relative to the reverse post-order.
|
||||
fn rpo_cmp_ebb(&self, a: Ebb, b: Ebb) -> Ordering {
|
||||
|
||||
self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
|
||||
}
|
||||
|
||||
@@ -491,6 +492,165 @@ impl DominatorTree {
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional pre-order information that can be computed for a dominator tree.
|
||||
///
|
||||
/// This data structure is computed from a `DominatorTree` and provides:
|
||||
///
|
||||
/// - A forward traversable dominator tree through the `children()` iterator.
|
||||
/// - An ordering of EBBs according to a dominator tree pre-order.
|
||||
/// - Constant time dominance checks at the EBB granularity.
|
||||
///
|
||||
/// The information in this auxillary data structure is not easy to update when the control flow
|
||||
/// graph changes, which is why it is kept separate.
|
||||
pub struct DominatorTreePreorder {
|
||||
nodes: EntityMap<Ebb, ExtraNode>,
|
||||
|
||||
// Scratch memory used by `compute_postorder()`.
|
||||
stack: Vec<Ebb>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct ExtraNode {
|
||||
/// First child node in the domtree.
|
||||
child: PackedOption<Ebb>,
|
||||
|
||||
/// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO.
|
||||
sibling: PackedOption<Ebb>,
|
||||
|
||||
/// Sequence number for this node in a pre-order traversal of the dominator tree.
|
||||
/// Unreachable blocks have number 0, the entry block is 1.
|
||||
pre_number: u32,
|
||||
|
||||
/// Maximum `pre_number` for the sub-tree of the dominator tree that is rooted at this node.
|
||||
/// This is always >= `pre_number`.
|
||||
pre_max: u32,
|
||||
}
|
||||
|
||||
/// Creating and computing the dominator tree pre-order.
|
||||
impl DominatorTreePreorder {
|
||||
/// Create a new blank `DominatorTreePreorder`.
|
||||
pub fn new() -> DominatorTreePreorder {
|
||||
DominatorTreePreorder {
|
||||
nodes: EntityMap::new(),
|
||||
stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recompute this data structure to match `domtree`.
|
||||
pub fn compute(&mut self, domtree: &DominatorTree, layout: &Layout) {
|
||||
self.nodes.clear();
|
||||
assert_eq!(self.stack.len(), 0);
|
||||
|
||||
// Step 1: Populate the child and sibling links.
|
||||
//
|
||||
// By following the CFG post-order and pushing to the front of the lists, we make sure that
|
||||
// sibling lists are ordered according to the CFG reverse post-order.
|
||||
for &ebb in domtree.cfg_postorder() {
|
||||
if let Some(idom_inst) = domtree.idom(ebb) {
|
||||
let idom = layout.pp_ebb(idom_inst);
|
||||
let sib = mem::replace(&mut self.nodes[idom].child, ebb.into());
|
||||
self.nodes[ebb].sibling = sib;
|
||||
} else {
|
||||
// The only EBB without an immediate dominator is the entry.
|
||||
self.stack.push(ebb);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2. Assign pre-order numbers from a DFS of the dominator tree.
|
||||
assert!(self.stack.len() <= 1);
|
||||
let mut n = 0;
|
||||
while let Some(ebb) = self.stack.pop() {
|
||||
n += 1;
|
||||
let node = &mut self.nodes[ebb];
|
||||
node.pre_number = n;
|
||||
node.pre_max = n;
|
||||
if let Some(n) = node.sibling.expand() {
|
||||
self.stack.push(n);
|
||||
}
|
||||
if let Some(n) = node.child.expand() {
|
||||
self.stack.push(n);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3. Propagate the `pre_max` numbers up the tree.
|
||||
// The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all
|
||||
// its dominator tree children.
|
||||
for &ebb in domtree.cfg_postorder() {
|
||||
if let Some(idom_inst) = domtree.idom(ebb) {
|
||||
let idom = layout.pp_ebb(idom_inst);
|
||||
let pre_max = cmp::max(self.nodes[ebb].pre_max, self.nodes[idom].pre_max);
|
||||
self.nodes[idom].pre_max = pre_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that enumerates the direct children of an EBB in the dominator tree.
|
||||
pub struct ChildIter<'a> {
|
||||
dtpo: &'a DominatorTreePreorder,
|
||||
next: PackedOption<Ebb>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ChildIter<'a> {
|
||||
type Item = Ebb;
|
||||
|
||||
fn next(&mut self) -> Option<Ebb> {
|
||||
let n = self.next.expand();
|
||||
if let Some(ebb) = n {
|
||||
self.next = self.dtpo.nodes[ebb].sibling;
|
||||
}
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
/// Query interface for the dominator tree pre-order.
|
||||
impl DominatorTreePreorder {
|
||||
/// Get an iterator over the direct children of `ebb` in the dominator tree.
|
||||
///
|
||||
/// These are the EBB's whose immediate dominator is an instruction in `ebb`, ordered according
|
||||
/// to the CFG reverse post-order.
|
||||
pub fn children(&self, ebb: Ebb) -> ChildIter {
|
||||
ChildIter {
|
||||
dtpo: self,
|
||||
next: self.nodes[ebb].child,
|
||||
}
|
||||
}
|
||||
|
||||
/// Fast, constant time dominance check with EBB granularity.
|
||||
///
|
||||
/// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant
|
||||
/// time. This is less general than the `DominatorTree` method because it only works with EBB
|
||||
/// program points.
|
||||
///
|
||||
/// An EBB is considered to dominate itself.
|
||||
pub fn dominates(&self, a: Ebb, b: Ebb) -> bool {
|
||||
let na = &self.nodes[a];
|
||||
let nb = &self.nodes[b];
|
||||
na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max
|
||||
}
|
||||
|
||||
/// Compare two EBBs according to the dominator pre-order.
|
||||
pub fn pre_cmp_ebb(&self, a: Ebb, b: Ebb) -> Ordering {
|
||||
self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number)
|
||||
}
|
||||
|
||||
/// Compare two program points according to the dominator tree pre-order.
|
||||
///
|
||||
/// This ordering of program points have the property that given a program point, pp, all the
|
||||
/// program points dominated by pp follow immediately and contiguously after pp in the order.
|
||||
pub fn pre_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
|
||||
where
|
||||
A: Into<ExpandedProgramPoint>,
|
||||
B: Into<ExpandedProgramPoint>,
|
||||
{
|
||||
let a = a.into();
|
||||
let b = b.into();
|
||||
self.pre_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b)).then(
|
||||
layout.cmp(a, b),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use cursor::{Cursor, FuncCursor};
|
||||
@@ -509,6 +669,9 @@ mod test {
|
||||
let dtree = DominatorTree::with_function(&func, &cfg);
|
||||
assert_eq!(0, dtree.nodes.keys().count());
|
||||
assert_eq!(dtree.cfg_postorder(), &[]);
|
||||
|
||||
let mut dtpo = DominatorTreePreorder::new();
|
||||
dtpo.compute(&dtree, &func.layout);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -550,6 +713,18 @@ mod test {
|
||||
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
|
||||
assert!(!dt.dominates(v2_def, ebb0, &cur.func.layout));
|
||||
assert!(!dt.dominates(ebb0, v2_def, &cur.func.layout));
|
||||
|
||||
let mut dtpo = DominatorTreePreorder::new();
|
||||
dtpo.compute(&dt, &cur.func.layout);
|
||||
assert!(dtpo.dominates(ebb0, ebb0));
|
||||
assert!(!dtpo.dominates(ebb0, ebb1));
|
||||
assert!(dtpo.dominates(ebb0, ebb2));
|
||||
assert!(!dtpo.dominates(ebb1, ebb0));
|
||||
assert!(dtpo.dominates(ebb1, ebb1));
|
||||
assert!(!dtpo.dominates(ebb1, ebb2));
|
||||
assert!(!dtpo.dominates(ebb2, ebb0));
|
||||
assert!(!dtpo.dominates(ebb2, ebb1));
|
||||
assert!(dtpo.dominates(ebb2, ebb2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user