Path: blob/main/cranelift/codegen/src/verifier/mod.rs
3069 views
//! A verifier for ensuring that functions are well formed.1//! It verifies:2//!3//! block integrity4//!5//! - All instructions reached from the `block_insts` iterator must belong to6//! the block as reported by `inst_block()`.7//! - Every block must end in a terminator instruction, and no other instruction8//! can be a terminator.9//! - Every value in the `block_params` iterator belongs to the block as reported by `value_block`.10//!11//! Instruction integrity12//!13//! - The instruction format must match the opcode.14//! - All result values must be created for multi-valued instructions.15//! - All referenced entities must exist. (Values, blocks, stack slots, ...)16//! - Instructions must not reference (eg. branch to) the entry block.17//!18//! SSA form19//!20//! - Values must be defined by an instruction that exists and that is inserted in21//! a block, or be an argument of an existing block.22//! - Values used by an instruction must dominate the instruction.23//!24//! Control flow graph and dominator tree integrity:25//!26//! - All predecessors in the CFG must be branches to the block.27//! - All branches to a block must be present in the CFG.28//! - A recomputed dominator tree is identical to the existing one.29//! - The entry block must not be a cold block.30//!31//! Type checking32//!33//! - Compare input and output values against the opcode's type constraints.34//! For polymorphic opcodes, determine the controlling type variable first.35//! - Branches and jumps must pass arguments to destination blocks that match the36//! expected types exactly. The number of arguments must match.37//! - All blocks in a jump table must take no arguments.38//! - Function calls are type checked against their signature.39//! - The entry block must take arguments that match the signature of the current40//! function.41//! - All return instructions must have return value operands matching the current42//! function signature.43//!44//! Global values45//!46//! - Detect cycles in global values.47//! - Detect use of 'vmctx' global value when no corresponding parameter is defined.48//!49//! Memory types50//!51//! - Ensure that struct fields are in offset order.52//! - Ensure that struct fields are completely within the overall53//! struct size, and do not overlap.54//!55//! TODO:56//! Ad hoc checking57//!58//! - Stack slot loads and stores must be in-bounds.59//! - Immediate constraints for certain opcodes, like `udiv_imm v3, 0`.60//! - `Insertlane` and `extractlane` instructions have immediate lane numbers that must be in61//! range for their polymorphic type.62//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number63//! of arguments must match the destination type, and the lane indexes must be in range.6465use crate::dbg::DisplayList;66use crate::dominator_tree::DominatorTree;67use crate::entity::SparseSet;68use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};69use crate::ir::entities::AnyEntity;70use crate::ir::instructions::{CallInfo, InstructionFormat, ResolvedConstraint};71use crate::ir::{self, ArgumentExtension, BlockArg, ExceptionTable};72use crate::ir::{73ArgumentPurpose, Block, Constant, DynamicStackSlot, FuncRef, Function, GlobalValue, Inst,74JumpTable, MemFlags, MemoryTypeData, Opcode, SigRef, StackSlot, Type, Value, ValueDef,75ValueList, types,76};77use crate::ir::{ExceptionTableItem, Signature};78use crate::isa::{CallConv, TargetIsa};79use crate::print_errors::pretty_verifier_error;80use crate::settings::FlagsOrIsa;81use crate::timing;82use alloc::collections::BTreeSet;83use alloc::string::{String, ToString};84use alloc::vec::Vec;85use core::fmt::{self, Display, Formatter};86use cranelift_entity::packed_option::ReservedValue;8788/// A verifier error.89#[derive(Debug, PartialEq, Eq, Clone)]90pub struct VerifierError {91/// The entity causing the verifier error.92pub location: AnyEntity,93/// Optionally provide some context for the given location; e.g., for `inst42` provide94/// `Some("v3 = iconst.i32 0")` for more comprehensible errors.95pub context: Option<String>,96/// The error message.97pub message: String,98}99100// This is manually implementing Error and Display instead of using thiserror to reduce the amount101// of dependencies used by Cranelift.102impl core::error::Error for VerifierError {}103104impl Display for VerifierError {105fn fmt(&self, f: &mut Formatter) -> fmt::Result {106match &self.context {107None => write!(f, "{}: {}", self.location, self.message),108Some(context) => write!(f, "{} ({}): {}", self.location, context, self.message),109}110}111}112113/// Convenience converter for making error-reporting less verbose.114///115/// Converts a tuple of `(location, context, message)` to a `VerifierError`.116/// ```117/// use cranelift_codegen::verifier::VerifierErrors;118/// use cranelift_codegen::ir::Inst;119/// let mut errors = VerifierErrors::new();120/// errors.report((Inst::from_u32(42), "v3 = iadd v1, v2", "iadd cannot be used with values of this type"));121/// // note the double parenthenses to use this syntax122/// ```123impl<L, C, M> From<(L, C, M)> for VerifierError124where125L: Into<AnyEntity>,126C: Into<String>,127M: Into<String>,128{129fn from(items: (L, C, M)) -> Self {130let (location, context, message) = items;131Self {132location: location.into(),133context: Some(context.into()),134message: message.into(),135}136}137}138139/// Convenience converter for making error-reporting less verbose.140///141/// Same as above but without `context`.142impl<L, M> From<(L, M)> for VerifierError143where144L: Into<AnyEntity>,145M: Into<String>,146{147fn from(items: (L, M)) -> Self {148let (location, message) = items;149Self {150location: location.into(),151context: None,152message: message.into(),153}154}155}156157/// Result of a step in the verification process.158///159/// Functions that return `VerifierStepResult` should also take a160/// mutable reference to `VerifierErrors` as argument in order to report161/// errors.162///163/// Here, `Ok` represents a step that **did not lead to a fatal error**,164/// meaning that the verification process may continue. However, other (non-fatal)165/// errors might have been reported through the previously mentioned `VerifierErrors`166/// argument.167pub type VerifierStepResult = Result<(), ()>;168169/// Result of a verification operation.170///171/// Unlike `VerifierStepResult` which may be `Ok` while still having reported172/// errors, this type always returns `Err` if an error (fatal or not) was reported.173pub type VerifierResult<T> = Result<T, VerifierErrors>;174175/// List of verifier errors.176#[derive(Debug, Default, PartialEq, Eq, Clone)]177pub struct VerifierErrors(pub Vec<VerifierError>);178179// This is manually implementing Error and Display instead of using thiserror to reduce the amount180// of dependencies used by Cranelift.181impl core::error::Error for VerifierErrors {}182183impl VerifierErrors {184/// Return a new `VerifierErrors` struct.185#[inline]186pub fn new() -> Self {187Self(Vec::new())188}189190/// Return whether no errors were reported.191#[inline]192pub fn is_empty(&self) -> bool {193self.0.is_empty()194}195196/// Return whether one or more errors were reported.197#[inline]198pub fn has_error(&self) -> bool {199!self.0.is_empty()200}201202/// Return a `VerifierStepResult` that is fatal if at least one error was reported,203/// and non-fatal otherwise.204#[inline]205pub fn as_result(&self) -> VerifierStepResult {206if self.is_empty() { Ok(()) } else { Err(()) }207}208209/// Report an error, adding it to the list of errors.210pub fn report(&mut self, error: impl Into<VerifierError>) {211self.0.push(error.into());212}213214/// Report a fatal error and return `Err`.215pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {216self.report(error);217Err(())218}219220/// Report a non-fatal error and return `Ok`.221pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {222self.report(error);223Ok(())224}225}226227impl From<Vec<VerifierError>> for VerifierErrors {228fn from(v: Vec<VerifierError>) -> Self {229Self(v)230}231}232233impl From<VerifierErrors> for Vec<VerifierError> {234fn from(errors: VerifierErrors) -> Vec<VerifierError> {235errors.0236}237}238239impl From<VerifierErrors> for VerifierResult<()> {240fn from(errors: VerifierErrors) -> VerifierResult<()> {241if errors.is_empty() {242Ok(())243} else {244Err(errors)245}246}247}248249impl Display for VerifierErrors {250fn fmt(&self, f: &mut Formatter) -> fmt::Result {251for err in &self.0 {252writeln!(f, "- {err}")?;253}254Ok(())255}256}257258/// Verify `func`.259pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(260func: &Function,261fisa: FOI,262) -> VerifierResult<()> {263let _tt = timing::verifier();264let mut errors = VerifierErrors::default();265let verifier = Verifier::new(func, fisa.into());266let result = verifier.run(&mut errors);267if errors.is_empty() {268result.unwrap();269Ok(())270} else {271Err(errors)272}273}274275/// Verify `func` after checking the integrity of associated context data structures `cfg` and276/// `domtree`.277pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>(278func: &Function,279cfg: &ControlFlowGraph,280domtree: &DominatorTree,281fisa: FOI,282errors: &mut VerifierErrors,283) -> VerifierStepResult {284let _tt = timing::verifier();285let verifier = Verifier::new(func, fisa.into());286if cfg.is_valid() {287verifier.cfg_integrity(cfg, errors)?;288}289if domtree.is_valid() {290verifier.domtree_integrity(domtree, errors)?;291}292verifier.run(errors)293}294295#[derive(Clone, Copy, Debug)]296enum BlockCallTargetType {297Normal,298ExNormalRet,299Exception,300}301302struct Verifier<'a> {303func: &'a Function,304expected_cfg: ControlFlowGraph,305expected_domtree: DominatorTree,306isa: Option<&'a dyn TargetIsa>,307}308309impl<'a> Verifier<'a> {310pub fn new(func: &'a Function, fisa: FlagsOrIsa<'a>) -> Self {311let expected_cfg = ControlFlowGraph::with_function(func);312let expected_domtree = DominatorTree::with_function(func, &expected_cfg);313Self {314func,315expected_cfg,316expected_domtree,317isa: fisa.isa,318}319}320321/// Determine a contextual error string for an instruction.322#[inline]323fn context(&self, inst: Inst) -> String {324self.func.dfg.display_inst(inst).to_string()325}326327// Check for:328// - cycles in the global value declarations.329// - use of 'vmctx' when no special parameter declares it.330fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult {331let mut cycle_seen = false;332let mut seen = SparseSet::new();333334'gvs: for gv in self.func.global_values.keys() {335seen.clear();336seen.insert(gv);337338let mut cur = gv;339loop {340match self.func.global_values[cur] {341ir::GlobalValueData::Load { base, .. }342| ir::GlobalValueData::IAddImm { base, .. } => {343if seen.insert(base).is_some() {344if !cycle_seen {345errors.report((346gv,347format!("global value cycle: {}", DisplayList(seen.as_slice())),348));349// ensures we don't report the cycle multiple times350cycle_seen = true;351}352continue 'gvs;353}354355cur = base;356}357_ => break,358}359}360361match self.func.global_values[gv] {362ir::GlobalValueData::VMContext { .. } => {363if self364.func365.special_param(ir::ArgumentPurpose::VMContext)366.is_none()367{368errors.report((gv, format!("undeclared vmctx reference {gv}")));369}370}371ir::GlobalValueData::IAddImm {372base, global_type, ..373} => {374if !global_type.is_int() {375errors.report((376gv,377format!("iadd_imm global value with non-int type {global_type}"),378));379} else if let Some(isa) = self.isa {380let base_type = self.func.global_values[base].global_type(isa);381if global_type != base_type {382errors.report((383gv,384format!(385"iadd_imm type {global_type} differs from operand type {base_type}"386),387));388}389}390}391ir::GlobalValueData::Load { base, .. } => {392if let Some(isa) = self.isa {393let base_type = self.func.global_values[base].global_type(isa);394let pointer_type = isa.pointer_type();395if base_type != pointer_type {396errors.report((397gv,398format!(399"base {base} has type {base_type}, which is not the pointer type {pointer_type}"400),401));402}403}404}405_ => {}406}407}408409// Invalid global values shouldn't stop us from verifying the rest of the function410Ok(())411}412413fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult {414// Verify that all fields are statically-sized and lie within415// the struct, do not overlap, and are in offset order416for (mt, mt_data) in &self.func.memory_types {417match mt_data {418MemoryTypeData::Struct { size, fields } => {419let mut last_offset = 0;420for field in fields {421if field.offset < last_offset {422errors.report((423mt,424format!(425"memory type {} has a field at offset {}, which is out-of-order",426mt, field.offset427),428));429}430last_offset = match field.offset.checked_add(u64::from(field.ty.bytes())) {431Some(o) => o,432None => {433errors.report((434mt,435format!(436"memory type {} has a field at offset {} of size {}; offset plus size overflows a u64",437mt, field.offset, field.ty.bytes()),438));439break;440}441};442443if last_offset > *size {444errors.report((445mt,446format!(447"memory type {} has a field at offset {} of size {} that overflows the struct size {}",448mt, field.offset, field.ty.bytes(), *size),449));450}451}452}453_ => {}454}455}456457Ok(())458}459460/// Check that the given block can be encoded as a BB, by checking that only461/// branching instructions are ending the block.462fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult {463match self.func.is_block_basic(block) {464Ok(()) => Ok(()),465Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),466}467}468469fn block_integrity(470&self,471block: Block,472inst: Inst,473errors: &mut VerifierErrors,474) -> VerifierStepResult {475let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();476let is_last_inst = self.func.layout.last_inst(block) == Some(inst);477478if is_terminator && !is_last_inst {479// Terminating instructions only occur at the end of blocks.480return errors.fatal((481inst,482self.context(inst),483format!("a terminator instruction was encountered before the end of {block}"),484));485}486if is_last_inst && !is_terminator {487return errors.fatal((block, "block does not end in a terminator instruction"));488}489490// Instructions belong to the correct block.491let inst_block = self.func.layout.inst_block(inst);492if inst_block != Some(block) {493return errors.fatal((494inst,495self.context(inst),496format!("should belong to {block} not {inst_block:?}"),497));498}499500// Parameters belong to the correct block.501for &arg in self.func.dfg.block_params(block) {502match self.func.dfg.value_def(arg) {503ValueDef::Param(arg_block, _) => {504if block != arg_block {505return errors.fatal((arg, format!("does not belong to {block}")));506}507}508_ => {509return errors.fatal((arg, "expected an argument, found a result"));510}511}512}513514Ok(())515}516517fn instruction_integrity(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {518let inst_data = &self.func.dfg.insts[inst];519let dfg = &self.func.dfg;520521// The instruction format matches the opcode522if inst_data.opcode().format() != InstructionFormat::from(inst_data) {523return errors.fatal((524inst,525self.context(inst),526"instruction opcode doesn't match instruction format",527));528}529530let expected_num_results = dfg.num_expected_results_for_verifier(inst);531532// All result values for multi-valued instructions are created533let got_results = dfg.inst_results(inst).len();534if got_results != expected_num_results {535return errors.fatal((536inst,537self.context(inst),538format!("expected {expected_num_results} result values, found {got_results}"),539));540}541542self.verify_entity_references(inst, errors)543}544545fn verify_entity_references(546&self,547inst: Inst,548errors: &mut VerifierErrors,549) -> VerifierStepResult {550use crate::ir::instructions::InstructionData::*;551552for arg in self.func.dfg.inst_values(inst) {553self.verify_inst_arg(inst, arg, errors)?;554555// All used values must be attached to something.556let original = self.func.dfg.resolve_aliases(arg);557if !self.func.dfg.value_is_attached(original) {558errors.report((559inst,560self.context(inst),561format!("argument {arg} -> {original} is not attached"),562));563}564}565566for &res in self.func.dfg.inst_results(inst) {567self.verify_inst_result(inst, res, errors)?;568}569570match self.func.dfg.insts[inst] {571MultiAry { ref args, .. } => {572self.verify_value_list(inst, args, errors)?;573}574Jump { destination, .. } => {575self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;576}577Brif {578arg,579blocks: [block_then, block_else],580..581} => {582self.verify_value(inst, arg, errors)?;583self.verify_block(inst, block_then.block(&self.func.dfg.value_lists), errors)?;584self.verify_block(inst, block_else.block(&self.func.dfg.value_lists), errors)?;585}586BranchTable { table, .. } => {587self.verify_jump_table(inst, table, errors)?;588}589Call {590opcode,591func_ref,592ref args,593..594} => {595self.verify_func_ref(inst, func_ref, errors)?;596self.verify_value_list(inst, args, errors)?;597self.verify_callee_patchability(inst, func_ref, opcode, errors)?;598}599CallIndirect {600sig_ref, ref args, ..601} => {602self.verify_sig_ref(inst, sig_ref, errors)?;603self.verify_value_list(inst, args, errors)?;604}605TryCall {606func_ref,607ref args,608exception,609..610} => {611self.verify_func_ref(inst, func_ref, errors)?;612self.verify_value_list(inst, args, errors)?;613self.verify_exception_table(inst, exception, errors)?;614self.verify_exception_compatible_abi(inst, exception, errors)?;615}616TryCallIndirect {617ref args,618exception,619..620} => {621self.verify_value_list(inst, args, errors)?;622self.verify_exception_table(inst, exception, errors)?;623self.verify_exception_compatible_abi(inst, exception, errors)?;624}625FuncAddr { func_ref, .. } => {626self.verify_func_ref(inst, func_ref, errors)?;627}628StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {629self.verify_stack_slot(inst, stack_slot, errors)?;630}631DynamicStackLoad {632dynamic_stack_slot, ..633}634| DynamicStackStore {635dynamic_stack_slot, ..636} => {637self.verify_dynamic_stack_slot(inst, dynamic_stack_slot, errors)?;638}639UnaryGlobalValue { global_value, .. } => {640self.verify_global_value(inst, global_value, errors)?;641}642NullAry {643opcode: Opcode::GetPinnedReg,644}645| Unary {646opcode: Opcode::SetPinnedReg,647..648} => {649if let Some(isa) = &self.isa {650if !isa.flags().enable_pinned_reg() {651return errors.fatal((652inst,653self.context(inst),654"GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg",655));656}657} else {658return errors.fatal((659inst,660self.context(inst),661"GetPinnedReg/SetPinnedReg need an ISA!",662));663}664}665NullAry {666opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,667} => {668if let Some(isa) = &self.isa {669// Backends may already rely on this check implicitly, so do670// not relax it without verifying that it is safe to do so.671if !isa.flags().preserve_frame_pointers() {672return errors.fatal((673inst,674self.context(inst),675"`get_frame_pointer`/`get_return_address` cannot be used without \676enabling `preserve_frame_pointers`",677));678}679} else {680return errors.fatal((681inst,682self.context(inst),683"`get_frame_pointer`/`get_return_address` require an ISA!",684));685}686}687LoadNoOffset {688opcode: Opcode::Bitcast,689flags,690arg,691} => {692self.verify_bitcast(inst, flags, arg, errors)?;693}694LoadNoOffset { opcode, arg, .. } if opcode.can_load() => {695self.verify_is_address(inst, arg, errors)?;696}697Load { opcode, arg, .. } if opcode.can_load() => {698self.verify_is_address(inst, arg, errors)?;699}700AtomicCas {701opcode,702args: [p, _, _],703..704} if opcode.can_load() || opcode.can_store() => {705self.verify_is_address(inst, p, errors)?;706}707AtomicRmw {708opcode,709args: [p, _],710..711} if opcode.can_load() || opcode.can_store() => {712self.verify_is_address(inst, p, errors)?;713}714Store {715opcode,716args: [_, p],717..718} if opcode.can_store() => {719self.verify_is_address(inst, p, errors)?;720}721StoreNoOffset {722opcode,723args: [_, p],724..725} if opcode.can_store() => {726self.verify_is_address(inst, p, errors)?;727}728UnaryConst {729opcode: opcode @ (Opcode::Vconst | Opcode::F128const),730constant_handle,731..732} => {733self.verify_constant_size(inst, opcode, constant_handle, errors)?;734}735736ExceptionHandlerAddress { block, imm, .. } => {737self.verify_block(inst, block, errors)?;738self.verify_try_call_handler_index(inst, block, imm.into(), errors)?;739}740741// Exhaustive list so we can't forget to add new formats742AtomicCas { .. }743| AtomicRmw { .. }744| LoadNoOffset { .. }745| StoreNoOffset { .. }746| Unary { .. }747| UnaryConst { .. }748| UnaryImm { .. }749| UnaryIeee16 { .. }750| UnaryIeee32 { .. }751| UnaryIeee64 { .. }752| Binary { .. }753| BinaryImm8 { .. }754| BinaryImm64 { .. }755| Ternary { .. }756| TernaryImm8 { .. }757| Shuffle { .. }758| IntAddTrap { .. }759| IntCompare { .. }760| IntCompareImm { .. }761| FloatCompare { .. }762| Load { .. }763| Store { .. }764| Trap { .. }765| CondTrap { .. }766| NullAry { .. } => {}767}768769Ok(())770}771772fn verify_block(773&self,774loc: impl Into<AnyEntity>,775e: Block,776errors: &mut VerifierErrors,777) -> VerifierStepResult {778if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {779return errors.fatal((loc, format!("invalid block reference {e}")));780}781if let Some(entry_block) = self.func.layout.entry_block() {782if e == entry_block {783return errors.fatal((loc, format!("invalid reference to entry block {e}")));784}785}786Ok(())787}788789fn verify_sig_ref(790&self,791inst: Inst,792s: SigRef,793errors: &mut VerifierErrors,794) -> VerifierStepResult {795if !self.func.dfg.signatures.is_valid(s) {796errors.fatal((797inst,798self.context(inst),799format!("invalid signature reference {s}"),800))801} else {802Ok(())803}804}805806fn verify_func_ref(807&self,808inst: Inst,809f: FuncRef,810errors: &mut VerifierErrors,811) -> VerifierStepResult {812if !self.func.dfg.ext_funcs.is_valid(f) {813errors.nonfatal((814inst,815self.context(inst),816format!("invalid function reference {f}"),817))818} else {819Ok(())820}821}822823fn verify_stack_slot(824&self,825inst: Inst,826ss: StackSlot,827errors: &mut VerifierErrors,828) -> VerifierStepResult {829if !self.func.sized_stack_slots.is_valid(ss) {830errors.nonfatal((inst, self.context(inst), format!("invalid stack slot {ss}")))831} else {832Ok(())833}834}835836fn verify_dynamic_stack_slot(837&self,838inst: Inst,839ss: DynamicStackSlot,840errors: &mut VerifierErrors,841) -> VerifierStepResult {842if !self.func.dynamic_stack_slots.is_valid(ss) {843errors.nonfatal((844inst,845self.context(inst),846format!("invalid dynamic stack slot {ss}"),847))848} else {849Ok(())850}851}852853fn verify_global_value(854&self,855inst: Inst,856gv: GlobalValue,857errors: &mut VerifierErrors,858) -> VerifierStepResult {859if !self.func.global_values.is_valid(gv) {860errors.nonfatal((861inst,862self.context(inst),863format!("invalid global value {gv}"),864))865} else {866Ok(())867}868}869870fn verify_value_list(871&self,872inst: Inst,873l: &ValueList,874errors: &mut VerifierErrors,875) -> VerifierStepResult {876if !l.is_valid(&self.func.dfg.value_lists) {877errors.nonfatal((878inst,879self.context(inst),880format!("invalid value list reference {l:?}"),881))882} else {883Ok(())884}885}886887fn verify_jump_table(888&self,889inst: Inst,890j: JumpTable,891errors: &mut VerifierErrors,892) -> VerifierStepResult {893if !self.func.stencil.dfg.jump_tables.is_valid(j) {894errors.nonfatal((895inst,896self.context(inst),897format!("invalid jump table reference {j}"),898))899} else {900let pool = &self.func.stencil.dfg.value_lists;901for block in self.func.stencil.dfg.jump_tables[j].all_branches() {902self.verify_block(inst, block.block(pool), errors)?;903}904Ok(())905}906}907908fn verify_exception_table(909&self,910inst: Inst,911et: ExceptionTable,912errors: &mut VerifierErrors,913) -> VerifierStepResult {914// Verify that the exception table reference itself is valid.915if !self.func.stencil.dfg.exception_tables.is_valid(et) {916errors.nonfatal((917inst,918self.context(inst),919format!("invalid exception table reference {et}"),920))?;921}922923let pool = &self.func.stencil.dfg.value_lists;924let exdata = &self.func.stencil.dfg.exception_tables[et];925926// Verify that the exception table's signature reference927// is valid.928self.verify_sig_ref(inst, exdata.signature(), errors)?;929930// Verify that the exception table's block references are valid.931for block in exdata.all_branches() {932self.verify_block(inst, block.block(pool), errors)?;933}934Ok(())935}936937fn verify_exception_compatible_abi(938&self,939inst: Inst,940et: ExceptionTable,941errors: &mut VerifierErrors,942) -> VerifierStepResult {943let callee_sig_ref = self.func.dfg.exception_tables[et].signature();944let callee_sig = &self.func.dfg.signatures[callee_sig_ref];945let callee_call_conv = callee_sig.call_conv;946if !callee_call_conv.supports_exceptions() {947errors.nonfatal((948inst,949self.context(inst),950format!(951"calling convention `{callee_call_conv}` of callee does not support exceptions"952),953))?;954}955Ok(())956}957958fn verify_callee_patchability(959&self,960inst: Inst,961func_ref: FuncRef,962opcode: Opcode,963errors: &mut VerifierErrors,964) -> VerifierStepResult {965let ir::ExtFuncData {966patchable,967colocated,968signature,969name: _,970} = self.func.dfg.ext_funcs[func_ref];971let signature = &self.func.dfg.signatures[signature];972if patchable && (opcode == Opcode::ReturnCall || opcode == Opcode::ReturnCallIndirect) {973errors.fatal((974inst,975self.context(inst),976"patchable funcref cannot be used in a return_call".to_string(),977))?;978}979if patchable && !colocated {980errors.fatal((981inst,982self.context(inst),983"patchable call to non-colocated function".to_string(),984))?;985}986if patchable && !signature.returns.is_empty() {987errors.fatal((988inst,989self.context(inst),990"patchable call cannot occur to a function with return values".to_string(),991))?;992}993Ok(())994}995996fn verify_value(997&self,998loc_inst: Inst,999v: Value,1000errors: &mut VerifierErrors,1001) -> VerifierStepResult {1002let dfg = &self.func.dfg;1003if !dfg.value_is_valid(v) {1004errors.nonfatal((1005loc_inst,1006self.context(loc_inst),1007format!("invalid value reference {v}"),1008))1009} else {1010Ok(())1011}1012}10131014fn verify_inst_arg(1015&self,1016loc_inst: Inst,1017v: Value,1018errors: &mut VerifierErrors,1019) -> VerifierStepResult {1020self.verify_value(loc_inst, v, errors)?;10211022let dfg = &self.func.dfg;1023let loc_block = self1024.func1025.layout1026.inst_block(loc_inst)1027.expect("Instruction not in layout.");1028let is_reachable = self.expected_domtree.is_reachable(loc_block);10291030// SSA form1031match dfg.value_def(v) {1032ValueDef::Result(def_inst, _) => {1033// Value is defined by an instruction that exists.1034if !dfg.inst_is_valid(def_inst) {1035return errors.fatal((1036loc_inst,1037self.context(loc_inst),1038format!("{v} is defined by invalid instruction {def_inst}"),1039));1040}1041// Defining instruction is inserted in a block.1042if self.func.layout.inst_block(def_inst) == None {1043return errors.fatal((1044loc_inst,1045self.context(loc_inst),1046format!("{v} is defined by {def_inst} which has no block"),1047));1048}1049// Defining instruction dominates the instruction that uses the value.1050if is_reachable {1051if !self1052.expected_domtree1053.dominates(def_inst, loc_inst, &self.func.layout)1054{1055return errors.fatal((1056loc_inst,1057self.context(loc_inst),1058format!("uses value {v} from non-dominating {def_inst}"),1059));1060}1061if def_inst == loc_inst {1062return errors.fatal((1063loc_inst,1064self.context(loc_inst),1065format!("uses value {v} from itself"),1066));1067}1068}1069}1070ValueDef::Param(block, _) => {1071// Value is defined by an existing block.1072if !dfg.block_is_valid(block) {1073return errors.fatal((1074loc_inst,1075self.context(loc_inst),1076format!("{v} is defined by invalid block {block}"),1077));1078}1079// Defining block is inserted in the layout1080if !self.func.layout.is_block_inserted(block) {1081return errors.fatal((1082loc_inst,1083self.context(loc_inst),1084format!("{v} is defined by {block} which is not in the layout"),1085));1086}1087let user_block = self.func.layout.inst_block(loc_inst).expect("Expected instruction to be in a block as we're traversing code already in layout");1088// The defining block dominates the instruction using this value.1089if is_reachable && !self.expected_domtree.block_dominates(block, user_block) {1090return errors.fatal((1091loc_inst,1092self.context(loc_inst),1093format!("uses value arg from non-dominating {block}"),1094));1095}1096}1097ValueDef::Union(_, _) => {1098// Nothing: union nodes themselves have no location,1099// so we cannot check any dominance properties.1100}1101}1102Ok(())1103}11041105fn verify_inst_result(1106&self,1107loc_inst: Inst,1108v: Value,1109errors: &mut VerifierErrors,1110) -> VerifierStepResult {1111self.verify_value(loc_inst, v, errors)?;11121113match self.func.dfg.value_def(v) {1114ValueDef::Result(def_inst, _) => {1115if def_inst != loc_inst {1116errors.fatal((1117loc_inst,1118self.context(loc_inst),1119format!("instruction result {v} is not defined by the instruction"),1120))1121} else {1122Ok(())1123}1124}1125ValueDef::Param(_, _) => errors.fatal((1126loc_inst,1127self.context(loc_inst),1128format!("instruction result {v} is not defined by the instruction"),1129)),1130ValueDef::Union(_, _) => errors.fatal((1131loc_inst,1132self.context(loc_inst),1133format!("instruction result {v} is a union node"),1134)),1135}1136}11371138fn verify_bitcast(1139&self,1140inst: Inst,1141flags: MemFlags,1142arg: Value,1143errors: &mut VerifierErrors,1144) -> VerifierStepResult {1145let typ = self.func.dfg.ctrl_typevar(inst);1146let value_type = self.func.dfg.value_type(arg);11471148if typ.bits() != value_type.bits() {1149errors.fatal((1150inst,1151format!(1152"The bitcast argument {} has a type of {} bits, which doesn't match an expected type of {} bits",1153arg,1154value_type.bits(),1155typ.bits()1156),1157))1158} else if flags != MemFlags::new()1159&& flags != MemFlags::new().with_endianness(ir::Endianness::Little)1160&& flags != MemFlags::new().with_endianness(ir::Endianness::Big)1161{1162errors.fatal((1163inst,1164"The bitcast instruction only accepts the `big` or `little` memory flags",1165))1166} else if flags == MemFlags::new() && typ.lane_count() != value_type.lane_count() {1167errors.fatal((1168inst,1169"Byte order specifier required for bitcast instruction changing lane count",1170))1171} else {1172Ok(())1173}1174}11751176fn verify_constant_size(1177&self,1178inst: Inst,1179opcode: Opcode,1180constant: Constant,1181errors: &mut VerifierErrors,1182) -> VerifierStepResult {1183let type_size = match opcode {1184Opcode::F128const => types::F128.bytes(),1185Opcode::Vconst => self.func.dfg.ctrl_typevar(inst).bytes(),1186_ => unreachable!("unexpected opcode {opcode:?}"),1187} as usize;1188let constant_size = self.func.dfg.constants.get(constant).len();1189if type_size != constant_size {1190errors.fatal((1191inst,1192format!(1193"The instruction expects {constant} to have a size of {type_size} bytes but it has {constant_size}"1194),1195))1196} else {1197Ok(())1198}1199}12001201fn verify_is_address(1202&self,1203loc_inst: Inst,1204v: Value,1205errors: &mut VerifierErrors,1206) -> VerifierStepResult {1207if let Some(isa) = self.isa {1208let pointer_width = isa.triple().pointer_width()?;1209let value_type = self.func.dfg.value_type(v);1210let expected_width = pointer_width.bits() as u32;1211let value_width = value_type.bits();1212if expected_width != value_width {1213errors.nonfatal((1214loc_inst,1215self.context(loc_inst),1216format!("invalid pointer width (got {value_width}, expected {expected_width}) encountered {v}"),1217))1218} else {1219Ok(())1220}1221} else {1222Ok(())1223}1224}12251226fn domtree_integrity(1227&self,1228domtree: &DominatorTree,1229errors: &mut VerifierErrors,1230) -> VerifierStepResult {1231// We consider two `DominatorTree`s to be equal if they return the same immediate1232// dominator for each block. Therefore the current domtree is valid if it matches the freshly1233// computed one.1234for block in self.func.layout.blocks() {1235let expected = self.expected_domtree.idom(block);1236let got = domtree.idom(block);1237if got != expected {1238return errors.fatal((1239block,1240format!("invalid domtree, expected idom({block}) = {expected:?}, got {got:?}"),1241));1242}1243}1244// We also verify if the postorder defined by `DominatorTree` is sane1245if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {1246return errors.fatal((1247AnyEntity::Function,1248"incorrect number of Blocks in postorder traversal",1249));1250}1251for (index, (&test_block, &true_block)) in domtree1252.cfg_postorder()1253.iter()1254.zip(self.expected_domtree.cfg_postorder().iter())1255.enumerate()1256{1257if test_block != true_block {1258return errors.fatal((1259test_block,1260format!(1261"invalid domtree, postorder block number {index} should be {true_block}, got {test_block}"1262),1263));1264}1265}1266Ok(())1267}12681269fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult {1270if let Some(block) = self.func.layout.entry_block() {1271let expected_types = &self.func.signature.params;1272let block_param_count = self.func.dfg.num_block_params(block);12731274if block_param_count != expected_types.len() {1275return errors.fatal((1276block,1277format!(1278"entry block parameters ({}) must match function signature ({})",1279block_param_count,1280expected_types.len()1281),1282));1283}12841285for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {1286let arg_type = self.func.dfg.value_type(arg);1287if arg_type != expected_types[i].value_type {1288errors.report((1289block,1290format!(1291"entry block parameter {} expected to have type {}, got {}",1292i, expected_types[i], arg_type1293),1294));1295}1296}1297}12981299errors.as_result()1300}13011302fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult {1303if let Some(entry_block) = self.func.layout.entry_block() {1304if self.func.layout.is_cold(entry_block) {1305return errors1306.fatal((entry_block, format!("entry block cannot be marked as cold")));1307}1308}1309errors.as_result()1310}13111312fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {1313let inst_data = &self.func.dfg.insts[inst];1314let constraints = inst_data.opcode().constraints();13151316let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {1317// For polymorphic opcodes, determine the controlling type variable first.1318let ctrl_type = self.func.dfg.ctrl_typevar(inst);13191320if !value_typeset.contains(ctrl_type) {1321errors.report((1322inst,1323self.context(inst),1324format!(1325"has an invalid controlling type {ctrl_type} (allowed set is {value_typeset:?})"1326),1327));1328}13291330ctrl_type1331} else {1332// Non-polymorphic instructions don't check the controlling type variable, so `Option`1333// is unnecessary and we can just make it `INVALID`.1334types::INVALID1335};13361337// Typechecking instructions is never fatal1338let _ = self.typecheck_results(inst, ctrl_type, errors);1339let _ = self.typecheck_fixed_args(inst, ctrl_type, errors);1340let _ = self.typecheck_variable_args(inst, errors);1341let _ = self.typecheck_return(inst, errors);1342let _ = self.typecheck_special(inst, errors);13431344Ok(())1345}13461347fn typecheck_results(1348&self,1349inst: Inst,1350ctrl_type: Type,1351errors: &mut VerifierErrors,1352) -> VerifierStepResult {1353let mut i = 0;1354for &result in self.func.dfg.inst_results(inst) {1355let result_type = self.func.dfg.value_type(result);1356let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type);1357if let Some(expected_type) = expected_type {1358if result_type != expected_type {1359errors.report((1360inst,1361self.context(inst),1362format!(1363"expected result {i} ({result}) to have type {expected_type}, found {result_type}"1364),1365));1366}1367} else {1368return errors.nonfatal((1369inst,1370self.context(inst),1371"has more result values than expected",1372));1373}1374i += 1;1375}13761377// There aren't any more result types left.1378if self.func.dfg.compute_result_type(inst, i, ctrl_type) != None {1379return errors.nonfatal((1380inst,1381self.context(inst),1382"has fewer result values than expected",1383));1384}1385Ok(())1386}13871388fn typecheck_fixed_args(1389&self,1390inst: Inst,1391ctrl_type: Type,1392errors: &mut VerifierErrors,1393) -> VerifierStepResult {1394let constraints = self.func.dfg.insts[inst].opcode().constraints();13951396for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {1397let arg_type = self.func.dfg.value_type(arg);1398match constraints.value_argument_constraint(i, ctrl_type) {1399ResolvedConstraint::Bound(expected_type) => {1400if arg_type != expected_type {1401errors.report((1402inst,1403self.context(inst),1404format!(1405"arg {i} ({arg}) has type {arg_type}, expected {expected_type}"1406),1407));1408}1409}1410ResolvedConstraint::Free(type_set) => {1411if !type_set.contains(arg_type) {1412errors.report((1413inst,1414self.context(inst),1415format!(1416"arg {i} ({arg}) with type {arg_type} failed to satisfy type set {type_set:?}"1417),1418));1419}1420}1421}1422}1423Ok(())1424}14251426/// Typecheck both instructions that contain variable arguments like calls, and those that1427/// include references to basic blocks with their arguments.1428fn typecheck_variable_args(1429&self,1430inst: Inst,1431errors: &mut VerifierErrors,1432) -> VerifierStepResult {1433match &self.func.dfg.insts[inst] {1434ir::InstructionData::Jump { destination, .. } => {1435self.typecheck_block_call(inst, destination, BlockCallTargetType::Normal, errors)?;1436}1437ir::InstructionData::Brif {1438blocks: [block_then, block_else],1439..1440} => {1441self.typecheck_block_call(inst, block_then, BlockCallTargetType::Normal, errors)?;1442self.typecheck_block_call(inst, block_else, BlockCallTargetType::Normal, errors)?;1443}1444ir::InstructionData::BranchTable { table, .. } => {1445for block in self.func.stencil.dfg.jump_tables[*table].all_branches() {1446self.typecheck_block_call(inst, block, BlockCallTargetType::Normal, errors)?;1447}1448}1449ir::InstructionData::TryCall { exception, .. }1450| ir::InstructionData::TryCallIndirect { exception, .. } => {1451let exdata = &self.func.dfg.exception_tables[*exception];1452self.typecheck_block_call(1453inst,1454exdata.normal_return(),1455BlockCallTargetType::ExNormalRet,1456errors,1457)?;1458for item in exdata.items() {1459match item {1460ExceptionTableItem::Tag(_, block_call)1461| ExceptionTableItem::Default(block_call) => {1462self.typecheck_block_call(1463inst,1464&block_call,1465BlockCallTargetType::Exception,1466errors,1467)?;1468}1469ExceptionTableItem::Context(_) => {}1470}1471}1472}1473inst => debug_assert!(!inst.opcode().is_branch()),1474}14751476match self.func.dfg.insts[inst]1477.analyze_call(&self.func.dfg.value_lists, &self.func.dfg.exception_tables)1478{1479CallInfo::Direct(func_ref, args) => {1480let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;1481let arg_types = self.func.dfg.signatures[sig_ref]1482.params1483.iter()1484.map(|a| a.value_type);1485self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;1486}1487CallInfo::DirectWithSig(func_ref, sig_ref, args) => {1488let expected_sig_ref = self.func.dfg.ext_funcs[func_ref].signature;1489let sigdata = &self.func.dfg.signatures;1490// Compare signatures by value, not by ID -- any1491// equivalent signature ID is acceptable.1492if sigdata[sig_ref] != sigdata[expected_sig_ref] {1493errors.nonfatal((1494inst,1495self.context(inst),1496format!(1497"exception table signature {sig_ref} did not match function {func_ref}'s signature {expected_sig_ref}"1498),1499))?;1500}1501let arg_types = self.func.dfg.signatures[sig_ref]1502.params1503.iter()1504.map(|a| a.value_type);1505self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;1506}1507CallInfo::Indirect(sig_ref, args) => {1508let arg_types = self.func.dfg.signatures[sig_ref]1509.params1510.iter()1511.map(|a| a.value_type);1512self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;1513}1514CallInfo::NotACall => {}1515}1516Ok(())1517}15181519fn typecheck_block_call(1520&self,1521inst: Inst,1522block: &ir::BlockCall,1523target_type: BlockCallTargetType,1524errors: &mut VerifierErrors,1525) -> VerifierStepResult {1526let pool = &self.func.dfg.value_lists;1527let block_params = self.func.dfg.block_params(block.block(pool));1528let args = block.args(pool);1529if args.len() != block_params.len() {1530return errors.nonfatal((1531inst,1532self.context(inst),1533format!(1534"mismatched argument count for `{}`: got {}, expected {}",1535self.func.dfg.display_inst(inst),1536args.len(),1537block_params.len(),1538),1539));1540}1541for (arg, param) in args.zip(block_params.iter()) {1542let Some(arg_ty) = self.block_call_arg_ty(arg, inst, target_type, errors)? else {1543continue;1544};1545let param_ty = self.func.dfg.value_type(*param);1546if arg_ty != param_ty {1547errors.nonfatal((1548inst,1549self.context(inst),1550format!("arg {arg} has type {arg_ty}, expected {param_ty}"),1551))?;1552}1553}1554Ok(())1555}15561557fn block_call_arg_ty(1558&self,1559arg: BlockArg,1560inst: Inst,1561target_type: BlockCallTargetType,1562errors: &mut VerifierErrors,1563) -> Result<Option<Type>, ()> {1564match arg {1565BlockArg::Value(v) => Ok(Some(self.func.dfg.value_type(v))),1566BlockArg::TryCallRet(_) | BlockArg::TryCallExn(_) => {1567// Get the invoked signature.1568let et = match self.func.dfg.insts[inst].exception_table() {1569Some(et) => et,1570None => {1571errors.fatal((1572inst,1573self.context(inst),1574format!(1575"`retN` block argument in block-call not on `try_call` instruction"1576),1577))?;1578unreachable!()1579}1580};1581let exdata = &self.func.dfg.exception_tables[et];1582let sig = &self.func.dfg.signatures[exdata.signature()];15831584match (arg, target_type) {1585(BlockArg::TryCallRet(i), BlockCallTargetType::ExNormalRet)1586if (i as usize) < sig.returns.len() =>1587{1588Ok(Some(sig.returns[i as usize].value_type))1589}1590(BlockArg::TryCallRet(_), BlockCallTargetType::ExNormalRet) => {1591errors.fatal((1592inst,1593self.context(inst),1594format!("out-of-bounds `retN` block argument"),1595))?;1596unreachable!()1597}1598(BlockArg::TryCallRet(_), _) => {1599errors.fatal((1600inst,1601self.context(inst),1602format!("`retN` block argument used outside normal-return target of `try_call`"),1603))?;1604unreachable!()1605}1606(BlockArg::TryCallExn(i), BlockCallTargetType::Exception) => {1607if let Some(isa) = self.isa {1608match sig1609.call_conv1610.exception_payload_types(isa.pointer_type())1611.get(i as usize)1612{1613Some(ty) => Ok(Some(*ty)),1614None => {1615errors.fatal((1616inst,1617self.context(inst),1618format!("out-of-bounds `exnN` block argument"),1619))?;1620unreachable!()1621}1622}1623} else {1624Ok(None)1625}1626}1627(BlockArg::TryCallExn(_), _) => {1628errors.fatal((1629inst,1630self.context(inst),1631format!("`exnN` block argument used outside normal-return target of `try_call`"),1632))?;1633unreachable!()1634}1635_ => unreachable!(),1636}1637}1638}1639}16401641fn typecheck_variable_args_iterator(1642&self,1643inst: Inst,1644iter: impl ExactSizeIterator<Item = Type>,1645variable_args: &[Value],1646errors: &mut VerifierErrors,1647) -> VerifierStepResult {1648let mut i = 0;16491650for expected_type in iter {1651if i >= variable_args.len() {1652// Result count mismatch handled below, we want the full argument count first though1653i += 1;1654continue;1655}1656let arg = variable_args[i];1657let arg_type = self.func.dfg.value_type(arg);1658if expected_type != arg_type {1659errors.report((1660inst,1661self.context(inst),1662format!(1663"arg {} ({}) has type {}, expected {}",1664i, variable_args[i], arg_type, expected_type1665),1666));1667}1668i += 1;1669}1670if i != variable_args.len() {1671return errors.nonfatal((1672inst,1673self.context(inst),1674format!(1675"mismatched argument count for `{}`: got {}, expected {}",1676self.func.dfg.display_inst(inst),1677variable_args.len(),1678i,1679),1680));1681}1682Ok(())1683}16841685fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {1686match self.func.dfg.insts[inst] {1687ir::InstructionData::MultiAry {1688opcode: Opcode::Return,1689args,1690} => {1691let types = args1692.as_slice(&self.func.dfg.value_lists)1693.iter()1694.map(|v| self.func.dfg.value_type(*v));1695self.typecheck_return_types(1696inst,1697types,1698errors,1699"arguments of return must match function signature",1700)?;1701}1702ir::InstructionData::Call {1703opcode: Opcode::ReturnCall,1704func_ref,1705..1706} => {1707let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;1708self.typecheck_tail_call(inst, sig_ref, errors)?;1709}1710ir::InstructionData::CallIndirect {1711opcode: Opcode::ReturnCallIndirect,1712sig_ref,1713..1714} => {1715self.typecheck_tail_call(inst, sig_ref, errors)?;1716}1717inst => debug_assert!(!inst.opcode().is_return()),1718}1719Ok(())1720}17211722fn typecheck_tail_call(1723&self,1724inst: Inst,1725sig_ref: SigRef,1726errors: &mut VerifierErrors,1727) -> VerifierStepResult {1728let signature = &self.func.dfg.signatures[sig_ref];1729let cc = signature.call_conv;1730if !cc.supports_tail_calls() {1731errors.report((1732inst,1733self.context(inst),1734format!("calling convention `{cc}` does not support tail calls"),1735));1736}1737if cc != self.func.signature.call_conv {1738errors.report((1739inst,1740self.context(inst),1741"callee's calling convention must match caller",1742));1743}1744let types = signature.returns.iter().map(|param| param.value_type);1745self.typecheck_return_types(inst, types, errors, "results of callee must match caller")?;1746Ok(())1747}17481749fn typecheck_return_types(1750&self,1751inst: Inst,1752actual_types: impl ExactSizeIterator<Item = Type>,1753errors: &mut VerifierErrors,1754message: &str,1755) -> VerifierStepResult {1756let expected_types = &self.func.signature.returns;1757if actual_types.len() != expected_types.len() {1758return errors.nonfatal((inst, self.context(inst), message));1759}1760for (i, (actual_type, &expected_type)) in actual_types.zip(expected_types).enumerate() {1761if actual_type != expected_type.value_type {1762errors.report((1763inst,1764self.context(inst),1765format!(1766"result {i} has type {actual_type}, must match function signature of \1767{expected_type}"1768),1769));1770}1771}1772Ok(())1773}17741775// Check special-purpose type constraints that can't be expressed in the normal opcode1776// constraints.1777fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {1778match self.func.dfg.insts[inst] {1779ir::InstructionData::UnaryGlobalValue { global_value, .. } => {1780if let Some(isa) = self.isa {1781let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));1782let global_type = self.func.global_values[global_value].global_type(isa);1783if inst_type != global_type {1784return errors.nonfatal((1785inst, self.context(inst),1786format!(1787"global_value instruction with type {inst_type} references global value with type {global_type}"1788)),1789);1790}1791}1792}1793_ => {}1794}1795Ok(())1796}17971798fn cfg_integrity(1799&self,1800cfg: &ControlFlowGraph,1801errors: &mut VerifierErrors,1802) -> VerifierStepResult {1803let mut expected_succs = BTreeSet::<Block>::new();1804let mut got_succs = BTreeSet::<Block>::new();1805let mut expected_preds = BTreeSet::<Inst>::new();1806let mut got_preds = BTreeSet::<Inst>::new();18071808for block in self.func.layout.blocks() {1809expected_succs.extend(self.expected_cfg.succ_iter(block));1810got_succs.extend(cfg.succ_iter(block));18111812let missing_succs: Vec<Block> =1813expected_succs.difference(&got_succs).cloned().collect();1814if !missing_succs.is_empty() {1815errors.report((1816block,1817format!("cfg lacked the following successor(s) {missing_succs:?}"),1818));1819continue;1820}18211822let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();1823if !excess_succs.is_empty() {1824errors.report((1825block,1826format!("cfg had unexpected successor(s) {excess_succs:?}"),1827));1828continue;1829}18301831expected_preds.extend(1832self.expected_cfg1833.pred_iter(block)1834.map(|BlockPredecessor { inst, .. }| inst),1835);1836got_preds.extend(1837cfg.pred_iter(block)1838.map(|BlockPredecessor { inst, .. }| inst),1839);18401841let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();1842if !missing_preds.is_empty() {1843errors.report((1844block,1845format!("cfg lacked the following predecessor(s) {missing_preds:?}"),1846));1847continue;1848}18491850let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();1851if !excess_preds.is_empty() {1852errors.report((1853block,1854format!("cfg had unexpected predecessor(s) {excess_preds:?}"),1855));1856continue;1857}18581859expected_succs.clear();1860got_succs.clear();1861expected_preds.clear();1862got_preds.clear();1863}1864errors.as_result()1865}18661867fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {1868let inst_data = &self.func.dfg.insts[inst];18691870match *inst_data {1871ir::InstructionData::Store { flags, .. } => {1872if flags.readonly() {1873errors.fatal((1874inst,1875self.context(inst),1876"A store instruction cannot have the `readonly` MemFlag",1877))1878} else {1879Ok(())1880}1881}1882ir::InstructionData::BinaryImm8 {1883opcode: ir::instructions::Opcode::Extractlane,1884imm: lane,1885arg,1886..1887}1888| ir::InstructionData::TernaryImm8 {1889opcode: ir::instructions::Opcode::Insertlane,1890imm: lane,1891args: [arg, _],1892..1893} => {1894// We must be specific about the opcodes above because other instructions are using1895// the same formats.1896let ty = self.func.dfg.value_type(arg);1897if lane as u32 >= ty.lane_count() {1898errors.fatal((1899inst,1900self.context(inst),1901format!("The lane {lane} does not index into the type {ty}",),1902))1903} else {1904Ok(())1905}1906}1907ir::InstructionData::Shuffle {1908opcode: ir::instructions::Opcode::Shuffle,1909imm,1910..1911} => {1912let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();1913if imm.len() != 16 {1914errors.fatal((1915inst,1916self.context(inst),1917format!("the shuffle immediate wasn't 16-bytes long"),1918))1919} else if let Some(i) = imm.iter().find(|i| **i >= 32) {1920errors.fatal((1921inst,1922self.context(inst),1923format!("shuffle immediate index {i} is larger than the maximum 31"),1924))1925} else {1926Ok(())1927}1928}1929_ => Ok(()),1930}1931}19321933fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {1934use crate::ir::instructions::InstructionData::UnaryImm;19351936let inst_data = &self.func.dfg.insts[inst];1937if let UnaryImm {1938opcode: Opcode::Iconst,1939imm,1940} = inst_data1941{1942let ctrl_typevar = self.func.dfg.ctrl_typevar(inst);1943let bounds_mask = match ctrl_typevar {1944types::I8 => u8::MAX.into(),1945types::I16 => u16::MAX.into(),1946types::I32 => u32::MAX.into(),1947types::I64 => u64::MAX,1948_ => unreachable!(),1949};19501951let value = imm.bits() as u64;1952if value & bounds_mask != value {1953errors.fatal((1954inst,1955self.context(inst),1956"constant immediate is out of bounds",1957))1958} else {1959Ok(())1960}1961} else {1962Ok(())1963}1964}19651966fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult {1967let params = self1968.func1969.signature1970.params1971.iter()1972.enumerate()1973.map(|p| (true, p));1974let returns = self1975.func1976.signature1977.returns1978.iter()1979.enumerate()1980.map(|p| (false, p));19811982for (is_argument, (i, param)) in params.chain(returns) {1983let is_return = !is_argument;1984let item = if is_argument {1985"Parameter"1986} else {1987"Return value"1988};19891990if param.value_type == types::INVALID {1991errors.report((1992AnyEntity::Function,1993format!("{item} at position {i} has an invalid type"),1994));1995}19961997if let ArgumentPurpose::StructArgument(_) = param.purpose {1998if is_return {1999errors.report((2000AnyEntity::Function,2001format!("{item} at position {i} can't be an struct argument"),2002))2003}2004}20052006let ty_allows_extension = param.value_type.is_int();2007let has_extension = param.extension != ArgumentExtension::None;2008if !ty_allows_extension && has_extension {2009errors.report((2010AnyEntity::Function,2011format!(2012"{} at position {} has invalid extension {:?}",2013item, i, param.extension2014),2015));2016}2017}20182019if errors.has_error() { Err(()) } else { Ok(()) }2020}20212022fn verify_try_call_handler_index(2023&self,2024inst: Inst,2025block: Block,2026index_imm: i64,2027errors: &mut VerifierErrors,2028) -> VerifierStepResult {2029if index_imm < 0 {2030return errors.fatal((2031inst,2032format!("exception handler index {index_imm} cannot be negative"),2033));2034}2035let Ok(index) = usize::try_from(index_imm) else {2036return errors.fatal((2037inst,2038format!("exception handler index {index_imm} is out-of-range"),2039));2040};2041let Some(terminator) = self.func.layout.last_inst(block) else {2042return errors.fatal((2043inst,2044format!("referenced block {block} does not have a terminator"),2045));2046};2047let Some(et) = self.func.dfg.insts[terminator].exception_table() else {2048return errors.fatal((2049inst,2050format!("referenced block {block} does not end in a try_call"),2051));2052};20532054let etd = &self.func.dfg.exception_tables[et];2055// The exception table's out-edges consist of all exceptional2056// edges first, followed by the normal return last. For N2057// out-edges, there are N-1 exception handlers that can be2058// selected.2059let num_exceptional_edges = etd.all_branches().len() - 1;2060if index >= num_exceptional_edges {2061return errors.fatal((2062inst,2063format!("exception handler index {index_imm} is out-of-range"),2064));2065}20662067Ok(())2068}20692070pub fn debug_tags(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {2071// Tags can only be present on calls and sequence points.2072let op = self.func.dfg.insts[inst].opcode();2073let tags_allowed = op.is_call() || op == Opcode::SequencePoint;2074let has_tags = self.func.debug_tags.has(inst);2075if has_tags && !tags_allowed {2076return errors.fatal((2077inst,2078"debug tags present on non-call, non-sequence point instruction".to_string(),2079));2080}20812082Ok(())2083}20842085fn verify_signature(2086&self,2087sig: &Signature,2088entity: impl Into<AnyEntity>,2089errors: &mut VerifierErrors,2090) -> VerifierStepResult {2091match sig.call_conv {2092CallConv::PreserveAll => {2093if !sig.returns.is_empty() {2094errors.fatal((2095entity,2096"Signature with `preserve_all` ABI cannot have return values".to_string(),2097))?;2098}2099}2100_ => {}2101}2102Ok(())2103}21042105fn verify_signatures(&self, errors: &mut VerifierErrors) -> VerifierStepResult {2106// Verify this function's own signature.2107self.verify_signature(&self.func.signature, AnyEntity::Function, errors)?;2108// Verify signatures referenced by any extfunc, using that2109// extfunc as the entity to which to attach the error.2110for (func, funcdata) in &self.func.dfg.ext_funcs {2111// Non-contiguous func entities result in placeholders2112// with invalid signatures; skip them.2113if !funcdata.signature.is_reserved_value() {2114self.verify_signature(&self.func.dfg.signatures[funcdata.signature], func, errors)?;2115}2116}2117// Verify all signatures, including those only used by2118// e.g. indirect calls. Technically this re-verifies2119// signatures verified above but we want the first pass to2120// attach errors to funcrefs and we also need to verify all2121// defined signatures.2122for (sig, sigdata) in &self.func.dfg.signatures {2123self.verify_signature(sigdata, sig, errors)?;2124}2125Ok(())2126}21272128pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {2129self.verify_global_values(errors)?;2130self.verify_memory_types(errors)?;2131self.typecheck_entry_block_params(errors)?;2132self.check_entry_not_cold(errors)?;2133self.typecheck_function_signature(errors)?;2134self.verify_signatures(errors)?;21352136for block in self.func.layout.blocks() {2137if self.func.layout.first_inst(block).is_none() {2138return errors.fatal((block, format!("{block} cannot be empty")));2139}2140for inst in self.func.layout.block_insts(block) {2141crate::trace!("verifying {inst:?}: {}", self.func.dfg.display_inst(inst));2142self.block_integrity(block, inst, errors)?;2143self.instruction_integrity(inst, errors)?;2144self.typecheck(inst, errors)?;2145self.immediate_constraints(inst, errors)?;2146self.iconst_bounds(inst, errors)?;2147self.debug_tags(inst, errors)?;2148}21492150self.encodable_as_bb(block, errors)?;2151}21522153if !errors.is_empty() {2154log::warn!(2155"Found verifier errors in function:\n{}",2156pretty_verifier_error(self.func, None, errors.clone())2157);2158}21592160Ok(())2161}2162}21632164#[cfg(test)]2165mod tests {2166use super::{Verifier, VerifierError, VerifierErrors};2167use crate::ir::instructions::{InstructionData, Opcode};2168use crate::ir::{AbiParam, Function, Type, types};2169use crate::settings;21702171macro_rules! assert_err_with_msg {2172($e:expr, $msg:expr) => {2173match $e.0.get(0) {2174None => panic!("Expected an error"),2175Some(&VerifierError { ref message, .. }) => {2176if !message.contains($msg) {2177#[cfg(feature = "std")]2178panic!("'{}' did not contain the substring '{}'", message, $msg);2179#[cfg(not(feature = "std"))]2180panic!("error message did not contain the expected substring");2181}2182}2183}2184};2185}21862187#[test]2188fn empty() {2189let func = Function::new();2190let flags = &settings::Flags::new(settings::builder());2191let verifier = Verifier::new(&func, flags.into());2192let mut errors = VerifierErrors::default();21932194assert_eq!(verifier.run(&mut errors), Ok(()));2195assert!(errors.0.is_empty());2196}21972198#[test]2199fn bad_instruction_format() {2200let mut func = Function::new();2201let block0 = func.dfg.make_block();2202func.layout.append_block(block0);2203let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {2204opcode: Opcode::F32const,2205imm: 0.into(),2206});2207func.layout.append_inst(nullary_with_bad_opcode, block0);2208let destination = func.dfg.block_call(block0, &[]);2209func.stencil.layout.append_inst(2210func.stencil.dfg.make_inst(InstructionData::Jump {2211opcode: Opcode::Jump,2212destination,2213}),2214block0,2215);2216let flags = &settings::Flags::new(settings::builder());2217let verifier = Verifier::new(&func, flags.into());2218let mut errors = VerifierErrors::default();22192220let _ = verifier.run(&mut errors);22212222assert_err_with_msg!(errors, "instruction format");2223}22242225fn test_iconst_bounds(immediate: i64, ctrl_typevar: Type) -> VerifierErrors {2226let mut func = Function::new();2227let block0 = func.dfg.make_block();2228func.layout.append_block(block0);22292230let test_inst = func.dfg.make_inst(InstructionData::UnaryImm {2231opcode: Opcode::Iconst,2232imm: immediate.into(),2233});22342235let end_inst = func.dfg.make_inst(InstructionData::MultiAry {2236opcode: Opcode::Return,2237args: Default::default(),2238});22392240func.dfg.make_inst_results(test_inst, ctrl_typevar);2241func.layout.append_inst(test_inst, block0);2242func.layout.append_inst(end_inst, block0);22432244let flags = &settings::Flags::new(settings::builder());2245let verifier = Verifier::new(&func, flags.into());2246let mut errors = VerifierErrors::default();22472248let _ = verifier.run(&mut errors);2249errors2250}22512252fn test_iconst_bounds_err(immediate: i64, ctrl_typevar: Type) {2253assert_err_with_msg!(2254test_iconst_bounds(immediate, ctrl_typevar),2255"constant immediate is out of bounds"2256);2257}22582259fn test_iconst_bounds_ok(immediate: i64, ctrl_typevar: Type) {2260assert!(test_iconst_bounds(immediate, ctrl_typevar).is_empty());2261}22622263#[test]2264fn negative_iconst_8() {2265test_iconst_bounds_err(-10, types::I8);2266}22672268#[test]2269fn negative_iconst_32() {2270test_iconst_bounds_err(-1, types::I32);2271}22722273#[test]2274fn large_iconst_8() {2275test_iconst_bounds_err(1 + u8::MAX as i64, types::I8);2276}22772278#[test]2279fn large_iconst_16() {2280test_iconst_bounds_err(10 + u16::MAX as i64, types::I16);2281}22822283#[test]2284fn valid_iconst_8() {2285test_iconst_bounds_ok(10, types::I8);2286}22872288#[test]2289fn valid_iconst_32() {2290test_iconst_bounds_ok(u32::MAX as i64, types::I32);2291}22922293#[test]2294fn test_function_invalid_param() {2295let mut func = Function::new();2296func.signature.params.push(AbiParam::new(types::INVALID));22972298let mut errors = VerifierErrors::default();2299let flags = &settings::Flags::new(settings::builder());2300let verifier = Verifier::new(&func, flags.into());23012302let _ = verifier.typecheck_function_signature(&mut errors);2303assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");2304}23052306#[test]2307fn test_function_invalid_return_value() {2308let mut func = Function::new();2309func.signature.returns.push(AbiParam::new(types::INVALID));23102311let mut errors = VerifierErrors::default();2312let flags = &settings::Flags::new(settings::builder());2313let verifier = Verifier::new(&func, flags.into());23142315let _ = verifier.typecheck_function_signature(&mut errors);2316assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");2317}23182319#[test]2320fn test_printing_contextual_errors() {2321// Build function.2322let mut func = Function::new();2323let block0 = func.dfg.make_block();2324func.layout.append_block(block0);23252326// Build instruction "f64const 0.0" (missing one required result)2327let inst = func.dfg.make_inst(InstructionData::UnaryIeee64 {2328opcode: Opcode::F64const,2329imm: 0.0.into(),2330});2331func.layout.append_inst(inst, block0);23322333// Setup verifier.2334let mut errors = VerifierErrors::default();2335let flags = &settings::Flags::new(settings::builder());2336let verifier = Verifier::new(&func, flags.into());23372338// Now the error message, when printed, should contain the instruction sequence causing the2339// error (i.e. f64const 0.0) and not only its entity value (i.e. inst0)2340let _ = verifier.typecheck_results(inst, types::I32, &mut errors);2341assert_eq!(2342format!("{}", errors.0[0]),2343"inst0 (f64const 0.0): has fewer result values than expected"2344)2345}23462347#[test]2348fn test_empty_block() {2349let mut func = Function::new();2350let block0 = func.dfg.make_block();2351func.layout.append_block(block0);23522353let flags = &settings::Flags::new(settings::builder());2354let verifier = Verifier::new(&func, flags.into());2355let mut errors = VerifierErrors::default();2356let _ = verifier.run(&mut errors);23572358assert_err_with_msg!(errors, "block0 cannot be empty");2359}2360}236123622363