cranelift_codegen/dominator_tree/
simple.rs

1//! A Dominator Tree represented as mappings of Blocks to their immediate dominator.
2//! Computed using Keith D. Cooper's "Simple, Fast Dominator Algorithm."   
3//! This version have been used in Cranelift for a very long time
4//! and should be quite stable. Used as a baseline i.e. in verification.
5
6use crate::entity::SecondaryMap;
7use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
8use crate::ir::{Block, Function, Layout, ProgramPoint};
9use crate::packed_option::PackedOption;
10use crate::timing;
11use crate::traversals::Dfs;
12use alloc::vec::Vec;
13use core::cmp::Ordering;
14
15/// RPO numbers are not first assigned in a contiguous way but as multiples of STRIDE, to leave
16/// room for modifications of the dominator tree.
17const STRIDE: u32 = 4;
18
19/// Dominator tree node. We keep one of these per block.
20#[derive(Clone, Default)]
21struct DomNode {
22    /// Number of this node in a reverse post-order traversal of the CFG, starting from 1.
23    /// This number is monotonic in the reverse postorder but not contiguous, since we leave
24    /// holes for later localized modifications of the dominator tree.
25    /// Unreachable nodes get number 0, all others are positive.
26    rpo_number: u32,
27
28    /// The immediate dominator of this block.
29    ///
30    /// This is `None` for unreachable blocks and the entry block which doesn't have an immediate
31    /// dominator.
32    idom: PackedOption<Block>,
33}
34
35/// The dominator tree for a single function.
36pub struct SimpleDominatorTree {
37    nodes: SecondaryMap<Block, DomNode>,
38
39    /// CFG post-order of all reachable blocks.
40    postorder: Vec<Block>,
41
42    /// Scratch traversal state used by `compute_postorder()`.
43    dfs: Dfs,
44
45    valid: bool,
46}
47
48/// Methods for querying the dominator tree.
49impl SimpleDominatorTree {
50    /// Is `block` reachable from the entry block?
51    pub fn is_reachable(&self, block: Block) -> bool {
52        self.nodes[block].rpo_number != 0
53    }
54
55    /// Get the CFG post-order of blocks that was used to compute the dominator tree.
56    ///
57    /// Note that this post-order is not updated automatically when the CFG is modified. It is
58    /// computed from scratch and cached by `compute()`.
59    pub fn cfg_postorder(&self) -> &[Block] {
60        debug_assert!(self.is_valid());
61        &self.postorder
62    }
63
64    /// Returns the immediate dominator of `block`.
65    ///
66    /// `block_a` is said to *dominate* `block_b` if all control flow paths from the function
67    /// entry to `block_b` must go through `block_a`.
68    ///
69    /// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
70    /// also dominate the immediate dominator.
71    ///
72    /// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
73    /// which has no dominators.
74    pub fn idom(&self, block: Block) -> Option<Block> {
75        self.nodes[block].idom.into()
76    }
77
78    /// Compare two blocks relative to the reverse post-order.
79    pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {
80        self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
81    }
82
83    /// Compare two program points relative to a reverse post-order traversal of the control-flow
84    /// graph.
85    ///
86    /// Return `Ordering::Less` if `a` comes before `b` in the RPO.
87    ///
88    /// If `a` and `b` belong to the same block, compare their relative position in the block.
89    pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
90    where
91        A: Into<ProgramPoint>,
92        B: Into<ProgramPoint>,
93    {
94        let a = a.into();
95        let b = b.into();
96        self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))
97            .then_with(|| layout.pp_cmp(a, b))
98    }
99
100    /// Returns `true` if `a` dominates `b`.
101    ///
102    /// This means that every control-flow path from the function entry to `b` must go through `a`.
103    ///
104    /// Dominance is ill defined for unreachable blocks. This function can always determine
105    /// dominance for instructions in the same block, but otherwise returns `false` if either block
106    /// is unreachable.
107    ///
108    /// An instruction is considered to dominate itself.
109    /// A block is also considered to dominate itself.
110    pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
111    where
112        A: Into<ProgramPoint>,
113        B: Into<ProgramPoint>,
114    {
115        let a = a.into();
116        let b = b.into();
117        match a {
118            ProgramPoint::Block(block_a) => match b {
119                ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
120                ProgramPoint::Inst(inst_b) => {
121                    let block_b = layout
122                        .inst_block(inst_b)
123                        .expect("Instruction not in layout.");
124                    self.block_dominates(block_a, block_b)
125                }
126            },
127            ProgramPoint::Inst(inst_a) => {
128                let block_a: Block = layout
129                    .inst_block(inst_a)
130                    .expect("Instruction not in layout.");
131                match b {
132                    ProgramPoint::Block(block_b) => {
133                        block_a != block_b && self.block_dominates(block_a, block_b)
134                    }
135                    ProgramPoint::Inst(inst_b) => {
136                        let block_b = layout
137                            .inst_block(inst_b)
138                            .expect("Instruction not in layout.");
139                        if block_a == block_b {
140                            layout.pp_cmp(a, b) != Ordering::Greater
141                        } else {
142                            self.block_dominates(block_a, block_b)
143                        }
144                    }
145                }
146            }
147        }
148    }
149
150    /// Returns `true` if `block_a` dominates `block_b`.
151    ///
152    /// A block is considered to dominate itself.
153    fn block_dominates(&self, block_a: Block, mut block_b: Block) -> bool {
154        let rpo_a = self.nodes[block_a].rpo_number;
155
156        // Run a finger up the dominator tree from b until we see a.
157        // Do nothing if b is unreachable.
158        while rpo_a < self.nodes[block_b].rpo_number {
159            let idom = match self.idom(block_b) {
160                Some(idom) => idom,
161                None => return false, // a is unreachable, so we climbed past the entry
162            };
163            block_b = idom;
164        }
165
166        block_a == block_b
167    }
168
169    /// Compute the common dominator of two basic blocks.
170    ///
171    /// Both basic blocks are assumed to be reachable.
172    fn common_dominator(&self, mut a: Block, mut b: Block) -> Block {
173        loop {
174            match self.rpo_cmp_block(a, b) {
175                Ordering::Less => {
176                    // `a` comes before `b` in the RPO. Move `b` up.
177                    let idom = self.nodes[b].idom.expect("Unreachable basic block?");
178                    b = idom;
179                }
180                Ordering::Greater => {
181                    // `b` comes before `a` in the RPO. Move `a` up.
182                    let idom = self.nodes[a].idom.expect("Unreachable basic block?");
183                    a = idom;
184                }
185                Ordering::Equal => break,
186            }
187        }
188
189        debug_assert_eq!(a, b, "Unreachable block passed to common_dominator?");
190
191        a
192    }
193}
194
195impl SimpleDominatorTree {
196    /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a
197    /// function.
198    pub fn new() -> Self {
199        Self {
200            nodes: SecondaryMap::new(),
201            postorder: Vec::new(),
202            dfs: Dfs::new(),
203            valid: false,
204        }
205    }
206
207    /// Allocate and compute a dominator tree.
208    pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
209        let block_capacity = func.layout.block_capacity();
210        let mut domtree = Self {
211            nodes: SecondaryMap::with_capacity(block_capacity),
212            postorder: Vec::with_capacity(block_capacity),
213            dfs: Dfs::new(),
214            valid: false,
215        };
216        domtree.compute(func, cfg);
217        domtree
218    }
219
220    /// Reset and compute a CFG post-order and dominator tree.
221    pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
222        let _tt = timing::domtree();
223        debug_assert!(cfg.is_valid());
224        self.compute_postorder(func);
225        self.compute_domtree(func, cfg);
226        self.valid = true;
227    }
228
229    /// Clear the data structures used to represent the dominator tree. This will leave the tree in
230    /// a state where `is_valid()` returns false.
231    pub fn clear(&mut self) {
232        self.nodes.clear();
233        self.postorder.clear();
234        self.valid = false;
235    }
236
237    /// Check if the dominator tree is in a valid state.
238    ///
239    /// Note that this doesn't perform any kind of validity checks. It simply checks if the
240    /// `compute()` method has been called since the last `clear()`. It does not check that the
241    /// dominator tree is consistent with the CFG.
242    pub fn is_valid(&self) -> bool {
243        self.valid
244    }
245
246    /// Reset all internal data structures and compute a post-order of the control flow graph.
247    ///
248    /// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones.
249    fn compute_postorder(&mut self, func: &Function) {
250        self.clear();
251        self.nodes.resize(func.dfg.num_blocks());
252        self.postorder.extend(self.dfs.post_order_iter(func));
253    }
254
255    /// Build a dominator tree from a control flow graph using Keith D. Cooper's
256    /// "Simple, Fast Dominator Algorithm."
257    fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {
258        // During this algorithm, `rpo_number` has the following values:
259        //
260        // 0: block is not reachable.
261        // 1: block is reachable, but has not yet been visited during the first pass. This is set by
262        // `compute_postorder`.
263        // 2+: block is reachable and has an assigned RPO number.
264
265        // We'll be iterating over a reverse post-order of the CFG, skipping the entry block.
266        let (entry_block, postorder) = match self.postorder.as_slice().split_last() {
267            Some((&eb, rest)) => (eb, rest),
268            None => return,
269        };
270        debug_assert_eq!(Some(entry_block), func.layout.entry_block());
271
272        // Do a first pass where we assign RPO numbers to all reachable nodes.
273        self.nodes[entry_block].rpo_number = 2 * STRIDE;
274        for (rpo_idx, &block) in postorder.iter().rev().enumerate() {
275            // Update the current node and give it an RPO number.
276            // The entry block got 2, the rest start at 3 by multiples of STRIDE to leave
277            // room for future dominator tree modifications.
278            //
279            // Since `compute_idom` will only look at nodes with an assigned RPO number, the
280            // function will never see an uninitialized predecessor.
281            //
282            // Due to the nature of the post-order traversal, every node we visit will have at
283            // least one predecessor that has previously been visited during this RPO.
284            self.nodes[block] = DomNode {
285                idom: self.compute_idom(block, cfg).into(),
286                rpo_number: (rpo_idx as u32 + 3) * STRIDE,
287            }
288        }
289
290        // Now that we have RPO numbers for everything and initial immediate dominator estimates,
291        // iterate until convergence.
292        //
293        // If the function is free of irreducible control flow, this will exit after one iteration.
294        let mut changed = true;
295        while changed {
296            changed = false;
297            for &block in postorder.iter().rev() {
298                let idom = self.compute_idom(block, cfg).into();
299                if self.nodes[block].idom != idom {
300                    self.nodes[block].idom = idom;
301                    changed = true;
302                }
303            }
304        }
305    }
306
307    // Compute the immediate dominator for `block` using the current `idom` states for the reachable
308    // nodes.
309    fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph) -> Block {
310        // Get an iterator with just the reachable, already visited predecessors to `block`.
311        // Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
312        // been visited yet, 0 for unreachable blocks.
313        let mut reachable_preds = cfg
314            .pred_iter(block)
315            .filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1)
316            .map(|pred| pred.block);
317
318        // The RPO must visit at least one predecessor before this node.
319        let mut idom = reachable_preds
320            .next()
321            .expect("block node must have one reachable predecessor");
322
323        for pred in reachable_preds {
324            idom = self.common_dominator(idom, pred);
325        }
326
327        idom
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334    use crate::cursor::{Cursor, FuncCursor};
335    use crate::ir::types::*;
336    use crate::ir::{InstBuilder, TrapCode};
337
338    #[test]
339    fn empty() {
340        let func = Function::new();
341        let cfg = ControlFlowGraph::with_function(&func);
342        debug_assert!(cfg.is_valid());
343        let dtree = SimpleDominatorTree::with_function(&func, &cfg);
344        assert_eq!(0, dtree.nodes.keys().count());
345        assert_eq!(dtree.cfg_postorder(), &[]);
346    }
347
348    #[test]
349    fn unreachable_node() {
350        let mut func = Function::new();
351        let block0 = func.dfg.make_block();
352        let v0 = func.dfg.append_block_param(block0, I32);
353        let block1 = func.dfg.make_block();
354        let block2 = func.dfg.make_block();
355        let trap_block = func.dfg.make_block();
356
357        let mut cur = FuncCursor::new(&mut func);
358
359        cur.insert_block(block0);
360        cur.ins().brif(v0, block2, &[], trap_block, &[]);
361
362        cur.insert_block(trap_block);
363        cur.ins().trap(TrapCode::unwrap_user(1));
364
365        cur.insert_block(block1);
366        let v1 = cur.ins().iconst(I32, 1);
367        let v2 = cur.ins().iadd(v0, v1);
368        cur.ins().jump(block0, &[v2]);
369
370        cur.insert_block(block2);
371        cur.ins().return_(&[v0]);
372
373        let cfg = ControlFlowGraph::with_function(cur.func);
374        let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
375
376        // Fall-through-first, prune-at-source DFT:
377        //
378        // block0 {
379        //   brif block2 {
380        //     trap
381        //     block2 {
382        //       return
383        //     } block2
384        // } block0
385        assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
386
387        let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
388        assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
389        assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
390
391        assert!(dt.dominates(block0, block0, &cur.func.layout));
392        assert!(!dt.dominates(block0, block1, &cur.func.layout));
393        assert!(dt.dominates(block0, block2, &cur.func.layout));
394        assert!(!dt.dominates(block1, block0, &cur.func.layout));
395        assert!(dt.dominates(block1, block1, &cur.func.layout));
396        assert!(!dt.dominates(block1, block2, &cur.func.layout));
397        assert!(!dt.dominates(block2, block0, &cur.func.layout));
398        assert!(!dt.dominates(block2, block1, &cur.func.layout));
399        assert!(dt.dominates(block2, block2, &cur.func.layout));
400    }
401
402    #[test]
403    fn non_zero_entry_block() {
404        let mut func = Function::new();
405        let block0 = func.dfg.make_block();
406        let block1 = func.dfg.make_block();
407        let block2 = func.dfg.make_block();
408        let block3 = func.dfg.make_block();
409        let cond = func.dfg.append_block_param(block3, I32);
410
411        let mut cur = FuncCursor::new(&mut func);
412
413        cur.insert_block(block3);
414        let jmp_block3_block1 = cur.ins().jump(block1, &[]);
415
416        cur.insert_block(block1);
417        let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
418
419        cur.insert_block(block2);
420        cur.ins().jump(block0, &[]);
421
422        cur.insert_block(block0);
423
424        let cfg = ControlFlowGraph::with_function(cur.func);
425        let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
426
427        // Fall-through-first, prune-at-source DFT:
428        //
429        // block3 {
430        //   block3:jump block1 {
431        //     block1 {
432        //       block1:brif block0 {
433        //         block1:jump block2 {
434        //           block2 {
435        //             block2:jump block0 (seen)
436        //           } block2
437        //         } block1:jump block2
438        //         block0 {
439        //         } block0
440        //       } block1:brif block0
441        //     } block1
442        //   } block3:jump block1
443        // } block3
444
445        assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
446
447        assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
448        assert_eq!(dt.idom(block3), None);
449        assert_eq!(dt.idom(block1).unwrap(), block3);
450        assert_eq!(dt.idom(block2).unwrap(), block1);
451        assert_eq!(dt.idom(block0).unwrap(), block1);
452
453        assert!(dt.dominates(
454            br_block1_block0_block2,
455            br_block1_block0_block2,
456            &cur.func.layout
457        ));
458        assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
459        assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
460
461        assert_eq!(
462            dt.rpo_cmp(block3, block3, &cur.func.layout),
463            Ordering::Equal
464        );
465        assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);
466        assert_eq!(
467            dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),
468            Ordering::Less
469        );
470        assert_eq!(
471            dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),
472            Ordering::Less
473        );
474    }
475
476    #[test]
477    fn backwards_layout() {
478        let mut func = Function::new();
479        let block0 = func.dfg.make_block();
480        let block1 = func.dfg.make_block();
481        let block2 = func.dfg.make_block();
482
483        let mut cur = FuncCursor::new(&mut func);
484
485        cur.insert_block(block0);
486        let jmp02 = cur.ins().jump(block2, &[]);
487
488        cur.insert_block(block1);
489        let trap = cur.ins().trap(TrapCode::unwrap_user(5));
490
491        cur.insert_block(block2);
492        let jmp21 = cur.ins().jump(block1, &[]);
493
494        let cfg = ControlFlowGraph::with_function(cur.func);
495        let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
496
497        assert_eq!(cur.func.layout.entry_block(), Some(block0));
498        assert_eq!(dt.idom(block0), None);
499        assert_eq!(dt.idom(block1), Some(block2));
500        assert_eq!(dt.idom(block2), Some(block0));
501
502        assert!(dt.dominates(block0, block0, &cur.func.layout));
503        assert!(dt.dominates(block0, jmp02, &cur.func.layout));
504        assert!(dt.dominates(block0, block1, &cur.func.layout));
505        assert!(dt.dominates(block0, trap, &cur.func.layout));
506        assert!(dt.dominates(block0, block2, &cur.func.layout));
507        assert!(dt.dominates(block0, jmp21, &cur.func.layout));
508
509        assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
510        assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
511        assert!(dt.dominates(jmp02, block1, &cur.func.layout));
512        assert!(dt.dominates(jmp02, trap, &cur.func.layout));
513        assert!(dt.dominates(jmp02, block2, &cur.func.layout));
514        assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
515
516        assert!(!dt.dominates(block1, block0, &cur.func.layout));
517        assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
518        assert!(dt.dominates(block1, block1, &cur.func.layout));
519        assert!(dt.dominates(block1, trap, &cur.func.layout));
520        assert!(!dt.dominates(block1, block2, &cur.func.layout));
521        assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
522
523        assert!(!dt.dominates(trap, block0, &cur.func.layout));
524        assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
525        assert!(!dt.dominates(trap, block1, &cur.func.layout));
526        assert!(dt.dominates(trap, trap, &cur.func.layout));
527        assert!(!dt.dominates(trap, block2, &cur.func.layout));
528        assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
529
530        assert!(!dt.dominates(block2, block0, &cur.func.layout));
531        assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
532        assert!(dt.dominates(block2, block1, &cur.func.layout));
533        assert!(dt.dominates(block2, trap, &cur.func.layout));
534        assert!(dt.dominates(block2, block2, &cur.func.layout));
535        assert!(dt.dominates(block2, jmp21, &cur.func.layout));
536
537        assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
538        assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
539        assert!(dt.dominates(jmp21, block1, &cur.func.layout));
540        assert!(dt.dominates(jmp21, trap, &cur.func.layout));
541        assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
542        assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
543    }
544
545    #[test]
546    fn insts_same_block() {
547        let mut func = Function::new();
548        let block0 = func.dfg.make_block();
549
550        let mut cur = FuncCursor::new(&mut func);
551
552        cur.insert_block(block0);
553        let v1 = cur.ins().iconst(I32, 1);
554        let v2 = cur.ins().iadd(v1, v1);
555        let v3 = cur.ins().iadd(v2, v2);
556        cur.ins().return_(&[]);
557
558        let cfg = ControlFlowGraph::with_function(cur.func);
559        let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
560
561        let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
562        let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
563        let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
564
565        assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
566        assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
567        assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
568
569        assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
570        assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
571        assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
572
573        assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
574        assert!(dt.dominates(block0, block0, &cur.func.layout));
575
576        assert!(dt.dominates(block0, v1_def, &cur.func.layout));
577        assert!(dt.dominates(block0, v2_def, &cur.func.layout));
578        assert!(dt.dominates(block0, v3_def, &cur.func.layout));
579
580        assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
581        assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
582        assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
583    }
584}