Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/dominator_tree.rs
1693 views
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`.
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,
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
/// First child node in the domtree.
128
child: PackedOption<Block>,
129
130
/// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO.
131
sibling: PackedOption<Block>,
132
133
/// Sequence number for this node in a pre-order traversal of the dominator tree.
134
/// Unreachable blocks have number 0, the entry block is 1.
135
dom_pre_number: u32,
136
137
/// Maximum `dom_pre_number` for the sub-tree of the dominator tree that is rooted at this node.
138
/// This is always >= `dom_pre_number`.
139
dom_pre_max: u32,
140
}
141
142
/// The dominator tree for a single function,
143
/// computed using Semi-NCA algorithm.
144
pub struct DominatorTree {
145
/// DFS spanning tree.
146
stree: SpanningTree,
147
/// List of CFG blocks in postorder.
148
postorder: Vec<Block>,
149
/// Dominator tree nodes.
150
nodes: SecondaryMap<Block, DominatorTreeNode>,
151
152
/// Stack for building the spanning tree.
153
dfs_worklist: Vec<TraversalEvent>,
154
/// Stack used for processing semidominator paths
155
/// in link-eval procedure.
156
eval_worklist: Vec<u32>,
157
158
valid: bool,
159
}
160
161
/// Methods for querying the dominator tree.
162
impl DominatorTree {
163
/// Is `block` reachable from the entry block?
164
pub fn is_reachable(&self, block: Block) -> bool {
165
self.nodes[block].pre_number != NOT_VISITED
166
}
167
168
/// Get the CFG post-order of blocks that was used to compute the dominator tree.
169
///
170
/// Note that this post-order is not updated automatically when the CFG is modified. It is
171
/// computed from scratch and cached by `compute()`.
172
pub fn cfg_postorder(&self) -> &[Block] {
173
debug_assert!(self.is_valid());
174
&self.postorder
175
}
176
177
/// Get an iterator over CFG reverse post-order of blocks used to compute the dominator tree.
178
///
179
/// Note that the post-order is not updated automatically when the CFG is modified. It is
180
/// computed from scratch and cached by `compute()`.
181
pub fn cfg_rpo(&self) -> impl Iterator<Item = &Block> {
182
debug_assert!(self.is_valid());
183
self.postorder.iter().rev()
184
}
185
186
/// Returns the immediate dominator of `block`.
187
///
188
/// `block_a` is said to *dominate* `block_b` if all control flow paths from the function
189
/// entry to `block_b` must go through `block_a`.
190
///
191
/// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
192
/// also dominate the immediate dominator.
193
///
194
/// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
195
/// which has no dominators.
196
pub fn idom(&self, block: Block) -> Option<Block> {
197
self.nodes[block].idom.into()
198
}
199
200
/// Returns `true` if `a` dominates `b`.
201
///
202
/// This means that every control-flow path from the function entry to `b` must go through `a`.
203
///
204
/// Dominance is ill defined for unreachable blocks. This function can always determine
205
/// dominance for instructions in the same block, but otherwise returns `false` if either block
206
/// is unreachable.
207
///
208
/// An instruction is considered to dominate itself.
209
/// A block is also considered to dominate itself.
210
pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
211
where
212
A: Into<ProgramPoint>,
213
B: Into<ProgramPoint>,
214
{
215
let a = a.into();
216
let b = b.into();
217
match a {
218
ProgramPoint::Block(block_a) => match b {
219
ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
220
ProgramPoint::Inst(inst_b) => {
221
let block_b = layout
222
.inst_block(inst_b)
223
.expect("Instruction not in layout.");
224
self.block_dominates(block_a, block_b)
225
}
226
},
227
ProgramPoint::Inst(inst_a) => {
228
let block_a: Block = layout
229
.inst_block(inst_a)
230
.expect("Instruction not in layout.");
231
match b {
232
ProgramPoint::Block(block_b) => {
233
block_a != block_b && self.block_dominates(block_a, block_b)
234
}
235
ProgramPoint::Inst(inst_b) => {
236
let block_b = layout
237
.inst_block(inst_b)
238
.expect("Instruction not in layout.");
239
if block_a == block_b {
240
layout.pp_cmp(a, b) != Ordering::Greater
241
} else {
242
self.block_dominates(block_a, block_b)
243
}
244
}
245
}
246
}
247
}
248
}
249
250
/// Returns `true` if `block_a` dominates `block_b`.
251
///
252
/// A block is considered to dominate itself.
253
/// This uses preorder numbers for O(1) constant time performance.
254
pub fn block_dominates(&self, block_a: Block, block_b: Block) -> bool {
255
let na = &self.nodes[block_a];
256
let nb = &self.nodes[block_b];
257
na.dom_pre_number <= nb.dom_pre_number && na.dom_pre_max >= nb.dom_pre_max
258
}
259
260
/// Get an iterator over the direct children of `block` in the dominator tree.
261
///
262
/// These are the blocks whose immediate dominator is `block`, ordered according
263
/// to the CFG reverse post-order.
264
pub fn children(&self, block: Block) -> ChildIter<'_> {
265
ChildIter {
266
domtree: self,
267
next: self.nodes[block].child,
268
}
269
}
270
}
271
272
impl DominatorTree {
273
/// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a
274
/// function.
275
pub fn new() -> Self {
276
Self {
277
stree: SpanningTree::new(),
278
nodes: SecondaryMap::new(),
279
postorder: Vec::new(),
280
dfs_worklist: Vec::new(),
281
eval_worklist: Vec::new(),
282
valid: false,
283
}
284
}
285
286
/// Allocate and compute a dominator tree.
287
pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
288
let block_capacity = func.layout.block_capacity();
289
let mut domtree = Self {
290
stree: SpanningTree::with_capacity(block_capacity),
291
nodes: SecondaryMap::with_capacity(block_capacity),
292
postorder: Vec::with_capacity(block_capacity),
293
dfs_worklist: Vec::new(),
294
eval_worklist: Vec::new(),
295
valid: false,
296
};
297
domtree.compute(func, cfg);
298
domtree
299
}
300
301
/// Reset and compute a CFG post-order and dominator tree,
302
/// using Semi-NCA algorithm, described in the paper:
303
///
304
/// Linear-Time Algorithms for Dominators and Related Problems.
305
/// Loukas Georgiadis, Princeton University, November 2005.
306
///
307
/// The same algorithm is used by Julia, SpiderMonkey and LLVM,
308
/// the implementation is heavily inspired by them.
309
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
310
let _tt = timing::domtree();
311
debug_assert!(cfg.is_valid());
312
313
self.clear();
314
self.compute_spanning_tree(func);
315
self.compute_domtree(cfg);
316
self.compute_domtree_preorder();
317
318
self.valid = true;
319
}
320
321
/// Clear the data structures used to represent the dominator tree. This will leave the tree in
322
/// a state where `is_valid()` returns false.
323
pub fn clear(&mut self) {
324
self.stree.clear();
325
self.nodes.clear();
326
self.postorder.clear();
327
self.valid = false;
328
}
329
330
/// Check if the dominator tree is in a valid state.
331
///
332
/// Note that this doesn't perform any kind of validity checks. It simply checks if the
333
/// `compute()` method has been called since the last `clear()`. It does not check that the
334
/// dominator tree is consistent with the CFG.
335
pub fn is_valid(&self) -> bool {
336
self.valid
337
}
338
339
/// Reset all internal data structures, build spanning tree
340
/// and compute a post-order of the control flow graph.
341
fn compute_spanning_tree(&mut self, func: &Function) {
342
self.nodes.resize(func.dfg.num_blocks());
343
self.stree.reserve(func.dfg.num_blocks());
344
345
if let Some(block) = func.layout.entry_block() {
346
self.dfs_worklist.push(TraversalEvent::Enter(0, block));
347
}
348
349
loop {
350
match self.dfs_worklist.pop() {
351
Some(TraversalEvent::Enter(parent, block)) => {
352
let node = &mut self.nodes[block];
353
if node.pre_number != NOT_VISITED {
354
continue;
355
}
356
357
self.dfs_worklist.push(TraversalEvent::Exit(block));
358
359
let pre_number = self.stree.push(parent, block);
360
node.pre_number = pre_number;
361
362
// Use the same traversal heuristics as in traversals.rs.
363
self.dfs_worklist.extend(
364
func.block_successors(block)
365
// Heuristic: chase the children in reverse. This puts
366
// the first successor block first in the postorder, all
367
// other things being equal, which tends to prioritize
368
// loop backedges over out-edges, putting the edge-block
369
// closer to the loop body and minimizing live-ranges in
370
// linear instruction space. This heuristic doesn't have
371
// any effect on the computation of dominators, and is
372
// purely for other consumers of the postorder we cache
373
// here.
374
.rev()
375
// A simple optimization: push less items to the stack.
376
.filter(|successor| self.nodes[*successor].pre_number == NOT_VISITED)
377
.map(|successor| TraversalEvent::Enter(pre_number, successor)),
378
);
379
}
380
Some(TraversalEvent::Exit(block)) => self.postorder.push(block),
381
None => break,
382
}
383
}
384
}
385
386
/// Eval-link procedure from the paper.
387
/// For a predecessor V of node W returns V if V < W, otherwise the minimum of sdom(U),
388
/// where U > W and U is on a semi-dominator path for W in CFG.
389
/// Use path compression to bring complexity down to O(m*log(n)).
390
fn eval(&mut self, v: u32, last_linked: u32) -> u32 {
391
if self.stree[v].ancestor < last_linked {
392
return self.stree[v].label;
393
}
394
395
// Follow semi-dominator path.
396
let mut root = v;
397
loop {
398
self.eval_worklist.push(root);
399
root = self.stree[root].ancestor;
400
401
if self.stree[root].ancestor < last_linked {
402
break;
403
}
404
}
405
406
let mut prev = root;
407
let root = self.stree[prev].ancestor;
408
409
// Perform path compression. Point all ancestors to the root
410
// and propagate minimal sdom(U) value from ancestors to children.
411
while let Some(curr) = self.eval_worklist.pop() {
412
if self.stree[prev].label < self.stree[curr].label {
413
self.stree[curr].label = self.stree[prev].label;
414
}
415
416
self.stree[curr].ancestor = root;
417
prev = curr;
418
}
419
420
self.stree[v].label
421
}
422
423
fn compute_domtree(&mut self, cfg: &ControlFlowGraph) {
424
// Compute semi-dominators.
425
for w in (1..self.stree.len() as u32).rev() {
426
let w_node = &mut self.stree[w];
427
let block = w_node.block.expect("Virtual root must have been excluded");
428
let mut semi = w_node.ancestor;
429
430
let last_linked = w + 1;
431
432
for pred in cfg
433
.pred_iter(block)
434
.map(|pred: BlockPredecessor| pred.block)
435
{
436
// Skip unreachable nodes.
437
if self.nodes[pred].pre_number == NOT_VISITED {
438
continue;
439
}
440
441
let semi_candidate = self.eval(self.nodes[pred].pre_number, last_linked);
442
semi = std::cmp::min(semi, semi_candidate);
443
}
444
445
let w_node = &mut self.stree[w];
446
w_node.label = semi;
447
w_node.semi = semi;
448
}
449
450
// Compute immediate dominators.
451
for v in 1..self.stree.len() as u32 {
452
let semi = self.stree[v].semi;
453
let block = self.stree[v]
454
.block
455
.expect("Virtual root must have been excluded");
456
let mut idom = self.stree[v].idom;
457
458
while idom > semi {
459
idom = self.stree[idom].idom;
460
}
461
462
self.stree[v].idom = idom;
463
464
self.nodes[block].idom = self.stree[idom].block;
465
}
466
}
467
468
/// Compute dominator tree preorder information.
469
///
470
/// This populates child/sibling links and preorder numbers for fast dominance checks.
471
fn compute_domtree_preorder(&mut self) {
472
// Step 1: Populate the child and sibling links.
473
//
474
// By following the CFG post-order and pushing to the front of the lists, we make sure that
475
// sibling lists are ordered according to the CFG reverse post-order.
476
for &block in &self.postorder {
477
if let Some(idom) = self.idom(block) {
478
let sib = mem::replace(&mut self.nodes[idom].child, block.into());
479
self.nodes[block].sibling = sib;
480
} else {
481
// The only block without an immediate dominator is the entry.
482
self.dfs_worklist.push(TraversalEvent::Enter(0, block));
483
}
484
}
485
486
// Step 2. Assign pre-order numbers from a DFS of the dominator tree.
487
debug_assert!(self.dfs_worklist.len() <= 1);
488
let mut n = 0;
489
while let Some(event) = self.dfs_worklist.pop() {
490
if let TraversalEvent::Enter(_, block) = event {
491
n += 1;
492
let node = &mut self.nodes[block];
493
node.dom_pre_number = n;
494
node.dom_pre_max = n;
495
if let Some(sibling) = node.sibling.expand() {
496
self.dfs_worklist.push(TraversalEvent::Enter(0, sibling));
497
}
498
if let Some(child) = node.child.expand() {
499
self.dfs_worklist.push(TraversalEvent::Enter(0, child));
500
}
501
}
502
}
503
504
// Step 3. Propagate the `dom_pre_max` numbers up the tree.
505
// The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all
506
// its dominator tree children.
507
for &block in &self.postorder {
508
if let Some(idom) = self.idom(block) {
509
let pre_max = cmp::max(self.nodes[block].dom_pre_max, self.nodes[idom].dom_pre_max);
510
self.nodes[idom].dom_pre_max = pre_max;
511
}
512
}
513
}
514
}
515
516
/// An iterator that enumerates the direct children of a block in the dominator tree.
517
pub struct ChildIter<'a> {
518
domtree: &'a DominatorTree,
519
next: PackedOption<Block>,
520
}
521
522
impl<'a> Iterator for ChildIter<'a> {
523
type Item = Block;
524
525
fn next(&mut self) -> Option<Block> {
526
let n = self.next.expand();
527
if let Some(block) = n {
528
self.next = self.domtree.nodes[block].sibling;
529
}
530
n
531
}
532
}
533
534
#[cfg(test)]
535
mod tests {
536
use super::*;
537
use crate::cursor::{Cursor, FuncCursor};
538
use crate::ir::types::*;
539
use crate::ir::{InstBuilder, TrapCode};
540
541
#[test]
542
fn empty() {
543
let func = Function::new();
544
let cfg = ControlFlowGraph::with_function(&func);
545
debug_assert!(cfg.is_valid());
546
let dtree = DominatorTree::with_function(&func, &cfg);
547
assert_eq!(0, dtree.nodes.keys().count());
548
assert_eq!(dtree.cfg_postorder(), &[]);
549
}
550
551
#[test]
552
fn unreachable_node() {
553
let mut func = Function::new();
554
let block0 = func.dfg.make_block();
555
let v0 = func.dfg.append_block_param(block0, I32);
556
let block1 = func.dfg.make_block();
557
let block2 = func.dfg.make_block();
558
let trap_block = func.dfg.make_block();
559
560
let mut cur = FuncCursor::new(&mut func);
561
562
cur.insert_block(block0);
563
cur.ins().brif(v0, block2, &[], trap_block, &[]);
564
565
cur.insert_block(trap_block);
566
cur.ins().trap(TrapCode::unwrap_user(1));
567
568
cur.insert_block(block1);
569
let v1 = cur.ins().iconst(I32, 1);
570
let v2 = cur.ins().iadd(v0, v1);
571
cur.ins().jump(block0, &[v2.into()]);
572
573
cur.insert_block(block2);
574
cur.ins().return_(&[v0]);
575
576
let cfg = ControlFlowGraph::with_function(cur.func);
577
let dt = DominatorTree::with_function(cur.func, &cfg);
578
579
// Fall-through-first, prune-at-source DFT:
580
//
581
// block0 {
582
// brif block2 {
583
// trap
584
// block2 {
585
// return
586
// } block2
587
// } block0
588
assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
589
590
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
591
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
592
assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
593
594
assert!(dt.block_dominates(block0, block0));
595
assert!(!dt.block_dominates(block0, block1));
596
assert!(dt.block_dominates(block0, block2));
597
assert!(!dt.block_dominates(block1, block0));
598
assert!(dt.block_dominates(block1, block1));
599
assert!(!dt.block_dominates(block1, block2));
600
assert!(!dt.block_dominates(block2, block0));
601
assert!(!dt.block_dominates(block2, block1));
602
assert!(dt.block_dominates(block2, block2));
603
}
604
605
#[test]
606
fn non_zero_entry_block() {
607
let mut func = Function::new();
608
let block0 = func.dfg.make_block();
609
let block1 = func.dfg.make_block();
610
let block2 = func.dfg.make_block();
611
let block3 = func.dfg.make_block();
612
let cond = func.dfg.append_block_param(block3, I32);
613
614
let mut cur = FuncCursor::new(&mut func);
615
616
cur.insert_block(block3);
617
let jmp_block3_block1 = cur.ins().jump(block1, &[]);
618
619
cur.insert_block(block1);
620
let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
621
622
cur.insert_block(block2);
623
cur.ins().jump(block0, &[]);
624
625
cur.insert_block(block0);
626
627
let cfg = ControlFlowGraph::with_function(cur.func);
628
let dt = DominatorTree::with_function(cur.func, &cfg);
629
630
// Fall-through-first, prune-at-source DFT:
631
//
632
// block3 {
633
// block3:jump block1 {
634
// block1 {
635
// block1:brif block0 {
636
// block1:jump block2 {
637
// block2 {
638
// block2:jump block0 (seen)
639
// } block2
640
// } block1:jump block2
641
// block0 {
642
// } block0
643
// } block1:brif block0
644
// } block1
645
// } block3:jump block1
646
// } block3
647
648
assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
649
650
assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
651
assert_eq!(dt.idom(block3), None);
652
assert_eq!(dt.idom(block1).unwrap(), block3);
653
assert_eq!(dt.idom(block2).unwrap(), block1);
654
assert_eq!(dt.idom(block0).unwrap(), block1);
655
656
assert!(dt.dominates(
657
br_block1_block0_block2,
658
br_block1_block0_block2,
659
&cur.func.layout
660
));
661
assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
662
assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
663
}
664
665
#[test]
666
fn backwards_layout() {
667
let mut func = Function::new();
668
let block0 = func.dfg.make_block();
669
let block1 = func.dfg.make_block();
670
let block2 = func.dfg.make_block();
671
672
let mut cur = FuncCursor::new(&mut func);
673
674
cur.insert_block(block0);
675
let jmp02 = cur.ins().jump(block2, &[]);
676
677
cur.insert_block(block1);
678
let trap = cur.ins().trap(TrapCode::unwrap_user(5));
679
680
cur.insert_block(block2);
681
let jmp21 = cur.ins().jump(block1, &[]);
682
683
let cfg = ControlFlowGraph::with_function(cur.func);
684
let dt = DominatorTree::with_function(cur.func, &cfg);
685
686
assert_eq!(cur.func.layout.entry_block(), Some(block0));
687
assert_eq!(dt.idom(block0), None);
688
assert_eq!(dt.idom(block1), Some(block2));
689
assert_eq!(dt.idom(block2), Some(block0));
690
691
assert!(dt.dominates(block0, block0, &cur.func.layout));
692
assert!(dt.dominates(block0, jmp02, &cur.func.layout));
693
assert!(dt.dominates(block0, block1, &cur.func.layout));
694
assert!(dt.dominates(block0, trap, &cur.func.layout));
695
assert!(dt.dominates(block0, block2, &cur.func.layout));
696
assert!(dt.dominates(block0, jmp21, &cur.func.layout));
697
698
assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
699
assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
700
assert!(dt.dominates(jmp02, block1, &cur.func.layout));
701
assert!(dt.dominates(jmp02, trap, &cur.func.layout));
702
assert!(dt.dominates(jmp02, block2, &cur.func.layout));
703
assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
704
705
assert!(!dt.dominates(block1, block0, &cur.func.layout));
706
assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
707
assert!(dt.dominates(block1, block1, &cur.func.layout));
708
assert!(dt.dominates(block1, trap, &cur.func.layout));
709
assert!(!dt.dominates(block1, block2, &cur.func.layout));
710
assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
711
712
assert!(!dt.dominates(trap, block0, &cur.func.layout));
713
assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
714
assert!(!dt.dominates(trap, block1, &cur.func.layout));
715
assert!(dt.dominates(trap, trap, &cur.func.layout));
716
assert!(!dt.dominates(trap, block2, &cur.func.layout));
717
assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
718
719
assert!(!dt.dominates(block2, block0, &cur.func.layout));
720
assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
721
assert!(dt.dominates(block2, block1, &cur.func.layout));
722
assert!(dt.dominates(block2, trap, &cur.func.layout));
723
assert!(dt.dominates(block2, block2, &cur.func.layout));
724
assert!(dt.dominates(block2, jmp21, &cur.func.layout));
725
726
assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
727
assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
728
assert!(dt.dominates(jmp21, block1, &cur.func.layout));
729
assert!(dt.dominates(jmp21, trap, &cur.func.layout));
730
assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
731
assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
732
}
733
734
#[test]
735
fn insts_same_block() {
736
let mut func = Function::new();
737
let block0 = func.dfg.make_block();
738
739
let mut cur = FuncCursor::new(&mut func);
740
741
cur.insert_block(block0);
742
let v1 = cur.ins().iconst(I32, 1);
743
let v2 = cur.ins().iadd(v1, v1);
744
let v3 = cur.ins().iadd(v2, v2);
745
cur.ins().return_(&[]);
746
747
let cfg = ControlFlowGraph::with_function(cur.func);
748
let dt = DominatorTree::with_function(cur.func, &cfg);
749
750
let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
751
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
752
let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
753
754
assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
755
assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
756
assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
757
758
assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
759
assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
760
assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
761
762
assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
763
assert!(dt.dominates(block0, block0, &cur.func.layout));
764
765
assert!(dt.dominates(block0, v1_def, &cur.func.layout));
766
assert!(dt.dominates(block0, v2_def, &cur.func.layout));
767
assert!(dt.dominates(block0, v3_def, &cur.func.layout));
768
769
assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
770
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
771
assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
772
}
773
}
774
775