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 alloc::vec::Vec; 9 use core::cmp; 10 use core::cmp::Ordering; 11 use core::mem; 12 13 mod simple; 14 15 pub use simple::SimpleDominatorTree; 16 17 /// Spanning tree node, used during domtree computation. 18 #[derive(Clone, Default)] 19 struct SpanningTreeNode { 20 /// This node's block in function CFG. 21 block: PackedOption<Block>, 22 /// Node's ancestor in the spanning tree. 23 /// Gets invalidated during semi-dominator computation. 24 ancestor: u32, 25 /// The smallest semi value discovered on any semi-dominator path 26 /// that went through the node up till the moment. 27 /// Gets updated in the course of semi-dominator computation. 28 label: u32, 29 /// Semidominator value for the node. 30 semi: u32, 31 /// Immediate dominator value for the node. 32 /// Initialized to node's ancestor in the spanning tree. 33 idom: u32, 34 } 35 36 /// DFS preorder number for unvisited nodes and the virtual root in the spanning tree. 37 const NOT_VISITED: u32 = 0; 38 39 /// Spanning tree, in CFG preorder. 40 /// Node 0 is the virtual root and doesn't have a corresponding block. 41 /// It's not required because function's CFG in Cranelift always have 42 /// a singular root, but helps to avoid additional checks. 43 /// Numbering nodes from 0 also follows the convention in 44 /// `SimpleDominatorTree` and `DominatorTreePreorder`. 45 #[derive(Clone, Default)] 46 struct SpanningTree { 47 nodes: Vec<SpanningTreeNode>, 48 } 49 50 impl SpanningTree { 51 fn new() -> Self { 52 // Include the virtual root. 53 Self { 54 nodes: vec![Default::default()], 55 } 56 } 57 58 fn with_capacity(capacity: usize) -> Self { 59 // Include the virtual root. 60 let mut nodes = Vec::with_capacity(capacity + 1); 61 nodes.push(Default::default()); 62 Self { nodes } 63 } 64 65 fn len(&self) -> usize { 66 self.nodes.len() 67 } 68 69 fn reserve(&mut self, capacity: usize) { 70 // Virtual root should be already included. 71 self.nodes.reserve(capacity); 72 } 73 74 fn clear(&mut self) { 75 self.nodes.resize(1, Default::default()); 76 } 77 78 /// Returns pre_number for the new node. 79 fn push(&mut self, ancestor: u32, block: Block) -> u32 { 80 // Virtual root should be already included. 81 debug_assert!(!self.nodes.is_empty()); 82 83 let pre_number = self.nodes.len() as u32; 84 85 self.nodes.push(SpanningTreeNode { 86 block: block.into(), 87 ancestor: ancestor, 88 label: pre_number, 89 semi: pre_number, 90 idom: ancestor, 91 }); 92 93 pre_number 94 } 95 } 96 97 impl std::ops::Index<u32> for SpanningTree { 98 type Output = SpanningTreeNode; 99 100 fn index(&self, idx: u32) -> &Self::Output { 101 &self.nodes[idx as usize] 102 } 103 } 104 105 impl std::ops::IndexMut<u32> for SpanningTree { 106 fn index_mut(&mut self, idx: u32) -> &mut Self::Output { 107 &mut self.nodes[idx as usize] 108 } 109 } 110 111 /// Traversal event to compute both preorder spanning tree 112 /// and postorder block list. Can't use `Dfs` from traversals.rs 113 /// here because of the need for parent links. 114 enum TraversalEvent { 115 Enter(u32, Block), 116 Exit(Block), 117 } 118 119 /// Dominator tree node. We keep one of these per block. 120 #[derive(Clone, Default)] 121 struct DominatorTreeNode { 122 /// Immediate dominator for the block, `None` for unreachable blocks. 123 idom: PackedOption<Block>, 124 /// Preorder traversal number, zero for unreachable blocks. 125 pre_number: u32, 126 } 127 128 /// The dominator tree for a single function, 129 /// computed using Semi-NCA algorithm. 130 pub struct DominatorTree { 131 /// DFS spanning tree. 132 stree: SpanningTree, 133 /// List of CFG blocks in postorder. 134 postorder: Vec<Block>, 135 /// Dominator tree nodes. 136 nodes: SecondaryMap<Block, DominatorTreeNode>, 137 138 /// Stack for building the spanning tree. 139 dfs_worklist: Vec<TraversalEvent>, 140 /// Stack used for processing semidominator paths 141 /// in link-eval procedure. 142 eval_worklist: Vec<u32>, 143 144 valid: bool, 145 } 146 147 /// Methods for querying the dominator tree. 148 impl DominatorTree { 149 /// Is `block` reachable from the entry block? 150 pub fn is_reachable(&self, block: Block) -> bool { 151 self.nodes[block].pre_number != NOT_VISITED 152 } 153 154 /// Get the CFG post-order of blocks that was used to compute the dominator tree. 155 /// 156 /// Note that this post-order is not updated automatically when the CFG is modified. It is 157 /// computed from scratch and cached by `compute()`. 158 pub fn cfg_postorder(&self) -> &[Block] { 159 debug_assert!(self.is_valid()); 160 &self.postorder 161 } 162 163 /// Get an iterator over CFG reverse post-order of blocks used to compute the dominator tree. 164 /// 165 /// Note that the post-order is not updated automatically when the CFG is modified. It is 166 /// computed from scratch and cached by `compute()`. 167 pub fn cfg_rpo(&self) -> impl Iterator<Item = &Block> { 168 debug_assert!(self.is_valid()); 169 self.postorder.iter().rev() 170 } 171 172 /// Returns the immediate dominator of `block`. 173 /// 174 /// `block_a` is said to *dominate* `block_b` if all control flow paths from the function 175 /// entry to `block_b` must go through `block_a`. 176 /// 177 /// The *immediate dominator* is the dominator that is closest to `block`. All other dominators 178 /// also dominate the immediate dominator. 179 /// 180 /// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block 181 /// which has no dominators. 182 pub fn idom(&self, block: Block) -> Option<Block> { 183 self.nodes[block].idom.into() 184 } 185 186 /// Returns `true` if `a` dominates `b`. 187 /// 188 /// This means that every control-flow path from the function entry to `b` must go through `a`. 189 /// 190 /// Dominance is ill defined for unreachable blocks. This function can always determine 191 /// dominance for instructions in the same block, but otherwise returns `false` if either block 192 /// is unreachable. 193 /// 194 /// An instruction is considered to dominate itself. 195 /// A block is also considered to dominate itself. 196 pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool 197 where 198 A: Into<ProgramPoint>, 199 B: Into<ProgramPoint>, 200 { 201 let a = a.into(); 202 let b = b.into(); 203 match a { 204 ProgramPoint::Block(block_a) => match b { 205 ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b), 206 ProgramPoint::Inst(inst_b) => { 207 let block_b = layout 208 .inst_block(inst_b) 209 .expect("Instruction not in layout."); 210 self.block_dominates(block_a, block_b) 211 } 212 }, 213 ProgramPoint::Inst(inst_a) => { 214 let block_a: Block = layout 215 .inst_block(inst_a) 216 .expect("Instruction not in layout."); 217 match b { 218 ProgramPoint::Block(block_b) => { 219 block_a != block_b && self.block_dominates(block_a, block_b) 220 } 221 ProgramPoint::Inst(inst_b) => { 222 let block_b = layout 223 .inst_block(inst_b) 224 .expect("Instruction not in layout."); 225 if block_a == block_b { 226 layout.pp_cmp(a, b) != Ordering::Greater 227 } else { 228 self.block_dominates(block_a, block_b) 229 } 230 } 231 } 232 } 233 } 234 } 235 236 /// Returns `true` if `block_a` dominates `block_b`. 237 /// 238 /// A block is considered to dominate itself. 239 fn block_dominates(&self, block_a: Block, mut block_b: Block) -> bool { 240 let pre_a = self.nodes[block_a].pre_number; 241 242 // Run a finger up the dominator tree from b until we see a. 243 // Do nothing if b is unreachable. 244 while pre_a < self.nodes[block_b].pre_number { 245 let idom = match self.idom(block_b) { 246 Some(idom) => idom, 247 None => return false, // a is unreachable, so we climbed past the entry 248 }; 249 block_b = idom; 250 } 251 252 block_a == block_b 253 } 254 } 255 256 impl DominatorTree { 257 /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a 258 /// function. 259 pub fn new() -> Self { 260 Self { 261 stree: SpanningTree::new(), 262 nodes: SecondaryMap::new(), 263 postorder: Vec::new(), 264 dfs_worklist: Vec::new(), 265 eval_worklist: Vec::new(), 266 valid: false, 267 } 268 } 269 270 /// Allocate and compute a dominator tree. 271 pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self { 272 let block_capacity = func.layout.block_capacity(); 273 let mut domtree = Self { 274 stree: SpanningTree::with_capacity(block_capacity), 275 nodes: SecondaryMap::with_capacity(block_capacity), 276 postorder: Vec::with_capacity(block_capacity), 277 dfs_worklist: Vec::new(), 278 eval_worklist: Vec::new(), 279 valid: false, 280 }; 281 domtree.compute(func, cfg); 282 domtree 283 } 284 285 /// Reset and compute a CFG post-order and dominator tree, 286 /// using Semi-NCA algorithm, described in the paper: 287 /// 288 /// Linear-Time Algorithms for Dominators and Related Problems. 289 /// Loukas Georgiadis, Princeton University, November 2005. 290 /// 291 /// The same algorithm is used by Julia, SpiderMonkey and LLVM, 292 /// the implementation is heavily inspired by them. 293 pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) { 294 let _tt = timing::domtree(); 295 debug_assert!(cfg.is_valid()); 296 297 self.clear(); 298 self.compute_spanning_tree(func); 299 self.compute_domtree(cfg); 300 301 self.valid = true; 302 } 303 304 /// Clear the data structures used to represent the dominator tree. This will leave the tree in 305 /// a state where `is_valid()` returns false. 306 pub fn clear(&mut self) { 307 self.stree.clear(); 308 self.nodes.clear(); 309 self.postorder.clear(); 310 self.valid = false; 311 } 312 313 /// Check if the dominator tree is in a valid state. 314 /// 315 /// Note that this doesn't perform any kind of validity checks. It simply checks if the 316 /// `compute()` method has been called since the last `clear()`. It does not check that the 317 /// dominator tree is consistent with the CFG. 318 pub fn is_valid(&self) -> bool { 319 self.valid 320 } 321 322 /// Reset all internal data structures, build spanning tree 323 /// and compute a post-order of the control flow graph. 324 fn compute_spanning_tree(&mut self, func: &Function) { 325 self.nodes.resize(func.dfg.num_blocks()); 326 self.stree.reserve(func.dfg.num_blocks()); 327 328 if let Some(block) = func.layout.entry_block() { 329 self.dfs_worklist.push(TraversalEvent::Enter(0, block)); 330 } 331 332 loop { 333 match self.dfs_worklist.pop() { 334 Some(TraversalEvent::Enter(parent, block)) => { 335 let node = &mut self.nodes[block]; 336 if node.pre_number != NOT_VISITED { 337 continue; 338 } 339 340 self.dfs_worklist.push(TraversalEvent::Exit(block)); 341 342 let pre_number = self.stree.push(parent, block); 343 node.pre_number = pre_number; 344 345 // Use the same traversal heuristics as in traversals.rs. 346 self.dfs_worklist.extend( 347 func.block_successors(block) 348 // Heuristic: chase the children in reverse. This puts 349 // the first successor block first in the postorder, all 350 // other things being equal, which tends to prioritize 351 // loop backedges over out-edges, putting the edge-block 352 // closer to the loop body and minimizing live-ranges in 353 // linear instruction space. This heuristic doesn't have 354 // any effect on the computation of dominators, and is 355 // purely for other consumers of the postorder we cache 356 // here. 357 .rev() 358 // A simple optimization: push less items to the stack. 359 .filter(|successor| self.nodes[*successor].pre_number == NOT_VISITED) 360 .map(|successor| TraversalEvent::Enter(pre_number, successor)), 361 ); 362 } 363 Some(TraversalEvent::Exit(block)) => self.postorder.push(block), 364 None => break, 365 } 366 } 367 } 368 369 /// Eval-link procedure from the paper. 370 /// For a predecessor V of node W returns V if V < W, otherwise the minimum of sdom(U), 371 /// where U > W and U is on a semi-dominator path for W in CFG. 372 /// Use path compression to bring complexity down to O(m*log(n)). 373 fn eval(&mut self, v: u32, last_linked: u32) -> u32 { 374 if self.stree[v].ancestor < last_linked { 375 return self.stree[v].label; 376 } 377 378 // Follow semi-dominator path. 379 let mut root = v; 380 loop { 381 self.eval_worklist.push(root); 382 root = self.stree[root].ancestor; 383 384 if self.stree[root].ancestor < last_linked { 385 break; 386 } 387 } 388 389 let mut prev = root; 390 let root = self.stree[prev].ancestor; 391 392 // Perform path compression. Point all ancestors to the root 393 // and propagate minimal sdom(U) value from ancestors to children. 394 while let Some(curr) = self.eval_worklist.pop() { 395 if self.stree[prev].label < self.stree[curr].label { 396 self.stree[curr].label = self.stree[prev].label; 397 } 398 399 self.stree[curr].ancestor = root; 400 prev = curr; 401 } 402 403 self.stree[v].label 404 } 405 406 fn compute_domtree(&mut self, cfg: &ControlFlowGraph) { 407 // Compute semi-dominators. 408 for w in (1..self.stree.len() as u32).rev() { 409 let w_node = &mut self.stree[w]; 410 let block = w_node.block.expect("Virtual root must have been excluded"); 411 let mut semi = w_node.ancestor; 412 413 let last_linked = w + 1; 414 415 for pred in cfg 416 .pred_iter(block) 417 .map(|pred: BlockPredecessor| pred.block) 418 { 419 // Skip unreachable nodes. 420 if self.nodes[pred].pre_number == NOT_VISITED { 421 continue; 422 } 423 424 let semi_candidate = self.eval(self.nodes[pred].pre_number, last_linked); 425 semi = std::cmp::min(semi, semi_candidate); 426 } 427 428 let w_node = &mut self.stree[w]; 429 w_node.label = semi; 430 w_node.semi = semi; 431 } 432 433 // Compute immediate dominators. 434 for v in 1..self.stree.len() as u32 { 435 let semi = self.stree[v].semi; 436 let block = self.stree[v] 437 .block 438 .expect("Virtual root must have been excluded"); 439 let mut idom = self.stree[v].idom; 440 441 while idom > semi { 442 idom = self.stree[idom].idom; 443 } 444 445 self.stree[v].idom = idom; 446 447 self.nodes[block].idom = self.stree[idom].block; 448 } 449 } 450 } 451 452 /// Optional pre-order information that can be computed for a dominator tree. 453 /// 454 /// This data structure is computed from a `DominatorTree` and provides: 455 /// 456 /// - A forward traversable dominator tree through the `children()` iterator. 457 /// - An ordering of blocks according to a dominator tree pre-order. 458 /// - Constant time dominance checks at the block granularity. 459 /// 460 /// The information in this auxiliary data structure is not easy to update when the control flow 461 /// graph changes, which is why it is kept separate. 462 pub struct DominatorTreePreorder { 463 nodes: SecondaryMap<Block, ExtraNode>, 464 465 // Scratch memory used by `compute_postorder()`. 466 stack: Vec<Block>, 467 } 468 469 #[derive(Default, Clone)] 470 struct ExtraNode { 471 /// First child node in the domtree. 472 child: PackedOption<Block>, 473 474 /// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO. 475 sibling: PackedOption<Block>, 476 477 /// Sequence number for this node in a pre-order traversal of the dominator tree. 478 /// Unreachable blocks have number 0, the entry block is 1. 479 pre_number: u32, 480 481 /// Maximum `pre_number` for the sub-tree of the dominator tree that is rooted at this node. 482 /// This is always >= `pre_number`. 483 pre_max: u32, 484 } 485 486 /// Creating and computing the dominator tree pre-order. 487 impl DominatorTreePreorder { 488 /// Create a new blank `DominatorTreePreorder`. 489 pub fn new() -> Self { 490 Self { 491 nodes: SecondaryMap::new(), 492 stack: Vec::new(), 493 } 494 } 495 496 /// Recompute this data structure to match `domtree`. 497 pub fn compute(&mut self, domtree: &DominatorTree) { 498 self.nodes.clear(); 499 500 // Step 1: Populate the child and sibling links. 501 // 502 // By following the CFG post-order and pushing to the front of the lists, we make sure that 503 // sibling lists are ordered according to the CFG reverse post-order. 504 for &block in domtree.cfg_postorder() { 505 if let Some(idom) = domtree.idom(block) { 506 let sib = mem::replace(&mut self.nodes[idom].child, block.into()); 507 self.nodes[block].sibling = sib; 508 } else { 509 // The only block without an immediate dominator is the entry. 510 self.stack.push(block); 511 } 512 } 513 514 // Step 2. Assign pre-order numbers from a DFS of the dominator tree. 515 debug_assert!(self.stack.len() <= 1); 516 let mut n = 0; 517 while let Some(block) = self.stack.pop() { 518 n += 1; 519 let node = &mut self.nodes[block]; 520 node.pre_number = n; 521 node.pre_max = n; 522 if let Some(n) = node.sibling.expand() { 523 self.stack.push(n); 524 } 525 if let Some(n) = node.child.expand() { 526 self.stack.push(n); 527 } 528 } 529 530 // Step 3. Propagate the `pre_max` numbers up the tree. 531 // The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all 532 // its dominator tree children. 533 for &block in domtree.cfg_postorder() { 534 if let Some(idom) = domtree.idom(block) { 535 let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max); 536 self.nodes[idom].pre_max = pre_max; 537 } 538 } 539 } 540 } 541 542 /// An iterator that enumerates the direct children of a block in the dominator tree. 543 pub struct ChildIter<'a> { 544 dtpo: &'a DominatorTreePreorder, 545 next: PackedOption<Block>, 546 } 547 548 impl<'a> Iterator for ChildIter<'a> { 549 type Item = Block; 550 551 fn next(&mut self) -> Option<Block> { 552 let n = self.next.expand(); 553 if let Some(block) = n { 554 self.next = self.dtpo.nodes[block].sibling; 555 } 556 n 557 } 558 } 559 560 /// Query interface for the dominator tree pre-order. 561 impl DominatorTreePreorder { 562 /// Get an iterator over the direct children of `block` in the dominator tree. 563 /// 564 /// These are the block's whose immediate dominator is an instruction in `block`, ordered according 565 /// to the CFG reverse post-order. 566 pub fn children(&self, block: Block) -> ChildIter { 567 ChildIter { 568 dtpo: self, 569 next: self.nodes[block].child, 570 } 571 } 572 573 /// Fast, constant time dominance check with block granularity. 574 /// 575 /// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant 576 /// time. This is less general than the `DominatorTree` method because it only works with block 577 /// program points. 578 /// 579 /// A block is considered to dominate itself. 580 pub fn dominates(&self, a: Block, b: Block) -> bool { 581 let na = &self.nodes[a]; 582 let nb = &self.nodes[b]; 583 na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max 584 } 585 586 /// Compare two blocks according to the dominator pre-order. 587 pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering { 588 self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number) 589 } 590 591 /// Compare two program points according to the dominator tree pre-order. 592 /// 593 /// This ordering of program points have the property that given a program point, pp, all the 594 /// program points dominated by pp follow immediately and contiguously after pp in the order. 595 pub fn pre_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering 596 where 597 A: Into<ProgramPoint>, 598 B: Into<ProgramPoint>, 599 { 600 let a = a.into(); 601 let b = b.into(); 602 self.pre_cmp_block(layout.pp_block(a), layout.pp_block(b)) 603 .then_with(|| layout.pp_cmp(a, b)) 604 } 605 } 606 607 #[cfg(test)] 608 mod tests { 609 use super::*; 610 use crate::cursor::{Cursor, FuncCursor}; 611 use crate::ir::types::*; 612 use crate::ir::{InstBuilder, TrapCode}; 613 614 #[test] 615 fn empty() { 616 let func = Function::new(); 617 let cfg = ControlFlowGraph::with_function(&func); 618 debug_assert!(cfg.is_valid()); 619 let dtree = DominatorTree::with_function(&func, &cfg); 620 assert_eq!(0, dtree.nodes.keys().count()); 621 assert_eq!(dtree.cfg_postorder(), &[]); 622 623 let mut dtpo = DominatorTreePreorder::new(); 624 dtpo.compute(&dtree); 625 } 626 627 #[test] 628 fn unreachable_node() { 629 let mut func = Function::new(); 630 let block0 = func.dfg.make_block(); 631 let v0 = func.dfg.append_block_param(block0, I32); 632 let block1 = func.dfg.make_block(); 633 let block2 = func.dfg.make_block(); 634 let trap_block = func.dfg.make_block(); 635 636 let mut cur = FuncCursor::new(&mut func); 637 638 cur.insert_block(block0); 639 cur.ins().brif(v0, block2, &[], trap_block, &[]); 640 641 cur.insert_block(trap_block); 642 cur.ins().trap(TrapCode::unwrap_user(1)); 643 644 cur.insert_block(block1); 645 let v1 = cur.ins().iconst(I32, 1); 646 let v2 = cur.ins().iadd(v0, v1); 647 cur.ins().jump(block0, &[v2]); 648 649 cur.insert_block(block2); 650 cur.ins().return_(&[v0]); 651 652 let cfg = ControlFlowGraph::with_function(cur.func); 653 let dt = DominatorTree::with_function(cur.func, &cfg); 654 655 // Fall-through-first, prune-at-source DFT: 656 // 657 // block0 { 658 // brif block2 { 659 // trap 660 // block2 { 661 // return 662 // } block2 663 // } block0 664 assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]); 665 666 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst(); 667 assert!(!dt.dominates(v2_def, block0, &cur.func.layout)); 668 assert!(!dt.dominates(block0, v2_def, &cur.func.layout)); 669 670 let mut dtpo = DominatorTreePreorder::new(); 671 dtpo.compute(&dt); 672 assert!(dtpo.dominates(block0, block0)); 673 assert!(!dtpo.dominates(block0, block1)); 674 assert!(dtpo.dominates(block0, block2)); 675 assert!(!dtpo.dominates(block1, block0)); 676 assert!(dtpo.dominates(block1, block1)); 677 assert!(!dtpo.dominates(block1, block2)); 678 assert!(!dtpo.dominates(block2, block0)); 679 assert!(!dtpo.dominates(block2, block1)); 680 assert!(dtpo.dominates(block2, block2)); 681 } 682 683 #[test] 684 fn non_zero_entry_block() { 685 let mut func = Function::new(); 686 let block0 = func.dfg.make_block(); 687 let block1 = func.dfg.make_block(); 688 let block2 = func.dfg.make_block(); 689 let block3 = func.dfg.make_block(); 690 let cond = func.dfg.append_block_param(block3, I32); 691 692 let mut cur = FuncCursor::new(&mut func); 693 694 cur.insert_block(block3); 695 let jmp_block3_block1 = cur.ins().jump(block1, &[]); 696 697 cur.insert_block(block1); 698 let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]); 699 700 cur.insert_block(block2); 701 cur.ins().jump(block0, &[]); 702 703 cur.insert_block(block0); 704 705 let cfg = ControlFlowGraph::with_function(cur.func); 706 let dt = DominatorTree::with_function(cur.func, &cfg); 707 708 // Fall-through-first, prune-at-source DFT: 709 // 710 // block3 { 711 // block3:jump block1 { 712 // block1 { 713 // block1:brif block0 { 714 // block1:jump block2 { 715 // block2 { 716 // block2:jump block0 (seen) 717 // } block2 718 // } block1:jump block2 719 // block0 { 720 // } block0 721 // } block1:brif block0 722 // } block1 723 // } block3:jump block1 724 // } block3 725 726 assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]); 727 728 assert_eq!(cur.func.layout.entry_block().unwrap(), block3); 729 assert_eq!(dt.idom(block3), None); 730 assert_eq!(dt.idom(block1).unwrap(), block3); 731 assert_eq!(dt.idom(block2).unwrap(), block1); 732 assert_eq!(dt.idom(block0).unwrap(), block1); 733 734 assert!(dt.dominates( 735 br_block1_block0_block2, 736 br_block1_block0_block2, 737 &cur.func.layout 738 )); 739 assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout)); 740 assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout)); 741 } 742 743 #[test] 744 fn backwards_layout() { 745 let mut func = Function::new(); 746 let block0 = func.dfg.make_block(); 747 let block1 = func.dfg.make_block(); 748 let block2 = func.dfg.make_block(); 749 750 let mut cur = FuncCursor::new(&mut func); 751 752 cur.insert_block(block0); 753 let jmp02 = cur.ins().jump(block2, &[]); 754 755 cur.insert_block(block1); 756 let trap = cur.ins().trap(TrapCode::unwrap_user(5)); 757 758 cur.insert_block(block2); 759 let jmp21 = cur.ins().jump(block1, &[]); 760 761 let cfg = ControlFlowGraph::with_function(cur.func); 762 let dt = DominatorTree::with_function(cur.func, &cfg); 763 764 assert_eq!(cur.func.layout.entry_block(), Some(block0)); 765 assert_eq!(dt.idom(block0), None); 766 assert_eq!(dt.idom(block1), Some(block2)); 767 assert_eq!(dt.idom(block2), Some(block0)); 768 769 assert!(dt.dominates(block0, block0, &cur.func.layout)); 770 assert!(dt.dominates(block0, jmp02, &cur.func.layout)); 771 assert!(dt.dominates(block0, block1, &cur.func.layout)); 772 assert!(dt.dominates(block0, trap, &cur.func.layout)); 773 assert!(dt.dominates(block0, block2, &cur.func.layout)); 774 assert!(dt.dominates(block0, jmp21, &cur.func.layout)); 775 776 assert!(!dt.dominates(jmp02, block0, &cur.func.layout)); 777 assert!(dt.dominates(jmp02, jmp02, &cur.func.layout)); 778 assert!(dt.dominates(jmp02, block1, &cur.func.layout)); 779 assert!(dt.dominates(jmp02, trap, &cur.func.layout)); 780 assert!(dt.dominates(jmp02, block2, &cur.func.layout)); 781 assert!(dt.dominates(jmp02, jmp21, &cur.func.layout)); 782 783 assert!(!dt.dominates(block1, block0, &cur.func.layout)); 784 assert!(!dt.dominates(block1, jmp02, &cur.func.layout)); 785 assert!(dt.dominates(block1, block1, &cur.func.layout)); 786 assert!(dt.dominates(block1, trap, &cur.func.layout)); 787 assert!(!dt.dominates(block1, block2, &cur.func.layout)); 788 assert!(!dt.dominates(block1, jmp21, &cur.func.layout)); 789 790 assert!(!dt.dominates(trap, block0, &cur.func.layout)); 791 assert!(!dt.dominates(trap, jmp02, &cur.func.layout)); 792 assert!(!dt.dominates(trap, block1, &cur.func.layout)); 793 assert!(dt.dominates(trap, trap, &cur.func.layout)); 794 assert!(!dt.dominates(trap, block2, &cur.func.layout)); 795 assert!(!dt.dominates(trap, jmp21, &cur.func.layout)); 796 797 assert!(!dt.dominates(block2, block0, &cur.func.layout)); 798 assert!(!dt.dominates(block2, jmp02, &cur.func.layout)); 799 assert!(dt.dominates(block2, block1, &cur.func.layout)); 800 assert!(dt.dominates(block2, trap, &cur.func.layout)); 801 assert!(dt.dominates(block2, block2, &cur.func.layout)); 802 assert!(dt.dominates(block2, jmp21, &cur.func.layout)); 803 804 assert!(!dt.dominates(jmp21, block0, &cur.func.layout)); 805 assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout)); 806 assert!(dt.dominates(jmp21, block1, &cur.func.layout)); 807 assert!(dt.dominates(jmp21, trap, &cur.func.layout)); 808 assert!(!dt.dominates(jmp21, block2, &cur.func.layout)); 809 assert!(dt.dominates(jmp21, jmp21, &cur.func.layout)); 810 } 811 812 #[test] 813 fn insts_same_block() { 814 let mut func = Function::new(); 815 let block0 = func.dfg.make_block(); 816 817 let mut cur = FuncCursor::new(&mut func); 818 819 cur.insert_block(block0); 820 let v1 = cur.ins().iconst(I32, 1); 821 let v2 = cur.ins().iadd(v1, v1); 822 let v3 = cur.ins().iadd(v2, v2); 823 cur.ins().return_(&[]); 824 825 let cfg = ControlFlowGraph::with_function(cur.func); 826 let dt = DominatorTree::with_function(cur.func, &cfg); 827 828 let v1_def = cur.func.dfg.value_def(v1).unwrap_inst(); 829 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst(); 830 let v3_def = cur.func.dfg.value_def(v3).unwrap_inst(); 831 832 assert!(dt.dominates(v1_def, v2_def, &cur.func.layout)); 833 assert!(dt.dominates(v2_def, v3_def, &cur.func.layout)); 834 assert!(dt.dominates(v1_def, v3_def, &cur.func.layout)); 835 836 assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout)); 837 assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout)); 838 assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout)); 839 840 assert!(dt.dominates(v2_def, v2_def, &cur.func.layout)); 841 assert!(dt.dominates(block0, block0, &cur.func.layout)); 842 843 assert!(dt.dominates(block0, v1_def, &cur.func.layout)); 844 assert!(dt.dominates(block0, v2_def, &cur.func.layout)); 845 assert!(dt.dominates(block0, v3_def, &cur.func.layout)); 846 847 assert!(!dt.dominates(v1_def, block0, &cur.func.layout)); 848 assert!(!dt.dominates(v2_def, block0, &cur.func.layout)); 849 assert!(!dt.dominates(v3_def, block0, &cur.func.layout)); 850 } 851 } 852