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>
1112use rustc_index::{Idx, IndexSlice, IndexVec};
1314use super::ControlFlowGraph;
1516#[cfg(test)]
17mod tests;
1819struct PreOrderFrame<Iter> {
20 pre_order_idx: PreorderIndex,
21 iter: Iter,
22}
2324impl ::std::fmt::Debug for PreorderIndex {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_fmt(format_args!("{0}", self.as_u32()))
}
}rustc_index::newtype_index! {
25#[orderable]
26struct PreorderIndex {}
27}2829#[derive(#[automatically_derived]
impl<Node: ::core::clone::Clone + Idx> ::core::clone::Clone for
Dominators<Node> {
#[inline]
fn clone(&self) -> Dominators<Node> {
Dominators { kind: ::core::clone::Clone::clone(&self.kind) }
}
}Clone, #[automatically_derived]
impl<Node: ::core::fmt::Debug + Idx> ::core::fmt::Debug for Dominators<Node> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f, "Dominators",
"kind", &&self.kind)
}
}Debug)]
30pub struct Dominators<Node: Idx> {
31 kind: Kind<Node>,
32}
3334#[derive(#[automatically_derived]
impl<Node: ::core::clone::Clone + Idx> ::core::clone::Clone for Kind<Node> {
#[inline]
fn clone(&self) -> Kind<Node> {
match self {
Kind::Path => Kind::Path,
Kind::General(__self_0) =>
Kind::General(::core::clone::Clone::clone(__self_0)),
}
}
}Clone, #[automatically_derived]
impl<Node: ::core::fmt::Debug + Idx> ::core::fmt::Debug for Kind<Node> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Kind::Path => ::core::fmt::Formatter::write_str(f, "Path"),
Kind::General(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"General", &__self_0),
}
}
}Debug)]
35enum Kind<Node: Idx> {
36/// A representation optimized for a small path graphs.
37Path,
38 General(Inner<Node>),
39}
4041pub 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.
44if is_small_path_graph(g) {
45Dominators { kind: Kind::Path }
46 } else {
47Dominators { kind: Kind::General(dominators_impl(g)) }
48 }
49}
5051fn is_small_path_graph<G: ControlFlowGraph>(g: &G) -> bool {
52if g.start_node().index() != 0 {
53return false;
54 }
55if g.num_nodes() == 1 {
56return true;
57 }
58if g.num_nodes() == 2 {
59return g.successors(g.start_node()).any(|n| n.index() == 1);
60 }
61false
62}
6364fn 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.
67let mut parent: IndexVec<PreorderIndex, PreorderIndex> =
68IndexVec::with_capacity(graph.num_nodes());
6970let mut stack = <[_]>::into_vec(::alloc::boxed::box_new([PreOrderFrame {
pre_order_idx: PreorderIndex::ZERO,
iter: graph.successors(graph.start_node()),
}]))vec![PreOrderFrame {
71 pre_order_idx: PreorderIndex::ZERO,
72 iter: graph.successors(graph.start_node()),
73 }];
74let mut pre_order_to_real: IndexVec<PreorderIndex, G::Node> =
75IndexVec::with_capacity(graph.num_nodes());
76let mut real_to_pre_order: IndexVec<G::Node, Option<PreorderIndex>> =
77IndexVec::from_elem_n(None, graph.num_nodes());
78pre_order_to_real.push(graph.start_node());
79parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now.
80real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO);
8182// 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() {
90for successor in frame.iter.by_ref() {
91if real_to_pre_order[successor].is_none() {
92let 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) });
9697continue 'recurse;
98 }
99 }
100101 stack.pop();
102 }
103104let reachable_vertices = pre_order_to_real.len();
105106let mut idom = IndexVec::from_elem_n(PreorderIndex::ZERO, reachable_vertices);
107let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices);
108let mut label = semi.clone();
109let mut bucket = IndexVec::from_elem_n(::alloc::vec::Vec::new()vec![], reachable_vertices);
110let mut lastlinked = None;
111112// 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).
129for 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.
140for &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.
159let y = eval(&mut parent, lastlinked, &semi, &mut label, v);
160 idom[v] = if semi[y] < w { y } else { w };
161 }
162163// This loop computes the semi[w] for w.
164semi[w] = w;
165for 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.
207let Some(v) = real_to_pre_order[v] else { continue };
208209// 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.
230let 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.
234235 // 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.
241let z = parent[w];
242if z != semi[w] {
243 bucket[semi[w]].push(w);
244 } else {
245 idom[w] = z;
246 }
247248// Optimization: We share the parent array between processed and not
249 // processed elements; lastlinked represents the divider.
250lastlinked = Some(w);
251 }
252253// 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.
260for w in PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices) {
261if idom[w] != semi[w] {
262 idom[w] = idom[idom[w]];
263 }
264 }
265266let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
267for (idx, node) in pre_order_to_real.iter_enumerated() {
268 immediate_dominators[*node] = Some(pre_order_to_real[idom[idx]]);
269 }
270271let start_node = graph.start_node();
272immediate_dominators[start_node] = None;
273274let time = compute_access_time(start_node, &immediate_dominators);
275276Inner { immediate_dominators, time }
277}
278279/// 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 {
297if is_processed(node, lastlinked) {
298compress(ancestor, lastlinked, semi, label, node);
299label[node]
300 } else {
301node302 }
303}
304305#[inline]
306fn is_processed(v: PreorderIndex, lastlinked: Option<PreorderIndex>) -> bool {
307if let Some(ll) = lastlinked { v >= ll } else { false }
308}
309310#[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) {
318if !is_processed(v, lastlinked) {
::core::panicking::panic("assertion failed: is_processed(v, lastlinked)")
};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.
323let mut stack: smallvec::SmallVec<[_; 8]> = {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(v);
vec
} else {
::smallvec::SmallVec::from_vec(<[_]>::into_vec(::alloc::boxed::box_new([v])))
}
}smallvec::smallvec![v];
324let mut u = ancestor[v];
325while is_processed(u, lastlinked) {
326 stack.push(u);
327 u = ancestor[u];
328 }
329330// Then in reverse order, popping the stack
331for &[v, u] in stack.array_windows().rev() {
332if semi[label[u]] < semi[label[v]] {
333 label[v] = label[u];
334 }
335 ancestor[v] = ancestor[u];
336 }
337}
338339/// Tracks the list of dominators for each node.
340#[derive(#[automatically_derived]
impl<N: ::core::clone::Clone + Idx> ::core::clone::Clone for Inner<N> {
#[inline]
fn clone(&self) -> Inner<N> {
Inner {
immediate_dominators: ::core::clone::Clone::clone(&self.immediate_dominators),
time: ::core::clone::Clone::clone(&self.time),
}
}
}Clone, #[automatically_derived]
impl<N: ::core::fmt::Debug + Idx> ::core::fmt::Debug for Inner<N> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "Inner",
"immediate_dominators", &self.immediate_dominators, "time",
&&self.time)
}
}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.
345immediate_dominators: IndexVec<N, Option<N>>,
346 time: IndexVec<N, Time>,
347}
348349impl<Node: Idx> Dominators<Node> {
350/// Returns true if node is reachable from the start node.
351pub fn is_reachable(&self, node: Node) -> bool {
352match &self.kind {
353 Kind::Path => true,
354 Kind::General(g) => g.time[node].start != 0,
355 }
356 }
357358/// Returns the immediate dominator of node, if any.
359pub fn immediate_dominator(&self, node: Node) -> Option<Node> {
360match &self.kind {
361 Kind::Path => {
362if 0 < node.index() {
363Some(Node::new(node.index() - 1))
364 } else {
365None366 }
367 }
368 Kind::General(g) => g.immediate_dominators[node],
369 }
370 }
371372/// Returns true if `a` dominates `b`.
373 ///
374 /// # Panics
375 ///
376 /// Panics if `b` is unreachable.
377#[inline]
378pub fn dominates(&self, a: Node, b: Node) -> bool {
379match &self.kind {
380 Kind::Path => a.index() <= b.index(),
381 Kind::General(g) => {
382let a = g.time[a];
383let b = g.time[b];
384if !(b.start != 0) {
{
::core::panicking::panic_fmt(format_args!("node {0:?} is not reachable",
b));
}
};assert!(b.start != 0, "node {b:?} is not reachable");
385a.start <= b.start && b.finish <= a.finish
386 }
387 }
388 }
389}
390391/// 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(#[automatically_derived]
impl ::core::marker::Copy for Time { }Copy, #[automatically_derived]
impl ::core::clone::Clone for Time {
#[inline]
fn clone(&self) -> Time {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]
impl ::core::default::Default for Time {
#[inline]
fn default() -> Time {
Time {
start: ::core::default::Default::default(),
finish: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]
impl ::core::fmt::Debug for Time {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "Time", "start",
&self.start, "finish", &&self.finish)
}
}Debug)]
394struct Time {
395 start: u32,
396 finish: u32,
397}
398399fn 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].
405let mut edges: IndexVec<N, std::ops::Range<u32>> =
406IndexVec::from_elem(0..0, immediate_dominators);
407for &idom in immediate_dominators.iter() {
408if let Some(idom) = idom {
409 edges[idom].end += 1;
410 }
411 }
412let mut m = 0;
413for e in edges.iter_mut() {
414 m += e.end;
415 e.start = m;
416 e.end = m;
417 }
418let mut node = IndexVec::from_elem_n(Idx::new(0), m.try_into().unwrap());
419for (i, &idom) in immediate_dominators.iter_enumerated() {
420if let Some(idom) = idom {
421 edges[idom].start -= 1;
422 node[edges[idom].start] = i;
423 }
424 }
425426// 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.
429let mut time: IndexVec<N, Time> = IndexVec::from_elem(Time::default(), immediate_dominators);
430let mut stack = Vec::new();
431432let mut discovered = 1;
433stack.push(start_node);
434time[start_node].start = discovered;
435436while let Some(&i) = stack.last() {
437let e = &mut edges[i];
438if e.start == e.end {
439// Finish processing vertex i.
440time[i].finish = discovered;
441 stack.pop();
442 } else {
443let j = node[e.start];
444 e.start += 1;
445// Start processing vertex j.
446discovered += 1;
447 time[j].start = discovered;
448 stack.push(j);
449 }
450 }
451452time453}