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