* Manually rename BasicBlock to BlockPredecessor BasicBlock is a pair of (Ebb, Inst) that is used to represent the basic block subcomponent of an Ebb that is a predecessor to an Ebb. Eventually we will be able to remove this struct, but for now it makes sense to give it a non-conflicting name so that we can start to transition Ebb to represent a basic block. I have not updated any comments that refer to BasicBlock, as eventually we will remove BlockPredecessor and replace with Block, which is a basic block, so the comments will become correct. * Manually rename SSABuilder block types to avoid conflict SSABuilder has its own Block and BlockData types. These along with associated identifier will cause conflicts in a later commit, so they are renamed to be more verbose here. * Automatically rename 'Ebb' to 'Block' in *.rs * Automatically rename 'EBB' to 'block' in *.rs * Automatically rename 'ebb' to 'block' in *.rs * Automatically rename 'extended basic block' to 'basic block' in *.rs * Automatically rename 'an basic block' to 'a basic block' in *.rs * Manually update comment for `Block` `Block`'s wikipedia article required an update. * Automatically rename 'an `Block`' to 'a `Block`' in *.rs * Automatically rename 'extended_basic_block' to 'basic_block' in *.rs * Automatically rename 'ebb' to 'block' in *.clif * Manually rename clif constant that contains 'ebb' as substring to avoid conflict * Automatically rename filecheck uses of 'EBB' to 'BB' 'regex: EBB' -> 'regex: BB' '$EBB' -> '$BB' * Automatically rename 'EBB' 'Ebb' to 'block' in *.clif * Automatically rename 'an block' to 'a block' in *.clif * Fix broken testcase when function name length increases Test function names are limited to 16 characters. This causes the new longer name to be truncated and fail a filecheck test. An outdated comment was also fixed.
147 lines
5.1 KiB
Rust
147 lines
5.1 KiB
Rust
//! Test command for verifying dominator trees.
|
|
//!
|
|
//! The `test domtree` test command looks for annotations on instructions like this:
|
|
//!
|
|
//! ```clif
|
|
//! jump block3 ; dominates: block3
|
|
//! ```
|
|
//!
|
|
//! This annotation means that the jump instruction is expected to be the immediate dominator of
|
|
//! `block3`.
|
|
//!
|
|
//! We verify that the dominator tree annotations are complete and correct.
|
|
//!
|
|
|
|
use crate::match_directive::match_directive;
|
|
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
|
use cranelift_codegen::dominator_tree::{DominatorTree, DominatorTreePreorder};
|
|
use cranelift_codegen::flowgraph::ControlFlowGraph;
|
|
use cranelift_codegen::ir::entities::AnyEntity;
|
|
use cranelift_codegen::ir::Function;
|
|
use cranelift_reader::TestCommand;
|
|
use std::borrow::{Borrow, Cow};
|
|
use std::collections::HashMap;
|
|
use std::fmt::{self, Write};
|
|
|
|
struct TestDomtree;
|
|
|
|
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
|
assert_eq!(parsed.command, "domtree");
|
|
if !parsed.options.is_empty() {
|
|
Err(format!("No options allowed on {}", parsed))
|
|
} else {
|
|
Ok(Box::new(TestDomtree))
|
|
}
|
|
}
|
|
|
|
impl SubTest for TestDomtree {
|
|
fn name(&self) -> &'static str {
|
|
"domtree"
|
|
}
|
|
|
|
// Extract our own dominator tree from
|
|
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
|
let func = func.borrow();
|
|
let cfg = ControlFlowGraph::with_function(func);
|
|
let domtree = DominatorTree::with_function(func, &cfg);
|
|
|
|
// Build an expected domtree from the source annotations.
|
|
let mut expected = HashMap::new();
|
|
for comment in &context.details.comments {
|
|
if let Some(tail) = match_directive(comment.text, "dominates:") {
|
|
let inst = match comment.entity {
|
|
AnyEntity::Inst(inst) => inst,
|
|
_ => {
|
|
return Err(format!(
|
|
"annotation on non-inst {}: {}",
|
|
comment.entity, comment.text
|
|
));
|
|
}
|
|
};
|
|
for src_block in tail.split_whitespace() {
|
|
let block = match context.details.map.lookup_str(src_block) {
|
|
Some(AnyEntity::Block(block)) => block,
|
|
_ => return Err(format!("expected defined block, got {}", src_block)),
|
|
};
|
|
|
|
// Annotations say that `inst` is the idom of `block`.
|
|
if expected.insert(block, inst).is_some() {
|
|
return Err(format!("multiple dominators for {}", src_block));
|
|
}
|
|
|
|
// Compare to computed domtree.
|
|
match domtree.idom(block) {
|
|
Some(got_inst) if got_inst != inst => {
|
|
return Err(format!(
|
|
"mismatching idoms for {}:\n\
|
|
want: {}, got: {}",
|
|
src_block, inst, got_inst
|
|
));
|
|
}
|
|
None => {
|
|
return Err(format!(
|
|
"mismatching idoms for {}:\n\
|
|
want: {}, got: unreachable",
|
|
src_block, inst
|
|
));
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we know that everything in `expected` is consistent with `domtree`.
|
|
// All other block's should be either unreachable or the entry block.
|
|
for block in func
|
|
.layout
|
|
.blocks()
|
|
.skip(1)
|
|
.filter(|block| !expected.contains_key(block))
|
|
{
|
|
if let Some(got_inst) = domtree.idom(block) {
|
|
return Err(format!(
|
|
"mismatching idoms for renumbered {}:\n\
|
|
want: unrechable, got: {}",
|
|
block, got_inst
|
|
));
|
|
}
|
|
}
|
|
|
|
let text = filecheck_text(func, &domtree).expect("formatting error");
|
|
run_filecheck(&text, context)
|
|
}
|
|
}
|
|
|
|
// Generate some output for filecheck testing
|
|
fn filecheck_text(func: &Function, domtree: &DominatorTree) -> Result<String, fmt::Error> {
|
|
let mut s = String::new();
|
|
|
|
write!(s, "cfg_postorder:")?;
|
|
for &block in domtree.cfg_postorder() {
|
|
write!(s, " {}", block)?;
|
|
}
|
|
writeln!(s)?;
|
|
|
|
// Compute and print out a pre-order of the dominator tree.
|
|
writeln!(s, "domtree_preorder {{")?;
|
|
let mut dtpo = DominatorTreePreorder::new();
|
|
dtpo.compute(domtree, &func.layout);
|
|
let mut stack = Vec::new();
|
|
stack.extend(func.layout.entry_block());
|
|
while let Some(block) = stack.pop() {
|
|
write!(s, " {}:", block)?;
|
|
let i = stack.len();
|
|
for ch in dtpo.children(block) {
|
|
write!(s, " {}", ch)?;
|
|
stack.push(ch);
|
|
}
|
|
writeln!(s)?;
|
|
// Reverse the children we just pushed so we'll pop them in order.
|
|
stack[i..].reverse();
|
|
}
|
|
writeln!(s, "}}")?;
|
|
|
|
Ok(s)
|
|
}
|