Skip to main content

cranelift_codegen/verifier/
mod.rs

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