use entity::{SecondaryMap, SparseSet};
use flowgraph::{BasicBlock, ControlFlowGraph};
use ir;
use ir::instructions::BranchInfo;
use isa;
use packed_option::PackedOption;
use timing;
use verifier::{VerifierErrors, VerifierStepResult};
pub fn verify_flags(
func: &ir::Function,
cfg: &ControlFlowGraph,
isa: Option<&isa::TargetIsa>,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let _tt = timing::verify_flags();
let mut verifier = FlagsVerifier {
func,
cfg,
encinfo: isa.map(|isa| isa.encoding_info()),
livein: SecondaryMap::new(),
};
verifier.check(errors)
}
struct FlagsVerifier<'a> {
func: &'a ir::Function,
cfg: &'a ControlFlowGraph,
encinfo: Option<isa::EncInfo>,
livein: SecondaryMap<ir::Ebb, PackedOption<ir::Value>>,
}
impl<'a> FlagsVerifier<'a> {
fn check(&mut self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
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, errors)? {
match self.livein[ebb].expand() {
None => {
self.livein[ebb] = value.into();
for BasicBlock { ebb: pred, .. } in self.cfg.pred_iter(ebb) {
worklist.insert(pred);
}
}
Some(old) if old != value => {
return fatal!(
errors,
ebb,
"conflicting live-in CPU flags: {} and {}",
old,
value
);
}
x => assert_eq!(x, Some(value)),
}
} else {
assert_eq!(self.livein[ebb].expand(), None);
}
}
Ok(())
}
fn visit_ebb(
&self,
ebb: ir::Ebb,
errors: &mut VerifierErrors,
) -> VerifierStepResult<Option<ir::Value>> {
let mut live_val = None;
for inst in self.func.layout.ebb_insts(ebb).rev() {
if let Some(live) = live_val {
for &res in self.func.dfg.inst_results(inst) {
if res == live {
live_val = None;
} else if self.func.dfg.value_type(res).is_flags() {
return fatal!(errors, inst, "{} clobbers live CPU flags in {}", res, live);
}
}
if self
.encinfo
.as_ref()
.and_then(|ei| ei.operand_constraints(self.func.encodings[inst]))
.map_or(false, |c| c.clobbers_flags)
&& live_val.is_some()
{
return fatal!(errors, inst, "encoding clobbers live CPU flags in {}", live);
}
}
for &arg in self.func.dfg.inst_args(inst) {
if self.func.dfg.value_type(arg).is_flags() {
merge(&mut live_val, arg, inst, errors)?;
}
}
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, errors)?;
}
}
BranchInfo::Table(jt, dest) => {
if let Some(dest) = dest {
if let Some(val) = self.livein[dest].expand() {
merge(&mut live_val, val, inst, errors)?;
}
}
for dest in self.func.jump_tables[jt].iter() {
if let Some(val) = self.livein[*dest].expand() {
merge(&mut live_val, val, inst, errors)?;
}
}
}
}
}
Ok(live_val)
}
}
fn merge(
a: &mut Option<ir::Value>,
b: ir::Value,
inst: ir::Inst,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
if let Some(va) = *a {
if b != va {
return fatal!(errors, inst, "conflicting live CPU flags: {} and {}", va, b);
}
} else {
*a = Some(b);
}
Ok(())
}