Validator.

This commit is contained in:
Chris Fallin 2022-12-01 19:56:27 -08:00
parent 510f833da2
commit 77141d2177
2 changed files with 83 additions and 0 deletions

View file

@ -30,6 +30,7 @@ macro_rules! op {
impl<'a> WasmFuncBackend<'a> { impl<'a> WasmFuncBackend<'a> {
pub fn new(body: &'a FunctionBody) -> Result<WasmFuncBackend<'a>> { pub fn new(body: &'a FunctionBody) -> Result<WasmFuncBackend<'a>> {
body.validate()?;
log::debug!("Backend compiling:\n{}\n", body.display_verbose("| ")); log::debug!("Backend compiling:\n{}\n", body.display_verbose("| "));
let cfg = CFGInfo::new(body); let cfg = CFGInfo::new(body);
log::debug!("CFG:\n{:?}\n", cfg); log::debug!("CFG:\n{:?}\n", cfg);

View file

@ -1,4 +1,5 @@
use super::{Block, FunctionBodyDisplay, Local, Module, Signature, Type, Value, ValueDef}; use super::{Block, FunctionBodyDisplay, Local, Module, Signature, Type, Value, ValueDef};
use crate::cfg::CFGInfo;
use crate::entity::{EntityRef, EntityVec, PerEntity}; use crate::entity::{EntityRef, EntityVec, PerEntity};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -188,6 +189,87 @@ impl FunctionBody {
pub fn display_verbose<'a>(&'a self, indent: &'a str) -> FunctionBodyDisplay<'a> { pub fn display_verbose<'a>(&'a self, indent: &'a str) -> FunctionBodyDisplay<'a> {
FunctionBodyDisplay(self, indent, /* verbose = */ true) FunctionBodyDisplay(self, indent, /* verbose = */ true)
} }
pub fn validate(&self) -> anyhow::Result<()> {
// Verify that every block's succs are accurate.
for (block, block_def) in self.blocks.entries() {
let mut actual_succs = vec![];
block_def
.terminator
.visit_successors(|succ| actual_succs.push(succ));
if &actual_succs[..] != &block_def.succs[..] {
anyhow::bail!(
"Incorrect successors on {}: actual {:?}, stored {:?}",
block,
actual_succs,
block_def.succs
);
}
}
// Compute the location where every value is defined.
let mut block_inst: PerEntity<Value, Option<(Block, Option<usize>)>> = PerEntity::default();
for (block, block_def) in self.blocks.entries() {
for &(_, param) in &block_def.params {
block_inst[param] = Some((block, None));
}
for (i, &inst) in block_def.insts.iter().enumerate() {
block_inst[inst] = Some((block, Some(i)));
}
}
// Verify that every instruction uses args at legal locations
// (same block but earlier, or a dominating block).
let cfg = CFGInfo::new(self);
let mut bad = vec![];
for (block, block_def) in self.blocks.entries() {
let mut visit_use = |u: Value, i: Option<usize>, inst: Option<Value>| {
let u = self.resolve_alias(u);
if block_inst[u].is_none() {
bad.push(format!(
"Use of arg {} at {:?} illegal: not defined",
u, inst
));
return;
}
let (def_block, def_idx) = block_inst[u].unwrap();
if def_block == block {
if def_idx >= i {
bad.push(format!(
"Use of arg {} by {:?} does not dominate location",
u, inst
));
}
} else {
if !cfg.dominates(def_block, block) {
bad.push(format!(
"Use of arg {} defined in {} by {:?} in {}: def does not dominate",
u, def_block, inst, block
));
}
}
};
for (i, &inst) in block_def.insts.iter().enumerate() {
self.values[inst].visit_uses(|u| {
visit_use(u, Some(i), Some(inst));
});
}
let terminator_idx = block_def.insts.len();
block_def.terminator.visit_uses(|u| {
visit_use(u, Some(terminator_idx), None);
});
}
if bad.len() > 0 {
anyhow::bail!(
"Body is:\n{}\nError(s) in SSA: {:?}",
self.display_verbose(" | "),
bad
);
}
Ok(())
}
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]