Skip to main content

cranelift_codegen/
dominator_tree.rs

1//! A Dominator Tree represented as mappings of Blocks to their immediate dominator.
2
3use crate::entity::SecondaryMap;
4use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
5use crate::ir::{Block, Function, Layout, ProgramPoint};
6use crate::packed_option::PackedOption;
7use crate::timing;
8use alloc::vec::Vec;
9use core::cmp;
10use core::cmp::Ordering;
11use core::mem;
12
13mod simple;
14
15pub use simple::SimpleDominatorTree;
16
17/// Spanning tree node, used during domtree computation.
18#[derive(Clone, Default)]
19struct SpanningTreeNode {
20    /// This node's block in function CFG.
21    block: PackedOption<Block>,
22    /// Node's ancestor in the spanning tree.
23    /// Gets invalidated during semi-dominator computation.
24    ancestor: u32,
25    /// The smallest semi value discovered on any semi-dominator path
26    /// that went through the node up till the moment.
27    /// Gets updated in the course of semi-dominator computation.
28    label: u32,
29    /// Semidominator value for the node.
30    semi: u32,
31    /// Immediate dominator value for the node.
32    /// Initialized to node's ancestor in the spanning tree.
33    idom: u32,
34}
35
36/// DFS preorder number for unvisited nodes and the virtual root in the spanning tree.
37const NOT_VISITED: u32 = 0;
38
39/// Spanning tree, in CFG preorder.
40/// Node 0 is the virtual root and doesn't have a corresponding block.
41/// It's not required because function's CFG in Cranelift always have
42/// a singular root, but helps to avoid additional checks.
43/// Numbering nodes from 0 also follows the convention in
44/// `SimpleDominatorTree`.
45#[derive(Clone, Default)]
46struct SpanningTree {
47    nodes: Vec<SpanningTreeNode>,
48}
49
50impl SpanningTree {
51    fn new() -> Self {
52        // Include the virtual root.
53        Self {
54            nodes: vec![Default::default()],
55        }
56    }
57
58    fn with_capacity(capacity: usize) -> Self {
59        // Include the virtual root.
60        let mut nodes = Vec::with_capacity(capacity + 1);
61        nodes.push(Default::default());
62        Self { nodes }
63    }
64
65    fn len(&self) -> usize {
66        self.nodes.len()
67    }
68
69    fn reserve(&mut self, capacity: usize) {
70        // Virtual root should be already included.
71        self.nodes.reserve(capacity);
72    }
73
74    fn clear(&mut self) {
75        self.nodes.resize(1, Default::default());
76    }
77
78    /// Returns pre_number for the new node.
79    fn push(&mut self, ancestor: u32, block: Block) -> u32 {
80        // Virtual root should be already included.
81        debug_assert!(!self.nodes.is_empty());
82
83        let pre_number = self.nodes.len() as u32;
84
85        self.nodes.push(SpanningTreeNode {
86            block: block.into(),
87            ancestor,
88            label: pre_number,
89            semi: pre_number,
90            idom: ancestor,
91        });
92
93        pre_number
94    }
95}
96
97impl core::ops::Index<u32> for SpanningTree {
98    type Output = SpanningTreeNode;
99
100    fn index(&self, idx: u32) -> &Self::Output {
101        &self.nodes[idx as usize]
102    }
103}
104
105impl core::ops::IndexMut<u32> for SpanningTree {
106    fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
107        &mut self.nodes[idx as usize]
108    }
109}
110
111/// Traversal event to compute both preorder spanning tree
112/// and postorder block list. Can't use `Dfs` from traversals.rs
113/// here because of the need for parent links.
114enum TraversalEvent {
115    Enter(u32, Block),
116    Exit(Block),
117}
118
119/// Dominator tree node. We keep one of these per block.
120#[derive(Clone, Default)]
121struct DominatorTreeNode {
122    /// Immediate dominator for the block, `None` for unreachable blocks.
123    idom: PackedOption<Block>,
124    /// Preorder traversal number, zero for unreachable blocks.
125    pre_number: u32,
126
127    /// First child node in the domtree.
128    child: PackedOption<Block>,
129
130    /// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO
131    /// (i.e. decreasing CFG post-order number).
132    sibling: PackedOption<Block>,
133
134    /// Sequence number for this node in a pre-order traversal of the dominator tree.
135    /// Unreachable blocks have number 0, the entry block is 1.
136    dom_pre_number: u32,
137
138    /// Maximum `dom_pre_number` for the sub-tree of the dominator tree that is rooted at this node.
139    /// This is always >= `dom_pre_number`.
140    dom_pre_max: u32,
141}
142
143/// The dominator tree for a single function,
144/// computed using Semi-NCA algorithm.
145pub struct DominatorTree {
146    /// DFS spanning tree.
147    stree: SpanningTree,
148    /// List of CFG blocks in postorder.
149    postorder: Vec<Block>,
150    /// Dominator tree nodes.
151    nodes: SecondaryMap<Block, DominatorTreeNode>,
152
153    /// Stack for building the spanning tree.
154    dfs_worklist: Vec<TraversalEvent>,
155    /// Stack used for processing semidominator paths
156    /// in link-eval procedure.
157    eval_worklist: Vec<u32>,
158
159    valid: bool,
160}
161
162impl core::fmt::Debug for DominatorTree {
163    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164        if !self.is_valid() {
165            return f.write_str("DominatorTree { <invalid> }");
166        }
167
168        let mut s = f.debug_tuple("DominatorTree");
169
170        if let Some((mut root, _)) = self.nodes.iter().find(|n| n.1.pre_number != NOT_VISITED) {
171            loop {
172                if let Some(b) = self.idom(root)
173                    && b != root
174                {
175                    root = b;
176                } else {
177                    break;
178                }
179            }
180
181            fn fmt_block(domtree: &DominatorTree, block: Block) -> impl core::fmt::Debug {
182                core::fmt::from_fn(move |f| {
183                    let children = domtree
184                        .children(block)
185                        .map(|c| fmt_block(domtree, c))
186                        .collect::<Vec<_>>();
187                    let mut s = f.debug_tuple(&format!("{block}"));
188                    if !children.is_empty() {
189                        s.field(&children);
190                    }
191                    s.finish()
192                })
193            }
194
195            s.field(&fmt_block(self, root));
196        }
197
198        s.finish()
199    }
200}
201
202/// Methods for querying the dominator tree.
203impl DominatorTree {
204    /// Is `block` reachable from the entry block?
205    pub fn is_reachable(&self, block: Block) -> bool {
206        self.nodes[block].pre_number != NOT_VISITED
207    }
208
209    /// Get the CFG post-order of blocks that was used to compute the dominator tree.
210    ///
211    /// Note that this post-order is not updated automatically when the CFG is modified. It is
212    /// computed from scratch and cached by `compute()`.
213    pub fn cfg_postorder(&self) -> &[Block] {
214        debug_assert!(self.is_valid());
215        &self.postorder
216    }
217
218    /// Get an iterator over CFG reverse post-order of blocks used to compute the dominator tree.
219    ///
220    /// Note that the post-order is not updated automatically when the CFG is modified. It is
221    /// computed from scratch and cached by `compute()`.
222    pub fn cfg_rpo(&self) -> impl Iterator<Item = &Block> {
223        debug_assert!(self.is_valid());
224        self.postorder.iter().rev()
225    }
226
227    /// Returns the immediate dominator of `block`.
228    ///
229    /// `block_a` is said to *dominate* `block_b` if all control flow paths from the function
230    /// entry to `block_b` must go through `block_a`.
231    ///
232    /// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
233    /// also dominate the immediate dominator.
234    ///
235    /// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
236    /// which has no dominators.
237    pub fn idom(&self, block: Block) -> Option<Block> {
238        self.nodes[block].idom.into()
239    }
240
241    /// Returns `true` if `a` dominates `b`.
242    ///
243    /// This means that every control-flow path from the function entry to `b` must go through `a`.
244    ///
245    /// Dominance is ill defined for unreachable blocks. This function can always determine
246    /// dominance for instructions in the same block, but otherwise returns `false` if either block
247    /// is unreachable.
248    ///
249    /// An instruction is considered to dominate itself.
250    /// A block is also considered to dominate itself.
251    pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
252    where
253        A: Into<ProgramPoint>,
254        B: Into<ProgramPoint>,
255    {
256        let a = a.into();
257        let b = b.into();
258        match a {
259            ProgramPoint::Block(block_a) => match b {
260                ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
261                ProgramPoint::Inst(inst_b) => {
262                    let block_b = layout
263                        .inst_block(inst_b)
264                        .expect("Instruction not in layout.");
265                    self.block_dominates(block_a, block_b)
266                }
267            },
268            ProgramPoint::Inst(inst_a) => {
269                let block_a: Block = layout
270                    .inst_block(inst_a)
271                    .expect("Instruction not in layout.");
272                match b {
273                    ProgramPoint::Block(block_b) => {
274                        block_a != block_b && self.block_dominates(block_a, block_b)
275                    }
276                    ProgramPoint::Inst(inst_b) => {
277                        let block_b = layout
278                            .inst_block(inst_b)
279                            .expect("Instruction not in layout.");
280                        if block_a == block_b {
281                            layout.pp_cmp(a, b) != Ordering::Greater
282                        } else {
283                            self.block_dominates(block_a, block_b)
284                        }
285                    }
286                }
287            }
288        }
289    }
290
291    /// Returns `true` if `block_a` dominates `block_b`.
292    ///
293    /// A block is considered to dominate itself.
294    /// This uses preorder numbers for O(1) constant time performance.
295    pub fn block_dominates(&self, block_a: Block, block_b: Block) -> bool {
296        let na = &self.nodes[block_a];
297        let nb = &self.nodes[block_b];
298        na.dom_pre_number <= nb.dom_pre_number && na.dom_pre_max >= nb.dom_pre_max
299    }
300
301    /// Get an iterator over the direct children of `block` in the dominator tree.
302    ///
303    /// These are the blocks whose immediate dominator is `block`, ordered by
304    /// decreasing CFG post-order number.
305    pub fn children(&self, block: Block) -> ChildIter<'_> {
306        ChildIter {
307            domtree: self,
308            next: self.nodes[block].child,
309        }
310    }
311}
312
313impl DominatorTree {
314    /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a
315    /// function.
316    pub fn new() -> Self {
317        Self {
318            stree: SpanningTree::new(),
319            nodes: SecondaryMap::new(),
320            postorder: Vec::new(),
321            dfs_worklist: Vec::new(),
322            eval_worklist: Vec::new(),
323            valid: false,
324        }
325    }
326
327    /// Allocate and compute a dominator tree.
328    pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
329        let block_capacity = func.layout.block_capacity();
330        let mut domtree = Self {
331            stree: SpanningTree::with_capacity(block_capacity),
332            nodes: SecondaryMap::with_capacity(block_capacity),
333            postorder: Vec::with_capacity(block_capacity),
334            dfs_worklist: Vec::new(),
335            eval_worklist: Vec::new(),
336            valid: false,
337        };
338        domtree.compute(func, cfg);
339        domtree
340    }
341
342    /// Reset and compute a CFG post-order and dominator tree,
343    /// using Semi-NCA algorithm, described in the paper:
344    ///
345    /// Linear-Time Algorithms for Dominators and Related Problems.
346    /// Loukas Georgiadis, Princeton University, November 2005.
347    ///
348    /// The same algorithm is used by Julia, SpiderMonkey and LLVM,
349    /// the implementation is heavily inspired by them.
350    pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
351        let _tt = timing::domtree();
352        debug_assert!(cfg.is_valid());
353
354        self.clear();
355        self.compute_spanning_tree(func);
356        self.compute_domtree(cfg);
357        self.compute_domtree_preorder();
358
359        self.valid = true;
360    }
361
362    /// Clear the data structures used to represent the dominator tree. This will leave the tree in
363    /// a state where `is_valid()` returns false.
364    pub fn clear(&mut self) {
365        self.stree.clear();
366        self.nodes.clear();
367        self.postorder.clear();
368        self.valid = false;
369    }
370
371    /// Check if the dominator tree is in a valid state.
372    ///
373    /// Note that this doesn't perform any kind of validity checks. It simply checks if the
374    /// `compute()` method has been called since the last `clear()`. It does not check that the
375    /// dominator tree is consistent with the CFG.
376    pub fn is_valid(&self) -> bool {
377        self.valid
378    }
379
380    /// Reset all internal data structures, build spanning tree
381    /// and compute a post-order of the control flow graph.
382    fn compute_spanning_tree(&mut self, func: &Function) {
383        self.nodes.resize(func.dfg.num_blocks());
384        self.stree.reserve(func.dfg.num_blocks());
385
386        if let Some(block) = func.layout.entry_block() {
387            self.dfs_worklist.push(TraversalEvent::Enter(0, block));
388        }
389
390        loop {
391            match self.dfs_worklist.pop() {
392                Some(TraversalEvent::Enter(parent, block)) => {
393                    let node = &mut self.nodes[block];
394                    if node.pre_number != NOT_VISITED {
395                        continue;
396                    }
397
398                    self.dfs_worklist.push(TraversalEvent::Exit(block));
399
400                    let pre_number = self.stree.push(parent, block);
401                    node.pre_number = pre_number;
402
403                    // Use the same traversal heuristics as in traversals.rs.
404                    self.dfs_worklist.extend(
405                        func.block_successors(block)
406                            // Heuristic: chase the children in reverse. This puts
407                            // the first successor block first in the postorder, all
408                            // other things being equal, which tends to prioritize
409                            // loop backedges over out-edges, putting the edge-block
410                            // closer to the loop body and minimizing live-ranges in
411                            // linear instruction space. This heuristic doesn't have
412                            // any effect on the computation of dominators, and is
413                            // purely for other consumers of the postorder we cache
414                            // here.
415                            .rev()
416                            // A simple optimization: push less items to the stack.
417                            .filter(|successor| self.nodes[*successor].pre_number == NOT_VISITED)
418                            .map(|successor| TraversalEvent::Enter(pre_number, successor)),
419                    );
420                }
421                Some(TraversalEvent::Exit(block)) => {
422                    self.postorder.push(block);
423                }
424                None => break,
425            }
426        }
427    }
428
429    /// Eval-link procedure from the paper.
430    /// For a predecessor V of node W returns V if V < W, otherwise the minimum of sdom(U),
431    /// where U > W and U is on a semi-dominator path for W in CFG.
432    /// Use path compression to bring complexity down to O(m*log(n)).
433    fn eval(&mut self, v: u32, last_linked: u32) -> u32 {
434        if self.stree[v].ancestor < last_linked {
435            return self.stree[v].label;
436        }
437
438        // Follow semi-dominator path.
439        let mut root = v;
440        loop {
441            self.eval_worklist.push(root);
442            root = self.stree[root].ancestor;
443
444            if self.stree[root].ancestor < last_linked {
445                break;
446            }
447        }
448
449        let mut prev = root;
450        let root = self.stree[prev].ancestor;
451
452        // Perform path compression. Point all ancestors to the root
453        // and propagate minimal sdom(U) value from ancestors to children.
454        while let Some(curr) = self.eval_worklist.pop() {
455            if self.stree[prev].label < self.stree[curr].label {
456                self.stree[curr].label = self.stree[prev].label;
457            }
458
459            self.stree[curr].ancestor = root;
460            prev = curr;
461        }
462
463        self.stree[v].label
464    }
465
466    fn compute_domtree(&mut self, cfg: &ControlFlowGraph) {
467        // Compute semi-dominators.
468        for w in (1..self.stree.len() as u32).rev() {
469            let w_node = &mut self.stree[w];
470            let block = w_node.block.expect("Virtual root must have been excluded");
471            let mut semi = w_node.ancestor;
472
473            let last_linked = w + 1;
474
475            for pred in cfg
476                .pred_iter(block)
477                .map(|pred: BlockPredecessor| pred.block)
478            {
479                // Skip unreachable nodes.
480                if self.nodes[pred].pre_number == NOT_VISITED {
481                    continue;
482                }
483
484                let semi_candidate = self.eval(self.nodes[pred].pre_number, last_linked);
485                semi = core::cmp::min(semi, semi_candidate);
486            }
487
488            let w_node = &mut self.stree[w];
489            w_node.label = semi;
490            w_node.semi = semi;
491        }
492
493        // Compute immediate dominators.
494        for v in 1..self.stree.len() as u32 {
495            let semi = self.stree[v].semi;
496            let block = self.stree[v]
497                .block
498                .expect("Virtual root must have been excluded");
499            let mut idom = self.stree[v].idom;
500
501            while idom > semi {
502                idom = self.stree[idom].idom;
503            }
504
505            self.stree[v].idom = idom;
506
507            self.nodes[block].idom = self.stree[idom].block;
508        }
509    }
510
511    /// Compute dominator tree preorder information.
512    ///
513    /// This populates child/sibling links and preorder numbers for fast dominance checks.
514    fn compute_domtree_preorder(&mut self) {
515        // Populate the child and sibling links.
516        //
517        // By following the CFG post-order and pushing to the front of the lists, we make sure that
518        // sibling lists are ordered according to the CFG reverse post-order (i.e. decreasing CFG
519        // post-order number).
520        for &block in &self.postorder {
521            if let Some(idom) = self.idom(block) {
522                let sib = mem::replace(&mut self.nodes[idom].child, block.into());
523                self.nodes[block].sibling = sib;
524            } else {
525                // The only block without an immediate dominator is the entry.
526                self.dfs_worklist.push(TraversalEvent::Enter(0, block));
527            }
528        }
529
530        // Assign pre-order numbers from a DFS of the dominator tree.
531        debug_assert!(self.dfs_worklist.len() <= 1);
532        let mut n = 0;
533        while let Some(event) = self.dfs_worklist.pop() {
534            if let TraversalEvent::Enter(_, block) = event {
535                n += 1;
536                let node = &mut self.nodes[block];
537                node.dom_pre_number = n;
538                node.dom_pre_max = n;
539                if let Some(sibling) = node.sibling.expand() {
540                    self.dfs_worklist.push(TraversalEvent::Enter(0, sibling));
541                }
542                if let Some(child) = node.child.expand() {
543                    self.dfs_worklist.push(TraversalEvent::Enter(0, child));
544                }
545            }
546        }
547
548        // Propagate the `dom_pre_max` numbers up the tree.
549        // The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all
550        // its dominator tree children.
551        for &block in &self.postorder {
552            if let Some(idom) = self.idom(block) {
553                let pre_max = cmp::max(self.nodes[block].dom_pre_max, self.nodes[idom].dom_pre_max);
554                self.nodes[idom].dom_pre_max = pre_max;
555            }
556        }
557    }
558}
559
560/// An iterator that enumerates the direct children of a block in the dominator tree.
561pub struct ChildIter<'a> {
562    domtree: &'a DominatorTree,
563    next: PackedOption<Block>,
564}
565
566impl<'a> Iterator for ChildIter<'a> {
567    type Item = Block;
568
569    fn next(&mut self) -> Option<Block> {
570        let n = self.next.expand();
571        if let Some(block) = n {
572            self.next = self.domtree.nodes[block].sibling;
573        }
574        n
575    }
576}
577
578#[cfg(test)]
579mod tests {
580    use super::*;
581    use crate::cursor::{Cursor, FuncCursor};
582    use crate::ir::types::*;
583    use crate::ir::{InstBuilder, TrapCode};
584
585    #[test]
586    fn empty() {
587        let func = Function::new();
588        let cfg = ControlFlowGraph::with_function(&func);
589        debug_assert!(cfg.is_valid());
590        let dtree = DominatorTree::with_function(&func, &cfg);
591        assert_eq!(0, dtree.nodes.keys().count());
592        assert_eq!(dtree.cfg_postorder(), &[]);
593    }
594
595    #[test]
596    fn unreachable_node() {
597        let mut func = Function::new();
598        let block0 = func.dfg.make_block();
599        let v0 = func.dfg.append_block_param(block0, I32);
600        let block1 = func.dfg.make_block();
601        let block2 = func.dfg.make_block();
602        let trap_block = func.dfg.make_block();
603
604        let mut cur = FuncCursor::new(&mut func);
605
606        cur.insert_block(block0);
607        cur.ins().brif(v0, block2, &[], trap_block, &[]);
608
609        cur.insert_block(trap_block);
610        cur.ins().trap(TrapCode::unwrap_user(1));
611
612        cur.insert_block(block1);
613        let v1 = cur.ins().iconst(I32, 1);
614        let v2 = cur.ins().iadd(v0, v1);
615        cur.ins().jump(block0, &[v2.into()]);
616
617        cur.insert_block(block2);
618        cur.ins().return_(&[v0]);
619
620        let cfg = ControlFlowGraph::with_function(cur.func);
621        let dt = DominatorTree::with_function(cur.func, &cfg);
622
623        // Fall-through-first, prune-at-source DFT:
624        //
625        // block0 {
626        //   brif block2 {
627        //     trap
628        //     block2 {
629        //       return
630        //     } block2
631        // } block0
632        assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
633
634        let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
635        assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
636        assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
637
638        assert!(dt.block_dominates(block0, block0));
639        assert!(!dt.block_dominates(block0, block1));
640        assert!(dt.block_dominates(block0, block2));
641        assert!(!dt.block_dominates(block1, block0));
642        assert!(dt.block_dominates(block1, block1));
643        assert!(!dt.block_dominates(block1, block2));
644        assert!(!dt.block_dominates(block2, block0));
645        assert!(!dt.block_dominates(block2, block1));
646        assert!(dt.block_dominates(block2, block2));
647    }
648
649    #[test]
650    fn non_zero_entry_block() {
651        let mut func = Function::new();
652        let block0 = func.dfg.make_block();
653        let block1 = func.dfg.make_block();
654        let block2 = func.dfg.make_block();
655        let block3 = func.dfg.make_block();
656        let cond = func.dfg.append_block_param(block3, I32);
657
658        let mut cur = FuncCursor::new(&mut func);
659
660        cur.insert_block(block3);
661        let jmp_block3_block1 = cur.ins().jump(block1, &[]);
662
663        cur.insert_block(block1);
664        let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
665
666        cur.insert_block(block2);
667        cur.ins().jump(block0, &[]);
668
669        cur.insert_block(block0);
670
671        let cfg = ControlFlowGraph::with_function(cur.func);
672        let dt = DominatorTree::with_function(cur.func, &cfg);
673
674        // Fall-through-first, prune-at-source DFT:
675        //
676        // block3 {
677        //   block3:jump block1 {
678        //     block1 {
679        //       block1:brif block0 {
680        //         block1:jump block2 {
681        //           block2 {
682        //             block2:jump block0 (seen)
683        //           } block2
684        //         } block1:jump block2
685        //         block0 {
686        //         } block0
687        //       } block1:brif block0
688        //     } block1
689        //   } block3:jump block1
690        // } block3
691
692        assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
693
694        assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
695        assert_eq!(dt.idom(block3), None);
696        assert_eq!(dt.idom(block1).unwrap(), block3);
697        assert_eq!(dt.idom(block2).unwrap(), block1);
698        assert_eq!(dt.idom(block0).unwrap(), block1);
699
700        assert!(dt.dominates(
701            br_block1_block0_block2,
702            br_block1_block0_block2,
703            &cur.func.layout
704        ));
705        assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
706        assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
707    }
708
709    #[test]
710    fn backwards_layout() {
711        let mut func = Function::new();
712        let block0 = func.dfg.make_block();
713        let block1 = func.dfg.make_block();
714        let block2 = func.dfg.make_block();
715
716        let mut cur = FuncCursor::new(&mut func);
717
718        cur.insert_block(block0);
719        let jmp02 = cur.ins().jump(block2, &[]);
720
721        cur.insert_block(block1);
722        let trap = cur.ins().trap(TrapCode::unwrap_user(5));
723
724        cur.insert_block(block2);
725        let jmp21 = cur.ins().jump(block1, &[]);
726
727        let cfg = ControlFlowGraph::with_function(cur.func);
728        let dt = DominatorTree::with_function(cur.func, &cfg);
729
730        assert_eq!(cur.func.layout.entry_block(), Some(block0));
731        assert_eq!(dt.idom(block0), None);
732        assert_eq!(dt.idom(block1), Some(block2));
733        assert_eq!(dt.idom(block2), Some(block0));
734
735        assert!(dt.dominates(block0, block0, &cur.func.layout));
736        assert!(dt.dominates(block0, jmp02, &cur.func.layout));
737        assert!(dt.dominates(block0, block1, &cur.func.layout));
738        assert!(dt.dominates(block0, trap, &cur.func.layout));
739        assert!(dt.dominates(block0, block2, &cur.func.layout));
740        assert!(dt.dominates(block0, jmp21, &cur.func.layout));
741
742        assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
743        assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
744        assert!(dt.dominates(jmp02, block1, &cur.func.layout));
745        assert!(dt.dominates(jmp02, trap, &cur.func.layout));
746        assert!(dt.dominates(jmp02, block2, &cur.func.layout));
747        assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
748
749        assert!(!dt.dominates(block1, block0, &cur.func.layout));
750        assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
751        assert!(dt.dominates(block1, block1, &cur.func.layout));
752        assert!(dt.dominates(block1, trap, &cur.func.layout));
753        assert!(!dt.dominates(block1, block2, &cur.func.layout));
754        assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
755
756        assert!(!dt.dominates(trap, block0, &cur.func.layout));
757        assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
758        assert!(!dt.dominates(trap, block1, &cur.func.layout));
759        assert!(dt.dominates(trap, trap, &cur.func.layout));
760        assert!(!dt.dominates(trap, block2, &cur.func.layout));
761        assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
762
763        assert!(!dt.dominates(block2, block0, &cur.func.layout));
764        assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
765        assert!(dt.dominates(block2, block1, &cur.func.layout));
766        assert!(dt.dominates(block2, trap, &cur.func.layout));
767        assert!(dt.dominates(block2, block2, &cur.func.layout));
768        assert!(dt.dominates(block2, jmp21, &cur.func.layout));
769
770        assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
771        assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
772        assert!(dt.dominates(jmp21, block1, &cur.func.layout));
773        assert!(dt.dominates(jmp21, trap, &cur.func.layout));
774        assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
775        assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
776    }
777
778    #[test]
779    fn insts_same_block() {
780        let mut func = Function::new();
781        let block0 = func.dfg.make_block();
782
783        let mut cur = FuncCursor::new(&mut func);
784
785        cur.insert_block(block0);
786        let v1 = cur.ins().iconst(I32, 1);
787        let v2 = cur.ins().iadd(v1, v1);
788        let v3 = cur.ins().iadd(v2, v2);
789        cur.ins().return_(&[]);
790
791        let cfg = ControlFlowGraph::with_function(cur.func);
792        let dt = DominatorTree::with_function(cur.func, &cfg);
793
794        let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
795        let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
796        let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
797
798        assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
799        assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
800        assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
801
802        assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
803        assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
804        assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
805
806        assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
807        assert!(dt.dominates(block0, block0, &cur.func.layout));
808
809        assert!(dt.dominates(block0, v1_def, &cur.func.layout));
810        assert!(dt.dominates(block0, v2_def, &cur.func.layout));
811        assert!(dt.dominates(block0, v3_def, &cur.func.layout));
812
813        assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
814        assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
815        assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
816    }
817}