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