Skip to content

Commit fff5775

Browse files
committed
Preprocess dominator tree to answer queries in O(1)
1 parent ab39f2d commit fff5775

File tree

5 files changed

+121
-49
lines changed

5 files changed

+121
-49
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
144144
if self.unwind_edge_count <= 1 {
145145
return;
146146
}
147-
let doms = self.body.basic_blocks.dominators();
147+
let dom_tree = self.body.basic_blocks.dominator_tree();
148148
let mut post_contract_node = FxHashMap::default();
149149
// Reusing the allocation across invocations of the closure
150150
let mut dom_path = vec![];
@@ -153,7 +153,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
153153
if let Some(root) = post_contract_node.get(&bb) {
154154
break *root;
155155
}
156-
let parent = doms.immediate_dominator(bb);
156+
let parent = dom_tree.immediate_dominator(bb);
157157
dom_path.push(bb);
158158
if !self.body.basic_blocks[parent].is_cleanup {
159159
break bb;

compiler/rustc_data_structures/src/graph/dominators/mod.rs

+107-21
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ rustc_index::newtype_index! {
2525
struct PreorderIndex {}
2626
}
2727

28-
pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
28+
pub fn dominator_tree<G: ControlFlowGraph>(graph: G) -> DominatorTree<G::Node> {
2929
// compute the post order index (rank) for each node
3030
let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
3131

@@ -201,7 +201,7 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
201201
immediate_dominators[*node] = Some(pre_order_to_real[idom[idx]]);
202202
}
203203

204-
Dominators { post_order_rank, immediate_dominators }
204+
DominatorTree { post_order_rank, immediate_dominators }
205205
}
206206

207207
/// Evaluate the link-eval virtual forest, providing the currently minimum semi
@@ -265,13 +265,14 @@ fn compress(
265265
}
266266

