1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! Verify CPU flags values.
use crate::entity::{EntitySet, SecondaryMap};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir;
use crate::ir::instructions::BranchInfo;
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,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let _tt = timing::verify_flags();
let mut verifier = FlagsVerifier {
func,
cfg,
livein: SecondaryMap::new(),
};
verifier.check(errors)
}
struct FlagsVerifier<'a> {
func: &'a ir::Function,
cfg: &'a ControlFlowGraph,
/// 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(());
}
}
}
// 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(())
}