petgraph/algo/
dominators.rs

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
15use std::cmp::Ordering;
16use std::collections::{hash_map::Iter, HashMap, HashSet};
17use std::hash::Hash;
18
19use crate::visit::{DfsPostOrder, GraphBase, IntoNeighbors, Visitable, Walker};
20
21/// The dominance relation for some graph and root.
22#[derive(Debug, Clone)]
23pub struct Dominators<N>
24where
25    N: Copy + Eq + Hash,
26{
27    root: N,
28    dominators: HashMap<N, N>,
29}
30
31impl<N> Dominators<N>
32where
33    N: Copy + Eq + Hash,
34{
35    /// Get the root node used to construct these dominance relations.
36    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.
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.
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.
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    /// Iterate over all nodes immediately dominated by the given node (not
84    /// including the given node itself).
85    pub fn immediately_dominated_by(&self, node: N) -> DominatedByIter<N> {
86        DominatedByIter {
87            iter: self.dominators.iter(),
88            node,
89        }
90    }
91}
92
93/// Iterator for a node's dominators.
94#[derive(Debug, Clone)]
95pub struct DominatorsIter<'a, N>
96where
97    N: 'a + Copy + Eq + Hash,
98{
99    dominators: &'a Dominators<N>,
100    node: Option<N>,
101}
102
103impl<'a, N> Iterator for DominatorsIter<'a, N>
104where
105    N: 'a + Copy + Eq + Hash,
106{
107    type Item = N;
108
109    fn next(&mut self) -> Option<Self::Item> {
110        let next = self.node.take();
111        if let Some(next) = next {
112            self.node = self.dominators.immediate_dominator(next);
113        }
114        next
115    }
116}
117
118/// Iterator for nodes dominated by a given node.
119#[derive(Debug, Clone)]
120pub struct DominatedByIter<'a, N>
121where
122    N: 'a + Copy + Eq + Hash,
123{
124    iter: Iter<'a, N, N>,
125    node: N,
126}
127
128impl<'a, N> Iterator for DominatedByIter<'a, N>
129where
130    N: 'a + Copy + Eq + Hash,
131{
132    type Item = N;
133
134    fn next(&mut self) -> Option<Self::Item> {
135        for (dominator, dominated) in self.iter.by_ref() {
136            // The root node dominates itself, but it should not be included in
137            // the results.
138            if dominated == &self.node && dominated != dominator {
139                return Some(*dominator);
140            }
141        }
142        None
143    }
144    fn size_hint(&self) -> (usize, Option<usize>) {
145        let (_, upper) = self.iter.size_hint();
146        (0, upper)
147    }
148}
149
150/// The undefined dominator sentinel, for when we have not yet discovered a
151/// node's dominator.
152const UNDEFINED: usize = ::std::usize::MAX;
153
154/// This is an implementation of the engineered ["Simple, Fast Dominance
155/// Algorithm"][0] discovered by Cooper et al.
156///
157/// This algorithm is **O(|V|²)**, and therefore has slower theoretical running time
158/// than the Lengauer-Tarjan algorithm (which is **O(|E| log |V|)**. However,
159/// Cooper et al found it to be faster in practice on control flow graphs of up
160/// to ~30,000 vertices.
161///
162/// [0]: http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
163pub fn simple_fast<G>(graph: G, root: G::NodeId) -> Dominators<G::NodeId>
164where
165    G: IntoNeighbors + Visitable,
166    <G as GraphBase>::NodeId: Eq + Hash,
167{
168    let (post_order, predecessor_sets) = simple_fast_post_order(graph, root);
169    let length = post_order.len();
170    debug_assert!(length > 0);
171    debug_assert!(post_order.last() == Some(&root));
172
173    // From here on out we use indices into `post_order` instead of actual
174    // `NodeId`s wherever possible. This greatly improves the performance of
175    // this implementation, but we have to pay a little bit of upfront cost to
176    // convert our data structures to play along first.
177
178    // Maps a node to its index into `post_order`.
179    let node_to_post_order_idx: HashMap<_, _> = post_order
180        .iter()
181        .enumerate()
182        .map(|(idx, &node)| (node, idx))
183        .collect();
184
185    // Maps a node's `post_order` index to its set of predecessors's indices
186    // into `post_order` (as a vec).
187    let idx_to_predecessor_vec =
188        predecessor_sets_to_idx_vecs(&post_order, &node_to_post_order_idx, predecessor_sets);
189
190    let mut dominators = vec![UNDEFINED; length];
191    dominators[length - 1] = length - 1;
192
193    let mut changed = true;
194    while changed {
195        changed = false;
196
197        // Iterate in reverse post order, skipping the root.
198
199        for idx in (0..length - 1).rev() {
200            debug_assert!(post_order[idx] != root);
201
202            // Take the intersection of every predecessor's dominator set; that
203            // is the current best guess at the immediate dominator for this
204            // node.
205
206            let new_idom_idx = {
207                let mut predecessors = idx_to_predecessor_vec[idx]
208                    .iter()
209                    .filter(|&&p| dominators[p] != UNDEFINED);
210                let new_idom_idx = predecessors.next().expect(
211                    "Because the root is initialized to dominate itself, and is the \
212                     first node in every path, there must exist a predecessor to this \
213                     node that also has a dominator",
214                );
215                predecessors.fold(*new_idom_idx, |new_idom_idx, &predecessor_idx| {
216                    intersect(&dominators, new_idom_idx, predecessor_idx)
217                })
218            };
219
220            debug_assert!(new_idom_idx < length);
221
222            if new_idom_idx != dominators[idx] {
223                dominators[idx] = new_idom_idx;
224                changed = true;
225            }
226        }
227    }
228
229    // All done! Translate the indices back into proper `G::NodeId`s.
230
231    debug_assert!(!dominators.iter().any(|&dom| dom == UNDEFINED));
232
233    Dominators {
234        root,
235        dominators: dominators
236            .into_iter()
237            .enumerate()
238            .map(|(idx, dom_idx)| (post_order[idx], post_order[dom_idx]))
239            .collect(),
240    }
241}
242
243fn intersect(dominators: &[usize], mut finger1: usize, mut finger2: usize) -> usize {
244    loop {
245        match finger1.cmp(&finger2) {
246            Ordering::Less => finger1 = dominators[finger1],
247            Ordering::Greater => finger2 = dominators[finger2],
248            Ordering::Equal => return finger1,
249        }
250    }
251}
252
253fn predecessor_sets_to_idx_vecs<N>(
254    post_order: &[N],
255    node_to_post_order_idx: &HashMap<N, usize>,
256    mut predecessor_sets: HashMap<N, HashSet<N>>,
257) -> Vec<Vec<usize>>
258where
259    N: Copy + Eq + Hash,
260{
261    post_order
262        .iter()
263        .map(|node| {
264            predecessor_sets
265                .remove(node)
266                .map(|predecessors| {
267                    predecessors
268                        .into_iter()
269                        .map(|p| *node_to_post_order_idx.get(&p).unwrap())
270                        .collect()
271                })
272                .unwrap_or_default()
273        })
274        .collect()
275}
276
277type PredecessorSets<NodeId> = HashMap<NodeId, HashSet<NodeId>>;
278
279fn simple_fast_post_order<G>(
280    graph: G,
281    root: G::NodeId,
282) -> (Vec<G::NodeId>, PredecessorSets<G::NodeId>)
283where
284    G: IntoNeighbors + Visitable,
285    <G as GraphBase>::NodeId: Eq + Hash,
286{
287    let mut post_order = vec![];
288    let mut predecessor_sets = HashMap::new();
289
290    for node in DfsPostOrder::new(graph, root).iter(graph) {
291        post_order.push(node);
292
293        for successor in graph.neighbors(node) {
294            predecessor_sets
295                .entry(successor)
296                .or_insert_with(HashSet::new)
297                .insert(node);
298        }
299    }
300
301    (post_order, predecessor_sets)
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307
308    #[test]
309    fn test_iter_dominators() {
310        let doms: Dominators<u32> = Dominators {
311            root: 0,
312            dominators: [(2, 1), (1, 0), (0, 0)].iter().cloned().collect(),
313        };
314
315        let all_doms: Vec<_> = doms.dominators(2).unwrap().collect();
316        assert_eq!(vec![2, 1, 0], all_doms);
317
318        assert_eq!(None::<()>, doms.dominators(99).map(|_| unreachable!()));
319
320        let strict_doms: Vec<_> = doms.strict_dominators(2).unwrap().collect();
321        assert_eq!(vec![1, 0], strict_doms);
322
323        assert_eq!(
324            None::<()>,
325            doms.strict_dominators(99).map(|_| unreachable!())
326        );
327
328        let dom_by: Vec<_> = doms.immediately_dominated_by(1).collect();
329        assert_eq!(vec![2], dom_by);
330        assert_eq!(None, doms.immediately_dominated_by(99).next());
331        assert_eq!(1, doms.immediately_dominated_by(0).count());
332    }
333}