diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 104a4119f3265..0d2ae115cb0d2 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -2,7 +2,12 @@ //! //! Algorithm based on Loukas Georgiadis, //! "Linear-Time Algorithms for Dominators and Related Problems", -//! 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. +//! use super::ControlFlowGraph; use rustc_index::vec::{Idx, IndexVec}; @@ -42,6 +47,14 @@ pub fn dominators(graph: G) -> Dominators { real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(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() { while let Some(successor) = frame.iter.next() { if real_to_pre_order[successor].is_none() { @@ -67,26 +80,95 @@ pub fn dominators(graph: G) -> Dominators { let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices); 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() { // Optimization: process buckets just once, at the start of the // iteration. Do not explicitly empty the bucket (even though it will // 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]; 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); idom[v] = if semi[y] < z { y } else { z }; } + // This loop computes the semi[w] for w. semi[w] = w; for v in graph.predecessors(pre_order_to_real[w]) { 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); 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 // 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] { bucket[semi[w]].push(w); } else { @@ -97,6 +179,14 @@ pub fn dominators(graph: G) -> Dominators { // processed elements; lastlinked represents the divider. 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) { if idom[w] != semi[w] { idom[w] = idom[idom[w]]; @@ -111,6 +201,16 @@ pub fn dominators(graph: G) -> 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] fn eval( ancestor: &mut IndexVec,