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