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, BlockArg, ExceptionTable};
73 use crate::ir::{
74     ArgumentPurpose, Block, Constant, DynamicStackSlot, FuncRef, Function, GlobalValue, Inst,
75     JumpTable, MemFlags, MemoryTypeData, Opcode, SigRef, StackSlot, Type, Value, ValueDef,
76     ValueList, types,
77 };
78 use crate::ir::{ExceptionTableItem, Signature};
79 use crate::isa::{CallConv, TargetIsa};
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::fmt::{self, Display, Formatter};
87 use cranelift_entity::packed_option::ReservedValue;
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 core::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 core::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() { Ok(()) } else { Err(()) }
208     }
209 
210     /// Report an error, adding it to the list of errors.
211     pub fn report(&mut self, error: impl Into<VerifierError>) {
212         self.0.push(error.into());
213     }
214 
215     /// Report a fatal error and return `Err`.
216     pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
217         self.report(error);
218         Err(())
219     }
220 
221     /// Report a non-fatal error and return `Ok`.
222     pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
223         self.report(error);
224         Ok(())
225     }
226 }
227 
228 impl From<Vec<VerifierError>> for VerifierErrors {
229     fn from(v: Vec<VerifierError>) -> Self {
230         Self(v)
231     }
232 }
233 
234 impl From<VerifierErrors> for Vec<VerifierError> {
235     fn from(errors: VerifierErrors) -> Vec<VerifierError> {
236         errors.0
237     }
238 }
239 
240 impl From<VerifierErrors> for VerifierResult<()> {
241     fn from(errors: VerifierErrors) -> VerifierResult<()> {
242         if errors.is_empty() {
243             Ok(())
244         } else {
245             Err(errors)
246         }
247     }
248 }
249 
250 impl Display for VerifierErrors {
251     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
252         for err in &self.0 {
253             writeln!(f, "- {err}")?;
254         }
255         Ok(())
256     }
257 }
258 
259 /// Verify `func`.
260 pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(
261     func: &Function,
262     fisa: FOI,
263 ) -> VerifierResult<()> {
264     let _tt = timing::verifier();
265     let mut errors = VerifierErrors::default();
266     let verifier = Verifier::new(func, fisa.into());
267     let result = verifier.run(&mut errors);
268     if errors.is_empty() {
269         result.unwrap();
270         Ok(())
271     } else {
272         Err(errors)
273     }
274 }
275 
276 /// Verify `func` after checking the integrity of associated context data structures `cfg` and
277 /// `domtree`.
278 pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>(
279     func: &Function,
280     cfg: &ControlFlowGraph,
281     domtree: &DominatorTree,
282     fisa: FOI,
283     errors: &mut VerifierErrors,
284 ) -> VerifierStepResult {
285     let _tt = timing::verifier();
286     let verifier = Verifier::new(func, fisa.into());
287     if cfg.is_valid() {
288         verifier.cfg_integrity(cfg, errors)?;
289     }
290     if domtree.is_valid() {
291         verifier.domtree_integrity(domtree, errors)?;
292     }
293     verifier.run(errors)
294 }
295 
296 #[derive(Clone, Copy, Debug)]
297 enum BlockCallTargetType {
298     Normal,
299     ExNormalRet,
300     Exception,
301 }
302 
303 struct Verifier<'a> {
304     func: &'a Function,
305     expected_cfg: ControlFlowGraph,
306     expected_domtree: DominatorTree,
307     isa: Option<&'a dyn TargetIsa>,
308 }
309 
310 impl<'a> Verifier<'a> {
311     pub fn new(func: &'a Function, fisa: FlagsOrIsa<'a>) -> Self {
312         let expected_cfg = ControlFlowGraph::with_function(func);
313         let expected_domtree = DominatorTree::with_function(func, &expected_cfg);
314         Self {
315             func,
316             expected_cfg,
317             expected_domtree,
318             isa: fisa.isa,
319         }
320     }
321 
322     /// Determine a contextual error string for an instruction.
323     #[inline]
324     fn context(&self, inst: Inst) -> String {
325         self.func.dfg.display_inst(inst).to_string()
326     }
327 
328     // Check for:
329     //  - cycles in the global value declarations.
330     //  - use of 'vmctx' when no special parameter declares it.
331     fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
332         let mut cycle_seen = false;
333         let mut seen = SparseSet::new();
334 
335         'gvs: for gv in self.func.global_values.keys() {
336             seen.clear();
337             seen.insert(gv);
338 
339             let mut cur = gv;
340             loop {
341                 match self.func.global_values[cur] {
342                     ir::GlobalValueData::Load { base, .. }
343                     | ir::GlobalValueData::IAddImm { base, .. } => {
344                         if seen.insert(base).is_some() {
345                             if !cycle_seen {
346                                 errors.report((
347                                     gv,
348                                     format!("global value cycle: {}", DisplayList(seen.as_slice())),
349                                 ));
350                                 // ensures we don't report the cycle multiple times
351                                 cycle_seen = true;
352                             }
353                             continue 'gvs;
354                         }
355 
356                         cur = base;
357                     }
358                     _ => break,
359                 }
360             }
361 
362             match self.func.global_values[gv] {
363                 ir::GlobalValueData::VMContext { .. } => {
364                     if self
365                         .func
366                         .special_param(ir::ArgumentPurpose::VMContext)
367                         .is_none()
368                     {
369                         errors.report((gv, format!("undeclared vmctx reference {gv}")));
370                     }
371                 }
372                 ir::GlobalValueData::IAddImm {
373                     base, global_type, ..
374                 } => {
375                     if !global_type.is_int() {
376                         errors.report((
377                             gv,
378                             format!("iadd_imm global value with non-int type {global_type}"),
379                         ));
380                     } else if let Some(isa) = self.isa {
381                         let base_type = self.func.global_values[base].global_type(isa);
382                         if global_type != base_type {
383                             errors.report((
384                                 gv,
385                                 format!(
386                                     "iadd_imm type {global_type} differs from operand type {base_type}"
387                                 ),
388                             ));
389                         }
390                     }
391                 }
392                 ir::GlobalValueData::Load { base, .. } => {
393                     if let Some(isa) = self.isa {
394                         let base_type = self.func.global_values[base].global_type(isa);
395                         let pointer_type = isa.pointer_type();
396                         if base_type != pointer_type {
397                             errors.report((
398                                 gv,
399                                 format!(
400                                     "base {base} has type {base_type}, which is not the pointer type {pointer_type}"
401                                 ),
402                             ));
403                         }
404                     }
405                 }
406                 _ => {}
407             }
408         }
409 
410         // Invalid global values shouldn't stop us from verifying the rest of the function
411         Ok(())
412     }
413 
414     fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
415         // Verify that all fields are statically-sized and lie within
416         // the struct, do not overlap, and are in offset order
417         for (mt, mt_data) in &self.func.memory_types {
418             match mt_data {
419                 MemoryTypeData::Struct { size, fields } => {
420                     let mut last_offset = 0;
421                     for field in fields {
422                         if field.offset < last_offset {
423                             errors.report((
424                                 mt,
425                                 format!(
426                                     "memory type {} has a field at offset {}, which is out-of-order",
427                                     mt, field.offset
428                                 ),
429                             ));
430                         }
431                         last_offset = match field.offset.checked_add(u64::from(field.ty.bytes())) {
432                             Some(o) => o,
433                             None => {
434                                 errors.report((
435                                         mt,
436                                         format!(
437                                             "memory type {} has a field at offset {} of size {}; offset plus size overflows a u64",
438                                             mt, field.offset, field.ty.bytes()),
439                                 ));
440                                 break;
441                             }
442                         };
443 
444                         if last_offset > *size {
445                             errors.report((
446                                         mt,
447                                         format!(
448                                             "memory type {} has a field at offset {} of size {} that overflows the struct size {}",
449                                             mt, field.offset, field.ty.bytes(), *size),
450                                           ));
451                         }
452                     }
453                 }
454                 _ => {}
455             }
456         }
457 
458         Ok(())
459     }
460 
461     /// Check that the given block can be encoded as a BB, by checking that only
462     /// branching instructions are ending the block.
463     fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult {
464         match self.func.is_block_basic(block) {
465             Ok(()) => Ok(()),
466             Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),
467         }
468     }
469 
470     fn block_integrity(
471         &self,
472         block: Block,
473         inst: Inst,
474         errors: &mut VerifierErrors,
475     ) -> VerifierStepResult {
476         let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();
477         let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
478 
479         if is_terminator && !is_last_inst {
480             // Terminating instructions only occur at the end of blocks.
481             return errors.fatal((
482                 inst,
483                 self.context(inst),
484                 format!("a terminator instruction was encountered before the end of {block}"),
485             ));
486         }
487         if is_last_inst && !is_terminator {
488             return errors.fatal((block, "block does not end in a terminator instruction"));
489         }
490 
491         // Instructions belong to the correct block.
492         let inst_block = self.func.layout.inst_block(inst);
493         if inst_block != Some(block) {
494             return errors.fatal((
495                 inst,
496                 self.context(inst),
497                 format!("should belong to {block} not {inst_block:?}"),
498             ));
499         }
500 
501         // Parameters belong to the correct block.
502         for &arg in self.func.dfg.block_params(block) {
503             match self.func.dfg.value_def(arg) {
504                 ValueDef::Param(arg_block, _) => {
505                     if block != arg_block {
506                         return errors.fatal((arg, format!("does not belong to {block}")));
507                     }
508                 }
509                 _ => {
510                     return errors.fatal((arg, "expected an argument, found a result"));
511                 }
512             }
513         }
514 
515         Ok(())
516     }
517 
518     fn instruction_integrity(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
519         let inst_data = &self.func.dfg.insts[inst];
520         let dfg = &self.func.dfg;
521 
522         // The instruction format matches the opcode
523         if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
524             return errors.fatal((
525                 inst,
526                 self.context(inst),
527                 "instruction opcode doesn't match instruction format",
528             ));
529         }
530 
531         let expected_num_results = dfg.num_expected_results_for_verifier(inst);
532 
533         // All result values for multi-valued instructions are created
534         let got_results = dfg.inst_results(inst).len();
535         if got_results != expected_num_results {
536             return errors.fatal((
537                 inst,
538                 self.context(inst),
539                 format!("expected {expected_num_results} result values, found {got_results}"),
540             ));
541         }
542 
543         self.verify_entity_references(inst, errors)
544     }
545 
546     fn verify_entity_references(
547         &self,
548         inst: Inst,
549         errors: &mut VerifierErrors,
550     ) -> VerifierStepResult {
551         use crate::ir::instructions::InstructionData::*;
552 
553         for arg in self.func.dfg.inst_values(inst) {
554             self.verify_inst_arg(inst, arg, errors)?;
555 
556             // All used values must be attached to something.
557             let original = self.func.dfg.resolve_aliases(arg);
558             if !self.func.dfg.value_is_attached(original) {
559                 errors.report((
560                     inst,
561                     self.context(inst),
562                     format!("argument {arg} -> {original} is not attached"),
563                 ));
564             }
565         }
566 
567         for &res in self.func.dfg.inst_results(inst) {
568             self.verify_inst_result(inst, res, errors)?;
569         }
570 
571         match self.func.dfg.insts[inst] {
572             MultiAry { ref args, .. } => {
573                 self.verify_value_list(inst, args, errors)?;
574             }
575             Jump { destination, .. } => {
576                 self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;
577             }
578             Brif {
579                 arg,
580                 blocks: [block_then, block_else],
581                 ..
582             } => {
583                 self.verify_value(inst, arg, errors)?;
584                 self.verify_block(inst, block_then.block(&self.func.dfg.value_lists), errors)?;
585                 self.verify_block(inst, block_else.block(&self.func.dfg.value_lists), errors)?;
586             }
587             BranchTable { table, .. } => {
588                 self.verify_jump_table(inst, table, errors)?;
589             }
590             Call {
591                 opcode,
592                 func_ref,
593                 ref args,
594                 ..
595             } => {
596                 self.verify_func_ref(inst, func_ref, errors)?;
597                 self.verify_value_list(inst, args, errors)?;
598                 self.verify_callee_patchability(inst, func_ref, opcode, errors)?;
599             }
600             CallIndirect {
601                 sig_ref, ref args, ..
602             } => {
603                 self.verify_sig_ref(inst, sig_ref, errors)?;
604                 self.verify_value_list(inst, args, errors)?;
605             }
606             TryCall {
607                 func_ref,
608                 ref args,
609                 exception,
610                 ..
611             } => {
612                 self.verify_func_ref(inst, func_ref, errors)?;
613                 self.verify_value_list(inst, args, errors)?;
614                 self.verify_exception_table(inst, exception, errors)?;
615                 self.verify_exception_compatible_abi(inst, exception, errors)?;
616             }
617             TryCallIndirect {
618                 ref args,
619                 exception,
620                 ..
621             } => {
622                 self.verify_value_list(inst, args, errors)?;
623                 self.verify_exception_table(inst, exception, errors)?;
624                 self.verify_exception_compatible_abi(inst, exception, errors)?;
625             }
626             FuncAddr { func_ref, .. } => {
627                 self.verify_func_ref(inst, func_ref, errors)?;
628             }
629             StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {
630                 self.verify_stack_slot(inst, stack_slot, errors)?;
631             }
632             DynamicStackLoad {
633                 dynamic_stack_slot, ..
634             }
635             | DynamicStackStore {
636                 dynamic_stack_slot, ..
637             } => {
638                 self.verify_dynamic_stack_slot(inst, dynamic_stack_slot, errors)?;
639             }
640             UnaryGlobalValue { global_value, .. } => {
641                 self.verify_global_value(inst, global_value, errors)?;
642             }
643             NullAry {
644                 opcode: Opcode::GetPinnedReg,
645             }
646             | Unary {
647                 opcode: Opcode::SetPinnedReg,
648                 ..
649             } => {
650                 if let Some(isa) = &self.isa {
651                     if !isa.flags().enable_pinned_reg() {
652                         return errors.fatal((
653                             inst,
654                             self.context(inst),
655                             "GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg",
656                         ));
657                     }
658                 } else {
659                     return errors.fatal((
660                         inst,
661                         self.context(inst),
662                         "GetPinnedReg/SetPinnedReg need an ISA!",
663                     ));
664                 }
665             }
666             NullAry {
667                 opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,
668             } => {
669                 if let Some(isa) = &self.isa {
670                     // Backends may already rely on this check implicitly, so do
671                     // not relax it without verifying that it is safe to do so.
672                     if !isa.flags().preserve_frame_pointers() {
673                         return errors.fatal((
674                             inst,
675                             self.context(inst),
676                             "`get_frame_pointer`/`get_return_address` cannot be used without \
677                              enabling `preserve_frame_pointers`",
678                         ));
679                     }
680                 } else {
681                     return errors.fatal((
682                         inst,
683                         self.context(inst),
684                         "`get_frame_pointer`/`get_return_address` require an ISA!",
685                     ));
686                 }
687             }
688             LoadNoOffset {
689                 opcode: Opcode::Bitcast,
690                 flags,
691                 arg,
692             } => {
693                 self.verify_bitcast(inst, flags, arg, errors)?;
694             }
695             LoadNoOffset { opcode, arg, .. } if opcode.can_load() => {
696                 self.verify_is_address(inst, arg, errors)?;
697             }
698             Load { opcode, arg, .. } if opcode.can_load() => {
699                 self.verify_is_address(inst, arg, errors)?;
700             }
701             AtomicCas {
702                 opcode,
703                 args: [p, _, _],
704                 ..
705             } if opcode.can_load() || opcode.can_store() => {
706                 self.verify_is_address(inst, p, errors)?;
707             }
708             AtomicRmw {
709                 opcode,
710                 args: [p, _],
711                 ..
712             } if opcode.can_load() || opcode.can_store() => {
713                 self.verify_is_address(inst, p, errors)?;
714             }
715             Store {
716                 opcode,
717                 args: [_, p],
718                 ..
719             } if opcode.can_store() => {
720                 self.verify_is_address(inst, p, errors)?;
721             }
722             StoreNoOffset {
723                 opcode,
724                 args: [_, p],
725                 ..
726             } if opcode.can_store() => {
727                 self.verify_is_address(inst, p, errors)?;
728             }
729             UnaryConst {
730                 opcode: opcode @ (Opcode::Vconst | Opcode::F128const),
731                 constant_handle,
732                 ..
733             } => {
734                 self.verify_constant_size(inst, opcode, constant_handle, errors)?;
735             }
736 
737             ExceptionHandlerAddress { block, imm, .. } => {
738                 self.verify_block(inst, block, errors)?;
739                 self.verify_try_call_handler_index(inst, block, imm.into(), errors)?;
740             }
741 
742             // Exhaustive list so we can't forget to add new formats
743             AtomicCas { .. }
744             | AtomicRmw { .. }
745             | LoadNoOffset { .. }
746             | StoreNoOffset { .. }
747             | Unary { .. }
748             | UnaryConst { .. }
749             | UnaryImm { .. }
750             | UnaryIeee16 { .. }
751             | UnaryIeee32 { .. }
752             | UnaryIeee64 { .. }
753             | Binary { .. }
754             | BinaryImm8 { .. }
755             | BinaryImm64 { .. }
756             | Ternary { .. }
757             | TernaryImm8 { .. }
758             | Shuffle { .. }
759             | IntAddTrap { .. }
760             | IntCompare { .. }
761             | IntCompareImm { .. }
762             | FloatCompare { .. }
763             | Load { .. }
764             | Store { .. }
765             | Trap { .. }
766             | CondTrap { .. }
767             | NullAry { .. } => {}
768         }
769 
770         Ok(())
771     }
772 
773     fn verify_block(
774         &self,
775         loc: impl Into<AnyEntity>,
776         e: Block,
777         errors: &mut VerifierErrors,
778     ) -> VerifierStepResult {
779         if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {
780             return errors.fatal((loc, format!("invalid block reference {e}")));
781         }
782         if let Some(entry_block) = self.func.layout.entry_block() {
783             if e == entry_block {
784                 return errors.fatal((loc, format!("invalid reference to entry block {e}")));
785             }
786         }
787         Ok(())
788     }
789 
790     fn verify_sig_ref(
791         &self,
792         inst: Inst,
793         s: SigRef,
794         errors: &mut VerifierErrors,
795     ) -> VerifierStepResult {
796         if !self.func.dfg.signatures.is_valid(s) {
797             errors.fatal((
798                 inst,
799                 self.context(inst),
800                 format!("invalid signature reference {s}"),
801             ))
802         } else {
803             Ok(())
804         }
805     }
806 
807     fn verify_func_ref(
808         &self,
809         inst: Inst,
810         f: FuncRef,
811         errors: &mut VerifierErrors,
812     ) -> VerifierStepResult {
813         if !self.func.dfg.ext_funcs.is_valid(f) {
814             errors.nonfatal((
815                 inst,
816                 self.context(inst),
817                 format!("invalid function reference {f}"),
818             ))
819         } else {
820             Ok(())
821         }
822     }
823 
824     fn verify_stack_slot(
825         &self,
826         inst: Inst,
827         ss: StackSlot,
828         errors: &mut VerifierErrors,
829     ) -> VerifierStepResult {
830         if !self.func.sized_stack_slots.is_valid(ss) {
831             errors.nonfatal((inst, self.context(inst), format!("invalid stack slot {ss}")))
832         } else {
833             Ok(())
834         }
835     }
836 
837     fn verify_dynamic_stack_slot(
838         &self,
839         inst: Inst,
840         ss: DynamicStackSlot,
841         errors: &mut VerifierErrors,
842     ) -> VerifierStepResult {
843         if !self.func.dynamic_stack_slots.is_valid(ss) {
844             errors.nonfatal((
845                 inst,
846                 self.context(inst),
847                 format!("invalid dynamic stack slot {ss}"),
848             ))
849         } else {
850             Ok(())
851         }
852     }
853 
854     fn verify_global_value(
855         &self,
856         inst: Inst,
857         gv: GlobalValue,
858         errors: &mut VerifierErrors,
859     ) -> VerifierStepResult {
860         if !self.func.global_values.is_valid(gv) {
861             errors.nonfatal((
862                 inst,
863                 self.context(inst),
864                 format!("invalid global value {gv}"),
865             ))
866         } else {
867             Ok(())
868         }
869     }
870 
871     fn verify_value_list(
872         &self,
873         inst: Inst,
874         l: &ValueList,
875         errors: &mut VerifierErrors,
876     ) -> VerifierStepResult {
877         if !l.is_valid(&self.func.dfg.value_lists) {
878             errors.nonfatal((
879                 inst,
880                 self.context(inst),
881                 format!("invalid value list reference {l:?}"),
882             ))
883         } else {
884             Ok(())
885         }
886     }
887 
888     fn verify_jump_table(
889         &self,
890         inst: Inst,
891         j: JumpTable,
892         errors: &mut VerifierErrors,
893     ) -> VerifierStepResult {
894         if !self.func.stencil.dfg.jump_tables.is_valid(j) {
895             errors.nonfatal((
896                 inst,
897                 self.context(inst),
898                 format!("invalid jump table reference {j}"),
899             ))
900         } else {
901             let pool = &self.func.stencil.dfg.value_lists;
902             for block in self.func.stencil.dfg.jump_tables[j].all_branches() {
903                 self.verify_block(inst, block.block(pool), errors)?;
904             }
905             Ok(())
906         }
907     }
908 
909     fn verify_exception_table(
910         &self,
911         inst: Inst,
912         et: ExceptionTable,
913         errors: &mut VerifierErrors,
914     ) -> VerifierStepResult {
915         // Verify that the exception table reference itself is valid.
916         if !self.func.stencil.dfg.exception_tables.is_valid(et) {
917             errors.nonfatal((
918                 inst,
919                 self.context(inst),
920                 format!("invalid exception table reference {et}"),
921             ))?;
922         }
923 
924         let pool = &self.func.stencil.dfg.value_lists;
925         let exdata = &self.func.stencil.dfg.exception_tables[et];
926 
927         // Verify that the exception table's signature reference
928         // is valid.
929         self.verify_sig_ref(inst, exdata.signature(), errors)?;
930 
931         // Verify that the exception table's block references are valid.
932         for block in exdata.all_branches() {
933             self.verify_block(inst, block.block(pool), errors)?;
934         }
935         Ok(())
936     }
937 
938     fn verify_exception_compatible_abi(
939         &self,
940         inst: Inst,
941         et: ExceptionTable,
942         errors: &mut VerifierErrors,
943     ) -> VerifierStepResult {
944         let callee_sig_ref = self.func.dfg.exception_tables[et].signature();
945         let callee_sig = &self.func.dfg.signatures[callee_sig_ref];
946         let callee_call_conv = callee_sig.call_conv;
947         if !callee_call_conv.supports_exceptions() {
948             errors.nonfatal((
949                 inst,
950                 self.context(inst),
951                 format!(
952                     "calling convention `{callee_call_conv}` of callee does not support exceptions"
953                 ),
954             ))?;
955         }
956         Ok(())
957     }
958 
959     fn verify_callee_patchability(
960         &self,
961         inst: Inst,
962         func_ref: FuncRef,
963         opcode: Opcode,
964         errors: &mut VerifierErrors,
965     ) -> VerifierStepResult {
966         let ir::ExtFuncData {
967             patchable,
968             colocated,
969             signature,
970             name: _,
971         } = self.func.dfg.ext_funcs[func_ref];
972         let signature = &self.func.dfg.signatures[signature];
973         if patchable && (opcode == Opcode::ReturnCall || opcode == Opcode::ReturnCallIndirect) {
974             errors.fatal((
975                 inst,
976                 self.context(inst),
977                 "patchable funcref cannot be used in a return_call".to_string(),
978             ))?;
979         }
980         if patchable && !colocated {
981             errors.fatal((
982                 inst,
983                 self.context(inst),
984                 "patchable call to non-colocated function".to_string(),
985             ))?;
986         }
987         if patchable && !signature.returns.is_empty() {
988             errors.fatal((
989                 inst,
990                 self.context(inst),
991                 "patchable call cannot occur to a function with return values".to_string(),
992             ))?;
993         }
994         Ok(())
995     }
996 
997     fn verify_value(
998         &self,
999         loc_inst: Inst,
1000         v: Value,
1001         errors: &mut VerifierErrors,
1002     ) -> VerifierStepResult {
1003         let dfg = &self.func.dfg;
1004         if !dfg.value_is_valid(v) {
1005             errors.nonfatal((
1006                 loc_inst,
1007                 self.context(loc_inst),
1008                 format!("invalid value reference {v}"),
1009             ))
1010         } else {
1011             Ok(())
1012         }
1013     }
1014 
1015     fn verify_inst_arg(
1016         &self,
1017         loc_inst: Inst,
1018         v: Value,
1019         errors: &mut VerifierErrors,
1020     ) -> VerifierStepResult {
1021         self.verify_value(loc_inst, v, errors)?;
1022 
1023         let dfg = &self.func.dfg;
1024         let loc_block = self
1025             .func
1026             .layout
1027             .inst_block(loc_inst)
1028             .expect("Instruction not in layout.");
1029         let is_reachable = self.expected_domtree.is_reachable(loc_block);
1030 
1031         // SSA form
1032         match dfg.value_def(v) {
1033             ValueDef::Result(def_inst, _) => {
1034                 // Value is defined by an instruction that exists.
1035                 if !dfg.inst_is_valid(def_inst) {
1036                     return errors.fatal((
1037                         loc_inst,
1038                         self.context(loc_inst),
1039                         format!("{v} is defined by invalid instruction {def_inst}"),
1040                     ));
1041                 }
1042                 // Defining instruction is inserted in a block.
1043                 if self.func.layout.inst_block(def_inst) == None {
1044                     return errors.fatal((
1045                         loc_inst,
1046                         self.context(loc_inst),
1047                         format!("{v} is defined by {def_inst} which has no block"),
1048                     ));
1049                 }
1050                 // Defining instruction dominates the instruction that uses the value.
1051                 if is_reachable {
1052                     if !self
1053                         .expected_domtree
1054                         .dominates(def_inst, loc_inst, &self.func.layout)
1055                     {
1056                         return errors.fatal((
1057                             loc_inst,
1058                             self.context(loc_inst),
1059                             format!("uses value {v} from non-dominating {def_inst}"),
1060                         ));
1061                     }
1062                     if def_inst == loc_inst {
1063                         return errors.fatal((
1064                             loc_inst,
1065                             self.context(loc_inst),
1066                             format!("uses value {v} from itself"),
1067                         ));
1068                     }
1069                 }
1070             }
1071             ValueDef::Param(block, _) => {
1072                 // Value is defined by an existing block.
1073                 if !dfg.block_is_valid(block) {
1074                     return errors.fatal((
1075                         loc_inst,
1076                         self.context(loc_inst),
1077                         format!("{v} is defined by invalid block {block}"),
1078                     ));
1079                 }
1080                 // Defining block is inserted in the layout
1081                 if !self.func.layout.is_block_inserted(block) {
1082                     return errors.fatal((
1083                         loc_inst,
1084                         self.context(loc_inst),
1085                         format!("{v} is defined by {block} which is not in the layout"),
1086                     ));
1087                 }
1088                 let user_block = self.func.layout.inst_block(loc_inst).expect("Expected instruction to be in a block as we're traversing code already in layout");
1089                 // The defining block dominates the instruction using this value.
1090                 if is_reachable && !self.expected_domtree.block_dominates(block, user_block) {
1091                     return errors.fatal((
1092                         loc_inst,
1093                         self.context(loc_inst),
1094                         format!("uses value arg from non-dominating {block}"),
1095                     ));
1096                 }
1097             }
1098             ValueDef::Union(_, _) => {
1099                 // Nothing: union nodes themselves have no location,
1100                 // so we cannot check any dominance properties.
1101             }
1102         }
1103         Ok(())
1104     }
1105 
1106     fn verify_inst_result(
1107         &self,
1108         loc_inst: Inst,
1109         v: Value,
1110         errors: &mut VerifierErrors,
1111     ) -> VerifierStepResult {
1112         self.verify_value(loc_inst, v, errors)?;
1113 
1114         match self.func.dfg.value_def(v) {
1115             ValueDef::Result(def_inst, _) => {
1116                 if def_inst != loc_inst {
1117                     errors.fatal((
1118                         loc_inst,
1119                         self.context(loc_inst),
1120                         format!("instruction result {v} is not defined by the instruction"),
1121                     ))
1122                 } else {
1123                     Ok(())
1124                 }
1125             }
1126             ValueDef::Param(_, _) => errors.fatal((
1127                 loc_inst,
1128                 self.context(loc_inst),
1129                 format!("instruction result {v} is not defined by the instruction"),
1130             )),
1131             ValueDef::Union(_, _) => errors.fatal((
1132                 loc_inst,
1133                 self.context(loc_inst),
1134                 format!("instruction result {v} is a union node"),
1135             )),
1136         }
1137     }
1138 
1139     fn verify_bitcast(
1140         &self,
1141         inst: Inst,
1142         flags: MemFlags,
1143         arg: Value,
1144         errors: &mut VerifierErrors,
1145     ) -> VerifierStepResult {
1146         let typ = self.func.dfg.ctrl_typevar(inst);
1147         let value_type = self.func.dfg.value_type(arg);
1148 
1149         if typ.bits() != value_type.bits() {
1150             errors.fatal((
1151                 inst,
1152                 format!(
1153                     "The bitcast argument {} has a type of {} bits, which doesn't match an expected type of {} bits",
1154                     arg,
1155                     value_type.bits(),
1156                     typ.bits()
1157                 ),
1158             ))
1159         } else if flags != MemFlags::new()
1160             && flags != MemFlags::new().with_endianness(ir::Endianness::Little)
1161             && flags != MemFlags::new().with_endianness(ir::Endianness::Big)
1162         {
1163             errors.fatal((
1164                 inst,
1165                 "The bitcast instruction only accepts the `big` or `little` memory flags",
1166             ))
1167         } else if flags == MemFlags::new() && typ.lane_count() != value_type.lane_count() {
1168             errors.fatal((
1169                 inst,
1170                 "Byte order specifier required for bitcast instruction changing lane count",
1171             ))
1172         } else {
1173             Ok(())
1174         }
1175     }
1176 
1177     fn verify_constant_size(
1178         &self,
1179         inst: Inst,
1180         opcode: Opcode,
1181         constant: Constant,
1182         errors: &mut VerifierErrors,
1183     ) -> VerifierStepResult {
1184         let type_size = match opcode {
1185             Opcode::F128const => types::F128.bytes(),
1186             Opcode::Vconst => self.func.dfg.ctrl_typevar(inst).bytes(),
1187             _ => unreachable!("unexpected opcode {opcode:?}"),
1188         } as usize;
1189         let constant_size = self.func.dfg.constants.get(constant).len();
1190         if type_size != constant_size {
1191             errors.fatal((
1192                 inst,
1193                 format!(
1194                     "The instruction expects {constant} to have a size of {type_size} bytes but it has {constant_size}"
1195                 ),
1196             ))
1197         } else {
1198             Ok(())
1199         }
1200     }
1201 
1202     fn verify_is_address(
1203         &self,
1204         loc_inst: Inst,
1205         v: Value,
1206         errors: &mut VerifierErrors,
1207     ) -> VerifierStepResult {
1208         if let Some(isa) = self.isa {
1209             let pointer_width = isa.triple().pointer_width()?;
1210             let value_type = self.func.dfg.value_type(v);
1211             let expected_width = pointer_width.bits() as u32;
1212             let value_width = value_type.bits();
1213             if expected_width != value_width {
1214                 errors.nonfatal((
1215                     loc_inst,
1216                     self.context(loc_inst),
1217                     format!("invalid pointer width (got {value_width}, expected {expected_width}) encountered {v}"),
1218                 ))
1219             } else {
1220                 Ok(())
1221             }
1222         } else {
1223             Ok(())
1224         }
1225     }
1226 
1227     fn domtree_integrity(
1228         &self,
1229         domtree: &DominatorTree,
1230         errors: &mut VerifierErrors,
1231     ) -> VerifierStepResult {
1232         // We consider two `DominatorTree`s to be equal if they return the same immediate
1233         // dominator for each block. Therefore the current domtree is valid if it matches the freshly
1234         // computed one.
1235         for block in self.func.layout.blocks() {
1236             let expected = self.expected_domtree.idom(block);
1237             let got = domtree.idom(block);
1238             if got != expected {
1239                 return errors.fatal((
1240                     block,
1241                     format!("invalid domtree, expected idom({block}) = {expected:?}, got {got:?}"),
1242                 ));
1243             }
1244         }
1245         // We also verify if the postorder defined by `DominatorTree` is sane
1246         if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {
1247             return errors.fatal((
1248                 AnyEntity::Function,
1249                 "incorrect number of Blocks in postorder traversal",
1250             ));
1251         }
1252         for (index, (&test_block, &true_block)) in domtree
1253             .cfg_postorder()
1254             .iter()
1255             .zip(self.expected_domtree.cfg_postorder().iter())
1256             .enumerate()
1257         {
1258             if test_block != true_block {
1259                 return errors.fatal((
1260                     test_block,
1261                     format!(
1262                         "invalid domtree, postorder block number {index} should be {true_block}, got {test_block}"
1263                     ),
1264                 ));
1265             }
1266         }
1267         Ok(())
1268     }
1269 
1270     fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1271         if let Some(block) = self.func.layout.entry_block() {
1272             let expected_types = &self.func.signature.params;
1273             let block_param_count = self.func.dfg.num_block_params(block);
1274 
1275             if block_param_count != expected_types.len() {
1276                 return errors.fatal((
1277                     block,
1278                     format!(
1279                         "entry block parameters ({}) must match function signature ({})",
1280                         block_param_count,
1281                         expected_types.len()
1282                     ),
1283                 ));
1284             }
1285 
1286             for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {
1287                 let arg_type = self.func.dfg.value_type(arg);
1288                 if arg_type != expected_types[i].value_type {
1289                     errors.report((
1290                         block,
1291                         format!(
1292                             "entry block parameter {} expected to have type {}, got {}",
1293                             i, expected_types[i], arg_type
1294                         ),
1295                     ));
1296                 }
1297             }
1298         }
1299 
1300         errors.as_result()
1301     }
1302 
1303     fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1304         if let Some(entry_block) = self.func.layout.entry_block() {
1305             if self.func.layout.is_cold(entry_block) {
1306                 return errors
1307                     .fatal((entry_block, format!("entry block cannot be marked as cold")));
1308             }
1309         }
1310         errors.as_result()
1311     }
1312 
1313     fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1314         let inst_data = &self.func.dfg.insts[inst];
1315         let constraints = inst_data.opcode().constraints();
1316 
1317         let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
1318             // For polymorphic opcodes, determine the controlling type variable first.
1319             let ctrl_type = self.func.dfg.ctrl_typevar(inst);
1320 
1321             if !value_typeset.contains(ctrl_type) {
1322                 errors.report((
1323                     inst,
1324                     self.context(inst),
1325                     format!(
1326                         "has an invalid controlling type {ctrl_type} (allowed set is {value_typeset:?})"
1327                     ),
1328                 ));
1329             }
1330 
1331             ctrl_type
1332         } else {
1333             // Non-polymorphic instructions don't check the controlling type variable, so `Option`
1334             // is unnecessary and we can just make it `INVALID`.
1335             types::INVALID
1336         };
1337 
1338         // Typechecking instructions is never fatal
1339         let _ = self.typecheck_results(inst, ctrl_type, errors);
1340         let _ = self.typecheck_fixed_args(inst, ctrl_type, errors);
1341         let _ = self.typecheck_variable_args(inst, errors);
1342         let _ = self.typecheck_return(inst, errors);
1343         let _ = self.typecheck_special(inst, errors);
1344 
1345         Ok(())
1346     }
1347 
1348     fn typecheck_results(
1349         &self,
1350         inst: Inst,
1351         ctrl_type: Type,
1352         errors: &mut VerifierErrors,
1353     ) -> VerifierStepResult {
1354         let mut i = 0;
1355         for &result in self.func.dfg.inst_results(inst) {
1356             let result_type = self.func.dfg.value_type(result);
1357             let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type);
1358             if let Some(expected_type) = expected_type {
1359                 if result_type != expected_type {
1360                     errors.report((
1361                         inst,
1362                         self.context(inst),
1363                         format!(
1364                             "expected result {i} ({result}) to have type {expected_type}, found {result_type}"
1365                         ),
1366                     ));
1367                 }
1368             } else {
1369                 return errors.nonfatal((
1370                     inst,
1371                     self.context(inst),
1372                     "has more result values than expected",
1373                 ));
1374             }
1375             i += 1;
1376         }
1377 
1378         // There aren't any more result types left.
1379         if self.func.dfg.compute_result_type(inst, i, ctrl_type) != None {
1380             return errors.nonfatal((
1381                 inst,
1382                 self.context(inst),
1383                 "has fewer result values than expected",
1384             ));
1385         }
1386         Ok(())
1387     }
1388 
1389     fn typecheck_fixed_args(
1390         &self,
1391         inst: Inst,
1392         ctrl_type: Type,
1393         errors: &mut VerifierErrors,
1394     ) -> VerifierStepResult {
1395         let constraints = self.func.dfg.insts[inst].opcode().constraints();
1396 
1397         for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {
1398             let arg_type = self.func.dfg.value_type(arg);
1399             match constraints.value_argument_constraint(i, ctrl_type) {
1400                 ResolvedConstraint::Bound(expected_type) => {
1401                     if arg_type != expected_type {
1402                         errors.report((
1403                             inst,
1404                             self.context(inst),
1405                             format!(
1406                                 "arg {i} ({arg}) has type {arg_type}, expected {expected_type}"
1407                             ),
1408                         ));
1409                     }
1410                 }
1411                 ResolvedConstraint::Free(type_set) => {
1412                     if !type_set.contains(arg_type) {
1413                         errors.report((
1414                             inst,
1415                             self.context(inst),
1416                             format!(
1417                                 "arg {i} ({arg}) with type {arg_type} failed to satisfy type set {type_set:?}"
1418                             ),
1419                         ));
1420                     }
1421                 }
1422             }
1423         }
1424         Ok(())
1425     }
1426 
1427     /// Typecheck both instructions that contain variable arguments like calls, and those that
1428     /// include references to basic blocks with their arguments.
1429     fn typecheck_variable_args(
1430         &self,
1431         inst: Inst,
1432         errors: &mut VerifierErrors,
1433     ) -> VerifierStepResult {
1434         match &self.func.dfg.insts[inst] {
1435             ir::InstructionData::Jump { destination, .. } => {
1436                 self.typecheck_block_call(inst, destination, BlockCallTargetType::Normal, errors)?;
1437             }
1438             ir::InstructionData::Brif {
1439                 blocks: [block_then, block_else],
1440                 ..
1441             } => {
1442                 self.typecheck_block_call(inst, block_then, BlockCallTargetType::Normal, errors)?;
1443                 self.typecheck_block_call(inst, block_else, BlockCallTargetType::Normal, errors)?;
1444             }
1445             ir::InstructionData::BranchTable { table, .. } => {
1446                 for block in self.func.stencil.dfg.jump_tables[*table].all_branches() {
1447                     self.typecheck_block_call(inst, block, BlockCallTargetType::Normal, errors)?;
1448                 }
1449             }
1450             ir::InstructionData::TryCall { exception, .. }
1451             | ir::InstructionData::TryCallIndirect { exception, .. } => {
1452                 let exdata = &self.func.dfg.exception_tables[*exception];
1453                 self.typecheck_block_call(
1454                     inst,
1455                     exdata.normal_return(),
1456                     BlockCallTargetType::ExNormalRet,
1457                     errors,
1458                 )?;
1459                 for item in exdata.items() {
1460                     match item {
1461                         ExceptionTableItem::Tag(_, block_call)
1462                         | ExceptionTableItem::Default(block_call) => {
1463                             self.typecheck_block_call(
1464                                 inst,
1465                                 &block_call,
1466                                 BlockCallTargetType::Exception,
1467                                 errors,
1468                             )?;
1469                         }
1470                         ExceptionTableItem::Context(_) => {}
1471                     }
1472                 }
1473             }
1474             inst => debug_assert!(!inst.opcode().is_branch()),
1475         }
1476 
1477         match self.func.dfg.insts[inst]
1478             .analyze_call(&self.func.dfg.value_lists, &self.func.dfg.exception_tables)
1479         {
1480             CallInfo::Direct(func_ref, args) => {
1481                 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1482                 let arg_types = self.func.dfg.signatures[sig_ref]
1483                     .params
1484                     .iter()
1485                     .map(|a| a.value_type);
1486                 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1487             }
1488             CallInfo::DirectWithSig(func_ref, sig_ref, args) => {
1489                 let expected_sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1490                 let sigdata = &self.func.dfg.signatures;
1491                 // Compare signatures by value, not by ID -- any
1492                 // equivalent signature ID is acceptable.
1493                 if sigdata[sig_ref] != sigdata[expected_sig_ref] {
1494                     errors.nonfatal((
1495                         inst,
1496                         self.context(inst),
1497                         format!(
1498                             "exception table signature {sig_ref} did not match function {func_ref}'s signature {expected_sig_ref}"
1499                         ),
1500                     ))?;
1501                 }
1502                 let arg_types = self.func.dfg.signatures[sig_ref]
1503                     .params
1504                     .iter()
1505                     .map(|a| a.value_type);
1506                 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1507             }
1508             CallInfo::Indirect(sig_ref, args) => {
1509                 let arg_types = self.func.dfg.signatures[sig_ref]
1510                     .params
1511                     .iter()
1512                     .map(|a| a.value_type);
1513                 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1514             }
1515             CallInfo::NotACall => {}
1516         }
1517         Ok(())
1518     }
1519 
1520     fn typecheck_block_call(
1521         &self,
1522         inst: Inst,
1523         block: &ir::BlockCall,
1524         target_type: BlockCallTargetType,
1525         errors: &mut VerifierErrors,
1526     ) -> VerifierStepResult {
1527         let pool = &self.func.dfg.value_lists;
1528         let block_params = self.func.dfg.block_params(block.block(pool));
1529         let args = block.args(pool);
1530         if args.len() != block_params.len() {
1531             return errors.nonfatal((
1532                 inst,
1533                 self.context(inst),
1534                 format!(
1535                     "mismatched argument count for `{}`: got {}, expected {}",
1536                     self.func.dfg.display_inst(inst),
1537                     args.len(),
1538                     block_params.len(),
1539                 ),
1540             ));
1541         }
1542         for (arg, param) in args.zip(block_params.iter()) {
1543             let Some(arg_ty) = self.block_call_arg_ty(arg, inst, target_type, errors)? else {
1544                 continue;
1545             };
1546             let param_ty = self.func.dfg.value_type(*param);
1547             if arg_ty != param_ty {
1548                 errors.nonfatal((
1549                     inst,
1550                     self.context(inst),
1551                     format!("arg {arg} has type {arg_ty}, expected {param_ty}"),
1552                 ))?;
1553             }
1554         }
1555         Ok(())
1556     }
1557 
1558     fn block_call_arg_ty(
1559         &self,
1560         arg: BlockArg,
1561         inst: Inst,
1562         target_type: BlockCallTargetType,
1563         errors: &mut VerifierErrors,
1564     ) -> Result<Option<Type>, ()> {
1565         match arg {
1566             BlockArg::Value(v) => Ok(Some(self.func.dfg.value_type(v))),
1567             BlockArg::TryCallRet(_) | BlockArg::TryCallExn(_) => {
1568                 // Get the invoked signature.
1569                 let et = match self.func.dfg.insts[inst].exception_table() {
1570                     Some(et) => et,
1571                     None => {
1572                         errors.fatal((
1573                             inst,
1574                             self.context(inst),
1575                             format!(
1576                                 "`retN` block argument in block-call not on `try_call` instruction"
1577                             ),
1578                         ))?;
1579                         unreachable!()
1580                     }
1581                 };
1582                 let exdata = &self.func.dfg.exception_tables[et];
1583                 let sig = &self.func.dfg.signatures[exdata.signature()];
1584 
1585                 match (arg, target_type) {
1586                     (BlockArg::TryCallRet(i), BlockCallTargetType::ExNormalRet)
1587                         if (i as usize) < sig.returns.len() =>
1588                     {
1589                         Ok(Some(sig.returns[i as usize].value_type))
1590                     }
1591                     (BlockArg::TryCallRet(_), BlockCallTargetType::ExNormalRet) => {
1592                         errors.fatal((
1593                             inst,
1594                             self.context(inst),
1595                             format!("out-of-bounds `retN` block argument"),
1596                         ))?;
1597                         unreachable!()
1598                     }
1599                     (BlockArg::TryCallRet(_), _) => {
1600                         errors.fatal((
1601                             inst,
1602                             self.context(inst),
1603                             format!("`retN` block argument used outside normal-return target of `try_call`"),
1604                         ))?;
1605                         unreachable!()
1606                     }
1607                     (BlockArg::TryCallExn(i), BlockCallTargetType::Exception) => {
1608                         if let Some(isa) = self.isa {
1609                             match sig
1610                                 .call_conv
1611                                 .exception_payload_types(isa.pointer_type())
1612                                 .get(i as usize)
1613                             {
1614                                 Some(ty) => Ok(Some(*ty)),
1615                                 None => {
1616                                     errors.fatal((
1617                                         inst,
1618                                         self.context(inst),
1619                                         format!("out-of-bounds `exnN` block argument"),
1620                                     ))?;
1621                                     unreachable!()
1622                                 }
1623                             }
1624                         } else {
1625                             Ok(None)
1626                         }
1627                     }
1628                     (BlockArg::TryCallExn(_), _) => {
1629                         errors.fatal((
1630                             inst,
1631                             self.context(inst),
1632                             format!("`exnN` block argument used outside normal-return target of `try_call`"),
1633                         ))?;
1634                         unreachable!()
1635                     }
1636                     _ => unreachable!(),
1637                 }
1638             }
1639         }
1640     }
1641 
1642     fn typecheck_variable_args_iterator(
1643         &self,
1644         inst: Inst,
1645         iter: impl ExactSizeIterator<Item = Type>,
1646         variable_args: &[Value],
1647         errors: &mut VerifierErrors,
1648     ) -> VerifierStepResult {
1649         let mut i = 0;
1650 
1651         for expected_type in iter {
1652             if i >= variable_args.len() {
1653                 // Result count mismatch handled below, we want the full argument count first though
1654                 i += 1;
1655                 continue;
1656             }
1657             let arg = variable_args[i];
1658             let arg_type = self.func.dfg.value_type(arg);
1659             if expected_type != arg_type {
1660                 errors.report((
1661                     inst,
1662                     self.context(inst),
1663                     format!(
1664                         "arg {} ({}) has type {}, expected {}",
1665                         i, variable_args[i], arg_type, expected_type
1666                     ),
1667                 ));
1668             }
1669             i += 1;
1670         }
1671         if i != variable_args.len() {
1672             return errors.nonfatal((
1673                 inst,
1674                 self.context(inst),
1675                 format!(
1676                     "mismatched argument count for `{}`: got {}, expected {}",
1677                     self.func.dfg.display_inst(inst),
1678                     variable_args.len(),
1679                     i,
1680                 ),
1681             ));
1682         }
1683         Ok(())
1684     }
1685 
1686     fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1687         match self.func.dfg.insts[inst] {
1688             ir::InstructionData::MultiAry {
1689                 opcode: Opcode::Return,
1690                 args,
1691             } => {
1692                 let types = args
1693                     .as_slice(&self.func.dfg.value_lists)
1694                     .iter()
1695                     .map(|v| self.func.dfg.value_type(*v));
1696                 self.typecheck_return_types(
1697                     inst,
1698                     types,
1699                     errors,
1700                     "arguments of return must match function signature",
1701                 )?;
1702             }
1703             ir::InstructionData::Call {
1704                 opcode: Opcode::ReturnCall,
1705                 func_ref,
1706                 ..
1707             } => {
1708                 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1709                 self.typecheck_tail_call(inst, sig_ref, errors)?;
1710             }
1711             ir::InstructionData::CallIndirect {
1712                 opcode: Opcode::ReturnCallIndirect,
1713                 sig_ref,
1714                 ..
1715             } => {
1716                 self.typecheck_tail_call(inst, sig_ref, errors)?;
1717             }
1718             inst => debug_assert!(!inst.opcode().is_return()),
1719         }
1720         Ok(())
1721     }
1722 
1723     fn typecheck_tail_call(
1724         &self,
1725         inst: Inst,
1726         sig_ref: SigRef,
1727         errors: &mut VerifierErrors,
1728     ) -> VerifierStepResult {
1729         let signature = &self.func.dfg.signatures[sig_ref];
1730         let cc = signature.call_conv;
1731         if !cc.supports_tail_calls() {
1732             errors.report((
1733                 inst,
1734                 self.context(inst),
1735                 format!("calling convention `{cc}` does not support tail calls"),
1736             ));
1737         }
1738         if cc != self.func.signature.call_conv {
1739             errors.report((
1740                 inst,
1741                 self.context(inst),
1742                 "callee's calling convention must match caller",
1743             ));
1744         }
1745         let types = signature.returns.iter().map(|param| param.value_type);
1746         self.typecheck_return_types(inst, types, errors, "results of callee must match caller")?;
1747         Ok(())
1748     }
1749 
1750     fn typecheck_return_types(
1751         &self,
1752         inst: Inst,
1753         actual_types: impl ExactSizeIterator<Item = Type>,
1754         errors: &mut VerifierErrors,
1755         message: &str,
1756     ) -> VerifierStepResult {
1757         let expected_types = &self.func.signature.returns;
1758         if actual_types.len() != expected_types.len() {
1759             return errors.nonfatal((inst, self.context(inst), message));
1760         }
1761         for (i, (actual_type, &expected_type)) in actual_types.zip(expected_types).enumerate() {
1762             if actual_type != expected_type.value_type {
1763                 errors.report((
1764                     inst,
1765                     self.context(inst),
1766                     format!(
1767                         "result {i} has type {actual_type}, must match function signature of \
1768                          {expected_type}"
1769                     ),
1770                 ));
1771             }
1772         }
1773         Ok(())
1774     }
1775 
1776     // Check special-purpose type constraints that can't be expressed in the normal opcode
1777     // constraints.
1778     fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1779         match self.func.dfg.insts[inst] {
1780             ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
1781                 if let Some(isa) = self.isa {
1782                     let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
1783                     let global_type = self.func.global_values[global_value].global_type(isa);
1784                     if inst_type != global_type {
1785                         return errors.nonfatal((
1786                             inst, self.context(inst),
1787                             format!(
1788                                 "global_value instruction with type {inst_type} references global value with type {global_type}"
1789                             )),
1790                         );
1791                     }
1792                 }
1793             }
1794             _ => {}
1795         }
1796         Ok(())
1797     }
1798 
1799     fn cfg_integrity(
1800         &self,
1801         cfg: &ControlFlowGraph,
1802         errors: &mut VerifierErrors,
1803     ) -> VerifierStepResult {
1804         let mut expected_succs = BTreeSet::<Block>::new();
1805         let mut got_succs = BTreeSet::<Block>::new();
1806         let mut expected_preds = BTreeSet::<Inst>::new();
1807         let mut got_preds = BTreeSet::<Inst>::new();
1808 
1809         for block in self.func.layout.blocks() {
1810             expected_succs.extend(self.expected_cfg.succ_iter(block));
1811             got_succs.extend(cfg.succ_iter(block));
1812 
1813             let missing_succs: Vec<Block> =
1814                 expected_succs.difference(&got_succs).cloned().collect();
1815             if !missing_succs.is_empty() {
1816                 errors.report((
1817                     block,
1818                     format!("cfg lacked the following successor(s) {missing_succs:?}"),
1819                 ));
1820                 continue;
1821             }
1822 
1823             let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
1824             if !excess_succs.is_empty() {
1825                 errors.report((
1826                     block,
1827                     format!("cfg had unexpected successor(s) {excess_succs:?}"),
1828                 ));
1829                 continue;
1830             }
1831 
1832             expected_preds.extend(
1833                 self.expected_cfg
1834                     .pred_iter(block)
1835                     .map(|BlockPredecessor { inst, .. }| inst),
1836             );
1837             got_preds.extend(
1838                 cfg.pred_iter(block)
1839                     .map(|BlockPredecessor { inst, .. }| inst),
1840             );
1841 
1842             let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
1843             if !missing_preds.is_empty() {
1844                 errors.report((
1845                     block,
1846                     format!("cfg lacked the following predecessor(s) {missing_preds:?}"),
1847                 ));
1848                 continue;
1849             }
1850 
1851             let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
1852             if !excess_preds.is_empty() {
1853                 errors.report((
1854                     block,
1855                     format!("cfg had unexpected predecessor(s) {excess_preds:?}"),
1856                 ));
1857                 continue;
1858             }
1859 
1860             expected_succs.clear();
1861             got_succs.clear();
1862             expected_preds.clear();
1863             got_preds.clear();
1864         }
1865         errors.as_result()
1866     }
1867 
1868     fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1869         let inst_data = &self.func.dfg.insts[inst];
1870 
1871         match *inst_data {
1872             ir::InstructionData::Store { flags, .. } => {
1873                 if flags.readonly() {
1874                     errors.fatal((
1875                         inst,
1876                         self.context(inst),
1877                         "A store instruction cannot have the `readonly` MemFlag",
1878                     ))
1879                 } else {
1880                     Ok(())
1881                 }
1882             }
1883             ir::InstructionData::BinaryImm8 {
1884                 opcode: ir::instructions::Opcode::Extractlane,
1885                 imm: lane,
1886                 arg,
1887                 ..
1888             }
1889             | ir::InstructionData::TernaryImm8 {
1890                 opcode: ir::instructions::Opcode::Insertlane,
1891                 imm: lane,
1892                 args: [arg, _],
1893                 ..
1894             } => {
1895                 // We must be specific about the opcodes above because other instructions are using
1896                 // the same formats.
1897                 let ty = self.func.dfg.value_type(arg);
1898                 if lane as u32 >= ty.lane_count() {
1899                     errors.fatal((
1900                         inst,
1901                         self.context(inst),
1902                         format!("The lane {lane} does not index into the type {ty}",),
1903                     ))
1904                 } else {
1905                     Ok(())
1906                 }
1907             }
1908             ir::InstructionData::Shuffle {
1909                 opcode: ir::instructions::Opcode::Shuffle,
1910                 imm,
1911                 ..
1912             } => {
1913                 let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
1914                 if imm.len() != 16 {
1915                     errors.fatal((
1916                         inst,
1917                         self.context(inst),
1918                         format!("the shuffle immediate wasn't 16-bytes long"),
1919                     ))
1920                 } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
1921                     errors.fatal((
1922                         inst,
1923                         self.context(inst),
1924                         format!("shuffle immediate index {i} is larger than the maximum 31"),
1925                     ))
1926                 } else {
1927                     Ok(())
1928                 }
1929             }
1930             _ => Ok(()),
1931         }
1932     }
1933 
1934     fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1935         use crate::ir::instructions::InstructionData::UnaryImm;
1936 
1937         let inst_data = &self.func.dfg.insts[inst];
1938         if let UnaryImm {
1939             opcode: Opcode::Iconst,
1940             imm,
1941         } = inst_data
1942         {
1943             let ctrl_typevar = self.func.dfg.ctrl_typevar(inst);
1944             let bounds_mask = match ctrl_typevar {
1945                 types::I8 => u8::MAX.into(),
1946                 types::I16 => u16::MAX.into(),
1947                 types::I32 => u32::MAX.into(),
1948                 types::I64 => u64::MAX,
1949                 _ => unreachable!(),
1950             };
1951 
1952             let value = imm.bits() as u64;
1953             if value & bounds_mask != value {
1954                 errors.fatal((
1955                     inst,
1956                     self.context(inst),
1957                     "constant immediate is out of bounds",
1958                 ))
1959             } else {
1960                 Ok(())
1961             }
1962         } else {
1963             Ok(())
1964         }
1965     }
1966 
1967     fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1968         let params = self
1969             .func
1970             .signature
1971             .params
1972             .iter()
1973             .enumerate()
1974             .map(|p| (true, p));
1975         let returns = self
1976             .func
1977             .signature
1978             .returns
1979             .iter()
1980             .enumerate()
1981             .map(|p| (false, p));
1982 
1983         for (is_argument, (i, param)) in params.chain(returns) {
1984             let is_return = !is_argument;
1985             let item = if is_argument {
1986                 "Parameter"
1987             } else {
1988                 "Return value"
1989             };
1990 
1991             if param.value_type == types::INVALID {
1992                 errors.report((
1993                     AnyEntity::Function,
1994                     format!("{item} at position {i} has an invalid type"),
1995                 ));
1996             }
1997 
1998             if let ArgumentPurpose::StructArgument(_) = param.purpose {
1999                 if is_return {
2000                     errors.report((
2001                         AnyEntity::Function,
2002                         format!("{item} at position {i} can't be an struct argument"),
2003                     ))
2004                 }
2005             }
2006 
2007             let ty_allows_extension = param.value_type.is_int();
2008             let has_extension = param.extension != ArgumentExtension::None;
2009             if !ty_allows_extension && has_extension {
2010                 errors.report((
2011                     AnyEntity::Function,
2012                     format!(
2013                         "{} at position {} has invalid extension {:?}",
2014                         item, i, param.extension
2015                     ),
2016                 ));
2017             }
2018         }
2019 
2020         if errors.has_error() { Err(()) } else { Ok(()) }
2021     }
2022 
2023     fn verify_try_call_handler_index(
2024         &self,
2025         inst: Inst,
2026         block: Block,
2027         index_imm: i64,
2028         errors: &mut VerifierErrors,
2029     ) -> VerifierStepResult {
2030         if index_imm < 0 {
2031             return errors.fatal((
2032                 inst,
2033                 format!("exception handler index {index_imm} cannot be negative"),
2034             ));
2035         }
2036         let Ok(index) = usize::try_from(index_imm) else {
2037             return errors.fatal((
2038                 inst,
2039                 format!("exception handler index {index_imm} is out-of-range"),
2040             ));
2041         };
2042         let Some(terminator) = self.func.layout.last_inst(block) else {
2043             return errors.fatal((
2044                 inst,
2045                 format!("referenced block {block} does not have a terminator"),
2046             ));
2047         };
2048         let Some(et) = self.func.dfg.insts[terminator].exception_table() else {
2049             return errors.fatal((
2050                 inst,
2051                 format!("referenced block {block} does not end in a try_call"),
2052             ));
2053         };
2054 
2055         let etd = &self.func.dfg.exception_tables[et];
2056         // The exception table's out-edges consist of all exceptional
2057         // edges first, followed by the normal return last. For N
2058         // out-edges, there are N-1 exception handlers that can be
2059         // selected.
2060         let num_exceptional_edges = etd.all_branches().len() - 1;
2061         if index >= num_exceptional_edges {
2062             return errors.fatal((
2063                 inst,
2064                 format!("exception handler index {index_imm} is out-of-range"),
2065             ));
2066         }
2067 
2068         Ok(())
2069     }
2070 
2071     pub fn debug_tags(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
2072         // Tags can only be present on calls and sequence points.
2073         let op = self.func.dfg.insts[inst].opcode();
2074         let tags_allowed = op.is_call() || op == Opcode::SequencePoint;
2075         let has_tags = self.func.debug_tags.has(inst);
2076         if has_tags && !tags_allowed {
2077             return errors.fatal((
2078                 inst,
2079                 "debug tags present on non-call, non-sequence point instruction".to_string(),
2080             ));
2081         }
2082 
2083         Ok(())
2084     }
2085 
2086     fn verify_signature(
2087         &self,
2088         sig: &Signature,
2089         entity: impl Into<AnyEntity>,
2090         errors: &mut VerifierErrors,
2091     ) -> VerifierStepResult {
2092         match sig.call_conv {
2093             CallConv::PreserveAll => {
2094                 if !sig.returns.is_empty() {
2095                     errors.fatal((
2096                         entity,
2097                         "Signature with `preserve_all` ABI cannot have return values".to_string(),
2098                     ))?;
2099                 }
2100             }
2101             _ => {}
2102         }
2103         Ok(())
2104     }
2105 
2106     fn verify_signatures(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
2107         // Verify this function's own signature.
2108         self.verify_signature(&self.func.signature, AnyEntity::Function, errors)?;
2109         // Verify signatures referenced by any extfunc, using that
2110         // extfunc as the entity to which to attach the error.
2111         for (func, funcdata) in &self.func.dfg.ext_funcs {
2112             // Non-contiguous func entities result in placeholders
2113             // with invalid signatures; skip them.
2114             if !funcdata.signature.is_reserved_value() {
2115                 self.verify_signature(&self.func.dfg.signatures[funcdata.signature], func, errors)?;
2116             }
2117         }
2118         // Verify all signatures, including those only used by
2119         // e.g. indirect calls. Technically this re-verifies
2120         // signatures verified above but we want the first pass to
2121         // attach errors to funcrefs and we also need to verify all
2122         // defined signatures.
2123         for (sig, sigdata) in &self.func.dfg.signatures {
2124             self.verify_signature(sigdata, sig, errors)?;
2125         }
2126         Ok(())
2127     }
2128 
2129     pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
2130         self.verify_global_values(errors)?;
2131         self.verify_memory_types(errors)?;
2132         self.typecheck_entry_block_params(errors)?;
2133         self.check_entry_not_cold(errors)?;
2134         self.typecheck_function_signature(errors)?;
2135         self.verify_signatures(errors)?;
2136 
2137         for block in self.func.layout.blocks() {
2138             if self.func.layout.first_inst(block).is_none() {
2139                 return errors.fatal((block, format!("{block} cannot be empty")));
2140             }
2141             for inst in self.func.layout.block_insts(block) {
2142                 crate::trace!("verifying {inst:?}: {}", self.func.dfg.display_inst(inst));
2143                 self.block_integrity(block, inst, errors)?;
2144                 self.instruction_integrity(inst, errors)?;
2145                 self.typecheck(inst, errors)?;
2146                 self.immediate_constraints(inst, errors)?;
2147                 self.iconst_bounds(inst, errors)?;
2148                 self.debug_tags(inst, errors)?;
2149             }
2150 
2151             self.encodable_as_bb(block, errors)?;
2152         }
2153 
2154         if !errors.is_empty() {
2155             log::warn!(
2156                 "Found verifier errors in function:\n{}",
2157                 pretty_verifier_error(self.func, None, errors.clone())
2158             );
2159         }
2160 
2161         Ok(())
2162     }
2163 }
2164 
2165 #[cfg(test)]
2166 mod tests {
2167     use super::{Verifier, VerifierError, VerifierErrors};
2168     use crate::ir::instructions::{InstructionData, Opcode};
2169     use crate::ir::{AbiParam, Function, Type, types};
2170     use crate::settings;
2171 
2172     macro_rules! assert_err_with_msg {
2173         ($e:expr, $msg:expr) => {
2174             match $e.0.get(0) {
2175                 None => panic!("Expected an error"),
2176                 Some(&VerifierError { ref message, .. }) => {
2177                     if !message.contains($msg) {
2178                         #[cfg(feature = "std")]
2179                         panic!("'{}' did not contain the substring '{}'", message, $msg);
2180                         #[cfg(not(feature = "std"))]
2181                         panic!("error message did not contain the expected substring");
2182                     }
2183                 }
2184             }
2185         };
2186     }
2187 
2188     #[test]
2189     fn empty() {
2190         let func = Function::new();
2191         let flags = &settings::Flags::new(settings::builder());
2192         let verifier = Verifier::new(&func, flags.into());
2193         let mut errors = VerifierErrors::default();
2194 
2195         assert_eq!(verifier.run(&mut errors), Ok(()));
2196         assert!(errors.0.is_empty());
2197     }
2198 
2199     #[test]
2200     fn bad_instruction_format() {
2201         let mut func = Function::new();
2202         let block0 = func.dfg.make_block();
2203         func.layout.append_block(block0);
2204         let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
2205             opcode: Opcode::F32const,
2206             imm: 0.into(),
2207         });
2208         func.layout.append_inst(nullary_with_bad_opcode, block0);
2209         let destination = func.dfg.block_call(block0, &[]);
2210         func.stencil.layout.append_inst(
2211             func.stencil.dfg.make_inst(InstructionData::Jump {
2212                 opcode: Opcode::Jump,
2213                 destination,
2214             }),
2215             block0,
2216         );
2217         let flags = &settings::Flags::new(settings::builder());
2218         let verifier = Verifier::new(&func, flags.into());
2219         let mut errors = VerifierErrors::default();
2220 
2221         let _ = verifier.run(&mut errors);
2222 
2223         assert_err_with_msg!(errors, "instruction format");
2224     }
2225 
2226     fn test_iconst_bounds(immediate: i64, ctrl_typevar: Type) -> VerifierErrors {
2227         let mut func = Function::new();
2228         let block0 = func.dfg.make_block();
2229         func.layout.append_block(block0);
2230 
2231         let test_inst = func.dfg.make_inst(InstructionData::UnaryImm {
2232             opcode: Opcode::Iconst,
2233             imm: immediate.into(),
2234         });
2235 
2236         let end_inst = func.dfg.make_inst(InstructionData::MultiAry {
2237             opcode: Opcode::Return,
2238             args: Default::default(),
2239         });
2240 
2241         func.dfg.make_inst_results(test_inst, ctrl_typevar);
2242         func.layout.append_inst(test_inst, block0);
2243         func.layout.append_inst(end_inst, block0);
2244 
2245         let flags = &settings::Flags::new(settings::builder());
2246         let verifier = Verifier::new(&func, flags.into());
2247         let mut errors = VerifierErrors::default();
2248 
2249         let _ = verifier.run(&mut errors);
2250         errors
2251     }
2252 
2253     fn test_iconst_bounds_err(immediate: i64, ctrl_typevar: Type) {
2254         assert_err_with_msg!(
2255             test_iconst_bounds(immediate, ctrl_typevar),
2256             "constant immediate is out of bounds"
2257         );
2258     }
2259 
2260     fn test_iconst_bounds_ok(immediate: i64, ctrl_typevar: Type) {
2261         assert!(test_iconst_bounds(immediate, ctrl_typevar).is_empty());
2262     }
2263 
2264     #[test]
2265     fn negative_iconst_8() {
2266         test_iconst_bounds_err(-10, types::I8);
2267     }
2268 
2269     #[test]
2270     fn negative_iconst_32() {
2271         test_iconst_bounds_err(-1, types::I32);
2272     }
2273 
2274     #[test]
2275     fn large_iconst_8() {
2276         test_iconst_bounds_err(1 + u8::MAX as i64, types::I8);
2277     }
2278 
2279     #[test]
2280     fn large_iconst_16() {
2281         test_iconst_bounds_err(10 + u16::MAX as i64, types::I16);
2282     }
2283 
2284     #[test]
2285     fn valid_iconst_8() {
2286         test_iconst_bounds_ok(10, types::I8);
2287     }
2288 
2289     #[test]
2290     fn valid_iconst_32() {
2291         test_iconst_bounds_ok(u32::MAX as i64, types::I32);
2292     }
2293 
2294     #[test]
2295     fn test_function_invalid_param() {
2296         let mut func = Function::new();
2297         func.signature.params.push(AbiParam::new(types::INVALID));
2298 
2299         let mut errors = VerifierErrors::default();
2300         let flags = &settings::Flags::new(settings::builder());
2301         let verifier = Verifier::new(&func, flags.into());
2302 
2303         let _ = verifier.typecheck_function_signature(&mut errors);
2304         assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");
2305     }
2306 
2307     #[test]
2308     fn test_function_invalid_return_value() {
2309         let mut func = Function::new();
2310         func.signature.returns.push(AbiParam::new(types::INVALID));
2311 
2312         let mut errors = VerifierErrors::default();
2313         let flags = &settings::Flags::new(settings::builder());
2314         let verifier = Verifier::new(&func, flags.into());
2315 
2316         let _ = verifier.typecheck_function_signature(&mut errors);
2317         assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");
2318     }
2319 
2320     #[test]
2321     fn test_printing_contextual_errors() {
2322         // Build function.
2323         let mut func = Function::new();
2324         let block0 = func.dfg.make_block();
2325         func.layout.append_block(block0);
2326 
2327         // Build instruction "f64const 0.0" (missing one required result)
2328         let inst = func.dfg.make_inst(InstructionData::UnaryIeee64 {
2329             opcode: Opcode::F64const,
2330             imm: 0.0.into(),
2331         });
2332         func.layout.append_inst(inst, block0);
2333 
2334         // Setup verifier.
2335         let mut errors = VerifierErrors::default();
2336         let flags = &settings::Flags::new(settings::builder());
2337         let verifier = Verifier::new(&func, flags.into());
2338 
2339         // Now the error message, when printed, should contain the instruction sequence causing the
2340         // error (i.e. f64const 0.0) and not only its entity value (i.e. inst0)
2341         let _ = verifier.typecheck_results(inst, types::I32, &mut errors);
2342         assert_eq!(
2343             format!("{}", errors.0[0]),
2344             "inst0 (f64const 0.0): has fewer result values than expected"
2345         )
2346     }
2347 
2348     #[test]
2349     fn test_empty_block() {
2350         let mut func = Function::new();
2351         let block0 = func.dfg.make_block();
2352         func.layout.append_block(block0);
2353 
2354         let flags = &settings::Flags::new(settings::builder());
2355         let verifier = Verifier::new(&func, flags.into());
2356         let mut errors = VerifierErrors::default();
2357         let _ = verifier.run(&mut errors);
2358 
2359         assert_err_with_msg!(errors, "block0 cannot be empty");
2360     }
2361 }
2362