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