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