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