Skip to content

Commit e8d7248

Browse files
Implement the simple Lengauer-Tarjan algorithm
This replaces the previous implementation with the simple variant of Lengauer-Tarjan, which performs better in the general case. Performance on the keccak benchmark is about equivalent between the two, but we don't see regressions (and indeed see improvements) on other benchmarks, even on a partially optimized implementation. The implementation here follows that of the pseudocode in "Linear-Time Algorithms for Dominators and Related Problems" thesis by Loukas Georgiadis. The next few commits will optimize the implementation as suggested in the thesis. Several related works are cited in the comments within the implementation, as well. Implement the simple Lengauer-Tarjan algorithm This replaces the previous implementation (from rust-lang#34169), which has not been optimized since, with the simple variant of Lengauer-Tarjan which performs better in the general case. A previous attempt -- not kept in commit history -- attempted a replacement with a bitset-based implementation, but this led to regressions on perf.rust-lang.org benchmarks and equivalent wins for the keccak benchmark, so was rejected. The implementation here follows that of the pseudocode in "Linear-Time Algorithms for Dominators and Related Problems" thesis by Loukas Georgiadis. The next few commits will optimize the implementation as suggested in the thesis. Several related works are cited in the comments within the implementation, as well. On the keccak benchmark, we were previously spending 15% of our cycles computing the NCA / intersect function; this function is quite expensive, especially on modern CPUs, as it chases pointers on every iteration in a tight loop. With this commit, we spend ~0.05% of our time in dominator computation.
1 parent 0fb1c37 commit e8d7248

File tree

2 files changed

+116
-39
lines changed

2 files changed

+116
-39
lines changed

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

+105-39
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
//! Finding the dominators in a control-flow graph.
22
//!
3-
//! Algorithm based on Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy,
4-
//! "A Simple, Fast Dominance Algorithm",
5-
//! Rice Computer Science TS-06-33870,
6-
//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>.
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
76
87
use super::iterate::reverse_post_order;
98
use super::ControlFlowGraph;
@@ -19,6 +18,11 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
1918
dominators_given_rpo(graph, &rpo)
2019
}
2120

21+
struct PreOrderFrame<Node, Iter> {
22+
node: Node,
23+
iter: Iter,
24+
}
25+
2226
fn dominators_given_rpo<G: ControlFlowGraph>(graph: G, rpo: &[G::Node]) -> Dominators<G::Node> {
2327
let start_node = graph.start_node();
2428
assert_eq!(rpo[0], start_node);
@@ -29,53 +33,115 @@ fn dominators_given_rpo<G: ControlFlowGraph>(graph: G, rpo: &[G::Node]) -> Domin
2933
post_order_rank[node] = index;
3034
}
3135

