use crate::{
BitSet,
graph::{NodeId, RootedGraph, Successors},
};
#[derive(Debug, Clone)]
pub struct DominatorTree {
entry: NodeId,
idom: Vec<NodeId>,
children: Vec<Vec<NodeId>>,
node_count: usize,
predecessors: Vec<Vec<NodeId>>,
tin: Vec<usize>,
tout: Vec<usize>,
}
impl DominatorTree {
#[inline]
pub fn entry(&self) -> NodeId {
self.entry
}
#[inline]
pub fn immediate_dominator(&self, node: NodeId) -> Option<NodeId> {
if node == self.entry {
None
} else {
self.idom.get(node.index()).copied()
}
}
const UNVISITED: usize = usize::MAX;
pub fn dominates(&self, a: NodeId, b: NodeId) -> bool {
if a == b {
return true;
}
let (Some(&a_in), Some(&b_in)) = (self.tin.get(a.index()), self.tin.get(b.index())) else {
return false;
};
if a_in == Self::UNVISITED || b_in == Self::UNVISITED {
return false;
}
let (Some(&a_out), Some(&b_out)) = (self.tout.get(a.index()), self.tout.get(b.index()))
else {
return false;
};
a_in <= b_in && b_out <= a_out
}
#[inline]
pub fn strictly_dominates(&self, a: NodeId, b: NodeId) -> bool {
a != b && self.dominates(a, b)
}
pub fn dominators(&self, node: NodeId) -> DominatorIterator<'_> {
DominatorIterator {
tree: self,
current: Some(node),
}
}
pub fn depth(&self, node: NodeId) -> usize {
let mut depth: usize = 0;
let mut current = node;
while current != self.entry {
let Some(&idom) = self.idom.get(current.index()) else {
return depth;
};
if idom == current {
return depth;
}
current = idom;
depth = depth.saturating_add(1);
}
depth
}
pub fn children(&self, node: NodeId) -> &[NodeId] {
self.children.get(node.index()).map_or(&[], Vec::as_slice)
}
#[inline]
pub fn node_count(&self) -> usize {
self.node_count
}
}
pub struct DominatorIterator<'a> {
tree: &'a DominatorTree,
current: Option<NodeId>,
}
impl Iterator for DominatorIterator<'_> {
type Item = NodeId;
fn next(&mut self) -> Option<Self::Item> {
let current = self.current?;
if current == self.tree.entry {
self.current = None;
return Some(current);
}
self.current = self
.tree
.idom
.get(current.index())
.copied()
.filter(|next| next.index() < self.tree.node_count());
Some(current)
}
}
pub fn compute_dominators<G>(graph: &G, entry: NodeId) -> DominatorTree
where
G: Successors,
{
let node_count = graph.node_count();
if node_count == 0 {
return DominatorTree {
entry,
idom: Vec::new(),
children: Vec::new(),
node_count: 0,
predecessors: Vec::new(),
tin: Vec::new(),
tout: Vec::new(),
};
}
let predecessors = precompute_predecessors(graph);
let mut lt = LengauerTarjan::new(node_count, entry);
lt.compute(graph, &predecessors);
let mut children: Vec<Vec<NodeId>> = vec![Vec::new(); node_count];
for i in 0..node_count {
let node = NodeId::new(i);
if node == entry {
continue;
}
let Some(parent) = lt.idom.get(i).copied() else {
continue;
};
if let Some(slot) = children.get_mut(parent.index()) {
slot.push(node);
}
}
let (tin, tout) = compute_dfs_intervals(entry, &children, node_count);
DominatorTree {
entry,
idom: lt.idom,
children,
node_count,
predecessors,
tin,
tout,
}
}
pub fn compute_dominators_rooted<G>(graph: &G) -> DominatorTree
where
G: RootedGraph,
{
compute_dominators(graph, graph.entry())
}
fn compute_dfs_intervals(
entry: NodeId,
children: &[Vec<NodeId>],
node_count: usize,
) -> (Vec<usize>, Vec<usize>) {
let mut tin = vec![DominatorTree::UNVISITED; node_count];
let mut tout = vec![DominatorTree::UNVISITED; node_count];
if entry.index() >= node_count {
return (tin, tout);
}
let mut timer: usize = 0;
if let Some(slot) = tin.get_mut(entry.index()) {
*slot = timer;
}
timer = timer.saturating_add(1);
let mut stack: Vec<(NodeId, usize)> = vec![(entry, 0)];
while let Some(&(node, next_child)) = stack.last() {
let kids = children
.get(node.index())
.map_or([].as_slice(), Vec::as_slice);
if let Some(&child) = kids.get(next_child) {
if let Some(top) = stack.last_mut() {
top.1 = next_child.saturating_add(1);
}
if tin.get(child.index()).copied() == Some(DominatorTree::UNVISITED) {
if let Some(slot) = tin.get_mut(child.index()) {
*slot = timer;
}
timer = timer.saturating_add(1);
stack.push((child, 0));
}
} else {
if let Some(slot) = tout.get_mut(node.index()) {
*slot = timer;
}
timer = timer.saturating_add(1);
stack.pop();
}
}
(tin, tout)
}
fn precompute_predecessors<G: Successors>(graph: &G) -> Vec<Vec<NodeId>> {
let n = graph.node_count();
let mut preds: Vec<Vec<NodeId>> = vec![Vec::new(); n];
for i in 0..n {
let v = NodeId::new(i);
for succ in graph.successors(v) {
if let Some(slot) = preds.get_mut(succ.index()) {
slot.push(v);
}
}
}
preds
}
struct LengauerTarjan {
entry: NodeId,
dfnum: Vec<usize>,
vertex: Vec<NodeId>,
parent: Vec<NodeId>,
semi: Vec<NodeId>,
idom: Vec<NodeId>,
ancestor: Vec<NodeId>,
best: Vec<NodeId>,
bucket: Vec<Vec<NodeId>>,
dfs_counter: usize,
}
impl LengauerTarjan {
#[inline]
fn sentinel() -> NodeId {
NodeId::new(usize::MAX)
}
#[inline]
fn get_node(slice: &[NodeId], i: usize) -> NodeId {
slice.get(i).copied().unwrap_or(Self::sentinel())
}
#[inline]
fn get_dfnum(&self, n: NodeId) -> usize {
self.dfnum.get(n.index()).copied().unwrap_or(0)
}
#[inline]
fn set_node(slice: &mut [NodeId], i: usize, value: NodeId) {
if let Some(slot) = slice.get_mut(i) {
*slot = value;
}
}
fn new(n: usize, entry: NodeId) -> Self {
let sentinel = NodeId::new(usize::MAX);
Self {
entry,
dfnum: vec![0; n],
vertex: vec![sentinel; n],
parent: vec![sentinel; n],
semi: (0..n).map(NodeId::new).collect(),
idom: vec![sentinel; n],
ancestor: vec![sentinel; n],
best: (0..n).map(NodeId::new).collect(),
bucket: vec![Vec::new(); n],
dfs_counter: 0,
}
}
fn compute<G: Successors>(&mut self, graph: &G, predecessors: &[Vec<NodeId>]) {
self.dfs(graph, self.entry);
for i in (1..self.dfs_counter).rev() {
let w = Self::get_node(&self.vertex, i);
let parent_w = Self::get_node(&self.parent, w.index());
let preds_w: &[NodeId] = predecessors.get(w.index()).map_or(&[], Vec::as_slice);
for v in preds_w {
let v = *v;
if self.get_dfnum(v) == 0 {
continue;
}
let u = self.eval(v);
let semi_u = Self::get_node(&self.semi, u.index());
let semi_w = Self::get_node(&self.semi, w.index());
if self.get_dfnum(semi_u) < self.get_dfnum(semi_w) {
Self::set_node(&mut self.semi, w.index(), semi_u);
}
}
let semi_w = Self::get_node(&self.semi, w.index());
if let Some(bucket) = self.bucket.get_mut(semi_w.index()) {
bucket.push(w);
}
self.link(parent_w, w);
let bucket = self
.bucket
.get_mut(parent_w.index())
.map_or_else(Vec::new, std::mem::take);
for v in bucket {
let u = self.eval(v);
let semi_u = Self::get_node(&self.semi, u.index());
let semi_v = Self::get_node(&self.semi, v.index());
if semi_u == semi_v {
Self::set_node(&mut self.idom, v.index(), parent_w);
} else {
Self::set_node(&mut self.idom, v.index(), u);
}
}
}
for i in 1..self.dfs_counter {
let w = Self::get_node(&self.vertex, i);
let idom_w = Self::get_node(&self.idom, w.index());
let semi_w = Self::get_node(&self.semi, w.index());
if idom_w != semi_w {
let idom_idom = Self::get_node(&self.idom, idom_w.index());
Self::set_node(&mut self.idom, w.index(), idom_idom);
}
}
Self::set_node(&mut self.idom, self.entry.index(), self.entry);
}
fn dfs<G: Successors>(&mut self, graph: &G, start: NodeId) {
let mut stack = vec![(start, false)];
while let Some((node, processed)) = stack.pop() {
let idx = node.index();
if processed {
continue;
}
if self.dfnum.get(idx).copied().unwrap_or(0) != 0 {
continue;
}
self.dfs_counter = self.dfs_counter.saturating_add(1);
if let Some(slot) = self.dfnum.get_mut(idx) {
*slot = self.dfs_counter;
}
let vertex_idx = self.dfs_counter.saturating_sub(1);
Self::set_node(&mut self.vertex, vertex_idx, node);
for succ in graph.successors(node) {
if self.get_dfnum(succ) == 0 {
Self::set_node(&mut self.parent, succ.index(), node);
stack.push((succ, false));
}
}
}
}
fn link(&mut self, w: NodeId, v: NodeId) {
Self::set_node(&mut self.ancestor, v.index(), w);
}
fn eval(&mut self, v: NodeId) -> NodeId {
let sentinel = Self::sentinel();
if Self::get_node(&self.ancestor, v.index()) == sentinel {
return v;
}
self.compress(v);
Self::get_node(&self.best, v.index())
}
fn compress(&mut self, v: NodeId) {
let sentinel = Self::sentinel();
let mut path = Vec::new();
let mut u = v;
loop {
let anc_u = Self::get_node(&self.ancestor, u.index());
let anc_anc_u = Self::get_node(&self.ancestor, anc_u.index());
if anc_anc_u == sentinel {
break;
}
path.push(u);
u = anc_u;
}
for &node in path.iter().rev() {
let ancestor_node = Self::get_node(&self.ancestor, node.index());
let best_ancestor = Self::get_node(&self.best, ancestor_node.index());
let best_node = Self::get_node(&self.best, node.index());
let semi_ba = Self::get_node(&self.semi, best_ancestor.index());
let semi_bn = Self::get_node(&self.semi, best_node.index());
if self.get_dfnum(semi_ba) < self.get_dfnum(semi_bn) {
Self::set_node(&mut self.best, node.index(), best_ancestor);
}
let new_anc = Self::get_node(&self.ancestor, ancestor_node.index());
Self::set_node(&mut self.ancestor, node.index(), new_anc);
}
}
}
pub fn compute_dominance_frontiers<G>(graph: &G, dom_tree: &DominatorTree) -> Vec<BitSet>
where
G: Successors,
{
let n = graph.node_count();
let mut frontiers: Vec<BitSet> = (0..n).map(|_| BitSet::lazy(n)).collect();
let fallback;
let all_preds: &[Vec<NodeId>] = if dom_tree.predecessors.len() == n {
&dom_tree.predecessors
} else {
fallback = precompute_predecessors(graph);
&fallback
};
for (node_idx, preds) in all_preds.iter().enumerate() {
let node = NodeId::new(node_idx);
if preds.len() < 2 {
continue; }
let idom_node = dom_tree.immediate_dominator(node);
for &pred in preds {
let mut runner = pred;
while Some(runner) != idom_node && runner != dom_tree.entry() && runner.index() < n {
if let Some(slot) = frontiers.get_mut(runner.index()) {
slot.insert(node.index());
}
if let Some(idom) = dom_tree.immediate_dominator(runner) {
if idom.index() >= n {
break;
}
runner = idom;
} else {
break;
}
}
if Some(runner) != idom_node
&& runner == dom_tree.entry()
&& runner.index() < n
&& let Some(slot) = frontiers.get_mut(runner.index())
{
slot.insert(node.index());
}
}
}
frontiers
}
#[cfg(test)]
mod tests {
use crate::graph::{
DirectedGraph, NodeId,
algorithms::dominators::{DominatorTree, compute_dominance_frontiers, compute_dominators},
};
fn dominates_by_chain_walk(tree: &DominatorTree, a: NodeId, b: NodeId) -> bool {
if a == b {
return true;
}
if b.index() >= tree.node_count {
return false;
}
let mut current = b;
while current != tree.entry {
let Some(&idom) = tree.idom.get(current.index()) else {
return false;
};
if idom == a {
return true;
}
if idom == current {
return false;
}
current = idom;
}
a == tree.entry
}
type DominanceCase = (&'static str, usize, Vec<(usize, usize)>);
fn graph_from(node_count: usize, edges: &[(usize, usize)]) -> DirectedGraph<'static, (), ()> {
let mut graph: DirectedGraph<'static, (), ()> = DirectedGraph::new();
for _ in 0..node_count {
graph.add_node(());
}
for &(from, to) in edges {
let _ = graph.add_edge(NodeId::new(from), NodeId::new(to), ());
}
graph
}
fn next_rand(state: &mut u64) -> u64 {
*state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
*state >> 33
}
#[test]
fn dominators_of_an_unreachable_node_yield_only_itself() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let reachable = graph.add_node("reachable");
let orphan = graph.add_node("orphan");
let _ = graph.add_edge(entry, reachable, ());
let tree = compute_dominators(&graph, entry);
let chain: Vec<NodeId> = tree.dominators(orphan).collect();
assert_eq!(
chain,
vec![orphan],
"an unreachable node dominates only itself and must not yield a sentinel"
);
for node in &chain {
assert!(
node.index() < graph.node_count(),
"every yielded id must index a real node, got {node:?}"
);
}
assert_eq!(
tree.dominators(reachable).collect::<Vec<_>>(),
vec![reachable, entry]
);
}
#[test]
fn dfs_interval_dominance_matches_chain_walk() {
let mut cases: Vec<DominanceCase> = vec![
("empty", 0, vec![]),
("single", 1, vec![]),
("self_loop", 1, vec![(0, 0)]),
("chain", 4, vec![(0, 1), (1, 2), (2, 3)]),
("diamond", 4, vec![(0, 1), (0, 2), (1, 3), (2, 3)]),
("loop", 3, vec![(0, 1), (1, 2), (2, 1)]),
(
"nested_loops",
5,
vec![(0, 1), (1, 2), (2, 3), (3, 2), (3, 4), (4, 1)],
),
(
"irreducible",
5,
vec![(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 3)],
),
("unreachable", 5, vec![(0, 1), (1, 2), (3, 4)]),
("unreachable_self_loop", 3, vec![(0, 1), (2, 2)]),
];
let mut state = 0x5DEE_CE66_D1BB_1A3Fu64;
for case in 0..40 {
let node_count = 2 + (next_rand(&mut state) as usize % 12);
let edge_count = 1 + (next_rand(&mut state) as usize % 24);
let mut edges = Vec::new();
for _ in 0..edge_count {
let from = next_rand(&mut state) as usize % node_count;
let to = next_rand(&mut state) as usize % node_count;
edges.push((from, to));
}
let name: &'static str = Box::leak(format!("generated_{case}").into_boxed_str());
cases.push((name, node_count, edges));
}
for (name, node_count, edges) in cases {
let graph = graph_from(node_count, &edges);
let tree = compute_dominators(&graph, NodeId::new(0));
let probe = node_count.saturating_add(2);
for a in 0..probe {
for b in 0..probe {
let (a, b) = (NodeId::new(a), NodeId::new(b));
assert_eq!(
tree.dominates(a, b),
dominates_by_chain_walk(&tree, a, b),
"dominates({}, {}) disagrees with the chain walk in case {name}",
a.index(),
b.index()
);
}
}
}
}
#[test]
fn dfs_interval_dominance_is_a_partial_order() {
let graph = graph_from(
8,
&[
(0, 1),
(0, 2),
(1, 3),
(2, 3),
(3, 4),
(4, 5),
(5, 4),
(5, 6),
(6, 7),
(7, 3),
],
);
let tree = compute_dominators(&graph, NodeId::new(0));
let nodes: Vec<NodeId> = (0..8).map(NodeId::new).collect();
for &a in &nodes {
assert!(tree.dominates(a, a), "not reflexive at {}", a.index());
for &b in &nodes {
if a != b && tree.dominates(a, b) {
assert!(
!tree.dominates(b, a),
"not antisymmetric: {} and {}",
a.index(),
b.index()
);
}
for &c in &nodes {
if tree.dominates(a, b) && tree.dominates(b, c) {
assert!(
tree.dominates(a, c),
"not transitive: {} -> {} -> {}",
a.index(),
b.index(),
c.index()
);
}
}
}
}
}
#[test]
fn test_dominator_empty_graph() {
let graph: DirectedGraph<(), ()> = DirectedGraph::new();
let entry = NodeId::new(0);
let dom_tree = compute_dominators(&graph, entry);
assert_eq!(dom_tree.node_count(), 0);
}
#[test]
fn test_dominator_single_node() {
let mut graph: DirectedGraph<(), ()> = DirectedGraph::new();
let entry = graph.add_node(());
let dom_tree = compute_dominators(&graph, entry);
assert_eq!(dom_tree.entry(), entry);
assert_eq!(dom_tree.immediate_dominator(entry), None);
assert!(dom_tree.dominates(entry, entry));
assert_eq!(dom_tree.depth(entry), 0);
}
#[test]
fn test_dominator_linear_chain() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let a = graph.add_node("a");
let b = graph.add_node("b");
let c = graph.add_node("c");
graph.add_edge(entry, a, ()).unwrap();
graph.add_edge(a, b, ()).unwrap();
graph.add_edge(b, c, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
assert_eq!(dom_tree.immediate_dominator(entry), None);
assert_eq!(dom_tree.immediate_dominator(a), Some(entry));
assert_eq!(dom_tree.immediate_dominator(b), Some(a));
assert_eq!(dom_tree.immediate_dominator(c), Some(b));
assert!(dom_tree.dominates(entry, a));
assert!(dom_tree.dominates(entry, b));
assert!(dom_tree.dominates(entry, c));
assert!(dom_tree.dominates(a, b));
assert!(dom_tree.dominates(a, c));
assert!(dom_tree.dominates(b, c));
assert!(!dom_tree.dominates(c, b));
assert!(!dom_tree.dominates(b, a));
assert_eq!(dom_tree.depth(entry), 0);
assert_eq!(dom_tree.depth(a), 1);
assert_eq!(dom_tree.depth(b), 2);
assert_eq!(dom_tree.depth(c), 3);
}
#[test]
fn test_dominator_diamond() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let a = graph.add_node("a");
let b = graph.add_node("b");
let exit = graph.add_node("exit");
graph.add_edge(entry, a, ()).unwrap();
graph.add_edge(entry, b, ()).unwrap();
graph.add_edge(a, exit, ()).unwrap();
graph.add_edge(b, exit, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
assert_eq!(dom_tree.immediate_dominator(a), Some(entry));
assert_eq!(dom_tree.immediate_dominator(b), Some(entry));
assert_eq!(dom_tree.immediate_dominator(exit), Some(entry));
assert!(!dom_tree.strictly_dominates(a, exit));
assert!(!dom_tree.strictly_dominates(b, exit));
assert!(dom_tree.dominates(entry, a));
assert!(dom_tree.dominates(entry, b));
assert!(dom_tree.dominates(entry, exit));
}
#[test]
fn test_dominator_if_then_else() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let cond = graph.add_node("cond");
let then_b = graph.add_node("then");
let else_b = graph.add_node("else");
let merge = graph.add_node("merge");
let exit = graph.add_node("exit");
graph.add_edge(entry, cond, ()).unwrap();
graph.add_edge(cond, then_b, ()).unwrap();
graph.add_edge(cond, else_b, ()).unwrap();
graph.add_edge(then_b, merge, ()).unwrap();
graph.add_edge(else_b, merge, ()).unwrap();
graph.add_edge(merge, exit, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
assert_eq!(dom_tree.immediate_dominator(cond), Some(entry));
assert_eq!(dom_tree.immediate_dominator(then_b), Some(cond));
assert_eq!(dom_tree.immediate_dominator(else_b), Some(cond));
assert_eq!(dom_tree.immediate_dominator(merge), Some(cond));
assert_eq!(dom_tree.immediate_dominator(exit), Some(merge));
assert!(dom_tree.dominates(cond, merge));
assert!(dom_tree.dominates(cond, exit));
assert!(!dom_tree.strictly_dominates(then_b, merge));
assert!(!dom_tree.strictly_dominates(else_b, merge));
}
#[test]
fn test_dominator_loop() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let header = graph.add_node("header");
let body = graph.add_node("body");
let exit = graph.add_node("exit");
graph.add_edge(entry, header, ()).unwrap();
graph.add_edge(header, body, ()).unwrap();
graph.add_edge(body, header, ()).unwrap(); graph.add_edge(body, exit, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
assert!(dom_tree.dominates(header, body));
assert!(!dom_tree.strictly_dominates(body, header));
}
#[test]
fn test_dominator_iterator() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let a = graph.add_node("a");
let b = graph.add_node("b");
let c = graph.add_node("c");
graph.add_edge(entry, a, ()).unwrap();
graph.add_edge(a, b, ()).unwrap();
graph.add_edge(b, c, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
let dominators: Vec<NodeId> = dom_tree.dominators(c).collect();
assert_eq!(dominators, vec![c, b, a, entry]);
let dominators: Vec<NodeId> = dom_tree.dominators(entry).collect();
assert_eq!(dominators, vec![entry]);
}
#[test]
fn test_dominator_children() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let a = graph.add_node("a");
let b = graph.add_node("b");
let exit = graph.add_node("exit");
graph.add_edge(entry, a, ()).unwrap();
graph.add_edge(entry, b, ()).unwrap();
graph.add_edge(a, exit, ()).unwrap();
graph.add_edge(b, exit, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
let mut children = dom_tree.children(entry).to_vec();
children.sort_by_key(|n| n.index());
assert_eq!(children, vec![a, b, exit]);
assert!(dom_tree.children(a).is_empty());
assert!(dom_tree.children(b).is_empty());
assert!(dom_tree.children(exit).is_empty());
}
#[test]
fn test_dominance_frontier_diamond() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let left = graph.add_node("left");
let right = graph.add_node("right");
let join = graph.add_node("join");
graph.add_edge(entry, left, ()).unwrap();
graph.add_edge(entry, right, ()).unwrap();
graph.add_edge(left, join, ()).unwrap();
graph.add_edge(right, join, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
let frontiers = compute_dominance_frontiers(&graph, &dom_tree);
assert!(frontiers[entry.index()].is_empty());
assert!(frontiers[left.index()].contains(join.index()));
assert_eq!(frontiers[left.index()].count(), 1);
assert!(frontiers[right.index()].contains(join.index()));
assert_eq!(frontiers[right.index()].count(), 1);
assert!(frontiers[join.index()].is_empty());
}
#[test]
fn test_dominance_frontier_loop() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let header = graph.add_node("header");
let body = graph.add_node("body");
let exit = graph.add_node("exit");
graph.add_edge(entry, header, ()).unwrap();
graph.add_edge(header, body, ()).unwrap();
graph.add_edge(body, header, ()).unwrap(); graph.add_edge(header, exit, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
let frontiers = compute_dominance_frontiers(&graph, &dom_tree);
assert!(frontiers[body.index()].contains(header.index()));
}
#[test]
fn test_dominance_frontier_nested_if() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let if1 = graph.add_node("if1");
let a = graph.add_node("a");
let b = graph.add_node("b");
let c = graph.add_node("c");
let d = graph.add_node("d");
let e = graph.add_node("e");
let join1 = graph.add_node("join1");
let join2 = graph.add_node("join2");
graph.add_edge(entry, if1, ()).unwrap();
graph.add_edge(if1, a, ()).unwrap();
graph.add_edge(if1, b, ()).unwrap();
graph.add_edge(a, c, ()).unwrap();
graph.add_edge(a, d, ()).unwrap();
graph.add_edge(b, e, ()).unwrap();
graph.add_edge(c, join1, ()).unwrap();
graph.add_edge(d, join1, ()).unwrap();
graph.add_edge(e, join2, ()).unwrap();
graph.add_edge(join1, join2, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
let frontiers = compute_dominance_frontiers(&graph, &dom_tree);
assert!(frontiers[c.index()].contains(join1.index()));
assert!(frontiers[d.index()].contains(join1.index()));
assert!(frontiers[join1.index()].contains(join2.index()));
assert!(frontiers[e.index()].contains(join2.index()));
}
#[test]
fn test_strictly_dominates() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let a = graph.add_node("a");
graph.add_edge(entry, a, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
assert!(dom_tree.dominates(entry, entry));
assert!(!dom_tree.strictly_dominates(entry, entry));
assert!(dom_tree.strictly_dominates(entry, a));
}
#[test]
fn test_dominator_complex_cfg() {
let mut graph: DirectedGraph<&str, ()> = DirectedGraph::new();
let entry = graph.add_node("entry");
let a = graph.add_node("a");
let b = graph.add_node("b");
let c = graph.add_node("c");
let d = graph.add_node("d");
let e = graph.add_node("e");
let f = graph.add_node("f");
let g = graph.add_node("g");
let h = graph.add_node("h");
graph.add_edge(entry, a, ()).unwrap();
graph.add_edge(a, b, ()).unwrap();
graph.add_edge(a, c, ()).unwrap();
graph.add_edge(b, d, ()).unwrap();
graph.add_edge(c, e, ()).unwrap();
graph.add_edge(d, f, ()).unwrap();
graph.add_edge(e, f, ()).unwrap();
graph.add_edge(e, g, ()).unwrap();
graph.add_edge(f, h, ()).unwrap();
let dom_tree = compute_dominators(&graph, entry);
assert!(dom_tree.dominates(a, b));
assert!(dom_tree.dominates(a, c));
assert!(dom_tree.dominates(a, d));
assert!(dom_tree.dominates(a, e));
assert!(dom_tree.dominates(a, f));
assert!(dom_tree.dominates(a, g));
assert!(dom_tree.dominates(a, h));
assert_eq!(dom_tree.immediate_dominator(f), Some(a));
assert_eq!(dom_tree.immediate_dominator(g), Some(e));
}
}