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