Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/dominator_tree/simple.rs
1693 views
1
//! A Dominator Tree represented as mappings of Blocks to their immediate dominator.
2
//! Computed using Keith D. Cooper's "Simple, Fast Dominator Algorithm."
3
//! This version have been used in Cranelift for a very long time
4
//! and should be quite stable. Used as a baseline i.e. in verification.
5
6
use crate::entity::SecondaryMap;
7
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
8
use crate::ir::{Block, Function, Layout, ProgramPoint};
9
use crate::packed_option::PackedOption;
10
use crate::timing;
11
use crate::traversals::Dfs;
12
use alloc::vec::Vec;
13
use core::cmp::Ordering;
14
15
/// RPO numbers are not first assigned in a contiguous way but as multiples of STRIDE, to leave
16
/// room for modifications of the dominator tree.
17
const STRIDE: u32 = 4;
18
19
/// Dominator tree node. We keep one of these per block.
20
#[derive(Clone, Default)]
21
struct DomNode {
22
/// Number of this node in a reverse post-order traversal of the CFG, starting from 1.
23
/// This number is monotonic in the reverse postorder but not contiguous, since we leave
24
/// holes for later localized modifications of the dominator tree.
25
/// Unreachable nodes get number 0, all others are positive.
26
rpo_number: u32,
27
28
/// The immediate dominator of this block.
29
///
30
/// This is `None` for unreachable blocks and the entry block which doesn't have an immediate
31
/// dominator.
32
idom: PackedOption<Block>,
33
}
34
35
/// The dominator tree for a single function.
36
pub struct SimpleDominatorTree {
37
nodes: SecondaryMap<Block, DomNode>,
38
39
/// CFG post-order of all reachable blocks.
40
postorder: Vec<Block>,
41
42
/// Scratch traversal state used by `compute_postorder()`.
43
dfs: Dfs,
44
45
valid: bool,
46
}
47
48
/// Methods for querying the dominator tree.
49
impl SimpleDominatorTree {
50
/// Is `block` reachable from the entry block?
51
pub fn is_reachable(&self, block: Block) -> bool {
52
self.nodes[block].rpo_number != 0
53
}
54
55
/// Get the CFG post-order of blocks that was used to compute the dominator tree.
56
///
57
/// Note that this post-order is not updated automatically when the CFG is modified. It is
58
/// computed from scratch and cached by `compute()`.
59
pub fn cfg_postorder(&self) -> &[Block] {
60
debug_assert!(self.is_valid());
61
&self.postorder
62
}
63
64
/// Returns the immediate dominator of `block`.
65
///
66
/// `block_a` is said to *dominate* `block_b` if all control flow paths from the function
67
/// entry to `block_b` must go through `block_a`.
68
///
69
/// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
70
/// also dominate the immediate dominator.
71
///
72
/// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
73
/// which has no dominators.
74
pub fn idom(&self, block: Block) -> Option<Block> {
75
self.nodes[block].idom.into()
76
}
77
78
/// Compare two blocks relative to the reverse post-order.
79
pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {
80
self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
81
}
82
83
/// Compare two program points relative to a reverse post-order traversal of the control-flow
84
/// graph.
85
///
86
/// Return `Ordering::Less` if `a` comes before `b` in the RPO.
87
///
88
/// If `a` and `b` belong to the same block, compare their relative position in the block.
89
pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
90
where
91
A: Into<ProgramPoint>,
92
B: Into<ProgramPoint>,
93
{
94
let a = a.into();
95
let b = b.into();
96
self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))
97
.then_with(|| layout.pp_cmp(a, b))
98
}
99
100
/// Returns `true` if `a` dominates `b`.
101
///
102
/// This means that every control-flow path from the function entry to `b` must go through `a`.
103
///
104
/// Dominance is ill defined for unreachable blocks. This function can always determine
105
/// dominance for instructions in the same block, but otherwise returns `false` if either block
106
/// is unreachable.
107
///
108
/// An instruction is considered to dominate itself.
109
/// A block is also considered to dominate itself.
110
pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
111
where
112
A: Into<ProgramPoint>,
113
B: Into<ProgramPoint>,
114
{
115
let a = a.into();
116
let b = b.into();
117
match a {
118
ProgramPoint::Block(block_a) => match b {
119
ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
120
ProgramPoint::Inst(inst_b) => {
121
let block_b = layout
122
.inst_block(inst_b)
123
.expect("Instruction not in layout.");
124
self.block_dominates(block_a, block_b)
125
}
126
},
127
ProgramPoint::Inst(inst_a) => {
128
let block_a: Block = layout
129
.inst_block(inst_a)
130
.expect("Instruction not in layout.");
131
match b {
132
ProgramPoint::Block(block_b) => {
133
block_a != block_b && self.block_dominates(block_a, block_b)
134
}
135
ProgramPoint::Inst(inst_b) => {
136
let block_b = layout
137
.inst_block(inst_b)
138
.expect("Instruction not in layout.");
139
if block_a == block_b {
140
layout.pp_cmp(a, b) != Ordering::Greater
141
} else {
142
self.block_dominates(block_a, block_b)
143
}
144
}
145
}
146
}
147
}
148
}
149
150
/// Returns `true` if `block_a` dominates `block_b`.
151
///
152
/// A block is considered to dominate itself.
153
fn block_dominates(&self, block_a: Block, mut block_b: Block) -> bool {
154
let rpo_a = self.nodes[block_a].rpo_number;
155
156
// Run a finger up the dominator tree from b until we see a.
157
// Do nothing if b is unreachable.
158
while rpo_a < self.nodes[block_b].rpo_number {
159
let idom = match self.idom(block_b) {
160
Some(idom) => idom,
161
None => return false, // a is unreachable, so we climbed past the entry
162
};
163
block_b = idom;
164
}
165
166
block_a == block_b
167
}
168
169
/// Compute the common dominator of two basic blocks.
170
///
171
/// Both basic blocks are assumed to be reachable.
172
fn common_dominator(&self, mut a: Block, mut b: Block) -> Block {
173
loop {
174
match self.rpo_cmp_block(a, b) {
175
Ordering::Less => {
176
// `a` comes before `b` in the RPO. Move `b` up.
177
let idom = self.nodes[b].idom.expect("Unreachable basic block?");
178
b = idom;
179
}
180
Ordering::Greater => {
181
// `b` comes before `a` in the RPO. Move `a` up.
182
let idom = self.nodes[a].idom.expect("Unreachable basic block?");
183
a = idom;
184
}
185
Ordering::Equal => break,
186
}
187
}
188
189
debug_assert_eq!(a, b, "Unreachable block passed to common_dominator?");
190
191
a
192
}
193
}
194
195
impl SimpleDominatorTree {
196
/// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a
197
/// function.
198
pub fn new() -> Self {
199
Self {
200
nodes: SecondaryMap::new(),
201
postorder: Vec::new(),
202
dfs: Dfs::new(),
203
valid: false,
204
}
205
}
206
207
/// Allocate and compute a dominator tree.
208
pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
209
let block_capacity = func.layout.block_capacity();
210
let mut domtree = Self {
211
nodes: SecondaryMap::with_capacity(block_capacity),
212
postorder: Vec::with_capacity(block_capacity),
213
dfs: Dfs::new(),
214
valid: false,
215
};
216
domtree.compute(func, cfg);
217
domtree
218
}
219
220
/// Reset and compute a CFG post-order and dominator tree.
221
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
222
let _tt = timing::domtree();
223
debug_assert!(cfg.is_valid());
224
self.compute_postorder(func);
225
self.compute_domtree(func, cfg);
226
self.valid = true;
227
}
228
229
/// Clear the data structures used to represent the dominator tree. This will leave the tree in
230
/// a state where `is_valid()` returns false.
231
pub fn clear(&mut self) {
232
self.nodes.clear();
233
self.postorder.clear();
234
self.valid = false;
235
}
236
237
/// Check if the dominator tree is in a valid state.
238
///
239
/// Note that this doesn't perform any kind of validity checks. It simply checks if the
240
/// `compute()` method has been called since the last `clear()`. It does not check that the
241
/// dominator tree is consistent with the CFG.
242
pub fn is_valid(&self) -> bool {
243
self.valid
244
}
245
246
/// Reset all internal data structures and compute a post-order of the control flow graph.
247
///
248
/// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones.
249
fn compute_postorder(&mut self, func: &Function) {
250
self.clear();
251
self.nodes.resize(func.dfg.num_blocks());
252
self.postorder.extend(self.dfs.post_order_iter(func));
253
}
254
255
/// Build a dominator tree from a control flow graph using Keith D. Cooper's
256
/// "Simple, Fast Dominator Algorithm."
257
fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {
258
// During this algorithm, `rpo_number` has the following values:
259
//
260
// 0: block is not reachable.
261
// 1: block is reachable, but has not yet been visited during the first pass. This is set by
262
// `compute_postorder`.
263
// 2+: block is reachable and has an assigned RPO number.
264
265
// We'll be iterating over a reverse post-order of the CFG, skipping the entry block.
266
let (entry_block, postorder) = match self.postorder.as_slice().split_last() {
267
Some((&eb, rest)) => (eb, rest),
268
None => return,
269
};
270
debug_assert_eq!(Some(entry_block), func.layout.entry_block());
271
272
// Do a first pass where we assign RPO numbers to all reachable nodes.
273
self.nodes[entry_block].rpo_number = 2 * STRIDE;
274
for (rpo_idx, &block) in postorder.iter().rev().enumerate() {
275
// Update the current node and give it an RPO number.
276
// The entry block got 2, the rest start at 3 by multiples of STRIDE to leave
277
// room for future dominator tree modifications.
278
//
279
// Since `compute_idom` will only look at nodes with an assigned RPO number, the
280
// function will never see an uninitialized predecessor.
281
//
282
// Due to the nature of the post-order traversal, every node we visit will have at
283
// least one predecessor that has previously been visited during this RPO.
284
self.nodes[block] = DomNode {
285
idom: self.compute_idom(block, cfg).into(),
286
rpo_number: (rpo_idx as u32 + 3) * STRIDE,
287
}
288
}
289
290
// Now that we have RPO numbers for everything and initial immediate dominator estimates,
291
// iterate until convergence.
292
//
293
// If the function is free of irreducible control flow, this will exit after one iteration.
294
let mut changed = true;
295
while changed {
296
changed = false;
297
for &block in postorder.iter().rev() {
298
let idom = self.compute_idom(block, cfg).into();
299
if self.nodes[block].idom != idom {
300
self.nodes[block].idom = idom;
301
changed = true;
302
}
303
}
304
}
305
}
306
307
// Compute the immediate dominator for `block` using the current `idom` states for the reachable
308
// nodes.
309
fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph) -> Block {
310
// Get an iterator with just the reachable, already visited predecessors to `block`.
311
// Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
312
// been visited yet, 0 for unreachable blocks.
313
let mut reachable_preds = cfg
314
.pred_iter(block)
315
.filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1)
316
.map(|pred| pred.block);
317
318
// The RPO must visit at least one predecessor before this node.
319
let mut idom = reachable_preds
320
.next()
321
.expect("block node must have one reachable predecessor");
322
323
for pred in reachable_preds {
324
idom = self.common_dominator(idom, pred);
325
}
326
327
idom
328
}
329
}
330
331
#[cfg(test)]
332
mod tests {
333
use super::*;
334
use crate::cursor::{Cursor, FuncCursor};
335
use crate::ir::types::*;
336
use crate::ir::{InstBuilder, TrapCode};
337
338
#[test]
339
fn empty() {
340
let func = Function::new();
341
let cfg = ControlFlowGraph::with_function(&func);
342
debug_assert!(cfg.is_valid());
343
let dtree = SimpleDominatorTree::with_function(&func, &cfg);
344
assert_eq!(0, dtree.nodes.keys().count());
345
assert_eq!(dtree.cfg_postorder(), &[]);
346
}
347
348
#[test]
349
fn unreachable_node() {
350
let mut func = Function::new();
351
let block0 = func.dfg.make_block();
352
let v0 = func.dfg.append_block_param(block0, I32);
353
let block1 = func.dfg.make_block();
354
let block2 = func.dfg.make_block();
355
let trap_block = func.dfg.make_block();
356
357
let mut cur = FuncCursor::new(&mut func);
358
359
cur.insert_block(block0);
360
cur.ins().brif(v0, block2, &[], trap_block, &[]);
361
362
cur.insert_block(trap_block);
363
cur.ins().trap(TrapCode::unwrap_user(1));
364
365
cur.insert_block(block1);
366
let v1 = cur.ins().iconst(I32, 1);
367
let v2 = cur.ins().iadd(v0, v1);
368
cur.ins().jump(block0, &[v2.into()]);
369
370
cur.insert_block(block2);
371
cur.ins().return_(&[v0]);
372
373
let cfg = ControlFlowGraph::with_function(cur.func);
374
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
375
376
// Fall-through-first, prune-at-source DFT:
377
//
378
// block0 {
379
// brif block2 {
380
// trap
381
// block2 {
382
// return
383
// } block2
384
// } block0
385
assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
386
387
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
388
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
389
assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
390
391
assert!(dt.dominates(block0, block0, &cur.func.layout));
392
assert!(!dt.dominates(block0, block1, &cur.func.layout));
393
assert!(dt.dominates(block0, block2, &cur.func.layout));
394
assert!(!dt.dominates(block1, block0, &cur.func.layout));
395
assert!(dt.dominates(block1, block1, &cur.func.layout));
396
assert!(!dt.dominates(block1, block2, &cur.func.layout));
397
assert!(!dt.dominates(block2, block0, &cur.func.layout));
398
assert!(!dt.dominates(block2, block1, &cur.func.layout));
399
assert!(dt.dominates(block2, block2, &cur.func.layout));
400
}
401
402
#[test]
403
fn non_zero_entry_block() {
404
let mut func = Function::new();
405
let block0 = func.dfg.make_block();
406
let block1 = func.dfg.make_block();
407
let block2 = func.dfg.make_block();
408
let block3 = func.dfg.make_block();
409
let cond = func.dfg.append_block_param(block3, I32);
410
411
let mut cur = FuncCursor::new(&mut func);
412
413
cur.insert_block(block3);
414
let jmp_block3_block1 = cur.ins().jump(block1, &[]);
415
416
cur.insert_block(block1);
417
let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
418
419
cur.insert_block(block2);
420
cur.ins().jump(block0, &[]);
421
422
cur.insert_block(block0);
423
424
let cfg = ControlFlowGraph::with_function(cur.func);
425
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
426
427
// Fall-through-first, prune-at-source DFT:
428
//
429
// block3 {
430
// block3:jump block1 {
431
// block1 {
432
// block1:brif block0 {
433
// block1:jump block2 {
434
// block2 {
435
// block2:jump block0 (seen)
436
// } block2
437
// } block1:jump block2
438
// block0 {
439
// } block0
440
// } block1:brif block0
441
// } block1
442
// } block3:jump block1
443
// } block3
444
445
assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
446
447
assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
448
assert_eq!(dt.idom(block3), None);
449
assert_eq!(dt.idom(block1).unwrap(), block3);
450
assert_eq!(dt.idom(block2).unwrap(), block1);
451
assert_eq!(dt.idom(block0).unwrap(), block1);
452
453
assert!(dt.dominates(
454
br_block1_block0_block2,
455
br_block1_block0_block2,
456
&cur.func.layout
457
));
458
assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
459
assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
460
461
assert_eq!(
462
dt.rpo_cmp(block3, block3, &cur.func.layout),
463
Ordering::Equal
464
);
465
assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);
466
assert_eq!(
467
dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),
468
Ordering::Less
469
);
470
assert_eq!(
471
dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),
472
Ordering::Less
473
);
474
}
475
476
#[test]
477
fn backwards_layout() {
478
let mut func = Function::new();
479
let block0 = func.dfg.make_block();
480
let block1 = func.dfg.make_block();
481
let block2 = func.dfg.make_block();
482
483
let mut cur = FuncCursor::new(&mut func);
484
485
cur.insert_block(block0);
486
let jmp02 = cur.ins().jump(block2, &[]);
487
488
cur.insert_block(block1);
489
let trap = cur.ins().trap(TrapCode::unwrap_user(5));
490
491
cur.insert_block(block2);
492
let jmp21 = cur.ins().jump(block1, &[]);
493
494
let cfg = ControlFlowGraph::with_function(cur.func);
495
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
496
497
assert_eq!(cur.func.layout.entry_block(), Some(block0));
498
assert_eq!(dt.idom(block0), None);
499
assert_eq!(dt.idom(block1), Some(block2));
500
assert_eq!(dt.idom(block2), Some(block0));
501
502
assert!(dt.dominates(block0, block0, &cur.func.layout));
503
assert!(dt.dominates(block0, jmp02, &cur.func.layout));
504
assert!(dt.dominates(block0, block1, &cur.func.layout));
505
assert!(dt.dominates(block0, trap, &cur.func.layout));
506
assert!(dt.dominates(block0, block2, &cur.func.layout));
507
assert!(dt.dominates(block0, jmp21, &cur.func.layout));
508
509
assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
510
assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
511
assert!(dt.dominates(jmp02, block1, &cur.func.layout));
512
assert!(dt.dominates(jmp02, trap, &cur.func.layout));
513
assert!(dt.dominates(jmp02, block2, &cur.func.layout));
514
assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
515
516
assert!(!dt.dominates(block1, block0, &cur.func.layout));
517
assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
518
assert!(dt.dominates(block1, block1, &cur.func.layout));
519
assert!(dt.dominates(block1, trap, &cur.func.layout));
520
assert!(!dt.dominates(block1, block2, &cur.func.layout));
521
assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
522
523
assert!(!dt.dominates(trap, block0, &cur.func.layout));
524
assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
525
assert!(!dt.dominates(trap, block1, &cur.func.layout));
526
assert!(dt.dominates(trap, trap, &cur.func.layout));
527
assert!(!dt.dominates(trap, block2, &cur.func.layout));
528
assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
529
530
assert!(!dt.dominates(block2, block0, &cur.func.layout));
531
assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
532
assert!(dt.dominates(block2, block1, &cur.func.layout));
533
assert!(dt.dominates(block2, trap, &cur.func.layout));
534
assert!(dt.dominates(block2, block2, &cur.func.layout));
535
assert!(dt.dominates(block2, jmp21, &cur.func.layout));
536
537
assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
538
assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
539
assert!(dt.dominates(jmp21, block1, &cur.func.layout));
540
assert!(dt.dominates(jmp21, trap, &cur.func.layout));
541
assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
542
assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
543
}
544
545
#[test]
546
fn insts_same_block() {
547
let mut func = Function::new();
548
let block0 = func.dfg.make_block();
549
550
let mut cur = FuncCursor::new(&mut func);
551
552
cur.insert_block(block0);
553
let v1 = cur.ins().iconst(I32, 1);
554
let v2 = cur.ins().iadd(v1, v1);
555
let v3 = cur.ins().iadd(v2, v2);
556
cur.ins().return_(&[]);
557
558
let cfg = ControlFlowGraph::with_function(cur.func);
559
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
560
561
let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
562
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
563
let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
564
565
assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
566
assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
567
assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
568
569
assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
570
assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
571
assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
572
573
assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
574
assert!(dt.dominates(block0, block0, &cur.func.layout));
575
576
assert!(dt.dominates(block0, v1_def, &cur.func.layout));
577
assert!(dt.dominates(block0, v2_def, &cur.func.layout));
578
assert!(dt.dominates(block0, v3_def, &cur.func.layout));
579
580
assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
581
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
582
assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
583
}
584
}
585
586