Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/filetests/src/test_domtree.rs
3068 views
1
//! Test command for verifying dominator trees.
2
//!
3
//! The `test domtree` test command looks for annotations on instructions like this:
4
//!
5
//! ```clif
6
//! jump block3 ; dominates: block3
7
//! ```
8
//!
9
//! This annotation means that the jump instruction is expected to be the immediate dominator of
10
//! `block3`.
11
//!
12
//! We verify that the dominator tree annotations are complete and correct.
13
//!
14
15
use crate::match_directive::match_directive;
16
use crate::subtest::{Context, SubTest, run_filecheck};
17
use cranelift_codegen::dominator_tree::DominatorTree;
18
use cranelift_codegen::flowgraph::ControlFlowGraph;
19
use cranelift_codegen::ir::Function;
20
use cranelift_codegen::ir::entities::AnyEntity;
21
use cranelift_reader::TestCommand;
22
use std::borrow::{Borrow, Cow};
23
use std::collections::HashMap;
24
use std::fmt::{self, Write};
25
26
struct TestDomtree;
27
28
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
29
assert_eq!(parsed.command, "domtree");
30
if !parsed.options.is_empty() {
31
anyhow::bail!("No options allowed on {parsed}")
32
}
33
Ok(Box::new(TestDomtree))
34
}
35
36
impl SubTest for TestDomtree {
37
fn name(&self) -> &'static str {
38
"domtree"
39
}
40
41
// Extract our own dominator tree from
42
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
43
let func = func.borrow();
44
let cfg = ControlFlowGraph::with_function(func);
45
let domtree = DominatorTree::with_function(func, &cfg);
46
47
// Build an expected domtree from the source annotations.
48
let mut expected = HashMap::new();
49
for comment in &context.details.comments {
50
if let Some(tail) = match_directive(comment.text, "dominates:") {
51
let inst = match comment.entity {
52
AnyEntity::Inst(inst) => inst,
53
_ => {
54
anyhow::bail!(
55
"annotation on non-inst {}: {}",
56
comment.entity,
57
comment.text
58
);
59
}
60
};
61
62
let expected_block = match func.layout.inst_block(inst) {
63
Some(expected_block) => expected_block,
64
_ => anyhow::bail!("instruction {inst} is not in layout"),
65
};
66
for src_block in tail.split_whitespace() {
67
let block = match context.details.map.lookup_str(src_block) {
68
Some(AnyEntity::Block(block)) => block,
69
_ => anyhow::bail!("expected defined block, got {src_block}"),
70
};
71
72
// Annotations say that `expected_block` is the idom of `block`.
73
if expected.insert(block, expected_block).is_some() {
74
anyhow::bail!("multiple dominators for {src_block}");
75
}
76
77
// Compare to computed domtree.
78
match domtree.idom(block) {
79
Some(got_block) if got_block != expected_block => {
80
anyhow::bail!(
81
"mismatching idoms for {src_block}:\n\
82
want: {inst}, got: {got_block}"
83
);
84
}
85
None => {
86
anyhow::bail!(
87
"mismatching idoms for {src_block}:\n\
88
want: {inst}, got: unreachable"
89
);
90
}
91
_ => {}
92
}
93
}
94
}
95
}
96
97
// Now we know that everything in `expected` is consistent with `domtree`.
98
// All other block's should be either unreachable or the entry block.
99
for block in func
100
.layout
101
.blocks()
102
.skip(1)
103
.filter(|block| !expected.contains_key(block))
104
{
105
if let Some(got_block) = domtree.idom(block) {
106
anyhow::bail!(
107
"mismatching idoms for renumbered {block}:\n\
108
want: unreachable, got: {got_block}"
109
);
110
}
111
}
112
113
let text = filecheck_text(func, &domtree).expect("formatting error");
114
run_filecheck(&text, context)
115
}
116
}
117
118
// Generate some output for filecheck testing
119
fn filecheck_text(func: &Function, domtree: &DominatorTree) -> Result<String, fmt::Error> {
120
let mut s = String::new();
121
122
write!(s, "cfg_postorder:")?;
123
for &block in domtree.cfg_postorder() {
124
write!(s, " {block}")?;
125
}
126
writeln!(s)?;
127
128
// Compute and print out a pre-order of the dominator tree.
129
writeln!(s, "domtree_preorder {{")?;
130
let mut stack = Vec::new();
131
stack.extend(func.layout.entry_block());
132
while let Some(block) = stack.pop() {
133
write!(s, " {block}:")?;
134
let i = stack.len();
135
for ch in domtree.children(block) {
136
write!(s, " {ch}")?;
137
stack.push(ch);
138
}
139
writeln!(s)?;
140
// Reverse the children we just pushed so we'll pop them in order.
141
stack[i..].reverse();
142
}
143
writeln!(s, "}}")?;
144
145
Ok(s)
146
}
147
148