2
2
//!
3
3
//! Algorithm based on Loukas Georgiadis,
4
4
//! "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>
6
11
7
12
use super :: ControlFlowGraph ;
8
13
use rustc_index:: vec:: { Idx , IndexVec } ;
@@ -42,6 +47,14 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
42
47
real_to_pre_order[ graph. start_node ( ) ] = Some ( PreorderIndex :: new ( 0 ) ) ;
43
48
let mut post_order_idx = 0 ;
44
49
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.
45
58
' recurse: while let Some ( frame) = stack. last_mut ( ) {
46
59
while let Some ( successor) = frame. iter . next ( ) {
47
60
if real_to_pre_order[ successor] . is_none ( ) {
@@ -67,26 +80,95 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
67
80
let mut bucket = IndexVec :: from_elem_n ( vec ! [ ] , reachable_vertices) ;
68
81
let mut lastlinked = None ;
69
82
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).
70
100
for w in ( PreorderIndex :: new ( 1 ) ..PreorderIndex :: new ( reachable_vertices) ) . rev ( ) {
71
101
// Optimization: process buckets just once, at the start of the
72
102
// iteration. Do not explicitly empty the bucket (even though it will
73
103
// 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.
74
111
let z = parent[ w] ;
75
112
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.
76
131
let y = eval ( & mut parent, lastlinked, & semi, & mut label, v) ;
77
132
idom[ v] = if semi[ y] < z { y } else { z } ;
78
133
}
79
134
135
+ // This loop computes the semi[w] for w.
80
136
semi[ w] = w;
81
137
for v in graph. predecessors ( pre_order_to_real[ w] ) {
82
138
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.
83
161
let x = eval ( & mut parent, lastlinked, & semi, & mut label, v) ;
84
162
semi[ w] = std:: cmp:: min ( semi[ w] , semi[ x] ) ;
85
163
}
86
- // semi[w] is now semidominator(w).
164
+ // semi[w] is now semidominator(w) and won't change any more .
87
165
88
166
// Optimization: Do not insert into buckets if parent[w] = semi[w], as
89
167
// 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.
90
172
if parent[ w] != semi[ w] {
91
173
bucket[ semi[ w] ] . push ( w) ;
92
174
} else {
@@ -97,6 +179,14 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
97
179
// processed elements; lastlinked represents the divider.
98
180
lastlinked = Some ( w) ;
99
181
}
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.
100
190
for w in PreorderIndex :: new ( 1 ) ..PreorderIndex :: new ( reachable_vertices) {
101
191
if idom[ w] != semi[ w] {
102
192
idom[ w] = idom[ idom[ w] ] ;
@@ -111,6 +201,16 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
111
201
Dominators { post_order_rank, immediate_dominators }
112
202
}
113
203
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.
114
214
#[ inline]
115
215
fn eval (
116
216
ancestor : & mut IndexVec < PreorderIndex , PreorderIndex > ,
0 commit comments