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