diff options
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/verifier/flags.rs')
-rw-r--r-- | third_party/rust/cranelift-codegen/src/verifier/flags.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen/src/verifier/flags.rs b/third_party/rust/cranelift-codegen/src/verifier/flags.rs new file mode 100644 index 0000000000..e4cfc80462 --- /dev/null +++ b/third_party/rust/cranelift-codegen/src/verifier/flags.rs @@ -0,0 +1,185 @@ +//! 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(()) +} |