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