Annotate comments onto the LT algorithm
This commit is contained in:
parent
3187480070
commit
15483ccf9d
1 changed files with 102 additions and 2 deletions
|
@ -2,7 +2,12 @@
|
||||||
//!
|
//!
|
||||||
//! Algorithm based on Loukas Georgiadis,
|
//! Algorithm based on Loukas Georgiadis,
|
||||||
//! "Linear-Time Algorithms for Dominators and Related Problems",
|
//! "Linear-Time Algorithms for Dominators and Related Problems",
|
||||||
//! ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf
|
//! <ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf>
|
||||||
|
//!
|
||||||
|
//! Additionally useful is the original Lengauer-Tarjan paper on this subject,
|
||||||
|
//! "A Fast Algorithm for Finding Dominators in a Flowgraph"
|
||||||
|
//! Thomas Lengauer and Robert Endre Tarjan.
|
||||||
|
//! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf>
|
||||||
|
|
||||||
use super::ControlFlowGraph;
|
use super::ControlFlowGraph;
|
||||||
use rustc_index::vec::{Idx, IndexVec};
|
use rustc_index::vec::{Idx, IndexVec};
|
||||||
|
@ -42,6 +47,14 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
|
||||||
real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0));
|
real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0));
|
||||||
let mut post_order_idx = 0;
|
let mut post_order_idx = 0;
|
||||||
|
|
||||||
|
// Traverse the graph, collecting a number of things:
|
||||||
|
//
|
||||||
|
// * Preorder mapping (to it, and back to the actual ordering)
|
||||||
|
// * Postorder mapping (used exclusively for rank_partial_cmp on the final product)
|
||||||
|
// * Parents for each vertex in the preorder tree
|
||||||
|
//
|
||||||
|
// These are all done here rather than through one of the 'standard'
|
||||||
|
// graph traversals to help make this fast.
|
||||||
'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 real_to_pre_order[successor].is_none() {
|
if real_to_pre_order[successor].is_none() {
|
||||||
|
@ -67,26 +80,95 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
|
||||||
let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices);
|
let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices);
|
||||||
let mut lastlinked = None;
|
let mut lastlinked = None;
|
||||||
|
|
||||||
|
// We loop over vertices in reverse preorder. This implements the pseudocode
|
||||||
|
// of the simple Lengauer-Tarjan algorithm. A few key facts are noted here
|
||||||
|
// which are helpful for understanding the code (full proofs and such are
|
||||||
|
// found in various papers, including one cited at the top of this file).
|
||||||
|
//
|
||||||
|
// For each vertex w (which is not the root),
|
||||||
|
// * semi[w] is a proper ancestor of the vertex w (i.e., semi[w] != w)
|
||||||
|
// * idom[w] is an ancestor of semi[w] (i.e., idom[w] may equal semi[w])
|
||||||
|
//
|
||||||
|
// An immediate dominator of w (idom[w]) is a vertex v where v dominates w
|
||||||
|
// and every other dominator of w dominates v. (Every vertex except the root has
|
||||||
|
// a unique immediate dominator.)
|
||||||
|
//
|
||||||
|
// A semidominator for a given vertex w (semi[w]) is the vertex v with minimum
|
||||||
|
// preorder number such that there exists a path from v to w in which all elements (other than w) have
|
||||||
|
// preorder numbers greater than w (i.e., this path is not the tree path to
|
||||||
|
// w).
|
||||||
for w in (PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices)).rev() {
|
for w in (PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices)).rev() {
|
||||||
// Optimization: process buckets just once, at the start of the
|
// Optimization: process buckets just once, at the start of the
|
||||||
// iteration. Do not explicitly empty the bucket (even though it will
|
// iteration. Do not explicitly empty the bucket (even though it will
|
||||||
// not be used again), to save some instructions.
|
// not be used again), to save some instructions.
|
||||||
|
//
|
||||||
|
// The bucket here contains the vertices whose semidominator is the
|
||||||
|
// vertex w, which we are guaranteed to have found: all vertices who can
|
||||||
|
// be semidominated by w must have a preorder number exceeding w, so
|
||||||
|
// they have been placed in the bucket.
|
||||||
|
//
|
||||||
|
// We compute a partial set of immediate dominators here.
|
||||||
let z = parent[w];
|
let z = parent[w];
|
||||||
for &v in bucket[z].iter() {
|
for &v in bucket[z].iter() {
|
||||||
|
// This uses the result of Lemma 5 from section 2 from the original
|
||||||
|
// 1979 paper, to compute either the immediate or relative dominator
|
||||||
|
// for a given vertex v.
|
||||||
|
//
|
||||||
|
// eval returns a vertex y, for which semi[y] is minimum among
|
||||||
|
// vertices semi[v] +> y *> v. Note that semi[v] = z as we're in the
|
||||||
|
// z bucket.
|
||||||
|
//
|
||||||
|
// Given such a vertex y, semi[y] <= semi[v] and idom[y] = idom[v].
|
||||||
|
// If semi[y] = semi[v], though, idom[v] = semi[v].
|
||||||
|
//
|
||||||
|
// Using this, we can either set idom[v] to be:
|
||||||
|
// * semi[v] (i.e. z), if semi[y] is z
|
||||||
|
// * idom[y], otherwise
|
||||||
|
//
|
||||||
|
// We don't directly set to idom[y] though as it's not necessarily
|
||||||
|
// known yet. The second preorder traversal will cleanup by updating
|
||||||
|
// the idom for any that were missed in this pass.
|
||||||
let y = eval(&mut parent, lastlinked, &semi, &mut label, v);
|
let y = eval(&mut parent, lastlinked, &semi, &mut label, v);
|
||||||
idom[v] = if semi[y] < z { y } else { z };
|
idom[v] = if semi[y] < z { y } else { z };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This loop computes the semi[w] for w.
|
||||||
semi[w] = w;
|
semi[w] = w;
|
||||||
for v in graph.predecessors(pre_order_to_real[w]) {
|
for v in graph.predecessors(pre_order_to_real[w]) {
|
||||||
let v = real_to_pre_order[v].unwrap();
|
let v = real_to_pre_order[v].unwrap();
|
||||||
|
|
||||||
|
// eval returns a vertex x from which semi[x] is minimum among
|
||||||
|
// vertices semi[v] +> x *> v.
|
||||||
|
//
|
||||||
|
// From Lemma 4 from section 2, we know that the semidominator of a
|
||||||
|
// vertex w is the minimum (by preorder number) vertex of the
|
||||||
|
// following:
|
||||||
|
//
|
||||||
|
// * direct predecessors of w with preorder number less than w
|
||||||
|
// * semidominators of u such that u > w and there exists (v, w)
|
||||||
|
// such that u *> v
|
||||||
|
//
|
||||||
|
// This loop therefore identifies such a minima. Note that any
|
||||||
|
// semidominator path to w must have all but the first vertex go
|
||||||
|
// through vertices numbered greater than w, so the reverse preorder
|
||||||
|
// traversal we are using guarantees that all of the information we
|
||||||
|
// might need is available at this point.
|
||||||
|
//
|
||||||
|
// The eval call will give us semi[x], which is either:
|
||||||
|
//
|
||||||
|
// * v itself, if v has not yet been processed
|
||||||
|
// * A possible 'best' semidominator for w.
|
||||||
let x = eval(&mut parent, lastlinked, &semi, &mut label, v);
|
let x = eval(&mut parent, lastlinked, &semi, &mut label, v);
|
||||||
semi[w] = std::cmp::min(semi[w], semi[x]);
|
semi[w] = std::cmp::min(semi[w], semi[x]);
|
||||||
}
|
}
|
||||||
// semi[w] is now semidominator(w).
|
// semi[w] is now semidominator(w) and won't change any more.
|
||||||
|
|
||||||
// Optimization: Do not insert into buckets if parent[w] = semi[w], as
|
// Optimization: Do not insert into buckets if parent[w] = semi[w], as
|
||||||
// we then immediately know the idom.
|
// we then immediately know the idom.
|
||||||
|
//
|
||||||
|
// If we don't yet know the idom directly, then push this vertex into
|
||||||
|
// our semidominator's bucket, where it will get processed at a later
|
||||||
|
// stage to compute its immediate dominator.
|
||||||
if parent[w] != semi[w] {
|
if parent[w] != semi[w] {
|
||||||
bucket[semi[w]].push(w);
|
bucket[semi[w]].push(w);
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,6 +179,14 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
|
||||||
// processed elements; lastlinked represents the divider.
|
// processed elements; lastlinked represents the divider.
|
||||||
lastlinked = Some(w);
|
lastlinked = Some(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finalize the idoms for any that were not fully settable during initial
|
||||||
|
// traversal.
|
||||||
|
//
|
||||||
|
// If idom[w] != semi[w] then we know that we've stored vertex y from above
|
||||||
|
// into idom[w]. It is known to be our 'relative dominator', which means
|
||||||
|
// that it's one of w's ancestors and has the same immediate dominator as w,
|
||||||
|
// so use that idom.
|
||||||
for w in PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices) {
|
for w in PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices) {
|
||||||
if idom[w] != semi[w] {
|
if idom[w] != semi[w] {
|
||||||
idom[w] = idom[idom[w]];
|
idom[w] = idom[idom[w]];
|
||||||
|
@ -111,6 +201,16 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
|
||||||
Dominators { post_order_rank, immediate_dominators }
|
Dominators { post_order_rank, immediate_dominators }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate the link-eval virtual forest, providing the currently minimum semi
|
||||||
|
/// value for the passed `node` (which may be itself).
|
||||||
|
///
|
||||||
|
/// This maintains that for every vertex v, `label[v]` is such that:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// semi[eval(v)] = min { semi[label[u]] | root_in_forest(v) +> u *> v }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// where `+>` is a proper ancestor and `*>` is just an ancestor.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eval(
|
fn eval(
|
||||||
ancestor: &mut IndexVec<PreorderIndex, PreorderIndex>,
|
ancestor: &mut IndexVec<PreorderIndex, PreorderIndex>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue