Files
wasmtime/lib/cretonne/src/verifier/cssa.rs
Jakob Stoklund Olesen 6d6035b918 CSSA verifier.
During register allocation, the code must be kept in conventional SSA
form. Add a verifier that checks this property.
2017-07-13 13:18:18 -07:00

123 lines
4.5 KiB
Rust

//! Verify conventional SSA form.
use dominator_tree::DominatorTree;
use flowgraph::ControlFlowGraph;
use ir::Function;
use regalloc::liveness::Liveness;
use regalloc::virtregs::VirtRegs;
use std::cmp::Ordering;
use verifier::Result;
/// Verify conventional SSA form for `func`.
///
/// Conventional SSA form is represented in Cretonne with the help of virtual registers:
///
/// - Two values are said to be *PHI-related* if one is an EBB argument and the other is passed as
/// a branch argument in a location that matches the first value.
/// - PHI-related values must belong to the same virtual register.
/// - Two values in the same virtual register must not have overlapping live ranges.
///
/// Additionally, we verify this property of virtual registers:
///
/// - The values in a virtual register are ordered according to the dominator tree's `rpo_cmp()`.
///
/// We don't verify that virtual registers are minimal. Minimal CSSA is not required.
pub fn verify_cssa(func: &Function,
cfg: &ControlFlowGraph,
domtree: &DominatorTree,
liveness: &Liveness,
virtregs: &VirtRegs)
-> Result {
let verifier = CssaVerifier {
func,
cfg,
domtree,
virtregs,
liveness,
};
verifier.check_virtregs()?;
verifier.check_cssa()?;
Ok(())
}
struct CssaVerifier<'a> {
func: &'a Function,
cfg: &'a ControlFlowGraph,
domtree: &'a DominatorTree,
virtregs: &'a VirtRegs,
liveness: &'a Liveness,
}
impl<'a> CssaVerifier<'a> {
fn check_virtregs(&self) -> Result {
for vreg in self.virtregs.all_virtregs() {
let values = self.virtregs.values(vreg);
for (idx, &val) in values.iter().enumerate() {
if !self.func.dfg.value_is_valid(val) {
return err!(val, "Invalid value in {}", vreg);
}
if !self.func.dfg.value_is_attached(val) {
return err!(val, "Detached value in {}", vreg);
}
if self.liveness.get(val).is_none() {
return err!(val, "Value in {} has no live range", vreg);
};
// Check RPO ordering with the previous values in the virtual register.
let def = self.func.dfg.value_def(val).into();
let def_ebb = self.func.layout.pp_ebb(def);
for &prev_val in &values[0..idx] {
let prev_def = self.func.dfg.value_def(prev_val);
// Enforce RPO of defs in the virtual register.
match self.domtree.rpo_cmp(prev_def, def, &self.func.layout) {
Ordering::Less => {}
Ordering::Equal => {
return err!(val, "Value in {} has same def as {}", vreg, prev_val);
}
Ordering::Greater => {
return err!(val,
"Value in {} in wrong order relative to {}",
vreg,
prev_val);
}
}
// Knowing that values are in RPO order, we can check for interference this
// way.
if self.liveness[prev_val].overlaps_def(def, def_ebb, &self.func.layout) {
return err!(val, "Value def in {} interferes with {}", vreg, prev_val);
}
}
}
}
Ok(())
}
fn check_cssa(&self) -> Result {
for ebb in self.func.layout.ebbs() {
let ebb_args = self.func.dfg.ebb_args(ebb);
for &(_, pred) in self.cfg.get_predecessors(ebb) {
let pred_args = self.func.dfg.inst_variable_args(pred);
// This should have been caught by an earlier verifier pass.
assert_eq!(ebb_args.len(),
pred_args.len(),
"Wrong arguments on branch.");
for (&ebb_arg, &pred_arg) in ebb_args.iter().zip(pred_args) {
if !self.virtregs.same_class(ebb_arg, pred_arg) {
return err!(pred,
"{} and {} must be in the same virtual register",
ebb_arg,
pred_arg);
}
}
}
}
Ok(())
}
}