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