Expanded instruction integrity checking in the verifier, now verifying result types and entity references.
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
d3df198747
commit
477fb4d5da
@@ -233,6 +233,12 @@ impl<T: EntityRef> EntityList<T> {
|
|||||||
pool.len_of(self).unwrap_or(0)
|
pool.len_of(self).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the list is valid
|
||||||
|
pub fn is_valid(&self, pool: &ListPool<T>) -> bool {
|
||||||
|
// We consider an empty list to be valid
|
||||||
|
self.is_empty() || pool.len_of(self) != None
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the list as a slice.
|
/// Get the list as a slice.
|
||||||
pub fn as_slice<'a>(&'a self, pool: &'a ListPool<T>) -> &'a [T] {
|
pub fn as_slice<'a>(&'a self, pool: &'a ListPool<T>) -> &'a [T] {
|
||||||
let idx = self.index as usize;
|
let idx = self.index as usize;
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ impl DataFlowGraph {
|
|||||||
pub fn num_ebbs(&self) -> usize {
|
pub fn num_ebbs(&self) -> usize {
|
||||||
self.ebbs.len()
|
self.ebbs.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the given ebb reference is valid.
|
||||||
|
pub fn ebb_is_valid(&self, ebb: Ebb) -> bool {
|
||||||
|
self.ebbs.is_valid(ebb)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handling values.
|
/// Handling values.
|
||||||
@@ -95,6 +100,14 @@ impl DataFlowGraph {
|
|||||||
vref
|
vref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if a value reference is valid.
|
||||||
|
pub fn value_is_valid(&self, v: Value) -> bool {
|
||||||
|
match v.expand() {
|
||||||
|
ExpandedValue::Direct(inst) => self.insts.is_valid(inst),
|
||||||
|
ExpandedValue::Table(index) => index < self.extended_values.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the type of a value.
|
/// Get the type of a value.
|
||||||
pub fn value_type(&self, v: Value) -> Type {
|
pub fn value_type(&self, v: Value) -> Type {
|
||||||
use ir::entities::ExpandedValue::*;
|
use ir::entities::ExpandedValue::*;
|
||||||
|
|||||||
@@ -12,16 +12,17 @@
|
|||||||
//! Instruction integrity
|
//! Instruction integrity
|
||||||
//!
|
//!
|
||||||
//! - The instruction format must match the opcode.
|
//! - The instruction format must match the opcode.
|
||||||
//! TODO:
|
|
||||||
//! - All result values must be created for multi-valued instructions.
|
//! - All result values must be created for multi-valued instructions.
|
||||||
//! - Instructions with no results must have a VOID `first_type()`.
|
//! - Instructions with no results must have a VOID `first_type()`.
|
||||||
//! - All referenced entities must exist. (Values, EBBs, stack slots, ...)
|
//! - All referenced entities must exist. (Values, EBBs, stack slots, ...)
|
||||||
//!
|
//!
|
||||||
//! SSA form
|
//! SSA form
|
||||||
//!
|
//!
|
||||||
|
//! TODO:
|
||||||
//! - Values must be defined by an instruction that exists and that is inserted in
|
//! - Values must be defined by an instruction that exists and that is inserted in
|
||||||
//! an EBB, or be an argument of an existing EBB.
|
//! an EBB, or be an argument of an existing EBB.
|
||||||
//! - Values used by an instruction must dominate the instruction.
|
//! - Values used by an instruction must dominate the instruction.
|
||||||
|
//!
|
||||||
//! Control flow graph and dominator tree integrity:
|
//! Control flow graph and dominator tree integrity:
|
||||||
//!
|
//!
|
||||||
//! - All predecessors in the CFG must be branches to the EBB.
|
//! - All predecessors in the CFG must be branches to the EBB.
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number
|
//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number
|
||||||
//! of arguments must match the destination type, and the lane indexes must be in range.
|
//! of arguments must match the destination type, and the lane indexes must be in range.
|
||||||
|
|
||||||
use ir::{Function, ValueDef, Ebb, Inst};
|
use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, Value};
|
||||||
use ir::instructions::InstructionFormat;
|
use ir::instructions::InstructionFormat;
|
||||||
use ir::entities::AnyEntity;
|
use ir::entities::AnyEntity;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
@@ -147,15 +148,144 @@ impl<'a> Verifier<'a> {
|
|||||||
|
|
||||||
fn instruction_integrity(&self, inst: Inst) -> Result<()> {
|
fn instruction_integrity(&self, inst: Inst) -> Result<()> {
|
||||||
let inst_data = &self.func.dfg[inst];
|
let inst_data = &self.func.dfg[inst];
|
||||||
|
let dfg = &self.func.dfg;
|
||||||
|
|
||||||
// The instruction format matches the opcode
|
// The instruction format matches the opcode
|
||||||
if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
|
if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
|
||||||
return err!(inst, "instruction opcode doesn't match instruction format");
|
return err!(inst, "instruction opcode doesn't match instruction format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fixed_results = inst_data.opcode().constraints().fixed_results();
|
||||||
|
// var_results is 0 if we aren't a call instruction
|
||||||
|
let var_results = dfg.call_signature(inst)
|
||||||
|
.map(|sig| dfg.signatures[sig].return_types.len())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let total_results = fixed_results + var_results;
|
||||||
|
|
||||||
|
if total_results == 0 {
|
||||||
|
// Instructions with no results have a NULL `first_type()`
|
||||||
|
let ret_type = inst_data.first_type();
|
||||||
|
if ret_type != types::VOID {
|
||||||
|
return err!(inst,
|
||||||
|
"instruction expected to have NULL return value, found {}",
|
||||||
|
ret_type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// All result values for multi-valued instructions are created
|
||||||
|
let got_results = dfg.inst_results(inst).count();
|
||||||
|
if got_results != total_results {
|
||||||
|
return err!(inst,
|
||||||
|
"expected {} result values, found {}",
|
||||||
|
total_results,
|
||||||
|
got_results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.verify_entity_references(inst)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_entity_references(&self, inst: Inst) -> Result<()> {
|
||||||
|
use ir::instructions::InstructionData::*;
|
||||||
|
|
||||||
|
for &arg in self.func.dfg[inst].arguments(&self.func.dfg.value_lists) {
|
||||||
|
self.verify_value(inst, arg)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for res in self.func.dfg.inst_results(inst) {
|
||||||
|
self.verify_value(inst, res)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.func.dfg[inst] {
|
||||||
|
&MultiAry { ref args, .. } => {
|
||||||
|
self.verify_value_list(inst, args)?;
|
||||||
|
}
|
||||||
|
&Jump { destination, ref args, .. } => {
|
||||||
|
self.verify_ebb(inst, destination)?;
|
||||||
|
self.verify_value_list(inst, args)?;
|
||||||
|
}
|
||||||
|
&Branch { destination, ref args, .. } => {
|
||||||
|
self.verify_ebb(inst, destination)?;
|
||||||
|
self.verify_value_list(inst, args)?;
|
||||||
|
}
|
||||||
|
&BranchTable { table, .. } => {
|
||||||
|
self.verify_jump_table(inst, table)?;
|
||||||
|
}
|
||||||
|
&Call { func_ref, ref args, .. } => {
|
||||||
|
self.verify_func_ref(inst, func_ref)?;
|
||||||
|
self.verify_value_list(inst, args)?;
|
||||||
|
}
|
||||||
|
&IndirectCall { sig_ref, ref args, .. } => {
|
||||||
|
self.verify_sig_ref(inst, sig_ref)?;
|
||||||
|
self.verify_value_list(inst, args)?;
|
||||||
|
}
|
||||||
|
// Exhaustive list so we can't forget to add new formats
|
||||||
|
&Nullary { .. } |
|
||||||
|
&Unary { .. } |
|
||||||
|
&UnaryImm { .. } |
|
||||||
|
&UnaryIeee32 { .. } |
|
||||||
|
&UnaryIeee64 { .. } |
|
||||||
|
&UnarySplit { .. } |
|
||||||
|
&Binary { .. } |
|
||||||
|
&BinaryImm { .. } |
|
||||||
|
&BinaryOverflow { .. } |
|
||||||
|
&Ternary { .. } |
|
||||||
|
&InsertLane { .. } |
|
||||||
|
&ExtractLane { .. } |
|
||||||
|
&IntCompare { .. } |
|
||||||
|
&FloatCompare { .. } => {}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_ebb(&self, inst: Inst, e: Ebb) -> Result<()> {
|
||||||
|
if !self.func.dfg.ebb_is_valid(e) {
|
||||||
|
err!(inst, "invalid ebb reference {}", e)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_sig_ref(&self, inst: Inst, s: SigRef) -> Result<()> {
|
||||||
|
if !self.func.dfg.signatures.is_valid(s) {
|
||||||
|
err!(inst, "invalid signature reference {}", s)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_func_ref(&self, inst: Inst, f: FuncRef) -> Result<()> {
|
||||||
|
if !self.func.dfg.ext_funcs.is_valid(f) {
|
||||||
|
err!(inst, "invalid function reference {}", f)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_value_list(&self, inst: Inst, l: &ValueList) -> Result<()> {
|
||||||
|
if !l.is_valid(&self.func.dfg.value_lists) {
|
||||||
|
err!(inst, "invalid value list reference {:?}", l)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_jump_table(&self, inst: Inst, j: JumpTable) -> Result<()> {
|
||||||
|
if !self.func.jump_tables.is_valid(j) {
|
||||||
|
err!(inst, "invalid jump table reference {}", j)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_value(&self, inst: Inst, v: Value) -> Result<()> {
|
||||||
|
if !self.func.dfg.value_is_valid(v) {
|
||||||
|
err!(inst, "invalid value reference {}", v)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(&self) -> Result<()> {
|
pub fn run(&self) -> Result<()> {
|
||||||
for ebb in self.func.layout.ebbs() {
|
for ebb in self.func.layout.ebbs() {
|
||||||
for inst in self.func.layout.ebb_insts(ebb) {
|
for inst in self.func.layout.ebb_insts(ebb) {
|
||||||
|
|||||||
Reference in New Issue
Block a user