llvm_ir_analysis/dominator_tree.rs
1use crate::control_flow_graph::{CFGNode, ControlFlowGraph};
2use llvm_ir::Name;
3use petgraph::prelude::{Dfs, DiGraphMap, Direction};
4use petgraph::visit::Walker;
5use std::cmp::Ordering;
6use std::collections::HashMap;
7
8/// The dominator tree for a particular function.
9///
10/// To construct a `DominatorTree`, use
11/// [`FunctionAnalysis`](struct.FunctionAnalysis.html), which you can get
12/// from [`ModuleAnalysis`](struct.ModuleAnalysis.html).
13pub struct DominatorTree<'m> {
14 /// The graph itself. An edge from bbX to bbY indicates that bbX is the
15 /// immediate dominator of bbY.
16 ///
17 /// That is:
18 /// - bbX strictly dominates bbY, i.e., bbX appears on every control-flow
19 /// path from the entry block to bbY (but bbX =/= bbY)
20 /// - Of the blocks that strictly dominate bbY, bbX is the closest to bbY
21 /// (farthest from entry) along paths from the entry block to bbY
22 pub(crate) graph: DiGraphMap<CFGNode<'m>, ()>,
23
24 /// Entry node for the function
25 pub(crate) entry_node: CFGNode<'m>,
26}
27
28/// The postdominator tree for a particular function.
29///
30/// To construct a `PostDominatorTree`, use
31/// [`FunctionAnalysis`](struct.FunctionAnalysis.html), which you can get
32/// from [`ModuleAnalysis`](struct.ModuleAnalysis.html).
33pub struct PostDominatorTree<'m> {
34 /// The graph itself. An edge from bbX to bbY indicates that bbX is the
35 /// immediate postdominator of bbY.
36 ///
37 /// That is:
38 /// - bbX strictly postdominates bbY, i.e., bbX appears on every control-flow
39 /// path from bbY to the function exit (but bbX =/= bbY)
40 /// - Of the blocks that strictly postdominate bbY, bbX is the closest to bbY
41 /// (farthest from exit) along paths from bbY to the function exit
42 pub(crate) graph: DiGraphMap<CFGNode<'m>, ()>,
43}
44
45/// Contains state used when constructing the `DominatorTree` or `PostDominatorTree`
46struct DomTreeBuilder<'m, 'a> {
47 /// The `ControlFlowGraph` we're working from
48 cfg: &'a ControlFlowGraph<'m>,
49
50 /// Map from `CFGNode` to its rpo number.
51 ///
52 /// Unreachable blocks won't be in this map; all reachable blocks will have
53 /// positive rpo numbers.
54 rpo_numbers: HashMap<CFGNode<'m>, usize>,
55
56 /// Map from `CFGNode` to the current estimate for its immediate dominator
57 /// (the entry node maps to `None`).
58 ///
59 /// Unreachable blocks won't be in this map.
60 idoms: HashMap<CFGNode<'m>, Option<CFGNode<'m>>>,
61}
62
63impl<'m, 'a> DomTreeBuilder<'m, 'a> {
64 /// Construct a new `DomTreeBuilder`.
65 ///
66 /// This will have no estimates for the immediate dominators.
67 fn new(cfg: &'a ControlFlowGraph<'m>) -> Self {
68 Self {
69 cfg,
70 rpo_numbers: Dfs::new(&cfg.graph, cfg.entry_node)
71 .iter(&cfg.graph)
72 .zip(1..)
73 .collect(),
74 idoms: HashMap::new(),
75 }
76 }
77
78 /// Build the dominator tree
79 fn build(mut self) -> DiGraphMap<CFGNode<'m>, ()> {
80 // algorithm heavily inspired by the domtree algorithm in Cranelift,
81 // which itself is Keith D. Cooper's "Simple, Fast, Dominator Algorithm"
82 // according to comments in Cranelift's code.
83
84 // first compute initial (preliminary) estimates for the immediate
85 // dominator of each block
86 for block in Dfs::new(&self.cfg.graph, self.cfg.entry_node).iter(&self.cfg.graph) {
87 self.idoms.insert(block, self.compute_idom(block));
88 }
89
90 let mut changed = true;
91 while changed {
92 changed = false;
93 for block in Dfs::new(&self.cfg.graph, self.cfg.entry_node).iter(&self.cfg.graph) {
94 let idom = self.compute_idom(block);
95 let prev_idom = self
96 .idoms
97 .get_mut(&block)
98 .expect("All nodes in the dfs should have an initialized idom by now");
99 if idom != *prev_idom {
100 *prev_idom = idom;
101 changed = true;
102 }
103 }
104 }
105
106 DiGraphMap::from_edges(
107 self.idoms
108 .into_iter()
109 .filter_map(|(block, idom)| Some((idom?, block))),
110 )
111 }
112
113 /// Compute the immediate dominator for `block` using the current `idom`
114 /// states for the nodes.
115 ///
116 /// `block` must be reachable in the CFG. Returns `None` only for the entry
117 /// block.
118 fn compute_idom(&self, block: CFGNode<'m>) -> Option<CFGNode<'m>> {
119 if block == self.cfg.entry_node {
120 return None;
121 }
122 // technically speaking, these are just the reachable preds which already have an idom estimate
123 let mut reachable_preds = self
124 .cfg
125 .preds_as_nodes(block)
126 .filter(|block| self.idoms.contains_key(block));
127
128 let mut idom = reachable_preds
129 .next()
130 .expect("expected a reachable block to have at least one reachable predecessor");
131
132 for pred in reachable_preds {
133 idom = self.common_dominator(idom, pred);
134 }
135
136 Some(idom)
137 }
138
139 /// Compute the common dominator of two nodes.
140 ///
141 /// Both nodes are assumed to be reachable.
142 fn common_dominator(&self, mut node_a: CFGNode<'m>, mut node_b: CFGNode<'m>) -> CFGNode<'m> {
143 loop {
144 match self.rpo_numbers[&node_a].cmp(&self.rpo_numbers[&node_b]) {
145 Ordering::Less => {
146 node_b = self.idoms[&node_b]
147 .expect("entry node should have the smallest rpo number");
148 }
149 Ordering::Greater => {
150 node_a = self.idoms[&node_a]
151 .expect("entry node should have the smallest rpo number");
152 }
153 Ordering::Equal => break,
154 }
155 }
156
157 node_a
158 }
159}
160
161impl<'m> DominatorTree<'m> {
162 pub(crate) fn new(cfg: &ControlFlowGraph<'m>) -> Self {
163 Self {
164 graph: DomTreeBuilder::new(cfg).build(),
165 entry_node: cfg.entry_node,
166 }
167 }
168
169 /// Get the immediate dominator of the basic block with the given `Name`.
170 ///
171 /// This will be `None` for the entry block or for any unreachable blocks,
172 /// and `Some` for all other blocks.
173 ///
174 /// A block bbX is the immediate dominator of bbY if and only if:
175 /// - bbX strictly dominates bbY, i.e., bbX appears on every control-flow
176 /// path from the entry block to bbY (but bbX =/= bbY)
177 /// - Of the blocks that strictly dominate bbY, bbX is the closest to bbY
178 /// (farthest from entry) along paths from the entry block to bbY
179 pub fn idom(&self, block: &'m Name) -> Option<&'m Name> {
180 let mut parents = self
181 .graph
182 .neighbors_directed(CFGNode::Block(block), Direction::Incoming);
183 let idom = parents.next()?;
184 if let Some(_) = parents.next() {
185 panic!("Block {:?} should have only one immediate dominator", block);
186 }
187 match idom {
188 CFGNode::Block(block) => Some(block),
189 CFGNode::Return => {
190 panic!("Return node shouldn't be the immediate dominator of anything")
191 }
192 }
193 }
194
195 /// Get the immediate dominator of `CFGNode::Return`.
196 ///
197 /// This will be the block bbX such that:
198 /// - bbX strictly dominates `CFGNode::Return`, i.e., bbX appears on every
199 /// control-flow path through the function (but bbX =/= `CFGNode::Return`)
200 /// - Of the blocks that strictly dominate `CFGNode::Return`, bbX is the
201 /// closest to `CFGNode::Return` (farthest from entry) along paths through
202 /// the function
203 ///
204 /// If the return node is unreachable (e.g., due to an infinite loop in the
205 /// function), then the return node has no immediate dominator, and `None` will
206 /// be returned.
207 pub fn idom_of_return(&self) -> Option<&'m Name> {
208 let mut parents = self
209 .graph
210 .neighbors_directed(CFGNode::Return, Direction::Incoming);
211 let idom = parents.next()?;
212 if let Some(_) = parents.next() {
213 panic!("Return node should have only one immediate dominator");
214 }
215 match idom {
216 CFGNode::Block(block) => Some(block),
217 CFGNode::Return => panic!("Return node shouldn't be its own immediate dominator"),
218 }
219 }
220
221 /// Get the children of the given basic block in the dominator tree, i.e.,
222 /// get all the blocks which are immediately dominated by `block`.
223 ///
224 /// See notes on `idom()`.
225 pub fn children<'s>(&'s self, block: &'m Name) -> impl Iterator<Item = CFGNode<'m>> + 's {
226 self.graph
227 .neighbors_directed(CFGNode::Block(block), Direction::Outgoing)
228 }
229
230 /// Does `node_a` dominate `node_b`?
231 ///
232 /// Note that every node dominates itself by definition, so if
233 /// `node_a == node_b`, this returns `true`.
234 /// See also `strictly_dominates()`
235 pub fn dominates(&self, node_a: CFGNode<'m>, node_b: CFGNode<'m>) -> bool {
236 petgraph::algo::has_path_connecting(&self.graph, node_a, node_b, None)
237 }
238
239 /// Does `node_a` strictly dominate `node_b`?
240 ///
241 /// This is the same as `dominates()`, except that if
242 /// `node_a == node_b`, this returns `false`.
243 pub fn strictly_dominates(&self, node_a: CFGNode<'m>, node_b: CFGNode<'m>) -> bool {
244 node_a != node_b && self.dominates(node_a, node_b)
245 }
246
247 /// Get the `Name` of the entry block for the function
248 pub fn entry(&self) -> &'m Name {
249 match self.entry_node {
250 CFGNode::Block(block) => block,
251 CFGNode::Return => panic!("Return node should not be entry"),
252 }
253 }
254}
255
256impl<'m> PostDominatorTree<'m> {
257 pub(crate) fn new(cfg: &ControlFlowGraph<'m>) -> Self {
258 // The postdominator relation for `cfg` is the dominator relation on
259 // the reversed `cfg` (Cytron et al, p. 477)
260
261 Self {
262 graph: DomTreeBuilder::new(&cfg.reversed()).build(),
263 }
264 }
265
266 /// Get the immediate postdominator of the basic block with the given `Name`.
267 ///
268 /// This will be `None` for unreachable blocks (or, in some cases, when the
269 /// function return is unreachable, e.g. due to an infinite loop), and `Some`
270 /// for all other blocks.
271 ///
272 /// A block bbX is the immediate postdominator of bbY if and only if:
273 /// - bbX strictly postdominates bbY, i.e., bbX appears on every control-flow
274 /// path from bbY to the function exit (but bbX =/= bbY)
275 /// - Of the blocks that strictly postdominate bbY, bbX is the closest to bbY
276 /// (farthest from exit) along paths from bbY to the function exit
277 ///
278 /// If the immediate postdominator is `CFGNode::Return`, that indicates that
279 /// there is no single basic block that postdominates the given block.
280 pub fn ipostdom(&self, block: &'m Name) -> Option<CFGNode<'m>> {
281 self.ipostdom_of_cfgnode(CFGNode::Block(block))
282 }
283
284 /// See notes on `ipostdom()`, but in addition, this will be `None` for
285 /// `CFGNode::Return`
286 pub(crate) fn ipostdom_of_cfgnode(&self, node: CFGNode<'m>) -> Option<CFGNode<'m>> {
287 let mut parents = self.graph.neighbors_directed(node, Direction::Incoming);
288 let ipostdom = parents.next()?;
289 if let Some(_) = parents.next() {
290 panic!(
291 "Block {:?} should have only one immediate postdominator",
292 node
293 );
294 }
295 Some(ipostdom)
296 }
297
298 /// Get the children of the given basic block in the postdominator tree, i.e.,
299 /// get all the blocks which are immediately postdominated by `block`.
300 ///
301 /// See notes on `ipostdom()`.
302 pub fn children<'s>(&'s self, block: &'m Name) -> impl Iterator<Item = CFGNode<'m>> + 's {
303 self.children_of_cfgnode(CFGNode::Block(block))
304 }
305
306 pub(crate) fn children_of_cfgnode<'s>(
307 &'s self,
308 node: CFGNode<'m>,
309 ) -> impl Iterator<Item = CFGNode<'m>> + 's {
310 self.graph.neighbors_directed(node, Direction::Outgoing)
311 }
312
313 /// Get the children of `CFGNode::Return` in the postdominator tree, i.e.,
314 /// get all the blocks which are immediately postdominated by `CFGNode::Return`.
315 ///
316 /// See notes on `ipostdom()`.
317 pub fn children_of_return<'s>(&'s self) -> impl Iterator<Item = &'m Name> + 's {
318 self.graph
319 .neighbors_directed(CFGNode::Return, Direction::Outgoing)
320 .map(|child| match child {
321 CFGNode::Block(block) => block,
322 CFGNode::Return => {
323 panic!("Return node shouldn't be the immediate postdominator of itself")
324 }
325 })
326 }
327
328 /// Does `node_a` postdominate `node_b`?
329 ///
330 /// Note that every node postdominates itself by definition, so if
331 /// `node_a == node_b`, this returns `true`.
332 /// See also `strictly_postdominates()`
333 pub fn postdominates(&self, node_a: CFGNode<'m>, node_b: CFGNode<'m>) -> bool {
334 petgraph::algo::has_path_connecting(&self.graph, node_a, node_b, None)
335 }
336
337 /// Does `node_a` strictly postdominate `node_b`?
338 ///
339 /// This is the same as `postdominates()`, except that if
340 /// `node_a == node_b`, this returns `false`.
341 pub fn strictly_postdominates(&self, node_a: CFGNode<'m>, node_b: CFGNode<'m>) -> bool {
342 node_a != node_b && self.postdominates(node_a, node_b)
343 }
344}