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