use super::ControlFlowGraph;
use rustc_index::{Idx, IndexSlice, IndexVec};
use std::cmp::Ordering;
#[cfg(test)]
mod tests;
struct PreOrderFrame<Iter> {
pre_order_idx: PreorderIndex,
iter: Iter,
}
rustc_index::newtype_index! {
#[orderable]
struct PreorderIndex {}
}
#[derive(Clone, Debug)]
pub struct Dominators<Node: Idx> {
kind: Kind<Node>,
}
#[derive(Clone, Debug)]
enum Kind<Node: Idx> {
Path,
General(Inner<Node>),
}
pub fn dominators<G: ControlFlowGraph>(g: &G) -> Dominators<G::Node> {
if is_small_path_graph(g) {
Dominators { kind: Kind::Path }
} else {
Dominators { kind: Kind::General(dominators_impl(g)) }
}
}
fn is_small_path_graph<G: ControlFlowGraph>(g: &G) -> bool {
if g.start_node().index() != 0 {
return false;
}
if g.num_nodes() == 1 {
return true;
}
if g.num_nodes() == 2 {
return g.successors(g.start_node()).any(|n| n.index() == 1);
}
false
}
fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
let mut parent: IndexVec<PreorderIndex, PreorderIndex> =
IndexVec::with_capacity(graph.num_nodes());
let mut stack = vec![PreOrderFrame {
pre_order_idx: PreorderIndex::new(0),
iter: graph.successors(graph.start_node()),
}];
let mut pre_order_to_real: IndexVec<PreorderIndex, G::Node> =
IndexVec::with_capacity(graph.num_nodes());
let mut real_to_pre_order: IndexVec<G::Node, Option<PreorderIndex>> =
IndexVec::from_elem_n(None, graph.num_nodes());
pre_order_to_real.push(graph.start_node());
parent.push(PreorderIndex::new(0)); real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0));
let mut post_order_idx = 0;
'recurse: while let Some(frame) = stack.last_mut() {
while let Some(successor) = frame.iter.next() {
if real_to_pre_order[successor].is_none() {
let pre_order_idx = pre_order_to_real.push(successor);
real_to_pre_order[successor] = Some(pre_order_idx);
parent.push(frame.pre_order_idx);
stack.push(PreOrderFrame { pre_order_idx, iter: graph.successors(successor) });
continue 'recurse;
}
}
post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx;
post_order_idx += 1;
stack.pop();
}
let reachable_vertices = pre_order_to_real.len();
let mut idom = IndexVec::from_elem_n(PreorderIndex::new(0), reachable_vertices);
let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices);
let mut label = semi.clone();
let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices);
let mut lastlinked = None;
for w in (PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices)).rev() {
for &v in bucket[w].iter() {
let y = eval(&mut parent, lastlinked, &semi, &mut label, v);
idom[v] = if semi[y] < w { y } else { w };
}
semi[w] = w;
for v in graph.predecessors(pre_order_to_real[w]) {
let Some(v) = real_to_pre_order[v] else { continue };
let x = eval(&mut parent, lastlinked, &semi, &mut label, v);
semi[w] = std::cmp::min(semi[w], semi[x]);
}
let z = parent[w];
if z != semi[w] {
bucket[semi[w]].push(w);
} else {
idom[w] = z;
}
lastlinked = Some(w);
}
for w in PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices) {
if idom[w] != semi[w] {
idom[w] = idom[idom[w]];
}
}
let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
for (idx, node) in pre_order_to_real.iter_enumerated() {
immediate_dominators[*node] = Some(pre_order_to_real[idom[idx]]);
}
let start_node = graph.start_node();
immediate_dominators[start_node] = None;
let time = compute_access_time(start_node, &immediate_dominators);
Inner { post_order_rank, immediate_dominators, time }
}
#[inline]
fn eval(
ancestor: &mut IndexSlice<PreorderIndex, PreorderIndex>,
lastlinked: Option<PreorderIndex>,
semi: &IndexSlice<PreorderIndex, PreorderIndex>,
label: &mut IndexSlice<PreorderIndex, PreorderIndex>,
node: PreorderIndex,
) -> PreorderIndex {
if is_processed(node, lastlinked) {
compress(ancestor, lastlinked, semi, label, node);
label[node]
} else {
node
}
}
#[inline]
fn is_processed(v: PreorderIndex, lastlinked: Option<PreorderIndex>) -> bool {
if let Some(ll) = lastlinked { v >= ll } else { false }
}
#[inline]
fn compress(
ancestor: &mut IndexSlice<PreorderIndex, PreorderIndex>,
lastlinked: Option<PreorderIndex>,
semi: &IndexSlice<PreorderIndex, PreorderIndex>,
label: &mut IndexSlice<PreorderIndex, PreorderIndex>,
v: PreorderIndex,
) {
assert!(is_processed(v, lastlinked));
let mut stack: smallvec::SmallVec<[_; 8]> = smallvec::smallvec![v];
let mut u = ancestor[v];
while is_processed(u, lastlinked) {
stack.push(u);
u = ancestor[u];
}
for &[v, u] in stack.array_windows().rev() {
if semi[label[u]] < semi[label[v]] {
label[v] = label[u];
}
ancestor[v] = ancestor[u];
}
}
#[derive(Clone, Debug)]
struct Inner<N: Idx> {
post_order_rank: IndexVec<N, usize>,
immediate_dominators: IndexVec<N, Option<N>>,
time: IndexVec<N, Time>,
}
impl<Node: Idx> Dominators<Node> {
pub fn is_reachable(&self, node: Node) -> bool {
match &self.kind {
Kind::Path => true,
Kind::General(g) => g.time[node].start != 0,
}
}
pub fn immediate_dominator(&self, node: Node) -> Option<Node> {
match &self.kind {
Kind::Path => {
if 0 < node.index() {
Some(Node::new(node.index() - 1))
} else {
None
}
}
Kind::General(g) => g.immediate_dominators[node],
}
}
pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering {
match &self.kind {
Kind::Path => lhs.index().cmp(&rhs.index()),
Kind::General(g) => g.post_order_rank[rhs].cmp(&g.post_order_rank[lhs]),
}
}
pub fn dominates(&self, a: Node, b: Node) -> bool {
match &self.kind {
Kind::Path => a.index() <= b.index(),
Kind::General(g) => {
let a = g.time[a];
let b = g.time[b];
assert!(b.start != 0, "node {b:?} is not reachable");
a.start <= b.start && b.finish <= a.finish
}
}
}
}
#[derive(Copy, Clone, Default, Debug)]
struct Time {
start: u32,
finish: u32,
}
fn compute_access_time<N: Idx>(
start_node: N,
immediate_dominators: &IndexSlice<N, Option<N>>,
) -> IndexVec<N, Time> {
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;
}
}
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 {
time[i].finish = discovered;
stack.pop();
} else {
let j = node[e.start];
e.start += 1;
discovered += 1;
time[j].start = discovered;
stack.push(j);
}
}
time
}