1 //! A Dominator Tree represented as mappings of Blocks to their immediate dominator. 2 3 use crate::entity::SecondaryMap; 4 use crate::flowgraph::{BlockPredecessor, ControlFlowGraph}; 5 use crate::ir::{Block, Function, Inst, Layout, ProgramPoint}; 6 use crate::packed_option::PackedOption; 7 use crate::timing; 8 use alloc::vec::Vec; 9 use core::cmp; 10 use core::cmp::Ordering; 11 use 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. 15 const STRIDE: u32 = 4; 16 17 /// Special RPO numbers used during `compute_postorder`. 18 const SEEN: u32 = 1; 19 20 /// Dominator tree node. We keep one of these per block. 21 #[derive(Clone, Default)] 22 struct 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. 38 enum Visit { 39 First, 40 Last, 41 } 42 43 /// The dominator tree for a single function. 44 pub 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. 57 impl 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 221 impl 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. 433 pub 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)] 441 struct 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. 458 impl 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. 521 pub struct ChildIter<'a> { 522 dtpo: &'a DominatorTreePreorder, 523 next: PackedOption<Block>, 524 } 525 526 impl<'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. 539 impl 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)] 586 mod tests { 587 use super::*; 588 use crate::cursor::{Cursor, FuncCursor}; 589 use crate::ir::types::*; 590 use crate::ir::{InstBuilder, TrapCode}; 591 592 #[test] 593 fn empty() { 594 let func = Function::new(); 595 let cfg = ControlFlowGraph::with_function(&func); 596 debug_assert!(cfg.is_valid()); 597 let dtree = DominatorTree::with_function(&func, &cfg); 598 assert_eq!(0, dtree.nodes.keys().count()); 599 assert_eq!(dtree.cfg_postorder(), &[]); 600 601 let mut dtpo = DominatorTreePreorder::new(); 602 dtpo.compute(&dtree, &func.layout); 603 } 604 605 #[test] 606 fn unreachable_node() { 607 let mut func = Function::new(); 608 let block0 = func.dfg.make_block(); 609 let v0 = func.dfg.append_block_param(block0, I32); 610 let block1 = func.dfg.make_block(); 611 let block2 = func.dfg.make_block(); 612 let trap_block = func.dfg.make_block(); 613 614 let mut cur = FuncCursor::new(&mut func); 615 616 cur.insert_block(block0); 617 cur.ins().brif(v0, block2, &[], trap_block, &[]); 618 619 cur.insert_block(trap_block); 620 cur.ins().trap(TrapCode::User(0)); 621 622 cur.insert_block(block1); 623 let v1 = cur.ins().iconst(I32, 1); 624 let v2 = cur.ins().iadd(v0, v1); 625 cur.ins().jump(block0, &[v2]); 626 627 cur.insert_block(block2); 628 cur.ins().return_(&[v0]); 629 630 let cfg = ControlFlowGraph::with_function(cur.func); 631 let dt = DominatorTree::with_function(cur.func, &cfg); 632 633 // Fall-through-first, prune-at-source DFT: 634 // 635 // block0 { 636 // brif block2 { 637 // trap 638 // block2 { 639 // return 640 // } block2 641 // } block0 642 assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]); 643 644 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst(); 645 assert!(!dt.dominates(v2_def, block0, &cur.func.layout)); 646 assert!(!dt.dominates(block0, v2_def, &cur.func.layout)); 647 648 let mut dtpo = DominatorTreePreorder::new(); 649 dtpo.compute(&dt, &cur.func.layout); 650 assert!(dtpo.dominates(block0, block0)); 651 assert!(!dtpo.dominates(block0, block1)); 652 assert!(dtpo.dominates(block0, block2)); 653 assert!(!dtpo.dominates(block1, block0)); 654 assert!(dtpo.dominates(block1, block1)); 655 assert!(!dtpo.dominates(block1, block2)); 656 assert!(!dtpo.dominates(block2, block0)); 657 assert!(!dtpo.dominates(block2, block1)); 658 assert!(dtpo.dominates(block2, block2)); 659 } 660 661 #[test] 662 fn non_zero_entry_block() { 663 let mut func = Function::new(); 664 let block0 = func.dfg.make_block(); 665 let block1 = func.dfg.make_block(); 666 let block2 = func.dfg.make_block(); 667 let block3 = func.dfg.make_block(); 668 let cond = func.dfg.append_block_param(block3, I32); 669 670 let mut cur = FuncCursor::new(&mut func); 671 672 cur.insert_block(block3); 673 let jmp_block3_block1 = cur.ins().jump(block1, &[]); 674 675 cur.insert_block(block1); 676 let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]); 677 678 cur.insert_block(block2); 679 cur.ins().jump(block0, &[]); 680 681 cur.insert_block(block0); 682 683 let cfg = ControlFlowGraph::with_function(cur.func); 684 let dt = DominatorTree::with_function(cur.func, &cfg); 685 686 // Fall-through-first, prune-at-source DFT: 687 // 688 // block3 { 689 // block3:jump block1 { 690 // block1 { 691 // block1:brif block0 { 692 // block1:jump block2 { 693 // block2 { 694 // block2:jump block0 (seen) 695 // } block2 696 // } block1:jump block2 697 // block0 { 698 // } block0 699 // } block1:brif block0 700 // } block1 701 // } block3:jump block1 702 // } block3 703 704 assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]); 705 706 assert_eq!(cur.func.layout.entry_block().unwrap(), block3); 707 assert_eq!(dt.idom(block3), None); 708 assert_eq!(dt.idom(block1).unwrap(), jmp_block3_block1); 709 assert_eq!(dt.idom(block2).unwrap(), br_block1_block0_block2); 710 assert_eq!(dt.idom(block0).unwrap(), br_block1_block0_block2); 711 712 assert!(dt.dominates( 713 br_block1_block0_block2, 714 br_block1_block0_block2, 715 &cur.func.layout 716 )); 717 assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout)); 718 assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout)); 719 720 assert_eq!( 721 dt.rpo_cmp(block3, block3, &cur.func.layout), 722 Ordering::Equal 723 ); 724 assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less); 725 assert_eq!( 726 dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout), 727 Ordering::Less 728 ); 729 assert_eq!( 730 dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout), 731 Ordering::Less 732 ); 733 } 734 735 #[test] 736 fn backwards_layout() { 737 let mut func = Function::new(); 738 let block0 = func.dfg.make_block(); 739 let block1 = func.dfg.make_block(); 740 let block2 = func.dfg.make_block(); 741 742 let mut cur = FuncCursor::new(&mut func); 743 744 cur.insert_block(block0); 745 let jmp02 = cur.ins().jump(block2, &[]); 746 747 cur.insert_block(block1); 748 let trap = cur.ins().trap(TrapCode::User(5)); 749 750 cur.insert_block(block2); 751 let jmp21 = cur.ins().jump(block1, &[]); 752 753 let cfg = ControlFlowGraph::with_function(cur.func); 754 let dt = DominatorTree::with_function(cur.func, &cfg); 755 756 assert_eq!(cur.func.layout.entry_block(), Some(block0)); 757 assert_eq!(dt.idom(block0), None); 758 assert_eq!(dt.idom(block1), Some(jmp21)); 759 assert_eq!(dt.idom(block2), Some(jmp02)); 760 761 assert!(dt.dominates(block0, block0, &cur.func.layout)); 762 assert!(dt.dominates(block0, jmp02, &cur.func.layout)); 763 assert!(dt.dominates(block0, block1, &cur.func.layout)); 764 assert!(dt.dominates(block0, trap, &cur.func.layout)); 765 assert!(dt.dominates(block0, block2, &cur.func.layout)); 766 assert!(dt.dominates(block0, jmp21, &cur.func.layout)); 767 768 assert!(!dt.dominates(jmp02, block0, &cur.func.layout)); 769 assert!(dt.dominates(jmp02, jmp02, &cur.func.layout)); 770 assert!(dt.dominates(jmp02, block1, &cur.func.layout)); 771 assert!(dt.dominates(jmp02, trap, &cur.func.layout)); 772 assert!(dt.dominates(jmp02, block2, &cur.func.layout)); 773 assert!(dt.dominates(jmp02, jmp21, &cur.func.layout)); 774 775 assert!(!dt.dominates(block1, block0, &cur.func.layout)); 776 assert!(!dt.dominates(block1, jmp02, &cur.func.layout)); 777 assert!(dt.dominates(block1, block1, &cur.func.layout)); 778 assert!(dt.dominates(block1, trap, &cur.func.layout)); 779 assert!(!dt.dominates(block1, block2, &cur.func.layout)); 780 assert!(!dt.dominates(block1, jmp21, &cur.func.layout)); 781 782 assert!(!dt.dominates(trap, block0, &cur.func.layout)); 783 assert!(!dt.dominates(trap, jmp02, &cur.func.layout)); 784 assert!(!dt.dominates(trap, block1, &cur.func.layout)); 785 assert!(dt.dominates(trap, trap, &cur.func.layout)); 786 assert!(!dt.dominates(trap, block2, &cur.func.layout)); 787 assert!(!dt.dominates(trap, jmp21, &cur.func.layout)); 788 789 assert!(!dt.dominates(block2, block0, &cur.func.layout)); 790 assert!(!dt.dominates(block2, jmp02, &cur.func.layout)); 791 assert!(dt.dominates(block2, block1, &cur.func.layout)); 792 assert!(dt.dominates(block2, trap, &cur.func.layout)); 793 assert!(dt.dominates(block2, block2, &cur.func.layout)); 794 assert!(dt.dominates(block2, jmp21, &cur.func.layout)); 795 796 assert!(!dt.dominates(jmp21, block0, &cur.func.layout)); 797 assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout)); 798 assert!(dt.dominates(jmp21, block1, &cur.func.layout)); 799 assert!(dt.dominates(jmp21, trap, &cur.func.layout)); 800 assert!(!dt.dominates(jmp21, block2, &cur.func.layout)); 801 assert!(dt.dominates(jmp21, jmp21, &cur.func.layout)); 802 } 803 } 804