Use preorder indices for data structures

This largely avoids remapping from and to the 'real' indices, with the exception
of predecessor lookup and the final merge back, and is conceptually better.
This commit is contained in:
Mark Rousskov 2021-05-08 19:13:11 -04:00
parent 92186cb5c9
commit 7d12767dc5

View file

@ -34,60 +34,53 @@ fn dominators_given_rpo<G: ControlFlowGraph>(graph: G, rpo: &[G::Node]) -> Domin
} }
let mut visited = BitSet::new_empty(graph.num_nodes()); let mut visited = BitSet::new_empty(graph.num_nodes());
let mut parent: IndexVec<G::Node, Option<G::Node>> = let mut parent: IndexVec<usize, Option<usize>> = IndexVec::from_elem_n(None, rpo.len());
IndexVec::from_elem_n(None, graph.num_nodes());
let mut pre_order_index: IndexVec<G::Node, Option<usize>> =
IndexVec::from_elem_n(None, graph.num_nodes());
let mut pre_order_nodes = Vec::with_capacity(rpo.len());
let mut stack = vec![PreOrderFrame { let mut stack = vec![PreOrderFrame { node: 0, iter: graph.successors(graph.start_node()) }];
node: graph.start_node(),
iter: graph.successors(graph.start_node()),
}];
visited.insert(graph.start_node()); visited.insert(graph.start_node());
let mut idx = 0; let mut pre_order_to_real = Vec::with_capacity(rpo.len());
pre_order_index[graph.start_node()] = Some(0); let mut real_to_pre_order: IndexVec<G::Node, Option<usize>> =
idx += 1; IndexVec::from_elem_n(None, graph.num_nodes());
pre_order_nodes.push(graph.start_node()); pre_order_to_real.push(graph.start_node());
real_to_pre_order[graph.start_node()] = Some(0);
let mut idx = 1;
'recurse: while let Some(frame) = stack.last_mut() { 'recurse: while let Some(frame) = stack.last_mut() {
while let Some(successor) = frame.iter.next() { while let Some(successor) = frame.iter.next() {
if visited.insert(successor) { if visited.insert(successor) {
parent[successor] = Some(frame.node); parent[idx] = Some(frame.node);
pre_order_index[successor] = Some(idx); pre_order_to_real.push(successor);
pre_order_nodes.push(successor); real_to_pre_order[successor] = Some(idx);
idx += 1;
stack.push(PreOrderFrame { node: successor, iter: graph.successors(successor) }); stack.push(PreOrderFrame { node: idx, iter: graph.successors(successor) });
idx += 1;
continue 'recurse; continue 'recurse;
} }
} }
stack.pop(); stack.pop();
} }
let mut idom = IndexVec::from_elem_n(graph.start_node(), graph.num_nodes()); let mut idom = IndexVec::from_elem_n(0, pre_order_to_real.len());
let mut semi = IndexVec::from_fn_n(std::convert::identity, graph.num_nodes()); let mut semi = IndexVec::from_fn_n(std::convert::identity, pre_order_to_real.len());
let mut label = semi.clone(); let mut label = semi.clone();
let mut bucket = IndexVec::from_elem_n(vec![], graph.num_nodes()); let mut bucket = IndexVec::from_elem_n(vec![], pre_order_to_real.len());
let mut lastlinked = None; let mut lastlinked = None;
for &w in pre_order_nodes[1..].iter().rev() { for w in (1..pre_order_to_real.len()).rev() {
// Optimization: process buckets just once. We need not explicitly empty // Optimization: process buckets just once, at the start of the
// the bucket here, but mem::take is pretty cheap. // iteration. Do not explicitly empty the bucket (even though it will
// not be used again), to save some instructions.
let z = parent[w].unwrap(); let z = parent[w].unwrap();
for v in std::mem::take(&mut bucket[z]) { for &v in bucket[z].iter() {
let y = eval(&pre_order_index, &mut parent, lastlinked, &semi, &mut label, v); let y = eval(&mut parent, lastlinked, &semi, &mut label, v);
idom[v] = if pre_order_index[semi[y]] < pre_order_index[z] { y } else { z }; idom[v] = if semi[y] < z { y } else { z };
} }
semi[w] = w; semi[w] = w;
for v in graph.predecessors(w) { for v in graph.predecessors(pre_order_to_real[w]) {
let x = eval(&pre_order_index, &mut parent, lastlinked, &semi, &mut label, v); let v = real_to_pre_order[v].unwrap();
semi[w] = if pre_order_index[semi[w]].unwrap() < pre_order_index[semi[x]].unwrap() { let x = eval(&mut parent, lastlinked, &semi, &mut label, v);
semi[w] semi[w] = std::cmp::min(semi[w], semi[x]);
} else {
semi[x]
};
} }
// semi[w] is now semidominator(w). // semi[w] is now semidominator(w).
@ -103,59 +96,51 @@ fn dominators_given_rpo<G: ControlFlowGraph>(graph: G, rpo: &[G::Node]) -> Domin
// processed elements; lastlinked represents the divider. // processed elements; lastlinked represents the divider.
lastlinked = Some(w); lastlinked = Some(w);
} }
for &w in pre_order_nodes.iter().skip(1) { for w in 1..pre_order_to_real.len() {
if idom[w] != semi[w] { if idom[w] != semi[w] {
idom[w] = idom[idom[w]]; idom[w] = idom[idom[w]];
} }
} }
let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes()); let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
for (node, idom_slot) in immediate_dominators.iter_enumerated_mut() { for (idx, node) in pre_order_to_real.iter().enumerate() {
if pre_order_index[node].is_some() { immediate_dominators[*node] = Some(pre_order_to_real[idom[idx]]);
*idom_slot = Some(idom[node]);
}
} }
Dominators { post_order_rank, immediate_dominators } Dominators { post_order_rank, immediate_dominators }
} }
fn eval<N: Idx>( fn eval<N: Idx>(
pre_order_index: &IndexVec<N, Option<usize>>,
ancestor: &mut IndexVec<N, Option<N>>, ancestor: &mut IndexVec<N, Option<N>>,
lastlinked: Option<N>, lastlinked: Option<N>,
semi: &IndexVec<N, N>, semi: &IndexVec<N, N>,
label: &mut IndexVec<N, N>, label: &mut IndexVec<N, N>,
node: N, node: N,
) -> N { ) -> N {
if is_processed(pre_order_index, node, lastlinked) { if is_processed(node, lastlinked) {
compress(pre_order_index, ancestor, lastlinked, semi, label, node); compress(ancestor, lastlinked, semi, label, node);
label[node] label[node]
} else { } else {
node node
} }
} }
fn is_processed<N: Idx>( fn is_processed<N: Idx>(v: N, lastlinked: Option<N>) -> bool {
pre_order_index: &IndexVec<N, Option<usize>>, if let Some(ll) = lastlinked { v >= ll } else { false }
v: N,
lastlinked: Option<N>,
) -> bool {
if let Some(ll) = lastlinked { pre_order_index[v] >= pre_order_index[ll] } else { false }
} }
fn compress<N: Idx>( fn compress<N: Idx>(
pre_order_index: &IndexVec<N, Option<usize>>,
ancestor: &mut IndexVec<N, Option<N>>, ancestor: &mut IndexVec<N, Option<N>>,
lastlinked: Option<N>, lastlinked: Option<N>,
semi: &IndexVec<N, N>, semi: &IndexVec<N, N>,
label: &mut IndexVec<N, N>, label: &mut IndexVec<N, N>,
v: N, v: N,
) { ) {
assert!(is_processed(pre_order_index, v, lastlinked)); assert!(is_processed(v, lastlinked));
let u = ancestor[v].unwrap(); let u = ancestor[v].unwrap();
if is_processed(pre_order_index, u, lastlinked) { if is_processed(u, lastlinked) {
compress(pre_order_index, ancestor, lastlinked, semi, label, u); compress(ancestor, lastlinked, semi, label, u);
if pre_order_index[semi[label[u]]] < pre_order_index[semi[label[v]]] { if semi[label[u]] < semi[label[v]] {
label[v] = label[u]; label[v] = label[u];
} }
ancestor[v] = ancestor[u]; ancestor[v] = ancestor[u];