32-
let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
33-
immediate_dominators[start_node] = Some(start_node);
34-
35-
let mut changed = true;
36-
while changed {
37-
changed = false;
38-
39-
for &node in &rpo[1..] {
40-
let mut new_idom = None;
41-
for pred in graph.predecessors(node) {
42-
if immediate_dominators[pred].is_some() {
43-
// (*) dominators for `pred` have been calculated
44-
new_idom = Some(if let Some(new_idom) = new_idom {
45-
intersect(&post_order_rank, &immediate_dominators, new_idom, pred)
46-
} else {
47-
pred
48-
});
49-
}
36+
let mut visited = BitSet::new_empty(graph.num_nodes());
37+
let mut parent: IndexVec<G::Node, Option<G::Node>> =
38+
IndexVec::from_elem_n(None, graph.num_nodes());
39+
let mut pre_order_index: IndexVec<G::Node, Option<usize>> =
40+
IndexVec::from_elem_n(None, graph.num_nodes());
41+
let mut pre_order_nodes = Vec::with_capacity(rpo.len());
42+
43+
let mut stack = vec![PreOrderFrame {
44+
node: graph.start_node(),
45+
iter: graph.successors(graph.start_node()),
46+
}];
47+
visited.insert(graph.start_node());
48+
let mut idx = 0;
49+
pre_order_index[graph.start_node()] = Some(0);
50+
idx += 1;
51+
pre_order_nodes.push(graph.start_node());
52+
53+
'recurse: while let Some(frame) = stack.last_mut() {
54+
while let Some(successor) = frame.iter.next() {
55+
if visited.insert(successor) {
56+
parent[successor] = Some(frame.node);
57+
pre_order_index[successor] = Some(idx);
58+
pre_order_nodes.push(successor);
59+
idx += 1;
60+
61+
stack.push(PreOrderFrame { node: successor, iter: graph.successors(successor) });
62+
continue 'recurse;
5063
}
64+
}
65+
stack.pop();
66+
}
5167

52-
if new_idom != immediate_dominators[node] {
53-
immediate_dominators[node] = new_idom;
54-
changed = true;
55-
}
68+
let mut ancestor = IndexVec::from_elem_n(None, graph.num_nodes());
69+
let mut idom = IndexVec::from_elem_n(graph.start_node(), graph.num_nodes());
70+
let mut semi = IndexVec::from_fn_n(std::convert::identity, graph.num_nodes());
71+
let mut label = semi.clone();
72+
let mut bucket = IndexVec::from_elem_n(vec![], graph.num_nodes());
73+
74+
for &w in pre_order_nodes[1..].iter().rev() {
75+
semi[w] = w;
76+
for v in graph.predecessors(w) {
77+
let x = eval(&pre_order_index, &mut ancestor, &semi, &mut label, v);
78+
semi[w] = if pre_order_index[semi[w]].unwrap() < pre_order_index[semi[x]].unwrap() {
79+
semi[w]
80+
} else {
81+
semi[x]
82+
};
83+
}
84+
// semi[w] is now semidominator(w).
85+
86+
bucket[semi[w]].push(w);
87+
88+
link(&mut ancestor, &parent, w);
89+
let z = parent[w].unwrap();
90+
for v in std::mem::take(&mut bucket[z]) {
91+
let y = eval(&pre_order_index, &mut ancestor, &semi, &mut label, v);
92+
idom[v] = if pre_order_index[semi[y]] < pre_order_index[z] { y } else { z };
93+
}
94+
}
95+
for &w in pre_order_nodes.iter().skip(1) {
96+
if idom[w] != semi[w] {
97+
idom[w] = idom[idom[w]];
98+
}
99+
}
100+
101+
let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
102+
for (node, idom_slot) in immediate_dominators.iter_enumerated_mut() {
103+
if pre_order_index[node].is_some() {
104+
*idom_slot = Some(idom[node]);
56105
}
57106
}
58107

59108
Dominators { post_order_rank, immediate_dominators }
60109
}
61110

62-
fn intersect<Node: Idx>(
63-
post_order_rank: &IndexVec<Node, usize>,
64-
immediate_dominators: &IndexVec<Node, Option<Node>>,
65-
mut node1: Node,
66-
mut node2: Node,
67-
) -> Node {
68-
while node1 != node2 {
69-
while post_order_rank[node1] < post_order_rank[node2] {
70-
node1 = immediate_dominators[node1].unwrap();
71-
}
111+
fn eval<N: Idx>(
112+
pre_order_index: &IndexVec<N, Option<usize>>,
113+
ancestor: &mut IndexVec<N, Option<N>>,
114+
semi: &IndexVec<N, N>,
115+
label: &mut IndexVec<N, N>,
116+
node: N,
117+
) -> N {
118+
if ancestor[node].is_some() {
119+
compress(pre_order_index, ancestor, semi, label, node);
120+
label[node]
121+
} else {
122+
node
123+
}
124+
}
72125

73-
while post_order_rank[node2] < post_order_rank[node1] {
74-
node2 = immediate_dominators[node2].unwrap();
126+
fn compress<N: Idx>(
127+
pre_order_index: &IndexVec<N, Option<usize>>,
128+
ancestor: &mut IndexVec<N, Option<N>>,
129+
semi: &IndexVec<N, N>,
130+
label: &mut IndexVec<N, N>,
131+
v: N,
132+
) {
133+
let u = ancestor[v].unwrap();
134+
if ancestor[u].is_some() {
135+
compress(pre_order_index, ancestor, semi, label, u);
136+
if pre_order_index[semi[label[u]]] < pre_order_index[semi[label[v]]] {
137+
label[v] = label[u];
75138
}
139+
ancestor[v] = ancestor[u];
76140
}
141+
}
77142

78-
node1
143+
fn link<N: Idx>(ancestor: &mut IndexVec<N, Option<N>>, parent: &IndexVec<N, Option<N>>, w: N) {
144+
ancestor[w] = Some(parent[w].unwrap());
79145
}
80146

81147
#[derive(Clone, Debug)]

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

+11
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,14 @@ fn paper() {
3232
assert_eq!(immediate_dominators[5], Some(6));
3333
assert_eq!(immediate_dominators[6], Some(6));
3434
}
35+
36+
#[test]
37+
fn paper_slt() {
38+
// example from the paper:
39+
let graph = TestGraph::new(
40+
1,
41+
&[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)],
42+
);
43+
44+
dominators(&graph);
45+
}

0 commit comments

Comments
 (0)