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