267267
#[derive(Clone, Debug)]
268-
pub struct Dominators<N: Idx> {
268+
pub struct DominatorTree<N: Idx> {
269269
post_order_rank: IndexVec<N, usize>,
270+
// Note: immediate_dominators[root] is Some(root)!
270271
immediate_dominators: IndexVec<N, Option<N>>,
271272
}
272273

273-
impl<Node: Idx> Dominators<Node> {
274-
pub fn is_reachable(&self, node: Node) -> bool {
274+
impl<Node: Idx> DominatorTree<Node> {
275+
fn is_reachable(&self, node: Node) -> bool {
275276
self.immediate_dominators[node].is_some()
276277
}
277278

@@ -282,25 +283,12 @@ impl<Node: Idx> Dominators<Node> {
282283

283284
pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
284285
assert!(self.is_reachable(node), "node {node:?} is not reachable");
285-
Iter { dominators: self, node: Some(node) }
286-
}
287-
288-
pub fn dominates(&self, dom: Node, node: Node) -> bool {
289-
// FIXME -- could be optimized by using post-order-rank
290-
self.dominators(node).any(|n| n == dom)
291-
}
292-
293-
/// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
294-
/// relationship, the dominator will always precede the dominated. (The relative ordering
295-
/// of two unrelated nodes will also be consistent, but otherwise the order has no
296-
/// meaning.) This method cannot be used to determine if either Node dominates the other.
297-
pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option<Ordering> {
298-
self.post_order_rank[rhs].partial_cmp(&self.post_order_rank[lhs])
286+
Iter { dom_tree: self, node: Some(node) }
299287
}
300288
}
301289

302290
pub struct Iter<'dom, Node: Idx> {
303-
dominators: &'dom Dominators<Node>,
291+
dom_tree: &'dom DominatorTree<Node>,
304292
node: Option<Node>,
305293
}
306294

@@ -309,7 +297,7 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
309297

310298
fn next(&mut self) -> Option<Self::Item> {
311299
if let Some(node) = self.node {
312-
let dom = self.dominators.immediate_dominator(node);
300+
let dom = self.dom_tree.immediate_dominator(node);
313301
if dom == node {
314302
self.node = None; // reached the root
315303
} else {
@@ -321,3 +309,101 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
321309
}
322310
}
323311
}
312+
313+
#[derive(Clone, Debug)]
314+
pub struct Dominators<Node: Idx> {
315+
time: IndexVec<Node, Time>,
316+
post_order_rank: IndexVec<Node, usize>,
317+
}
318+
319+
/// Describes the number of vertices discovered at the time when processing of a particular vertex
320+
/// started and when it finished. Both values are zero for unreachable vertices.
321+
#[derive(Copy, Clone, Default, Debug)]
322+
struct Time {
323+
start: u32,
324+
finish: u32,
325+
}
326+
327+
impl<Node: Idx> Dominators<Node> {
328+
pub fn dummy() -> Self {
329+
Self { time: Default::default(), post_order_rank: Default::default() }
330+
}
331+
332+
/// Returns true if `a` dominates `b`.
333+
///
334+
/// # Panics
335+
///
336+
/// Panics if `b` is unreachable.
337+
pub fn dominates(&self, a: Node, b: Node) -> bool {
338+
let a = self.time[a];
339+
let b = self.time[b];
340+
assert!(b.start != 0, "node {b:?} is not reachable");
341+
a.start <= b.start && b.finish <= a.finish
342+
}
343+
344+
/// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
345+
/// relationship, the dominator will always precede the dominated. (The relative ordering
346+
/// of two unrelated nodes will also be consistent, but otherwise the order has no
347+
/// meaning.) This method cannot be used to determine if either Node dominates the other.
348+
pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option<Ordering> {
349+
self.post_order_rank[rhs].partial_cmp(&self.post_order_rank[lhs])
350+
}
351+
}
352+
353+
pub fn dominators<G: Copy + ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
354+
let DominatorTree { mut immediate_dominators, post_order_rank } = dominator_tree(graph);
355+
356+
immediate_dominators[graph.start_node()] = None;
357+
358+
// Transpose the dominator tree edges, so that child nodes of vertex v are stored in
359+
// node[edges[v].start..edges[y].end].
360+
let mut edges: IndexVec<G::Node, std::ops::Range<u32>> =
361+
IndexVec::from_elem_n(0..0, graph.num_nodes());
362+
for &idom in immediate_dominators.iter() {
363+
if let Some(idom) = idom {
364+
edges[idom].end += 1;
365+
}
366+
}
367+
let mut m = 0;
368+
for e in edges.iter_mut() {
369+
m += e.end;
370+
e.start = m;
371+
e.end = m;
372+
}
373+
let mut node = IndexVec::from_elem_n(Idx::new(0), m.try_into().unwrap());
374+
for (i, &idom) in immediate_dominators.iter_enumerated() {
375+
if let Some(idom) = idom {
376+
edges[idom].start -= 1;
377+
node[edges[idom].start] = i;
378+
}
379+
}
380+
381+
// Perform a depth-first search of the dominator tree. Record the number of vertices discovered
382+
// when vertex v is discovered first as time[v].start, and when its processing is finished as
383+
// time[v].finish.
384+
let mut time: IndexVec<G::Node, Time> =
385+
IndexVec::from_elem_n(Time::default(), graph.num_nodes());
386+
let mut stack = Vec::new();
387+
388+
let mut discovered = 1;
389+
stack.push(graph.start_node());
390+
time[graph.start_node()].start = discovered;
391+
392+
while let Some(&i) = stack.last() {
393+
let e = &mut edges[i];
394+
if e.start == e.end {
395+
// Finish processing vertex i.
396+
time[i].finish = discovered;
397+
stack.pop();
398+
} else {
399+
let j = node[e.start];
400+
e.start += 1;
401+
// Start processing vertex j.
402+
discovered += 1;
403+
time[j].start = discovered;
404+
stack.push(j);
405+
}
406+
}
407+
408+
Dominators { time, post_order_rank }
409+
}

compiler/rustc_data_structures/src/graph/dominators/tests.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use super::super::tests::TestGraph;
66
fn diamond() {
77
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
88

9-
let dominators = dominators(&graph);
10-
let immediate_dominators = &dominators.immediate_dominators;
9+
let dom_tree = dominator_tree(&graph);
10+
let immediate_dominators = &dom_tree.immediate_dominators;
1111
assert_eq!(immediate_dominators[0], Some(0));
1212
assert_eq!(immediate_dominators[1], Some(0));
1313
assert_eq!(immediate_dominators[2], Some(0));
@@ -22,8 +22,8 @@ fn paper() {
2222
&[(6, 5), (6, 4), (5, 1), (4, 2), (4, 3), (1, 2), (2, 3), (3, 2), (2, 1)],
2323
);
2424

25-
let dominators = dominators(&graph);
26-
let immediate_dominators = &dominators.immediate_dominators;
25+
let dom_tree = dominator_tree(&graph);
26+
let immediate_dominators = &dom_tree.immediate_dominators;
2727
assert_eq!(immediate_dominators[0], None); // <-- note that 0 is not in graph
2828
assert_eq!(immediate_dominators[1], Some(6));
2929
assert_eq!(immediate_dominators[2], Some(6));
@@ -41,5 +41,5 @@ fn paper_slt() {
4141
&[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)],
4242
);
4343

44-
dominators(&graph);
44+
dominator_tree(&graph);
4545
}

