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