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