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