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