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