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