use log::trace;
use std::collections::HashSet;
use std::marker::PhantomData;
use super::traits::DirectedGraphNode;
type Index = usize;
type DominatorInfo = Vec<HashSet<Index>>;
type ImmediateDominatorInfo = Vec<Option<Index>>;
pub struct DominatorTree<T: DirectedGraphNode> {
dominators: DominatorInfo,
immediate_dominators: ImmediateDominatorInfo,
dominator_successors: DominatorInfo,
dominance_frontier: DominatorInfo,
marker: PhantomData<T>,
}
impl<T: DirectedGraphNode> DominatorTree<T> {
pub fn new(basic_blocks: &[T]) -> DominatorTree<T> {
let dominators = compute_dominators(basic_blocks);
let (immediate_dominators, dominator_successors) =
compute_immediate_dominators(basic_blocks, &dominators);
let dominance_frontier = compute_dominance_frontier(basic_blocks, &immediate_dominators);
assert!(immediate_dominators[0].is_none());
DominatorTree {
dominators,
immediate_dominators,
dominator_successors,
dominance_frontier,
marker: PhantomData,
}
}
pub fn entry_block(&self) -> Index {
Index::default()
}
pub fn get_dominators(&self, i: Index) -> HashSet<Index> {
self.dominators[i].clone()
}
pub fn get_immediate_dominator(&self, i: Index) -> Option<Index> {
self.immediate_dominators[i]
}
pub fn get_dominator_successors(&self, i: Index) -> HashSet<Index> {
self.dominator_successors[i].clone()
}
pub fn get_dominance_frontier(&self, i: Index) -> HashSet<Index> {
self.dominance_frontier[i].clone()
}
}
fn compute_dominators<T: DirectedGraphNode>(basic_blocks: &[T]) -> DominatorInfo {
let mut dominators = Vec::new();
let nof_blocks = basic_blocks.len();
dominators.push(HashSet::from([0]));
for _ in 1..basic_blocks.len() {
dominators.push((0..nof_blocks).collect());
}
let mut done = false;
while !done {
done = true;
for i in 1..nof_blocks {
let mut new_dominators: HashSet<usize> = (0..nof_blocks).collect();
for &j in basic_blocks[i].predecessors() {
new_dominators = new_dominators.intersection(&dominators[j]).copied().collect();
}
new_dominators.insert(i);
if new_dominators != dominators[i] {
dominators[i] = new_dominators;
done = false;
}
}
}
dominators
}
fn compute_immediate_dominators<T: DirectedGraphNode>(
basic_blocks: &[T],
dominators: &DominatorInfo,
) -> (ImmediateDominatorInfo, DominatorInfo) {
let nof_blocks = basic_blocks.len();
let mut immediate_dominators = vec![None; nof_blocks];
let mut dominator_successors = vec![HashSet::new(); nof_blocks];
for i in 0..nof_blocks {
trace!("the dominator set of block {i} is {:?}", dominators[i]);
let mut idom_candidates: HashSet<usize> = dominators[i].clone();
idom_candidates.remove(&i);
if idom_candidates.len() > 1 {
let mut all_dominators: HashSet<usize> = HashSet::new();
for j in &idom_candidates {
if all_dominators.contains(j) {
continue;
}
all_dominators = dominators[*j]
.clone()
.into_iter()
.filter(|&k| k != *j) .collect::<HashSet<usize>>()
.union(&all_dominators)
.copied()
.collect();
}
idom_candidates = &idom_candidates - &all_dominators;
assert!(idom_candidates.len() <= 1);
}
if let Some(&j) = idom_candidates.iter().next() {
trace!("the immediate dominator of {i} is {j}");
immediate_dominators[i] = Some(j);
dominator_successors[j].insert(i);
}
}
(immediate_dominators, dominator_successors)
}
fn compute_dominance_frontier<T: DirectedGraphNode>(
basic_blocks: &[T],
immediate_dominators: &ImmediateDominatorInfo,
) -> DominatorInfo {
let nof_blocks = basic_blocks.len();
let mut dominance_frontier = vec![HashSet::new(); nof_blocks];
for i in 0..nof_blocks {
if basic_blocks[i].predecessors().len() > 1 {
for &j in basic_blocks[i].predecessors() {
let mut k = j;
while Some(k) != immediate_dominators[i] {
dominance_frontier[k].insert(i);
k = match immediate_dominators[k] {
Some(idom) => idom,
None => break,
};
}
}
}
}
dominance_frontier
}