use std::collections::HashSet;
use crate::utils::graph::{NodeId, RootedGraph, Successors};
#[derive(Debug, Clone)]
pub struct DominatorTree {
entry: NodeId,
idom: Vec<NodeId>,
node_count: 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 {
Some(self.idom[node.index()])
}
}
pub fn dominates(&self, a: NodeId, b: NodeId) -> bool {
if a == b {
return true;
}
if b.index() >= self.node_count {
return false;
}
let mut current = b;
while current != self.entry {
if current.index() >= self.node_count {
return false;
}
let idom = self.idom[current.index()];
if idom == a {
return true;
}
if idom == current {
return false;
}
current = idom;
}
a == self.entry
}
#[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 = 0;
let mut current = node;
while current != self.entry {
current = self.idom[current.index()];
depth += 1;
}
depth
}
pub fn children(&self, node: NodeId) -> Vec<NodeId> {
let mut result = Vec::new();
for i in 0..self.node_count {
let n = NodeId::new(i);
if n != self.entry && self.idom[i] == node {
result.push(n);
}
}
result
}
#[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;
Some(current)
} else {
self.current = Some(self.tree.idom[current.index()]);
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(),
node_count: 0,
};
}
let mut lt = LengauerTarjan::new(node_count, entry);
lt.compute(graph);
DominatorTree {
entry,
idom: lt.idom,
node_count,
}
}
pub fn compute_dominators_rooted<G>(graph: &G) -> DominatorTree
where
G: RootedGraph,
{
compute_dominators(graph, graph.entry())
}
struct LengauerTarjan {
n: usize,
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 {
fn new(n: usize, entry: NodeId) -> Self {
let sentinel = NodeId::new(usize::MAX);
Self {
n,
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) {
self.dfs(graph, self.entry);
for i in (1..self.dfs_counter).rev() {
let w = self.vertex[i];
let parent_w = self.parent[w.index()];
for v in self.predecessors_of(graph, w) {
if self.dfnum[v.index()] == 0 {
continue;
}
let u = self.eval(v);
if self.dfnum[self.semi[u.index()].index()]
< self.dfnum[self.semi[w.index()].index()]
{
self.semi[w.index()] = self.semi[u.index()];
}
}
let semi_w = self.semi[w.index()];
self.bucket[semi_w.index()].push(w);
self.link(parent_w, w);
let bucket = std::mem::take(&mut self.bucket[parent_w.index()]);
for v in bucket {
let u = self.eval(v);
if self.semi[u.index()] == self.semi[v.index()] {
self.idom[v.index()] = parent_w;
} else {
self.idom[v.index()] = u;
}
}
}
for i in 1..self.dfs_counter {
let w = self.vertex[i];
if self.idom[w.index()] != self.semi[w.index()] {
self.idom[w.index()] = self.idom[self.idom[w.index()].index()];
}
}
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[idx] != 0 {
continue;
}
self.dfs_counter += 1;
self.dfnum[idx] = self.dfs_counter;
self.vertex[self.dfs_counter - 1] = node;
for succ in graph.successors(node) {
if self.dfnum[succ.index()] == 0 {
self.parent[succ.index()] = node;
stack.push((succ, false));
}
}
}
}
fn predecessors_of<G: Successors>(&self, graph: &G, node: NodeId) -> Vec<NodeId> {
let mut preds = Vec::new();
for i in 0..self.n {
let v = NodeId::new(i);
for succ in graph.successors(v) {
if succ == node {
preds.push(v);
break;
}
}
}
preds
}
fn link(&mut self, w: NodeId, v: NodeId) {
self.ancestor[v.index()] = w;
}
fn eval(&mut self, v: NodeId) -> NodeId {
let sentinel = NodeId::new(usize::MAX);
if self.ancestor[v.index()] == sentinel {
return v;
}
self.compress(v);
self.best[v.index()]
}
fn compress(&mut self, v: NodeId) {
let sentinel = NodeId::new(usize::MAX);
let ancestor_v = self.ancestor[v.index()];
if self.ancestor[ancestor_v.index()] == sentinel {
return;
}
self.compress(ancestor_v);
let best_ancestor = self.best[ancestor_v.index()];
let best_v = self.best[v.index()];
if self.dfnum[self.semi[best_ancestor.index()].index()]
< self.dfnum[self.semi[best_v.index()].index()]
{
self.best[v.index()] = best_ancestor;
}
self.ancestor[v.index()] = self.ancestor[ancestor_v.index()];
}
}
pub fn compute_dominance_frontiers<G>(graph: &G, dom_tree: &DominatorTree) -> Vec<HashSet<NodeId>>
where
G: Successors,
{
let n = graph.node_count();
let mut frontiers: Vec<HashSet<NodeId>> = vec![HashSet::new(); n];
for node_idx in 0..n {
let node = NodeId::new(node_idx);
let preds = get_predecessors(graph, node);
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 {
frontiers[runner.index()].insert(node);
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 {
frontiers[runner.index()].insert(node);
}
}
}
frontiers
}
fn get_predecessors<G: Successors>(graph: &G, node: NodeId) -> Vec<NodeId> {
let mut preds = Vec::new();
for i in 0..graph.node_count() {
let v = NodeId::new(i);
for succ in graph.successors(v) {
if succ == node {
preds.push(v);
break;
}
}
}
preds
}
#[cfg(test)]
mod tests {
use crate::utils::graph::{
algorithms::dominators::{compute_dominance_frontiers, compute_dominators},
DirectedGraph, NodeId,
};
#[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);
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));
assert_eq!(frontiers[left.index()].len(), 1);
assert!(frontiers[right.index()].contains(&join));
assert_eq!(frontiers[right.index()].len(), 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));
}
#[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));
assert!(frontiers[d.index()].contains(&join1));
assert!(frontiers[join1.index()].contains(&join2));
assert!(frontiers[e.index()].contains(&join2));
}
#[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));
}
}