Skip to content

Commit a0f572e

Browse files
authored
Auto merge of #34169 - scottcarr:dominator-cache, r=nikomatsakis
[MIR] Add Dominators to MIR and Add Graph Algorithms ~~This PR assumes PR #34149 lands.~~ Add generic graph algorithms to rustc_data_structures. Add dominators and successors to the ~~cache (that currently only holds predecessors).~~ `Mir`.
2 parents 126af08 + 66d60c7 commit a0f572e

File tree

14 files changed

+871
-3
lines changed

14 files changed

+871
-3
lines changed

src/librustc/mir/cache.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use mir::repr::{Mir, BasicBlock};
1515

1616
use rustc_serialize as serialize;
1717

18-
#[derive(Clone)]
18+
#[derive(Clone, Debug)]
1919
pub struct Cache {
2020
predecessors: RefCell<Option<IndexVec<BasicBlock, Vec<BasicBlock>>>>
2121
}

src/librustc/mir/repr.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use graphviz::IntoCow;
1212
use middle::const_val::ConstVal;
1313
use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
1414
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
15+
use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
16+
use rustc_data_structures::control_flow_graph::{GraphPredecessors, GraphSuccessors};
17+
use rustc_data_structures::control_flow_graph::ControlFlowGraph;
1518
use hir::def_id::DefId;
1619
use ty::subst::Substs;
1720
use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
@@ -24,6 +27,7 @@ use std::cell::Ref;
2427
use std::fmt::{self, Debug, Formatter, Write};
2528
use std::{iter, u32};
2629
use std::ops::{Index, IndexMut};
30+
use std::vec::IntoIter;
2731
use syntax::ast::{self, Name};
2832
use syntax::codemap::Span;
2933

@@ -54,7 +58,7 @@ macro_rules! newtype_index {
5458
}
5559

5660
/// Lowered representation of a single function.
57-
#[derive(Clone, RustcEncodable, RustcDecodable)]
61+
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
5862
pub struct Mir<'tcx> {
5963
/// List of basic blocks. References to basic block use a newtyped index type `BasicBlock`
6064
/// that indexes into this vector.
@@ -145,6 +149,11 @@ impl<'tcx> Mir<'tcx> {
145149
Ref::map(self.predecessors(), |p| &p[bb])
146150
}
147151

152+
#[inline]
153+
pub fn dominators(&self) -> Dominators<BasicBlock> {
154+
dominators(self)
155+
}
156+
148157
/// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order)
149158
/// to their index in the whole list of locals. This is useful if you
150159
/// want to treat all locals the same instead of repeating yourself.
@@ -1190,3 +1199,33 @@ fn node_to_string(node_id: ast::NodeId) -> String {
11901199
fn item_path_str(def_id: DefId) -> String {
11911200
ty::tls::with(|tcx| tcx.item_path_str(def_id))
11921201
}
1202+
1203+
impl<'tcx> ControlFlowGraph for Mir<'tcx> {
1204+
1205+
type Node = BasicBlock;
1206+
1207+
fn num_nodes(&self) -> usize { self.basic_blocks.len() }
1208+
1209+
fn start_node(&self) -> Self::Node { START_BLOCK }
1210+
1211+
fn predecessors<'graph>(&'graph self, node: Self::Node)
1212+
-> <Self as GraphPredecessors<'graph>>::Iter
1213+
{
1214+
self.predecessors_for(node).clone().into_iter()
1215+
}
1216+
fn successors<'graph>(&'graph self, node: Self::Node)
1217+
-> <Self as GraphSuccessors<'graph>>::Iter
1218+
{
1219+
self.basic_blocks[node].terminator().successors().into_owned().into_iter()
1220+
}
1221+
}
1222+
1223+
impl<'a, 'b> GraphPredecessors<'b> for Mir<'a> {
1224+
type Item = BasicBlock;
1225+
type Iter = IntoIter<BasicBlock>;
1226+
}
1227+
1228+
impl<'a, 'b> GraphSuccessors<'b> for Mir<'a> {
1229+
type Item = BasicBlock;
1230+
type Iter = IntoIter<BasicBlock>;
1231+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Algorithm citation:
12+
//! A Simple, Fast Dominance Algorithm.
13+
//! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
14+
//! Rice Computer Science TS-06-33870
15+
//! https://www.cs.rice.edu/~keith/EMBED/dom.pdf
16+
17+
use super::ControlFlowGraph;
18+
use super::iterate::reverse_post_order;
19+
use super::super::indexed_vec::{IndexVec, Idx};
20+
21+
use std::fmt;
22+
23+
#[cfg(test)]
24+
mod test;
25+
26+
pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
27+
let start_node = graph.start_node();
28+
let rpo = reverse_post_order(graph, start_node);
29+
dominators_given_rpo(graph, &rpo)
30+
}
31+
32+
pub fn dominators_given_rpo<G: ControlFlowGraph>(graph: &G,
33+
rpo: &[G::Node])
34+
-> Dominators<G::Node> {
35+
let start_node = graph.start_node();
36+
assert_eq!(rpo[0], start_node);
37+
38+
// compute the post order index (rank) for each node
39+
let mut post_order_rank: IndexVec<G::Node, usize> = IndexVec::from_elem_n(usize::default(),
40+
graph.num_nodes());
41+
for (index, node) in rpo.iter().rev().cloned().enumerate() {
42+
post_order_rank[node] = index;
43+
}
44+
45+
let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
46+
IndexVec::from_elem_n(Option::default(), graph.num_nodes());
47+
immediate_dominators[start_node] = Some(start_node);
48+
49+
let mut changed = true;
50+
while changed {
51+
changed = false;
52+
53+
for &node in &rpo[1..] {
54+
let mut new_idom = None;
55+
for pred in graph.predecessors(node) {
56+
if immediate_dominators[pred].is_some() {
57+
// (*)
58+
// (*) dominators for `pred` have been calculated
59+
new_idom = intersect_opt(&post_order_rank,
60+
&immediate_dominators,
61+
new_idom,
62+
Some(pred));
63+
}
64+
}
65+
66+
if new_idom != immediate_dominators[node] {
67+
immediate_dominators[node] = new_idom;
68+
changed = true;
69+
}
70+
}
71+
}
72+
73+
Dominators {
74+
post_order_rank: post_order_rank,
75+
immediate_dominators: immediate_dominators,
76+
}
77+
}
78+
79+
fn intersect_opt<Node: Idx>(post_order_rank: &IndexVec<Node, usize>,
80+
immediate_dominators: &IndexVec<Node, Option<Node>>,
81+
node1: Option<Node>,
82+
node2: Option<Node>)
83+
-> Option<Node> {
84+
match (node1, node2) {
85+
(None, None) => None,
86+
(Some(n), None) | (None, Some(n)) => Some(n),
87+
(Some(n1), Some(n2)) => Some(intersect(post_order_rank, immediate_dominators, n1, n2)),
88+
}
89+
}
90+
91+
fn intersect<Node: Idx>(post_order_rank: &IndexVec<Node, usize>,
92+
immediate_dominators: &IndexVec<Node, Option<Node>>,
93+
mut node1: Node,
94+
mut node2: Node)
95+
-> Node {
96+
while node1 != node2 {
97+
while post_order_rank[node1] < post_order_rank[node2] {
98+
node1 = immediate_dominators[node1].unwrap();
99+
}
100+
101+
while post_order_rank[node2] < post_order_rank[node1] {
102+
node2 = immediate_dominators[node2].unwrap();
103+
}
104+
}
105+
return node1;
106+
}
107+
108+
#[derive(Clone, Debug)]
109+
pub struct Dominators<N: Idx> {
110+
post_order_rank: IndexVec<N, usize>,
111+
immediate_dominators: IndexVec<N, Option<N>>,
112+
}
113+
114+
impl<Node: Idx> Dominators<Node> {
115+
pub fn is_reachable(&self, node: Node) -> bool {
116+
self.immediate_dominators[node].is_some()
117+
}
118+
119+
pub fn immediate_dominator(&self, node: Node) -> Node {
120+
assert!(self.is_reachable(node), "node {:?} is not reachable", node);
121+
self.immediate_dominators[node].unwrap()
122+
}
123+
124+
pub fn dominators(&self, node: Node) -> Iter<Node> {
125+
assert!(self.is_reachable(node), "node {:?} is not reachable", node);
126+
Iter {
127+
dominators: self,
128+
node: Some(node),
129+
}
130+
}
131+
132+
pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
133+
// FIXME -- could be optimized by using post-order-rank
134+
self.dominators(node).any(|n| n == dom)
135+
}
136+
137+
pub fn mutual_dominator_node(&self, node1: Node, node2: Node) -> Node {
138+
assert!(self.is_reachable(node1),
139+
"node {:?} is not reachable",
140+
node1);
141+
assert!(self.is_reachable(node2),
142+
"node {:?} is not reachable",
143+
node2);
144+
intersect::<Node>(&self.post_order_rank,
145+
&self.immediate_dominators,
146+
node1,
147+
node2)
148+
}
149+
150+
pub fn mutual_dominator<I>(&self, iter: I) -> Option<Node>
151+
where I: IntoIterator<Item = Node>
152+
{
153+
let mut iter = iter.into_iter();
154+
iter.next()
155+
.map(|dom| iter.fold(dom, |dom, node| self.mutual_dominator_node(dom, node)))
156+
}
157+
158+
pub fn all_immediate_dominators(&self) -> &IndexVec<Node, Option<Node>> {
159+
&self.immediate_dominators
160+
}
161+
162+
pub fn dominator_tree(&self) -> DominatorTree<Node> {
163+
let elem: Vec<Node> = Vec::new();
164+
let mut children: IndexVec<Node, Vec<Node>> =
165+
IndexVec::from_elem_n(elem, self.immediate_dominators.len());
166+
let mut root = None;
167+
for (index, immed_dom) in self.immediate_dominators.iter().enumerate() {
168+
let node = Node::new(index);
169+
match *immed_dom {
170+
None => {
171+
// node not reachable
172+
}
173+
Some(immed_dom) => {
174+
if node == immed_dom {
175+
root = Some(node);
176+
} else {
177+
children[immed_dom].push(node);
178+
}
179+
}
180+
}
181+
}
182+
DominatorTree {
183+
root: root.unwrap(),
184+
children: children,
185+
}
186+
}
187+
}
188+
189+
pub struct Iter<'dom, Node: Idx + 'dom> {
190+
dominators: &'dom Dominators<Node>,
191+
node: Option<Node>,
192+
}
193+
194+
impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
195+
type Item = Node;
196+
197+
fn next(&mut self) -> Option<Self::Item> {
198+
if let Some(node) = self.node {
199+
let dom = self.dominators.immediate_dominator(node);
200+
if dom == node {
201+
self.node = None; // reached the root
202+
} else {
203+
self.node = Some(dom);
204+
}
205+
return Some(node);
206+
} else {
207+
return None;
208+
}
209+
}
210+
}
211+
212+
pub struct DominatorTree<N: Idx> {
213+
root: N,
214+
children: IndexVec<N, Vec<N>>,
215+
}
216+
217+
impl<Node: Idx> DominatorTree<Node> {
218+
pub fn root(&self) -> Node {
219+
self.root
220+
}
221+
222+
pub fn children(&self, node: Node) -> &[Node] {
223+
&self.children[node]
224+
}
225+
226+
pub fn iter_children_of(&self, node: Node) -> IterChildrenOf<Node> {
227+
IterChildrenOf {
228+
tree: self,
229+
stack: vec![node],
230+
}
231+
}
232+
}
233+
234+
pub struct IterChildrenOf<'iter, Node: Idx + 'iter> {
235+
tree: &'iter DominatorTree<Node>,
236+
stack: Vec<Node>,
237+
}
238+
239+
impl<'iter, Node: Idx> Iterator for IterChildrenOf<'iter, Node> {
240+
type Item = Node;
241+
242+
fn next(&mut self) -> Option<Node> {
243+
if let Some(node) = self.stack.pop() {
244+
self.stack.extend(self.tree.children(node));
245+
Some(node)
246+
} else {
247+
None
248+
}
249+
}
250+
}
251+
252+
impl<Node: Idx> fmt::Debug for DominatorTree<Node> {
253+
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
254+
fmt::Debug::fmt(&DominatorTreeNode {
255+
tree: self,
256+
node: self.root,
257+
},
258+
fmt)
259+
}
260+
}
261+
262+
struct DominatorTreeNode<'tree, Node: Idx> {
263+
tree: &'tree DominatorTree<Node>,
264+
node: Node,
265+
}
266+
267+
impl<'tree, Node: Idx> fmt::Debug for DominatorTreeNode<'tree, Node> {
268+
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
269+
let subtrees: Vec<_> = self.tree
270+
.children(self.node)
271+
.iter()
272+
.map(|&child| {
273+
DominatorTreeNode {
274+
tree: self.tree,
275+
node: child,
276+
}
277+
})
278+
.collect();
279+
fmt.debug_tuple("")
280+
.field(&self.node)
281+
.field(&subtrees)
282+
.finish()
283+
}
284+
}

0 commit comments

Comments
 (0)