Skip to main content

cranelift_codegen/verifier/
mod.rs

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