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 @ (Opcode::Vconst | Opcode::F128const),
675                 constant_handle,
676                 ..
677             } => {
678                 self.verify_constant_size(inst, opcode, 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             | UnaryIeee16 { .. }
690             | UnaryIeee32 { .. }
691             | UnaryIeee64 { .. }
692             | Binary { .. }
693             | BinaryImm8 { .. }
694             | BinaryImm64 { .. }
695             | Ternary { .. }
696             | TernaryImm8 { .. }
697             | Shuffle { .. }
698             | IntAddTrap { .. }
699             | IntCompare { .. }
700             | IntCompareImm { .. }
701             | FloatCompare { .. }
702             | Load { .. }
703             | Store { .. }
704             | Trap { .. }
705             | CondTrap { .. }
706             | NullAry { .. } => {}
707         }
708 
709         Ok(())
710     }
711 
712     fn verify_block(
713         &self,
714         loc: impl Into<AnyEntity>,
715         e: Block,
716         errors: &mut VerifierErrors,
717     ) -> VerifierStepResult {
718         if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {
719             return errors.fatal((loc, format!("invalid block reference {}", e)));
720         }
721         if let Some(entry_block) = self.func.layout.entry_block() {
722             if e == entry_block {
723                 return errors.fatal((loc, format!("invalid reference to entry block {}", e)));
724             }
725         }
726         Ok(())
727     }
728 
729     fn verify_sig_ref(
730         &self,
731         inst: Inst,
732         s: SigRef,
733         errors: &mut VerifierErrors,
734     ) -> VerifierStepResult {
735         if !self.func.dfg.signatures.is_valid(s) {
736             errors.fatal((
737                 inst,
738                 self.context(inst),
739                 format!("invalid signature reference {}", s),
740             ))
741         } else {
742             Ok(())
743         }
744     }
745 
746     fn verify_func_ref(
747         &self,
748         inst: Inst,
749         f: FuncRef,
750         errors: &mut VerifierErrors,
751     ) -> VerifierStepResult {
752         if !self.func.dfg.ext_funcs.is_valid(f) {
753             errors.nonfatal((
754                 inst,
755                 self.context(inst),
756                 format!("invalid function reference {}", f),
757             ))
758         } else {
759             Ok(())
760         }
761     }
762 
763     fn verify_stack_slot(
764         &self,
765         inst: Inst,
766         ss: StackSlot,
767         errors: &mut VerifierErrors,
768     ) -> VerifierStepResult {
769         if !self.func.sized_stack_slots.is_valid(ss) {
770             errors.nonfatal((
771                 inst,
772                 self.context(inst),
773                 format!("invalid stack slot {}", ss),
774             ))
775         } else {
776             Ok(())
777         }
778     }
779 
780     fn verify_dynamic_stack_slot(
781         &self,
782         inst: Inst,
783         ss: DynamicStackSlot,
784         errors: &mut VerifierErrors,
785     ) -> VerifierStepResult {
786         if !self.func.dynamic_stack_slots.is_valid(ss) {
787             errors.nonfatal((
788                 inst,
789                 self.context(inst),
790                 format!("invalid dynamic stack slot {}", ss),
791             ))
792         } else {
793             Ok(())
794         }
795     }
796 
797     fn verify_global_value(
798         &self,
799         inst: Inst,
800         gv: GlobalValue,
801         errors: &mut VerifierErrors,
802     ) -> VerifierStepResult {
803         if !self.func.global_values.is_valid(gv) {
804             errors.nonfatal((
805                 inst,
806                 self.context(inst),
807                 format!("invalid global value {}", gv),
808             ))
809         } else {
810             Ok(())
811         }
812     }
813 
814     fn verify_value_list(
815         &self,
816         inst: Inst,
817         l: &ValueList,
818         errors: &mut VerifierErrors,
819     ) -> VerifierStepResult {
820         if !l.is_valid(&self.func.dfg.value_lists) {
821             errors.nonfatal((
822                 inst,
823                 self.context(inst),
824                 format!("invalid value list reference {:?}", l),
825             ))
826         } else {
827             Ok(())
828         }
829     }
830 
831     fn verify_jump_table(
832         &self,
833         inst: Inst,
834         j: JumpTable,
835         errors: &mut VerifierErrors,
836     ) -> VerifierStepResult {
837         if !self.func.stencil.dfg.jump_tables.is_valid(j) {
838             errors.nonfatal((
839                 inst,
840                 self.context(inst),
841                 format!("invalid jump table reference {}", j),
842             ))
843         } else {
844             let pool = &self.func.stencil.dfg.value_lists;
845             for block in self.func.stencil.dfg.jump_tables[j].all_branches() {
846                 self.verify_block(inst, block.block(pool), errors)?;
847             }
848             Ok(())
849         }
850     }
851 
852     fn verify_value(
853         &self,
854         loc_inst: Inst,
855         v: Value,
856         errors: &mut VerifierErrors,
857     ) -> VerifierStepResult {
858         let dfg = &self.func.dfg;
859         if !dfg.value_is_valid(v) {
860             errors.nonfatal((
861                 loc_inst,
862                 self.context(loc_inst),
863                 format!("invalid value reference {}", v),
864             ))
865         } else {
866             Ok(())
867         }
868     }
869 
870     fn verify_inst_arg(
871         &self,
872         loc_inst: Inst,
873         v: Value,
874         errors: &mut VerifierErrors,
875     ) -> VerifierStepResult {
876         self.verify_value(loc_inst, v, errors)?;
877 
878         let dfg = &self.func.dfg;
879         let loc_block = self
880             .func
881             .layout
882             .inst_block(loc_inst)
883             .expect("Instruction not in layout.");
884         let is_reachable = self.expected_domtree.is_reachable(loc_block);
885 
886         // SSA form
887         match dfg.value_def(v) {
888             ValueDef::Result(def_inst, _) => {
889                 // Value is defined by an instruction that exists.
890                 if !dfg.inst_is_valid(def_inst) {
891                     return errors.fatal((
892                         loc_inst,
893                         self.context(loc_inst),
894                         format!("{} is defined by invalid instruction {}", v, def_inst),
895                     ));
896                 }
897                 // Defining instruction is inserted in a block.
898                 if self.func.layout.inst_block(def_inst) == None {
899                     return errors.fatal((
900                         loc_inst,
901                         self.context(loc_inst),
902                         format!("{} is defined by {} which has no block", v, def_inst),
903                     ));
904                 }
905                 // Defining instruction dominates the instruction that uses the value.
906                 if is_reachable {
907                     if !self
908                         .expected_domtree
909                         .dominates(def_inst, loc_inst, &self.func.layout)
910                     {
911                         return errors.fatal((
912                             loc_inst,
913                             self.context(loc_inst),
914                             format!("uses value {} from non-dominating {}", v, def_inst),
915                         ));
916                     }
917                     if def_inst == loc_inst {
918                         return errors.fatal((
919                             loc_inst,
920                             self.context(loc_inst),
921                             format!("uses value {} from itself", v),
922                         ));
923                     }
924                 }
925             }
926             ValueDef::Param(block, _) => {
927                 // Value is defined by an existing block.
928                 if !dfg.block_is_valid(block) {
929                     return errors.fatal((
930                         loc_inst,
931                         self.context(loc_inst),
932                         format!("{} is defined by invalid block {}", v, block),
933                     ));
934                 }
935                 // Defining block is inserted in the layout
936                 if !self.func.layout.is_block_inserted(block) {
937                     return errors.fatal((
938                         loc_inst,
939                         self.context(loc_inst),
940                         format!("{} is defined by {} which is not in the layout", v, block),
941                     ));
942                 }
943                 // The defining block dominates the instruction using this value.
944                 if is_reachable
945                     && !self
946                         .expected_domtree
947                         .dominates(block, loc_inst, &self.func.layout)
948                 {
949                     return errors.fatal((
950                         loc_inst,
951                         self.context(loc_inst),
952                         format!("uses value arg from non-dominating {}", block),
953                     ));
954                 }
955             }
956             ValueDef::Union(_, _) => {
957                 // Nothing: union nodes themselves have no location,
958                 // so we cannot check any dominance properties.
959             }
960         }
961         Ok(())
962     }
963 
964     fn verify_inst_result(
965         &self,
966         loc_inst: Inst,
967         v: Value,
968         errors: &mut VerifierErrors,
969     ) -> VerifierStepResult {
970         self.verify_value(loc_inst, v, errors)?;
971 
972         match self.func.dfg.value_def(v) {
973             ValueDef::Result(def_inst, _) => {
974                 if def_inst != loc_inst {
975                     errors.fatal((
976                         loc_inst,
977                         self.context(loc_inst),
978                         format!("instruction result {} is not defined by the instruction", v),
979                     ))
980                 } else {
981                     Ok(())
982                 }
983             }
984             ValueDef::Param(_, _) => errors.fatal((
985                 loc_inst,
986                 self.context(loc_inst),
987                 format!("instruction result {} is not defined by the instruction", v),
988             )),
989             ValueDef::Union(_, _) => errors.fatal((
990                 loc_inst,
991                 self.context(loc_inst),
992                 format!("instruction result {} is a union node", v),
993             )),
994         }
995     }
996 
997     fn verify_bitcast(
998         &self,
999         inst: Inst,
1000         flags: MemFlags,
1001         arg: Value,
1002         errors: &mut VerifierErrors,
1003     ) -> VerifierStepResult {
1004         let typ = self.func.dfg.ctrl_typevar(inst);
1005         let value_type = self.func.dfg.value_type(arg);
1006 
1007         if typ.bits() != value_type.bits() {
1008             errors.fatal((
1009                 inst,
1010                 format!(
1011                     "The bitcast argument {} has a type of {} bits, which doesn't match an expected type of {} bits",
1012                     arg,
1013                     value_type.bits(),
1014                     typ.bits()
1015                 ),
1016             ))
1017         } else if flags != MemFlags::new()
1018             && flags != MemFlags::new().with_endianness(ir::Endianness::Little)
1019             && flags != MemFlags::new().with_endianness(ir::Endianness::Big)
1020         {
1021             errors.fatal((
1022                 inst,
1023                 "The bitcast instruction only accepts the `big` or `little` memory flags",
1024             ))
1025         } else if flags == MemFlags::new() && typ.lane_count() != value_type.lane_count() {
1026             errors.fatal((
1027                 inst,
1028                 "Byte order specifier required for bitcast instruction changing lane count",
1029             ))
1030         } else {
1031             Ok(())
1032         }
1033     }
1034 
1035     fn verify_constant_size(
1036         &self,
1037         inst: Inst,
1038         opcode: Opcode,
1039         constant: Constant,
1040         errors: &mut VerifierErrors,
1041     ) -> VerifierStepResult {
1042         let type_size = match opcode {
1043             Opcode::F128const => types::F128.bytes(),
1044             Opcode::Vconst => self.func.dfg.ctrl_typevar(inst).bytes(),
1045             _ => unreachable!("unexpected opcode {opcode:?}"),
1046         } as usize;
1047         let constant_size = self.func.dfg.constants.get(constant).len();
1048         if type_size != constant_size {
1049             errors.fatal((
1050                 inst,
1051                 format!(
1052                     "The instruction expects {} to have a size of {} bytes but it has {}",
1053                     constant, type_size, constant_size
1054                 ),
1055             ))
1056         } else {
1057             Ok(())
1058         }
1059     }
1060 
1061     fn domtree_integrity(
1062         &self,
1063         domtree: &DominatorTree,
1064         errors: &mut VerifierErrors,
1065     ) -> VerifierStepResult {
1066         // We consider two `DominatorTree`s to be equal if they return the same immediate
1067         // dominator for each block. Therefore the current domtree is valid if it matches the freshly
1068         // computed one.
1069         for block in self.func.layout.blocks() {
1070             let expected = self.expected_domtree.idom(block);
1071             let got = domtree.idom(block);
1072             if got != expected {
1073                 return errors.fatal((
1074                     block,
1075                     format!(
1076                         "invalid domtree, expected idom({}) = {:?}, got {:?}",
1077                         block, expected, got
1078                     ),
1079                 ));
1080             }
1081         }
1082         // We also verify if the postorder defined by `DominatorTree` is sane
1083         if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {
1084             return errors.fatal((
1085                 AnyEntity::Function,
1086                 "incorrect number of Blocks in postorder traversal",
1087             ));
1088         }
1089         for (index, (&test_block, &true_block)) in domtree
1090             .cfg_postorder()
1091             .iter()
1092             .zip(self.expected_domtree.cfg_postorder().iter())
1093             .enumerate()
1094         {
1095             if test_block != true_block {
1096                 return errors.fatal((
1097                     test_block,
1098                     format!(
1099                         "invalid domtree, postorder block number {} should be {}, got {}",
1100                         index, true_block, test_block
1101                     ),
1102                 ));
1103             }
1104         }
1105         // We verify rpo_cmp_block on pairs of adjacent blocks in the postorder
1106         for (&prev_block, &next_block) in domtree.cfg_postorder().iter().adjacent_pairs() {
1107             if self.expected_domtree.rpo_cmp_block(prev_block, next_block) != Ordering::Greater {
1108                 return errors.fatal((
1109                     next_block,
1110                     format!(
1111                         "invalid domtree, rpo_cmp_block does not say {} is greater than {}; rpo = {:#?}",
1112                         prev_block, next_block, domtree.cfg_postorder()
1113                     ),
1114                 ));
1115             }
1116         }
1117         Ok(())
1118     }
1119 
1120     fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1121         if let Some(block) = self.func.layout.entry_block() {
1122             let expected_types = &self.func.signature.params;
1123             let block_param_count = self.func.dfg.num_block_params(block);
1124 
1125             if block_param_count != expected_types.len() {
1126                 return errors.fatal((
1127                     block,
1128                     format!(
1129                         "entry block parameters ({}) must match function signature ({})",
1130                         block_param_count,
1131                         expected_types.len()
1132                     ),
1133                 ));
1134             }
1135 
1136             for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {
1137                 let arg_type = self.func.dfg.value_type(arg);
1138                 if arg_type != expected_types[i].value_type {
1139                     errors.report((
1140                         block,
1141                         format!(
1142                             "entry block parameter {} expected to have type {}, got {}",
1143                             i, expected_types[i], arg_type
1144                         ),
1145                     ));
1146                 }
1147             }
1148         }
1149 
1150         errors.as_result()
1151     }
1152 
1153     fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1154         if let Some(entry_block) = self.func.layout.entry_block() {
1155             if self.func.layout.is_cold(entry_block) {
1156                 return errors
1157                     .fatal((entry_block, format!("entry block cannot be marked as cold")));
1158             }
1159         }
1160         errors.as_result()
1161     }
1162 
1163     fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1164         let inst_data = &self.func.dfg.insts[inst];
1165         let constraints = inst_data.opcode().constraints();
1166 
1167         let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
1168             // For polymorphic opcodes, determine the controlling type variable first.
1169             let ctrl_type = self.func.dfg.ctrl_typevar(inst);
1170 
1171             if !value_typeset.contains(ctrl_type) {
1172                 errors.report((
1173                     inst,
1174                     self.context(inst),
1175                     format!(
1176                         "has an invalid controlling type {} (allowed set is {:?})",
1177                         ctrl_type, value_typeset
1178                     ),
1179                 ));
1180             }
1181 
1182             ctrl_type
1183         } else {
1184             // Non-polymorphic instructions don't check the controlling type variable, so `Option`
1185             // is unnecessary and we can just make it `INVALID`.
1186             types::INVALID
1187         };
1188 
1189         // Typechecking instructions is never fatal
1190         let _ = self.typecheck_results(inst, ctrl_type, errors);
1191         let _ = self.typecheck_fixed_args(inst, ctrl_type, errors);
1192         let _ = self.typecheck_variable_args(inst, errors);
1193         let _ = self.typecheck_return(inst, errors);
1194         let _ = self.typecheck_special(inst, errors);
1195 
1196         Ok(())
1197     }
1198 
1199     fn typecheck_results(
1200         &self,
1201         inst: Inst,
1202         ctrl_type: Type,
1203         errors: &mut VerifierErrors,
1204     ) -> VerifierStepResult {
1205         let mut i = 0;
1206         for &result in self.func.dfg.inst_results(inst) {
1207             let result_type = self.func.dfg.value_type(result);
1208             let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type);
1209             if let Some(expected_type) = expected_type {
1210                 if result_type != expected_type {
1211                     errors.report((
1212                         inst,
1213                         self.context(inst),
1214                         format!(
1215                             "expected result {} ({}) to have type {}, found {}",
1216                             i, result, expected_type, result_type
1217                         ),
1218                     ));
1219                 }
1220             } else {
1221                 return errors.nonfatal((
1222                     inst,
1223                     self.context(inst),
1224                     "has more result values than expected",
1225                 ));
1226             }
1227             i += 1;
1228         }
1229 
1230         // There aren't any more result types left.
1231         if self.func.dfg.compute_result_type(inst, i, ctrl_type) != None {
1232             return errors.nonfatal((
1233                 inst,
1234                 self.context(inst),
1235                 "has fewer result values than expected",
1236             ));
1237         }
1238         Ok(())
1239     }
1240 
1241     fn typecheck_fixed_args(
1242         &self,
1243         inst: Inst,
1244         ctrl_type: Type,
1245         errors: &mut VerifierErrors,
1246     ) -> VerifierStepResult {
1247         let constraints = self.func.dfg.insts[inst].opcode().constraints();
1248 
1249         for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {
1250             let arg_type = self.func.dfg.value_type(arg);
1251             match constraints.value_argument_constraint(i, ctrl_type) {
1252                 ResolvedConstraint::Bound(expected_type) => {
1253                     if arg_type != expected_type {
1254                         errors.report((
1255                             inst,
1256                             self.context(inst),
1257                             format!(
1258                                 "arg {} ({}) has type {}, expected {}",
1259                                 i, arg, arg_type, expected_type
1260                             ),
1261                         ));
1262                     }
1263                 }
1264                 ResolvedConstraint::Free(type_set) => {
1265                     if !type_set.contains(arg_type) {
1266                         errors.report((
1267                             inst,
1268                             self.context(inst),
1269                             format!(
1270                                 "arg {} ({}) with type {} failed to satisfy type set {:?}",
1271                                 i, arg, arg_type, type_set
1272                             ),
1273                         ));
1274                     }
1275                 }
1276             }
1277         }
1278         Ok(())
1279     }
1280 
1281     /// Typecheck both instructions that contain variable arguments like calls, and those that
1282     /// include references to basic blocks with their arguments.
1283     fn typecheck_variable_args(
1284         &self,
1285         inst: Inst,
1286         errors: &mut VerifierErrors,
1287     ) -> VerifierStepResult {
1288         match &self.func.dfg.insts[inst] {
1289             ir::InstructionData::Jump { destination, .. } => {
1290                 self.typecheck_block_call(inst, destination, errors)?;
1291             }
1292             ir::InstructionData::Brif {
1293                 blocks: [block_then, block_else],
1294                 ..
1295             } => {
1296                 self.typecheck_block_call(inst, block_then, errors)?;
1297                 self.typecheck_block_call(inst, block_else, errors)?;
1298             }
1299             ir::InstructionData::BranchTable { table, .. } => {
1300                 for block in self.func.stencil.dfg.jump_tables[*table].all_branches() {
1301                     self.typecheck_block_call(inst, block, errors)?;
1302                 }
1303             }
1304             inst => debug_assert!(!inst.opcode().is_branch()),
1305         }
1306 
1307         match self.func.dfg.insts[inst].analyze_call(&self.func.dfg.value_lists) {
1308             CallInfo::Direct(func_ref, args) => {
1309                 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1310                 let arg_types = self.func.dfg.signatures[sig_ref]
1311                     .params
1312                     .iter()
1313                     .map(|a| a.value_type);
1314                 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1315             }
1316             CallInfo::Indirect(sig_ref, args) => {
1317                 let arg_types = self.func.dfg.signatures[sig_ref]
1318                     .params
1319                     .iter()
1320                     .map(|a| a.value_type);
1321                 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1322             }
1323             CallInfo::NotACall => {}
1324         }
1325         Ok(())
1326     }
1327 
1328     fn typecheck_block_call(
1329         &self,
1330         inst: Inst,
1331         block: &ir::BlockCall,
1332         errors: &mut VerifierErrors,
1333     ) -> VerifierStepResult {
1334         let pool = &self.func.dfg.value_lists;
1335         let iter = self
1336             .func
1337             .dfg
1338             .block_params(block.block(pool))
1339             .iter()
1340             .map(|&v| self.func.dfg.value_type(v));
1341         let args = block.args_slice(pool);
1342         self.typecheck_variable_args_iterator(inst, iter, args, errors)
1343     }
1344 
1345     fn typecheck_variable_args_iterator<I: Iterator<Item = Type>>(
1346         &self,
1347         inst: Inst,
1348         iter: I,
1349         variable_args: &[Value],
1350         errors: &mut VerifierErrors,
1351     ) -> VerifierStepResult {
1352         let mut i = 0;
1353 
1354         for expected_type in iter {
1355             if i >= variable_args.len() {
1356                 // Result count mismatch handled below, we want the full argument count first though
1357                 i += 1;
1358                 continue;
1359             }
1360             let arg = variable_args[i];
1361             let arg_type = self.func.dfg.value_type(arg);
1362             if expected_type != arg_type {
1363                 errors.report((
1364                     inst,
1365                     self.context(inst),
1366                     format!(
1367                         "arg {} ({}) has type {}, expected {}",
1368                         i, variable_args[i], arg_type, expected_type
1369                     ),
1370                 ));
1371             }
1372             i += 1;
1373         }
1374         if i != variable_args.len() {
1375             return errors.nonfatal((
1376                 inst,
1377                 self.context(inst),
1378                 format!(
1379                     "mismatched argument count for `{}`: got {}, expected {}",
1380                     self.func.dfg.display_inst(inst),
1381                     variable_args.len(),
1382                     i,
1383                 ),
1384             ));
1385         }
1386         Ok(())
1387     }
1388 
1389     fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1390         match self.func.dfg.insts[inst] {
1391             ir::InstructionData::MultiAry {
1392                 opcode: Opcode::Return,
1393                 args,
1394             } => {
1395                 let types = args
1396                     .as_slice(&self.func.dfg.value_lists)
1397                     .iter()
1398                     .map(|v| self.func.dfg.value_type(*v));
1399                 self.typecheck_return_types(
1400                     inst,
1401                     types,
1402                     errors,
1403                     "arguments of return must match function signature",
1404                 )?;
1405             }
1406             ir::InstructionData::Call {
1407                 opcode: Opcode::ReturnCall,
1408                 func_ref,
1409                 ..
1410             } => {
1411                 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1412                 self.typecheck_tail_call(inst, sig_ref, errors)?;
1413             }
1414             ir::InstructionData::CallIndirect {
1415                 opcode: Opcode::ReturnCallIndirect,
1416                 sig_ref,
1417                 ..
1418             } => {
1419                 self.typecheck_tail_call(inst, sig_ref, errors)?;
1420             }
1421             inst => debug_assert!(!inst.opcode().is_return()),
1422         }
1423         Ok(())
1424     }
1425 
1426     fn typecheck_tail_call(
1427         &self,
1428         inst: Inst,
1429         sig_ref: SigRef,
1430         errors: &mut VerifierErrors,
1431     ) -> VerifierStepResult {
1432         let signature = &self.func.dfg.signatures[sig_ref];
1433         let cc = signature.call_conv;
1434         if !cc.supports_tail_calls() {
1435             errors.report((
1436                 inst,
1437                 self.context(inst),
1438                 format!("calling convention `{cc}` does not support tail calls"),
1439             ));
1440         }
1441         if cc != self.func.signature.call_conv {
1442             errors.report((
1443                 inst,
1444                 self.context(inst),
1445                 "callee's calling convention must match caller",
1446             ));
1447         }
1448         let types = signature.returns.iter().map(|param| param.value_type);
1449         self.typecheck_return_types(inst, types, errors, "results of callee must match caller")?;
1450         Ok(())
1451     }
1452 
1453     fn typecheck_return_types(
1454         &self,
1455         inst: Inst,
1456         actual_types: impl ExactSizeIterator<Item = Type>,
1457         errors: &mut VerifierErrors,
1458         message: &str,
1459     ) -> VerifierStepResult {
1460         let expected_types = &self.func.signature.returns;
1461         if actual_types.len() != expected_types.len() {
1462             return errors.nonfatal((inst, self.context(inst), message));
1463         }
1464         for (i, (actual_type, &expected_type)) in actual_types.zip(expected_types).enumerate() {
1465             if actual_type != expected_type.value_type {
1466                 errors.report((
1467                     inst,
1468                     self.context(inst),
1469                     format!(
1470                         "result {i} has type {actual_type}, must match function signature of \
1471                          {expected_type}"
1472                     ),
1473                 ));
1474             }
1475         }
1476         Ok(())
1477     }
1478 
1479     // Check special-purpose type constraints that can't be expressed in the normal opcode
1480     // constraints.
1481     fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1482         match self.func.dfg.insts[inst] {
1483             ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
1484                 if let Some(isa) = self.isa {
1485                     let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
1486                     let global_type = self.func.global_values[global_value].global_type(isa);
1487                     if inst_type != global_type {
1488                         return errors.nonfatal((
1489                             inst, self.context(inst),
1490                             format!(
1491                                 "global_value instruction with type {} references global value with type {}",
1492                                 inst_type, global_type
1493                             )),
1494                         );
1495                     }
1496                 }
1497             }
1498             _ => {}
1499         }
1500         Ok(())
1501     }
1502 
1503     fn cfg_integrity(
1504         &self,
1505         cfg: &ControlFlowGraph,
1506         errors: &mut VerifierErrors,
1507     ) -> VerifierStepResult {
1508         let mut expected_succs = BTreeSet::<Block>::new();
1509         let mut got_succs = BTreeSet::<Block>::new();
1510         let mut expected_preds = BTreeSet::<Inst>::new();
1511         let mut got_preds = BTreeSet::<Inst>::new();
1512 
1513         for block in self.func.layout.blocks() {
1514             expected_succs.extend(self.expected_cfg.succ_iter(block));
1515             got_succs.extend(cfg.succ_iter(block));
1516 
1517             let missing_succs: Vec<Block> =
1518                 expected_succs.difference(&got_succs).cloned().collect();
1519             if !missing_succs.is_empty() {
1520                 errors.report((
1521                     block,
1522                     format!("cfg lacked the following successor(s) {:?}", missing_succs),
1523                 ));
1524                 continue;
1525             }
1526 
1527             let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
1528             if !excess_succs.is_empty() {
1529                 errors.report((
1530                     block,
1531                     format!("cfg had unexpected successor(s) {:?}", excess_succs),
1532                 ));
1533                 continue;
1534             }
1535 
1536             expected_preds.extend(
1537                 self.expected_cfg
1538                     .pred_iter(block)
1539                     .map(|BlockPredecessor { inst, .. }| inst),
1540             );
1541             got_preds.extend(
1542                 cfg.pred_iter(block)
1543                     .map(|BlockPredecessor { inst, .. }| inst),
1544             );
1545 
1546             let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
1547             if !missing_preds.is_empty() {
1548                 errors.report((
1549                     block,
1550                     format!(
1551                         "cfg lacked the following predecessor(s) {:?}",
1552                         missing_preds
1553                     ),
1554                 ));
1555                 continue;
1556             }
1557 
1558             let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
1559             if !excess_preds.is_empty() {
1560                 errors.report((
1561                     block,
1562                     format!("cfg had unexpected predecessor(s) {:?}", excess_preds),
1563                 ));
1564                 continue;
1565             }
1566 
1567             expected_succs.clear();
1568             got_succs.clear();
1569             expected_preds.clear();
1570             got_preds.clear();
1571         }
1572         errors.as_result()
1573     }
1574 
1575     fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1576         let inst_data = &self.func.dfg.insts[inst];
1577 
1578         match *inst_data {
1579             ir::InstructionData::Store { flags, .. } => {
1580                 if flags.readonly() {
1581                     errors.fatal((
1582                         inst,
1583                         self.context(inst),
1584                         "A store instruction cannot have the `readonly` MemFlag",
1585                     ))
1586                 } else {
1587                     Ok(())
1588                 }
1589             }
1590             ir::InstructionData::BinaryImm8 {
1591                 opcode: ir::instructions::Opcode::Extractlane,
1592                 imm: lane,
1593                 arg,
1594                 ..
1595             }
1596             | ir::InstructionData::TernaryImm8 {
1597                 opcode: ir::instructions::Opcode::Insertlane,
1598                 imm: lane,
1599                 args: [arg, _],
1600                 ..
1601             } => {
1602                 // We must be specific about the opcodes above because other instructions are using
1603                 // the same formats.
1604                 let ty = self.func.dfg.value_type(arg);
1605                 if lane as u32 >= ty.lane_count() {
1606                     errors.fatal((
1607                         inst,
1608                         self.context(inst),
1609                         format!("The lane {} does not index into the type {}", lane, ty,),
1610                     ))
1611                 } else {
1612                     Ok(())
1613                 }
1614             }
1615             ir::InstructionData::Shuffle {
1616                 opcode: ir::instructions::Opcode::Shuffle,
1617                 imm,
1618                 ..
1619             } => {
1620                 let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
1621                 if imm.len() != 16 {
1622                     errors.fatal((
1623                         inst,
1624                         self.context(inst),
1625                         format!("the shuffle immediate wasn't 16-bytes long"),
1626                     ))
1627                 } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
1628                     errors.fatal((
1629                         inst,
1630                         self.context(inst),
1631                         format!("shuffle immediate index {i} is larger than the maximum 31"),
1632                     ))
1633                 } else {
1634                     Ok(())
1635                 }
1636             }
1637             _ => Ok(()),
1638         }
1639     }
1640 
1641     fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1642         use crate::ir::instructions::InstructionData::UnaryImm;
1643 
1644         let inst_data = &self.func.dfg.insts[inst];
1645         if let UnaryImm {
1646             opcode: Opcode::Iconst,
1647             imm,
1648         } = inst_data
1649         {
1650             let ctrl_typevar = self.func.dfg.ctrl_typevar(inst);
1651             let bounds_mask = match ctrl_typevar {
1652                 types::I8 => u8::MAX.into(),
1653                 types::I16 => u16::MAX.into(),
1654                 types::I32 => u32::MAX.into(),
1655                 types::I64 => u64::MAX,
1656                 _ => unreachable!(),
1657             };
1658 
1659             let value = imm.bits() as u64;
1660             if value & bounds_mask != value {
1661                 errors.fatal((
1662                     inst,
1663                     self.context(inst),
1664                     "constant immediate is out of bounds",
1665                 ))
1666             } else {
1667                 Ok(())
1668             }
1669         } else {
1670             Ok(())
1671         }
1672     }
1673 
1674     fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1675         let params = self
1676             .func
1677             .signature
1678             .params
1679             .iter()
1680             .enumerate()
1681             .map(|p| (true, p));
1682         let returns = self
1683             .func
1684             .signature
1685             .returns
1686             .iter()
1687             .enumerate()
1688             .map(|p| (false, p));
1689 
1690         for (is_argument, (i, param)) in params.chain(returns) {
1691             let is_return = !is_argument;
1692             let item = if is_argument {
1693                 "Parameter"
1694             } else {
1695                 "Return value"
1696             };
1697 
1698             if param.value_type == types::INVALID {
1699                 errors.report((
1700                     AnyEntity::Function,
1701                     format!("{item} at position {i} has an invalid type"),
1702                 ));
1703             }
1704 
1705             if let ArgumentPurpose::StructArgument(_) = param.purpose {
1706                 if is_return {
1707                     errors.report((
1708                         AnyEntity::Function,
1709                         format!("{item} at position {i} can't be an struct argument"),
1710                     ))
1711                 }
1712             }
1713 
1714             let ty_allows_extension = param.value_type.is_int();
1715             let has_extension = param.extension != ArgumentExtension::None;
1716             if !ty_allows_extension && has_extension {
1717                 errors.report((
1718                     AnyEntity::Function,
1719                     format!(
1720                         "{} at position {} has invalid extension {:?}",
1721                         item, i, param.extension
1722                     ),
1723                 ));
1724             }
1725         }
1726 
1727         if errors.has_error() {
1728             Err(())
1729         } else {
1730             Ok(())
1731         }
1732     }
1733 
1734     pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1735         self.verify_global_values(errors)?;
1736         self.verify_memory_types(errors)?;
1737         self.typecheck_entry_block_params(errors)?;
1738         self.check_entry_not_cold(errors)?;
1739         self.typecheck_function_signature(errors)?;
1740 
1741         for block in self.func.layout.blocks() {
1742             if self.func.layout.first_inst(block).is_none() {
1743                 return errors.fatal((block, format!("{} cannot be empty", block)));
1744             }
1745             for inst in self.func.layout.block_insts(block) {
1746                 self.block_integrity(block, inst, errors)?;
1747                 self.instruction_integrity(inst, errors)?;
1748                 self.typecheck(inst, errors)?;
1749                 self.immediate_constraints(inst, errors)?;
1750                 self.iconst_bounds(inst, errors)?;
1751             }
1752 
1753             self.encodable_as_bb(block, errors)?;
1754         }
1755 
1756         if !errors.is_empty() {
1757             log::warn!(
1758                 "Found verifier errors in function:\n{}",
1759                 pretty_verifier_error(self.func, None, errors.clone())
1760             );
1761         }
1762 
1763         Ok(())
1764     }
1765 }
1766 
1767 #[cfg(test)]
1768 mod tests {
1769     use super::{Verifier, VerifierError, VerifierErrors};
1770     use crate::ir::instructions::{InstructionData, Opcode};
1771     use crate::ir::{types, AbiParam, Function, Type};
1772     use crate::settings;
1773 
1774     macro_rules! assert_err_with_msg {
1775         ($e:expr, $msg:expr) => {
1776             match $e.0.get(0) {
1777                 None => panic!("Expected an error"),
1778                 Some(&VerifierError { ref message, .. }) => {
1779                     if !message.contains($msg) {
1780                         #[cfg(feature = "std")]
1781                         panic!("'{}' did not contain the substring '{}'", message, $msg);
1782                         #[cfg(not(feature = "std"))]
1783                         panic!("error message did not contain the expected substring");
1784                     }
1785                 }
1786             }
1787         };
1788     }
1789 
1790     #[test]
1791     fn empty() {
1792         let func = Function::new();
1793         let flags = &settings::Flags::new(settings::builder());
1794         let verifier = Verifier::new(&func, flags.into());
1795         let mut errors = VerifierErrors::default();
1796 
1797         assert_eq!(verifier.run(&mut errors), Ok(()));
1798         assert!(errors.0.is_empty());
1799     }
1800 
1801     #[test]
1802     fn bad_instruction_format() {
1803         let mut func = Function::new();
1804         let block0 = func.dfg.make_block();
1805         func.layout.append_block(block0);
1806         let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
1807             opcode: Opcode::F32const,
1808             imm: 0.into(),
1809         });
1810         func.layout.append_inst(nullary_with_bad_opcode, block0);
1811         let destination = func.dfg.block_call(block0, &[]);
1812         func.stencil.layout.append_inst(
1813             func.stencil.dfg.make_inst(InstructionData::Jump {
1814                 opcode: Opcode::Jump,
1815                 destination,
1816             }),
1817             block0,
1818         );
1819         let flags = &settings::Flags::new(settings::builder());
1820         let verifier = Verifier::new(&func, flags.into());
1821         let mut errors = VerifierErrors::default();
1822 
1823         let _ = verifier.run(&mut errors);
1824 
1825         assert_err_with_msg!(errors, "instruction format");
1826     }
1827 
1828     fn test_iconst_bounds(immediate: i64, ctrl_typevar: Type) -> VerifierErrors {
1829         let mut func = Function::new();
1830         let block0 = func.dfg.make_block();
1831         func.layout.append_block(block0);
1832 
1833         let test_inst = func.dfg.make_inst(InstructionData::UnaryImm {
1834             opcode: Opcode::Iconst,
1835             imm: immediate.into(),
1836         });
1837 
1838         let end_inst = func.dfg.make_inst(InstructionData::MultiAry {
1839             opcode: Opcode::Return,
1840             args: Default::default(),
1841         });
1842 
1843         func.dfg.make_inst_results(test_inst, ctrl_typevar);
1844         func.layout.append_inst(test_inst, block0);
1845         func.layout.append_inst(end_inst, block0);
1846 
1847         let flags = &settings::Flags::new(settings::builder());
1848         let verifier = Verifier::new(&func, flags.into());
1849         let mut errors = VerifierErrors::default();
1850 
1851         let _ = verifier.run(&mut errors);
1852         errors
1853     }
1854 
1855     fn test_iconst_bounds_err(immediate: i64, ctrl_typevar: Type) {
1856         assert_err_with_msg!(
1857             test_iconst_bounds(immediate, ctrl_typevar),
1858             "constant immediate is out of bounds"
1859         );
1860     }
1861 
1862     fn test_iconst_bounds_ok(immediate: i64, ctrl_typevar: Type) {
1863         assert!(test_iconst_bounds(immediate, ctrl_typevar).is_empty());
1864     }
1865 
1866     #[test]
1867     fn negative_iconst_8() {
1868         test_iconst_bounds_err(-10, types::I8);
1869     }
1870 
1871     #[test]
1872     fn negative_iconst_32() {
1873         test_iconst_bounds_err(-1, types::I32);
1874     }
1875 
1876     #[test]
1877     fn large_iconst_8() {
1878         test_iconst_bounds_err(1 + u8::MAX as i64, types::I8);
1879     }
1880 
1881     #[test]
1882     fn large_iconst_16() {
1883         test_iconst_bounds_err(10 + u16::MAX as i64, types::I16);
1884     }
1885 
1886     #[test]
1887     fn valid_iconst_8() {
1888         test_iconst_bounds_ok(10, types::I8);
1889     }
1890 
1891     #[test]
1892     fn valid_iconst_32() {
1893         test_iconst_bounds_ok(u32::MAX as i64, types::I32);
1894     }
1895 
1896     #[test]
1897     fn test_function_invalid_param() {
1898         let mut func = Function::new();
1899         func.signature.params.push(AbiParam::new(types::INVALID));
1900 
1901         let mut errors = VerifierErrors::default();
1902         let flags = &settings::Flags::new(settings::builder());
1903         let verifier = Verifier::new(&func, flags.into());
1904 
1905         let _ = verifier.typecheck_function_signature(&mut errors);
1906         assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");
1907     }
1908 
1909     #[test]
1910     fn test_function_invalid_return_value() {
1911         let mut func = Function::new();
1912         func.signature.returns.push(AbiParam::new(types::INVALID));
1913 
1914         let mut errors = VerifierErrors::default();
1915         let flags = &settings::Flags::new(settings::builder());
1916         let verifier = Verifier::new(&func, flags.into());
1917 
1918         let _ = verifier.typecheck_function_signature(&mut errors);
1919         assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");
1920     }
1921 
1922     #[test]
1923     fn test_printing_contextual_errors() {
1924         // Build function.
1925         let mut func = Function::new();
1926         let block0 = func.dfg.make_block();
1927         func.layout.append_block(block0);
1928 
1929         // Build instruction "f64const 0.0" (missing one required result)
1930         let inst = func.dfg.make_inst(InstructionData::UnaryIeee64 {
1931             opcode: Opcode::F64const,
1932             imm: 0.into(),
1933         });
1934         func.layout.append_inst(inst, block0);
1935 
1936         // Setup verifier.
1937         let mut errors = VerifierErrors::default();
1938         let flags = &settings::Flags::new(settings::builder());
1939         let verifier = Verifier::new(&func, flags.into());
1940 
1941         // Now the error message, when printed, should contain the instruction sequence causing the
1942         // error (i.e. f64const 0.0) and not only its entity value (i.e. inst0)
1943         let _ = verifier.typecheck_results(inst, types::I32, &mut errors);
1944         assert_eq!(
1945             format!("{}", errors.0[0]),
1946             "inst0 (f64const 0.0): has fewer result values than expected"
1947         )
1948     }
1949 
1950     #[test]
1951     fn test_empty_block() {
1952         let mut func = Function::new();
1953         let block0 = func.dfg.make_block();
1954         func.layout.append_block(block0);
1955 
1956         let flags = &settings::Flags::new(settings::builder());
1957         let verifier = Verifier::new(&func, flags.into());
1958         let mut errors = VerifierErrors::default();
1959         let _ = verifier.run(&mut errors);
1960 
1961         assert_err_with_msg!(errors, "block0 cannot be empty");
1962     }
1963 }
1964