| //! 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(()) |
| } |
| } |