compiler/rustc_middle/src/mir/basic_blocks.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use crate::mir::traversal::PostorderCache;
55
use crate::mir::{BasicBlock, BasicBlockData, Successors, START_BLOCK};
66

77
use rustc_data_structures::graph;
8-
use rustc_data_structures::graph::dominators::{dominators, Dominators};
8+
use rustc_data_structures::graph::dominators::{
9+
dominator_tree, dominators, DominatorTree, Dominators,
10+
};
911
use rustc_index::vec::IndexVec;
1012

1113
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
@@ -35,6 +37,10 @@ impl<'tcx> BasicBlocks<'tcx> {
3537
self.is_cyclic.is_cyclic(self)
3638
}
3739

40+
pub fn dominator_tree(&self) -> DominatorTree<BasicBlock> {
41+
dominator_tree(&self)
42+
}
43+
3844
#[inline]
3945
pub fn dominators(&self) -> Dominators<BasicBlock> {
4046
dominators(&self)

compiler/rustc_mir_transform/src/coverage/graph.rs

-20
Original file line numberDiff line numberDiff line change
@@ -652,26 +652,6 @@ pub(super) fn find_loop_backedges(
652652
let mut backedges = IndexVec::from_elem_n(Vec::<BasicCoverageBlock>::new(), num_bcbs);
653653

654654
// Identify loops by their backedges.
655-
//
656-
// The computational complexity is bounded by: n(s) x d where `n` is the number of
657-
// `BasicCoverageBlock` nodes (the simplified/reduced representation of the CFG derived from the
658-
// MIR); `s` is the average number of successors per node (which is most likely less than 2, and
659-
// independent of the size of the function, so it can be treated as a constant);
660-
// and `d` is the average number of dominators per node.
661-
//
662-
// The average number of dominators depends on the size and complexity of the function, and
663-
// nodes near the start of the function's control flow graph typically have less dominators
664-
// than nodes near the end of the CFG. Without doing a detailed mathematical analysis, I
665-
// think the resulting complexity has the characteristics of O(n log n).
666-
//
667-
// The overall complexity appears to be comparable to many other MIR transform algorithms, and I
668-
// don't expect that this function is creating a performance hot spot, but if this becomes an
669-
// issue, there may be ways to optimize the `dominates` algorithm (as indicated by an
670-
// existing `FIXME` comment in that code), or possibly ways to optimize it's usage here, perhaps
671-
// by keeping track of results for visited `BasicCoverageBlock`s if they can be used to short
672-
// circuit downstream `dominates` checks.
673-
//
674-
// For now, that kind of optimization seems unnecessarily complicated.
675655
for (bcb, _) in basic_coverage_blocks.iter_enumerated() {
676656
for &successor in &basic_coverage_blocks.successors[bcb] {
677657
if basic_coverage_blocks.dominates(successor, bcb) {

0 commit comments

Comments
 (0)