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