Files
wasmtime/cranelift/codegen/src/verifier/flags.rs
Chris Fallin 48cf2c2f50 Address review comments:
- Undo temporary changes to default features (`all-arch`) and a
  signal-handler test.
- Remove `SIGTRAP` handler: no longer needed now that we've found an
  "undefined opcode" option on ARM64.
- Rename pp.rs to pretty_print.rs in machinst/.
- Only use empty stack-probe on non-x86. As per a comment in
  rust-lang/compiler-builtins [1], LLVM only supports stack probes on
  x86 and x86-64. Thus, on any other CPU architecture, we cannot refer
  to `__rust_probestack`, because it does not exist.
- Rename arm64 to aarch64.
- Use `target` directive in vcode filetests.
- Run the flags verifier, but without encinfo, when using new backends.
- Clean up warning overrides.
- Fix up use of casts: use u32::from(x) and siblings when possible,
  u32::try_from(x).unwrap() when not, to avoid silent truncation.
- Take immutable `Function` borrows as input; we don't actually
  mutate the input IR.
- Lots of other miscellaneous cleanups.

[1] cae3e6ea23/src/probestack.rs (L39)
2020-04-15 17:21:28 -07:00

186 lines
6.6 KiB
Rust

//! Verify CPU flags values.
use crate::entity::{EntitySet, SecondaryMap};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir;
use crate::ir::instructions::BranchInfo;
use crate::isa;
use crate::packed_option::PackedOption;
use crate::timing;
use crate::verifier::{VerifierErrors, VerifierStepResult};
/// 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<&dyn isa::TargetIsa>,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let _tt = timing::verify_flags();
let encinfo = if isa.is_none() || isa.unwrap().get_mach_backend().is_some() {
None
} else {
Some(isa.unwrap().encoding_info())
};
let mut verifier = FlagsVerifier {
func,
cfg,
encinfo,
livein: SecondaryMap::new(),
};
verifier.check(errors)
}
struct FlagsVerifier<'a> {
func: &'a ir::Function,
cfg: &'a ControlFlowGraph,
encinfo: Option<isa::EncInfo>,
/// The single live-in flags value (if any) for each block.
livein: SecondaryMap<ir::Block, PackedOption<ir::Value>>,
}
impl<'a> FlagsVerifier<'a> {
fn check(&mut self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
// List of blocks that need to be processed. blocks 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 = EntitySet::with_capacity(self.func.layout.block_capacity());
for block in self.func.layout.blocks() {
worklist.insert(block);
}
while let Some(block) = worklist.pop() {
if let Some(value) = self.visit_block(block, errors)? {
// The block has live-in flags. Check if the value changed.
match self.livein[block].expand() {
// Revisit any predecessor blocks the first time we see a live-in for `block`.
None => {
self.livein[block] = value.into();
for BlockPredecessor { block: pred, .. } in self.cfg.pred_iter(block) {
worklist.insert(pred);
}
}
Some(old) if old != value => {
return errors.fatal((
block,
format!("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[block].expand(), None);
}
}
Ok(())
}
/// Check flags usage in `block` and return the live-in flags value, if any.
fn visit_block(
&self,
block: ir::Block,
errors: &mut VerifierErrors,
) -> VerifierStepResult<Option<ir::Value>> {
// 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.block_insts(block).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() {
errors
.report((inst, format!("{} clobbers live CPU flags in {}", res, live)));
return Err(());
}
}
// 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_or(false, |c| c.clobbers_flags)
&& live_val.is_some()
{
errors.report((
inst,
format!("encoding clobbers live CPU flags in {}", live),
));
return Err(());
}
}
// 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, errors)?;
}
}
// Include live-in flags to successor blocks.
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)?;
}
}
}
}
}
// 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,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
if let Some(va) = *a {
if b != va {
return errors.fatal((
inst,
format!("conflicting live CPU flags: {} and {}", va, b),
));
}
} else {
*a = Some(b);
}
Ok(())
}