cranelift_codegen/verifier/
mod.rs

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