Skip to main content

cranelift_codegen/verifier/
mod.rs

1//! A verifier for ensuring that functions are well formed.
2//! It verifies:
3//!
4//! block integrity
5//!
6//! - All instructions reached from the `block_insts` iterator must belong to
7//!   the block as reported by `inst_block()`.
8//! - Every block must end in a terminator instruction, and no other instruction
9//!   can be a terminator.
10//! - Every value in the `block_params` iterator belongs to the block as reported by `value_block`.
11//!
12//! Instruction integrity
13//!
14//! - The instruction format must match the opcode.
15//! - All result values must be created for multi-valued instructions.
16//! - All referenced entities must exist. (Values, blocks, stack slots, ...)
17//! - Instructions must not reference (eg. branch to) the entry block.
18//!
19//! SSA form
20//!
21//! - Values must be defined by an instruction that exists and that is inserted in
22//!   a block, or be an argument of an existing block.
23//! - Values used by an instruction must dominate the instruction.
24//!
25//! Control flow graph and dominator tree integrity:
26//!
27//! - All predecessors in the CFG must be branches to the block.
28//! - All branches to a block must be present in the CFG.
29//! - A recomputed dominator tree is identical to the existing one.
30//! - The entry block must not be a cold block.
31//!
32//! Type checking
33//!
34//! - Compare input and output values against the opcode's type constraints.
35//!   For polymorphic opcodes, determine the controlling type variable first.
36//! - Branches and jumps must pass arguments to destination blocks that match the
37//!   expected types exactly. The number of arguments must match.
38//! - All blocks in a jump table must take no arguments.
39//! - Function calls are type checked against their signature.
40//! - The entry block must take arguments that match the signature of the current
41//!   function.
42//! - All return instructions must have return value operands matching the current
43//!   function signature.
44//!
45//! Global values
46//!
47//! - Detect cycles in global values.
48//! - Detect use of 'vmctx' global value when no corresponding parameter is defined.
49//!
50//! Memory types
51//!
52//! - Ensure that struct fields are in offset order.
53//! - Ensure that struct fields are completely within the overall
54//!   struct size, and do not overlap.
55//!
56//! TODO:
57//! Ad hoc checking
58//!
59//! - Stack slot loads and stores must be in-bounds.
60//! - Immediate constraints for certain opcodes, like `udiv_imm v3, 0`.
61//! - `Insertlane` and `extractlane` instructions have immediate lane numbers that must be in
62//!   range for their polymorphic type.
63//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number
64//!   of arguments must match the destination type, and the lane indexes must be in range.
65
66use crate::dbg::DisplayList;
67use crate::dominator_tree::DominatorTree;
68use crate::entity::SparseSet;
69use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
70use crate::ir::entities::AnyEntity;
71use crate::ir::instructions::{CallInfo, InstructionFormat, ResolvedConstraint};
72use crate::ir::{self, ArgumentExtension, BlockArg, ExceptionTable};
73use crate::ir::{
74    ArgumentPurpose, Block, Constant, DynamicStackSlot, FuncRef, Function, GlobalValue, Inst,
75    JumpTable, MemFlags, Opcode, SigRef, StackSlot, Type, Value, ValueDef, ValueList, 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;
87
88/// A verifier error.
89#[derive(Debug, PartialEq, Eq, Clone)]
90pub struct VerifierError {
91    /// The entity causing the verifier error.
92    pub location: AnyEntity,
93    /// Optionally provide some context for the given location; e.g., for `inst42` provide
94    /// `Some("v3 = iconst.i32 0")` for more comprehensible errors.
95    pub context: Option<String>,
96    /// The error message.
97    pub message: String,
98}
99
100// This is manually implementing Error and Display instead of using thiserror to reduce the amount
101// of dependencies used by Cranelift.
102impl core::error::Error for VerifierError {}
103
104impl Display for VerifierError {
105    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
106        match &self.context {
107            None => write!(f, "{}: {}", self.location, self.message),
108            Some(context) => write!(f, "{} ({}): {}", self.location, context, self.message),
109        }
110    }
111}
112
113/// 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 syntax
122/// ```
123impl<L, C, M> From<(L, C, M)> for VerifierError
124where
125    L: Into<AnyEntity>,
126    C: Into<String>,
127    M: Into<String>,
128{
129    fn from(items: (L, C, M)) -> Self {
130        let (location, context, message) = items;
131        Self {
132            location: location.into(),
133            context: Some(context.into()),
134            message: message.into(),
135        }
136    }
137}
138
139/// Convenience converter for making error-reporting less verbose.
140///
141/// Same as above but without `context`.
142impl<L, M> From<(L, M)> for VerifierError
143where
144    L: Into<AnyEntity>,
145    M: Into<String>,
146{
147    fn from(items: (L, M)) -> Self {
148        let (location, message) = items;
149        Self {
150            location: location.into(),
151            context: None,
152            message: message.into(),
153        }
154    }
155}
156
157/// Result of a step in the verification process.
158///
159/// Functions that return `VerifierStepResult` should also take a
160/// mutable reference to `VerifierErrors` as argument in order to report
161/// 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<(), ()>;
168
169/// Result of a verification operation.
170///
171/// Unlike `VerifierStepResult` which may be `Ok` while still having reported
172/// errors, this type always returns `Err` if an error (fatal or not) was reported.
173pub type VerifierResult<T> = Result<T, VerifierErrors>;
174
175/// List of verifier errors.
176#[derive(Debug, Default, PartialEq, Eq, Clone)]
177pub struct VerifierErrors(pub Vec<VerifierError>);
178
179// This is manually implementing Error and Display instead of using thiserror to reduce the amount
180// of dependencies used by Cranelift.
181impl core::error::Error for VerifierErrors {}
182
183impl VerifierErrors {
184    /// Return a new `VerifierErrors` struct.
185    #[inline]
186    pub fn new() -> Self {
187        Self(Vec::new())
188    }
189
190    /// Return whether no errors were reported.
191    #[inline]
192    pub fn is_empty(&self) -> bool {
193        self.0.is_empty()
194    }
195
196    /// Return whether one or more errors were reported.
197    #[inline]
198    pub fn has_error(&self) -> bool {
199        !self.0.is_empty()
200    }
201
202    /// Return a `VerifierStepResult` that is fatal if at least one error was reported,
203    /// and non-fatal otherwise.
204    #[inline]
205    pub fn as_result(&self) -> VerifierStepResult {
206        if self.is_empty() { Ok(()) } else { Err(()) }
207    }
208
209    /// Report an error, adding it to the list of errors.
210    pub fn report(&mut self, error: impl Into<VerifierError>) {
211        self.0.push(error.into());
212    }
213
214    /// Report a fatal error and return `Err`.
215    pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
216        self.report(error);
217        Err(())
218    }
219
220    /// Report a non-fatal error and return `Ok`.
221    pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
222        self.report(error);
223        Ok(())
224    }
225}
226
227impl From<Vec<VerifierError>> for VerifierErrors {
228    fn from(v: Vec<VerifierError>) -> Self {
229        Self(v)
230    }
231}
232
233impl From<VerifierErrors> for Vec<VerifierError> {
234    fn from(errors: VerifierErrors) -> Vec<VerifierError> {
235        errors.0
236    }
237}
238
239impl From<VerifierErrors> for VerifierResult<()> {
240    fn from(errors: VerifierErrors) -> VerifierResult<()> {
241        if errors.is_empty() {
242            Ok(())
243        } else {
244            Err(errors)
245        }
246    }
247}
248
249impl Display for VerifierErrors {
250    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
251        for err in &self.0 {
252            writeln!(f, "- {err}")?;
253        }
254        Ok(())
255    }
256}
257
258/// Verify `func`.
259pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(
260    func: &Function,
261    fisa: FOI,
262) -> VerifierResult<()> {
263    let _tt = timing::verifier();
264    let mut errors = VerifierErrors::default();
265    let verifier = Verifier::new(func, fisa.into());
266    let result = verifier.run(&mut errors);
267    if errors.is_empty() {
268        result.unwrap();
269        Ok(())
270    } else {
271        Err(errors)
272    }
273}
274
275/// Verify `func` after checking the integrity of associated context data structures `cfg` and
276/// `domtree`.
277pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>(
278    func: &Function,
279    cfg: &ControlFlowGraph,
280    domtree: &DominatorTree,
281    fisa: FOI,
282    errors: &mut VerifierErrors,
283) -> VerifierStepResult {
284    let _tt = timing::verifier();
285    let verifier = Verifier::new(func, fisa.into());
286    if cfg.is_valid() {
287        verifier.cfg_integrity(cfg, errors)?;
288    }
289    if domtree.is_valid() {
290        verifier.domtree_integrity(domtree, errors)?;
291    }
292    verifier.run(errors)
293}
294
295#[derive(Clone, Copy, Debug)]
296enum BlockCallTargetType {
297    Normal,
298    ExNormalRet,
299    Exception,
300}
301
302struct Verifier<'a> {
303    func: &'a Function,
304    expected_cfg: ControlFlowGraph,
305    expected_domtree: DominatorTree,
306    isa: Option<&'a dyn TargetIsa>,
307}
308
309impl<'a> Verifier<'a> {
310    pub fn new(func: &'a Function, fisa: FlagsOrIsa<'a>) -> Self {
311        let expected_cfg = ControlFlowGraph::with_function(func);
312        let expected_domtree = DominatorTree::with_function(func, &expected_cfg);
313        Self {
314            func,
315            expected_cfg,
316            expected_domtree,
317            isa: fisa.isa,
318        }
319    }
320
321    /// Determine a contextual error string for an instruction.
322    #[inline]
323    fn context(&self, inst: Inst) -> String {
324        self.func.dfg.display_inst(inst).to_string()
325    }
326
327    // Check for:
328    //  - cycles in the global value declarations.
329    //  - use of 'vmctx' when no special parameter declares it.
330    fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
331        let mut cycle_seen = false;
332        let mut seen = SparseSet::new();
333
334        'gvs: for gv in self.func.global_values.keys() {
335            seen.clear();
336            seen.insert(gv);
337
338            let mut cur = gv;
339            loop {
340                match self.func.global_values[cur] {
341                    ir::GlobalValueData::Load { base, .. }
342                    | ir::GlobalValueData::IAddImm { base, .. } => {
343                        if seen.insert(base).is_some() {
344                            if !cycle_seen {
345                                errors.report((
346                                    gv,
347                                    format!("global value cycle: {}", DisplayList(seen.as_slice())),
348                                ));
349                                // ensures we don't report the cycle multiple times
350                                cycle_seen = true;
351                            }
352                            continue 'gvs;
353                        }
354
355                        cur = base;
356                    }
357                    _ => break,
358                }
359            }
360
361            match self.func.global_values[gv] {
362                ir::GlobalValueData::VMContext { .. } => {
363                    if self
364                        .func
365                        .special_param(ir::ArgumentPurpose::VMContext)
366                        .is_none()
367                    {
368                        errors.report((gv, format!("undeclared vmctx reference {gv}")));
369                    }
370                }
371                ir::GlobalValueData::IAddImm {
372                    base, global_type, ..
373                } => {
374                    if !global_type.is_int() {
375                        errors.report((
376                            gv,
377                            format!("iadd_imm global value with non-int type {global_type}"),
378                        ));
379                    } else if let Some(isa) = self.isa {
380                        let base_type = self.func.global_values[base].global_type(isa);
381                        if global_type != base_type {
382                            errors.report((
383                                gv,
384                                format!(
385                                    "iadd_imm type {global_type} differs from operand type {base_type}"
386                                ),
387                            ));
388                        }
389                    }
390                }
391                ir::GlobalValueData::Load { base, .. } => {
392                    if let Some(isa) = self.isa {
393                        let base_type = self.func.global_values[base].global_type(isa);
394                        let pointer_type = isa.pointer_type();
395                        if base_type != pointer_type {
396                            errors.report((
397                                gv,
398                                format!(
399                                    "base {base} has type {base_type}, which is not the pointer type {pointer_type}"
400                                ),
401                            ));
402                        }
403                    }
404                }
405                _ => {}
406            }
407        }
408
409        // Invalid global values shouldn't stop us from verifying the rest of the function
410        Ok(())
411    }
412
413    /// Check that the given block can be encoded as a BB, by checking that only
414    /// branching instructions are ending the block.
415    fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult {
416        match self.func.is_block_basic(block) {
417            Ok(()) => Ok(()),
418            Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),
419        }
420    }
421
422    fn block_integrity(
423        &self,
424        block: Block,
425        inst: Inst,
426        errors: &mut VerifierErrors,
427    ) -> VerifierStepResult {
428        let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();
429        let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
430
431        if is_terminator && !is_last_inst {
432            // Terminating instructions only occur at the end of blocks.
433            return errors.fatal((
434                inst,
435                self.context(inst),
436                format!("a terminator instruction was encountered before the end of {block}"),
437            ));
438        }
439        if is_last_inst && !is_terminator {
440            return errors.fatal((block, "block does not end in a terminator instruction"));
441        }
442
443        // Instructions belong to the correct block.
444        let inst_block = self.func.layout.inst_block(inst);
445        if inst_block != Some(block) {
446            return errors.fatal((
447                inst,
448                self.context(inst),
449                format!("should belong to {block} not {inst_block:?}"),
450            ));
451        }
452
453        // Parameters belong to the correct block.
454        for &arg in self.func.dfg.block_params(block) {
455            match self.func.dfg.value_def(arg) {
456                ValueDef::Param(arg_block, _) => {
457                    if block != arg_block {
458                        return errors.fatal((arg, format!("does not belong to {block}")));
459                    }
460                }
461                _ => {
462                    return errors.fatal((arg, "expected an argument, found a result"));
463                }
464            }
465        }
466
467        Ok(())
468    }
469
470    fn instruction_integrity(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
471        let inst_data = &self.func.dfg.insts[inst];
472        let dfg = &self.func.dfg;
473
474        // The instruction format matches the opcode
475        if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
476            return errors.fatal((
477                inst,
478                self.context(inst),
479                "instruction opcode doesn't match instruction format",
480            ));
481        }
482
483        let expected_num_results = dfg.num_expected_results_for_verifier(inst);
484
485        // All result values for multi-valued instructions are created
486        let got_results = dfg.inst_results(inst).len();
487        if got_results != expected_num_results {
488            return errors.fatal((
489                inst,
490                self.context(inst),
491                format!("expected {expected_num_results} result values, found {got_results}"),
492            ));
493        }
494
495        self.verify_entity_references(inst, errors)
496    }
497
498    fn verify_entity_references(
499        &self,
500        inst: Inst,
501        errors: &mut VerifierErrors,
502    ) -> VerifierStepResult {
503        use crate::ir::instructions::InstructionData::*;
504
505        for arg in self.func.dfg.inst_values(inst) {
506            self.verify_inst_arg(inst, arg, errors)?;
507
508            // All used values must be attached to something.
509            let original = self.func.dfg.resolve_aliases(arg);
510            if !self.func.dfg.value_is_attached(original) {
511                errors.report((
512                    inst,
513                    self.context(inst),
514                    format!("argument {arg} -> {original} is not attached"),
515                ));
516            }
517        }
518
519        for &res in self.func.dfg.inst_results(inst) {
520            self.verify_inst_result(inst, res, errors)?;
521        }
522
523        match self.func.dfg.insts[inst] {
524            MultiAry { ref args, .. } => {
525                self.verify_value_list(inst, args, errors)?;
526            }
527            Jump { destination, .. } => {
528                self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;
529            }
530            Brif {
531                arg,
532                blocks: [block_then, block_else],
533                ..
534            } => {
535                self.verify_value(inst, arg, errors)?;
536                self.verify_block(inst, block_then.block(&self.func.dfg.value_lists), errors)?;
537                self.verify_block(inst, block_else.block(&self.func.dfg.value_lists), errors)?;
538            }
539            BranchTable { table, .. } => {
540                self.verify_jump_table(inst, table, errors)?;
541            }
542            Call {
543                opcode,
544                func_ref,
545                ref args,
546                ..
547            } => {
548                self.verify_func_ref(inst, func_ref, errors)?;
549                self.verify_value_list(inst, args, errors)?;
550                self.verify_callee_patchability(inst, func_ref, opcode, errors)?;
551            }
552            CallIndirect {
553                sig_ref, ref args, ..
554            } => {
555                self.verify_sig_ref(inst, sig_ref, errors)?;
556                self.verify_value_list(inst, args, errors)?;
557            }
558            TryCall {
559                func_ref,
560                ref args,
561                exception,
562                ..
563            } => {
564                self.verify_func_ref(inst, func_ref, errors)?;
565                self.verify_value_list(inst, args, errors)?;
566                self.verify_exception_table(inst, exception, errors)?;
567                self.verify_exception_compatible_abi(inst, exception, errors)?;
568            }
569            TryCallIndirect {
570                ref args,
571                exception,
572                ..
573            } => {
574                self.verify_value_list(inst, args, errors)?;
575                self.verify_exception_table(inst, exception, errors)?;
576                self.verify_exception_compatible_abi(inst, exception, errors)?;
577            }
578            FuncAddr { func_ref, .. } => {
579                self.verify_func_ref(inst, func_ref, errors)?;
580            }
581            StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {
582                self.verify_stack_slot(inst, stack_slot, errors)?;
583            }
584            DynamicStackLoad {
585                dynamic_stack_slot, ..
586            }
587            | DynamicStackStore {
588                dynamic_stack_slot, ..
589            } => {
590                self.verify_dynamic_stack_slot(inst, dynamic_stack_slot, errors)?;
591            }
592            UnaryGlobalValue { global_value, .. } => {
593                self.verify_global_value(inst, global_value, errors)?;
594            }
595            NullAry {
596                opcode: Opcode::GetPinnedReg,
597            }
598            | Unary {
599                opcode: Opcode::SetPinnedReg,
600                ..
601            } => {
602                if let Some(isa) = &self.isa {
603                    if !isa.flags().enable_pinned_reg() {
604                        return errors.fatal((
605                            inst,
606                            self.context(inst),
607                            "GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg",
608                        ));
609                    }
610                } else {
611                    return errors.fatal((
612                        inst,
613                        self.context(inst),
614                        "GetPinnedReg/SetPinnedReg need an ISA!",
615                    ));
616                }
617            }
618            NullAry {
619                opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,
620            } => {
621                if let Some(isa) = &self.isa {
622                    // Backends may already rely on this check implicitly, so do
623                    // not relax it without verifying that it is safe to do so.
624                    if !isa.flags().preserve_frame_pointers() {
625                        return errors.fatal((
626                            inst,
627                            self.context(inst),
628                            "`get_frame_pointer`/`get_return_address` cannot be used without \
629                             enabling `preserve_frame_pointers`",
630                        ));
631                    }
632                } else {
633                    return errors.fatal((
634                        inst,
635                        self.context(inst),
636                        "`get_frame_pointer`/`get_return_address` require an ISA!",
637                    ));
638                }
639            }
640            LoadNoOffset {
641                opcode: Opcode::Bitcast,
642                flags,
643                arg,
644            } => {
645                self.verify_bitcast(inst, flags, arg, errors)?;
646            }
647            LoadNoOffset { opcode, arg, .. } if opcode.can_load() => {
648                self.verify_is_address(inst, arg, errors)?;
649            }
650            Load { opcode, arg, .. } if opcode.can_load() => {
651                self.verify_is_address(inst, arg, errors)?;
652            }
653            AtomicCas {
654                opcode,
655                args: [p, _, _],
656                ..
657            } if opcode.can_load() || opcode.can_store() => {
658                self.verify_is_address(inst, p, errors)?;
659            }
660            AtomicRmw {
661                opcode,
662                args: [p, _],
663                ..
664            } if opcode.can_load() || opcode.can_store() => {
665                self.verify_is_address(inst, p, errors)?;
666            }
667            Store {
668                opcode,
669                args: [_, p],
670                ..
671            } if opcode.can_store() => {
672                self.verify_is_address(inst, p, errors)?;
673            }
674            StoreNoOffset {
675                opcode,
676                args: [_, p],
677                ..
678            } if opcode.can_store() => {
679                self.verify_is_address(inst, p, errors)?;
680            }
681            UnaryConst {
682                opcode: opcode @ (Opcode::Vconst | Opcode::F128const),
683                constant_handle,
684                ..
685            } => {
686                self.verify_constant_size(inst, opcode, constant_handle, errors)?;
687            }
688
689            ExceptionHandlerAddress { block, imm, .. } => {
690                self.verify_block(inst, block, errors)?;
691                self.verify_try_call_handler_index(inst, block, imm.into(), errors)?;
692            }
693
694            // Exhaustive list so we can't forget to add new formats
695            AtomicCas { .. }
696            | AtomicRmw { .. }
697            | LoadNoOffset { .. }
698            | StoreNoOffset { .. }
699            | Unary { .. }
700            | UnaryConst { .. }
701            | UnaryImm { .. }
702            | UnaryIeee16 { .. }
703            | UnaryIeee32 { .. }
704            | UnaryIeee64 { .. }
705            | Binary { .. }
706            | BinaryImm8 { .. }
707            | BinaryImm64 { .. }
708            | Ternary { .. }
709            | TernaryImm8 { .. }
710            | Shuffle { .. }
711            | IntAddTrap { .. }
712            | IntCompare { .. }
713            | IntCompareImm { .. }
714            | FloatCompare { .. }
715            | Load { .. }
716            | Store { .. }
717            | Trap { .. }
718            | CondTrap { .. }
719            | NullAry { .. } => {}
720        }
721
722        Ok(())
723    }
724
725    fn verify_block(
726        &self,
727        loc: impl Into<AnyEntity>,
728        e: Block,
729        errors: &mut VerifierErrors,
730    ) -> VerifierStepResult {
731        if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {
732            return errors.fatal((loc, format!("invalid block reference {e}")));
733        }
734        if let Some(entry_block) = self.func.layout.entry_block() {
735            if e == entry_block {
736                return errors.fatal((loc, format!("invalid reference to entry block {e}")));
737            }
738        }
739        Ok(())
740    }
741
742    fn verify_sig_ref(
743        &self,
744        inst: Inst,
745        s: SigRef,
746        errors: &mut VerifierErrors,
747    ) -> VerifierStepResult {
748        if !self.func.dfg.signatures.is_valid(s) {
749            errors.fatal((
750                inst,
751                self.context(inst),
752                format!("invalid signature reference {s}"),
753            ))
754        } else {
755            Ok(())
756        }
757    }
758
759    fn verify_func_ref(
760        &self,
761        inst: Inst,
762        f: FuncRef,
763        errors: &mut VerifierErrors,
764    ) -> VerifierStepResult {
765        if !self.func.dfg.ext_funcs.is_valid(f) {
766            errors.nonfatal((
767                inst,
768                self.context(inst),
769                format!("invalid function reference {f}"),
770            ))
771        } else {
772            Ok(())
773        }
774    }
775
776    fn verify_stack_slot(
777        &self,
778        inst: Inst,
779        ss: StackSlot,
780        errors: &mut VerifierErrors,
781    ) -> VerifierStepResult {
782        if !self.func.sized_stack_slots.is_valid(ss) {
783            errors.nonfatal((inst, self.context(inst), format!("invalid stack slot {ss}")))
784        } else {
785            Ok(())
786        }
787    }
788
789    fn verify_dynamic_stack_slot(
790        &self,
791        inst: Inst,
792        ss: DynamicStackSlot,
793        errors: &mut VerifierErrors,
794    ) -> VerifierStepResult {
795        if !self.func.dynamic_stack_slots.is_valid(ss) {
796            errors.nonfatal((
797                inst,
798                self.context(inst),
799                format!("invalid dynamic stack slot {ss}"),
800            ))
801        } else {
802            Ok(())
803        }
804    }
805
806    fn verify_global_value(
807        &self,
808        inst: Inst,
809        gv: GlobalValue,
810        errors: &mut VerifierErrors,
811    ) -> VerifierStepResult {
812        if !self.func.global_values.is_valid(gv) {
813            errors.nonfatal((
814                inst,
815                self.context(inst),
816                format!("invalid global value {gv}"),
817            ))
818        } else {
819            Ok(())
820        }
821    }
822
823    fn verify_value_list(
824        &self,
825        inst: Inst,
826        l: &ValueList,
827        errors: &mut VerifierErrors,
828    ) -> VerifierStepResult {
829        if !l.is_valid(&self.func.dfg.value_lists) {
830            errors.nonfatal((
831                inst,
832                self.context(inst),
833                format!("invalid value list reference {l:?}"),
834            ))
835        } else {
836            Ok(())
837        }
838    }
839
840    fn verify_jump_table(
841        &self,
842        inst: Inst,
843        j: JumpTable,
844        errors: &mut VerifierErrors,
845    ) -> VerifierStepResult {
846        if !self.func.stencil.dfg.jump_tables.is_valid(j) {
847            errors.nonfatal((
848                inst,
849                self.context(inst),
850                format!("invalid jump table reference {j}"),
851            ))
852        } else {
853            let pool = &self.func.stencil.dfg.value_lists;
854            for block in self.func.stencil.dfg.jump_tables[j].all_branches() {
855                self.verify_block(inst, block.block(pool), errors)?;
856            }
857            Ok(())
858        }
859    }
860
861    fn verify_exception_table(
862        &self,
863        inst: Inst,
864        et: ExceptionTable,
865        errors: &mut VerifierErrors,
866    ) -> VerifierStepResult {
867        // Verify that the exception table reference itself is valid.
868        if !self.func.stencil.dfg.exception_tables.is_valid(et) {
869            errors.nonfatal((
870                inst,
871                self.context(inst),
872                format!("invalid exception table reference {et}"),
873            ))?;
874        }
875
876        let pool = &self.func.stencil.dfg.value_lists;
877        let exdata = &self.func.stencil.dfg.exception_tables[et];
878
879        // Verify that the exception table's signature reference
880        // is valid.
881        self.verify_sig_ref(inst, exdata.signature(), errors)?;
882
883        // Verify that the exception table's block references are valid.
884        for block in exdata.all_branches() {
885            self.verify_block(inst, block.block(pool), errors)?;
886        }
887        Ok(())
888    }
889
890    fn verify_exception_compatible_abi(
891        &self,
892        inst: Inst,
893        et: ExceptionTable,
894        errors: &mut VerifierErrors,
895    ) -> VerifierStepResult {
896        let callee_sig_ref = self.func.dfg.exception_tables[et].signature();
897        let callee_sig = &self.func.dfg.signatures[callee_sig_ref];
898        let callee_call_conv = callee_sig.call_conv;
899        if !callee_call_conv.supports_exceptions() {
900            errors.nonfatal((
901                inst,
902                self.context(inst),
903                format!(
904                    "calling convention `{callee_call_conv}` of callee does not support exceptions"
905                ),
906            ))?;
907        }
908        Ok(())
909    }
910
911    fn verify_callee_patchability(
912        &self,
913        inst: Inst,
914        func_ref: FuncRef,
915        opcode: Opcode,
916        errors: &mut VerifierErrors,
917    ) -> VerifierStepResult {
918        let ir::ExtFuncData {
919            patchable,
920            colocated,
921            signature,
922            name: _,
923        } = self.func.dfg.ext_funcs[func_ref];
924        let signature = &self.func.dfg.signatures[signature];
925        if patchable && (opcode == Opcode::ReturnCall || opcode == Opcode::ReturnCallIndirect) {
926            errors.fatal((
927                inst,
928                self.context(inst),
929                "patchable funcref cannot be used in a return_call".to_string(),
930            ))?;
931        }
932        if patchable && !colocated {
933            errors.fatal((
934                inst,
935                self.context(inst),
936                "patchable call to non-colocated function".to_string(),
937            ))?;
938        }
939        if patchable && !signature.returns.is_empty() {
940            errors.fatal((
941                inst,
942                self.context(inst),
943                "patchable call cannot occur to a function with return values".to_string(),
944            ))?;
945        }
946        Ok(())
947    }
948
949    fn verify_value(
950        &self,
951        loc_inst: Inst,
952        v: Value,
953        errors: &mut VerifierErrors,
954    ) -> VerifierStepResult {
955        let dfg = &self.func.dfg;
956        if !dfg.value_is_valid(v) {
957            errors.nonfatal((
958                loc_inst,
959                self.context(loc_inst),
960                format!("invalid value reference {v}"),
961            ))
962        } else {
963            Ok(())
964        }
965    }
966
967    fn verify_inst_arg(
968        &self,
969        loc_inst: Inst,
970        v: Value,
971        errors: &mut VerifierErrors,
972    ) -> VerifierStepResult {
973        self.verify_value(loc_inst, v, errors)?;
974
975        let dfg = &self.func.dfg;
976        let loc_block = self
977            .func
978            .layout
979            .inst_block(loc_inst)
980            .expect("Instruction not in layout.");
981        let is_reachable = self.expected_domtree.is_reachable(loc_block);
982
983        // SSA form
984        match dfg.value_def(v) {
985            ValueDef::Result(def_inst, _) => {
986                // Value is defined by an instruction that exists.
987                if !dfg.inst_is_valid(def_inst) {
988                    return errors.fatal((
989                        loc_inst,
990                        self.context(loc_inst),
991                        format!("{v} is defined by invalid instruction {def_inst}"),
992                    ));
993                }
994                // Defining instruction is inserted in a block.
995                if self.func.layout.inst_block(def_inst) == None {
996                    return errors.fatal((
997                        loc_inst,
998                        self.context(loc_inst),
999                        format!("{v} is defined by {def_inst} which has no block"),
1000                    ));
1001                }
1002                // Defining instruction dominates the instruction that uses the value.
1003                if is_reachable {
1004                    if !self
1005                        .expected_domtree
1006                        .dominates(def_inst, loc_inst, &self.func.layout)
1007                    {
1008                        return errors.fatal((
1009                            loc_inst,
1010                            self.context(loc_inst),
1011                            format!("uses value {v} from non-dominating {def_inst}"),
1012                        ));
1013                    }
1014                    if def_inst == loc_inst {
1015                        return errors.fatal((
1016                            loc_inst,
1017                            self.context(loc_inst),
1018                            format!("uses value {v} from itself"),
1019                        ));
1020                    }
1021                }
1022            }
1023            ValueDef::Param(block, _) => {
1024                // Value is defined by an existing block.
1025                if !dfg.block_is_valid(block) {
1026                    return errors.fatal((
1027                        loc_inst,
1028                        self.context(loc_inst),
1029                        format!("{v} is defined by invalid block {block}"),
1030                    ));
1031                }
1032                // Defining block is inserted in the layout
1033                if !self.func.layout.is_block_inserted(block) {
1034                    return errors.fatal((
1035                        loc_inst,
1036                        self.context(loc_inst),
1037                        format!("{v} is defined by {block} which is not in the layout"),
1038                    ));
1039                }
1040                let 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");
1041                // The defining block dominates the instruction using this value.
1042                if is_reachable && !self.expected_domtree.block_dominates(block, user_block) {
1043                    return errors.fatal((
1044                        loc_inst,
1045                        self.context(loc_inst),
1046                        format!("uses value arg from non-dominating {block}"),
1047                    ));
1048                }
1049            }
1050            ValueDef::Union(_, _) => {
1051                // Nothing: union nodes themselves have no location,
1052                // so we cannot check any dominance properties.
1053            }
1054        }
1055        Ok(())
1056    }
1057
1058    fn verify_inst_result(
1059        &self,
1060        loc_inst: Inst,
1061        v: Value,
1062        errors: &mut VerifierErrors,
1063    ) -> VerifierStepResult {
1064        self.verify_value(loc_inst, v, errors)?;
1065
1066        match self.func.dfg.value_def(v) {
1067            ValueDef::Result(def_inst, _) => {
1068                if def_inst != loc_inst {
1069                    errors.fatal((
1070                        loc_inst,
1071                        self.context(loc_inst),
1072                        format!("instruction result {v} is not defined by the instruction"),
1073                    ))
1074                } else {
1075                    Ok(())
1076                }
1077            }
1078            ValueDef::Param(_, _) => errors.fatal((
1079                loc_inst,
1080                self.context(loc_inst),
1081                format!("instruction result {v} is not defined by the instruction"),
1082            )),
1083            ValueDef::Union(_, _) => errors.fatal((
1084                loc_inst,
1085                self.context(loc_inst),
1086                format!("instruction result {v} is a union node"),
1087            )),
1088        }
1089    }
1090
1091    fn verify_bitcast(
1092        &self,
1093        inst: Inst,
1094        flags: MemFlags,
1095        arg: Value,
1096        errors: &mut VerifierErrors,
1097    ) -> VerifierStepResult {
1098        let typ = self.func.dfg.ctrl_typevar(inst);
1099        let value_type = self.func.dfg.value_type(arg);
1100
1101        if typ.bits() != value_type.bits() {
1102            errors.fatal((
1103                inst,
1104                format!(
1105                    "The bitcast argument {} has a type of {} bits, which doesn't match an expected type of {} bits",
1106                    arg,
1107                    value_type.bits(),
1108                    typ.bits()
1109                ),
1110            ))
1111        } else if flags != MemFlags::new()
1112            && flags != MemFlags::new().with_endianness(ir::Endianness::Little)
1113            && flags != MemFlags::new().with_endianness(ir::Endianness::Big)
1114        {
1115            errors.fatal((
1116                inst,
1117                "The bitcast instruction only accepts the `big` or `little` memory flags",
1118            ))
1119        } else if flags == MemFlags::new() && typ.lane_count() != value_type.lane_count() {
1120            errors.fatal((
1121                inst,
1122                "Byte order specifier required for bitcast instruction changing lane count",
1123            ))
1124        } else {
1125            Ok(())
1126        }
1127    }
1128
1129    fn verify_constant_size(
1130        &self,
1131        inst: Inst,
1132        opcode: Opcode,
1133        constant: Constant,
1134        errors: &mut VerifierErrors,
1135    ) -> VerifierStepResult {
1136        let type_size = match opcode {
1137            Opcode::F128const => types::F128.bytes(),
1138            Opcode::Vconst => self.func.dfg.ctrl_typevar(inst).bytes(),
1139            _ => unreachable!("unexpected opcode {opcode:?}"),
1140        } as usize;
1141        let constant_size = self.func.dfg.constants.get(constant).len();
1142        if type_size != constant_size {
1143            errors.fatal((
1144                inst,
1145                format!(
1146                    "The instruction expects {constant} to have a size of {type_size} bytes but it has {constant_size}"
1147                ),
1148            ))
1149        } else {
1150            Ok(())
1151        }
1152    }
1153
1154    fn verify_is_address(
1155        &self,
1156        loc_inst: Inst,
1157        v: Value,
1158        errors: &mut VerifierErrors,
1159    ) -> VerifierStepResult {
1160        if let Some(isa) = self.isa {
1161            let pointer_width = isa.triple().pointer_width()?;
1162            let value_type = self.func.dfg.value_type(v);
1163            let expected_width = pointer_width.bits() as u32;
1164            let value_width = value_type.bits();
1165            if expected_width != value_width {
1166                errors.nonfatal((
1167                    loc_inst,
1168                    self.context(loc_inst),
1169                    format!("invalid pointer width (got {value_width}, expected {expected_width}) encountered {v}"),
1170                ))
1171            } else {
1172                Ok(())
1173            }
1174        } else {
1175            Ok(())
1176        }
1177    }
1178
1179    fn domtree_integrity(
1180        &self,
1181        domtree: &DominatorTree,
1182        errors: &mut VerifierErrors,
1183    ) -> VerifierStepResult {
1184        // We consider two `DominatorTree`s to be equal if they return the same immediate
1185        // dominator for each block. Therefore the current domtree is valid if it matches the freshly
1186        // computed one.
1187        for block in self.func.layout.blocks() {
1188            let expected = self.expected_domtree.idom(block);
1189            let got = domtree.idom(block);
1190            if got != expected {
1191                return errors.fatal((
1192                    block,
1193                    format!("invalid domtree, expected idom({block}) = {expected:?}, got {got:?}"),
1194                ));
1195            }
1196        }
1197        // We also verify if the postorder defined by `DominatorTree` is sane
1198        if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {
1199            return errors.fatal((
1200                AnyEntity::Function,
1201                "incorrect number of Blocks in postorder traversal",
1202            ));
1203        }
1204        for (index, (&test_block, &true_block)) in domtree
1205            .cfg_postorder()
1206            .iter()
1207            .zip(self.expected_domtree.cfg_postorder().iter())
1208            .enumerate()
1209        {
1210            if test_block != true_block {
1211                return errors.fatal((
1212                    test_block,
1213                    format!(
1214                        "invalid domtree, postorder block number {index} should be {true_block}, got {test_block}"
1215                    ),
1216                ));
1217            }
1218        }
1219        Ok(())
1220    }
1221
1222    fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1223        if let Some(block) = self.func.layout.entry_block() {
1224            let expected_types = &self.func.signature.params;
1225            let block_param_count = self.func.dfg.num_block_params(block);
1226
1227            if block_param_count != expected_types.len() {
1228                return errors.fatal((
1229                    block,
1230                    format!(
1231                        "entry block parameters ({}) must match function signature ({})",
1232                        block_param_count,
1233                        expected_types.len()
1234                    ),
1235                ));
1236            }
1237
1238            for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {
1239                let arg_type = self.func.dfg.value_type(arg);
1240                if arg_type != expected_types[i].value_type {
1241                    errors.report((
1242                        block,
1243                        format!(
1244                            "entry block parameter {} expected to have type {}, got {}",
1245                            i, expected_types[i], arg_type
1246                        ),
1247                    ));
1248                }
1249            }
1250        }
1251
1252        errors.as_result()
1253    }
1254
1255    fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1256        if let Some(entry_block) = self.func.layout.entry_block() {
1257            if self.func.layout.is_cold(entry_block) {
1258                return errors
1259                    .fatal((entry_block, format!("entry block cannot be marked as cold")));
1260            }
1261        }
1262        errors.as_result()
1263    }
1264
1265    fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1266        let inst_data = &self.func.dfg.insts[inst];
1267        let constraints = inst_data.opcode().constraints();
1268
1269        let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
1270            // For polymorphic opcodes, determine the controlling type variable first.
1271            let ctrl_type = self.func.dfg.ctrl_typevar(inst);
1272
1273            if !value_typeset.contains(ctrl_type) {
1274                errors.report((
1275                    inst,
1276                    self.context(inst),
1277                    format!(
1278                        "has an invalid controlling type {ctrl_type} (allowed set is {value_typeset:?})"
1279                    ),
1280                ));
1281            }
1282
1283            ctrl_type
1284        } else {
1285            // Non-polymorphic instructions don't check the controlling type variable, so `Option`
1286            // is unnecessary and we can just make it `INVALID`.
1287            types::INVALID
1288        };
1289
1290        // Typechecking instructions is never fatal
1291        let _ = self.typecheck_results(inst, ctrl_type, errors);
1292        let _ = self.typecheck_fixed_args(inst, ctrl_type, errors);
1293        let _ = self.typecheck_variable_args(inst, errors);
1294        let _ = self.typecheck_return(inst, errors);
1295        let _ = self.typecheck_special(inst, errors);
1296
1297        Ok(())
1298    }
1299
1300    fn typecheck_results(
1301        &self,
1302        inst: Inst,
1303        ctrl_type: Type,
1304        errors: &mut VerifierErrors,
1305    ) -> VerifierStepResult {
1306        let mut i = 0;
1307        for &result in self.func.dfg.inst_results(inst) {
1308            let result_type = self.func.dfg.value_type(result);
1309            let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type);
1310            if let Some(expected_type) = expected_type {
1311                if result_type != expected_type {
1312                    errors.report((
1313                        inst,
1314                        self.context(inst),
1315                        format!(
1316                            "expected result {i} ({result}) to have type {expected_type}, found {result_type}"
1317                        ),
1318                    ));
1319                }
1320            } else {
1321                return errors.nonfatal((
1322                    inst,
1323                    self.context(inst),
1324                    "has more result values than expected",
1325                ));
1326            }
1327            i += 1;
1328        }
1329
1330        // There aren't any more result types left.
1331        if self.func.dfg.compute_result_type(inst, i, ctrl_type) != None {
1332            return errors.nonfatal((
1333                inst,
1334                self.context(inst),
1335                "has fewer result values than expected",
1336            ));
1337        }
1338        Ok(())
1339    }
1340
1341    fn typecheck_fixed_args(
1342        &self,
1343        inst: Inst,
1344        ctrl_type: Type,
1345        errors: &mut VerifierErrors,
1346    ) -> VerifierStepResult {
1347        let constraints = self.func.dfg.insts[inst].opcode().constraints();
1348
1349        for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {
1350            let arg_type = self.func.dfg.value_type(arg);
1351            match constraints.value_argument_constraint(i, ctrl_type) {
1352                ResolvedConstraint::Bound(expected_type) => {
1353                    if arg_type != expected_type {
1354                        errors.report((
1355                            inst,
1356                            self.context(inst),
1357                            format!(
1358                                "arg {i} ({arg}) has type {arg_type}, expected {expected_type}"
1359                            ),
1360                        ));
1361                    }
1362                }
1363                ResolvedConstraint::Free(type_set) => {
1364                    if !type_set.contains(arg_type) {
1365                        errors.report((
1366                            inst,
1367                            self.context(inst),
1368                            format!(
1369                                "arg {i} ({arg}) with type {arg_type} failed to satisfy type set {type_set:?}"
1370                            ),
1371                        ));
1372                    }
1373                }
1374            }
1375        }
1376        Ok(())
1377    }
1378
1379    /// Typecheck both instructions that contain variable arguments like calls, and those that
1380    /// include references to basic blocks with their arguments.
1381    fn typecheck_variable_args(
1382        &self,
1383        inst: Inst,
1384        errors: &mut VerifierErrors,
1385    ) -> VerifierStepResult {
1386        match &self.func.dfg.insts[inst] {
1387            ir::InstructionData::Jump { destination, .. } => {
1388                self.typecheck_block_call(inst, destination, BlockCallTargetType::Normal, errors)?;
1389            }
1390            ir::InstructionData::Brif {
1391                blocks: [block_then, block_else],
1392                ..
1393            } => {
1394                self.typecheck_block_call(inst, block_then, BlockCallTargetType::Normal, errors)?;
1395                self.typecheck_block_call(inst, block_else, BlockCallTargetType::Normal, errors)?;
1396            }
1397            ir::InstructionData::BranchTable { table, .. } => {
1398                for block in self.func.stencil.dfg.jump_tables[*table].all_branches() {
1399                    self.typecheck_block_call(inst, block, BlockCallTargetType::Normal, errors)?;
1400                }
1401            }
1402            ir::InstructionData::TryCall { exception, .. }
1403            | ir::InstructionData::TryCallIndirect { exception, .. } => {
1404                let exdata = &self.func.dfg.exception_tables[*exception];
1405                self.typecheck_block_call(
1406                    inst,
1407                    exdata.normal_return(),
1408                    BlockCallTargetType::ExNormalRet,
1409                    errors,
1410                )?;
1411                for item in exdata.items() {
1412                    match item {
1413                        ExceptionTableItem::Tag(_, block_call)
1414                        | ExceptionTableItem::Default(block_call) => {
1415                            self.typecheck_block_call(
1416                                inst,
1417                                &block_call,
1418                                BlockCallTargetType::Exception,
1419                                errors,
1420                            )?;
1421                        }
1422                        ExceptionTableItem::Context(_) => {}
1423                    }
1424                }
1425            }
1426            inst => debug_assert!(!inst.opcode().is_branch()),
1427        }
1428
1429        match self.func.dfg.insts[inst]
1430            .analyze_call(&self.func.dfg.value_lists, &self.func.dfg.exception_tables)
1431        {
1432            CallInfo::Direct(func_ref, args) => {
1433                let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1434                let arg_types = self.func.dfg.signatures[sig_ref]
1435                    .params
1436                    .iter()
1437                    .map(|a| a.value_type);
1438                self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1439            }
1440            CallInfo::DirectWithSig(func_ref, sig_ref, args) => {
1441                let expected_sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1442                let sigdata = &self.func.dfg.signatures;
1443                // Compare signatures by value, not by ID -- any
1444                // equivalent signature ID is acceptable.
1445                if sigdata[sig_ref] != sigdata[expected_sig_ref] {
1446                    errors.nonfatal((
1447                        inst,
1448                        self.context(inst),
1449                        format!(
1450                            "exception table signature {sig_ref} did not match function {func_ref}'s signature {expected_sig_ref}"
1451                        ),
1452                    ))?;
1453                }
1454                let arg_types = self.func.dfg.signatures[sig_ref]
1455                    .params
1456                    .iter()
1457                    .map(|a| a.value_type);
1458                self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1459            }
1460            CallInfo::Indirect(sig_ref, args) => {
1461                let arg_types = self.func.dfg.signatures[sig_ref]
1462                    .params
1463                    .iter()
1464                    .map(|a| a.value_type);
1465                self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1466            }
1467            CallInfo::NotACall => {}
1468        }
1469        Ok(())
1470    }
1471
1472    fn typecheck_block_call(
1473        &self,
1474        inst: Inst,
1475        block: &ir::BlockCall,
1476        target_type: BlockCallTargetType,
1477        errors: &mut VerifierErrors,
1478    ) -> VerifierStepResult {
1479        let pool = &self.func.dfg.value_lists;
1480        let block_params = self.func.dfg.block_params(block.block(pool));
1481        let args = block.args(pool);
1482        if args.len() != block_params.len() {
1483            return errors.nonfatal((
1484                inst,
1485                self.context(inst),
1486                format!(
1487                    "mismatched argument count for `{}`: got {}, expected {}",
1488                    self.func.dfg.display_inst(inst),
1489                    args.len(),
1490                    block_params.len(),
1491                ),
1492            ));
1493        }
1494        for (arg, param) in args.zip(block_params.iter()) {
1495            let Some(arg_ty) = self.block_call_arg_ty(arg, inst, target_type, errors)? else {
1496                continue;
1497            };
1498            let param_ty = self.func.dfg.value_type(*param);
1499            if arg_ty != param_ty {
1500                errors.nonfatal((
1501                    inst,
1502                    self.context(inst),
1503                    format!("arg {arg} has type {arg_ty}, expected {param_ty}"),
1504                ))?;
1505            }
1506        }
1507        Ok(())
1508    }
1509
1510    fn block_call_arg_ty(
1511        &self,
1512        arg: BlockArg,
1513        inst: Inst,
1514        target_type: BlockCallTargetType,
1515        errors: &mut VerifierErrors,
1516    ) -> Result<Option<Type>, ()> {
1517        match arg {
1518            BlockArg::Value(v) => Ok(Some(self.func.dfg.value_type(v))),
1519            BlockArg::TryCallRet(_) | BlockArg::TryCallExn(_) => {
1520                // Get the invoked signature.
1521                let et = match self.func.dfg.insts[inst].exception_table() {
1522                    Some(et) => et,
1523                    None => {
1524                        errors.fatal((
1525                            inst,
1526                            self.context(inst),
1527                            format!(
1528                                "`retN` block argument in block-call not on `try_call` instruction"
1529                            ),
1530                        ))?;
1531                        unreachable!()
1532                    }
1533                };
1534                let exdata = &self.func.dfg.exception_tables[et];
1535                let sig = &self.func.dfg.signatures[exdata.signature()];
1536
1537                match (arg, target_type) {
1538                    (BlockArg::TryCallRet(i), BlockCallTargetType::ExNormalRet)
1539                        if (i as usize) < sig.returns.len() =>
1540                    {
1541                        Ok(Some(sig.returns[i as usize].value_type))
1542                    }
1543                    (BlockArg::TryCallRet(_), BlockCallTargetType::ExNormalRet) => {
1544                        errors.fatal((
1545                            inst,
1546                            self.context(inst),
1547                            format!("out-of-bounds `retN` block argument"),
1548                        ))?;
1549                        unreachable!()
1550                    }
1551                    (BlockArg::TryCallRet(_), _) => {
1552                        errors.fatal((
1553                            inst,
1554                            self.context(inst),
1555                            format!("`retN` block argument used outside normal-return target of `try_call`"),
1556                        ))?;
1557                        unreachable!()
1558                    }
1559                    (BlockArg::TryCallExn(i), BlockCallTargetType::Exception) => {
1560                        if let Some(isa) = self.isa {
1561                            match sig
1562                                .call_conv
1563                                .exception_payload_types(isa.pointer_type())
1564                                .get(i as usize)
1565                            {
1566                                Some(ty) => Ok(Some(*ty)),
1567                                None => {
1568                                    errors.fatal((
1569                                        inst,
1570                                        self.context(inst),
1571                                        format!("out-of-bounds `exnN` block argument"),
1572                                    ))?;
1573                                    unreachable!()
1574                                }
1575                            }
1576                        } else {
1577                            Ok(None)
1578                        }
1579                    }
1580                    (BlockArg::TryCallExn(_), _) => {
1581                        errors.fatal((
1582                            inst,
1583                            self.context(inst),
1584                            format!("`exnN` block argument used outside normal-return target of `try_call`"),
1585                        ))?;
1586                        unreachable!()
1587                    }
1588                    _ => unreachable!(),
1589                }
1590            }
1591        }
1592    }
1593
1594    fn typecheck_variable_args_iterator(
1595        &self,
1596        inst: Inst,
1597        iter: impl ExactSizeIterator<Item = Type>,
1598        variable_args: &[Value],
1599        errors: &mut VerifierErrors,
1600    ) -> VerifierStepResult {
1601        let mut i = 0;
1602
1603        for expected_type in iter {
1604            if i >= variable_args.len() {
1605                // Result count mismatch handled below, we want the full argument count first though
1606                i += 1;
1607                continue;
1608            }
1609            let arg = variable_args[i];
1610            let arg_type = self.func.dfg.value_type(arg);
1611            if expected_type != arg_type {
1612                errors.report((
1613                    inst,
1614                    self.context(inst),
1615                    format!(
1616                        "arg {} ({}) has type {}, expected {}",
1617                        i, variable_args[i], arg_type, expected_type
1618                    ),
1619                ));
1620            }
1621            i += 1;
1622        }
1623        if i != variable_args.len() {
1624            return errors.nonfatal((
1625                inst,
1626                self.context(inst),
1627                format!(
1628                    "mismatched argument count for `{}`: got {}, expected {}",
1629                    self.func.dfg.display_inst(inst),
1630                    variable_args.len(),
1631                    i,
1632                ),
1633            ));
1634        }
1635        Ok(())
1636    }
1637
1638    fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1639        match self.func.dfg.insts[inst] {
1640            ir::InstructionData::MultiAry {
1641                opcode: Opcode::Return,
1642                args,
1643            } => {
1644                let types = args
1645                    .as_slice(&self.func.dfg.value_lists)
1646                    .iter()
1647                    .map(|v| self.func.dfg.value_type(*v));
1648                self.typecheck_return_types(
1649                    inst,
1650                    types,
1651                    errors,
1652                    "arguments of return must match function signature",
1653                )?;
1654            }
1655            ir::InstructionData::Call {
1656                opcode: Opcode::ReturnCall,
1657                func_ref,
1658                ..
1659            } => {
1660                let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1661                self.typecheck_tail_call(inst, sig_ref, errors)?;
1662            }
1663            ir::InstructionData::CallIndirect {
1664                opcode: Opcode::ReturnCallIndirect,
1665                sig_ref,
1666                ..
1667            } => {
1668                self.typecheck_tail_call(inst, sig_ref, errors)?;
1669            }
1670            inst => debug_assert!(!inst.opcode().is_return()),
1671        }
1672        Ok(())
1673    }
1674
1675    fn typecheck_tail_call(
1676        &self,
1677        inst: Inst,
1678        sig_ref: SigRef,
1679        errors: &mut VerifierErrors,
1680    ) -> VerifierStepResult {
1681        let signature = &self.func.dfg.signatures[sig_ref];
1682        let cc = signature.call_conv;
1683        if !cc.supports_tail_calls() {
1684            errors.report((
1685                inst,
1686                self.context(inst),
1687                format!("calling convention `{cc}` does not support tail calls"),
1688            ));
1689        }
1690        if cc != self.func.signature.call_conv {
1691            errors.report((
1692                inst,
1693                self.context(inst),
1694                "callee's calling convention must match caller",
1695            ));
1696        }
1697        let types = signature.returns.iter().map(|param| param.value_type);
1698        self.typecheck_return_types(inst, types, errors, "results of callee must match caller")?;
1699        Ok(())
1700    }
1701
1702    fn typecheck_return_types(
1703        &self,
1704        inst: Inst,
1705        actual_types: impl ExactSizeIterator<Item = Type>,
1706        errors: &mut VerifierErrors,
1707        message: &str,
1708    ) -> VerifierStepResult {
1709        let expected_types = &self.func.signature.returns;
1710        if actual_types.len() != expected_types.len() {
1711            return errors.nonfatal((inst, self.context(inst), message));
1712        }
1713        for (i, (actual_type, &expected_type)) in actual_types.zip(expected_types).enumerate() {
1714            if actual_type != expected_type.value_type {
1715                errors.report((
1716                    inst,
1717                    self.context(inst),
1718                    format!(
1719                        "result {i} has type {actual_type}, must match function signature of \
1720                         {expected_type}"
1721                    ),
1722                ));
1723            }
1724        }
1725        Ok(())
1726    }
1727
1728    // Check special-purpose type constraints that can't be expressed in the normal opcode
1729    // constraints.
1730    fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1731        match self.func.dfg.insts[inst] {
1732            ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
1733                if let Some(isa) = self.isa {
1734                    let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
1735                    let global_type = self.func.global_values[global_value].global_type(isa);
1736                    if inst_type != global_type {
1737                        return errors.nonfatal((
1738                            inst, self.context(inst),
1739                            format!(
1740                                "global_value instruction with type {inst_type} references global value with type {global_type}"
1741                            )),
1742                        );
1743                    }
1744                }
1745            }
1746            _ => {}
1747        }
1748        Ok(())
1749    }
1750
1751    fn cfg_integrity(
1752        &self,
1753        cfg: &ControlFlowGraph,
1754        errors: &mut VerifierErrors,
1755    ) -> VerifierStepResult {
1756        let mut expected_succs = BTreeSet::<Block>::new();
1757        let mut got_succs = BTreeSet::<Block>::new();
1758        let mut expected_preds = BTreeSet::<Inst>::new();
1759        let mut got_preds = BTreeSet::<Inst>::new();
1760
1761        for block in self.func.layout.blocks() {
1762            expected_succs.extend(self.expected_cfg.succ_iter(block));
1763            got_succs.extend(cfg.succ_iter(block));
1764
1765            let missing_succs: Vec<Block> =
1766                expected_succs.difference(&got_succs).cloned().collect();
1767            if !missing_succs.is_empty() {
1768                errors.report((
1769                    block,
1770                    format!("cfg lacked the following successor(s) {missing_succs:?}"),
1771                ));
1772                continue;
1773            }
1774
1775            let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
1776            if !excess_succs.is_empty() {
1777                errors.report((
1778                    block,
1779                    format!("cfg had unexpected successor(s) {excess_succs:?}"),
1780                ));
1781                continue;
1782            }
1783
1784            expected_preds.extend(
1785                self.expected_cfg
1786                    .pred_iter(block)
1787                    .map(|BlockPredecessor { inst, .. }| inst),
1788            );
1789            got_preds.extend(
1790                cfg.pred_iter(block)
1791                    .map(|BlockPredecessor { inst, .. }| inst),
1792            );
1793
1794            let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
1795            if !missing_preds.is_empty() {
1796                errors.report((
1797                    block,
1798                    format!("cfg lacked the following predecessor(s) {missing_preds:?}"),
1799                ));
1800                continue;
1801            }
1802
1803            let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
1804            if !excess_preds.is_empty() {
1805                errors.report((
1806                    block,
1807                    format!("cfg had unexpected predecessor(s) {excess_preds:?}"),
1808                ));
1809                continue;
1810            }
1811
1812            expected_succs.clear();
1813            got_succs.clear();
1814            expected_preds.clear();
1815            got_preds.clear();
1816        }
1817        errors.as_result()
1818    }
1819
1820    fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1821        let inst_data = &self.func.dfg.insts[inst];
1822
1823        match *inst_data {
1824            ir::InstructionData::Store { flags, .. } => {
1825                if flags.readonly() {
1826                    errors.fatal((
1827                        inst,
1828                        self.context(inst),
1829                        "A store instruction cannot have the `readonly` MemFlag",
1830                    ))
1831                } else {
1832                    Ok(())
1833                }
1834            }
1835            ir::InstructionData::BinaryImm8 {
1836                opcode: ir::instructions::Opcode::Extractlane,
1837                imm: lane,
1838                arg,
1839                ..
1840            }
1841            | ir::InstructionData::TernaryImm8 {
1842                opcode: ir::instructions::Opcode::Insertlane,
1843                imm: lane,
1844                args: [arg, _],
1845                ..
1846            } => {
1847                // We must be specific about the opcodes above because other instructions are using
1848                // the same formats.
1849                let ty = self.func.dfg.value_type(arg);
1850                if lane as u32 >= ty.lane_count() {
1851                    errors.fatal((
1852                        inst,
1853                        self.context(inst),
1854                        format!("The lane {lane} does not index into the type {ty}",),
1855                    ))
1856                } else {
1857                    Ok(())
1858                }
1859            }
1860            ir::InstructionData::Shuffle {
1861                opcode: ir::instructions::Opcode::Shuffle,
1862                imm,
1863                ..
1864            } => {
1865                let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
1866                if imm.len() != 16 {
1867                    errors.fatal((
1868                        inst,
1869                        self.context(inst),
1870                        format!("the shuffle immediate wasn't 16-bytes long"),
1871                    ))
1872                } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
1873                    errors.fatal((
1874                        inst,
1875                        self.context(inst),
1876                        format!("shuffle immediate index {i} is larger than the maximum 31"),
1877                    ))
1878                } else {
1879                    Ok(())
1880                }
1881            }
1882            _ => Ok(()),
1883        }
1884    }
1885
1886    fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1887        use crate::ir::instructions::InstructionData::UnaryImm;
1888
1889        let inst_data = &self.func.dfg.insts[inst];
1890        if let UnaryImm {
1891            opcode: Opcode::Iconst,
1892            imm,
1893        } = inst_data
1894        {
1895            let ctrl_typevar = self.func.dfg.ctrl_typevar(inst);
1896            let bounds_mask = match ctrl_typevar {
1897                types::I8 => u8::MAX.into(),
1898                types::I16 => u16::MAX.into(),
1899                types::I32 => u32::MAX.into(),
1900                types::I64 => u64::MAX,
1901                _ => unreachable!(),
1902            };
1903
1904            let value = imm.bits() as u64;
1905            if value & bounds_mask != value {
1906                errors.fatal((
1907                    inst,
1908                    self.context(inst),
1909                    "constant immediate is out of bounds",
1910                ))
1911            } else {
1912                Ok(())
1913            }
1914        } else {
1915            Ok(())
1916        }
1917    }
1918
1919    fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1920        let params = self
1921            .func
1922            .signature
1923            .params
1924            .iter()
1925            .enumerate()
1926            .map(|p| (true, p));
1927        let returns = self
1928            .func
1929            .signature
1930            .returns
1931            .iter()
1932            .enumerate()
1933            .map(|p| (false, p));
1934
1935        for (is_argument, (i, param)) in params.chain(returns) {
1936            let is_return = !is_argument;
1937            let item = if is_argument {
1938                "Parameter"
1939            } else {
1940                "Return value"
1941            };
1942
1943            if param.value_type == types::INVALID {
1944                errors.report((
1945                    AnyEntity::Function,
1946                    format!("{item} at position {i} has an invalid type"),
1947                ));
1948            }
1949
1950            if let ArgumentPurpose::StructArgument(_) = param.purpose {
1951                if is_return {
1952                    errors.report((
1953                        AnyEntity::Function,
1954                        format!("{item} at position {i} can't be an struct argument"),
1955                    ))
1956                }
1957            }
1958
1959            let ty_allows_extension = param.value_type.is_int();
1960            let has_extension = param.extension != ArgumentExtension::None;
1961            if !ty_allows_extension && has_extension {
1962                errors.report((
1963                    AnyEntity::Function,
1964                    format!(
1965                        "{} at position {} has invalid extension {:?}",
1966                        item, i, param.extension
1967                    ),
1968                ));
1969            }
1970        }
1971
1972        if errors.has_error() { Err(()) } else { Ok(()) }
1973    }
1974
1975    fn verify_try_call_handler_index(
1976        &self,
1977        inst: Inst,
1978        block: Block,
1979        index_imm: i64,
1980        errors: &mut VerifierErrors,
1981    ) -> VerifierStepResult {
1982        if index_imm < 0 {
1983            return errors.fatal((
1984                inst,
1985                format!("exception handler index {index_imm} cannot be negative"),
1986            ));
1987        }
1988        let Ok(index) = usize::try_from(index_imm) else {
1989            return errors.fatal((
1990                inst,
1991                format!("exception handler index {index_imm} is out-of-range"),
1992            ));
1993        };
1994        let Some(terminator) = self.func.layout.last_inst(block) else {
1995            return errors.fatal((
1996                inst,
1997                format!("referenced block {block} does not have a terminator"),
1998            ));
1999        };
2000        let Some(et) = self.func.dfg.insts[terminator].exception_table() else {
2001            return errors.fatal((
2002                inst,
2003                format!("referenced block {block} does not end in a try_call"),
2004            ));
2005        };
2006
2007        let etd = &self.func.dfg.exception_tables[et];
2008        // The exception table's out-edges consist of all exceptional
2009        // edges first, followed by the normal return last. For N
2010        // out-edges, there are N-1 exception handlers that can be
2011        // selected.
2012        let num_exceptional_edges = etd.all_branches().len() - 1;
2013        if index >= num_exceptional_edges {
2014            return errors.fatal((
2015                inst,
2016                format!("exception handler index {index_imm} is out-of-range"),
2017            ));
2018        }
2019
2020        Ok(())
2021    }
2022
2023    pub fn debug_tags(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
2024        // Tags can only be present on calls and sequence points.
2025        let op = self.func.dfg.insts[inst].opcode();
2026        let tags_allowed = op.is_call() || op == Opcode::SequencePoint;
2027        let has_tags = self.func.debug_tags.has(inst);
2028        if has_tags && !tags_allowed {
2029            return errors.fatal((
2030                inst,
2031                "debug tags present on non-call, non-sequence point instruction".to_string(),
2032            ));
2033        }
2034
2035        Ok(())
2036    }
2037
2038    fn verify_signature(
2039        &self,
2040        sig: &Signature,
2041        entity: impl Into<AnyEntity>,
2042        errors: &mut VerifierErrors,
2043    ) -> VerifierStepResult {
2044        match sig.call_conv {
2045            CallConv::PreserveAll => {
2046                if !sig.returns.is_empty() {
2047                    errors.fatal((
2048                        entity,
2049                        "Signature with `preserve_all` ABI cannot have return values".to_string(),
2050                    ))?;
2051                }
2052            }
2053            _ => {}
2054        }
2055        Ok(())
2056    }
2057
2058    fn verify_signatures(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
2059        // Verify this function's own signature.
2060        self.verify_signature(&self.func.signature, AnyEntity::Function, errors)?;
2061        // Verify signatures referenced by any extfunc, using that
2062        // extfunc as the entity to which to attach the error.
2063        for (func, funcdata) in &self.func.dfg.ext_funcs {
2064            // Non-contiguous func entities result in placeholders
2065            // with invalid signatures; skip them.
2066            if !funcdata.signature.is_reserved_value() {
2067                self.verify_signature(&self.func.dfg.signatures[funcdata.signature], func, errors)?;
2068            }
2069        }
2070        // Verify all signatures, including those only used by
2071        // e.g. indirect calls. Technically this re-verifies
2072        // signatures verified above but we want the first pass to
2073        // attach errors to funcrefs and we also need to verify all
2074        // defined signatures.
2075        for (sig, sigdata) in &self.func.dfg.signatures {
2076            self.verify_signature(sigdata, sig, errors)?;
2077        }
2078        Ok(())
2079    }
2080
2081    pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
2082        self.verify_global_values(errors)?;
2083        self.typecheck_entry_block_params(errors)?;
2084        self.check_entry_not_cold(errors)?;
2085        self.typecheck_function_signature(errors)?;
2086        self.verify_signatures(errors)?;
2087
2088        for block in self.func.layout.blocks() {
2089            if self.func.layout.first_inst(block).is_none() {
2090                return errors.fatal((block, format!("{block} cannot be empty")));
2091            }
2092            for inst in self.func.layout.block_insts(block) {
2093                crate::trace!("verifying {inst:?}: {}", self.func.dfg.display_inst(inst));
2094                self.block_integrity(block, inst, errors)?;
2095                self.instruction_integrity(inst, errors)?;
2096                self.typecheck(inst, errors)?;
2097                self.immediate_constraints(inst, errors)?;
2098                self.iconst_bounds(inst, errors)?;
2099                self.debug_tags(inst, errors)?;
2100            }
2101
2102            self.encodable_as_bb(block, errors)?;
2103        }
2104
2105        if !errors.is_empty() {
2106            log::warn!(
2107                "Found verifier errors in function:\n{}",
2108                pretty_verifier_error(self.func, None, errors.clone())
2109            );
2110        }
2111
2112        Ok(())
2113    }
2114}
2115
2116#[cfg(test)]
2117mod tests {
2118    use super::{Verifier, VerifierError, VerifierErrors};
2119    use crate::ir::instructions::{InstructionData, Opcode};
2120    use crate::ir::{AbiParam, Function, Type, types};
2121    use crate::settings;
2122
2123    macro_rules! assert_err_with_msg {
2124        ($e:expr, $msg:expr) => {
2125            match $e.0.get(0) {
2126                None => panic!("Expected an error"),
2127                Some(&VerifierError { ref message, .. }) => {
2128                    if !message.contains($msg) {
2129                        #[cfg(feature = "std")]
2130                        panic!("'{}' did not contain the substring '{}'", message, $msg);
2131                        #[cfg(not(feature = "std"))]
2132                        panic!("error message did not contain the expected substring");
2133                    }
2134                }
2135            }
2136        };
2137    }
2138
2139    #[test]
2140    fn empty() {
2141        let func = Function::new();
2142        let flags = &settings::Flags::new(settings::builder());
2143        let verifier = Verifier::new(&func, flags.into());
2144        let mut errors = VerifierErrors::default();
2145
2146        assert_eq!(verifier.run(&mut errors), Ok(()));
2147        assert!(errors.0.is_empty());
2148    }
2149
2150    #[test]
2151    fn bad_instruction_format() {
2152        let mut func = Function::new();
2153        let block0 = func.dfg.make_block();
2154        func.layout.append_block(block0);
2155        let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
2156            opcode: Opcode::F32const,
2157            imm: 0.into(),
2158        });
2159        func.layout.append_inst(nullary_with_bad_opcode, block0);
2160        let destination = func.dfg.block_call(block0, &[]);
2161        func.stencil.layout.append_inst(
2162            func.stencil.dfg.make_inst(InstructionData::Jump {
2163                opcode: Opcode::Jump,
2164                destination,
2165            }),
2166            block0,
2167        );
2168        let flags = &settings::Flags::new(settings::builder());
2169        let verifier = Verifier::new(&func, flags.into());
2170        let mut errors = VerifierErrors::default();
2171
2172        let _ = verifier.run(&mut errors);
2173
2174        assert_err_with_msg!(errors, "instruction format");
2175    }
2176
2177    fn test_iconst_bounds(immediate: i64, ctrl_typevar: Type) -> VerifierErrors {
2178        let mut func = Function::new();
2179        let block0 = func.dfg.make_block();
2180        func.layout.append_block(block0);
2181
2182        let test_inst = func.dfg.make_inst(InstructionData::UnaryImm {
2183            opcode: Opcode::Iconst,
2184            imm: immediate.into(),
2185        });
2186
2187        let end_inst = func.dfg.make_inst(InstructionData::MultiAry {
2188            opcode: Opcode::Return,
2189            args: Default::default(),
2190        });
2191
2192        func.dfg.make_inst_results(test_inst, ctrl_typevar);
2193        func.layout.append_inst(test_inst, block0);
2194        func.layout.append_inst(end_inst, block0);
2195
2196        let flags = &settings::Flags::new(settings::builder());
2197        let verifier = Verifier::new(&func, flags.into());
2198        let mut errors = VerifierErrors::default();
2199
2200        let _ = verifier.run(&mut errors);
2201        errors
2202    }
2203
2204    fn test_iconst_bounds_err(immediate: i64, ctrl_typevar: Type) {
2205        assert_err_with_msg!(
2206            test_iconst_bounds(immediate, ctrl_typevar),
2207            "constant immediate is out of bounds"
2208        );
2209    }
2210
2211    fn test_iconst_bounds_ok(immediate: i64, ctrl_typevar: Type) {
2212        assert!(test_iconst_bounds(immediate, ctrl_typevar).is_empty());
2213    }
2214
2215    #[test]
2216    fn negative_iconst_8() {
2217        test_iconst_bounds_err(-10, types::I8);
2218    }
2219
2220    #[test]
2221    fn negative_iconst_32() {
2222        test_iconst_bounds_err(-1, types::I32);
2223    }
2224
2225    #[test]
2226    fn large_iconst_8() {
2227        test_iconst_bounds_err(1 + u8::MAX as i64, types::I8);
2228    }
2229
2230    #[test]
2231    fn large_iconst_16() {
2232        test_iconst_bounds_err(10 + u16::MAX as i64, types::I16);
2233    }
2234
2235    #[test]
2236    fn valid_iconst_8() {
2237        test_iconst_bounds_ok(10, types::I8);
2238    }
2239
2240    #[test]
2241    fn valid_iconst_32() {
2242        test_iconst_bounds_ok(u32::MAX as i64, types::I32);
2243    }
2244
2245    #[test]
2246    fn test_function_invalid_param() {
2247        let mut func = Function::new();
2248        func.signature.params.push(AbiParam::new(types::INVALID));
2249
2250        let mut errors = VerifierErrors::default();
2251        let flags = &settings::Flags::new(settings::builder());
2252        let verifier = Verifier::new(&func, flags.into());
2253
2254        let _ = verifier.typecheck_function_signature(&mut errors);
2255        assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");
2256    }
2257
2258    #[test]
2259    fn test_function_invalid_return_value() {
2260        let mut func = Function::new();
2261        func.signature.returns.push(AbiParam::new(types::INVALID));
2262
2263        let mut errors = VerifierErrors::default();
2264        let flags = &settings::Flags::new(settings::builder());
2265        let verifier = Verifier::new(&func, flags.into());
2266
2267        let _ = verifier.typecheck_function_signature(&mut errors);
2268        assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");
2269    }
2270
2271    #[test]
2272    fn test_printing_contextual_errors() {
2273        // Build function.
2274        let mut func = Function::new();
2275        let block0 = func.dfg.make_block();
2276        func.layout.append_block(block0);
2277
2278        // Build instruction "f64const 0.0" (missing one required result)
2279        let inst = func.dfg.make_inst(InstructionData::UnaryIeee64 {
2280            opcode: Opcode::F64const,
2281            imm: 0.0.into(),
2282        });
2283        func.layout.append_inst(inst, block0);
2284
2285        // Setup verifier.
2286        let mut errors = VerifierErrors::default();
2287        let flags = &settings::Flags::new(settings::builder());
2288        let verifier = Verifier::new(&func, flags.into());
2289
2290        // Now the error message, when printed, should contain the instruction sequence causing the
2291        // error (i.e. f64const 0.0) and not only its entity value (i.e. inst0)
2292        let _ = verifier.typecheck_results(inst, types::I32, &mut errors);
2293        assert_eq!(
2294            format!("{}", errors.0[0]),
2295            "inst0 (f64const 0.0): has fewer result values than expected"
2296        )
2297    }
2298
2299    #[test]
2300    fn test_empty_block() {
2301        let mut func = Function::new();
2302        let block0 = func.dfg.make_block();
2303        func.layout.append_block(block0);
2304
2305        let flags = &settings::Flags::new(settings::builder());
2306        let verifier = Verifier::new(&func, flags.into());
2307        let mut errors = VerifierErrors::default();
2308        let _ = verifier.run(&mut errors);
2309
2310        assert_err_with_msg!(errors, "block0 cannot be empty");
2311    }
2312}