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//! TODO:
51//! Ad hoc checking
52//!
53//! - Stack slot loads and stores must be in-bounds.
54//! - Immediate constraints for certain opcodes, like `udiv_imm v3, 0`.
55//! - `Insertlane` and `extractlane` instructions have immediate lane numbers that must be in
56//!   range for their polymorphic type.
57//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number
58//!   of arguments must match the destination type, and the lane indexes must be in range.
59
60use crate::dbg::DisplayList;
61use crate::dominator_tree::DominatorTree;
62use crate::entity::SparseSet;
63use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
64use crate::ir::entities::AnyEntity;
65use crate::ir::instructions::{CallInfo, InstructionFormat, ResolvedConstraint};
66use crate::ir::{self, ArgumentExtension};
67use crate::ir::{
68    types, ArgumentPurpose, Block, Constant, DynamicStackSlot, FuncRef, Function, GlobalValue,
69    Inst, JumpTable, MemFlags, Opcode, SigRef, StackSlot, Type, Value, ValueDef, ValueList,
70};
71use crate::isa::TargetIsa;
72use crate::iterators::IteratorExtras;
73use crate::print_errors::pretty_verifier_error;
74use crate::settings::FlagsOrIsa;
75use crate::timing;
76use alloc::collections::BTreeSet;
77use alloc::string::{String, ToString};
78use alloc::vec::Vec;
79use core::cmp::Ordering;
80use core::fmt::{self, Display, Formatter};
81
82/// A verifier error.
83#[derive(Debug, PartialEq, Eq, Clone)]
84pub struct VerifierError {
85    /// The entity causing the verifier error.
86    pub location: AnyEntity,
87    /// Optionally provide some context for the given location; e.g., for `inst42` provide
88    /// `Some("v3 = iconst.i32 0")` for more comprehensible errors.
89    pub context: Option<String>,
90    /// The error message.
91    pub message: String,
92}
93
94// This is manually implementing Error and Display instead of using thiserror to reduce the amount
95// of dependencies used by Cranelift.
96impl std::error::Error for VerifierError {}
97
98impl Display for VerifierError {
99    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
100        match &self.context {
101            None => write!(f, "{}: {}", self.location, self.message),
102            Some(context) => write!(f, "{} ({}): {}", self.location, context, self.message),
103        }
104    }
105}
106
107/// Convenience converter for making error-reporting less verbose.
108///
109/// Converts a tuple of `(location, context, message)` to a `VerifierError`.
110/// ```
111/// use cranelift_codegen::verifier::VerifierErrors;
112/// use cranelift_codegen::ir::Inst;
113/// let mut errors = VerifierErrors::new();
114/// errors.report((Inst::from_u32(42), "v3 = iadd v1, v2", "iadd cannot be used with values of this type"));
115/// // note the double parenthenses to use this syntax
116/// ```
117impl<L, C, M> From<(L, C, M)> for VerifierError
118where
119    L: Into<AnyEntity>,
120    C: Into<String>,
121    M: Into<String>,
122{
123    fn from(items: (L, C, M)) -> Self {
124        let (location, context, message) = items;
125        Self {
126            location: location.into(),
127            context: Some(context.into()),
128            message: message.into(),
129        }
130    }
131}
132
133/// Convenience converter for making error-reporting less verbose.
134///
135/// Same as above but without `context`.
136impl<L, M> From<(L, M)> for VerifierError
137where
138    L: Into<AnyEntity>,
139    M: Into<String>,
140{
141    fn from(items: (L, M)) -> Self {
142        let (location, message) = items;
143        Self {
144            location: location.into(),
145            context: None,
146            message: message.into(),
147        }
148    }
149}
150
151/// Result of a step in the verification process.
152///
153/// Functions that return `VerifierStepResult<()>` should also take a
154/// mutable reference to `VerifierErrors` as argument in order to report
155/// errors.
156///
157/// Here, `Ok` represents a step that **did not lead to a fatal error**,
158/// meaning that the verification process may continue. However, other (non-fatal)
159/// errors might have been reported through the previously mentioned `VerifierErrors`
160/// argument.
161pub type VerifierStepResult<T> = Result<T, ()>;
162
163/// Result of a verification operation.
164///
165/// Unlike `VerifierStepResult<()>` which may be `Ok` while still having reported
166/// errors, this type always returns `Err` if an error (fatal or not) was reported.
167pub type VerifierResult<T> = Result<T, VerifierErrors>;
168
169/// List of verifier errors.
170#[derive(Debug, Default, PartialEq, Eq, Clone)]
171pub struct VerifierErrors(pub Vec<VerifierError>);
172
173// This is manually implementing Error and Display instead of using thiserror to reduce the amount
174// of dependencies used by Cranelift.
175impl std::error::Error for VerifierErrors {}
176
177impl VerifierErrors {
178    /// Return a new `VerifierErrors` struct.
179    #[inline]
180    pub fn new() -> Self {
181        Self(Vec::new())
182    }
183
184    /// Return whether no errors were reported.
185    #[inline]
186    pub fn is_empty(&self) -> bool {
187        self.0.is_empty()
188    }
189
190    /// Return whether one or more errors were reported.
191    #[inline]
192    pub fn has_error(&self) -> bool {
193        !self.0.is_empty()
194    }
195
196    /// Return a `VerifierStepResult` that is fatal if at least one error was reported,
197    /// and non-fatal otherwise.
198    #[inline]
199    pub fn as_result(&self) -> VerifierStepResult<()> {
200        if self.is_empty() {
201            Ok(())
202        } else {
203            Err(())
204        }
205    }
206
207    /// Report an error, adding it to the list of errors.
208    pub fn report(&mut self, error: impl Into<VerifierError>) {
209        self.0.push(error.into());
210    }
211
212    /// Report a fatal error and return `Err`.
213    pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult<()> {
214        self.report(error);
215        Err(())
216    }
217
218    /// Report a non-fatal error and return `Ok`.
219    pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult<()> {
220        self.report(error);
221        Ok(())
222    }
223}
224
225impl From<Vec<VerifierError>> for VerifierErrors {
226    fn from(v: Vec<VerifierError>) -> Self {
227        Self(v)
228    }
229}
230
231impl Into<Vec<VerifierError>> for VerifierErrors {
232    fn into(self) -> Vec<VerifierError> {
233        self.0
234    }
235}
236
237impl Into<VerifierResult<()>> for VerifierErrors {
238    fn into(self) -> VerifierResult<()> {
239        if self.is_empty() {
240            Ok(())
241        } else {
242            Err(self)
243        }
244    }
245}
246
247impl Display for VerifierErrors {
248    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
249        for err in &self.0 {
250            writeln!(f, "- {}", err)?;
251        }
252        Ok(())
253    }
254}
255
256/// Verify `func`.
257pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(
258    func: &Function,
259    fisa: FOI,
260) -> VerifierResult<()> {
261    let _tt = timing::verifier();
262    let mut errors = VerifierErrors::default();
263    let verifier = Verifier::new(func, fisa.into());
264    let result = verifier.run(&mut errors);
265    if errors.is_empty() {
266        result.unwrap();
267        Ok(())
268    } else {
269        Err(errors)
270    }
271}
272
273/// Verify `func` after checking the integrity of associated context data structures `cfg` and
274/// `domtree`.
275pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>(
276    func: &Function,
277    cfg: &ControlFlowGraph,
278    domtree: &DominatorTree,
279    fisa: FOI,
280    errors: &mut VerifierErrors,
281) -> VerifierStepResult<()> {
282    let _tt = timing::verifier();
283    let verifier = Verifier::new(func, fisa.into());
284    if cfg.is_valid() {
285        verifier.cfg_integrity(cfg, errors)?;
286    }
287    if domtree.is_valid() {
288        verifier.domtree_integrity(domtree, errors)?;
289    }
290    verifier.run(errors)
291}
292
293struct Verifier<'a> {
294    func: &'a Function,
295    expected_cfg: ControlFlowGraph,
296    expected_domtree: DominatorTree,
297    isa: Option<&'a dyn TargetIsa>,
298}
299
300impl<'a> Verifier<'a> {
301    pub fn new(func: &'a Function, fisa: FlagsOrIsa<'a>) -> Self {
302        let expected_cfg = ControlFlowGraph::with_function(func);
303        let expected_domtree = DominatorTree::with_function(func, &expected_cfg);
304        Self {
305            func,
306            expected_cfg,
307            expected_domtree,
308            isa: fisa.isa,
309        }
310    }
311
312    /// Determine a contextual error string for an instruction.
313    #[inline]
314    fn context(&self, inst: Inst) -> String {
315        self.func.dfg.display_inst(inst).to_string()
316    }
317
318    // Check for:
319    //  - cycles in the global value declarations.
320    //  - use of 'vmctx' when no special parameter declares it.
321    fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
322        let mut cycle_seen = false;
323        let mut seen = SparseSet::new();
324
325        'gvs: for gv in self.func.global_values.keys() {
326            seen.clear();
327            seen.insert(gv);
328
329            let mut cur = gv;
330            loop {
331                match self.func.global_values[cur] {
332                    ir::GlobalValueData::Load { base, .. }
333                    | ir::GlobalValueData::IAddImm { base, .. } => {
334                        if seen.insert(base).is_some() {
335                            if !cycle_seen {
336                                errors.report((
337                                    gv,
338                                    format!("global value cycle: {}", DisplayList(seen.as_slice())),
339                                ));
340                                // ensures we don't report the cycle multiple times
341                                cycle_seen = true;
342                            }
343                            continue 'gvs;
344                        }
345
346                        cur = base;
347                    }
348                    _ => break,
349                }
350            }
351
352            match self.func.global_values[gv] {
353                ir::GlobalValueData::VMContext { .. } => {
354                    if self
355                        .func
356                        .special_param(ir::ArgumentPurpose::VMContext)
357                        .is_none()
358                    {
359                        errors.report((gv, format!("undeclared vmctx reference {}", gv)));
360                    }
361                }
362                ir::GlobalValueData::IAddImm {
363                    base, global_type, ..
364                } => {
365                    if !global_type.is_int() {
366                        errors.report((
367                            gv,
368                            format!("iadd_imm global value with non-int type {}", global_type),
369                        ));
370                    } else if let Some(isa) = self.isa {
371                        let base_type = self.func.global_values[base].global_type(isa);
372                        if global_type != base_type {
373                            errors.report((
374                                gv,
375                                format!(
376                                    "iadd_imm type {} differs from operand type {}",
377                                    global_type, base_type
378                                ),
379                            ));
380                        }
381                    }
382                }
383                ir::GlobalValueData::Load { base, .. } => {
384                    if let Some(isa) = self.isa {
385                        let base_type = self.func.global_values[base].global_type(isa);
386                        let pointer_type = isa.pointer_type();
387                        if base_type != pointer_type {
388                            errors.report((
389                                gv,
390                                format!(
391                                    "base {} has type {}, which is not the pointer type {}",
392                                    base, base_type, pointer_type
393                                ),
394                            ));
395                        }
396                    }
397                }
398                _ => {}
399            }
400        }
401
402        // Invalid global values shouldn't stop us from verifying the rest of the function
403        Ok(())
404    }
405
406    fn verify_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
407        if let Some(isa) = self.isa {
408            for (table, table_data) in &self.func.tables {
409                let base = table_data.base_gv;
410                if !self.func.global_values.is_valid(base) {
411                    return errors.nonfatal((table, format!("invalid base global value {}", base)));
412                }
413
414                let pointer_type = isa.pointer_type();
415                let base_type = self.func.global_values[base].global_type(isa);
416                if base_type != pointer_type {
417                    errors.report((
418                        table,
419                        format!(
420                            "table base has type {}, which is not the pointer type {}",
421                            base_type, pointer_type
422                        ),
423                    ));
424                }
425
426                let bound_gv = table_data.bound_gv;
427                if !self.func.global_values.is_valid(bound_gv) {
428                    return errors
429                        .nonfatal((table, format!("invalid bound global value {}", bound_gv)));
430                }
431
432                let index_type = table_data.index_type;
433                let bound_type = self.func.global_values[bound_gv].global_type(isa);
434                if index_type != bound_type {
435                    errors.report((
436                        table,
437                        format!(
438                            "table index type {} differs from the type of its bound, {}",
439                            index_type, bound_type
440                        ),
441                    ));
442                }
443            }
444        }
445
446        Ok(())
447    }
448
449    /// Check that the given block can be encoded as a BB, by checking that only
450    /// branching instructions are ending the block.
451    fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
452        match self.func.is_block_basic(block) {
453            Ok(()) => Ok(()),
454            Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),
455        }
456    }
457
458    fn block_integrity(
459        &self,
460        block: Block,
461        inst: Inst,
462        errors: &mut VerifierErrors,
463    ) -> VerifierStepResult<()> {
464        let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();
465        let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
466
467        if is_terminator && !is_last_inst {
468            // Terminating instructions only occur at the end of blocks.
469            return errors.fatal((
470                inst,
471                self.context(inst),
472                format!(
473                    "a terminator instruction was encountered before the end of {}",
474                    block
475                ),
476            ));
477        }
478        if is_last_inst && !is_terminator {
479            return errors.fatal((block, "block does not end in a terminator instruction"));
480        }
481
482        // Instructions belong to the correct block.
483        let inst_block = self.func.layout.inst_block(inst);
484        if inst_block != Some(block) {
485            return errors.fatal((
486                inst,
487                self.context(inst),
488                format!("should belong to {} not {:?}", block, inst_block),
489            ));
490        }
491
492        // Parameters belong to the correct block.
493        for &arg in self.func.dfg.block_params(block) {
494            match self.func.dfg.value_def(arg) {
495                ValueDef::Param(arg_block, _) => {
496                    if block != arg_block {
497                        return errors.fatal((arg, format!("does not belong to {}", block)));
498                    }
499                }
500                _ => {
501                    return errors.fatal((arg, "expected an argument, found a result"));
502                }
503            }
504        }
505
506        Ok(())
507    }
508
509    fn instruction_integrity(
510        &self,
511        inst: Inst,
512        errors: &mut VerifierErrors,
513    ) -> VerifierStepResult<()> {
514        let inst_data = &self.func.dfg.insts[inst];
515        let dfg = &self.func.dfg;
516
517        // The instruction format matches the opcode
518        if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
519            return errors.fatal((
520                inst,
521                self.context(inst),
522                "instruction opcode doesn't match instruction format",
523            ));
524        }
525
526        let expected_num_results = dfg.num_expected_results_for_verifier(inst);
527
528        // All result values for multi-valued instructions are created
529        let got_results = dfg.inst_results(inst).len();
530        if got_results != expected_num_results {
531            return errors.fatal((
532                inst,
533                self.context(inst),
534                format!("expected {expected_num_results} result values, found {got_results}"),
535            ));
536        }
537
538        self.verify_entity_references(inst, errors)
539    }
540
541    fn verify_entity_references(
542        &self,
543        inst: Inst,
544        errors: &mut VerifierErrors,
545    ) -> VerifierStepResult<()> {
546        use crate::ir::instructions::InstructionData::*;
547
548        for arg in self.func.dfg.inst_values(inst) {
549            self.verify_inst_arg(inst, arg, errors)?;
550
551            // All used values must be attached to something.
552            let original = self.func.dfg.resolve_aliases(arg);
553            if !self.func.dfg.value_is_attached(original) {
554                errors.report((
555                    inst,
556                    self.context(inst),
557                    format!("argument {} -> {} is not attached", arg, original),
558                ));
559            }
560        }
561
562        for &res in self.func.dfg.inst_results(inst) {
563            self.verify_inst_result(inst, res, errors)?;
564        }
565
566        match self.func.dfg.insts[inst] {
567            MultiAry { ref args, .. } => {
568                self.verify_value_list(inst, args, errors)?;
569            }
570            Jump { destination, .. } => {
571                self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;
572            }
573            Brif {
574                arg,
575                blocks: [block_then, block_else],
576                ..
577            } => {
578                self.verify_value(inst, arg, errors)?;
579                self.verify_block(inst, block_then.block(&self.func.dfg.value_lists), errors)?;
580                self.verify_block(inst, block_else.block(&self.func.dfg.value_lists), errors)?;
581            }
582            BranchTable { table, .. } => {
583                self.verify_jump_table(inst, table, errors)?;
584            }
585            Call {
586                func_ref, ref args, ..
587            } => {
588                self.verify_func_ref(inst, func_ref, errors)?;
589                self.verify_value_list(inst, args, errors)?;
590            }
591            CallIndirect {
592                sig_ref, ref args, ..
593            } => {
594                self.verify_sig_ref(inst, sig_ref, errors)?;
595                self.verify_value_list(inst, args, errors)?;
596            }
597            FuncAddr { func_ref, .. } => {
598                self.verify_func_ref(inst, func_ref, errors)?;
599            }
600            StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {
601                self.verify_stack_slot(inst, stack_slot, errors)?;
602            }
603            DynamicStackLoad {
604                dynamic_stack_slot, ..
605            }
606            | DynamicStackStore {
607                dynamic_stack_slot, ..
608            } => {
609                self.verify_dynamic_stack_slot(inst, dynamic_stack_slot, errors)?;
610            }
611            UnaryGlobalValue { global_value, .. } => {
612                self.verify_global_value(inst, global_value, errors)?;
613            }
614            TableAddr { table, .. } => {
615                self.verify_table(inst, table, errors)?;
616            }
617            NullAry {
618                opcode: Opcode::GetPinnedReg,
619            }
620            | Unary {
621                opcode: Opcode::SetPinnedReg,
622                ..
623            } => {
624                if let Some(isa) = &self.isa {
625                    if !isa.flags().enable_pinned_reg() {
626                        return errors.fatal((
627                            inst,
628                            self.context(inst),
629                            "GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg",
630                        ));
631                    }
632                } else {
633                    return errors.fatal((
634                        inst,
635                        self.context(inst),
636                        "GetPinnedReg/SetPinnedReg need an ISA!",
637                    ));
638                }
639            }
640            NullAry {
641                opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,
642            } => {
643                if let Some(isa) = &self.isa {
644                    // Backends may already rely on this check implicitly, so do
645                    // not relax it without verifying that it is safe to do so.
646                    if !isa.flags().preserve_frame_pointers() {
647                        return errors.fatal((
648                            inst,
649                            self.context(inst),
650                            "`get_frame_pointer`/`get_return_address` cannot be used without \
651                             enabling `preserve_frame_pointers`",
652                        ));
653                    }
654                } else {
655                    return errors.fatal((
656                        inst,
657                        self.context(inst),
658                        "`get_frame_pointer`/`get_return_address` require an ISA!",
659                    ));
660                }
661            }
662            LoadNoOffset {
663                opcode: Opcode::Bitcast,
664                flags,
665                arg,
666            } => {
667                self.verify_bitcast(inst, flags, arg, errors)?;
668            }
669            UnaryConst {
670                opcode: Opcode::Vconst,
671                constant_handle,
672                ..
673            } => {
674                self.verify_constant_size(inst, constant_handle, errors)?;
675            }
676
677            // Exhaustive list so we can't forget to add new formats
678            AtomicCas { .. }
679            | AtomicRmw { .. }
680            | LoadNoOffset { .. }
681            | StoreNoOffset { .. }
682            | Unary { .. }
683            | UnaryConst { .. }
684            | UnaryImm { .. }
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((
766                inst,
767                self.context(inst),
768                format!("invalid stack slot {}", ss),
769            ))
770        } else {
771            Ok(())
772        }
773    }
774
775    fn verify_dynamic_stack_slot(
776        &self,
777        inst: Inst,
778        ss: DynamicStackSlot,
779        errors: &mut VerifierErrors,
780    ) -> VerifierStepResult<()> {
781        if !self.func.dynamic_stack_slots.is_valid(ss) {
782            errors.nonfatal((
783                inst,
784                self.context(inst),
785                format!("invalid dynamic stack slot {}", ss),
786            ))
787        } else {
788            Ok(())
789        }
790    }
791
792    fn verify_global_value(
793        &self,
794        inst: Inst,
795        gv: GlobalValue,
796        errors: &mut VerifierErrors,
797    ) -> VerifierStepResult<()> {
798        if !self.func.global_values.is_valid(gv) {
799            errors.nonfatal((
800                inst,
801                self.context(inst),
802                format!("invalid global value {}", gv),
803            ))
804        } else {
805            Ok(())
806        }
807    }
808
809    fn verify_table(
810        &self,
811        inst: Inst,
812        table: ir::Table,
813        errors: &mut VerifierErrors,
814    ) -> VerifierStepResult<()> {
815        if !self.func.tables.is_valid(table) {
816            errors.nonfatal((inst, self.context(inst), format!("invalid table {}", table)))
817        } else {
818            Ok(())
819        }
820    }
821
822    fn verify_value_list(
823        &self,
824        inst: Inst,
825        l: &ValueList,
826        errors: &mut VerifierErrors,
827    ) -> VerifierStepResult<()> {
828        if !l.is_valid(&self.func.dfg.value_lists) {
829            errors.nonfatal((
830                inst,
831                self.context(inst),
832                format!("invalid value list reference {:?}", l),
833            ))
834        } else {
835            Ok(())
836        }
837    }
838
839    fn verify_jump_table(
840        &self,
841        inst: Inst,
842        j: JumpTable,
843        errors: &mut VerifierErrors,
844    ) -> VerifierStepResult<()> {
845        if !self.func.stencil.dfg.jump_tables.is_valid(j) {
846            errors.nonfatal((
847                inst,
848                self.context(inst),
849                format!("invalid jump table reference {}", j),
850            ))
851        } else {
852            let pool = &self.func.stencil.dfg.value_lists;
853            for block in self.func.stencil.dfg.jump_tables[j].all_branches() {
854                self.verify_block(inst, block.block(pool), errors)?;
855            }
856            Ok(())
857        }
858    }
859
860    fn verify_value(
861        &self,
862        loc_inst: Inst,
863        v: Value,
864        errors: &mut VerifierErrors,
865    ) -> VerifierStepResult<()> {
866        let dfg = &self.func.dfg;
867        if !dfg.value_is_valid(v) {
868            errors.nonfatal((
869                loc_inst,
870                self.context(loc_inst),
871                format!("invalid value reference {}", v),
872            ))
873        } else {
874            Ok(())
875        }
876    }
877
878    fn verify_inst_arg(
879        &self,
880        loc_inst: Inst,
881        v: Value,
882        errors: &mut VerifierErrors,
883    ) -> VerifierStepResult<()> {
884        self.verify_value(loc_inst, v, errors)?;
885
886        let dfg = &self.func.dfg;
887        let loc_block = self
888            .func
889            .layout
890            .inst_block(loc_inst)
891            .expect("Instruction not in layout.");
892        let is_reachable = self.expected_domtree.is_reachable(loc_block);
893
894        // SSA form
895        match dfg.value_def(v) {
896            ValueDef::Result(def_inst, _) => {
897                // Value is defined by an instruction that exists.
898                if !dfg.inst_is_valid(def_inst) {
899                    return errors.fatal((
900                        loc_inst,
901                        self.context(loc_inst),
902                        format!("{} is defined by invalid instruction {}", v, def_inst),
903                    ));
904                }
905                // Defining instruction is inserted in a block.
906                if self.func.layout.inst_block(def_inst) == None {
907                    return errors.fatal((
908                        loc_inst,
909                        self.context(loc_inst),
910                        format!("{} is defined by {} which has no block", v, def_inst),
911                    ));
912                }
913                // Defining instruction dominates the instruction that uses the value.
914                if is_reachable {
915                    if !self
916                        .expected_domtree
917                        .dominates(def_inst, loc_inst, &self.func.layout)
918                    {
919                        return errors.fatal((
920                            loc_inst,
921                            self.context(loc_inst),
922                            format!("uses value {} from non-dominating {}", v, def_inst),
923                        ));
924                    }
925                    if def_inst == loc_inst {
926                        return errors.fatal((
927                            loc_inst,
928                            self.context(loc_inst),
929                            format!("uses value {} from itself", v),
930                        ));
931                    }
932                }
933            }
934            ValueDef::Param(block, _) => {
935                // Value is defined by an existing block.
936                if !dfg.block_is_valid(block) {
937                    return errors.fatal((
938                        loc_inst,
939                        self.context(loc_inst),
940                        format!("{} is defined by invalid block {}", v, block),
941                    ));
942                }
943                // Defining block is inserted in the layout
944                if !self.func.layout.is_block_inserted(block) {
945                    return errors.fatal((
946                        loc_inst,
947                        self.context(loc_inst),
948                        format!("{} is defined by {} which is not in the layout", v, block),
949                    ));
950                }
951                // The defining block dominates the instruction using this value.
952                if is_reachable
953                    && !self
954                        .expected_domtree
955                        .dominates(block, loc_inst, &self.func.layout)
956                {
957                    return errors.fatal((
958                        loc_inst,
959                        self.context(loc_inst),
960                        format!("uses value arg from non-dominating {}", block),
961                    ));
962                }
963            }
964            ValueDef::Union(_, _) => {
965                // Nothing: union nodes themselves have no location,
966                // so we cannot check any dominance properties.
967            }
968        }
969        Ok(())
970    }
971
972    fn verify_inst_result(
973        &self,
974        loc_inst: Inst,
975        v: Value,
976        errors: &mut VerifierErrors,
977    ) -> VerifierStepResult<()> {
978        self.verify_value(loc_inst, v, errors)?;
979
980        match self.func.dfg.value_def(v) {
981            ValueDef::Result(def_inst, _) => {
982                if def_inst != loc_inst {
983                    errors.fatal((
984                        loc_inst,
985                        self.context(loc_inst),
986                        format!("instruction result {} is not defined by the instruction", v),
987                    ))
988                } else {
989                    Ok(())
990                }
991            }
992            ValueDef::Param(_, _) => errors.fatal((
993                loc_inst,
994                self.context(loc_inst),
995                format!("instruction result {} is not defined by the instruction", v),
996            )),
997            ValueDef::Union(_, _) => errors.fatal((
998                loc_inst,
999                self.context(loc_inst),
1000                format!("instruction result {} is a union node", v),
1001            )),
1002        }
1003    }
1004
1005    fn verify_bitcast(
1006        &self,
1007        inst: Inst,
1008        flags: MemFlags,
1009        arg: Value,
1010        errors: &mut VerifierErrors,
1011    ) -> VerifierStepResult<()> {
1012        let typ = self.func.dfg.ctrl_typevar(inst);
1013        let value_type = self.func.dfg.value_type(arg);
1014
1015        if typ.bits() != value_type.bits() {
1016            errors.fatal((
1017                inst,
1018                format!(
1019                    "The bitcast argument {} has a type of {} bits, which doesn't match an expected type of {} bits",
1020                    arg,
1021                    value_type.bits(),
1022                    typ.bits()
1023                ),
1024            ))
1025        } else if flags != MemFlags::new()
1026            && flags != MemFlags::new().with_endianness(ir::Endianness::Little)
1027            && flags != MemFlags::new().with_endianness(ir::Endianness::Big)
1028        {
1029            errors.fatal((
1030                inst,
1031                "The bitcast instruction only accepts the `big` or `little` memory flags",
1032            ))
1033        } else if flags == MemFlags::new() && typ.lane_count() != value_type.lane_count() {
1034            errors.fatal((
1035                inst,
1036                "Byte order specifier required for bitcast instruction changing lane count",
1037            ))
1038        } else {
1039            Ok(())
1040        }
1041    }
1042
1043    fn verify_constant_size(
1044        &self,
1045        inst: Inst,
1046        constant: Constant,
1047        errors: &mut VerifierErrors,
1048    ) -> VerifierStepResult<()> {
1049        let type_size = self.func.dfg.ctrl_typevar(inst).bytes() as usize;
1050        let constant_size = self.func.dfg.constants.get(constant).len();
1051        if type_size != constant_size {
1052            errors.fatal((
1053                inst,
1054                format!(
1055                    "The instruction expects {} to have a size of {} bytes but it has {}",
1056                    constant, type_size, constant_size
1057                ),
1058            ))
1059        } else {
1060            Ok(())
1061        }
1062    }
1063
1064    fn domtree_integrity(
1065        &self,
1066        domtree: &DominatorTree,
1067        errors: &mut VerifierErrors,
1068    ) -> VerifierStepResult<()> {
1069        // We consider two `DominatorTree`s to be equal if they return the same immediate
1070        // dominator for each block. Therefore the current domtree is valid if it matches the freshly
1071        // computed one.
1072        for block in self.func.layout.blocks() {
1073            let expected = self.expected_domtree.idom(block);
1074            let got = domtree.idom(block);
1075            if got != expected {
1076                return errors.fatal((
1077                    block,
1078                    format!(
1079                        "invalid domtree, expected idom({}) = {:?}, got {:?}",
1080                        block, expected, got
1081                    ),
1082                ));
1083            }
1084        }
1085        // We also verify if the postorder defined by `DominatorTree` is sane
1086        if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {
1087            return errors.fatal((
1088                AnyEntity::Function,
1089                "incorrect number of Blocks in postorder traversal",
1090            ));
1091        }
1092        for (index, (&test_block, &true_block)) in domtree
1093            .cfg_postorder()
1094            .iter()
1095            .zip(self.expected_domtree.cfg_postorder().iter())
1096            .enumerate()
1097        {
1098            if test_block != true_block {
1099                return errors.fatal((
1100                    test_block,
1101                    format!(
1102                        "invalid domtree, postorder block number {} should be {}, got {}",
1103                        index, true_block, test_block
1104                    ),
1105                ));
1106            }
1107        }
1108        // We verify rpo_cmp_block on pairs of adjacent blocks in the postorder
1109        for (&prev_block, &next_block) in domtree.cfg_postorder().iter().adjacent_pairs() {
1110            if self.expected_domtree.rpo_cmp_block(prev_block, next_block) != Ordering::Greater {
1111                return errors.fatal((
1112                    next_block,
1113                    format!(
1114                        "invalid domtree, rpo_cmp_block does not says {} is greater than {}",
1115                        prev_block, next_block
1116                    ),
1117                ));
1118            }
1119        }
1120        Ok(())
1121    }
1122
1123    fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
1124        if let Some(block) = self.func.layout.entry_block() {
1125            let expected_types = &self.func.signature.params;
1126            let block_param_count = self.func.dfg.num_block_params(block);
1127
1128            if block_param_count != expected_types.len() {
1129                return errors.fatal((
1130                    block,
1131                    format!(
1132                        "entry block parameters ({}) must match function signature ({})",
1133                        block_param_count,
1134                        expected_types.len()
1135                    ),
1136                ));
1137            }
1138
1139            for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {
1140                let arg_type = self.func.dfg.value_type(arg);
1141                if arg_type != expected_types[i].value_type {
1142                    errors.report((
1143                        block,
1144                        format!(
1145                            "entry block parameter {} expected to have type {}, got {}",
1146                            i, expected_types[i], arg_type
1147                        ),
1148                    ));
1149                }
1150            }
1151        }
1152
1153        errors.as_result()
1154    }
1155
1156    fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
1157        if let Some(entry_block) = self.func.layout.entry_block() {
1158            if self.func.layout.is_cold(entry_block) {
1159                return errors
1160                    .fatal((entry_block, format!("entry block cannot be marked as cold")));
1161            }
1162        }
1163        errors.as_result()
1164    }
1165
1166    fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
1167        let inst_data = &self.func.dfg.insts[inst];
1168        let constraints = inst_data.opcode().constraints();
1169
1170        let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
1171            // For polymorphic opcodes, determine the controlling type variable first.
1172            let ctrl_type = self.func.dfg.ctrl_typevar(inst);
1173
1174            if !value_typeset.contains(ctrl_type) {
1175                errors.report((
1176                    inst,
1177                    self.context(inst),
1178                    format!("has an invalid controlling type {}", ctrl_type),
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::TableAddr { table, arg, .. } => {
1484                let index_type = self.func.dfg.value_type(arg);
1485                let table_index_type = self.func.tables[table].index_type;
1486                if index_type != table_index_type {
1487                    return errors.nonfatal((
1488                        inst,
1489                        self.context(inst),
1490                        format!(
1491                            "index type {} differs from table index type {}",
1492                            index_type, table_index_type,
1493                        ),
1494                    ));
1495                }
1496            }
1497            ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
1498                if let Some(isa) = self.isa {
1499                    let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
1500                    let global_type = self.func.global_values[global_value].global_type(isa);
1501                    if inst_type != global_type {
1502                        return errors.nonfatal((
1503                            inst, self.context(inst),
1504                            format!(
1505                                "global_value instruction with type {} references global value with type {}",
1506                                inst_type, global_type
1507                            )),
1508                        );
1509                    }
1510                }
1511            }
1512            _ => {}
1513        }
1514        Ok(())
1515    }
1516
1517    fn cfg_integrity(
1518        &self,
1519        cfg: &ControlFlowGraph,
1520        errors: &mut VerifierErrors,
1521    ) -> VerifierStepResult<()> {
1522        let mut expected_succs = BTreeSet::<Block>::new();
1523        let mut got_succs = BTreeSet::<Block>::new();
1524        let mut expected_preds = BTreeSet::<Inst>::new();
1525        let mut got_preds = BTreeSet::<Inst>::new();
1526
1527        for block in self.func.layout.blocks() {
1528            expected_succs.extend(self.expected_cfg.succ_iter(block));
1529            got_succs.extend(cfg.succ_iter(block));
1530
1531            let missing_succs: Vec<Block> =
1532                expected_succs.difference(&got_succs).cloned().collect();
1533            if !missing_succs.is_empty() {
1534                errors.report((
1535                    block,
1536                    format!("cfg lacked the following successor(s) {:?}", missing_succs),
1537                ));
1538                continue;
1539            }
1540
1541            let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
1542            if !excess_succs.is_empty() {
1543                errors.report((
1544                    block,
1545                    format!("cfg had unexpected successor(s) {:?}", excess_succs),
1546                ));
1547                continue;
1548            }
1549
1550            expected_preds.extend(
1551                self.expected_cfg
1552                    .pred_iter(block)
1553                    .map(|BlockPredecessor { inst, .. }| inst),
1554            );
1555            got_preds.extend(
1556                cfg.pred_iter(block)
1557                    .map(|BlockPredecessor { inst, .. }| inst),
1558            );
1559
1560            let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
1561            if !missing_preds.is_empty() {
1562                errors.report((
1563                    block,
1564                    format!(
1565                        "cfg lacked the following predecessor(s) {:?}",
1566                        missing_preds
1567                    ),
1568                ));
1569                continue;
1570            }
1571
1572            let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
1573            if !excess_preds.is_empty() {
1574                errors.report((
1575                    block,
1576                    format!("cfg had unexpected predecessor(s) {:?}", excess_preds),
1577                ));
1578                continue;
1579            }
1580
1581            expected_succs.clear();
1582            got_succs.clear();
1583            expected_preds.clear();
1584            got_preds.clear();
1585        }
1586        errors.as_result()
1587    }
1588
1589    fn immediate_constraints(
1590        &self,
1591        inst: Inst,
1592        errors: &mut VerifierErrors,
1593    ) -> VerifierStepResult<()> {
1594        let inst_data = &self.func.dfg.insts[inst];
1595
1596        match *inst_data {
1597            ir::InstructionData::Store { flags, .. } => {
1598                if flags.readonly() {
1599                    errors.fatal((
1600                        inst,
1601                        self.context(inst),
1602                        "A store instruction cannot have the `readonly` MemFlag",
1603                    ))
1604                } else {
1605                    Ok(())
1606                }
1607            }
1608            ir::InstructionData::BinaryImm8 {
1609                opcode: ir::instructions::Opcode::Extractlane,
1610                imm: lane,
1611                arg,
1612                ..
1613            }
1614            | ir::InstructionData::TernaryImm8 {
1615                opcode: ir::instructions::Opcode::Insertlane,
1616                imm: lane,
1617                args: [arg, _],
1618                ..
1619            } => {
1620                // We must be specific about the opcodes above because other instructions are using
1621                // the same formats.
1622                let ty = self.func.dfg.value_type(arg);
1623                if lane as u32 >= ty.lane_count() {
1624                    errors.fatal((
1625                        inst,
1626                        self.context(inst),
1627                        format!("The lane {} does not index into the type {}", lane, ty,),
1628                    ))
1629                } else {
1630                    Ok(())
1631                }
1632            }
1633            ir::InstructionData::Shuffle {
1634                opcode: ir::instructions::Opcode::Shuffle,
1635                imm,
1636                ..
1637            } => {
1638                let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
1639                if imm.len() != 16 {
1640                    errors.fatal((
1641                        inst,
1642                        self.context(inst),
1643                        format!("the shuffle immediate wasn't 16-bytes long"),
1644                    ))
1645                } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
1646                    errors.fatal((
1647                        inst,
1648                        self.context(inst),
1649                        format!("shuffle immediate index {i} is larger than the maximum 31"),
1650                    ))
1651                } else {
1652                    Ok(())
1653                }
1654            }
1655            _ => Ok(()),
1656        }
1657    }
1658
1659    fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
1660        let params = self
1661            .func
1662            .signature
1663            .params
1664            .iter()
1665            .enumerate()
1666            .map(|p| (true, p));
1667        let returns = self
1668            .func
1669            .signature
1670            .returns
1671            .iter()
1672            .enumerate()
1673            .map(|p| (false, p));
1674
1675        for (is_argument, (i, param)) in params.chain(returns) {
1676            let is_return = !is_argument;
1677            let item = if is_argument {
1678                "Parameter"
1679            } else {
1680                "Return value"
1681            };
1682
1683            if param.value_type == types::INVALID {
1684                errors.report((
1685                    AnyEntity::Function,
1686                    format!("{item} at position {i} has an invalid type"),
1687                ));
1688            }
1689
1690            if let ArgumentPurpose::StructArgument(_) = param.purpose {
1691                if is_return {
1692                    errors.report((
1693                        AnyEntity::Function,
1694                        format!("{item} at position {i} can't be an struct argument"),
1695                    ))
1696                }
1697            }
1698
1699            let ty_allows_extension = param.value_type.is_int();
1700            let has_extension = param.extension != ArgumentExtension::None;
1701            if !ty_allows_extension && has_extension {
1702                errors.report((
1703                    AnyEntity::Function,
1704                    format!(
1705                        "{} at position {} has invalid extension {:?}",
1706                        item, i, param.extension
1707                    ),
1708                ));
1709            }
1710        }
1711
1712        if errors.has_error() {
1713            Err(())
1714        } else {
1715            Ok(())
1716        }
1717    }
1718
1719    pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
1720        self.verify_global_values(errors)?;
1721        self.verify_tables(errors)?;
1722        self.typecheck_entry_block_params(errors)?;
1723        self.check_entry_not_cold(errors)?;
1724        self.typecheck_function_signature(errors)?;
1725
1726        for block in self.func.layout.blocks() {
1727            if self.func.layout.first_inst(block).is_none() {
1728                return errors.fatal((block, format!("{} cannot be empty", block)));
1729            }
1730            for inst in self.func.layout.block_insts(block) {
1731                self.block_integrity(block, inst, errors)?;
1732                self.instruction_integrity(inst, errors)?;
1733                self.typecheck(inst, errors)?;
1734                self.immediate_constraints(inst, errors)?;
1735            }
1736
1737            self.encodable_as_bb(block, errors)?;
1738        }
1739
1740        if !errors.is_empty() {
1741            log::warn!(
1742                "Found verifier errors in function:\n{}",
1743                pretty_verifier_error(self.func, None, errors.clone())
1744            );
1745        }
1746
1747        Ok(())
1748    }
1749}
1750
1751#[cfg(test)]
1752mod tests {
1753    use super::{Verifier, VerifierError, VerifierErrors};
1754    use crate::ir::instructions::{InstructionData, Opcode};
1755    use crate::ir::{types, AbiParam, Function};
1756    use crate::settings;
1757
1758    macro_rules! assert_err_with_msg {
1759        ($e:expr, $msg:expr) => {
1760            match $e.0.get(0) {
1761                None => panic!("Expected an error"),
1762                Some(&VerifierError { ref message, .. }) => {
1763                    if !message.contains($msg) {
1764                        #[cfg(feature = "std")]
1765                        panic!("'{}' did not contain the substring '{}'", message, $msg);
1766                        #[cfg(not(feature = "std"))]
1767                        panic!("error message did not contain the expected substring");
1768                    }
1769                }
1770            }
1771        };
1772    }
1773
1774    #[test]
1775    fn empty() {
1776        let func = Function::new();
1777        let flags = &settings::Flags::new(settings::builder());
1778        let verifier = Verifier::new(&func, flags.into());
1779        let mut errors = VerifierErrors::default();
1780
1781        assert_eq!(verifier.run(&mut errors), Ok(()));
1782        assert!(errors.0.is_empty());
1783    }
1784
1785    #[test]
1786    fn bad_instruction_format() {
1787        let mut func = Function::new();
1788        let block0 = func.dfg.make_block();
1789        func.layout.append_block(block0);
1790        let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
1791            opcode: Opcode::F32const,
1792            imm: 0.into(),
1793        });
1794        func.layout.append_inst(nullary_with_bad_opcode, block0);
1795        let destination = func.dfg.block_call(block0, &[]);
1796        func.stencil.layout.append_inst(
1797            func.stencil.dfg.make_inst(InstructionData::Jump {
1798                opcode: Opcode::Jump,
1799                destination,
1800            }),
1801            block0,
1802        );
1803        let flags = &settings::Flags::new(settings::builder());
1804        let verifier = Verifier::new(&func, flags.into());
1805        let mut errors = VerifierErrors::default();
1806
1807        let _ = verifier.run(&mut errors);
1808
1809        assert_err_with_msg!(errors, "instruction format");
1810    }
1811
1812    #[test]
1813    fn test_function_invalid_param() {
1814        let mut func = Function::new();
1815        func.signature.params.push(AbiParam::new(types::INVALID));
1816
1817        let mut errors = VerifierErrors::default();
1818        let flags = &settings::Flags::new(settings::builder());
1819        let verifier = Verifier::new(&func, flags.into());
1820
1821        let _ = verifier.typecheck_function_signature(&mut errors);
1822        assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");
1823    }
1824
1825    #[test]
1826    fn test_function_invalid_return_value() {
1827        let mut func = Function::new();
1828        func.signature.returns.push(AbiParam::new(types::INVALID));
1829
1830        let mut errors = VerifierErrors::default();
1831        let flags = &settings::Flags::new(settings::builder());
1832        let verifier = Verifier::new(&func, flags.into());
1833
1834        let _ = verifier.typecheck_function_signature(&mut errors);
1835        assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");
1836    }
1837
1838    #[test]
1839    fn test_printing_contextual_errors() {
1840        // Build function.
1841        let mut func = Function::new();
1842        let block0 = func.dfg.make_block();
1843        func.layout.append_block(block0);
1844
1845        // Build instruction: v0, v1 = iconst 42
1846        let inst = func.dfg.make_inst(InstructionData::UnaryImm {
1847            opcode: Opcode::Iconst,
1848            imm: 42.into(),
1849        });
1850        func.dfg.append_result(inst, types::I32);
1851        func.dfg.append_result(inst, types::I32);
1852        func.layout.append_inst(inst, block0);
1853
1854        // Setup verifier.
1855        let mut errors = VerifierErrors::default();
1856        let flags = &settings::Flags::new(settings::builder());
1857        let verifier = Verifier::new(&func, flags.into());
1858
1859        // Now the error message, when printed, should contain the instruction sequence causing the
1860        // error (i.e. v0, v1 = iconst.i32 42) and not only its entity value (i.e. inst0)
1861        let _ = verifier.typecheck_results(inst, types::I32, &mut errors);
1862        assert_eq!(
1863            format!("{}", errors.0[0]),
1864            "inst0 (v0, v1 = iconst.i32 42): has more result values than expected"
1865        )
1866    }
1867
1868    #[test]
1869    fn test_empty_block() {
1870        let mut func = Function::new();
1871        let block0 = func.dfg.make_block();
1872        func.layout.append_block(block0);
1873
1874        let flags = &settings::Flags::new(settings::builder());
1875        let verifier = Verifier::new(&func, flags.into());
1876        let mut errors = VerifierErrors::default();
1877        let _ = verifier.run(&mut errors);
1878
1879        assert_err_with_msg!(errors, "block0 cannot be empty");
1880    }
1881}