rustc_data_structures/graph/dominators/
mod.rs

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