Preprocess dominator tree to answer queries in O(1)
This commit is contained in:
parent
6c64870fa6
commit
aa1267f630
7 changed files with 137 additions and 55 deletions
|
@ -26,7 +26,7 @@ rustc_index::newtype_index! {
|
|||
struct PreorderIndex {}
|
||||
}
|
||||
|
||||
pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
|
||||
pub fn dominator_tree<G: ControlFlowGraph>(graph: G) -> DominatorTree<G::Node> {
|
||||
// compute the post order index (rank) for each node
|
||||
let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
|
||||
|
||||
|
@ -244,7 +244,7 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
|
|||
|
||||
let start_node = graph.start_node();
|
||||
immediate_dominators[start_node] = None;
|
||||
Dominators { start_node, post_order_rank, immediate_dominators }
|
||||
DominatorTree { start_node, post_order_rank, immediate_dominators }
|
||||
}
|
||||
|
||||
/// Evaluate the link-eval virtual forest, providing the currently minimum semi
|
||||
|
@ -309,16 +309,18 @@ fn compress(
|
|||
|
||||
/// Tracks the list of dominators for each node.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Dominators<N: Idx> {
|
||||
pub struct DominatorTree<N: Idx> {
|
||||
start_node: N,
|
||||
post_order_rank: IndexVec<N, usize>,
|
||||
// Even though we track only the immediate dominator of each node, it's
|
||||
// possible to get its full list of dominators by looking up the dominator
|
||||
// of each dominator. (See the `impl Iterator for Iter` definition).
|
||||
//
|
||||
// Note: immediate_dominators[root] is Some(root)!
|
||||
immediate_dominators: IndexVec<N, Option<N>>,
|
||||
}
|
||||
|
||||
impl<Node: Idx> Dominators<Node> {
|
||||
impl<Node: Idx> DominatorTree<Node> {
|
||||
/// Returns true if node is reachable from the start node.
|
||||
pub fn is_reachable(&self, node: Node) -> bool {
|
||||
node == self.start_node || self.immediate_dominators[node].is_some()
|
||||
|
@ -333,12 +335,7 @@ impl<Node: Idx> Dominators<Node> {
|
|||
/// See the `impl Iterator for Iter` definition to understand how this works.
|
||||
pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
|
||||
assert!(self.is_reachable(node), "node {node:?} is not reachable");
|
||||
Iter { dominators: self, node: Some(node) }
|
||||
}
|
||||
|
||||
pub fn dominates(&self, dom: Node, node: Node) -> bool {
|
||||
// FIXME -- could be optimized by using post-order-rank
|
||||
self.dominators(node).any(|n| n == dom)
|
||||
Iter { dom_tree: self, node: Some(node) }
|
||||
}
|
||||
|
||||
/// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
|
||||
|
@ -351,7 +348,7 @@ impl<Node: Idx> Dominators<Node> {
|
|||
}
|
||||
|
||||
pub struct Iter<'dom, Node: Idx> {
|
||||
dominators: &'dom Dominators<Node>,
|
||||
dom_tree: &'dom DominatorTree<Node>,
|
||||
node: Option<Node>,
|
||||
}
|
||||
|
||||
|
@ -360,10 +357,96 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(node) = self.node {
|
||||
self.node = self.dominators.immediate_dominator(node);
|
||||
self.node = self.dom_tree.immediate_dominator(node);
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Dominators<Node: Idx> {
|
||||
time: IndexVec<Node, Time>,
|
||||
}
|
||||
|
||||
/// Describes the number of vertices discovered at the time when processing of a particular vertex
|
||||
/// started and when it finished. Both values are zero for unreachable vertices.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
struct Time {
|
||||
start: u32,
|
||||
finish: u32,
|
||||
}
|
||||
|
||||
impl<Node: Idx> Dominators<Node> {
|
||||
pub fn dummy() -> Self {
|
||||
Self { time: Default::default() }
|
||||
}
|
||||
|
||||
/// Returns true if `a` dominates `b`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `b` is unreachable.
|
||||
pub fn dominates(&self, a: Node, b: Node) -> bool {
|
||||
let a = self.time[a];
|
||||
let b = self.time[b];
|
||||
assert!(b.start != 0, "node {b:?} is not reachable");
|
||||
a.start <= b.start && b.finish <= a.finish
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dominators<N: Idx>(tree: &DominatorTree<N>) -> Dominators<N> {
|
||||
let DominatorTree { start_node, ref immediate_dominators, post_order_rank: _ } = *tree;
|
||||
|
||||
// Transpose the dominator tree edges, so that child nodes of vertex v are stored in
|
||||
// node[edges[v].start..edges[y].end].
|
||||
let mut edges: IndexVec<N, std::ops::Range<u32>> =
|
||||
IndexVec::from_elem(0..0, immediate_dominators);
|
||||
for &idom in immediate_dominators.iter() {
|
||||
if let Some(idom) = idom {
|
||||
edges[idom].end += 1;
|
||||
}
|
||||
}
|
||||
let mut m = 0;
|
||||
for e in edges.iter_mut() {
|
||||
m += e.end;
|
||||
e.start = m;
|
||||
e.end = m;
|
||||
}
|
||||
let mut node = IndexVec::from_elem_n(Idx::new(0), m.try_into().unwrap());
|
||||
for (i, &idom) in immediate_dominators.iter_enumerated() {
|
||||
if let Some(idom) = idom {
|
||||
edges[idom].start -= 1;
|
||||
node[edges[idom].start] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform a depth-first search of the dominator tree. Record the number of vertices discovered
|
||||
// when vertex v is discovered first as time[v].start, and when its processing is finished as
|
||||
// time[v].finish.
|
||||
let mut time: IndexVec<N, Time> = IndexVec::from_elem(Time::default(), immediate_dominators);
|
||||
let mut stack = Vec::new();
|
||||
|
||||
let mut discovered = 1;
|
||||
stack.push(start_node);
|
||||
time[start_node].start = discovered;
|
||||
|
||||
while let Some(&i) = stack.last() {
|
||||
let e = &mut edges[i];
|
||||
if e.start == e.end {
|
||||
// Finish processing vertex i.
|
||||
time[i].finish = discovered;
|
||||
stack.pop();
|
||||
} else {
|
||||
let j = node[e.start];
|
||||
e.start += 1;
|
||||
// Start processing vertex j.
|
||||
discovered += 1;
|
||||
time[j].start = discovered;
|
||||
stack.push(j);
|
||||
}
|
||||
}
|
||||
|
||||
Dominators { time }
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use super::super::tests::TestGraph;
|
|||
fn diamond() {
|
||||
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
|
||||
|
||||
let dominators = dominators(&graph);
|
||||
let immediate_dominators = &dominators.immediate_dominators;
|
||||
let tree = dominator_tree(&graph);
|
||||
let immediate_dominators = &tree.immediate_dominators;
|
||||
assert_eq!(immediate_dominators[0], None);
|
||||
assert_eq!(immediate_dominators[1], Some(0));
|
||||
assert_eq!(immediate_dominators[2], Some(0));
|
||||
|
@ -22,8 +22,8 @@ fn paper() {
|
|||
&[(6, 5), (6, 4), (5, 1), (4, 2), (4, 3), (1, 2), (2, 3), (3, 2), (2, 1)],
|
||||
);
|
||||
|
||||
let dominators = dominators(&graph);
|
||||
let immediate_dominators = &dominators.immediate_dominators;
|
||||
let dom_tree = dominator_tree(&graph);
|
||||
let immediate_dominators = &dom_tree.immediate_dominators;
|
||||
assert_eq!(immediate_dominators[0], None); // <-- note that 0 is not in graph
|
||||
assert_eq!(immediate_dominators[1], Some(6));
|
||||
assert_eq!(immediate_dominators[2], Some(6));
|
||||
|
@ -41,15 +41,15 @@ fn paper_slt() {
|
|||
&[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)],
|
||||
);
|
||||
|
||||
dominators(&graph);
|
||||
dominator_tree(&graph);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn immediate_dominator() {
|
||||
let graph = TestGraph::new(1, &[(1, 2), (2, 3)]);
|
||||
let dominators = dominators(&graph);
|
||||
assert_eq!(dominators.immediate_dominator(0), None);
|
||||
assert_eq!(dominators.immediate_dominator(1), None);
|
||||
assert_eq!(dominators.immediate_dominator(2), Some(1));
|
||||
assert_eq!(dominators.immediate_dominator(3), Some(2));
|
||||
let tree = dominator_tree(&graph);
|
||||
assert_eq!(tree.immediate_dominator(0), None);
|
||||
assert_eq!(tree.immediate_dominator(1), None);
|
||||
assert_eq!(tree.immediate_dominator(2), Some(1));
|
||||
assert_eq!(tree.immediate_dominator(3), Some(2));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue