22//!
33//! Algorithm based on Loukas Georgiadis,
44//! "Linear-Time Algorithms for Dominators and Related Problems",
5- //! ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf
5+ //! <ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf>
6+ //!
7+ //! Additionally useful is the original Lengauer-Tarjan paper on this subject,
8+ //! "A Fast Algorithm for Finding Dominators in a Flowgraph"
9+ //! Thomas Lengauer and Robert Endre Tarjan.
10+ //! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf>
611
712use super :: ControlFlowGraph ;
813use rustc_index:: vec:: { Idx , IndexVec } ;
@@ -42,6 +47,14 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
4247 real_to_pre_order[ graph. start_node ( ) ] = Some ( PreorderIndex :: new ( 0 ) ) ;
4348 let mut post_order_idx = 0 ;
4449
50+ // Traverse the graph, collecting a number of things:
51+ //
52+ // * Preorder mapping (to it, and back to the actual ordering)
53+ // * Postorder mapping (used exclusively for rank_partial_cmp on the final product)
54+ // * Parents for each vertex in the preorder tree
55+ //
56+ // These are all done here rather than through one of the 'standard'
57+ // graph traversals to help make this fast.
4558 ' recurse: while let Some ( frame) = stack. last_mut ( ) {
4659 while let Some ( successor) = frame. iter . next ( ) {
4760 if real_to_pre_order[ successor] . is_none ( ) {
@@ -67,26 +80,95 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
6780 let mut bucket = IndexVec :: from_elem_n ( vec ! [ ] , reachable_vertices) ;
6881 let mut lastlinked = None ;
6982
83+ // We loop over vertices in reverse preorder. This implements the pseudocode
84+ // of the simple Lengauer-Tarjan algorithm. A few key facts are noted here
85+ // which are helpful for understanding the code (full proofs and such are
86+ // found in various papers, including one cited at the top of this file).
87+ //
88+ // For each vertex w (which is not the root),
89+ // * semi[w] is a proper ancestor of the vertex w (i.e., semi[w] != w)
90+ // * idom[w] is an ancestor of semi[w] (i.e., idom[w] may equal semi[w])
91+ //
92+ // An immediate dominator of w (idom[w]) is a vertex v where v dominates w
93+ // and every other dominator of w dominates v. (Every vertex except the root has
94+ // a unique immediate dominator.)
95+ //
96+ // A semidominator for a given vertex w (semi[w]) is the vertex v with minimum
97+ // preorder number such that there exists a path from v to w in which all elements (other than w) have
98+ // preorder numbers greater than w (i.e., this path is not the tree path to
99+ // w).
70100 for w in ( PreorderIndex :: new ( 1 ) ..PreorderIndex :: new ( reachable_vertices) ) . rev ( ) {
71101 // Optimization: process buckets just once, at the start of the
72102 // iteration. Do not explicitly empty the bucket (even though it will
73103 // not be used again), to save some instructions.
104+ //
105+ // The bucket here contains the vertices whose semidominator is the
106+ // vertex w, which we are guaranteed to have found: all vertices who can
107+ // be semidominated by w must have a preorder number exceeding w, so
108+ // they have been placed in the bucket.
109+ //
110+ // We compute a partial set of immediate dominators here.
74111 let z = parent[ w] ;
75112 for & v in bucket[ z] . iter ( ) {
113+ // This uses the result of Lemma 5 from section 2 from the original
114+ // 1979 paper, to compute either the immediate or relative dominator
115+ // for a given vertex v.
116+ //
117+ // eval returns a vertex y, for which semi[y] is minimum among
118+ // vertices semi[v] +> y *> v. Note that semi[v] = z as we're in the
119+ // z bucket.
120+ //
121+ // Given such a vertex y, semi[y] <= semi[v] and idom[y] = idom[v].
122+ // If semi[y] = semi[v], though, idom[v] = semi[v].
123+ //
124+ // Using this, we can either set idom[v] to be:
125+ // * semi[v] (i.e. z), if semi[y] is z
126+ // * idom[y], otherwise
127+ //
128+ // We don't directly set to idom[y] though as it's not necessarily
129+ // known yet. The second preorder traversal will cleanup by updating
130+ // the idom for any that were missed in this pass.
76131 let y = eval ( & mut parent, lastlinked, & semi, & mut label, v) ;
77132 idom[ v] = if semi[ y] < z { y } else { z } ;
78133 }
79134
135+ // This loop computes the semi[w] for w.
80136 semi[ w] = w;
81137 for v in graph. predecessors ( pre_order_to_real[ w] ) {
82138 let v = real_to_pre_order[ v] . unwrap ( ) ;
139+
140+ // eval returns a vertex x from which semi[x] is minimum among
141+ // vertices semi[v] +> x *> v.
142+ //
143+ // From Lemma 4 from section 2, we know that the semidominator of a
144+ // vertex w is the minimum (by preorder number) vertex of the
145+ // following:
146+ //
147+ // * direct predecessors of w with preorder number less than w
148+ // * semidominators of u such that u > w and there exists (v, w)
149+ // such that u *> v
150+ //
151+ // This loop therefore identifies such a minima. Note that any
152+ // semidominator path to w must have all but the first vertex go
153+ // through vertices numbered greater than w, so the reverse preorder
154+ // traversal we are using guarantees that all of the information we
155+ // might need is available at this point.
156+ //
157+ // The eval call will give us semi[x], which is either:
158+ //
159+ // * v itself, if v has not yet been processed
160+ // * A possible 'best' semidominator for w.
83161 let x = eval ( & mut parent, lastlinked, & semi, & mut label, v) ;
84162 semi[ w] = std:: cmp:: min ( semi[ w] , semi[ x] ) ;
85163 }
86- // semi[w] is now semidominator(w).
164+ // semi[w] is now semidominator(w) and won't change any more .
87165
88166 // Optimization: Do not insert into buckets if parent[w] = semi[w], as
89167 // we then immediately know the idom.
168+ //
169+ // If we don't yet know the idom directly, then push this vertex into
170+ // our semidominator's bucket, where it will get processed at a later
171+ // stage to compute its immediate dominator.
90172 if parent[ w] != semi[ w] {
91173 bucket[ semi[ w] ] . push ( w) ;
92174 } else {
@@ -97,6 +179,14 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
97179 // processed elements; lastlinked represents the divider.
98180 lastlinked = Some ( w) ;
99181 }
182+
183+ // Finalize the idoms for any that were not fully settable during initial
184+ // traversal.
185+ //
186+ // If idom[w] != semi[w] then we know that we've stored vertex y from above
187+ // into idom[w]. It is known to be our 'relative dominator', which means
188+ // that it's one of w's ancestors and has the same immediate dominator as w,
189+ // so use that idom.
100190 for w in PreorderIndex :: new ( 1 ) ..PreorderIndex :: new ( reachable_vertices) {
101191 if idom[ w] != semi[ w] {
102192 idom[ w] = idom[ idom[ w] ] ;
@@ -111,6 +201,16 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
111201 Dominators { post_order_rank, immediate_dominators }
112202}
113203
204+ /// Evaluate the link-eval virtual forest, providing the currently minimum semi
205+ /// value for the passed `node` (which may be itself).
206+ ///
207+ /// This maintains that for every vertex v, `label[v]` is such that:
208+ ///
209+ /// ```text
210+ /// semi[eval(v)] = min { semi[label[u]] | root_in_forest(v) +> u *> v }
211+ /// ```
212+ ///
213+ /// where `+>` is a proper ancestor and `*>` is just an ancestor.
114214#[ inline]
115215fn eval (
116216 ancestor : & mut IndexVec < PreorderIndex , PreorderIndex > ,
0 commit comments