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