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