1 //! Compute dominators of a control-flow graph.
2 //!
3 //! # The Dominance Relation
4 //!
5 //! In a directed graph with a root node **R**, a node **A** is said to *dominate* a
6 //! node **B** iff every path from **R** to **B** contains **A**.
7 //!
8 //! The node **A** is said to *strictly dominate* the node **B** iff **A** dominates
9 //! **B** and **A ≠ B**.
10 //!
11 //! The node **A** is said to be the *immediate dominator* of a node **B** iff it
12 //! strictly dominates **B** and there does not exist any node **C** where **A**
13 //! dominates **C** and **C** dominates **B**.
14 
15 use std::cmp::Ordering;
16 use std::collections::{HashMap, HashSet};
17 use std::hash::Hash;
18 
19 use crate::visit::{DfsPostOrder, GraphBase, IntoNeighbors, Visitable, Walker};
20 
21 /// The dominance relation for some graph and root.
22 #[derive(Debug, Clone)]
23 pub struct Dominators<N>
24 where
25     N: Copy + Eq + Hash,
26 {
27     root: N,
28     dominators: HashMap<N, N>,
29 }
30 
31 impl<N> Dominators<N>
32 where
33     N: Copy + Eq + Hash,
34 {
35     /// Get the root node used to construct these dominance relations.
root(&self) -> N36     pub fn root(&self) -> N {
37         self.root
38     }
39 
40     /// Get the immediate dominator of the given node.
41     ///
42     /// Returns `None` for any node that is not reachable from the root, and for
43     /// the root itself.
immediate_dominator(&self, node: N) -> Option<N>44     pub fn immediate_dominator(&self, node: N) -> Option<N> {
45         if node == self.root {
46             None
47         } else {
48             self.dominators.get(&node).cloned()
49         }
50     }
51 
52     /// Iterate over the given node's strict dominators.
53     ///
54     /// If the given node is not reachable from the root, then `None` is
55     /// returned.
strict_dominators(&self, node: N) -> Option<DominatorsIter<N>>56     pub fn strict_dominators(&self, node: N) -> Option<DominatorsIter<N>> {
57         if self.dominators.contains_key(&node) {
58             Some(DominatorsIter {
59                 dominators: self,
60                 node: self.immediate_dominator(node),
61             })
62         } else {
63             None
64         }
65     }
66 
67     /// Iterate over all of the given node's dominators (including the given
68     /// node itself).
69     ///
70     /// If the given node is not reachable from the root, then `None` is
71     /// returned.
dominators(&self, node: N) -> Option<DominatorsIter<N>>72     pub fn dominators(&self, node: N) -> Option<DominatorsIter<N>> {
73         if self.dominators.contains_key(&node) {
74             Some(DominatorsIter {
75                 dominators: self,
76                 node: Some(node),
77             })
78         } else {
79             None
80         }
81     }
82 }
83 
84 /// Iterator for a node's dominators.
85 pub struct DominatorsIter<'a, N>
86 where
87     N: 'a + Copy + Eq + Hash,
88 {
89     dominators: &'a Dominators<N>,
90     node: Option<N>,
91 }
92 
93 impl<'a, N> Iterator for DominatorsIter<'a, N>
94 where
95     N: 'a + Copy + Eq + Hash,
96 {
97     type Item = N;
98 
next(&mut self) -> Option<Self::Item>99     fn next(&mut self) -> Option<Self::Item> {
100         let next = self.node.take();
101         if let Some(next) = next {
102             self.node = self.dominators.immediate_dominator(next);
103         }
104         next
105     }
106 }
107 
108 /// The undefined dominator sentinel, for when we have not yet discovered a
109 /// node's dominator.
110 const UNDEFINED: usize = ::std::usize::MAX;
111 
112 /// This is an implementation of the engineered ["Simple, Fast Dominance
113 /// Algorithm"][0] discovered by Cooper et al.
114 ///
115 /// This algorithm is **O(|V|²)**, and therefore has slower theoretical running time
116 /// than the Lengauer-Tarjan algorithm (which is **O(|E| log |V|)**. However,
117 /// Cooper et al found it to be faster in practice on control flow graphs of up
118 /// to ~30,000 vertices.
119 ///
120 /// [0]: http://www.cs.rice.edu/~keith/EMBED/dom.pdf
simple_fast<G>(graph: G, root: G::NodeId) -> Dominators<G::NodeId> where G: IntoNeighbors + Visitable, <G as GraphBase>::NodeId: Eq + Hash,121 pub fn simple_fast<G>(graph: G, root: G::NodeId) -> Dominators<G::NodeId>
122 where
123     G: IntoNeighbors + Visitable,
124     <G as GraphBase>::NodeId: Eq + Hash,
125 {
126     let (post_order, predecessor_sets) = simple_fast_post_order(graph, root);
127     let length = post_order.len();
128     debug_assert!(length > 0);
129     debug_assert!(post_order.last() == Some(&root));
130 
131     // From here on out we use indices into `post_order` instead of actual
132     // `NodeId`s wherever possible. This greatly improves the performance of
133     // this implementation, but we have to pay a little bit of upfront cost to
134     // convert our data structures to play along first.
135 
136     // Maps a node to its index into `post_order`.
137     let node_to_post_order_idx: HashMap<_, _> = post_order
138         .iter()
139         .enumerate()
140         .map(|(idx, &node)| (node, idx))
141         .collect();
142 
143     // Maps a node's `post_order` index to its set of predecessors's indices
144     // into `post_order` (as a vec).
145     let idx_to_predecessor_vec =
146         predecessor_sets_to_idx_vecs(&post_order, &node_to_post_order_idx, predecessor_sets);
147 
148     let mut dominators = vec![UNDEFINED; length];
149     dominators[length - 1] = length - 1;
150 
151     let mut changed = true;
152     while changed {
153         changed = false;
154 
155         // Iterate in reverse post order, skipping the root.
156 
157         for idx in (0..length - 1).rev() {
158             debug_assert!(post_order[idx] != root);
159 
160             // Take the intersection of every predecessor's dominator set; that
161             // is the current best guess at the immediate dominator for this
162             // node.
163 
164             let new_idom_idx = {
165                 let mut predecessors = idx_to_predecessor_vec[idx]
166                     .iter()
167                     .filter(|&&p| dominators[p] != UNDEFINED);
168                 let new_idom_idx = predecessors.next().expect(
169                     "Because the root is initialized to dominate itself, and is the \
170                      first node in every path, there must exist a predecessor to this \
171                      node that also has a dominator",
172                 );
173                 predecessors.fold(*new_idom_idx, |new_idom_idx, &predecessor_idx| {
174                     intersect(&dominators, new_idom_idx, predecessor_idx)
175                 })
176             };
177 
178             debug_assert!(new_idom_idx < length);
179 
180             if new_idom_idx != dominators[idx] {
181                 dominators[idx] = new_idom_idx;
182                 changed = true;
183             }
184         }
185     }
186 
187     // All done! Translate the indices back into proper `G::NodeId`s.
188 
189     debug_assert!(!dominators.iter().any(|&dom| dom == UNDEFINED));
190 
191     Dominators {
192         root,
193         dominators: dominators
194             .into_iter()
195             .enumerate()
196             .map(|(idx, dom_idx)| (post_order[idx], post_order[dom_idx]))
197             .collect(),
198     }
199 }
200 
intersect(dominators: &[usize], mut finger1: usize, mut finger2: usize) -> usize201 fn intersect(dominators: &[usize], mut finger1: usize, mut finger2: usize) -> usize {
202     loop {
203         match finger1.cmp(&finger2) {
204             Ordering::Less => finger1 = dominators[finger1],
205             Ordering::Greater => finger2 = dominators[finger2],
206             Ordering::Equal => return finger1,
207         }
208     }
209 }
210 
predecessor_sets_to_idx_vecs<N>( post_order: &[N], node_to_post_order_idx: &HashMap<N, usize>, mut predecessor_sets: HashMap<N, HashSet<N>>, ) -> Vec<Vec<usize>> where N: Copy + Eq + Hash,211 fn predecessor_sets_to_idx_vecs<N>(
212     post_order: &[N],
213     node_to_post_order_idx: &HashMap<N, usize>,
214     mut predecessor_sets: HashMap<N, HashSet<N>>,
215 ) -> Vec<Vec<usize>>
216 where
217     N: Copy + Eq + Hash,
218 {
219     post_order
220         .iter()
221         .map(|node| {
222             predecessor_sets
223                 .remove(node)
224                 .map(|predecessors| {
225                     predecessors
226                         .into_iter()
227                         .map(|p| *node_to_post_order_idx.get(&p).unwrap())
228                         .collect()
229                 })
230                 .unwrap_or_else(Vec::new)
231         })
232         .collect()
233 }
234 
simple_fast_post_order<G>( graph: G, root: G::NodeId, ) -> (Vec<G::NodeId>, HashMap<G::NodeId, HashSet<G::NodeId>>) where G: IntoNeighbors + Visitable, <G as GraphBase>::NodeId: Eq + Hash,235 fn simple_fast_post_order<G>(
236     graph: G,
237     root: G::NodeId,
238 ) -> (Vec<G::NodeId>, HashMap<G::NodeId, HashSet<G::NodeId>>)
239 where
240     G: IntoNeighbors + Visitable,
241     <G as GraphBase>::NodeId: Eq + Hash,
242 {
243     let mut post_order = vec![];
244     let mut predecessor_sets = HashMap::new();
245 
246     for node in DfsPostOrder::new(graph, root).iter(graph) {
247         post_order.push(node);
248 
249         for successor in graph.neighbors(node) {
250             predecessor_sets
251                 .entry(successor)
252                 .or_insert_with(HashSet::new)
253                 .insert(node);
254         }
255     }
256 
257     (post_order, predecessor_sets)
258 }
259 
260 #[cfg(test)]
261 mod tests {
262     use super::*;
263 
264     #[test]
test_iter_dominators()265     fn test_iter_dominators() {
266         let doms: Dominators<u32> = Dominators {
267             root: 0,
268             dominators: [(2, 1), (1, 0), (0, 0)].iter().cloned().collect(),
269         };
270 
271         let all_doms: Vec<_> = doms.dominators(2).unwrap().collect();
272         assert_eq!(vec![2, 1, 0], all_doms);
273 
274         assert_eq!(None::<()>, doms.dominators(99).map(|_| unreachable!()));
275 
276         let strict_doms: Vec<_> = doms.strict_dominators(2).unwrap().collect();
277         assert_eq!(vec![1, 0], strict_doms);
278 
279         assert_eq!(
280             None::<()>,
281             doms.strict_dominators(99).map(|_| unreachable!())
282         );
283     }
284 }
285