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: ebb3
|
||||||
; sameln: ebb1
|
; sameln: ebb1
|
||||||
; sameln: ebb0
|
; 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: ebb1
|
||||||
; sameln: ebb0
|
; 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 {
|
function %loop2(i32) native {
|
||||||
ebb0(v0: i32):
|
ebb0(v0: i32):
|
||||||
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
||||||
@@ -82,3 +91,14 @@ function %loop2(i32) native {
|
|||||||
; sameln: ebb2
|
; sameln: ebb2
|
||||||
; sameln: ebb1
|
; sameln: ebb1
|
||||||
; sameln: ebb0
|
; 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
|
test domtree
|
||||||
|
|
||||||
function %test(i32) {
|
function %loop1(i32) {
|
||||||
ebb0(v0: i32):
|
ebb0(v0: i32):
|
||||||
brz v0, ebb1 ; dominates: ebb1 ebb6
|
brz v0, ebb1 ; dominates: ebb1 ebb6
|
||||||
brnz v0, ebb2 ; dominates: ebb2 ebb9
|
brnz v0, ebb2 ; dominates: ebb2 ebb9
|
||||||
@@ -30,7 +30,20 @@ function %test(i32) {
|
|||||||
return
|
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):
|
ebb0(v0: i32):
|
||||||
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
||||||
jump ebb2 ; dominates: ebb2
|
jump ebb2 ; dominates: ebb2
|
||||||
@@ -55,3 +68,12 @@ function %test(i32) native {
|
|||||||
; sameln: ebb2
|
; sameln: ebb2
|
||||||
; sameln: ebb1
|
; sameln: ebb1
|
||||||
; sameln: ebb0
|
; 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:
|
ebb11:
|
||||||
return
|
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:
|
ebb13:
|
||||||
return
|
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.
|
//! 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::flowgraph::ControlFlowGraph;
|
||||||
use cretonne::ir::Function;
|
use cretonne::ir::Function;
|
||||||
use cretonne::ir::entities::AnyEntity;
|
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)
|
run_filecheck(&text, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate some output for filecheck testing
|
// 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();
|
let mut s = String::new();
|
||||||
|
|
||||||
write!(s, "cfg_postorder:")?;
|
write!(s, "cfg_postorder:")?;
|
||||||
@@ -125,5 +125,24 @@ fn filecheck_text(domtree: &DominatorTree) -> result::Result<String, fmt::Error>
|
|||||||
}
|
}
|
||||||
writeln!(s, "")?;
|
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)
|
Ok(s)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use flowgraph::{ControlFlowGraph, BasicBlock};
|
|||||||
use ir::{Ebb, Inst, Function, Layout, ProgramOrder, ExpandedProgramPoint};
|
use ir::{Ebb, Inst, Function, Layout, ProgramOrder, ExpandedProgramPoint};
|
||||||
use ir::instructions::BranchInfo;
|
use ir::instructions::BranchInfo;
|
||||||
use packed_option::PackedOption;
|
use packed_option::PackedOption;
|
||||||
|
use std::cmp;
|
||||||
|
use std::mem;
|
||||||
use timing;
|
use timing;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@@ -83,7 +85,6 @@ impl DominatorTree {
|
|||||||
|
|
||||||
/// Compare two EBBs relative to the reverse post-order.
|
/// Compare two EBBs relative to the reverse post-order.
|
||||||
fn rpo_cmp_ebb(&self, a: Ebb, b: Ebb) -> Ordering {
|
fn rpo_cmp_ebb(&self, a: Ebb, b: Ebb) -> Ordering {
|
||||||
|
|
||||||
self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use cursor::{Cursor, FuncCursor};
|
use cursor::{Cursor, FuncCursor};
|
||||||
@@ -509,6 +669,9 @@ mod test {
|
|||||||
let dtree = DominatorTree::with_function(&func, &cfg);
|
let dtree = DominatorTree::with_function(&func, &cfg);
|
||||||
assert_eq!(0, dtree.nodes.keys().count());
|
assert_eq!(0, dtree.nodes.keys().count());
|
||||||
assert_eq!(dtree.cfg_postorder(), &[]);
|
assert_eq!(dtree.cfg_postorder(), &[]);
|
||||||
|
|
||||||
|
let mut dtpo = DominatorTreePreorder::new();
|
||||||
|
dtpo.compute(&dtree, &func.layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -550,6 +713,18 @@ mod test {
|
|||||||
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
|
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
|
||||||
assert!(!dt.dominates(v2_def, ebb0, &cur.func.layout));
|
assert!(!dt.dominates(v2_def, ebb0, &cur.func.layout));
|
||||||
assert!(!dt.dominates(ebb0, v2_def, &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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user