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