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