1 // Copyright (c) 2021 The Vulkano developers 2 // Licensed under the Apache License, Version 2.0 3 // <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT 5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, 6 // at your option. All files in the project carrying such 7 // notice may not be copied, modified, or distributed except 8 // according to those terms. 9 10 //! Parsing and analysis utilities for SPIR-V shader binaries. 11 //! 12 //! This can be used to inspect and validate a SPIR-V module at runtime. The `Spirv` type does some 13 //! validation, but you should not assume that code that is read successfully is valid. 14 //! 15 //! For more information about SPIR-V modules, instructions and types, see the 16 //! [SPIR-V specification](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html). 17 18 use crate::Version; 19 use ahash::{HashMap, HashMapExt}; 20 use std::{ 21 error::Error, 22 fmt::{Display, Error as FmtError, Formatter}, 23 ops::Range, 24 string::FromUtf8Error, 25 }; 26 27 // Generated by build.rs 28 include!(concat!(env!("OUT_DIR"), "/spirv_parse.rs")); 29 30 /// A parsed and analyzed SPIR-V module. 31 #[derive(Clone, Debug)] 32 pub struct Spirv { 33 version: Version, 34 bound: u32, 35 instructions: Vec<Instruction>, 36 ids: HashMap<Id, IdDataIndices>, 37 38 // Items described in the spec section "Logical Layout of a Module" 39 range_capability: Range<usize>, 40 range_extension: Range<usize>, 41 range_ext_inst_import: Range<usize>, 42 memory_model: usize, 43 range_entry_point: Range<usize>, 44 range_execution_mode: Range<usize>, 45 range_name: Range<usize>, 46 range_decoration: Range<usize>, 47 range_global: Range<usize>, 48 } 49 50 impl Spirv { 51 /// Parses a SPIR-V document from a list of words. new(words: &[u32]) -> Result<Spirv, SpirvError>52 pub fn new(words: &[u32]) -> Result<Spirv, SpirvError> { 53 if words.len() < 5 { 54 return Err(SpirvError::InvalidHeader); 55 } 56 57 if words[0] != 0x07230203 { 58 return Err(SpirvError::InvalidHeader); 59 } 60 61 let version = Version { 62 major: (words[1] & 0x00ff0000) >> 16, 63 minor: (words[1] & 0x0000ff00) >> 8, 64 patch: words[1] & 0x000000ff, 65 }; 66 67 let bound = words[3]; 68 69 let instructions = { 70 let mut ret = Vec::new(); 71 let mut rest = &words[5..]; 72 while !rest.is_empty() { 73 let word_count = (rest[0] >> 16) as usize; 74 assert!(word_count >= 1); 75 76 if rest.len() < word_count { 77 return Err(ParseError { 78 instruction: ret.len(), 79 word: rest.len(), 80 error: ParseErrors::UnexpectedEOF, 81 words: rest.to_owned(), 82 } 83 .into()); 84 } 85 86 let mut reader = InstructionReader::new(&rest[0..word_count], ret.len()); 87 let instruction = Instruction::parse(&mut reader)?; 88 89 if !reader.is_empty() { 90 return Err(reader.map_err(ParseErrors::LeftoverOperands).into()); 91 } 92 93 ret.push(instruction); 94 rest = &rest[word_count..]; 95 } 96 ret 97 }; 98 99 // It is impossible for a valid SPIR-V file to contain more Ids than instructions, so put 100 // a sane upper limit on the allocation. This prevents a malicious file from causing huge 101 // memory allocations. 102 let mut ids = HashMap::with_capacity(instructions.len().min(bound as usize)); 103 let mut range_capability: Option<Range<usize>> = None; 104 let mut range_extension: Option<Range<usize>> = None; 105 let mut range_ext_inst_import: Option<Range<usize>> = None; 106 let mut range_memory_model: Option<Range<usize>> = None; 107 let mut range_entry_point: Option<Range<usize>> = None; 108 let mut range_execution_mode: Option<Range<usize>> = None; 109 let mut range_name: Option<Range<usize>> = None; 110 let mut range_decoration: Option<Range<usize>> = None; 111 let mut range_global: Option<Range<usize>> = None; 112 let mut in_function = false; 113 114 fn set_range(range: &mut Option<Range<usize>>, index: usize) -> Result<(), SpirvError> { 115 if let Some(range) = range { 116 if range.end != index { 117 return Err(SpirvError::BadLayout { index }); 118 } 119 120 range.end = index + 1; 121 } else { 122 *range = Some(Range { 123 start: index, 124 end: index + 1, 125 }); 126 } 127 128 Ok(()) 129 } 130 131 for (index, instruction) in instructions.iter().enumerate() { 132 if let Some(id) = instruction.result_id() { 133 if u32::from(id) >= bound { 134 return Err(SpirvError::IdOutOfBounds { id, index, bound }); 135 } 136 137 let members = if let Instruction::TypeStruct { member_types, .. } = instruction { 138 member_types 139 .iter() 140 .map(|_| StructMemberDataIndices::default()) 141 .collect() 142 } else { 143 Vec::new() 144 }; 145 let data = IdDataIndices { 146 index, 147 names: Vec::new(), 148 decorations: Vec::new(), 149 members, 150 }; 151 if let Some(first) = ids.insert(id, data) { 152 return Err(SpirvError::DuplicateId { 153 id, 154 first_index: first.index, 155 second_index: index, 156 }); 157 } 158 } 159 160 match instruction { 161 Instruction::Capability { .. } => set_range(&mut range_capability, index)?, 162 Instruction::Extension { .. } => set_range(&mut range_extension, index)?, 163 Instruction::ExtInstImport { .. } => set_range(&mut range_ext_inst_import, index)?, 164 Instruction::MemoryModel { .. } => set_range(&mut range_memory_model, index)?, 165 Instruction::EntryPoint { .. } => set_range(&mut range_entry_point, index)?, 166 Instruction::ExecutionMode { .. } | Instruction::ExecutionModeId { .. } => { 167 set_range(&mut range_execution_mode, index)? 168 } 169 Instruction::Name { .. } | Instruction::MemberName { .. } => { 170 set_range(&mut range_name, index)? 171 } 172 Instruction::Decorate { .. } 173 | Instruction::MemberDecorate { .. } 174 | Instruction::DecorationGroup { .. } 175 | Instruction::GroupDecorate { .. } 176 | Instruction::GroupMemberDecorate { .. } 177 | Instruction::DecorateId { .. } 178 | Instruction::DecorateString { .. } 179 | Instruction::MemberDecorateString { .. } => { 180 set_range(&mut range_decoration, index)? 181 } 182 Instruction::TypeVoid { .. } 183 | Instruction::TypeBool { .. } 184 | Instruction::TypeInt { .. } 185 | Instruction::TypeFloat { .. } 186 | Instruction::TypeVector { .. } 187 | Instruction::TypeMatrix { .. } 188 | Instruction::TypeImage { .. } 189 | Instruction::TypeSampler { .. } 190 | Instruction::TypeSampledImage { .. } 191 | Instruction::TypeArray { .. } 192 | Instruction::TypeRuntimeArray { .. } 193 | Instruction::TypeStruct { .. } 194 | Instruction::TypeOpaque { .. } 195 | Instruction::TypePointer { .. } 196 | Instruction::TypeFunction { .. } 197 | Instruction::TypeEvent { .. } 198 | Instruction::TypeDeviceEvent { .. } 199 | Instruction::TypeReserveId { .. } 200 | Instruction::TypeQueue { .. } 201 | Instruction::TypePipe { .. } 202 | Instruction::TypeForwardPointer { .. } 203 | Instruction::TypePipeStorage { .. } 204 | Instruction::TypeNamedBarrier { .. } 205 | Instruction::TypeRayQueryKHR { .. } 206 | Instruction::TypeAccelerationStructureKHR { .. } 207 | Instruction::TypeCooperativeMatrixNV { .. } 208 | Instruction::TypeVmeImageINTEL { .. } 209 | Instruction::TypeAvcImePayloadINTEL { .. } 210 | Instruction::TypeAvcRefPayloadINTEL { .. } 211 | Instruction::TypeAvcSicPayloadINTEL { .. } 212 | Instruction::TypeAvcMcePayloadINTEL { .. } 213 | Instruction::TypeAvcMceResultINTEL { .. } 214 | Instruction::TypeAvcImeResultINTEL { .. } 215 | Instruction::TypeAvcImeResultSingleReferenceStreamoutINTEL { .. } 216 | Instruction::TypeAvcImeResultDualReferenceStreamoutINTEL { .. } 217 | Instruction::TypeAvcImeSingleReferenceStreaminINTEL { .. } 218 | Instruction::TypeAvcImeDualReferenceStreaminINTEL { .. } 219 | Instruction::TypeAvcRefResultINTEL { .. } 220 | Instruction::TypeAvcSicResultINTEL { .. } 221 | Instruction::ConstantTrue { .. } 222 | Instruction::ConstantFalse { .. } 223 | Instruction::Constant { .. } 224 | Instruction::ConstantComposite { .. } 225 | Instruction::ConstantSampler { .. } 226 | Instruction::ConstantNull { .. } 227 | Instruction::ConstantPipeStorage { .. } 228 | Instruction::SpecConstantTrue { .. } 229 | Instruction::SpecConstantFalse { .. } 230 | Instruction::SpecConstant { .. } 231 | Instruction::SpecConstantComposite { .. } 232 | Instruction::SpecConstantOp { .. } => set_range(&mut range_global, index)?, 233 Instruction::Undef { .. } if !in_function => set_range(&mut range_global, index)?, 234 Instruction::Variable { storage_class, .. } 235 if *storage_class != StorageClass::Function => 236 { 237 set_range(&mut range_global, index)? 238 } 239 Instruction::Function { .. } => { 240 in_function = true; 241 } 242 Instruction::Line { .. } | Instruction::NoLine { .. } => { 243 if !in_function { 244 set_range(&mut range_global, index)? 245 } 246 } 247 _ => (), 248 } 249 } 250 251 let mut spirv = Spirv { 252 version, 253 bound, 254 instructions, 255 ids, 256 257 range_capability: range_capability.unwrap_or_default(), 258 range_extension: range_extension.unwrap_or_default(), 259 range_ext_inst_import: range_ext_inst_import.unwrap_or_default(), 260 memory_model: if let Some(range) = range_memory_model { 261 if range.end - range.start != 1 { 262 return Err(SpirvError::MemoryModelInvalid); 263 } 264 265 range.start 266 } else { 267 return Err(SpirvError::MemoryModelInvalid); 268 }, 269 range_entry_point: range_entry_point.unwrap_or_default(), 270 range_execution_mode: range_execution_mode.unwrap_or_default(), 271 range_name: range_name.unwrap_or_default(), 272 range_decoration: range_decoration.unwrap_or_default(), 273 range_global: range_global.unwrap_or_default(), 274 }; 275 276 for index in spirv.range_name.clone() { 277 match &spirv.instructions[index] { 278 Instruction::Name { target, .. } => { 279 spirv.ids.get_mut(target).unwrap().names.push(index); 280 } 281 Instruction::MemberName { ty, member, .. } => { 282 spirv.ids.get_mut(ty).unwrap().members[*member as usize] 283 .names 284 .push(index); 285 } 286 _ => unreachable!(), 287 } 288 } 289 290 // First handle all regular decorations, including those targeting decoration groups. 291 for index in spirv.range_decoration.clone() { 292 match &spirv.instructions[index] { 293 Instruction::Decorate { target, .. } 294 | Instruction::DecorateId { target, .. } 295 | Instruction::DecorateString { target, .. } => { 296 spirv.ids.get_mut(target).unwrap().decorations.push(index); 297 } 298 Instruction::MemberDecorate { 299 structure_type: target, 300 member, 301 .. 302 } 303 | Instruction::MemberDecorateString { 304 struct_type: target, 305 member, 306 .. 307 } => { 308 spirv.ids.get_mut(target).unwrap().members[*member as usize] 309 .decorations 310 .push(index); 311 } 312 _ => (), 313 } 314 } 315 316 // Then, with decoration groups having their lists complete, handle group decorates. 317 for index in spirv.range_decoration.clone() { 318 match &spirv.instructions[index] { 319 Instruction::GroupDecorate { 320 decoration_group, 321 targets, 322 .. 323 } => { 324 let indices = { 325 let data = &spirv.ids[decoration_group]; 326 if !matches!( 327 spirv.instructions[data.index], 328 Instruction::DecorationGroup { .. } 329 ) { 330 return Err(SpirvError::GroupDecorateNotGroup { index }); 331 }; 332 data.decorations.clone() 333 }; 334 335 for target in targets { 336 spirv 337 .ids 338 .get_mut(target) 339 .unwrap() 340 .decorations 341 .extend(&indices); 342 } 343 } 344 Instruction::GroupMemberDecorate { 345 decoration_group, 346 targets, 347 .. 348 } => { 349 let indices = { 350 let data = &spirv.ids[decoration_group]; 351 if !matches!( 352 spirv.instructions[data.index], 353 Instruction::DecorationGroup { .. } 354 ) { 355 return Err(SpirvError::GroupDecorateNotGroup { index }); 356 }; 357 data.decorations.clone() 358 }; 359 360 for (target, member) in targets { 361 spirv.ids.get_mut(target).unwrap().members[*member as usize] 362 .decorations 363 .extend(&indices); 364 } 365 } 366 _ => (), 367 } 368 } 369 370 Ok(spirv) 371 } 372 373 /// Returns a reference to the instructions in the module. 374 #[inline] instructions(&self) -> &[Instruction]375 pub fn instructions(&self) -> &[Instruction] { 376 &self.instructions 377 } 378 379 /// Returns the SPIR-V version that the module is compiled for. 380 #[inline] version(&self) -> Version381 pub fn version(&self) -> Version { 382 self.version 383 } 384 385 /// Returns the upper bound of `Id`s. All `Id`s should have a numeric value strictly less than 386 /// this value. 387 #[inline] bound(&self) -> u32388 pub fn bound(&self) -> u32 { 389 self.bound 390 } 391 392 /// Returns information about an `Id`. 393 /// 394 /// # Panics 395 /// 396 /// - Panics if `id` is not defined in this module. This can in theory only happpen if you are 397 /// mixing `Id`s from different modules. 398 #[inline] id(&self, id: Id) -> IdInfo<'_>399 pub fn id(&self, id: Id) -> IdInfo<'_> { 400 IdInfo { 401 data_indices: &self.ids[&id], 402 instructions: &self.instructions, 403 } 404 } 405 406 /// Returns an iterator over all `Capability` instructions. 407 #[inline] iter_capability(&self) -> impl ExactSizeIterator<Item = &Instruction>408 pub fn iter_capability(&self) -> impl ExactSizeIterator<Item = &Instruction> { 409 self.instructions[self.range_capability.clone()].iter() 410 } 411 412 /// Returns an iterator over all `Extension` instructions. 413 #[inline] iter_extension(&self) -> impl ExactSizeIterator<Item = &Instruction>414 pub fn iter_extension(&self) -> impl ExactSizeIterator<Item = &Instruction> { 415 self.instructions[self.range_extension.clone()].iter() 416 } 417 418 /// Returns an iterator over all `ExtInstImport` instructions. 419 #[inline] iter_ext_inst_import(&self) -> impl ExactSizeIterator<Item = &Instruction>420 pub fn iter_ext_inst_import(&self) -> impl ExactSizeIterator<Item = &Instruction> { 421 self.instructions[self.range_ext_inst_import.clone()].iter() 422 } 423 424 /// Returns the `MemoryModel` instruction. 425 #[inline] memory_model(&self) -> &Instruction426 pub fn memory_model(&self) -> &Instruction { 427 &self.instructions[self.memory_model] 428 } 429 430 /// Returns an iterator over all `EntryPoint` instructions. 431 #[inline] iter_entry_point(&self) -> impl ExactSizeIterator<Item = &Instruction>432 pub fn iter_entry_point(&self) -> impl ExactSizeIterator<Item = &Instruction> { 433 self.instructions[self.range_entry_point.clone()].iter() 434 } 435 436 /// Returns an iterator over all execution mode instructions. 437 #[inline] iter_execution_mode(&self) -> impl ExactSizeIterator<Item = &Instruction>438 pub fn iter_execution_mode(&self) -> impl ExactSizeIterator<Item = &Instruction> { 439 self.instructions[self.range_execution_mode.clone()].iter() 440 } 441 442 /// Returns an iterator over all name debug instructions. 443 #[inline] iter_name(&self) -> impl ExactSizeIterator<Item = &Instruction>444 pub fn iter_name(&self) -> impl ExactSizeIterator<Item = &Instruction> { 445 self.instructions[self.range_name.clone()].iter() 446 } 447 448 /// Returns an iterator over all decoration instructions. 449 #[inline] iter_decoration(&self) -> impl ExactSizeIterator<Item = &Instruction>450 pub fn iter_decoration(&self) -> impl ExactSizeIterator<Item = &Instruction> { 451 self.instructions[self.range_decoration.clone()].iter() 452 } 453 454 /// Returns an iterator over all global declaration instructions: types, 455 /// constants and global variables. 456 /// 457 /// Note: This can also include `Line` and `NoLine` instructions. 458 #[inline] iter_global(&self) -> impl ExactSizeIterator<Item = &Instruction>459 pub fn iter_global(&self) -> impl ExactSizeIterator<Item = &Instruction> { 460 self.instructions[self.range_global.clone()].iter() 461 } 462 } 463 464 #[derive(Clone, Debug)] 465 struct IdDataIndices { 466 index: usize, 467 names: Vec<usize>, 468 decorations: Vec<usize>, 469 members: Vec<StructMemberDataIndices>, 470 } 471 472 #[derive(Clone, Debug, Default)] 473 struct StructMemberDataIndices { 474 names: Vec<usize>, 475 decorations: Vec<usize>, 476 } 477 478 /// Information associated with an `Id`. 479 #[derive(Clone, Debug)] 480 pub struct IdInfo<'a> { 481 data_indices: &'a IdDataIndices, 482 instructions: &'a [Instruction], 483 } 484 485 impl<'a> IdInfo<'a> { 486 /// Returns the instruction that defines this `Id` with a `result_id` operand. 487 #[inline] instruction(&self) -> &'a Instruction488 pub fn instruction(&self) -> &'a Instruction { 489 &self.instructions[self.data_indices.index] 490 } 491 492 /// Returns an iterator over all name debug instructions that target this `Id`. 493 #[inline] iter_name(&self) -> impl ExactSizeIterator<Item = &'a Instruction>494 pub fn iter_name(&self) -> impl ExactSizeIterator<Item = &'a Instruction> { 495 let instructions = self.instructions; 496 self.data_indices 497 .names 498 .iter() 499 .map(move |&index| &instructions[index]) 500 } 501 502 /// Returns an iterator over all decorate instructions, that target this `Id`. This includes any 503 /// decorate instructions that target this `Id` indirectly via a `DecorationGroup`. 504 #[inline] iter_decoration(&self) -> impl ExactSizeIterator<Item = &'a Instruction>505 pub fn iter_decoration(&self) -> impl ExactSizeIterator<Item = &'a Instruction> { 506 let instructions = self.instructions; 507 self.data_indices 508 .decorations 509 .iter() 510 .map(move |&index| &instructions[index]) 511 } 512 513 /// If this `Id` refers to a `TypeStruct`, returns an iterator of information about each member 514 /// of the struct. Empty otherwise. 515 #[inline] iter_members(&self) -> impl ExactSizeIterator<Item = StructMemberInfo<'a>>516 pub fn iter_members(&self) -> impl ExactSizeIterator<Item = StructMemberInfo<'a>> { 517 let instructions = self.instructions; 518 self.data_indices 519 .members 520 .iter() 521 .map(move |data_indices| StructMemberInfo { 522 data_indices, 523 instructions, 524 }) 525 } 526 } 527 528 /// Information associated with a member of a `TypeStruct` instruction. 529 #[derive(Clone, Debug)] 530 pub struct StructMemberInfo<'a> { 531 data_indices: &'a StructMemberDataIndices, 532 instructions: &'a [Instruction], 533 } 534 535 impl<'a> StructMemberInfo<'a> { 536 /// Returns an iterator over all name debug instructions that target this struct member. 537 #[inline] iter_name(&self) -> impl ExactSizeIterator<Item = &'a Instruction>538 pub fn iter_name(&self) -> impl ExactSizeIterator<Item = &'a Instruction> { 539 let instructions = self.instructions; 540 self.data_indices 541 .names 542 .iter() 543 .map(move |&index| &instructions[index]) 544 } 545 546 /// Returns an iterator over all decorate instructions that target this struct member. This 547 /// includes any decorate instructions that target this member indirectly via a 548 /// `DecorationGroup`. 549 #[inline] iter_decoration(&self) -> impl ExactSizeIterator<Item = &'a Instruction>550 pub fn iter_decoration(&self) -> impl ExactSizeIterator<Item = &'a Instruction> { 551 let instructions = self.instructions; 552 self.data_indices 553 .decorations 554 .iter() 555 .map(move |&index| &instructions[index]) 556 } 557 } 558 559 /// Used in SPIR-V to refer to the result of another instruction. 560 /// 561 /// Ids are global across a module, and are always assigned by exactly one instruction. 562 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 563 #[repr(transparent)] 564 pub struct Id(u32); 565 566 impl From<Id> for u32 { 567 #[inline] from(id: Id) -> u32568 fn from(id: Id) -> u32 { 569 id.0 570 } 571 } 572 573 impl Display for Id { 574 #[inline] fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>575 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 576 write!(f, "%{}", self.0) 577 } 578 } 579 580 /// Helper type for parsing the words of an instruction. 581 #[derive(Debug)] 582 struct InstructionReader<'a> { 583 words: &'a [u32], 584 next_word: usize, 585 instruction: usize, 586 } 587 588 impl<'a> InstructionReader<'a> { 589 /// Constructs a new reader from a slice of words for a single instruction, including the opcode 590 /// word. `instruction` is the number of the instruction currently being read, and is used for 591 /// error reporting. new(words: &'a [u32], instruction: usize) -> Self592 fn new(words: &'a [u32], instruction: usize) -> Self { 593 debug_assert!(!words.is_empty()); 594 Self { 595 words, 596 next_word: 0, 597 instruction, 598 } 599 } 600 601 /// Returns whether the reader has reached the end of the current instruction. is_empty(&self) -> bool602 fn is_empty(&self) -> bool { 603 self.next_word >= self.words.len() 604 } 605 606 /// Converts the `ParseErrors` enum to the `ParseError` struct, adding contextual information. map_err(&self, error: ParseErrors) -> ParseError607 fn map_err(&self, error: ParseErrors) -> ParseError { 608 ParseError { 609 instruction: self.instruction, 610 word: self.next_word - 1, // -1 because the word has already been read 611 error, 612 words: self.words.to_owned(), 613 } 614 } 615 616 /// Returns the next word in the sequence. next_u32(&mut self) -> Result<u32, ParseError>617 fn next_u32(&mut self) -> Result<u32, ParseError> { 618 let word = *self.words.get(self.next_word).ok_or(ParseError { 619 instruction: self.instruction, 620 word: self.next_word, // No -1 because we didn't advance yet 621 error: ParseErrors::MissingOperands, 622 words: self.words.to_owned(), 623 })?; 624 self.next_word += 1; 625 626 Ok(word) 627 } 628 629 /* 630 /// Returns the next two words as a single `u64`. 631 #[inline] 632 fn next_u64(&mut self) -> Result<u64, ParseError> { 633 Ok(self.next_u32()? as u64 | (self.next_u32()? as u64) << 32) 634 } 635 */ 636 637 /// Reads a nul-terminated string. next_string(&mut self) -> Result<String, ParseError>638 fn next_string(&mut self) -> Result<String, ParseError> { 639 let mut bytes = Vec::new(); 640 loop { 641 let word = self.next_u32()?.to_le_bytes(); 642 643 if let Some(nul) = word.iter().position(|&b| b == 0) { 644 bytes.extend(&word[0..nul]); 645 break; 646 } else { 647 bytes.extend(word); 648 } 649 } 650 String::from_utf8(bytes).map_err(|err| self.map_err(ParseErrors::FromUtf8Error(err))) 651 } 652 653 /// Reads all remaining words. remainder(&mut self) -> Vec<u32>654 fn remainder(&mut self) -> Vec<u32> { 655 let vec = self.words[self.next_word..].to_owned(); 656 self.next_word = self.words.len(); 657 vec 658 } 659 } 660 661 /// Error that can happen when reading a SPIR-V module. 662 #[derive(Clone, Debug)] 663 pub enum SpirvError { 664 BadLayout { 665 index: usize, 666 }, 667 DuplicateId { 668 id: Id, 669 first_index: usize, 670 second_index: usize, 671 }, 672 GroupDecorateNotGroup { 673 index: usize, 674 }, 675 IdOutOfBounds { 676 id: Id, 677 index: usize, 678 bound: u32, 679 }, 680 InvalidHeader, 681 MemoryModelInvalid, 682 ParseError(ParseError), 683 } 684 685 impl Display for SpirvError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>686 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 687 match self { 688 Self::BadLayout { index } => write!( 689 f, 690 "the instruction at index {} does not follow the logical layout of a module", 691 index, 692 ), 693 Self::DuplicateId { 694 id, 695 first_index, 696 second_index, 697 } => write!( 698 f, 699 "id {} is assigned more than once, by instructions {} and {}", 700 id, first_index, second_index, 701 ), 702 Self::GroupDecorateNotGroup { index } => write!( 703 f, 704 "a GroupDecorate or GroupMemberDecorate instruction at index {} referred to an Id \ 705 that was not a DecorationGroup", 706 index, 707 ), 708 Self::IdOutOfBounds { id, bound, index } => write!( 709 f, 710 "id {}, assigned at instruction {}, is not below the maximum bound {}", 711 id, index, bound, 712 ), 713 Self::InvalidHeader => write!(f, "the SPIR-V module header is invalid"), 714 Self::MemoryModelInvalid => { 715 write!(f, "the MemoryModel instruction is not present exactly once") 716 } 717 Self::ParseError(_) => write!(f, "parse error"), 718 } 719 } 720 } 721 722 impl Error for SpirvError { source(&self) -> Option<&(dyn Error + 'static)>723 fn source(&self) -> Option<&(dyn Error + 'static)> { 724 match self { 725 Self::ParseError(err) => Some(err), 726 _ => None, 727 } 728 } 729 } 730 731 impl From<ParseError> for SpirvError { from(err: ParseError) -> Self732 fn from(err: ParseError) -> Self { 733 Self::ParseError(err) 734 } 735 } 736 737 /// Error that can happen when parsing SPIR-V instructions into Rust data structures. 738 #[derive(Clone, Debug)] 739 pub struct ParseError { 740 /// The instruction number the error happened at, starting from 0. 741 pub instruction: usize, 742 /// The word from the start of the instruction that the error happened at, starting from 0. 743 pub word: usize, 744 /// The error. 745 pub error: ParseErrors, 746 /// The words of the instruction. 747 pub words: Vec<u32>, 748 } 749 750 impl Display for ParseError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>751 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 752 write!( 753 f, 754 "at instruction {}, word {}: {}", 755 self.instruction, self.word, self.error, 756 ) 757 } 758 } 759 760 impl Error for ParseError {} 761 762 /// Individual types of parse error that can happen. 763 #[derive(Clone, Debug)] 764 pub enum ParseErrors { 765 FromUtf8Error(FromUtf8Error), 766 LeftoverOperands, 767 MissingOperands, 768 UnexpectedEOF, 769 UnknownEnumerant(&'static str, u32), 770 UnknownOpcode(u16), 771 UnknownSpecConstantOpcode(u16), 772 } 773 774 impl Display for ParseErrors { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>775 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 776 match self { 777 Self::FromUtf8Error(_) => write!(f, "invalid UTF-8 in string literal"), 778 Self::LeftoverOperands => write!(f, "unparsed operands remaining"), 779 Self::MissingOperands => write!( 780 f, 781 "the instruction and its operands require more words than are present in the \ 782 instruction", 783 ), 784 Self::UnexpectedEOF => write!(f, "encountered unexpected end of file"), 785 Self::UnknownEnumerant(ty, enumerant) => { 786 write!(f, "invalid enumerant {} for enum {}", enumerant, ty) 787 } 788 Self::UnknownOpcode(opcode) => write!(f, "invalid instruction opcode {}", opcode), 789 Self::UnknownSpecConstantOpcode(opcode) => { 790 write!(f, "invalid spec constant instruction opcode {}", opcode) 791 } 792 } 793 } 794 } 795