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