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