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