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