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