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::instructions::BranchInfo; 6 use crate::ir::{Block, ExpandedProgramPoint, Function, Inst, Layout, ProgramOrder, Value}; 7 use crate::packed_option::PackedOption; 8 use crate::timing; 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 /// Special RPO numbers used during `compute_postorder`. 19 const DONE: u32 = 1; 20 const SEEN: u32 = 2; 21 22 /// Dominator tree node. We keep one of these per block. 23 #[derive(Clone, Default)] 24 struct DomNode { 25 /// Number of this node in a reverse post-order traversal of the CFG, starting from 1. 26 /// This number is monotonic in the reverse postorder but not contiguous, since we leave 27 /// holes for later localized modifications of the dominator tree. 28 /// Unreachable nodes get number 0, all others are positive. 29 rpo_number: u32, 30 31 /// The immediate dominator of this block, represented as the branch or jump instruction at the 32 /// end of the dominating basic block. 33 /// 34 /// This is `None` for unreachable blocks and the entry block which doesn't have an immediate 35 /// dominator. 36 idom: PackedOption<Inst>, 37 } 38 39 /// The dominator tree for a single function. 40 pub struct DominatorTree { 41 nodes: SecondaryMap<Block, DomNode>, 42 43 /// CFG post-order of all reachable blocks. 44 postorder: Vec<Block>, 45 46 /// Scratch memory used by `compute_postorder()`. 47 stack: Vec<Block>, 48 49 valid: bool, 50 } 51 52 /// Methods for querying the dominator tree. 53 impl DominatorTree { 54 /// Is `block` reachable from the entry block? 55 pub fn is_reachable(&self, block: Block) -> bool { 56 self.nodes[block].rpo_number != 0 57 } 58 59 /// Get the CFG post-order of blocks that was used to compute the dominator tree. 60 /// 61 /// Note that this post-order is not updated automatically when the CFG is modified. It is 62 /// computed from scratch and cached by `compute()`. 63 pub fn cfg_postorder(&self) -> &[Block] { 64 debug_assert!(self.is_valid()); 65 &self.postorder 66 } 67 68 /// Returns the immediate dominator of `block`. 69 /// 70 /// The immediate dominator of a basic block is a basic block which we represent by 71 /// the branch or jump instruction at the end of the basic block. This does not have to be the 72 /// terminator of its block. 73 /// 74 /// A branch or jump is said to *dominate* `block` if all control flow paths from the function 75 /// entry to `block` must go through the branch. 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<Inst> { 83 self.nodes[block].idom.into() 84 } 85 86 /// Compare two blocks relative to the reverse post-order. 87 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<ExpandedProgramPoint>, 100 B: Into<ExpandedProgramPoint>, 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(layout.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 pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool 118 where 119 A: Into<ExpandedProgramPoint>, 120 B: Into<ExpandedProgramPoint>, 121 { 122 let a = a.into(); 123 let b = b.into(); 124 match a { 125 ExpandedProgramPoint::Block(block_a) => { 126 a == b || self.last_dominator(block_a, b, layout).is_some() 127 } 128 ExpandedProgramPoint::Inst(inst_a) => { 129 let block_a = layout 130 .inst_block(inst_a) 131 .expect("Instruction not in layout."); 132 match self.last_dominator(block_a, b, layout) { 133 Some(last) => layout.cmp(inst_a, last) != Ordering::Greater, 134 None => false, 135 } 136 } 137 } 138 } 139 140 /// Find the last instruction in `a` that dominates `b`. 141 /// If no instructions in `a` dominate `b`, return `None`. 142 pub fn last_dominator<B>(&self, a: Block, b: B, layout: &Layout) -> Option<Inst> 143 where 144 B: Into<ExpandedProgramPoint>, 145 { 146 let (mut block_b, mut inst_b) = match b.into() { 147 ExpandedProgramPoint::Block(block) => (block, None), 148 ExpandedProgramPoint::Inst(inst) => ( 149 layout.inst_block(inst).expect("Instruction not in layout."), 150 Some(inst), 151 ), 152 }; 153 let rpo_a = self.nodes[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 None, // a is unreachable, so we climbed past the entry 161 }; 162 block_b = layout.inst_block(idom).expect("Dominator got removed."); 163 inst_b = Some(idom); 164 } 165 if a == block_b { 166 inst_b 167 } else { 168 None 169 } 170 } 171 172 /// Compute the common dominator of two basic blocks. 173 /// 174 /// Both basic blocks are assumed to be reachable. 175 pub fn common_dominator( 176 &self, 177 mut a: BlockPredecessor, 178 mut b: BlockPredecessor, 179 layout: &Layout, 180 ) -> BlockPredecessor { 181 loop { 182 match self.rpo_cmp_block(a.block, b.block) { 183 Ordering::Less => { 184 // `a` comes before `b` in the RPO. Move `b` up. 185 let idom = self.nodes[b.block].idom.expect("Unreachable basic block?"); 186 b = BlockPredecessor::new( 187 layout.inst_block(idom).expect("Dangling idom instruction"), 188 idom, 189 ); 190 } 191 Ordering::Greater => { 192 // `b` comes before `a` in the RPO. Move `a` up. 193 let idom = self.nodes[a.block].idom.expect("Unreachable basic block?"); 194 a = BlockPredecessor::new( 195 layout.inst_block(idom).expect("Dangling idom instruction"), 196 idom, 197 ); 198 } 199 Ordering::Equal => break, 200 } 201 } 202 203 debug_assert_eq!( 204 a.block, b.block, 205 "Unreachable block passed to common_dominator?" 206 ); 207 208 // We're in the same block. The common dominator is the earlier instruction. 209 if layout.cmp(a.inst, b.inst) == Ordering::Less { 210 a 211 } else { 212 b 213 } 214 } 215 } 216 217 impl DominatorTree { 218 /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a 219 /// function. 220 pub fn new() -> Self { 221 Self { 222 nodes: SecondaryMap::new(), 223 postorder: Vec::new(), 224 stack: Vec::new(), 225 valid: false, 226 } 227 } 228 229 /// Allocate and compute a dominator tree. 230 pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self { 231 let block_capacity = func.layout.block_capacity(); 232 let mut domtree = Self { 233 nodes: SecondaryMap::with_capacity(block_capacity), 234 postorder: Vec::with_capacity(block_capacity), 235 stack: Vec::new(), 236 valid: false, 237 }; 238 domtree.compute(func, cfg); 239 domtree 240 } 241 242 /// Reset and compute a CFG post-order and dominator tree. 243 pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) { 244 let _tt = timing::domtree(); 245 debug_assert!(cfg.is_valid()); 246 self.compute_postorder(func); 247 self.compute_domtree(func, cfg); 248 self.valid = true; 249 } 250 251 /// Clear the data structures used to represent the dominator tree. This will leave the tree in 252 /// a state where `is_valid()` returns false. 253 pub fn clear(&mut self) { 254 self.nodes.clear(); 255 self.postorder.clear(); 256 debug_assert!(self.stack.is_empty()); 257 self.valid = false; 258 } 259 260 /// Check if the dominator tree is in a valid state. 261 /// 262 /// Note that this doesn't perform any kind of validity checks. It simply checks if the 263 /// `compute()` method has been called since the last `clear()`. It does not check that the 264 /// dominator tree is consistent with the CFG. 265 pub fn is_valid(&self) -> bool { 266 self.valid 267 } 268 269 /// Reset all internal data structures and compute a post-order of the control flow graph. 270 /// 271 /// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones. 272 fn compute_postorder(&mut self, func: &Function) { 273 self.clear(); 274 self.nodes.resize(func.dfg.num_blocks()); 275 276 // This algorithm is a depth first traversal (DFT) of the control flow graph, computing a 277 // post-order of the blocks that are reachable form the entry block. A DFT post-order is not 278 // unique. The specific order we get is controlled by two factors: 279 // 280 // 1. The order each node's children are visited, and 281 // 2. The method used for pruning graph edges to get a tree. 282 // 283 // There are two ways of viewing the CFG as a graph: 284 // 285 // 1. Each block is a node, with outgoing edges for all the branches in the block. 286 // 2. Each basic block is a node, with outgoing edges for the single branch at the end of 287 // the BB. (A block is a linear sequence of basic blocks). 288 // 289 // The first graph is a contraction of the second one. We want to compute a block post-order 290 // that is compatible both graph interpretations. That is, if you compute a BB post-order 291 // and then remove those BBs that do not correspond to block headers, you get a post-order of 292 // the block graph. 293 // 294 // Node child order: 295 // 296 // In the BB graph, we always go down the fall-through path first and follow the branch 297 // destination second. 298 // 299 // In the block graph, this is equivalent to visiting block successors in a bottom-up 300 // order, starting from the destination of the block's terminating jump, ending at the 301 // destination of the first branch in the block. 302 // 303 // Edge pruning: 304 // 305 // In the BB graph, we keep an edge to a block the first time we visit the *source* side 306 // of the edge. Any subsequent edges to the same block are pruned. 307 // 308 // The equivalent tree is reached in the block graph by keeping the first edge to a block 309 // in a top-down traversal of the successors. (And then visiting edges in a bottom-up 310 // order). 311 // 312 // This pruning method makes it possible to compute the DFT without storing lots of 313 // information about the progress through a block. 314 315 // During this algorithm only, use `rpo_number` to hold the following state: 316 // 317 // 0: block has not yet been reached in the pre-order. 318 // SEEN: block has been pushed on the stack but successors not yet pushed. 319 // DONE: Successors pushed. 320 321 match func.layout.entry_block() { 322 Some(block) => { 323 self.stack.push(block); 324 self.nodes[block].rpo_number = SEEN; 325 } 326 None => return, 327 } 328 329 while let Some(block) = self.stack.pop() { 330 match self.nodes[block].rpo_number { 331 SEEN => { 332 // This is the first time we pop the block, so we need to scan its successors and 333 // then revisit it. 334 self.nodes[block].rpo_number = DONE; 335 self.stack.push(block); 336 self.push_successors(func, block); 337 } 338 DONE => { 339 // This is the second time we pop the block, so all successors have been 340 // processed. 341 self.postorder.push(block); 342 } 343 _ => unreachable!(), 344 } 345 } 346 } 347 348 /// Push `block` successors onto `self.stack`, filtering out those that have already been seen. 349 /// 350 /// The successors are pushed in program order which is important to get a split-invariant 351 /// post-order. Split-invariant means that if a block is split in two, we get the same 352 /// post-order except for the insertion of the new block header at the split point. 353 fn push_successors(&mut self, func: &Function, block: Block) { 354 for inst in func.layout.block_likely_branches(block) { 355 match func.dfg.analyze_branch(inst) { 356 BranchInfo::SingleDest(succ, _) => self.push_if_unseen(succ), 357 BranchInfo::Table(jt, dest) => { 358 for succ in func.jump_tables[jt].iter() { 359 self.push_if_unseen(*succ); 360 } 361 if let Some(dest) = dest { 362 self.push_if_unseen(dest); 363 } 364 } 365 BranchInfo::NotABranch => {} 366 } 367 } 368 } 369 370 /// Push `block` onto `self.stack` if it has not already been seen. 371 fn push_if_unseen(&mut self, block: Block) { 372 if self.nodes[block].rpo_number == 0 { 373 self.nodes[block].rpo_number = SEEN; 374 self.stack.push(block); 375 } 376 } 377 378 /// Build a dominator tree from a control flow graph using Keith D. Cooper's 379 /// "Simple, Fast Dominator Algorithm." 380 fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) { 381 // During this algorithm, `rpo_number` has the following values: 382 // 383 // 0: block is not reachable. 384 // 1: block is reachable, but has not yet been visited during the first pass. This is set by 385 // `compute_postorder`. 386 // 2+: block is reachable and has an assigned RPO number. 387 388 // We'll be iterating over a reverse post-order of the CFG, skipping the entry block. 389 let (entry_block, postorder) = match self.postorder.as_slice().split_last() { 390 Some((&eb, rest)) => (eb, rest), 391 None => return, 392 }; 393 debug_assert_eq!(Some(entry_block), func.layout.entry_block()); 394 395 // Do a first pass where we assign RPO numbers to all reachable nodes. 396 self.nodes[entry_block].rpo_number = 2 * STRIDE; 397 for (rpo_idx, &block) in postorder.iter().rev().enumerate() { 398 // Update the current node and give it an RPO number. 399 // The entry block got 2, the rest start at 3 by multiples of STRIDE to leave 400 // room for future dominator tree modifications. 401 // 402 // Since `compute_idom` will only look at nodes with an assigned RPO number, the 403 // function will never see an uninitialized predecessor. 404 // 405 // Due to the nature of the post-order traversal, every node we visit will have at 406 // least one predecessor that has previously been visited during this RPO. 407 self.nodes[block] = DomNode { 408 idom: self.compute_idom(block, cfg, &func.layout).into(), 409 rpo_number: (rpo_idx as u32 + 3) * STRIDE, 410 } 411 } 412 413 // Now that we have RPO numbers for everything and initial immediate dominator estimates, 414 // iterate until convergence. 415 // 416 // If the function is free of irreducible control flow, this will exit after one iteration. 417 let mut changed = true; 418 while changed { 419 changed = false; 420 for &block in postorder.iter().rev() { 421 let idom = self.compute_idom(block, cfg, &func.layout).into(); 422 if self.nodes[block].idom != idom { 423 self.nodes[block].idom = idom; 424 changed = true; 425 } 426 } 427 } 428 } 429 430 // Compute the immediate dominator for `block` using the current `idom` states for the reachable 431 // nodes. 432 fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph, layout: &Layout) -> Inst { 433 // Get an iterator with just the reachable, already visited predecessors to `block`. 434 // Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't 435 // been visited yet, 0 for unreachable blocks. 436 let mut reachable_preds = cfg 437 .pred_iter(block) 438 .filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1); 439 440 // The RPO must visit at least one predecessor before this node. 441 let mut idom = reachable_preds 442 .next() 443 .expect("block node must have one reachable predecessor"); 444 445 for pred in reachable_preds { 446 idom = self.common_dominator(idom, pred, layout); 447 } 448 449 idom.inst 450 } 451 } 452 453 /// Optional pre-order information that can be computed for a dominator tree. 454 /// 455 /// This data structure is computed from a `DominatorTree` and provides: 456 /// 457 /// - A forward traversable dominator tree through the `children()` iterator. 458 /// - An ordering of blocks according to a dominator tree pre-order. 459 /// - Constant time dominance checks at the block granularity. 460 /// 461 /// The information in this auxiliary data structure is not easy to update when the control flow 462 /// graph changes, which is why it is kept separate. 463 pub struct DominatorTreePreorder { 464 nodes: SecondaryMap<Block, ExtraNode>, 465 466 // Scratch memory used by `compute_postorder()`. 467 stack: Vec<Block>, 468 } 469 470 #[derive(Default, Clone)] 471 struct ExtraNode { 472 /// First child node in the domtree. 473 child: PackedOption<Block>, 474 475 /// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO. 476 sibling: PackedOption<Block>, 477 478 /// Sequence number for this node in a pre-order traversal of the dominator tree. 479 /// Unreachable blocks have number 0, the entry block is 1. 480 pre_number: u32, 481 482 /// Maximum `pre_number` for the sub-tree of the dominator tree that is rooted at this node. 483 /// This is always >= `pre_number`. 484 pre_max: u32, 485 } 486 487 /// Creating and computing the dominator tree pre-order. 488 impl DominatorTreePreorder { 489 /// Create a new blank `DominatorTreePreorder`. 490 pub fn new() -> Self { 491 Self { 492 nodes: SecondaryMap::new(), 493 stack: Vec::new(), 494 } 495 } 496 497 /// Recompute this data structure to match `domtree`. 498 pub fn compute(&mut self, domtree: &DominatorTree, layout: &Layout) { 499 self.nodes.clear(); 500 debug_assert_eq!(self.stack.len(), 0); 501 502 // Step 1: Populate the child and sibling links. 503 // 504 // By following the CFG post-order and pushing to the front of the lists, we make sure that 505 // sibling lists are ordered according to the CFG reverse post-order. 506 for &block in domtree.cfg_postorder() { 507 if let Some(idom_inst) = domtree.idom(block) { 508 let idom = layout.pp_block(idom_inst); 509 let sib = mem::replace(&mut self.nodes[idom].child, block.into()); 510 self.nodes[block].sibling = sib; 511 } else { 512 // The only block without an immediate dominator is the entry. 513 self.stack.push(block); 514 } 515 } 516 517 // Step 2. Assign pre-order numbers from a DFS of the dominator tree. 518 debug_assert!(self.stack.len() <= 1); 519 let mut n = 0; 520 while let Some(block) = self.stack.pop() { 521 n += 1; 522 let node = &mut self.nodes[block]; 523 node.pre_number = n; 524 node.pre_max = n; 525 if let Some(n) = node.sibling.expand() { 526 self.stack.push(n); 527 } 528 if let Some(n) = node.child.expand() { 529 self.stack.push(n); 530 } 531 } 532 533 // Step 3. Propagate the `pre_max` numbers up the tree. 534 // The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all 535 // its dominator tree children. 536 for &block in domtree.cfg_postorder() { 537 if let Some(idom_inst) = domtree.idom(block) { 538 let idom = layout.pp_block(idom_inst); 539 let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max); 540 self.nodes[idom].pre_max = pre_max; 541 } 542 } 543 } 544 } 545 546 /// An iterator that enumerates the direct children of a block in the dominator tree. 547 pub struct ChildIter<'a> { 548 dtpo: &'a DominatorTreePreorder, 549 next: PackedOption<Block>, 550 } 551 552 impl<'a> Iterator for ChildIter<'a> { 553 type Item = Block; 554 555 fn next(&mut self) -> Option<Block> { 556 let n = self.next.expand(); 557 if let Some(block) = n { 558 self.next = self.dtpo.nodes[block].sibling; 559 } 560 n 561 } 562 } 563 564 /// Query interface for the dominator tree pre-order. 565 impl DominatorTreePreorder { 566 /// Get an iterator over the direct children of `block` in the dominator tree. 567 /// 568 /// These are the block's whose immediate dominator is an instruction in `block`, ordered according 569 /// to the CFG reverse post-order. 570 pub fn children(&self, block: Block) -> ChildIter { 571 ChildIter { 572 dtpo: self, 573 next: self.nodes[block].child, 574 } 575 } 576 577 /// Fast, constant time dominance check with block granularity. 578 /// 579 /// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant 580 /// time. This is less general than the `DominatorTree` method because it only works with block 581 /// program points. 582 /// 583 /// A block is considered to dominate itself. 584 pub fn dominates(&self, a: Block, b: Block) -> bool { 585 let na = &self.nodes[a]; 586 let nb = &self.nodes[b]; 587 na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max 588 } 589 590 /// Compare two blocks according to the dominator pre-order. 591 pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering { 592 self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number) 593 } 594 595 /// Compare two program points according to the dominator tree pre-order. 596 /// 597 /// This ordering of program points have the property that given a program point, pp, all the 598 /// program points dominated by pp follow immediately and contiguously after pp in the order. 599 pub fn pre_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering 600 where 601 A: Into<ExpandedProgramPoint>, 602 B: Into<ExpandedProgramPoint>, 603 { 604 let a = a.into(); 605 let b = b.into(); 606 self.pre_cmp_block(layout.pp_block(a), layout.pp_block(b)) 607 .then(layout.cmp(a, b)) 608 } 609 610 /// Compare two value defs according to the dominator tree pre-order. 611 /// 612 /// Two values defined at the same program point are compared according to their parameter or 613 /// result order. 614 /// 615 /// This is a total ordering of the values in the function. 616 pub fn pre_cmp_def(&self, a: Value, b: Value, func: &Function) -> Ordering { 617 let da = func.dfg.value_def(a); 618 let db = func.dfg.value_def(b); 619 self.pre_cmp(da, db, &func.layout) 620 .then_with(|| da.num().cmp(&db.num())) 621 } 622 } 623 624 #[cfg(test)] 625 mod tests { 626 use super::*; 627 use crate::cursor::{Cursor, FuncCursor}; 628 use crate::flowgraph::ControlFlowGraph; 629 use crate::ir::types::*; 630 use crate::ir::{Function, InstBuilder, TrapCode}; 631 632 #[test] 633 fn empty() { 634 let func = Function::new(); 635 let cfg = ControlFlowGraph::with_function(&func); 636 debug_assert!(cfg.is_valid()); 637 let dtree = DominatorTree::with_function(&func, &cfg); 638 assert_eq!(0, dtree.nodes.keys().count()); 639 assert_eq!(dtree.cfg_postorder(), &[]); 640 641 let mut dtpo = DominatorTreePreorder::new(); 642 dtpo.compute(&dtree, &func.layout); 643 } 644 645 #[test] 646 fn unreachable_node() { 647 let mut func = Function::new(); 648 let block0 = func.dfg.make_block(); 649 let v0 = func.dfg.append_block_param(block0, I32); 650 let block1 = func.dfg.make_block(); 651 let block2 = func.dfg.make_block(); 652 653 let mut cur = FuncCursor::new(&mut func); 654 655 cur.insert_block(block0); 656 cur.ins().brnz(v0, block2, &[]); 657 cur.ins().trap(TrapCode::User(0)); 658 659 cur.insert_block(block1); 660 let v1 = cur.ins().iconst(I32, 1); 661 let v2 = cur.ins().iadd(v0, v1); 662 cur.ins().jump(block0, &[v2]); 663 664 cur.insert_block(block2); 665 cur.ins().return_(&[v0]); 666 667 let cfg = ControlFlowGraph::with_function(cur.func); 668 let dt = DominatorTree::with_function(cur.func, &cfg); 669 670 // Fall-through-first, prune-at-source DFT: 671 // 672 // block0 { 673 // brnz block2 { 674 // trap 675 // block2 { 676 // return 677 // } block2 678 // } block0 679 assert_eq!(dt.cfg_postorder(), &[block2, block0]); 680 681 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst(); 682 assert!(!dt.dominates(v2_def, block0, &cur.func.layout)); 683 assert!(!dt.dominates(block0, v2_def, &cur.func.layout)); 684 685 let mut dtpo = DominatorTreePreorder::new(); 686 dtpo.compute(&dt, &cur.func.layout); 687 assert!(dtpo.dominates(block0, block0)); 688 assert!(!dtpo.dominates(block0, block1)); 689 assert!(dtpo.dominates(block0, block2)); 690 assert!(!dtpo.dominates(block1, block0)); 691 assert!(dtpo.dominates(block1, block1)); 692 assert!(!dtpo.dominates(block1, block2)); 693 assert!(!dtpo.dominates(block2, block0)); 694 assert!(!dtpo.dominates(block2, block1)); 695 assert!(dtpo.dominates(block2, block2)); 696 } 697 698 #[test] 699 fn non_zero_entry_block() { 700 let mut func = Function::new(); 701 let block0 = func.dfg.make_block(); 702 let block1 = func.dfg.make_block(); 703 let block2 = func.dfg.make_block(); 704 let block3 = func.dfg.make_block(); 705 let cond = func.dfg.append_block_param(block3, I32); 706 707 let mut cur = FuncCursor::new(&mut func); 708 709 cur.insert_block(block3); 710 let jmp_block3_block1 = cur.ins().jump(block1, &[]); 711 712 cur.insert_block(block1); 713 let br_block1_block0 = cur.ins().brnz(cond, block0, &[]); 714 let jmp_block1_block2 = cur.ins().jump(block2, &[]); 715 716 cur.insert_block(block2); 717 cur.ins().jump(block0, &[]); 718 719 cur.insert_block(block0); 720 721 let cfg = ControlFlowGraph::with_function(cur.func); 722 let dt = DominatorTree::with_function(cur.func, &cfg); 723 724 // Fall-through-first, prune-at-source DFT: 725 // 726 // block3 { 727 // block3:jump block1 { 728 // block1 { 729 // block1:brnz block0 { 730 // block1:jump block2 { 731 // block2 { 732 // block2:jump block0 (seen) 733 // } block2 734 // } block1:jump block2 735 // block0 { 736 // } block0 737 // } block1:brnz block0 738 // } block1 739 // } block3:jump block1 740 // } block3 741 742 assert_eq!(dt.cfg_postorder(), &[block2, block0, block1, block3]); 743 744 assert_eq!(cur.func.layout.entry_block().unwrap(), block3); 745 assert_eq!(dt.idom(block3), None); 746 assert_eq!(dt.idom(block1).unwrap(), jmp_block3_block1); 747 assert_eq!(dt.idom(block2).unwrap(), jmp_block1_block2); 748 assert_eq!(dt.idom(block0).unwrap(), br_block1_block0); 749 750 assert!(dt.dominates(br_block1_block0, br_block1_block0, &cur.func.layout)); 751 assert!(!dt.dominates(br_block1_block0, jmp_block3_block1, &cur.func.layout)); 752 assert!(dt.dominates(jmp_block3_block1, br_block1_block0, &cur.func.layout)); 753 754 assert_eq!( 755 dt.rpo_cmp(block3, block3, &cur.func.layout), 756 Ordering::Equal 757 ); 758 assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less); 759 assert_eq!( 760 dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout), 761 Ordering::Less 762 ); 763 assert_eq!( 764 dt.rpo_cmp(jmp_block3_block1, jmp_block1_block2, &cur.func.layout), 765 Ordering::Less 766 ); 767 } 768 769 #[test] 770 fn backwards_layout() { 771 let mut func = Function::new(); 772 let block0 = func.dfg.make_block(); 773 let block1 = func.dfg.make_block(); 774 let block2 = func.dfg.make_block(); 775 776 let mut cur = FuncCursor::new(&mut func); 777 778 cur.insert_block(block0); 779 let jmp02 = cur.ins().jump(block2, &[]); 780 781 cur.insert_block(block1); 782 let trap = cur.ins().trap(TrapCode::User(5)); 783 784 cur.insert_block(block2); 785 let jmp21 = cur.ins().jump(block1, &[]); 786 787 let cfg = ControlFlowGraph::with_function(cur.func); 788 let dt = DominatorTree::with_function(cur.func, &cfg); 789 790 assert_eq!(cur.func.layout.entry_block(), Some(block0)); 791 assert_eq!(dt.idom(block0), None); 792 assert_eq!(dt.idom(block1), Some(jmp21)); 793 assert_eq!(dt.idom(block2), Some(jmp02)); 794 795 assert!(dt.dominates(block0, block0, &cur.func.layout)); 796 assert!(dt.dominates(block0, jmp02, &cur.func.layout)); 797 assert!(dt.dominates(block0, block1, &cur.func.layout)); 798 assert!(dt.dominates(block0, trap, &cur.func.layout)); 799 assert!(dt.dominates(block0, block2, &cur.func.layout)); 800 assert!(dt.dominates(block0, jmp21, &cur.func.layout)); 801 802 assert!(!dt.dominates(jmp02, block0, &cur.func.layout)); 803 assert!(dt.dominates(jmp02, jmp02, &cur.func.layout)); 804 assert!(dt.dominates(jmp02, block1, &cur.func.layout)); 805 assert!(dt.dominates(jmp02, trap, &cur.func.layout)); 806 assert!(dt.dominates(jmp02, block2, &cur.func.layout)); 807 assert!(dt.dominates(jmp02, jmp21, &cur.func.layout)); 808 809 assert!(!dt.dominates(block1, block0, &cur.func.layout)); 810 assert!(!dt.dominates(block1, jmp02, &cur.func.layout)); 811 assert!(dt.dominates(block1, block1, &cur.func.layout)); 812 assert!(dt.dominates(block1, trap, &cur.func.layout)); 813 assert!(!dt.dominates(block1, block2, &cur.func.layout)); 814 assert!(!dt.dominates(block1, jmp21, &cur.func.layout)); 815 816 assert!(!dt.dominates(trap, block0, &cur.func.layout)); 817 assert!(!dt.dominates(trap, jmp02, &cur.func.layout)); 818 assert!(!dt.dominates(trap, block1, &cur.func.layout)); 819 assert!(dt.dominates(trap, trap, &cur.func.layout)); 820 assert!(!dt.dominates(trap, block2, &cur.func.layout)); 821 assert!(!dt.dominates(trap, jmp21, &cur.func.layout)); 822 823 assert!(!dt.dominates(block2, block0, &cur.func.layout)); 824 assert!(!dt.dominates(block2, jmp02, &cur.func.layout)); 825 assert!(dt.dominates(block2, block1, &cur.func.layout)); 826 assert!(dt.dominates(block2, trap, &cur.func.layout)); 827 assert!(dt.dominates(block2, block2, &cur.func.layout)); 828 assert!(dt.dominates(block2, jmp21, &cur.func.layout)); 829 830 assert!(!dt.dominates(jmp21, block0, &cur.func.layout)); 831 assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout)); 832 assert!(dt.dominates(jmp21, block1, &cur.func.layout)); 833 assert!(dt.dominates(jmp21, trap, &cur.func.layout)); 834 assert!(!dt.dominates(jmp21, block2, &cur.func.layout)); 835 assert!(dt.dominates(jmp21, jmp21, &cur.func.layout)); 836 } 837 } 838