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