Rename the 'cretonne' crate to 'cretonne-codegen'.
This fixes the next part of #287.
This commit is contained in:
165
lib/codegen/src/verifier/cssa.rs
Normal file
165
lib/codegen/src/verifier/cssa.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
//! Verify conventional SSA form.
|
||||
|
||||
use dbg::DisplayList;
|
||||
use dominator_tree::{DominatorTree, DominatorTreePreorder};
|
||||
use flowgraph::ControlFlowGraph;
|
||||
use ir::{ExpandedProgramPoint, Function};
|
||||
use regalloc::liveness::Liveness;
|
||||
use regalloc::virtregs::VirtRegs;
|
||||
use timing;
|
||||
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 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,
|
||||
) -> Result {
|
||||
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()?;
|
||||
verifier.check_cssa()?;
|
||||
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) -> 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 topological ordering with the previous values in the virtual register.
|
||||
let def: ExpandedProgramPoint = 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: ExpandedProgramPoint = self.func.dfg.value_def(prev_val).into();
|
||||
let prev_ebb = self.func.layout.pp_ebb(prev_def);
|
||||
|
||||
if prev_def == def {
|
||||
return err!(
|
||||
val,
|
||||
"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_ebb, prev_ebb) &&
|
||||
self.domtree.dominates(def, prev_def, &self.func.layout)
|
||||
{
|
||||
return err!(
|
||||
val,
|
||||
"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_ebb = self.func.layout.pp_ebb(prev_def);
|
||||
|
||||
if self.preorder.dominates(prev_ebb, def_ebb) &&
|
||||
self.domtree.dominates(prev_def, def, &self.func.layout)
|
||||
{
|
||||
let ctx = self.liveness.context(&self.func.layout);
|
||||
if self.liveness[prev_val].overlaps_def(def, def_ebb, ctx) {
|
||||
return err!(
|
||||
val,
|
||||
"Value def in {} = {} interferes with {}",
|
||||
vreg,
|
||||
DisplayList(values),
|
||||
prev_val
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_cssa(&self) -> Result {
|
||||
for ebb in self.func.layout.ebbs() {
|
||||
let ebb_params = self.func.dfg.ebb_params(ebb);
|
||||
for (_, pred) in self.cfg.pred_iter(ebb) {
|
||||
let pred_args = self.func.dfg.inst_variable_args(pred);
|
||||
// This should have been caught by an earlier verifier pass.
|
||||
assert_eq!(
|
||||
ebb_params.len(),
|
||||
pred_args.len(),
|
||||
"Wrong arguments on branch."
|
||||
);
|
||||
|
||||
for (&ebb_param, &pred_arg) in ebb_params.iter().zip(pred_args) {
|
||||
if !self.virtregs.same_class(ebb_param, pred_arg) {
|
||||
return err!(
|
||||
pred,
|
||||
"{} and {} must be in the same virtual register",
|
||||
ebb_param,
|
||||
pred_arg
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
153
lib/codegen/src/verifier/flags.rs
Normal file
153
lib/codegen/src/verifier/flags.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
//! Verify CPU flags values.
|
||||
|
||||
use entity::{EntityMap, SparseSet};
|
||||
use flowgraph::ControlFlowGraph;
|
||||
use ir;
|
||||
use ir::instructions::BranchInfo;
|
||||
use isa;
|
||||
use packed_option::PackedOption;
|
||||
use std::result;
|
||||
use timing;
|
||||
use verifier::{Error, Result};
|
||||
|
||||
/// Verify that CPU flags are used correctly.
|
||||
///
|
||||
/// The value types `iflags` and `fflags` represent CPU flags which usually live in a
|
||||
/// special-purpose register, so they can't be used as freely as other value types that can live in
|
||||
/// any register.
|
||||
///
|
||||
/// We verify the following conditions:
|
||||
///
|
||||
/// - At most one flags value can be live at a time.
|
||||
/// - A flags value can not be live across an instruction that clobbers the flags.
|
||||
///
|
||||
///
|
||||
pub fn verify_flags(
|
||||
func: &ir::Function,
|
||||
cfg: &ControlFlowGraph,
|
||||
isa: Option<&isa::TargetIsa>,
|
||||
) -> Result {
|
||||
let _tt = timing::verify_flags();
|
||||
let mut verifier = FlagsVerifier {
|
||||
func,
|
||||
cfg,
|
||||
encinfo: isa.map(|isa| isa.encoding_info()),
|
||||
livein: EntityMap::new(),
|
||||
};
|
||||
verifier.check()
|
||||
}
|
||||
|
||||
struct FlagsVerifier<'a> {
|
||||
func: &'a ir::Function,
|
||||
cfg: &'a ControlFlowGraph,
|
||||
encinfo: Option<isa::EncInfo>,
|
||||
|
||||
/// The single live-in flags value (if any) for each EBB.
|
||||
livein: EntityMap<ir::Ebb, PackedOption<ir::Value>>,
|
||||
}
|
||||
|
||||
impl<'a> FlagsVerifier<'a> {
|
||||
fn check(&mut self) -> Result {
|
||||
// List of EBBs that need to be processed. EBBs may be re-added to this list when we detect
|
||||
// that one of their successor blocks needs a live-in flags value.
|
||||
let mut worklist = SparseSet::new();
|
||||
for ebb in self.func.layout.ebbs() {
|
||||
worklist.insert(ebb);
|
||||
}
|
||||
|
||||
while let Some(ebb) = worklist.pop() {
|
||||
if let Some(value) = self.visit_ebb(ebb)? {
|
||||
// The EBB has live-in flags. Check if the value changed.
|
||||
match self.livein[ebb].expand() {
|
||||
// Revisit any predecessor blocks the first time we see a live-in for `ebb`.
|
||||
None => {
|
||||
self.livein[ebb] = value.into();
|
||||
for (pred, _) in self.cfg.pred_iter(ebb) {
|
||||
worklist.insert(pred);
|
||||
}
|
||||
}
|
||||
Some(old) if old != value => {
|
||||
return err!(ebb, "conflicting live-in CPU flags: {} and {}", old, value);
|
||||
}
|
||||
x => assert_eq!(x, Some(value)),
|
||||
}
|
||||
} else {
|
||||
// Existing live-in flags should never be able to disappear.
|
||||
assert_eq!(self.livein[ebb].expand(), None);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check flags usage in `ebb` and return the live-in flags value, if any.
|
||||
fn visit_ebb(&self, ebb: ir::Ebb) -> result::Result<Option<ir::Value>, Error> {
|
||||
// The single currently live flags value.
|
||||
let mut live_val = None;
|
||||
|
||||
// Visit instructions backwards so we can track liveness accurately.
|
||||
for inst in self.func.layout.ebb_insts(ebb).rev() {
|
||||
// Check if `inst` interferes with existing live flags.
|
||||
if let Some(live) = live_val {
|
||||
for &res in self.func.dfg.inst_results(inst) {
|
||||
if res == live {
|
||||
// We've reached the def of `live_flags`, so it is no longer live above.
|
||||
live_val = None;
|
||||
} else if self.func.dfg.value_type(res).is_flags() {
|
||||
return err!(inst, "{} clobbers live CPU flags in {}", res, live);
|
||||
}
|
||||
}
|
||||
|
||||
// Does the instruction have an encoding that clobbers the CPU flags?
|
||||
if self.encinfo
|
||||
.as_ref()
|
||||
.and_then(|ei| ei.operand_constraints(self.func.encodings[inst]))
|
||||
.map(|c| c.clobbers_flags)
|
||||
.unwrap_or(false) && live_val.is_some()
|
||||
{
|
||||
return err!(inst, "encoding clobbers live CPU flags in {}", live);
|
||||
}
|
||||
}
|
||||
|
||||
// Now look for live ranges of CPU flags that end here.
|
||||
for &arg in self.func.dfg.inst_args(inst) {
|
||||
if self.func.dfg.value_type(arg).is_flags() {
|
||||
merge(&mut live_val, arg, inst)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Include live-in flags to successor EBBs.
|
||||
match self.func.dfg.analyze_branch(inst) {
|
||||
BranchInfo::NotABranch => {}
|
||||
BranchInfo::SingleDest(dest, _) => {
|
||||
if let Some(val) = self.livein[dest].expand() {
|
||||
merge(&mut live_val, val, inst)?;
|
||||
}
|
||||
}
|
||||
BranchInfo::Table(jt) => {
|
||||
for (_, dest) in self.func.jump_tables[jt].entries() {
|
||||
if let Some(val) = self.livein[dest].expand() {
|
||||
merge(&mut live_val, val, inst)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the required live-in flags value.
|
||||
Ok(live_val)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge live flags values, or return an error on conflicting values.
|
||||
fn merge(a: &mut Option<ir::Value>, b: ir::Value, inst: ir::Inst) -> Result {
|
||||
if let Some(va) = *a {
|
||||
if b != va {
|
||||
return err!(inst, "conflicting live CPU flags: {} and {}", va, b);
|
||||
}
|
||||
} else {
|
||||
*a = Some(b);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
220
lib/codegen/src/verifier/liveness.rs
Normal file
220
lib/codegen/src/verifier/liveness.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
//! Liveness verifier.
|
||||
|
||||
use flowgraph::ControlFlowGraph;
|
||||
use ir::entities::AnyEntity;
|
||||
use ir::{ExpandedProgramPoint, Function, Inst, ProgramOrder, ProgramPoint, Value};
|
||||
use isa::TargetIsa;
|
||||
use regalloc::liveness::Liveness;
|
||||
use regalloc::liverange::LiveRange;
|
||||
use std::cmp::Ordering;
|
||||
use timing;
|
||||
use verifier::Result;
|
||||
|
||||
/// Verify liveness information for `func`.
|
||||
///
|
||||
/// The provided control flow graph is assumed to be sound.
|
||||
///
|
||||
/// - All values in the program must have a live range.
|
||||
/// - The live range def point must match where the value is defined.
|
||||
/// - The live range must reach all uses.
|
||||
/// - When a live range is live-in to an EBB, it must be live at all the predecessors.
|
||||
/// - The live range affinity must be compatible with encoding constraints.
|
||||
///
|
||||
/// We don't verify that live ranges are minimal. This would require recomputing live ranges for
|
||||
/// all values.
|
||||
pub fn verify_liveness(
|
||||
isa: &TargetIsa,
|
||||
func: &Function,
|
||||
cfg: &ControlFlowGraph,
|
||||
liveness: &Liveness,
|
||||
) -> Result {
|
||||
let _tt = timing::verify_liveness();
|
||||
let verifier = LivenessVerifier {
|
||||
isa,
|
||||
func,
|
||||
cfg,
|
||||
liveness,
|
||||
};
|
||||
verifier.check_ebbs()?;
|
||||
verifier.check_insts()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct LivenessVerifier<'a> {
|
||||
isa: &'a TargetIsa,
|
||||
func: &'a Function,
|
||||
cfg: &'a ControlFlowGraph,
|
||||
liveness: &'a Liveness,
|
||||
}
|
||||
|
||||
impl<'a> LivenessVerifier<'a> {
|
||||
/// Check all EBB arguments.
|
||||
fn check_ebbs(&self) -> Result {
|
||||
for ebb in self.func.layout.ebbs() {
|
||||
for &val in self.func.dfg.ebb_params(ebb) {
|
||||
let lr = match self.liveness.get(val) {
|
||||
Some(lr) => lr,
|
||||
None => return err!(ebb, "EBB arg {} has no live range", val),
|
||||
};
|
||||
self.check_lr(ebb.into(), val, lr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check all instructions.
|
||||
fn check_insts(&self) -> Result {
|
||||
for ebb in self.func.layout.ebbs() {
|
||||
for inst in self.func.layout.ebb_insts(ebb) {
|
||||
let encoding = self.func.encodings[inst];
|
||||
|
||||
// Check the defs.
|
||||
for &val in self.func.dfg.inst_results(inst) {
|
||||
let lr = match self.liveness.get(val) {
|
||||
Some(lr) => lr,
|
||||
None => return err!(inst, "{} has no live range", val),
|
||||
};
|
||||
self.check_lr(inst.into(), val, lr)?;
|
||||
|
||||
if encoding.is_legal() {
|
||||
// A legal instruction is not allowed to define ghost values.
|
||||
if lr.affinity.is_none() {
|
||||
return err!(
|
||||
inst,
|
||||
"{} is a ghost value defined by a real [{}] instruction",
|
||||
val,
|
||||
self.isa.encoding_info().display(encoding)
|
||||
);
|
||||
}
|
||||
} else if !lr.affinity.is_none() {
|
||||
// A non-encoded instruction can only define ghost values.
|
||||
return err!(
|
||||
inst,
|
||||
"{} is a real {} value defined by a ghost instruction",
|
||||
val,
|
||||
lr.affinity.display(&self.isa.register_info())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the uses.
|
||||
for &val in self.func.dfg.inst_args(inst) {
|
||||
let lr = match self.liveness.get(val) {
|
||||
Some(lr) => lr,
|
||||
None => return err!(inst, "{} has no live range", val),
|
||||
};
|
||||
if !self.live_at_use(lr, inst) {
|
||||
return err!(inst, "{} is not live at this use", val);
|
||||
}
|
||||
|
||||
// A legal instruction is not allowed to depend on ghost values.
|
||||
if encoding.is_legal() && lr.affinity.is_none() {
|
||||
return err!(
|
||||
inst,
|
||||
"{} is a ghost value used by a real [{}] instruction",
|
||||
val,
|
||||
self.isa.encoding_info().display(encoding)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Is `lr` live at the use `inst`?
|
||||
fn live_at_use(&self, lr: &LiveRange, inst: Inst) -> bool {
|
||||
let ctx = self.liveness.context(&self.func.layout);
|
||||
|
||||
// Check if `inst` is in the def range, not including the def itself.
|
||||
if ctx.order.cmp(lr.def(), inst) == Ordering::Less &&
|
||||
ctx.order.cmp(inst, lr.def_local_end()) != Ordering::Greater
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise see if `inst` is in one of the live-in ranges.
|
||||
match lr.livein_local_end(ctx.order.inst_ebb(inst).unwrap(), ctx) {
|
||||
Some(end) => ctx.order.cmp(inst, end) != Ordering::Greater,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the integrity of the live range `lr`.
|
||||
fn check_lr(&self, def: ProgramPoint, val: Value, lr: &LiveRange) -> Result {
|
||||
let l = &self.func.layout;
|
||||
|
||||
let loc: AnyEntity = match def.into() {
|
||||
ExpandedProgramPoint::Ebb(e) => e.into(),
|
||||
ExpandedProgramPoint::Inst(i) => i.into(),
|
||||
};
|
||||
if lr.def() != def {
|
||||
return err!(loc, "Wrong live range def ({}) for {}", lr.def(), val);
|
||||
}
|
||||
if lr.is_dead() {
|
||||
if !lr.is_local() {
|
||||
return err!(loc, "Dead live range {} should be local", val);
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let def_ebb = match def.into() {
|
||||
ExpandedProgramPoint::Ebb(e) => e,
|
||||
ExpandedProgramPoint::Inst(i) => l.inst_ebb(i).unwrap(),
|
||||
};
|
||||
match lr.def_local_end().into() {
|
||||
ExpandedProgramPoint::Ebb(e) => {
|
||||
return err!(loc, "Def local range for {} can't end at {}", val, e)
|
||||
}
|
||||
ExpandedProgramPoint::Inst(i) => {
|
||||
if self.func.layout.inst_ebb(i) != Some(def_ebb) {
|
||||
return err!(loc, "Def local end for {} in wrong ebb", val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now check the live-in intervals against the CFG.
|
||||
for (mut ebb, end) in lr.liveins(self.liveness.context(l)) {
|
||||
if !l.is_ebb_inserted(ebb) {
|
||||
return err!(loc, "{} livein at {} which is not in the layout", val, ebb);
|
||||
}
|
||||
let end_ebb = match l.inst_ebb(end) {
|
||||
Some(e) => e,
|
||||
None => {
|
||||
return err!(
|
||||
loc,
|
||||
"{} livein for {} ends at {} which is not in the layout",
|
||||
val,
|
||||
ebb,
|
||||
end
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Check all the EBBs in the interval independently.
|
||||
loop {
|
||||
// If `val` is live-in at `ebb`, it must be live at all the predecessors.
|
||||
for (_, pred) in self.cfg.pred_iter(ebb) {
|
||||
if !self.live_at_use(lr, pred) {
|
||||
return err!(
|
||||
pred,
|
||||
"{} is live in to {} but not live at predecessor",
|
||||
val,
|
||||
ebb
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ebb == end_ebb {
|
||||
break;
|
||||
}
|
||||
ebb = match l.next_ebb(ebb) {
|
||||
Some(e) => e,
|
||||
None => return err!(loc, "end of {} livein ({}) never reached", val, end_ebb),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
318
lib/codegen/src/verifier/locations.rs
Normal file
318
lib/codegen/src/verifier/locations.rs
Normal file
@@ -0,0 +1,318 @@
|
||||
//! Verify value locations.
|
||||
|
||||
use ir;
|
||||
use isa;
|
||||
use regalloc::RegDiversions;
|
||||
use regalloc::liveness::Liveness;
|
||||
use timing;
|
||||
use verifier::Result;
|
||||
|
||||
/// Verify value locations for `func`.
|
||||
///
|
||||
/// After register allocation, every value must be assigned to a location - either a register or a
|
||||
/// stack slot. These locations must be compatible with the constraints described by the
|
||||
/// instruction encoding recipes.
|
||||
///
|
||||
/// Values can be temporarily diverted to a different location by using the `regmove`, `regspill`,
|
||||
/// and `regfill` instructions, but only inside an EBB.
|
||||
///
|
||||
/// If a liveness analysis is provided, it is used to verify that there are no active register
|
||||
/// diversions across control flow edges.
|
||||
pub fn verify_locations(
|
||||
isa: &isa::TargetIsa,
|
||||
func: &ir::Function,
|
||||
liveness: Option<&Liveness>,
|
||||
) -> Result {
|
||||
let _tt = timing::verify_locations();
|
||||
let verifier = LocationVerifier {
|
||||
isa,
|
||||
func,
|
||||
reginfo: isa.register_info(),
|
||||
encinfo: isa.encoding_info(),
|
||||
liveness,
|
||||
};
|
||||
verifier.check_constraints()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct LocationVerifier<'a> {
|
||||
isa: &'a isa::TargetIsa,
|
||||
func: &'a ir::Function,
|
||||
reginfo: isa::RegInfo,
|
||||
encinfo: isa::EncInfo,
|
||||
liveness: Option<&'a Liveness>,
|
||||
}
|
||||
|
||||
impl<'a> LocationVerifier<'a> {
|
||||
/// Check that the assigned value locations match the operand constraints of their uses.
|
||||
fn check_constraints(&self) -> Result {
|
||||
let dfg = &self.func.dfg;
|
||||
let mut divert = RegDiversions::new();
|
||||
|
||||
for ebb in self.func.layout.ebbs() {
|
||||
// Diversions are reset at the top of each EBB. No diversions can exist across control
|
||||
// flow edges.
|
||||
divert.clear();
|
||||
for inst in self.func.layout.ebb_insts(ebb) {
|
||||
let enc = self.func.encodings[inst];
|
||||
|
||||
if enc.is_legal() {
|
||||
self.check_enc_constraints(inst, enc, &divert)?
|
||||
} else {
|
||||
self.check_ghost_results(inst)?;
|
||||
}
|
||||
|
||||
if let Some(sig) = dfg.call_signature(inst) {
|
||||
self.check_call_abi(inst, sig, &divert)?;
|
||||
}
|
||||
|
||||
let opcode = dfg[inst].opcode();
|
||||
if opcode.is_return() {
|
||||
self.check_return_abi(inst, &divert)?;
|
||||
} else if opcode.is_branch() {
|
||||
if !divert.is_empty() {
|
||||
self.check_cfg_edges(inst, &divert)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.update_diversions(inst, &mut divert)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check encoding constraints against the current value locations.
|
||||
fn check_enc_constraints(
|
||||
&self,
|
||||
inst: ir::Inst,
|
||||
enc: isa::Encoding,
|
||||
divert: &RegDiversions,
|
||||
) -> Result {
|
||||
let constraints = self.encinfo.operand_constraints(enc).expect(
|
||||
"check_enc_constraints requires a legal encoding",
|
||||
);
|
||||
|
||||
if constraints.satisfied(inst, divert, self.func) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// TODO: We could give a better error message here.
|
||||
err!(
|
||||
inst,
|
||||
"{} constraints not satisfied",
|
||||
self.encinfo.display(enc)
|
||||
)
|
||||
}
|
||||
|
||||
/// Check that the result values produced by a ghost instruction are not assigned a value
|
||||
/// location.
|
||||
fn check_ghost_results(&self, inst: ir::Inst) -> Result {
|
||||
let results = self.func.dfg.inst_results(inst);
|
||||
|
||||
for &res in results {
|
||||
let loc = self.func.locations[res];
|
||||
if loc.is_assigned() {
|
||||
return err!(
|
||||
inst,
|
||||
"ghost result {} value must not have a location ({}).",
|
||||
res,
|
||||
loc.display(&self.reginfo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check the ABI argument and result locations for a call.
|
||||
fn check_call_abi(&self, inst: ir::Inst, sig: ir::SigRef, divert: &RegDiversions) -> Result {
|
||||
let sig = &self.func.dfg.signatures[sig];
|
||||
let varargs = self.func.dfg.inst_variable_args(inst);
|
||||
let results = self.func.dfg.inst_results(inst);
|
||||
|
||||
for (abi, &value) in sig.params.iter().zip(varargs) {
|
||||
self.check_abi_location(
|
||||
inst,
|
||||
value,
|
||||
abi,
|
||||
divert.get(value, &self.func.locations),
|
||||
ir::StackSlotKind::OutgoingArg,
|
||||
)?;
|
||||
}
|
||||
|
||||
for (abi, &value) in sig.returns.iter().zip(results) {
|
||||
self.check_abi_location(
|
||||
inst,
|
||||
value,
|
||||
abi,
|
||||
self.func.locations[value],
|
||||
ir::StackSlotKind::OutgoingArg,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check the ABI argument locations for a return.
|
||||
fn check_return_abi(&self, inst: ir::Inst, divert: &RegDiversions) -> Result {
|
||||
let sig = &self.func.signature;
|
||||
let varargs = self.func.dfg.inst_variable_args(inst);
|
||||
|
||||
for (abi, &value) in sig.returns.iter().zip(varargs) {
|
||||
self.check_abi_location(
|
||||
inst,
|
||||
value,
|
||||
abi,
|
||||
divert.get(value, &self.func.locations),
|
||||
ir::StackSlotKind::IncomingArg,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check a single ABI location.
|
||||
fn check_abi_location(
|
||||
&self,
|
||||
inst: ir::Inst,
|
||||
value: ir::Value,
|
||||
abi: &ir::AbiParam,
|
||||
loc: ir::ValueLoc,
|
||||
want_kind: ir::StackSlotKind,
|
||||
) -> Result {
|
||||
match abi.location {
|
||||
ir::ArgumentLoc::Unassigned => {}
|
||||
ir::ArgumentLoc::Reg(reg) => {
|
||||
if loc != ir::ValueLoc::Reg(reg) {
|
||||
return err!(
|
||||
inst,
|
||||
"ABI expects {} in {}, got {}",
|
||||
value,
|
||||
abi.location.display(&self.reginfo),
|
||||
loc.display(&self.reginfo)
|
||||
);
|
||||
}
|
||||
}
|
||||
ir::ArgumentLoc::Stack(offset) => {
|
||||
if let ir::ValueLoc::Stack(ss) = loc {
|
||||
let slot = &self.func.stack_slots[ss];
|
||||
if slot.kind != want_kind {
|
||||
return err!(
|
||||
inst,
|
||||
"call argument {} should be in a {} slot, but {} is {}",
|
||||
value,
|
||||
want_kind,
|
||||
ss,
|
||||
slot.kind
|
||||
);
|
||||
}
|
||||
if slot.offset.unwrap() != offset {
|
||||
return err!(
|
||||
inst,
|
||||
"ABI expects {} at stack offset {}, but {} is at {}",
|
||||
value,
|
||||
offset,
|
||||
ss,
|
||||
slot.offset.unwrap()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return err!(
|
||||
inst,
|
||||
"ABI expects {} at stack offset {}, got {}",
|
||||
value,
|
||||
offset,
|
||||
loc.display(&self.reginfo)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update diversions to reflect the current instruction and check their consistency.
|
||||
fn update_diversions(&self, inst: ir::Inst, divert: &mut RegDiversions) -> Result {
|
||||
let (arg, src) = match self.func.dfg[inst] {
|
||||
ir::InstructionData::RegMove { arg, src, .. } |
|
||||
ir::InstructionData::RegSpill { arg, src, .. } => (arg, ir::ValueLoc::Reg(src)),
|
||||
ir::InstructionData::RegFill { arg, src, .. } => (arg, ir::ValueLoc::Stack(src)),
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
if let Some(d) = divert.diversion(arg) {
|
||||
if d.to != src {
|
||||
return err!(
|
||||
inst,
|
||||
"inconsistent with current diversion to {}",
|
||||
d.to.display(&self.reginfo)
|
||||
);
|
||||
}
|
||||
} else if self.func.locations[arg] != src {
|
||||
return err!(
|
||||
inst,
|
||||
"inconsistent with global location {}",
|
||||
self.func.locations[arg].display(&self.reginfo)
|
||||
);
|
||||
}
|
||||
|
||||
divert.apply(&self.func.dfg[inst]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// We have active diversions before a branch. Make sure none of the diverted values are live
|
||||
/// on the outgoing CFG edges.
|
||||
fn check_cfg_edges(&self, inst: ir::Inst, divert: &RegDiversions) -> Result {
|
||||
use ir::instructions::BranchInfo::*;
|
||||
|
||||
// We can only check CFG edges if we have a liveness analysis.
|
||||
let liveness = match self.liveness {
|
||||
Some(l) => l,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let dfg = &self.func.dfg;
|
||||
|
||||
match dfg.analyze_branch(inst) {
|
||||
NotABranch => {
|
||||
panic!(
|
||||
"No branch information for {}",
|
||||
dfg.display_inst(inst, self.isa)
|
||||
)
|
||||
}
|
||||
SingleDest(ebb, _) => {
|
||||
for d in divert.all() {
|
||||
let lr = &liveness[d.value];
|
||||
if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
||||
return err!(
|
||||
inst,
|
||||
"{} is diverted to {} and live in to {}",
|
||||
d.value,
|
||||
d.to.display(&self.reginfo),
|
||||
ebb
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Table(jt) => {
|
||||
for d in divert.all() {
|
||||
let lr = &liveness[d.value];
|
||||
for (_, ebb) in self.func.jump_tables[jt].entries() {
|
||||
if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
||||
return err!(
|
||||
inst,
|
||||
"{} is diverted to {} and live in to {}",
|
||||
d.value,
|
||||
d.to.display(&self.reginfo),
|
||||
ebb
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1209
lib/codegen/src/verifier/mod.rs
Normal file
1209
lib/codegen/src/verifier/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user