|
|
@ -157,7 +157,7 @@ where |
|
|
|
|
|
|
|
/// Result of a step in the verification process.
|
|
|
|
///
|
|
|
|
/// Functions that return `VerifierStepResult<()>` should also take a
|
|
|
|
/// Functions that return `VerifierStepResult` should also take a
|
|
|
|
/// mutable reference to `VerifierErrors` as argument in order to report
|
|
|
|
/// errors.
|
|
|
|
///
|
|
|
@ -165,11 +165,11 @@ where |
|
|
|
/// meaning that the verification process may continue. However, other (non-fatal)
|
|
|
|
/// errors might have been reported through the previously mentioned `VerifierErrors`
|
|
|
|
/// argument.
|
|
|
|
pub type VerifierStepResult<T> = Result<T, ()>; |
|
|
|
pub type VerifierStepResult = Result<(), ()>; |
|
|
|
|
|
|
|
/// Result of a verification operation.
|
|
|
|
///
|
|
|
|
/// Unlike `VerifierStepResult<()>` which may be `Ok` while still having reported
|
|
|
|
/// Unlike `VerifierStepResult` which may be `Ok` while still having reported
|
|
|
|
/// errors, this type always returns `Err` if an error (fatal or not) was reported.
|
|
|
|
pub type VerifierResult<T> = Result<T, VerifierErrors>; |
|
|
|
|
|
|
@ -203,7 +203,7 @@ impl VerifierErrors { |
|
|
|
/// Return a `VerifierStepResult` that is fatal if at least one error was reported,
|
|
|
|
/// and non-fatal otherwise.
|
|
|
|
#[inline] |
|
|
|
pub fn as_result(&self) -> VerifierStepResult<()> { |
|
|
|
pub fn as_result(&self) -> VerifierStepResult { |
|
|
|
if self.is_empty() { |
|
|
|
Ok(()) |
|
|
|
} else { |
|
|
@ -217,13 +217,13 @@ impl VerifierErrors { |
|
|
|
} |
|
|
|
|
|
|
|
/// Report a fatal error and return `Err`.
|
|
|
|
pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult<()> { |
|
|
|
pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult { |
|
|
|
self.report(error); |
|
|
|
Err(()) |
|
|
|
} |
|
|
|
|
|
|
|
/// Report a non-fatal error and return `Ok`.
|
|
|
|
pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult<()> { |
|
|
|
pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult { |
|
|
|
self.report(error); |
|
|
|
Ok(()) |
|
|
|
} |
|
|
@ -285,7 +285,7 @@ pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>( |
|
|
|
domtree: &DominatorTree, |
|
|
|
fisa: FOI, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let _tt = timing::verifier(); |
|
|
|
let verifier = Verifier::new(func, fisa.into()); |
|
|
|
if cfg.is_valid() { |
|
|
@ -325,7 +325,7 @@ impl<'a> Verifier<'a> { |
|
|
|
// Check for:
|
|
|
|
// - cycles in the global value declarations.
|
|
|
|
// - use of 'vmctx' when no special parameter declares it.
|
|
|
|
fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
let mut cycle_seen = false; |
|
|
|
let mut seen = SparseSet::new(); |
|
|
|
|
|
|
@ -410,7 +410,7 @@ impl<'a> Verifier<'a> { |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
// Verify that all fields are statically-sized and lie within
|
|
|
|
// the struct, do not overlap, and are in offset order
|
|
|
|
for (mt, mt_data) in &self.func.memory_types { |
|
|
@ -457,7 +457,7 @@ impl<'a> Verifier<'a> { |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn verify_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn verify_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
if let Some(isa) = self.isa { |
|
|
|
for (table, table_data) in &self.func.tables { |
|
|
|
let base = table_data.base_gv; |
|
|
@ -502,7 +502,7 @@ impl<'a> Verifier<'a> { |
|
|
|
|
|
|
|
/// Check that the given block can be encoded as a BB, by checking that only
|
|
|
|
/// branching instructions are ending the block.
|
|
|
|
fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
match self.func.is_block_basic(block) { |
|
|
|
Ok(()) => Ok(()), |
|
|
|
Err((inst, message)) => errors.fatal((inst, self.context(inst), message)), |
|
|
@ -514,7 +514,7 @@ impl<'a> Verifier<'a> { |
|
|
|
block: Block, |
|
|
|
inst: Inst, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator(); |
|
|
|
let is_last_inst = self.func.layout.last_inst(block) == Some(inst); |
|
|
|
|
|
|
@ -560,11 +560,7 @@ impl<'a> Verifier<'a> { |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn instruction_integrity( |
|
|
|
&self, |
|
|
|
inst: Inst, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
fn instruction_integrity(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
let inst_data = &self.func.dfg.insts[inst]; |
|
|
|
let dfg = &self.func.dfg; |
|
|
|
|
|
|
@ -596,7 +592,7 @@ impl<'a> Verifier<'a> { |
|
|
|
&self, |
|
|
|
inst: Inst, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
use crate::ir::instructions::InstructionData::*; |
|
|
|
|
|
|
|
for arg in self.func.dfg.inst_values(inst) { |
|
|
@ -763,7 +759,7 @@ impl<'a> Verifier<'a> { |
|
|
|
loc: impl Into<AnyEntity>, |
|
|
|
e: Block, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) { |
|
|
|
return errors.fatal((loc, format!("invalid block reference {}", e))); |
|
|
|
} |
|
|
@ -780,7 +776,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
s: SigRef, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.dfg.signatures.is_valid(s) { |
|
|
|
errors.fatal(( |
|
|
|
inst, |
|
|
@ -797,7 +793,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
f: FuncRef, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.dfg.ext_funcs.is_valid(f) { |
|
|
|
errors.nonfatal(( |
|
|
|
inst, |
|
|
@ -814,7 +810,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
ss: StackSlot, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.sized_stack_slots.is_valid(ss) { |
|
|
|
errors.nonfatal(( |
|
|
|
inst, |
|
|
@ -831,7 +827,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
ss: DynamicStackSlot, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.dynamic_stack_slots.is_valid(ss) { |
|
|
|
errors.nonfatal(( |
|
|
|
inst, |
|
|
@ -848,7 +844,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
gv: GlobalValue, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.global_values.is_valid(gv) { |
|
|
|
errors.nonfatal(( |
|
|
|
inst, |
|
|
@ -865,7 +861,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
table: ir::Table, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.tables.is_valid(table) { |
|
|
|
errors.nonfatal((inst, self.context(inst), format!("invalid table {}", table))) |
|
|
|
} else { |
|
|
@ -878,7 +874,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
l: &ValueList, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !l.is_valid(&self.func.dfg.value_lists) { |
|
|
|
errors.nonfatal(( |
|
|
|
inst, |
|
|
@ -895,7 +891,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
j: JumpTable, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
if !self.func.stencil.dfg.jump_tables.is_valid(j) { |
|
|
|
errors.nonfatal(( |
|
|
|
inst, |
|
|
@ -916,7 +912,7 @@ impl<'a> Verifier<'a> { |
|
|
|
loc_inst: Inst, |
|
|
|
v: Value, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let dfg = &self.func.dfg; |
|
|
|
if !dfg.value_is_valid(v) { |
|
|
|
errors.nonfatal(( |
|
|
@ -934,7 +930,7 @@ impl<'a> Verifier<'a> { |
|
|
|
loc_inst: Inst, |
|
|
|
v: Value, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
self.verify_value(loc_inst, v, errors)?; |
|
|
|
|
|
|
|
let dfg = &self.func.dfg; |
|
|
@ -1028,7 +1024,7 @@ impl<'a> Verifier<'a> { |
|
|
|
loc_inst: Inst, |
|
|
|
v: Value, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
self.verify_value(loc_inst, v, errors)?; |
|
|
|
|
|
|
|
match self.func.dfg.value_def(v) { |
|
|
@ -1062,7 +1058,7 @@ impl<'a> Verifier<'a> { |
|
|
|
flags: MemFlags, |
|
|
|
arg: Value, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let typ = self.func.dfg.ctrl_typevar(inst); |
|
|
|
let value_type = self.func.dfg.value_type(arg); |
|
|
|
|
|
|
@ -1099,7 +1095,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
constant: Constant, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let type_size = self.func.dfg.ctrl_typevar(inst).bytes() as usize; |
|
|
|
let constant_size = self.func.dfg.constants.get(constant).len(); |
|
|
|
if type_size != constant_size { |
|
|
@ -1119,7 +1115,7 @@ impl<'a> Verifier<'a> { |
|
|
|
&self, |
|
|
|
domtree: &DominatorTree, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
// We consider two `DominatorTree`s to be equal if they return the same immediate
|
|
|
|
// dominator for each block. Therefore the current domtree is valid if it matches the freshly
|
|
|
|
// computed one.
|
|
|
@ -1174,7 +1170,7 @@ impl<'a> Verifier<'a> { |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
if let Some(block) = self.func.layout.entry_block() { |
|
|
|
let expected_types = &self.func.signature.params; |
|
|
|
let block_param_count = self.func.dfg.num_block_params(block); |
|
|
@ -1207,7 +1203,7 @@ impl<'a> Verifier<'a> { |
|
|
|
errors.as_result() |
|
|
|
} |
|
|
|
|
|
|
|
fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
if let Some(entry_block) = self.func.layout.entry_block() { |
|
|
|
if self.func.layout.is_cold(entry_block) { |
|
|
|
return errors |
|
|
@ -1217,7 +1213,7 @@ impl<'a> Verifier<'a> { |
|
|
|
errors.as_result() |
|
|
|
} |
|
|
|
|
|
|
|
fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
let inst_data = &self.func.dfg.insts[inst]; |
|
|
|
let constraints = inst_data.opcode().constraints(); |
|
|
|
|
|
|
@ -1258,7 +1254,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
ctrl_type: Type, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let mut i = 0; |
|
|
|
for &result in self.func.dfg.inst_results(inst) { |
|
|
|
let result_type = self.func.dfg.value_type(result); |
|
|
@ -1300,7 +1296,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
ctrl_type: Type, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let constraints = self.func.dfg.insts[inst].opcode().constraints(); |
|
|
|
|
|
|
|
for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() { |
|
|
@ -1341,7 +1337,7 @@ impl<'a> Verifier<'a> { |
|
|
|
&self, |
|
|
|
inst: Inst, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
match &self.func.dfg.insts[inst] { |
|
|
|
ir::InstructionData::Jump { destination, .. } => { |
|
|
|
self.typecheck_block_call(inst, destination, errors)?; |
|
|
@ -1387,7 +1383,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
block: &ir::BlockCall, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let pool = &self.func.dfg.value_lists; |
|
|
|
let iter = self |
|
|
|
.func |
|
|
@ -1405,7 +1401,7 @@ impl<'a> Verifier<'a> { |
|
|
|
iter: I, |
|
|
|
variable_args: &[Value], |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let mut i = 0; |
|
|
|
|
|
|
|
for expected_type in iter { |
|
|
@ -1443,7 +1439,7 @@ impl<'a> Verifier<'a> { |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
match self.func.dfg.insts[inst] { |
|
|
|
ir::InstructionData::MultiAry { |
|
|
|
opcode: Opcode::Return, |
|
|
@ -1485,7 +1481,7 @@ impl<'a> Verifier<'a> { |
|
|
|
inst: Inst, |
|
|
|
sig_ref: SigRef, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let signature = &self.func.dfg.signatures[sig_ref]; |
|
|
|
let cc = signature.call_conv; |
|
|
|
if !cc.supports_tail_calls() { |
|
|
@ -1513,7 +1509,7 @@ impl<'a> Verifier<'a> { |
|
|
|
actual_types: impl ExactSizeIterator<Item = Type>, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
message: &str, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let expected_types = &self.func.signature.returns; |
|
|
|
if actual_types.len() != expected_types.len() { |
|
|
|
return errors.nonfatal((inst, self.context(inst), message)); |
|
|
@ -1535,7 +1531,7 @@ impl<'a> Verifier<'a> { |
|
|
|
|
|
|
|
// Check special-purpose type constraints that can't be expressed in the normal opcode
|
|
|
|
// constraints.
|
|
|
|
fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
match self.func.dfg.insts[inst] { |
|
|
|
ir::InstructionData::TableAddr { table, arg, .. } => { |
|
|
|
let index_type = self.func.dfg.value_type(arg); |
|
|
@ -1575,7 +1571,7 @@ impl<'a> Verifier<'a> { |
|
|
|
&self, |
|
|
|
cfg: &ControlFlowGraph, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
) -> VerifierStepResult { |
|
|
|
let mut expected_succs = BTreeSet::<Block>::new(); |
|
|
|
let mut got_succs = BTreeSet::<Block>::new(); |
|
|
|
let mut expected_preds = BTreeSet::<Inst>::new(); |
|
|
@ -1643,11 +1639,7 @@ impl<'a> Verifier<'a> { |
|
|
|
errors.as_result() |
|
|
|
} |
|
|
|
|
|
|
|
fn immediate_constraints( |
|
|
|
&self, |
|
|
|
inst: Inst, |
|
|
|
errors: &mut VerifierErrors, |
|
|
|
) -> VerifierStepResult<()> { |
|
|
|
fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
let inst_data = &self.func.dfg.insts[inst]; |
|
|
|
|
|
|
|
match *inst_data { |
|
|
@ -1713,7 +1705,7 @@ impl<'a> Verifier<'a> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
use crate::ir::instructions::InstructionData::UnaryImm; |
|
|
|
|
|
|
|
let inst_data = &self.func.dfg.insts[inst]; |
|
|
@ -1746,7 +1738,7 @@ impl<'a> Verifier<'a> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
let params = self |
|
|
|
.func |
|
|
|
.signature |
|
|
@ -1806,7 +1798,7 @@ impl<'a> Verifier<'a> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> { |
|
|
|
pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult { |
|
|
|
self.verify_global_values(errors)?; |
|
|
|
self.verify_memory_types(errors)?; |
|
|
|
self.verify_tables(errors)?; |
|
|
|