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