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