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