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