blob: d3e95bf51b6e9ebed930a8176ceaccd7586e60fb [file] [log] [blame]
//! Verify conventional SSA form.
use crate::dbg::DisplayList;
use crate::dominator_tree::{DominatorTree, DominatorTreePreorder};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir::{ExpandedProgramPoint, Function};
use crate::regalloc::liveness::Liveness;
use crate::regalloc::virtregs::VirtRegs;
use crate::timing;
use crate::verifier::{VerifierErrors, VerifierStepResult};
/// Verify conventional SSA form for `func`.
///
/// Conventional SSA form is represented in Cranelift with the help of virtual registers:
///
/// - Two values are said to be *PHI-related* if one is a block 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 topologically ordered w.r.t. dominance.
///
/// 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,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let _tt = timing::verify_cssa();
let mut preorder = DominatorTreePreorder::new();
preorder.compute(domtree, &func.layout);
let verifier = CssaVerifier {
func,
cfg,
domtree,
virtregs,
liveness,
preorder,
};
verifier.check_virtregs(errors)?;
verifier.check_cssa(errors)?;
Ok(())
}
struct CssaVerifier<'a> {
func: &'a Function,
cfg: &'a ControlFlowGraph,
domtree: &'a DominatorTree,
virtregs: &'a VirtRegs,
liveness: &'a Liveness,
preorder: DominatorTreePreorder,
}
impl<'a> CssaVerifier<'a> {
fn check_virtregs(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
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 errors.fatal((val, format!("Invalid value in {}", vreg)));
}
if !self.func.dfg.value_is_attached(val) {
return errors.fatal((val, format!("Detached value in {}", vreg)));
}
if self.liveness.get(val).is_none() {
return errors.fatal((val, format!("Value in {} has no live range", vreg)));
};
// Check topological ordering with the previous values in the virtual register.
let def: ExpandedProgramPoint = self.func.dfg.value_def(val).into();
let def_block = self.func.layout.pp_block(def);
for &prev_val in &values[0..idx] {
let prev_def: ExpandedProgramPoint = self.func.dfg.value_def(prev_val).into();
let prev_block = self.func.layout.pp_block(prev_def);
if prev_def == def {
return errors.fatal((
val,
format!(
"Values {} and {} in {} = {} defined at the same program point",
prev_val,
val,
vreg,
DisplayList(values)
),
));
}
// Enforce topological ordering of defs in the virtual register.
if self.preorder.dominates(def_block, prev_block)
&& self.domtree.dominates(def, prev_def, &self.func.layout)
{
return errors.fatal((
val,
format!(
"Value in {} = {} def dominates previous {}",
vreg,
DisplayList(values),
prev_val
),
));
}
}
// Knowing that values are in topo order, we can check for interference this
// way.
// We only have to check against the nearest dominating value.
for &prev_val in values[0..idx].iter().rev() {
let prev_def: ExpandedProgramPoint = self.func.dfg.value_def(prev_val).into();
let prev_block = self.func.layout.pp_block(prev_def);
if self.preorder.dominates(prev_block, def_block)
&& self.domtree.dominates(prev_def, def, &self.func.layout)
{
if self.liveness[prev_val].overlaps_def(def, def_block, &self.func.layout) {
return errors.fatal((
val,
format!(
"Value def in {} = {} interferes with {}",
vreg,
DisplayList(values),
prev_val
),
));
} else {
break;
}
}
}
}
}
Ok(())
}
fn check_cssa(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
for block in self.func.layout.blocks() {
let block_params = self.func.dfg.block_params(block);
for BlockPredecessor { inst: pred, .. } in self.cfg.pred_iter(block) {
let pred_args = self.func.dfg.inst_variable_args(pred);
// This should have been caught by an earlier verifier pass.
assert_eq!(
block_params.len(),
pred_args.len(),
"Wrong arguments on branch."
);
for (&block_param, &pred_arg) in block_params.iter().zip(pred_args) {
if !self.virtregs.same_class(block_param, pred_arg) {
return errors.fatal((
pred,
format!(
"{} and {} must be in the same virtual register",
block_param, pred_arg
),
));
}
}
}
}
Ok(())
}
}