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