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