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