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, Inst, 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
13/// RPO numbers are not first assigned in a contiguous way but as multiples of STRIDE, to leave
14/// room for modifications of the dominator tree.
15const STRIDE: u32 = 4;
16
17/// Special RPO numbers used during `compute_postorder`.
18const SEEN: u32 = 1;
19
20/// Dominator tree node. We keep one of these per block.
21#[derive(Clone, Default)]
22struct DomNode {
23 /// Number of this node in a reverse post-order traversal of the CFG, starting from 1.
24 /// This number is monotonic in the reverse postorder but not contiguous, since we leave
25 /// holes for later localized modifications of the dominator tree.
26 /// Unreachable nodes get number 0, all others are positive.
27 rpo_number: u32,
28
29 /// The immediate dominator of this block, represented as the branch or jump instruction at the
30 /// end of the dominating basic block.
31 ///
32 /// This is `None` for unreachable blocks and the entry block which doesn't have an immediate
33 /// dominator.
34 idom: PackedOption<Inst>,
35}
36
37/// DFT stack state marker for computing the cfg postorder.
38enum Visit {
39 First,
40 Last,
41}
42
43/// The dominator tree for a single function.
44pub struct DominatorTree {
45 nodes: SecondaryMap<Block, DomNode>,
46
47 /// CFG post-order of all reachable blocks.
48 postorder: Vec<Block>,
49
50 /// Scratch memory used by `compute_postorder()`.
51 stack: Vec<(Visit, Block)>,
52
53 valid: bool,
54}
55
56/// Methods for querying the dominator tree.
57impl DominatorTree {
58 /// Is `block` reachable from the entry block?
59 pub fn is_reachable(&self, block: Block) -> bool {
60 self.nodes[block].rpo_number != 0
61 }
62
63 /// Get the CFG post-order of blocks that was used to compute the dominator tree.
64 ///
65 /// Note that this post-order is not updated automatically when the CFG is modified. It is
66 /// computed from scratch and cached by `compute()`.
67 pub fn cfg_postorder(&self) -> &[Block] {
68 debug_assert!(self.is_valid());
69 &self.postorder
70 }
71
72 /// Returns the immediate dominator of `block`.
73 ///
74 /// The immediate dominator of a basic block is a basic block which we represent by
75 /// the branch or jump instruction at the end of the basic block. This does not have to be the
76 /// terminator of its block.
77 ///
78 /// A branch or jump is said to *dominate* `block` if all control flow paths from the function
79 /// entry to `block` must go through the branch.
80 ///
81 /// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
82 /// also dominate the immediate dominator.
83 ///
84 /// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
85 /// which has no dominators.
86 pub fn idom(&self, block: Block) -> Option<Inst> {
87 self.nodes[block].idom.into()
88 }
89
90 /// Compare two blocks relative to the reverse post-order.
91 pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {
92 self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
93 }
94
95 /// Compare two program points relative to a reverse post-order traversal of the control-flow
96 /// graph.
97 ///
98 /// Return `Ordering::Less` if `a` comes before `b` in the RPO.
99 ///
100 /// If `a` and `b` belong to the same block, compare their relative position in the block.
101 pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
102 where
103 A: Into<ProgramPoint>,
104 B: Into<ProgramPoint>,
105 {
106 let a = a.into();
107 let b = b.into();
108 self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))
109 .then_with(|| layout.pp_cmp(a, b))
110 }
111
112 /// Returns `true` if `a` dominates `b`.
113 ///
114 /// This means that every control-flow path from the function entry to `b` must go through `a`.
115 ///
116 /// Dominance is ill defined for unreachable blocks. This function can always determine
117 /// dominance for instructions in the same block, but otherwise returns `false` if either block
118 /// is unreachable.
119 ///
120 /// An instruction is considered to dominate itself.
121 pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
122 where
123 A: Into<ProgramPoint>,
124 B: Into<ProgramPoint>,
125 {
126 let a = a.into();
127 let b = b.into();
128 match a {
129 ProgramPoint::Block(block_a) => {
130 a == b || self.last_dominator(block_a, b, layout).is_some()
131 }
132 ProgramPoint::Inst(inst_a) => {
133 let block_a = layout
134 .inst_block(inst_a)
135 .expect("Instruction not in layout.");
136 match self.last_dominator(block_a, b, layout) {
137 Some(last) => layout.pp_cmp(inst_a, last) != Ordering::Greater,
138 None => false,
139 }
140 }
141 }
142 }
143
144 /// Find the last instruction in `a` that dominates `b`.
145 /// If no instructions in `a` dominate `b`, return `None`.
146 pub fn last_dominator<B>(&self, a: Block, b: B, layout: &Layout) -> Option<Inst>
147 where
148 B: Into<ProgramPoint>,
149 {
150 let (mut block_b, mut inst_b) = match b.into() {
151 ProgramPoint::Block(block) => (block, None),
152 ProgramPoint::Inst(inst) => (
153 layout.inst_block(inst).expect("Instruction not in layout."),
154 Some(inst),
155 ),
156 };
157 let rpo_a = self.nodes[a].rpo_number;
158
159 // Run a finger up the dominator tree from b until we see a.
160 // Do nothing if b is unreachable.
161 while rpo_a < self.nodes[block_b].rpo_number {
162 let idom = match self.idom(block_b) {
163 Some(idom) => idom,
164 None => return None, // a is unreachable, so we climbed past the entry
165 };
166 block_b = layout.inst_block(idom).expect("Dominator got removed.");
167 inst_b = Some(idom);
168 }
169 if a == block_b {
170 inst_b
171 } else {
172 None
173 }
174 }
175
176 /// Compute the common dominator of two basic blocks.
177 ///
178 /// Both basic blocks are assumed to be reachable.
179 pub fn common_dominator(
180 &self,
181 mut a: BlockPredecessor,
182 mut b: BlockPredecessor,
183 layout: &Layout,
184 ) -> BlockPredecessor {
185 loop {
186 match self.rpo_cmp_block(a.block, b.block) {
187 Ordering::Less => {
188 // `a` comes before `b` in the RPO. Move `b` up.
189 let idom = self.nodes[b.block].idom.expect("Unreachable basic block?");
190 b = BlockPredecessor::new(
191 layout.inst_block(idom).expect("Dangling idom instruction"),
192 idom,
193 );
194 }
195 Ordering::Greater => {
196 // `b` comes before `a` in the RPO. Move `a` up.
197 let idom = self.nodes[a.block].idom.expect("Unreachable basic block?");
198 a = BlockPredecessor::new(
199 layout.inst_block(idom).expect("Dangling idom instruction"),
200 idom,
201 );
202 }
203 Ordering::Equal => break,
204 }
205 }
206
207 debug_assert_eq!(
208 a.block, b.block,
209 "Unreachable block passed to common_dominator?"
210 );
211
212 // We're in the same block. The common dominator is the earlier instruction.
213 if layout.pp_cmp(a.inst, b.inst) == Ordering::Less {
214 a
215 } else {
216 b
217 }
218 }
219}
220
221impl DominatorTree {
222 /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a
223 /// function.
224 pub fn new() -> Self {
225 Self {
226 nodes: SecondaryMap::new(),
227 postorder: Vec::new(),
228 stack: Vec::new(),
229 valid: false,
230 }
231 }
232
233 /// Allocate and compute a dominator tree.
234 pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
235 let block_capacity = func.layout.block_capacity();
236 let mut domtree = Self {
237 nodes: SecondaryMap::with_capacity(block_capacity),
238 postorder: Vec::with_capacity(block_capacity),
239 stack: Vec::new(),
240 valid: false,
241 };
242 domtree.compute(func, cfg);
243 domtree
244 }
245
246 /// Reset and compute a CFG post-order and dominator tree.
247 pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
248 let _tt = timing::domtree();
249 debug_assert!(cfg.is_valid());
250 self.compute_postorder(func);
251 self.compute_domtree(func, cfg);
252 self.valid = true;
253 }
254
255 /// Clear the data structures used to represent the dominator tree. This will leave the tree in
256 /// a state where `is_valid()` returns false.
257 pub fn clear(&mut self) {
258 self.nodes.clear();
259 self.postorder.clear();
260 debug_assert!(self.stack.is_empty());
261 self.valid = false;
262 }
263
264 /// Check if the dominator tree is in a valid state.
265 ///
266 /// Note that this doesn't perform any kind of validity checks. It simply checks if the
267 /// `compute()` method has been called since the last `clear()`. It does not check that the
268 /// dominator tree is consistent with the CFG.
269 pub fn is_valid(&self) -> bool {
270 self.valid
271 }
272
273 /// Reset all internal data structures and compute a post-order of the control flow graph.
274 ///
275 /// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones.
276 fn compute_postorder(&mut self, func: &Function) {
277 self.clear();
278 self.nodes.resize(func.dfg.num_blocks());
279
280 // This algorithm is a depth first traversal (DFT) of the control flow graph, computing a
281 // post-order of the blocks that are reachable form the entry block. A DFT post-order is not
282 // unique. The specific order we get is controlled by the order each node's children are
283 // visited.
284 //
285 // We view the CFG as a graph where each `BlockCall` value of a terminating branch
286 // instruction is an edge. A consequence of this is that we visit successor nodes in the
287 // reverse order specified by the branch instruction that terminates the basic block.
288 // (Reversed because we are using a stack to control traversal, and push the successors in
289 // the order the branch instruction specifies -- there's no good reason for this particular
290 // order.)
291 //
292 // During this algorithm only, use `rpo_number` to hold the following state:
293 //
294 // 0: block has not yet had its first visit
295 // SEEN: block has been visited at least once, implying that all of its successors are on
296 // the stack
297
298 match func.layout.entry_block() {
299 Some(block) => {
300 self.stack.push((Visit::First, block));
301 }
302 None => return,
303 }
304
305 while let Some((visit, block)) = self.stack.pop() {
306 match visit {
307 Visit::First => {
308 if self.nodes[block].rpo_number == 0 {
309 // This is the first time we pop the block, so we need to scan its
310 // successors and then revisit it.
311 self.nodes[block].rpo_number = SEEN;
312 self.stack.push((Visit::Last, block));
313 if let Some(inst) = func.stencil.layout.last_inst(block) {
314 // Heuristic: chase the children in reverse. This puts the first
315 // successor block first in the postorder, all other things being
316 // equal, which tends to prioritize loop backedges over out-edges,
317 // putting the edge-block closer to the loop body and minimizing
318 // live-ranges in linear instruction space. This heuristic doesn't have
319 // any effect on the computation of dominators, and is purely for other
320 // consumers of the postorder we cache here.
321 for block in func.stencil.dfg.insts[inst]
322 .branch_destination(&func.stencil.dfg.jump_tables)
323 .iter()
324 .rev()
325 {
326 let succ = block.block(&func.stencil.dfg.value_lists);
327
328 // This is purely an optimization to avoid additional iterations of
329 // the loop, and is not required; it's merely inlining the check
330 // from the outer conditional of this case to avoid the extra loop
331 // iteration.
332 if self.nodes[succ].rpo_number == 0 {
333 self.stack.push((Visit::First, succ))
334 }
335 }
336 }
337 }
338 }
339
340 Visit::Last => {
341 // We've finished all this node's successors.
342 self.postorder.push(block);
343 }
344 }
345 }
346 }
347
348 /// Build a dominator tree from a control flow graph using Keith D. Cooper's
349 /// "Simple, Fast Dominator Algorithm."
350 fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {
351 // During this algorithm, `rpo_number` has the following values:
352 //
353 // 0: block is not reachable.
354 // 1: block is reachable, but has not yet been visited during the first pass. This is set by
355 // `compute_postorder`.
356 // 2+: block is reachable and has an assigned RPO number.
357
358 // We'll be iterating over a reverse post-order of the CFG, skipping the entry block.
359 let (entry_block, postorder) = match self.postorder.as_slice().split_last() {
360 Some((&eb, rest)) => (eb, rest),
361 None => return,
362 };
363 debug_assert_eq!(Some(entry_block), func.layout.entry_block());
364
365 // Do a first pass where we assign RPO numbers to all reachable nodes.
366 self.nodes[entry_block].rpo_number = 2 * STRIDE;
367 for (rpo_idx, &block) in postorder.iter().rev().enumerate() {
368 // Update the current node and give it an RPO number.
369 // The entry block got 2, the rest start at 3 by multiples of STRIDE to leave
370 // room for future dominator tree modifications.
371 //
372 // Since `compute_idom` will only look at nodes with an assigned RPO number, the
373 // function will never see an uninitialized predecessor.
374 //
375 // Due to the nature of the post-order traversal, every node we visit will have at
376 // least one predecessor that has previously been visited during this RPO.
377 self.nodes[block] = DomNode {
378 idom: self.compute_idom(block, cfg, &func.layout).into(),
379 rpo_number: (rpo_idx as u32 + 3) * STRIDE,
380 }
381 }
382
383 // Now that we have RPO numbers for everything and initial immediate dominator estimates,
384 // iterate until convergence.
385 //
386 // If the function is free of irreducible control flow, this will exit after one iteration.
387 let mut changed = true;
388 while changed {
389 changed = false;
390 for &block in postorder.iter().rev() {
391 let idom = self.compute_idom(block, cfg, &func.layout).into();
392 if self.nodes[block].idom != idom {
393 self.nodes[block].idom = idom;
394 changed = true;
395 }
396 }
397 }
398 }
399
400 // Compute the immediate dominator for `block` using the current `idom` states for the reachable
401 // nodes.
402 fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph, layout: &Layout) -> Inst {
403 // Get an iterator with just the reachable, already visited predecessors to `block`.
404 // Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
405 // been visited yet, 0 for unreachable blocks.
406 let mut reachable_preds = cfg
407 .pred_iter(block)
408 .filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1);
409
410 // The RPO must visit at least one predecessor before this node.
411 let mut idom = reachable_preds
412 .next()
413 .expect("block node must have one reachable predecessor");
414
415 for pred in reachable_preds {
416 idom = self.common_dominator(idom, pred, layout);
417 }
418
419 idom.inst
420 }
421}
422
423/// Optional pre-order information that can be computed for a dominator tree.
424///
425/// This data structure is computed from a `DominatorTree` and provides:
426///
427/// - A forward traversable dominator tree through the `children()` iterator.
428/// - An ordering of blocks according to a dominator tree pre-order.
429/// - Constant time dominance checks at the block granularity.
430///
431/// The information in this auxiliary data structure is not easy to update when the control flow
432/// graph changes, which is why it is kept separate.
433pub struct DominatorTreePreorder {
434 nodes: SecondaryMap<Block, ExtraNode>,
435
436 // Scratch memory used by `compute_postorder()`.
437 stack: Vec<Block>,
438}
439
440#[derive(Default, Clone)]
441struct ExtraNode {
442 /// First child node in the domtree.
443 child: PackedOption<Block>,
444
445 /// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO.
446 sibling: PackedOption<Block>,
447
448 /// Sequence number for this node in a pre-order traversal of the dominator tree.
449 /// Unreachable blocks have number 0, the entry block is 1.
450 pre_number: u32,
451
452 /// Maximum `pre_number` for the sub-tree of the dominator tree that is rooted at this node.
453 /// This is always >= `pre_number`.
454 pre_max: u32,
455}
456
457/// Creating and computing the dominator tree pre-order.
458impl DominatorTreePreorder {
459 /// Create a new blank `DominatorTreePreorder`.
460 pub fn new() -> Self {
461 Self {
462 nodes: SecondaryMap::new(),
463 stack: Vec::new(),
464 }
465 }
466
467 /// Recompute this data structure to match `domtree`.
468 pub fn compute(&mut self, domtree: &DominatorTree, layout: &Layout) {
469 self.nodes.clear();
470 debug_assert_eq!(self.stack.len(), 0);
471
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 domtree.cfg_postorder() {
477 if let Some(idom_inst) = domtree.idom(block) {
478 let idom = layout
479 .inst_block(idom_inst)
480 .expect("Instruction not in layout.");
481 let sib = mem::replace(&mut self.nodes[idom].child, block.into());
482 self.nodes[block].sibling = sib;
483 } else {
484 // The only block without an immediate dominator is the entry.
485 self.stack.push(block);
486 }
487 }
488
489 // Step 2. Assign pre-order numbers from a DFS of the dominator tree.
490 debug_assert!(self.stack.len() <= 1);
491 let mut n = 0;
492 while let Some(block) = self.stack.pop() {
493 n += 1;
494 let node = &mut self.nodes[block];
495 node.pre_number = n;
496 node.pre_max = n;
497 if let Some(n) = node.sibling.expand() {
498 self.stack.push(n);
499 }
500 if let Some(n) = node.child.expand() {
501 self.stack.push(n);
502 }
503 }
504
505 // Step 3. Propagate the `pre_max` numbers up the tree.
506 // The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all
507 // its dominator tree children.
508 for &block in domtree.cfg_postorder() {
509 if let Some(idom_inst) = domtree.idom(block) {
510 let idom = layout
511 .inst_block(idom_inst)
512 .expect("Instruction not in layout.");
513 let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max);
514 self.nodes[idom].pre_max = pre_max;
515 }
516 }
517 }
518}
519
520/// An iterator that enumerates the direct children of a block in the dominator tree.
521pub struct ChildIter<'a> {
522 dtpo: &'a DominatorTreePreorder,
523 next: PackedOption<Block>,
524}
525
526impl<'a> Iterator for ChildIter<'a> {
527 type Item = Block;
528
529 fn next(&mut self) -> Option<Block> {
530 let n = self.next.expand();
531 if let Some(block) = n {
532 self.next = self.dtpo.nodes[block].sibling;
533 }
534 n
535 }
536}
537
538/// Query interface for the dominator tree pre-order.
539impl DominatorTreePreorder {
540 /// Get an iterator over the direct children of `block` in the dominator tree.
541 ///
542 /// These are the block's whose immediate dominator is an instruction in `block`, ordered according
543 /// to the CFG reverse post-order.
544 pub fn children(&self, block: Block) -> ChildIter {
545 ChildIter {
546 dtpo: self,
547 next: self.nodes[block].child,
548 }
549 }
550
551 /// Fast, constant time dominance check with block granularity.
552 ///
553 /// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant
554 /// time. This is less general than the `DominatorTree` method because it only works with block
555 /// program points.
556 ///
557 /// A block is considered to dominate itself.
558 pub fn dominates(&self, a: Block, b: Block) -> bool {
559 let na = &self.nodes[a];
560 let nb = &self.nodes[b];
561 na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max
562 }
563
564 /// Compare two blocks according to the dominator pre-order.
565 pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering {
566 self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number)
567 }
568
569 /// Compare two program points according to the dominator tree pre-order.
570 ///
571 /// This ordering of program points have the property that given a program point, pp, all the
572 /// program points dominated by pp follow immediately and contiguously after pp in the order.
573 pub fn pre_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
574 where
575 A: Into<ProgramPoint>,
576 B: Into<ProgramPoint>,
577 {
578 let a = a.into();
579 let b = b.into();
580 self.pre_cmp_block(layout.pp_block(a), layout.pp_block(b))
581 .then_with(|| layout.pp_cmp(a, b))
582 }
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588 use crate::cursor::{Cursor, FuncCursor};
589 use crate::flowgraph::ControlFlowGraph;
590 use crate::ir::types::*;
591 use crate::ir::{Function, InstBuilder, TrapCode};
592
593 #[test]
594 fn empty() {
595 let func = Function::new();
596 let cfg = ControlFlowGraph::with_function(&func);
597 debug_assert!(cfg.is_valid());
598 let dtree = DominatorTree::with_function(&func, &cfg);
599 assert_eq!(0, dtree.nodes.keys().count());
600 assert_eq!(dtree.cfg_postorder(), &[]);
601
602 let mut dtpo = DominatorTreePreorder::new();
603 dtpo.compute(&dtree, &func.layout);
604 }
605
606 #[test]
607 fn unreachable_node() {
608 let mut func = Function::new();
609 let block0 = func.dfg.make_block();
610 let v0 = func.dfg.append_block_param(block0, I32);
611 let block1 = func.dfg.make_block();
612 let block2 = func.dfg.make_block();
613 let trap_block = func.dfg.make_block();
614
615 let mut cur = FuncCursor::new(&mut func);
616
617 cur.insert_block(block0);
618 cur.ins().brif(v0, block2, &[], trap_block, &[]);
619
620 cur.insert_block(trap_block);
621 cur.ins().trap(TrapCode::User(0));
622
623 cur.insert_block(block1);
624 let v1 = cur.ins().iconst(I32, 1);
625 let v2 = cur.ins().iadd(v0, v1);
626 cur.ins().jump(block0, &[v2]);
627
628 cur.insert_block(block2);
629 cur.ins().return_(&[v0]);
630
631 let cfg = ControlFlowGraph::with_function(cur.func);
632 let dt = DominatorTree::with_function(cur.func, &cfg);
633
634 // Fall-through-first, prune-at-source DFT:
635 //
636 // block0 {
637 // brif block2 {
638 // trap
639 // block2 {
640 // return
641 // } block2
642 // } block0
643 assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
644
645 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
646 assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
647 assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
648
649 let mut dtpo = DominatorTreePreorder::new();
650 dtpo.compute(&dt, &cur.func.layout);
651 assert!(dtpo.dominates(block0, block0));
652 assert!(!dtpo.dominates(block0, block1));
653 assert!(dtpo.dominates(block0, block2));
654 assert!(!dtpo.dominates(block1, block0));
655 assert!(dtpo.dominates(block1, block1));
656 assert!(!dtpo.dominates(block1, block2));
657 assert!(!dtpo.dominates(block2, block0));
658 assert!(!dtpo.dominates(block2, block1));
659 assert!(dtpo.dominates(block2, block2));
660 }
661
662 #[test]
663 fn non_zero_entry_block() {
664 let mut func = Function::new();
665 let block0 = func.dfg.make_block();
666 let block1 = func.dfg.make_block();
667 let block2 = func.dfg.make_block();
668 let block3 = func.dfg.make_block();
669 let cond = func.dfg.append_block_param(block3, I32);
670
671 let mut cur = FuncCursor::new(&mut func);
672
673 cur.insert_block(block3);
674 let jmp_block3_block1 = cur.ins().jump(block1, &[]);
675
676 cur.insert_block(block1);
677 let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
678
679 cur.insert_block(block2);
680 cur.ins().jump(block0, &[]);
681
682 cur.insert_block(block0);
683
684 let cfg = ControlFlowGraph::with_function(cur.func);
685 let dt = DominatorTree::with_function(cur.func, &cfg);
686
687 // Fall-through-first, prune-at-source DFT:
688 //
689 // block3 {
690 // block3:jump block1 {
691 // block1 {
692 // block1:brif block0 {
693 // block1:jump block2 {
694 // block2 {
695 // block2:jump block0 (seen)
696 // } block2
697 // } block1:jump block2
698 // block0 {
699 // } block0
700 // } block1:brif block0
701 // } block1
702 // } block3:jump block1
703 // } block3
704
705 assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
706
707 assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
708 assert_eq!(dt.idom(block3), None);
709 assert_eq!(dt.idom(block1).unwrap(), jmp_block3_block1);
710 assert_eq!(dt.idom(block2).unwrap(), br_block1_block0_block2);
711 assert_eq!(dt.idom(block0).unwrap(), br_block1_block0_block2);
712
713 assert!(dt.dominates(
714 br_block1_block0_block2,
715 br_block1_block0_block2,
716 &cur.func.layout
717 ));
718 assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
719 assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
720
721 assert_eq!(
722 dt.rpo_cmp(block3, block3, &cur.func.layout),
723 Ordering::Equal
724 );
725 assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);
726 assert_eq!(
727 dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),
728 Ordering::Less
729 );
730 assert_eq!(
731 dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),
732 Ordering::Less
733 );
734 }
735
736 #[test]
737 fn backwards_layout() {
738 let mut func = Function::new();
739 let block0 = func.dfg.make_block();
740 let block1 = func.dfg.make_block();
741 let block2 = func.dfg.make_block();
742
743 let mut cur = FuncCursor::new(&mut func);
744
745 cur.insert_block(block0);
746 let jmp02 = cur.ins().jump(block2, &[]);
747
748 cur.insert_block(block1);
749 let trap = cur.ins().trap(TrapCode::User(5));
750
751 cur.insert_block(block2);
752 let jmp21 = cur.ins().jump(block1, &[]);
753
754 let cfg = ControlFlowGraph::with_function(cur.func);
755 let dt = DominatorTree::with_function(cur.func, &cfg);
756
757 assert_eq!(cur.func.layout.entry_block(), Some(block0));
758 assert_eq!(dt.idom(block0), None);
759 assert_eq!(dt.idom(block1), Some(jmp21));
760 assert_eq!(dt.idom(block2), Some(jmp02));
761
762 assert!(dt.dominates(block0, block0, &cur.func.layout));
763 assert!(dt.dominates(block0, jmp02, &cur.func.layout));
764 assert!(dt.dominates(block0, block1, &cur.func.layout));
765 assert!(dt.dominates(block0, trap, &cur.func.layout));
766 assert!(dt.dominates(block0, block2, &cur.func.layout));
767 assert!(dt.dominates(block0, jmp21, &cur.func.layout));
768
769 assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
770 assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
771 assert!(dt.dominates(jmp02, block1, &cur.func.layout));
772 assert!(dt.dominates(jmp02, trap, &cur.func.layout));
773 assert!(dt.dominates(jmp02, block2, &cur.func.layout));
774 assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
775
776 assert!(!dt.dominates(block1, block0, &cur.func.layout));
777 assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
778 assert!(dt.dominates(block1, block1, &cur.func.layout));
779 assert!(dt.dominates(block1, trap, &cur.func.layout));
780 assert!(!dt.dominates(block1, block2, &cur.func.layout));
781 assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
782
783 assert!(!dt.dominates(trap, block0, &cur.func.layout));
784 assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
785 assert!(!dt.dominates(trap, block1, &cur.func.layout));
786 assert!(dt.dominates(trap, trap, &cur.func.layout));
787 assert!(!dt.dominates(trap, block2, &cur.func.layout));
788 assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
789
790 assert!(!dt.dominates(block2, block0, &cur.func.layout));
791 assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
792 assert!(dt.dominates(block2, block1, &cur.func.layout));
793 assert!(dt.dominates(block2, trap, &cur.func.layout));
794 assert!(dt.dominates(block2, block2, &cur.func.layout));
795 assert!(dt.dominates(block2, jmp21, &cur.func.layout));
796
797 assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
798 assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
799 assert!(dt.dominates(jmp21, block1, &cur.func.layout));
800 assert!(dt.dominates(jmp21, trap, &cur.func.layout));
801 assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
802 assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
803 }
804}