]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | //! Finding the dominators in a control-flow graph. |
2 | //! | |
a2a8927a XL |
3 | //! Algorithm based on Loukas Georgiadis, |
4 | //! "Linear-Time Algorithms for Dominators and Related Problems", | |
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> | |
3157f602 | 11 | |
04c3a46a FG |
12 | use std::cmp::Ordering; |
13 | ||
49aad941 FG |
14 | use rustc_index::{Idx, IndexSlice, IndexVec}; |
15 | ||
04c3a46a | 16 | use super::ControlFlowGraph; |
3157f602 | 17 | |
3157f602 | 18 | #[cfg(test)] |
416331ca | 19 | mod tests; |
3157f602 | 20 | |
a2a8927a XL |
21 | struct PreOrderFrame<Iter> { |
22 | pre_order_idx: PreorderIndex, | |
23 | iter: Iter, | |
3157f602 XL |
24 | } |
25 | ||
a2a8927a | 26 | rustc_index::newtype_index! { |
4b012472 | 27 | #[orderable] |
9c376795 | 28 | struct PreorderIndex {} |
a2a8927a | 29 | } |
3157f602 | 30 | |
ed00b5ec FG |
31 | #[derive(Clone, Debug)] |
32 | pub struct Dominators<Node: Idx> { | |
33 | kind: Kind<Node>, | |
34 | } | |
35 | ||
36 | #[derive(Clone, Debug)] | |
37 | enum Kind<Node: Idx> { | |
38 | /// A representation optimized for a small path graphs. | |
39 | Path, | |
40 | General(Inner<Node>), | |
41 | } | |
42 | ||
43 | pub fn dominators<G: ControlFlowGraph>(g: &G) -> Dominators<G::Node> { | |
44 | // We often encounter MIR bodies with 1 or 2 basic blocks. Special case the dominators | |
45 | // computation and representation for those cases. | |
46 | if is_small_path_graph(g) { | |
47 | Dominators { kind: Kind::Path } | |
48 | } else { | |
49 | Dominators { kind: Kind::General(dominators_impl(g)) } | |
50 | } | |
51 | } | |
52 | ||
53 | fn is_small_path_graph<G: ControlFlowGraph>(g: &G) -> bool { | |
54 | if g.start_node().index() != 0 { | |
55 | return false; | |
56 | } | |
57 | if g.num_nodes() == 1 { | |
58 | return true; | |
59 | } | |
60 | if g.num_nodes() == 2 { | |
61 | return g.successors(g.start_node()).any(|n| n.index() == 1); | |
62 | } | |
63 | false | |
64 | } | |
65 | ||
66 | fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> { | |
3157f602 | 67 | // compute the post order index (rank) for each node |
5869c6ff | 68 | let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes()); |
3157f602 | 69 | |
a2a8927a XL |
70 | // We allocate capacity for the full set of nodes, because most of the time |
71 | // most of the nodes *are* reachable. | |
72 | let mut parent: IndexVec<PreorderIndex, PreorderIndex> = | |
73 | IndexVec::with_capacity(graph.num_nodes()); | |
74 | ||
75 | let mut stack = vec![PreOrderFrame { | |
e8be2606 | 76 | pre_order_idx: PreorderIndex::ZERO, |
a2a8927a XL |
77 | iter: graph.successors(graph.start_node()), |
78 | }]; | |
79 | let mut pre_order_to_real: IndexVec<PreorderIndex, G::Node> = | |
80 | IndexVec::with_capacity(graph.num_nodes()); | |
81 | let mut real_to_pre_order: IndexVec<G::Node, Option<PreorderIndex>> = | |
82 | IndexVec::from_elem_n(None, graph.num_nodes()); | |
83 | pre_order_to_real.push(graph.start_node()); | |
e8be2606 FG |
84 | parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now. |
85 | real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO); | |
a2a8927a XL |
86 | let mut post_order_idx = 0; |
87 | ||
88 | // Traverse the graph, collecting a number of things: | |
89 | // | |
90 | // * Preorder mapping (to it, and back to the actual ordering) | |
781aab86 | 91 | // * Postorder mapping (used exclusively for `cmp_in_dominator_order` on the final product) |
a2a8927a XL |
92 | // * Parents for each vertex in the preorder tree |
93 | // | |
94 | // These are all done here rather than through one of the 'standard' | |
95 | // graph traversals to help make this fast. | |
96 | 'recurse: while let Some(frame) = stack.last_mut() { | |
31ef2f64 | 97 | for successor in frame.iter.by_ref() { |
a2a8927a XL |
98 | if real_to_pre_order[successor].is_none() { |
99 | let pre_order_idx = pre_order_to_real.push(successor); | |
100 | real_to_pre_order[successor] = Some(pre_order_idx); | |
101 | parent.push(frame.pre_order_idx); | |
102 | stack.push(PreOrderFrame { pre_order_idx, iter: graph.successors(successor) }); | |
3157f602 | 103 | |
a2a8927a | 104 | continue 'recurse; |
3157f602 XL |
105 | } |
106 | } | |
a2a8927a XL |
107 | post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx; |
108 | post_order_idx += 1; | |
109 | ||
110 | stack.pop(); | |
3157f602 XL |
111 | } |
112 | ||
a2a8927a XL |
113 | let reachable_vertices = pre_order_to_real.len(); |
114 | ||
e8be2606 | 115 | let mut idom = IndexVec::from_elem_n(PreorderIndex::ZERO, reachable_vertices); |
a2a8927a XL |
116 | let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices); |
117 | let mut label = semi.clone(); | |
118 | let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices); | |
119 | let mut lastlinked = None; | |
3157f602 | 120 | |
a2a8927a XL |
121 | // We loop over vertices in reverse preorder. This implements the pseudocode |
122 | // of the simple Lengauer-Tarjan algorithm. A few key facts are noted here | |
123 | // which are helpful for understanding the code (full proofs and such are | |
124 | // found in various papers, including one cited at the top of this file). | |
125 | // | |
126 | // For each vertex w (which is not the root), | |
127 | // * semi[w] is a proper ancestor of the vertex w (i.e., semi[w] != w) | |
128 | // * idom[w] is an ancestor of semi[w] (i.e., idom[w] may equal semi[w]) | |
129 | // | |
130 | // An immediate dominator of w (idom[w]) is a vertex v where v dominates w | |
131 | // and every other dominator of w dominates v. (Every vertex except the root has | |
132 | // a unique immediate dominator.) | |
133 | // | |
134 | // A semidominator for a given vertex w (semi[w]) is the vertex v with minimum | |
135 | // preorder number such that there exists a path from v to w in which all elements (other than w) have | |
136 | // preorder numbers greater than w (i.e., this path is not the tree path to | |
137 | // w). | |
138 | for w in (PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices)).rev() { | |
139 | // Optimization: process buckets just once, at the start of the | |
140 | // iteration. Do not explicitly empty the bucket (even though it will | |
141 | // not be used again), to save some instructions. | |
142 | // | |
143 | // The bucket here contains the vertices whose semidominator is the | |
144 | // vertex w, which we are guaranteed to have found: all vertices who can | |
145 | // be semidominated by w must have a preorder number exceeding w, so | |
146 | // they have been placed in the bucket. | |
147 | // | |
148 | // We compute a partial set of immediate dominators here. | |
49aad941 | 149 | for &v in bucket[w].iter() { |
a2a8927a XL |
150 | // This uses the result of Lemma 5 from section 2 from the original |
151 | // 1979 paper, to compute either the immediate or relative dominator | |
152 | // for a given vertex v. | |
153 | // | |
154 | // eval returns a vertex y, for which semi[y] is minimum among | |
49aad941 FG |
155 | // vertices semi[v] +> y *> v. Note that semi[v] = w as we're in the |
156 | // w bucket. | |
a2a8927a XL |
157 | // |
158 | // Given such a vertex y, semi[y] <= semi[v] and idom[y] = idom[v]. | |
159 | // If semi[y] = semi[v], though, idom[v] = semi[v]. | |
160 | // | |
161 | // Using this, we can either set idom[v] to be: | |
49aad941 | 162 | // * semi[v] (i.e. w), if semi[y] is w |
a2a8927a XL |
163 | // * idom[y], otherwise |
164 | // | |
165 | // We don't directly set to idom[y] though as it's not necessarily | |
166 | // known yet. The second preorder traversal will cleanup by updating | |
167 | // the idom for any that were missed in this pass. | |
168 | let y = eval(&mut parent, lastlinked, &semi, &mut label, v); | |
49aad941 | 169 | idom[v] = if semi[y] < w { y } else { w }; |
3157f602 XL |
170 | } |
171 | ||
a2a8927a XL |
172 | // This loop computes the semi[w] for w. |
173 | semi[w] = w; | |
174 | for v in graph.predecessors(pre_order_to_real[w]) { | |
9ffffee4 FG |
175 | // TL;DR: Reachable vertices may have unreachable predecessors, so ignore any of them. |
176 | // | |
177 | // Ignore blocks which are not connected to the entry block. | |
178 | // | |
179 | // The algorithm that was used to traverse the graph and build the | |
180 | // `pre_order_to_real` and `real_to_pre_order` vectors does so by | |
181 | // starting from the entry block and following the successors. | |
182 | // Therefore, any blocks not reachable from the entry block will be | |
183 | // set to `None` in the `pre_order_to_real` vector. | |
184 | // | |
185 | // For example, in this graph, A and B should be skipped: | |
186 | // | |
187 | // ┌─────┐ | |
188 | // │ │ | |
189 | // └──┬──┘ | |
190 | // │ | |
191 | // ┌──▼──┐ ┌─────┐ | |
192 | // │ │ │ A │ | |
193 | // └──┬──┘ └──┬──┘ | |
194 | // │ │ | |
195 | // ┌───────┴───────┐ │ | |
196 | // │ │ │ | |
197 | // ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ | |
198 | // │ │ │ │ │ B │ | |
199 | // └──┬──┘ └──┬──┘ └──┬──┘ | |
200 | // │ └──────┬─────┘ | |
201 | // ┌──▼──┐ │ | |
202 | // │ │ │ | |
203 | // └──┬──┘ ┌──▼──┐ | |
204 | // │ │ │ | |
205 | // │ └─────┘ | |
206 | // ┌──▼──┐ | |
207 | // │ │ | |
208 | // └──┬──┘ | |
209 | // │ | |
210 | // ┌──▼──┐ | |
211 | // │ │ | |
212 | // └─────┘ | |
213 | // | |
214 | // ...this may be the case if a MirPass modifies the CFG to remove | |
215 | // or rearrange certain blocks/edges. | |
add651ee | 216 | let Some(v) = real_to_pre_order[v] else { continue }; |
a2a8927a XL |
217 | |
218 | // eval returns a vertex x from which semi[x] is minimum among | |
219 | // vertices semi[v] +> x *> v. | |
220 | // | |
221 | // From Lemma 4 from section 2, we know that the semidominator of a | |
222 | // vertex w is the minimum (by preorder number) vertex of the | |
223 | // following: | |
224 | // | |
225 | // * direct predecessors of w with preorder number less than w | |
226 | // * semidominators of u such that u > w and there exists (v, w) | |
227 | // such that u *> v | |
228 | // | |
229 | // This loop therefore identifies such a minima. Note that any | |
230 | // semidominator path to w must have all but the first vertex go | |
231 | // through vertices numbered greater than w, so the reverse preorder | |
232 | // traversal we are using guarantees that all of the information we | |
233 | // might need is available at this point. | |
234 | // | |
235 | // The eval call will give us semi[x], which is either: | |
236 | // | |
237 | // * v itself, if v has not yet been processed | |
238 | // * A possible 'best' semidominator for w. | |
239 | let x = eval(&mut parent, lastlinked, &semi, &mut label, v); | |
240 | semi[w] = std::cmp::min(semi[w], semi[x]); | |
241 | } | |
242 | // semi[w] is now semidominator(w) and won't change any more. | |
243 | ||
244 | // Optimization: Do not insert into buckets if parent[w] = semi[w], as | |
245 | // we then immediately know the idom. | |
246 | // | |
247 | // If we don't yet know the idom directly, then push this vertex into | |
248 | // our semidominator's bucket, where it will get processed at a later | |
249 | // stage to compute its immediate dominator. | |
49aad941 FG |
250 | let z = parent[w]; |
251 | if z != semi[w] { | |
a2a8927a XL |
252 | bucket[semi[w]].push(w); |
253 | } else { | |
49aad941 | 254 | idom[w] = z; |
3157f602 | 255 | } |
a2a8927a XL |
256 | |
257 | // Optimization: We share the parent array between processed and not | |
258 | // processed elements; lastlinked represents the divider. | |
259 | lastlinked = Some(w); | |
3157f602 | 260 | } |
b7449926 | 261 | |
a2a8927a XL |
262 | // Finalize the idoms for any that were not fully settable during initial |
263 | // traversal. | |
264 | // | |
265 | // If idom[w] != semi[w] then we know that we've stored vertex y from above | |
266 | // into idom[w]. It is known to be our 'relative dominator', which means | |
267 | // that it's one of w's ancestors and has the same immediate dominator as w, | |
268 | // so use that idom. | |
269 | for w in PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices) { | |
270 | if idom[w] != semi[w] { | |
271 | idom[w] = idom[idom[w]]; | |
272 | } | |
273 | } | |
274 | ||
275 | let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes()); | |
276 | for (idx, node) in pre_order_to_real.iter_enumerated() { | |
277 | immediate_dominators[*node] = Some(pre_order_to_real[idom[idx]]); | |
278 | } | |
279 | ||
49aad941 FG |
280 | let start_node = graph.start_node(); |
281 | immediate_dominators[start_node] = None; | |
282 | ||
283 | let time = compute_access_time(start_node, &immediate_dominators); | |
284 | ||
ed00b5ec | 285 | Inner { post_order_rank, immediate_dominators, time } |
a2a8927a XL |
286 | } |
287 | ||
288 | /// Evaluate the link-eval virtual forest, providing the currently minimum semi | |
289 | /// value for the passed `node` (which may be itself). | |
290 | /// | |
291 | /// This maintains that for every vertex v, `label[v]` is such that: | |
292 | /// | |
293 | /// ```text | |
294 | /// semi[eval(v)] = min { semi[label[u]] | root_in_forest(v) +> u *> v } | |
295 | /// ``` | |
296 | /// | |
297 | /// where `+>` is a proper ancestor and `*>` is just an ancestor. | |
298 | #[inline] | |
299 | fn eval( | |
353b0b11 | 300 | ancestor: &mut IndexSlice<PreorderIndex, PreorderIndex>, |
a2a8927a | 301 | lastlinked: Option<PreorderIndex>, |
353b0b11 FG |
302 | semi: &IndexSlice<PreorderIndex, PreorderIndex>, |
303 | label: &mut IndexSlice<PreorderIndex, PreorderIndex>, | |
a2a8927a XL |
304 | node: PreorderIndex, |
305 | ) -> PreorderIndex { | |
306 | if is_processed(node, lastlinked) { | |
307 | compress(ancestor, lastlinked, semi, label, node); | |
308 | label[node] | |
309 | } else { | |
310 | node | |
311 | } | |
312 | } | |
313 | ||
314 | #[inline] | |
315 | fn is_processed(v: PreorderIndex, lastlinked: Option<PreorderIndex>) -> bool { | |
316 | if let Some(ll) = lastlinked { v >= ll } else { false } | |
317 | } | |
318 | ||
319 | #[inline] | |
320 | fn compress( | |
353b0b11 | 321 | ancestor: &mut IndexSlice<PreorderIndex, PreorderIndex>, |
a2a8927a | 322 | lastlinked: Option<PreorderIndex>, |
353b0b11 FG |
323 | semi: &IndexSlice<PreorderIndex, PreorderIndex>, |
324 | label: &mut IndexSlice<PreorderIndex, PreorderIndex>, | |
a2a8927a XL |
325 | v: PreorderIndex, |
326 | ) { | |
327 | assert!(is_processed(v, lastlinked)); | |
5e7ed085 FG |
328 | // Compute the processed list of ancestors |
329 | // | |
330 | // We use a heap stack here to avoid recursing too deeply, exhausting the | |
331 | // stack space. | |
332 | let mut stack: smallvec::SmallVec<[_; 8]> = smallvec::smallvec![v]; | |
333 | let mut u = ancestor[v]; | |
334 | while is_processed(u, lastlinked) { | |
335 | stack.push(u); | |
336 | u = ancestor[u]; | |
337 | } | |
338 | ||
339 | // Then in reverse order, popping the stack | |
340 | for &[v, u] in stack.array_windows().rev() { | |
a2a8927a XL |
341 | if semi[label[u]] < semi[label[v]] { |
342 | label[v] = label[u]; | |
343 | } | |
344 | ancestor[v] = ancestor[u]; | |
345 | } | |
3157f602 XL |
346 | } |
347 | ||
9ffffee4 | 348 | /// Tracks the list of dominators for each node. |
3157f602 | 349 | #[derive(Clone, Debug)] |
ed00b5ec | 350 | struct Inner<N: Idx> { |
3157f602 | 351 | post_order_rank: IndexVec<N, usize>, |
9ffffee4 FG |
352 | // Even though we track only the immediate dominator of each node, it's |
353 | // possible to get its full list of dominators by looking up the dominator | |
ed00b5ec | 354 | // of each dominator. |
3157f602 | 355 | immediate_dominators: IndexVec<N, Option<N>>, |
49aad941 | 356 | time: IndexVec<N, Time>, |
3157f602 XL |
357 | } |
358 | ||
359 | impl<Node: Idx> Dominators<Node> { | |
49aad941 | 360 | /// Returns true if node is reachable from the start node. |
3157f602 | 361 | pub fn is_reachable(&self, node: Node) -> bool { |
ed00b5ec FG |
362 | match &self.kind { |
363 | Kind::Path => true, | |
364 | Kind::General(g) => g.time[node].start != 0, | |
365 | } | |
3157f602 XL |
366 | } |
367 | ||
49aad941 FG |
368 | /// Returns the immediate dominator of node, if any. |
369 | pub fn immediate_dominator(&self, node: Node) -> Option<Node> { | |
ed00b5ec FG |
370 | match &self.kind { |
371 | Kind::Path => { | |
372 | if 0 < node.index() { | |
373 | Some(Node::new(node.index() - 1)) | |
374 | } else { | |
375 | None | |
376 | } | |
377 | } | |
378 | Kind::General(g) => g.immediate_dominators[node], | |
379 | } | |
3157f602 | 380 | } |
29967ef6 XL |
381 | |
382 | /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator | |
383 | /// relationship, the dominator will always precede the dominated. (The relative ordering | |
384 | /// of two unrelated nodes will also be consistent, but otherwise the order has no | |
385 | /// meaning.) This method cannot be used to determine if either Node dominates the other. | |
781aab86 | 386 | pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering { |
ed00b5ec FG |
387 | match &self.kind { |
388 | Kind::Path => lhs.index().cmp(&rhs.index()), | |
389 | Kind::General(g) => g.post_order_rank[rhs].cmp(&g.post_order_rank[lhs]), | |
390 | } | |
29967ef6 | 391 | } |
49aad941 FG |
392 | |
393 | /// Returns true if `a` dominates `b`. | |
394 | /// | |
395 | /// # Panics | |
396 | /// | |
397 | /// Panics if `b` is unreachable. | |
c0240ec0 | 398 | #[inline] |
49aad941 | 399 | pub fn dominates(&self, a: Node, b: Node) -> bool { |
ed00b5ec FG |
400 | match &self.kind { |
401 | Kind::Path => a.index() <= b.index(), | |
402 | Kind::General(g) => { | |
403 | let a = g.time[a]; | |
404 | let b = g.time[b]; | |
405 | assert!(b.start != 0, "node {b:?} is not reachable"); | |
406 | a.start <= b.start && b.finish <= a.finish | |
407 | } | |
3157f602 XL |
408 | } |
409 | } | |
410 | } | |
49aad941 FG |
411 | |
412 | /// Describes the number of vertices discovered at the time when processing of a particular vertex | |
413 | /// started and when it finished. Both values are zero for unreachable vertices. | |
414 | #[derive(Copy, Clone, Default, Debug)] | |
415 | struct Time { | |
416 | start: u32, | |
417 | finish: u32, | |
418 | } | |
419 | ||
420 | fn compute_access_time<N: Idx>( | |
421 | start_node: N, | |
422 | immediate_dominators: &IndexSlice<N, Option<N>>, | |
423 | ) -> IndexVec<N, Time> { | |
424 | // Transpose the dominator tree edges, so that child nodes of vertex v are stored in | |
425 | // node[edges[v].start..edges[v].end]. | |
426 | let mut edges: IndexVec<N, std::ops::Range<u32>> = | |
427 | IndexVec::from_elem(0..0, immediate_dominators); | |
428 | for &idom in immediate_dominators.iter() { | |
429 | if let Some(idom) = idom { | |
430 | edges[idom].end += 1; | |
431 | } | |
432 | } | |
433 | let mut m = 0; | |
434 | for e in edges.iter_mut() { | |
435 | m += e.end; | |
436 | e.start = m; | |
437 | e.end = m; | |
438 | } | |
439 | let mut node = IndexVec::from_elem_n(Idx::new(0), m.try_into().unwrap()); | |
440 | for (i, &idom) in immediate_dominators.iter_enumerated() { | |
441 | if let Some(idom) = idom { | |
442 | edges[idom].start -= 1; | |
443 | node[edges[idom].start] = i; | |
444 | } | |
445 | } | |
446 | ||
447 | // Perform a depth-first search of the dominator tree. Record the number of vertices discovered | |
448 | // when vertex v is discovered first as time[v].start, and when its processing is finished as | |
449 | // time[v].finish. | |
450 | let mut time: IndexVec<N, Time> = IndexVec::from_elem(Time::default(), immediate_dominators); | |
451 | let mut stack = Vec::new(); | |
452 | ||
453 | let mut discovered = 1; | |
454 | stack.push(start_node); | |
455 | time[start_node].start = discovered; | |
456 | ||
457 | while let Some(&i) = stack.last() { | |
458 | let e = &mut edges[i]; | |
459 | if e.start == e.end { | |
460 | // Finish processing vertex i. | |
461 | time[i].finish = discovered; | |
462 | stack.pop(); | |
463 | } else { | |
464 | let j = node[e.start]; | |
465 | e.start += 1; | |
466 | // Start processing vertex j. | |
467 | discovered += 1; | |
468 | time[j].start = discovered; | |
469 | stack.push(j); | |
470 | } | |
471 | } | |
472 | ||
473 | time | |
474 | } |