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