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