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