naga/front/spv/
mod.rs

1/*!
2Frontend for [SPIR-V][spv] (Standard Portable Intermediate Representation).
3
4## ID lookups
5
6Our IR links to everything with `Handle`, while SPIR-V uses IDs.
7In order to keep track of the associations, the parser has many lookup tables.
8There map `spv::Word` into a specific IR handle, plus potentially a bit of
9extra info, such as the related SPIR-V type ID.
10TODO: would be nice to find ways that avoid looking up as much
11
12## Inputs/Outputs
13
14We create a private variable for each input/output. The relevant inputs are
15populated at the start of an entry point. The outputs are saved at the end.
16
17The function associated with an entry point is wrapped in another function,
18such that we can handle any `Return` statements without problems.
19
20## Row-major matrices
21
22We don't handle them natively, since the IR only expects column majority.
23Instead, we detect when such matrix is accessed in the `OpAccessChain`,
24and we generate a parallel expression that loads the value, but transposed.
25This value then gets used instead of `OpLoad` result later on.
26
27[spv]: https://www.khronos.org/registry/SPIR-V/
28*/
29
30mod convert;
31mod error;
32mod function;
33mod image;
34mod next_block;
35mod null;
36
37pub use error::Error;
38
39use alloc::{borrow::ToOwned, string::String, vec, vec::Vec};
40use core::{convert::TryInto, mem, num::NonZeroU32};
41
42use half::f16;
43use petgraph::graphmap::GraphMap;
44
45use super::atomic_upgrade::Upgrades;
46use crate::{
47    arena::{Arena, Handle, UniqueArena},
48    proc::{Alignment, Layouter},
49    FastHashMap, FastHashSet, FastIndexMap,
50};
51use convert::*;
52use function::*;
53
54pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[
55    spirv::Capability::Shader,
56    spirv::Capability::VulkanMemoryModel,
57    spirv::Capability::ClipDistance,
58    spirv::Capability::CullDistance,
59    spirv::Capability::SampleRateShading,
60    spirv::Capability::DerivativeControl,
61    spirv::Capability::Matrix,
62    spirv::Capability::ImageQuery,
63    spirv::Capability::Sampled1D,
64    spirv::Capability::Image1D,
65    spirv::Capability::SampledCubeArray,
66    spirv::Capability::ImageCubeArray,
67    spirv::Capability::StorageImageExtendedFormats,
68    spirv::Capability::Int8,
69    spirv::Capability::Int16,
70    spirv::Capability::Int64,
71    spirv::Capability::Int64Atomics,
72    spirv::Capability::Float16,
73    spirv::Capability::AtomicFloat32AddEXT,
74    spirv::Capability::Float64,
75    spirv::Capability::Geometry,
76    spirv::Capability::MultiView,
77    spirv::Capability::StorageBuffer16BitAccess,
78    spirv::Capability::UniformAndStorageBuffer16BitAccess,
79    spirv::Capability::GroupNonUniform,
80    spirv::Capability::GroupNonUniformVote,
81    spirv::Capability::GroupNonUniformArithmetic,
82    spirv::Capability::GroupNonUniformBallot,
83    spirv::Capability::GroupNonUniformShuffle,
84    spirv::Capability::GroupNonUniformShuffleRelative,
85    spirv::Capability::RuntimeDescriptorArray,
86    spirv::Capability::StorageImageMultisample,
87    spirv::Capability::FragmentBarycentricKHR,
88    // tricky ones
89    spirv::Capability::UniformBufferArrayDynamicIndexing,
90    spirv::Capability::StorageBufferArrayDynamicIndexing,
91];
92pub const SUPPORTED_EXTENSIONS: &[&str] = &[
93    "SPV_KHR_storage_buffer_storage_class",
94    "SPV_KHR_vulkan_memory_model",
95    "SPV_KHR_multiview",
96    "SPV_EXT_descriptor_indexing",
97    "SPV_EXT_shader_atomic_float_add",
98    "SPV_KHR_16bit_storage",
99];
100pub const SUPPORTED_EXT_SETS: &[&str] = &["GLSL.std.450"];
101
102#[derive(Copy, Clone)]
103pub struct Instruction {
104    op: spirv::Op,
105    wc: u16,
106}
107
108impl Instruction {
109    const fn expect(self, count: u16) -> Result<(), Error> {
110        if self.wc == count {
111            Ok(())
112        } else {
113            Err(Error::InvalidOperandCount(self.op, self.wc))
114        }
115    }
116
117    fn expect_at_least(self, count: u16) -> Result<u16, Error> {
118        self.wc
119            .checked_sub(count)
120            .ok_or(Error::InvalidOperandCount(self.op, self.wc))
121    }
122}
123
124impl crate::TypeInner {
125    fn can_comparison_sample(&self, module: &crate::Module) -> bool {
126        match *self {
127            crate::TypeInner::Image {
128                class:
129                    crate::ImageClass::Sampled {
130                        kind: crate::ScalarKind::Float,
131                        multi: false,
132                    },
133                ..
134            } => true,
135            crate::TypeInner::Sampler { .. } => true,
136            crate::TypeInner::BindingArray { base, .. } => {
137                module.types[base].inner.can_comparison_sample(module)
138            }
139            _ => false,
140        }
141    }
142}
143
144#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
145pub enum ModuleState {
146    Empty,
147    Capability,
148    Extension,
149    ExtInstImport,
150    MemoryModel,
151    EntryPoint,
152    ExecutionMode,
153    Source,
154    Name,
155    ModuleProcessed,
156    Annotation,
157    Type,
158    Function,
159}
160
161trait LookupHelper {
162    type Target;
163    fn lookup(&self, key: spirv::Word) -> Result<&Self::Target, Error>;
164}
165
166impl<T> LookupHelper for FastHashMap<spirv::Word, T> {
167    type Target = T;
168    fn lookup(&self, key: spirv::Word) -> Result<&T, Error> {
169        self.get(&key).ok_or(Error::InvalidId(key))
170    }
171}
172
173impl crate::ImageDimension {
174    const fn required_coordinate_size(&self) -> Option<crate::VectorSize> {
175        match *self {
176            crate::ImageDimension::D1 => None,
177            crate::ImageDimension::D2 => Some(crate::VectorSize::Bi),
178            crate::ImageDimension::D3 => Some(crate::VectorSize::Tri),
179            crate::ImageDimension::Cube => Some(crate::VectorSize::Tri),
180        }
181    }
182}
183
184type MemberIndex = u32;
185
186bitflags::bitflags! {
187    #[derive(Clone, Copy, Debug, Default)]
188    struct DecorationFlags: u32 {
189        const NON_READABLE = 0x1;
190        const NON_WRITABLE = 0x2;
191    }
192}
193
194impl DecorationFlags {
195    fn to_storage_access(self) -> crate::StorageAccess {
196        let mut access = crate::StorageAccess::LOAD | crate::StorageAccess::STORE;
197        if self.contains(DecorationFlags::NON_READABLE) {
198            access &= !crate::StorageAccess::LOAD;
199        }
200        if self.contains(DecorationFlags::NON_WRITABLE) {
201            access &= !crate::StorageAccess::STORE;
202        }
203        access
204    }
205}
206
207#[derive(Debug, PartialEq)]
208enum Majority {
209    Column,
210    Row,
211}
212
213#[derive(Debug, Default)]
214struct Decoration {
215    name: Option<String>,
216    built_in: Option<spirv::Word>,
217    location: Option<spirv::Word>,
218    desc_set: Option<spirv::Word>,
219    desc_index: Option<spirv::Word>,
220    specialization_constant_id: Option<spirv::Word>,
221    storage_buffer: bool,
222    offset: Option<spirv::Word>,
223    array_stride: Option<NonZeroU32>,
224    matrix_stride: Option<NonZeroU32>,
225    matrix_major: Option<Majority>,
226    invariant: bool,
227    interpolation: Option<crate::Interpolation>,
228    sampling: Option<crate::Sampling>,
229    flags: DecorationFlags,
230}
231
232impl Decoration {
233    fn debug_name(&self) -> &str {
234        match self.name {
235            Some(ref name) => name.as_str(),
236            None => "?",
237        }
238    }
239
240    const fn resource_binding(&self) -> Option<crate::ResourceBinding> {
241        match *self {
242            Decoration {
243                desc_set: Some(group),
244                desc_index: Some(binding),
245                ..
246            } => Some(crate::ResourceBinding { group, binding }),
247            _ => None,
248        }
249    }
250
251    fn io_binding(&self) -> Result<crate::Binding, Error> {
252        match *self {
253            Decoration {
254                built_in: Some(built_in),
255                location: None,
256                invariant,
257                ..
258            } => Ok(crate::Binding::BuiltIn(map_builtin(built_in, invariant)?)),
259            Decoration {
260                built_in: None,
261                location: Some(location),
262                interpolation,
263                sampling,
264                ..
265            } => Ok(crate::Binding::Location {
266                location,
267                interpolation,
268                sampling,
269                blend_src: None,
270                per_primitive: false,
271            }),
272            _ => Err(Error::MissingDecoration(spirv::Decoration::Location)),
273        }
274    }
275}
276
277#[derive(Debug)]
278struct LookupFunctionType {
279    parameter_type_ids: Vec<spirv::Word>,
280    return_type_id: spirv::Word,
281}
282
283struct LookupFunction {
284    handle: Handle<crate::Function>,
285    parameters_sampling: Vec<image::SamplingFlags>,
286}
287
288#[derive(Debug)]
289struct EntryPoint {
290    stage: crate::ShaderStage,
291    name: String,
292    early_depth_test: Option<crate::EarlyDepthTest>,
293    workgroup_size: [u32; 3],
294    variable_ids: Vec<spirv::Word>,
295}
296
297#[derive(Clone, Debug)]
298struct LookupType {
299    handle: Handle<crate::Type>,
300    base_id: Option<spirv::Word>,
301}
302
303#[derive(Debug)]
304enum Constant {
305    Constant(Handle<crate::Constant>),
306    Override(Handle<crate::Override>),
307}
308
309impl Constant {
310    const fn to_expr(&self) -> crate::Expression {
311        match *self {
312            Self::Constant(c) => crate::Expression::Constant(c),
313            Self::Override(o) => crate::Expression::Override(o),
314        }
315    }
316}
317
318#[derive(Debug)]
319struct LookupConstant {
320    inner: Constant,
321    type_id: spirv::Word,
322}
323
324#[derive(Debug)]
325enum Variable {
326    Global,
327    Input(crate::FunctionArgument),
328    Output(crate::FunctionResult),
329}
330
331#[derive(Debug)]
332struct LookupVariable {
333    inner: Variable,
334    handle: Handle<crate::GlobalVariable>,
335    type_id: spirv::Word,
336}
337
338/// Information about SPIR-V result ids, stored in `Frontend::lookup_expression`.
339#[derive(Clone, Debug)]
340struct LookupExpression {
341    /// The `Expression` constructed for this result.
342    ///
343    /// Note that, while a SPIR-V result id can be used in any block dominated
344    /// by its definition, a Naga `Expression` is only in scope for the rest of
345    /// its subtree. `Frontend::get_expr_handle` takes care of spilling the result
346    /// to a `LocalVariable` which can then be used anywhere.
347    handle: Handle<crate::Expression>,
348
349    /// The SPIR-V type of this result.
350    type_id: spirv::Word,
351
352    /// The label id of the block that defines this expression.
353    ///
354    /// This is zero for globals, constants, and function parameters, since they
355    /// originate outside any function's block.
356    block_id: spirv::Word,
357}
358
359#[derive(Debug)]
360struct LookupMember {
361    type_id: spirv::Word,
362    // This is true for either matrices, or arrays of matrices (yikes).
363    row_major: bool,
364}
365
366#[derive(Clone, Debug)]
367enum LookupLoadOverride {
368    /// For arrays of matrices, we track them but not loading yet.
369    Pending,
370    /// For matrices, vectors, and scalars, we pre-load the data.
371    Loaded(Handle<crate::Expression>),
372}
373
374#[derive(PartialEq)]
375enum ExtendedClass {
376    Global(crate::AddressSpace),
377    Input,
378    Output,
379}
380
381#[derive(Clone, Debug)]
382pub struct Options {
383    /// The IR coordinate space matches all the APIs except SPIR-V,
384    /// so by default we flip the Y coordinate of the `BuiltIn::Position`.
385    /// This flag can be used to avoid this.
386    pub adjust_coordinate_space: bool,
387    /// Only allow shaders with the known set of capabilities.
388    pub strict_capabilities: bool,
389    pub block_ctx_dump_prefix: Option<String>,
390}
391
392impl Default for Options {
393    fn default() -> Self {
394        Options {
395            adjust_coordinate_space: true,
396            strict_capabilities: true,
397            block_ctx_dump_prefix: None,
398        }
399    }
400}
401
402/// An index into the `BlockContext::bodies` table.
403type BodyIndex = usize;
404
405/// An intermediate representation of a Naga [`Statement`].
406///
407/// `Body` and `BodyFragment` values form a tree: the `BodyIndex` fields of the
408/// variants are indices of the child `Body` values in [`BlockContext::bodies`].
409/// The `lower` function assembles the final `Statement` tree from this `Body`
410/// tree. See [`BlockContext`] for details.
411///
412/// [`Statement`]: crate::Statement
413#[derive(Debug)]
414enum BodyFragment {
415    BlockId(spirv::Word),
416    If {
417        condition: Handle<crate::Expression>,
418        accept: BodyIndex,
419        reject: BodyIndex,
420    },
421    Loop {
422        /// The body of the loop. Its [`Body::parent`] is the block containing
423        /// this `Loop` fragment.
424        body: BodyIndex,
425
426        /// The loop's continuing block. This is a grandchild: its
427        /// [`Body::parent`] is the loop body block, whose index is above.
428        continuing: BodyIndex,
429
430        /// If the SPIR-V loop's back-edge branch is conditional, this is the
431        /// expression that must be `false` for the back-edge to be taken, with
432        /// `true` being for the "loop merge" (which breaks out of the loop).
433        break_if: Option<Handle<crate::Expression>>,
434    },
435    Switch {
436        selector: Handle<crate::Expression>,
437        cases: Vec<(i32, BodyIndex)>,
438        default: BodyIndex,
439    },
440    Break,
441    Continue,
442}
443
444/// An intermediate representation of a Naga [`Block`].
445///
446/// This will be assembled into a `Block` once we've added spills for phi nodes
447/// and out-of-scope expressions. See [`BlockContext`] for details.
448///
449/// [`Block`]: crate::Block
450#[derive(Debug)]
451struct Body {
452    /// The index of the direct parent of this body
453    parent: usize,
454    data: Vec<BodyFragment>,
455}
456
457impl Body {
458    /// Creates a new empty `Body` with the specified `parent`
459    pub const fn with_parent(parent: usize) -> Self {
460        Body {
461            parent,
462            data: Vec::new(),
463        }
464    }
465}
466
467#[derive(Debug)]
468struct PhiExpression {
469    /// The local variable used for the phi node
470    local: Handle<crate::LocalVariable>,
471    /// List of (expression, block)
472    expressions: Vec<(spirv::Word, spirv::Word)>,
473}
474
475#[derive(Copy, Clone, Debug, PartialEq, Eq)]
476enum MergeBlockInformation {
477    LoopMerge,
478    LoopContinue,
479    SelectionMerge,
480    SwitchMerge,
481}
482
483/// Fragments of Naga IR, to be assembled into `Statements` once data flow is
484/// resolved.
485///
486/// We can't build a Naga `Statement` tree directly from SPIR-V blocks for three
487/// main reasons:
488///
489/// - We parse a function's SPIR-V blocks in the order they appear in the file.
490///   Within a function, SPIR-V requires that a block must precede any blocks it
491///   structurally dominates, but doesn't say much else about the order in which
492///   they must appear. So while we know we'll see control flow header blocks
493///   before their child constructs and merge blocks, those children and the
494///   merge blocks may appear in any order - perhaps even intermingled with
495///   children of other constructs.
496///
497/// - A SPIR-V expression can be used in any SPIR-V block dominated by its
498///   definition, whereas Naga expressions are scoped to the rest of their
499///   subtree. This means that discovering an expression use later in the
500///   function retroactively requires us to have spilled that expression into a
501///   local variable back before we left its scope. (The docs for
502///   [`Frontend::get_expr_handle`] explain this in more detail.)
503///
504/// - We translate SPIR-V OpPhi expressions as Naga local variables in which we
505///   store the appropriate value before jumping to the OpPhi's block.
506///
507/// All these cases require us to go back and amend previously generated Naga IR
508/// based on things we discover later. But modifying old blocks in arbitrary
509/// spots in a `Statement` tree is awkward.
510///
511/// Instead, as we iterate through the function's body, we accumulate
512/// control-flow-free fragments of Naga IR in the [`blocks`] table, while
513/// building a skeleton of the Naga `Statement` tree in [`bodies`]. We note any
514/// spills and temporaries we must introduce in [`phis`].
515///
516/// Finally, once we've processed the entire function, we add temporaries and
517/// spills to the fragmentary `Blocks` as directed by `phis`, and assemble them
518/// into the final Naga `Statement` tree as directed by `bodies`.
519///
520/// [`blocks`]: BlockContext::blocks
521/// [`bodies`]: BlockContext::bodies
522/// [`phis`]: BlockContext::phis
523#[derive(Debug)]
524struct BlockContext<'function> {
525    /// Phi nodes encountered when parsing the function, used to generate spills
526    /// to local variables.
527    phis: Vec<PhiExpression>,
528
529    /// Fragments of control-flow-free Naga IR.
530    ///
531    /// These will be stitched together into a proper [`Statement`] tree according
532    /// to `bodies`, once parsing is complete.
533    ///
534    /// [`Statement`]: crate::Statement
535    blocks: FastHashMap<spirv::Word, crate::Block>,
536
537    /// Map from each SPIR-V block's label id to the index of the [`Body`] in
538    /// [`bodies`] the block should append its contents to.
539    ///
540    /// Since each statement in a Naga [`Block`] dominates the next, we are sure
541    /// to encounter their SPIR-V blocks in order. Thus, by having this table
542    /// map a SPIR-V structured control flow construct's merge block to the same
543    /// body index as its header block, when we encounter the merge block, we
544    /// will simply pick up building the [`Body`] where the header left off.
545    ///
546    /// A function's first block is special: it is the only block we encounter
547    /// without having seen its label mentioned in advance. (It's simply the
548    /// first `OpLabel` after the `OpFunction`.) We thus assume that any block
549    /// missing an entry here must be the first block, which always has body
550    /// index zero.
551    ///
552    /// [`bodies`]: BlockContext::bodies
553    /// [`Block`]: crate::Block
554    body_for_label: FastHashMap<spirv::Word, BodyIndex>,
555
556    /// SPIR-V metadata about merge/continue blocks.
557    mergers: FastHashMap<spirv::Word, MergeBlockInformation>,
558
559    /// A table of `Body` values, each representing a block in the final IR.
560    ///
561    /// The first element is always the function's top-level block.
562    bodies: Vec<Body>,
563
564    /// The module we're building.
565    module: &'function mut crate::Module,
566
567    /// Id of the function currently being processed
568    function_id: spirv::Word,
569    /// Expression arena of the function currently being processed
570    expressions: &'function mut Arena<crate::Expression>,
571    /// Local variables arena of the function currently being processed
572    local_arena: &'function mut Arena<crate::LocalVariable>,
573    /// Arguments of the function currently being processed
574    arguments: &'function [crate::FunctionArgument],
575    /// Metadata about the usage of function parameters as sampling objects
576    parameter_sampling: &'function mut [image::SamplingFlags],
577}
578
579enum SignAnchor {
580    Result,
581    Operand,
582}
583
584pub struct Frontend<I> {
585    data: I,
586    data_offset: usize,
587    state: ModuleState,
588    layouter: Layouter,
589    temp_bytes: Vec<u8>,
590    ext_glsl_id: Option<spirv::Word>,
591    future_decor: FastHashMap<spirv::Word, Decoration>,
592    future_member_decor: FastHashMap<(spirv::Word, MemberIndex), Decoration>,
593    lookup_member: FastHashMap<(Handle<crate::Type>, MemberIndex), LookupMember>,
594    handle_sampling: FastHashMap<Handle<crate::GlobalVariable>, image::SamplingFlags>,
595
596    /// A record of what is accessed by [`Atomic`] statements we've
597    /// generated, so we can upgrade the types of their operands.
598    ///
599    /// [`Atomic`]: crate::Statement::Atomic
600    upgrade_atomics: Upgrades,
601
602    lookup_type: FastHashMap<spirv::Word, LookupType>,
603    lookup_void_type: Option<spirv::Word>,
604    lookup_storage_buffer_types: FastHashMap<Handle<crate::Type>, crate::StorageAccess>,
605    lookup_constant: FastHashMap<spirv::Word, LookupConstant>,
606    lookup_variable: FastHashMap<spirv::Word, LookupVariable>,
607    lookup_expression: FastHashMap<spirv::Word, LookupExpression>,
608    // Load overrides are used to work around row-major matrices
609    lookup_load_override: FastHashMap<spirv::Word, LookupLoadOverride>,
610    lookup_sampled_image: FastHashMap<spirv::Word, image::LookupSampledImage>,
611    lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,
612    lookup_function: FastHashMap<spirv::Word, LookupFunction>,
613    lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>,
614    // When parsing functions, each entry point function gets an entry here so that additional
615    // processing for them can be performed after all function parsing.
616    deferred_entry_points: Vec<(EntryPoint, spirv::Word)>,
617    //Note: each `OpFunctionCall` gets a single entry here, indexed by the
618    // dummy `Handle<crate::Function>` of the call site.
619    deferred_function_calls: Vec<spirv::Word>,
620    dummy_functions: Arena<crate::Function>,
621    // Graph of all function calls through the module.
622    // It's used to sort the functions (as nodes) topologically,
623    // so that in the IR any called function is already known.
624    function_call_graph: GraphMap<
625        spirv::Word,
626        (),
627        petgraph::Directed,
628        core::hash::BuildHasherDefault<rustc_hash::FxHasher>,
629    >,
630    options: Options,
631
632    /// Maps for a switch from a case target to the respective body and associated literals that
633    /// use that target block id.
634    ///
635    /// Used to preserve allocations between instruction parsing.
636    switch_cases: FastIndexMap<spirv::Word, (BodyIndex, Vec<i32>)>,
637
638    /// Tracks access to gl_PerVertex's builtins, it is used to cull unused builtins since initializing those can
639    /// affect performance and the mere presence of some of these builtins might cause backends to error since they
640    /// might be unsupported.
641    ///
642    /// The problematic builtins are: PointSize, ClipDistance and CullDistance.
643    ///
644    /// glslang declares those by default even though they are never written to
645    /// (see <https://github.com/KhronosGroup/glslang/issues/1868>)
646    gl_per_vertex_builtin_access: FastHashSet<crate::BuiltIn>,
647}
648
649impl<I: Iterator<Item = u32>> Frontend<I> {
650    pub fn new(data: I, options: &Options) -> Self {
651        Frontend {
652            data,
653            data_offset: 0,
654            state: ModuleState::Empty,
655            layouter: Layouter::default(),
656            temp_bytes: Vec::new(),
657            ext_glsl_id: None,
658            future_decor: FastHashMap::default(),
659            future_member_decor: FastHashMap::default(),
660            handle_sampling: FastHashMap::default(),
661            lookup_member: FastHashMap::default(),
662            upgrade_atomics: Default::default(),
663            lookup_type: FastHashMap::default(),
664            lookup_void_type: None,
665            lookup_storage_buffer_types: FastHashMap::default(),
666            lookup_constant: FastHashMap::default(),
667            lookup_variable: FastHashMap::default(),
668            lookup_expression: FastHashMap::default(),
669            lookup_load_override: FastHashMap::default(),
670            lookup_sampled_image: FastHashMap::default(),
671            lookup_function_type: FastHashMap::default(),
672            lookup_function: FastHashMap::default(),
673            lookup_entry_point: FastHashMap::default(),
674            deferred_entry_points: Vec::default(),
675            deferred_function_calls: Vec::default(),
676            dummy_functions: Arena::new(),
677            function_call_graph: GraphMap::new(),
678            options: options.clone(),
679            switch_cases: FastIndexMap::default(),
680            gl_per_vertex_builtin_access: FastHashSet::default(),
681        }
682    }
683
684    fn span_from(&self, from: usize) -> crate::Span {
685        crate::Span::from(from..self.data_offset)
686    }
687
688    fn span_from_with_op(&self, from: usize) -> crate::Span {
689        crate::Span::from((from - 4)..self.data_offset)
690    }
691
692    fn next(&mut self) -> Result<u32, Error> {
693        if let Some(res) = self.data.next() {
694            self.data_offset += 4;
695            Ok(res)
696        } else {
697            Err(Error::IncompleteData)
698        }
699    }
700
701    fn next_inst(&mut self) -> Result<Instruction, Error> {
702        let word = self.next()?;
703        let (wc, opcode) = ((word >> 16) as u16, (word & 0xffff) as u16);
704        if wc == 0 {
705            return Err(Error::InvalidWordCount);
706        }
707        let op = spirv::Op::from_u32(opcode as u32).ok_or(Error::UnknownInstruction(opcode))?;
708
709        Ok(Instruction { op, wc })
710    }
711
712    fn next_string(&mut self, mut count: u16) -> Result<(String, u16), Error> {
713        self.temp_bytes.clear();
714        loop {
715            if count == 0 {
716                return Err(Error::BadString);
717            }
718            count -= 1;
719            let chars = self.next()?.to_le_bytes();
720            let pos = chars.iter().position(|&c| c == 0).unwrap_or(4);
721            self.temp_bytes.extend_from_slice(&chars[..pos]);
722            if pos < 4 {
723                break;
724            }
725        }
726        core::str::from_utf8(&self.temp_bytes)
727            .map(|s| (s.to_owned(), count))
728            .map_err(|_| Error::BadString)
729    }
730
731    fn next_decoration(
732        &mut self,
733        inst: Instruction,
734        base_words: u16,
735        dec: &mut Decoration,
736    ) -> Result<(), Error> {
737        let raw = self.next()?;
738        let dec_typed = spirv::Decoration::from_u32(raw).ok_or(Error::InvalidDecoration(raw))?;
739        log::trace!("\t\t{}: {:?}", dec.debug_name(), dec_typed);
740        match dec_typed {
741            spirv::Decoration::BuiltIn => {
742                inst.expect(base_words + 2)?;
743                dec.built_in = Some(self.next()?);
744            }
745            spirv::Decoration::Location => {
746                inst.expect(base_words + 2)?;
747                dec.location = Some(self.next()?);
748            }
749            spirv::Decoration::DescriptorSet => {
750                inst.expect(base_words + 2)?;
751                dec.desc_set = Some(self.next()?);
752            }
753            spirv::Decoration::Binding => {
754                inst.expect(base_words + 2)?;
755                dec.desc_index = Some(self.next()?);
756            }
757            spirv::Decoration::BufferBlock => {
758                dec.storage_buffer = true;
759            }
760            spirv::Decoration::Offset => {
761                inst.expect(base_words + 2)?;
762                dec.offset = Some(self.next()?);
763            }
764            spirv::Decoration::ArrayStride => {
765                inst.expect(base_words + 2)?;
766                dec.array_stride = NonZeroU32::new(self.next()?);
767            }
768            spirv::Decoration::MatrixStride => {
769                inst.expect(base_words + 2)?;
770                dec.matrix_stride = NonZeroU32::new(self.next()?);
771            }
772            spirv::Decoration::Invariant => {
773                dec.invariant = true;
774            }
775            spirv::Decoration::NoPerspective => {
776                dec.interpolation = Some(crate::Interpolation::Linear);
777            }
778            spirv::Decoration::Flat => {
779                dec.interpolation = Some(crate::Interpolation::Flat);
780            }
781            spirv::Decoration::Centroid => {
782                dec.sampling = Some(crate::Sampling::Centroid);
783            }
784            spirv::Decoration::Sample => {
785                dec.sampling = Some(crate::Sampling::Sample);
786            }
787            spirv::Decoration::NonReadable => {
788                dec.flags |= DecorationFlags::NON_READABLE;
789            }
790            spirv::Decoration::NonWritable => {
791                dec.flags |= DecorationFlags::NON_WRITABLE;
792            }
793            spirv::Decoration::ColMajor => {
794                dec.matrix_major = Some(Majority::Column);
795            }
796            spirv::Decoration::RowMajor => {
797                dec.matrix_major = Some(Majority::Row);
798            }
799            spirv::Decoration::SpecId => {
800                dec.specialization_constant_id = Some(self.next()?);
801            }
802            other => {
803                let level = match other {
804                    // Block decorations show up everywhere and we don't
805                    // really care about them, so to prevent log spam
806                    // we demote them to debug level.
807                    spirv::Decoration::Block => log::Level::Debug,
808                    _ => log::Level::Warn,
809                };
810
811                log::log!(level, "Unknown decoration {other:?}");
812                for _ in base_words + 1..inst.wc {
813                    let _var = self.next()?;
814                }
815            }
816        }
817        Ok(())
818    }
819
820    /// Return the Naga [`Expression`] to use in `body_idx` to refer to the SPIR-V result `id`.
821    ///
822    /// Ideally, we would just have a map from each SPIR-V instruction id to the
823    /// [`Handle`] for the Naga [`Expression`] we generated for it.
824    /// Unfortunately, SPIR-V and Naga IR are different enough that such a
825    /// straightforward relationship isn't possible.
826    ///
827    /// In SPIR-V, an instruction's result id can be used by any instruction
828    /// dominated by that instruction. In Naga, an [`Expression`] is only in
829    /// scope for the remainder of its [`Block`]. In pseudocode:
830    ///
831    /// ```ignore
832    ///     loop {
833    ///         a = f();
834    ///         g(a);
835    ///         break;
836    ///     }
837    ///     h(a);
838    /// ```
839    ///
840    /// Suppose the calls to `f`, `g`, and `h` are SPIR-V instructions. In
841    /// SPIR-V, both the `g` and `h` instructions are allowed to refer to `a`,
842    /// because the loop body, including `f`, dominates both of them.
843    ///
844    /// But if `a` is a Naga [`Expression`], its scope ends at the end of the
845    /// block it's evaluated in: the loop body. Thus, while the [`Expression`]
846    /// we generate for `g` can refer to `a`, the one we generate for `h`
847    /// cannot.
848    ///
849    /// Instead, the SPIR-V front end must generate Naga IR like this:
850    ///
851    /// ```ignore
852    ///     var temp; // INTRODUCED
853    ///     loop {
854    ///         a = f();
855    ///         g(a);
856    ///         temp = a; // INTRODUCED
857    ///     }
858    ///     h(temp); // ADJUSTED
859    /// ```
860    ///
861    /// In other words, where `a` is in scope, [`Expression`]s can refer to it
862    /// directly; but once it is out of scope, we need to spill it to a
863    /// temporary and refer to that instead.
864    ///
865    /// Given a SPIR-V expression `id` and the index `body_idx` of the [body]
866    /// that wants to refer to it:
867    ///
868    /// - If the Naga [`Expression`] we generated for `id` is in scope in
869    ///   `body_idx`, then we simply return its `Handle<Expression>`.
870    ///
871    /// - Otherwise, introduce a new [`LocalVariable`], and add an entry to
872    ///   [`BlockContext::phis`] to arrange for `id`'s value to be spilled to
873    ///   it. Then emit a fresh [`Load`] of that temporary variable for use in
874    ///   `body_idx`'s block, and return its `Handle`.
875    ///
876    /// The SPIR-V domination rule ensures that the introduced [`LocalVariable`]
877    /// will always have been initialized before it is used.
878    ///
879    /// `lookup` must be the [`LookupExpression`] for `id`.
880    ///
881    /// `body_idx` argument must be the index of the [`Body`] that hopes to use
882    /// `id`'s [`Expression`].
883    ///
884    /// [`Expression`]: crate::Expression
885    /// [`Handle`]: crate::Handle
886    /// [`Block`]: crate::Block
887    /// [body]: BlockContext::bodies
888    /// [`LocalVariable`]: crate::LocalVariable
889    /// [`Load`]: crate::Expression::Load
890    fn get_expr_handle(
891        &self,
892        id: spirv::Word,
893        lookup: &LookupExpression,
894        ctx: &mut BlockContext,
895        emitter: &mut crate::proc::Emitter,
896        block: &mut crate::Block,
897        body_idx: BodyIndex,
898    ) -> Handle<crate::Expression> {
899        // What `Body` was `id` defined in?
900        let expr_body_idx = ctx
901            .body_for_label
902            .get(&lookup.block_id)
903            .copied()
904            .unwrap_or(0);
905
906        // Don't need to do a load/store if the expression is in the main body
907        // or if the expression is in the same body as where the query was
908        // requested. The body_idx might actually not be the final one if a loop
909        // or conditional occurs but in those cases we know that the new body
910        // will be a subscope of the body that was passed so we can still reuse
911        // the handle and not issue a load/store.
912        if is_parent(body_idx, expr_body_idx, ctx) {
913            lookup.handle
914        } else {
915            // Add a temporary variable of the same type which will be used to
916            // store the original expression and used in the current block
917            let ty = self.lookup_type[&lookup.type_id].handle;
918            let local = ctx.local_arena.append(
919                crate::LocalVariable {
920                    name: None,
921                    ty,
922                    init: None,
923                },
924                crate::Span::default(),
925            );
926
927            block.extend(emitter.finish(ctx.expressions));
928            let pointer = ctx.expressions.append(
929                crate::Expression::LocalVariable(local),
930                crate::Span::default(),
931            );
932            emitter.start(ctx.expressions);
933            let expr = ctx
934                .expressions
935                .append(crate::Expression::Load { pointer }, crate::Span::default());
936
937            // Add a slightly odd entry to the phi table, so that while `id`'s
938            // `Expression` is still in scope, the usual phi processing will
939            // spill its value to `local`, where we can find it later.
940            //
941            // This pretends that the block in which `id` is defined is the
942            // predecessor of some other block with a phi in it that cites id as
943            // one of its sources, and uses `local` as its variable. There is no
944            // such phi, but nobody needs to know that.
945            ctx.phis.push(PhiExpression {
946                local,
947                expressions: vec![(id, lookup.block_id)],
948            });
949
950            expr
951        }
952    }
953
954    fn parse_expr_unary_op(
955        &mut self,
956        ctx: &mut BlockContext,
957        emitter: &mut crate::proc::Emitter,
958        block: &mut crate::Block,
959        block_id: spirv::Word,
960        body_idx: usize,
961        op: crate::UnaryOperator,
962    ) -> Result<(), Error> {
963        let start = self.data_offset;
964        let result_type_id = self.next()?;
965        let result_id = self.next()?;
966        let p_id = self.next()?;
967
968        let p_lexp = self.lookup_expression.lookup(p_id)?;
969        let handle = self.get_expr_handle(p_id, p_lexp, ctx, emitter, block, body_idx);
970
971        let expr = crate::Expression::Unary { op, expr: handle };
972        self.lookup_expression.insert(
973            result_id,
974            LookupExpression {
975                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
976                type_id: result_type_id,
977                block_id,
978            },
979        );
980        Ok(())
981    }
982
983    fn parse_expr_binary_op(
984        &mut self,
985        ctx: &mut BlockContext,
986        emitter: &mut crate::proc::Emitter,
987        block: &mut crate::Block,
988        block_id: spirv::Word,
989        body_idx: usize,
990        op: crate::BinaryOperator,
991    ) -> Result<(), Error> {
992        let start = self.data_offset;
993        let result_type_id = self.next()?;
994        let result_id = self.next()?;
995        let p1_id = self.next()?;
996        let p2_id = self.next()?;
997
998        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
999        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1000        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1001        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1002
1003        let expr = crate::Expression::Binary { op, left, right };
1004        self.lookup_expression.insert(
1005            result_id,
1006            LookupExpression {
1007                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1008                type_id: result_type_id,
1009                block_id,
1010            },
1011        );
1012        Ok(())
1013    }
1014
1015    /// A more complicated version of the unary op,
1016    /// where we force the operand to have the same type as the result.
1017    fn parse_expr_unary_op_sign_adjusted(
1018        &mut self,
1019        ctx: &mut BlockContext,
1020        emitter: &mut crate::proc::Emitter,
1021        block: &mut crate::Block,
1022        block_id: spirv::Word,
1023        body_idx: usize,
1024        op: crate::UnaryOperator,
1025    ) -> Result<(), Error> {
1026        let start = self.data_offset;
1027        let result_type_id = self.next()?;
1028        let result_id = self.next()?;
1029        let p1_id = self.next()?;
1030        let span = self.span_from_with_op(start);
1031
1032        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1033        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1034
1035        let result_lookup_ty = self.lookup_type.lookup(result_type_id)?;
1036        let kind = ctx.module.types[result_lookup_ty.handle]
1037            .inner
1038            .scalar_kind()
1039            .unwrap();
1040
1041        let expr = crate::Expression::Unary {
1042            op,
1043            expr: if p1_lexp.type_id == result_type_id {
1044                left
1045            } else {
1046                ctx.expressions.append(
1047                    crate::Expression::As {
1048                        expr: left,
1049                        kind,
1050                        convert: None,
1051                    },
1052                    span,
1053                )
1054            },
1055        };
1056
1057        self.lookup_expression.insert(
1058            result_id,
1059            LookupExpression {
1060                handle: ctx.expressions.append(expr, span),
1061                type_id: result_type_id,
1062                block_id,
1063            },
1064        );
1065        Ok(())
1066    }
1067
1068    /// A more complicated version of the binary op,
1069    /// where we force the operand to have the same type as the result.
1070    /// This is mostly needed for "i++" and "i--" coming from GLSL.
1071    #[allow(clippy::too_many_arguments)]
1072    fn parse_expr_binary_op_sign_adjusted(
1073        &mut self,
1074        ctx: &mut BlockContext,
1075        emitter: &mut crate::proc::Emitter,
1076        block: &mut crate::Block,
1077        block_id: spirv::Word,
1078        body_idx: usize,
1079        op: crate::BinaryOperator,
1080        // For arithmetic operations, we need the sign of operands to match the result.
1081        // For boolean operations, however, the operands need to match the signs, but
1082        // result is always different - a boolean.
1083        anchor: SignAnchor,
1084    ) -> Result<(), Error> {
1085        let start = self.data_offset;
1086        let result_type_id = self.next()?;
1087        let result_id = self.next()?;
1088        let p1_id = self.next()?;
1089        let p2_id = self.next()?;
1090        let span = self.span_from_with_op(start);
1091
1092        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1093        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1094        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1095        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1096
1097        let expected_type_id = match anchor {
1098            SignAnchor::Result => result_type_id,
1099            SignAnchor::Operand => p1_lexp.type_id,
1100        };
1101        let expected_lookup_ty = self.lookup_type.lookup(expected_type_id)?;
1102        let kind = ctx.module.types[expected_lookup_ty.handle]
1103            .inner
1104            .scalar_kind()
1105            .unwrap();
1106
1107        let expr = crate::Expression::Binary {
1108            op,
1109            left: if p1_lexp.type_id == expected_type_id {
1110                left
1111            } else {
1112                ctx.expressions.append(
1113                    crate::Expression::As {
1114                        expr: left,
1115                        kind,
1116                        convert: None,
1117                    },
1118                    span,
1119                )
1120            },
1121            right: if p2_lexp.type_id == expected_type_id {
1122                right
1123            } else {
1124                ctx.expressions.append(
1125                    crate::Expression::As {
1126                        expr: right,
1127                        kind,
1128                        convert: None,
1129                    },
1130                    span,
1131                )
1132            },
1133        };
1134
1135        self.lookup_expression.insert(
1136            result_id,
1137            LookupExpression {
1138                handle: ctx.expressions.append(expr, span),
1139                type_id: result_type_id,
1140                block_id,
1141            },
1142        );
1143        Ok(())
1144    }
1145
1146    /// A version of the binary op where one or both of the arguments might need to be casted to a
1147    /// specific integer kind (unsigned or signed), used for operations like OpINotEqual or
1148    /// OpUGreaterThan.
1149    #[allow(clippy::too_many_arguments)]
1150    fn parse_expr_int_comparison(
1151        &mut self,
1152        ctx: &mut BlockContext,
1153        emitter: &mut crate::proc::Emitter,
1154        block: &mut crate::Block,
1155        block_id: spirv::Word,
1156        body_idx: usize,
1157        op: crate::BinaryOperator,
1158        kind: crate::ScalarKind,
1159    ) -> Result<(), Error> {
1160        let start = self.data_offset;
1161        let result_type_id = self.next()?;
1162        let result_id = self.next()?;
1163        let p1_id = self.next()?;
1164        let p2_id = self.next()?;
1165        let span = self.span_from_with_op(start);
1166
1167        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1168        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1169        let p1_lookup_ty = self.lookup_type.lookup(p1_lexp.type_id)?;
1170        let p1_kind = ctx.module.types[p1_lookup_ty.handle]
1171            .inner
1172            .scalar_kind()
1173            .unwrap();
1174        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1175        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1176        let p2_lookup_ty = self.lookup_type.lookup(p2_lexp.type_id)?;
1177        let p2_kind = ctx.module.types[p2_lookup_ty.handle]
1178            .inner
1179            .scalar_kind()
1180            .unwrap();
1181
1182        let expr = crate::Expression::Binary {
1183            op,
1184            left: if p1_kind == kind {
1185                left
1186            } else {
1187                ctx.expressions.append(
1188                    crate::Expression::As {
1189                        expr: left,
1190                        kind,
1191                        convert: None,
1192                    },
1193                    span,
1194                )
1195            },
1196            right: if p2_kind == kind {
1197                right
1198            } else {
1199                ctx.expressions.append(
1200                    crate::Expression::As {
1201                        expr: right,
1202                        kind,
1203                        convert: None,
1204                    },
1205                    span,
1206                )
1207            },
1208        };
1209
1210        self.lookup_expression.insert(
1211            result_id,
1212            LookupExpression {
1213                handle: ctx.expressions.append(expr, span),
1214                type_id: result_type_id,
1215                block_id,
1216            },
1217        );
1218        Ok(())
1219    }
1220
1221    fn parse_expr_shift_op(
1222        &mut self,
1223        ctx: &mut BlockContext,
1224        emitter: &mut crate::proc::Emitter,
1225        block: &mut crate::Block,
1226        block_id: spirv::Word,
1227        body_idx: usize,
1228        op: crate::BinaryOperator,
1229    ) -> Result<(), Error> {
1230        let start = self.data_offset;
1231        let result_type_id = self.next()?;
1232        let result_id = self.next()?;
1233        let p1_id = self.next()?;
1234        let p2_id = self.next()?;
1235
1236        let span = self.span_from_with_op(start);
1237
1238        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1239        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1240        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1241        let p2_handle = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1242        // convert the shift to Uint
1243        let right = ctx.expressions.append(
1244            crate::Expression::As {
1245                expr: p2_handle,
1246                kind: crate::ScalarKind::Uint,
1247                convert: None,
1248            },
1249            span,
1250        );
1251
1252        let expr = crate::Expression::Binary { op, left, right };
1253        self.lookup_expression.insert(
1254            result_id,
1255            LookupExpression {
1256                handle: ctx.expressions.append(expr, span),
1257                type_id: result_type_id,
1258                block_id,
1259            },
1260        );
1261        Ok(())
1262    }
1263
1264    fn parse_expr_derivative(
1265        &mut self,
1266        ctx: &mut BlockContext,
1267        emitter: &mut crate::proc::Emitter,
1268        block: &mut crate::Block,
1269        block_id: spirv::Word,
1270        body_idx: usize,
1271        (axis, ctrl): (crate::DerivativeAxis, crate::DerivativeControl),
1272    ) -> Result<(), Error> {
1273        let start = self.data_offset;
1274        let result_type_id = self.next()?;
1275        let result_id = self.next()?;
1276        let arg_id = self.next()?;
1277
1278        let arg_lexp = self.lookup_expression.lookup(arg_id)?;
1279        let arg_handle = self.get_expr_handle(arg_id, arg_lexp, ctx, emitter, block, body_idx);
1280
1281        let expr = crate::Expression::Derivative {
1282            axis,
1283            ctrl,
1284            expr: arg_handle,
1285        };
1286        self.lookup_expression.insert(
1287            result_id,
1288            LookupExpression {
1289                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1290                type_id: result_type_id,
1291                block_id,
1292            },
1293        );
1294        Ok(())
1295    }
1296
1297    #[allow(clippy::too_many_arguments)]
1298    fn insert_composite(
1299        &self,
1300        root_expr: Handle<crate::Expression>,
1301        root_type_id: spirv::Word,
1302        object_expr: Handle<crate::Expression>,
1303        selections: &[spirv::Word],
1304        type_arena: &UniqueArena<crate::Type>,
1305        expressions: &mut Arena<crate::Expression>,
1306        span: crate::Span,
1307    ) -> Result<Handle<crate::Expression>, Error> {
1308        let selection = match selections.first() {
1309            Some(&index) => index,
1310            None => return Ok(object_expr),
1311        };
1312        let root_span = expressions.get_span(root_expr);
1313        let root_lookup = self.lookup_type.lookup(root_type_id)?;
1314
1315        let (count, child_type_id) = match type_arena[root_lookup.handle].inner {
1316            crate::TypeInner::Struct { ref members, .. } => {
1317                let child_member = self
1318                    .lookup_member
1319                    .get(&(root_lookup.handle, selection))
1320                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1321                (members.len(), child_member.type_id)
1322            }
1323            crate::TypeInner::Array { size, .. } => {
1324                let size = match size {
1325                    crate::ArraySize::Constant(size) => size.get(),
1326                    crate::ArraySize::Pending(_) => {
1327                        unreachable!();
1328                    }
1329                    // A runtime sized array is not a composite type
1330                    crate::ArraySize::Dynamic => {
1331                        return Err(Error::InvalidAccessType(root_type_id))
1332                    }
1333                };
1334
1335                let child_type_id = root_lookup
1336                    .base_id
1337                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1338
1339                (size as usize, child_type_id)
1340            }
1341            crate::TypeInner::Vector { size, .. }
1342            | crate::TypeInner::Matrix { columns: size, .. } => {
1343                let child_type_id = root_lookup
1344                    .base_id
1345                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1346                (size as usize, child_type_id)
1347            }
1348            _ => return Err(Error::InvalidAccessType(root_type_id)),
1349        };
1350
1351        let mut components = Vec::with_capacity(count);
1352        for index in 0..count as u32 {
1353            let expr = expressions.append(
1354                crate::Expression::AccessIndex {
1355                    base: root_expr,
1356                    index,
1357                },
1358                if index == selection { span } else { root_span },
1359            );
1360            components.push(expr);
1361        }
1362        components[selection as usize] = self.insert_composite(
1363            components[selection as usize],
1364            child_type_id,
1365            object_expr,
1366            &selections[1..],
1367            type_arena,
1368            expressions,
1369            span,
1370        )?;
1371
1372        Ok(expressions.append(
1373            crate::Expression::Compose {
1374                ty: root_lookup.handle,
1375                components,
1376            },
1377            span,
1378        ))
1379    }
1380
1381    /// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`].
1382    ///
1383    /// Return a [`Handle`] for a Naga [`Expression`] that holds the value of
1384    /// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it
1385    /// is a pointer.
1386    ///
1387    /// This may entail spilling `pointer_id`'s value to a temporary:
1388    /// see [`get_expr_handle`]'s documentation.
1389    ///
1390    /// [`Expression`]: crate::Expression
1391    /// [`Type`]: crate::Type
1392    /// [`Handle`]: crate::Handle
1393    /// [`get_expr_handle`]: Frontend::get_expr_handle
1394    fn get_exp_and_base_ty_handles(
1395        &self,
1396        pointer_id: spirv::Word,
1397        ctx: &mut BlockContext,
1398        emitter: &mut crate::proc::Emitter,
1399        block: &mut crate::Block,
1400        body_idx: usize,
1401    ) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {
1402        log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
1403        let p_lexp_handle;
1404        let p_lexp_ty_id;
1405        {
1406            let lexp = self.lookup_expression.lookup(pointer_id)?;
1407            p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);
1408            p_lexp_ty_id = lexp.type_id;
1409        };
1410
1411        log::trace!("\t\t\tlooking up pointer type {pointer_id:?}");
1412        let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
1413        let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
1414
1415        log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}");
1416        let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
1417
1418        Ok((p_lexp_handle, p_base_ty.handle))
1419    }
1420
1421    #[allow(clippy::too_many_arguments)]
1422    fn parse_atomic_expr_with_value(
1423        &mut self,
1424        inst: Instruction,
1425        emitter: &mut crate::proc::Emitter,
1426        ctx: &mut BlockContext,
1427        block: &mut crate::Block,
1428        block_id: spirv::Word,
1429        body_idx: usize,
1430        atomic_function: crate::AtomicFunction,
1431    ) -> Result<(), Error> {
1432        inst.expect(7)?;
1433        let start = self.data_offset;
1434        let result_type_id = self.next()?;
1435        let result_id = self.next()?;
1436        let pointer_id = self.next()?;
1437        let _scope_id = self.next()?;
1438        let _memory_semantics_id = self.next()?;
1439        let value_id = self.next()?;
1440        let span = self.span_from_with_op(start);
1441
1442        let (p_lexp_handle, p_base_ty_handle) =
1443            self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;
1444
1445        log::trace!("\t\t\tlooking up value expr {value_id:?}");
1446        let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;
1447
1448        block.extend(emitter.finish(ctx.expressions));
1449        // Create an expression for our result
1450        let r_lexp_handle = {
1451            let expr = crate::Expression::AtomicResult {
1452                ty: p_base_ty_handle,
1453                comparison: false,
1454            };
1455            let handle = ctx.expressions.append(expr, span);
1456            self.lookup_expression.insert(
1457                result_id,
1458                LookupExpression {
1459                    handle,
1460                    type_id: result_type_id,
1461                    block_id,
1462                },
1463            );
1464            handle
1465        };
1466        emitter.start(ctx.expressions);
1467
1468        // Create a statement for the op itself
1469        let stmt = crate::Statement::Atomic {
1470            pointer: p_lexp_handle,
1471            fun: atomic_function,
1472            value: v_lexp_handle,
1473            result: Some(r_lexp_handle),
1474        };
1475        block.push(stmt, span);
1476
1477        // Store any associated global variables so we can upgrade their types later
1478        self.record_atomic_access(ctx, p_lexp_handle)?;
1479
1480        Ok(())
1481    }
1482
1483    fn make_expression_storage(
1484        &mut self,
1485        globals: &Arena<crate::GlobalVariable>,
1486        constants: &Arena<crate::Constant>,
1487        overrides: &Arena<crate::Override>,
1488    ) -> Arena<crate::Expression> {
1489        let mut expressions = Arena::new();
1490        #[allow(clippy::panic)]
1491        {
1492            assert!(self.lookup_expression.is_empty());
1493        }
1494        // register global variables
1495        for (&id, var) in self.lookup_variable.iter() {
1496            let span = globals.get_span(var.handle);
1497            let handle = expressions.append(crate::Expression::GlobalVariable(var.handle), span);
1498            self.lookup_expression.insert(
1499                id,
1500                LookupExpression {
1501                    type_id: var.type_id,
1502                    handle,
1503                    // Setting this to an invalid id will cause get_expr_handle
1504                    // to default to the main body making sure no load/stores
1505                    // are added.
1506                    block_id: 0,
1507                },
1508            );
1509        }
1510        // register constants
1511        for (&id, con) in self.lookup_constant.iter() {
1512            let (expr, span) = match con.inner {
1513                Constant::Constant(c) => (crate::Expression::Constant(c), constants.get_span(c)),
1514                Constant::Override(o) => (crate::Expression::Override(o), overrides.get_span(o)),
1515            };
1516            let handle = expressions.append(expr, span);
1517            self.lookup_expression.insert(
1518                id,
1519                LookupExpression {
1520                    type_id: con.type_id,
1521                    handle,
1522                    // Setting this to an invalid id will cause get_expr_handle
1523                    // to default to the main body making sure no load/stores
1524                    // are added.
1525                    block_id: 0,
1526                },
1527            );
1528        }
1529        // done
1530        expressions
1531    }
1532
1533    fn switch(&mut self, state: ModuleState, op: spirv::Op) -> Result<(), Error> {
1534        if state < self.state {
1535            Err(Error::UnsupportedInstruction(self.state, op))
1536        } else {
1537            self.state = state;
1538            Ok(())
1539        }
1540    }
1541
1542    /// Walk the statement tree and patch it in the following cases:
1543    /// 1. Function call targets are replaced by `deferred_function_calls` map
1544    fn patch_statements(
1545        &mut self,
1546        statements: &mut crate::Block,
1547        expressions: &mut Arena<crate::Expression>,
1548        fun_parameter_sampling: &mut [image::SamplingFlags],
1549    ) -> Result<(), Error> {
1550        use crate::Statement as S;
1551        let mut i = 0usize;
1552        while i < statements.len() {
1553            match statements[i] {
1554                S::Emit(_) => {}
1555                S::Block(ref mut block) => {
1556                    self.patch_statements(block, expressions, fun_parameter_sampling)?;
1557                }
1558                S::If {
1559                    condition: _,
1560                    ref mut accept,
1561                    ref mut reject,
1562                } => {
1563                    self.patch_statements(reject, expressions, fun_parameter_sampling)?;
1564                    self.patch_statements(accept, expressions, fun_parameter_sampling)?;
1565                }
1566                S::Switch {
1567                    selector: _,
1568                    ref mut cases,
1569                } => {
1570                    for case in cases.iter_mut() {
1571                        self.patch_statements(&mut case.body, expressions, fun_parameter_sampling)?;
1572                    }
1573                }
1574                S::Loop {
1575                    ref mut body,
1576                    ref mut continuing,
1577                    break_if: _,
1578                } => {
1579                    self.patch_statements(body, expressions, fun_parameter_sampling)?;
1580                    self.patch_statements(continuing, expressions, fun_parameter_sampling)?;
1581                }
1582                S::Break
1583                | S::Continue
1584                | S::Return { .. }
1585                | S::Kill
1586                | S::ControlBarrier(_)
1587                | S::MemoryBarrier(_)
1588                | S::Store { .. }
1589                | S::ImageStore { .. }
1590                | S::Atomic { .. }
1591                | S::ImageAtomic { .. }
1592                | S::RayQuery { .. }
1593                | S::SubgroupBallot { .. }
1594                | S::SubgroupCollectiveOperation { .. }
1595                | S::SubgroupGather { .. } => {}
1596                S::Call {
1597                    function: ref mut callee,
1598                    ref arguments,
1599                    ..
1600                } => {
1601                    let fun_id = self.deferred_function_calls[callee.index()];
1602                    let fun_lookup = self.lookup_function.lookup(fun_id)?;
1603                    *callee = fun_lookup.handle;
1604
1605                    // Patch sampling flags
1606                    for (arg_index, arg) in arguments.iter().enumerate() {
1607                        let flags = match fun_lookup.parameters_sampling.get(arg_index) {
1608                            Some(&flags) if !flags.is_empty() => flags,
1609                            _ => continue,
1610                        };
1611
1612                        match expressions[*arg] {
1613                            crate::Expression::GlobalVariable(handle) => {
1614                                if let Some(sampling) = self.handle_sampling.get_mut(&handle) {
1615                                    *sampling |= flags
1616                                }
1617                            }
1618                            crate::Expression::FunctionArgument(i) => {
1619                                fun_parameter_sampling[i as usize] |= flags;
1620                            }
1621                            ref other => return Err(Error::InvalidGlobalVar(other.clone())),
1622                        }
1623                    }
1624                }
1625                S::WorkGroupUniformLoad { .. } => unreachable!(),
1626            }
1627            i += 1;
1628        }
1629        Ok(())
1630    }
1631
1632    fn patch_function(
1633        &mut self,
1634        handle: Option<Handle<crate::Function>>,
1635        fun: &mut crate::Function,
1636    ) -> Result<(), Error> {
1637        // Note: this search is a bit unfortunate
1638        let (fun_id, mut parameters_sampling) = match handle {
1639            Some(h) => {
1640                let (&fun_id, lookup) = self
1641                    .lookup_function
1642                    .iter_mut()
1643                    .find(|&(_, ref lookup)| lookup.handle == h)
1644                    .unwrap();
1645                (fun_id, mem::take(&mut lookup.parameters_sampling))
1646            }
1647            None => (0, Vec::new()),
1648        };
1649
1650        for (_, expr) in fun.expressions.iter_mut() {
1651            if let crate::Expression::CallResult(ref mut function) = *expr {
1652                let fun_id = self.deferred_function_calls[function.index()];
1653                *function = self.lookup_function.lookup(fun_id)?.handle;
1654            }
1655        }
1656
1657        self.patch_statements(
1658            &mut fun.body,
1659            &mut fun.expressions,
1660            &mut parameters_sampling,
1661        )?;
1662
1663        if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {
1664            lookup.parameters_sampling = parameters_sampling;
1665        }
1666        Ok(())
1667    }
1668
1669    pub fn parse(mut self) -> Result<crate::Module, Error> {
1670        let mut module = {
1671            if self.next()? != spirv::MAGIC_NUMBER {
1672                return Err(Error::InvalidHeader);
1673            }
1674            let version_raw = self.next()?;
1675            let generator = self.next()?;
1676            let _bound = self.next()?;
1677            let _schema = self.next()?;
1678            log::debug!("Generated by {generator} version {version_raw:x}");
1679            crate::Module::default()
1680        };
1681
1682        self.layouter.clear();
1683        self.dummy_functions = Arena::new();
1684        self.lookup_function.clear();
1685        self.function_call_graph.clear();
1686
1687        loop {
1688            use spirv::Op;
1689
1690            let inst = match self.next_inst() {
1691                Ok(inst) => inst,
1692                Err(Error::IncompleteData) => break,
1693                Err(other) => return Err(other),
1694            };
1695            log::debug!("\t{:?} [{}]", inst.op, inst.wc);
1696
1697            match inst.op {
1698                Op::Capability => self.parse_capability(inst),
1699                Op::Extension => self.parse_extension(inst),
1700                Op::ExtInstImport => self.parse_ext_inst_import(inst),
1701                Op::MemoryModel => self.parse_memory_model(inst),
1702                Op::EntryPoint => self.parse_entry_point(inst),
1703                Op::ExecutionMode => self.parse_execution_mode(inst),
1704                Op::String => self.parse_string(inst),
1705                Op::Source => self.parse_source(inst),
1706                Op::SourceExtension => self.parse_source_extension(inst),
1707                Op::Name => self.parse_name(inst),
1708                Op::MemberName => self.parse_member_name(inst),
1709                Op::ModuleProcessed => self.parse_module_processed(inst),
1710                Op::Decorate => self.parse_decorate(inst),
1711                Op::MemberDecorate => self.parse_member_decorate(inst),
1712                Op::TypeVoid => self.parse_type_void(inst),
1713                Op::TypeBool => self.parse_type_bool(inst, &mut module),
1714                Op::TypeInt => self.parse_type_int(inst, &mut module),
1715                Op::TypeFloat => self.parse_type_float(inst, &mut module),
1716                Op::TypeVector => self.parse_type_vector(inst, &mut module),
1717                Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),
1718                Op::TypeFunction => self.parse_type_function(inst),
1719                Op::TypePointer => self.parse_type_pointer(inst, &mut module),
1720                Op::TypeArray => self.parse_type_array(inst, &mut module),
1721                Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),
1722                Op::TypeStruct => self.parse_type_struct(inst, &mut module),
1723                Op::TypeImage => self.parse_type_image(inst, &mut module),
1724                Op::TypeSampledImage => self.parse_type_sampled_image(inst),
1725                Op::TypeSampler => self.parse_type_sampler(inst, &mut module),
1726                Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),
1727                Op::ConstantComposite | Op::SpecConstantComposite => {
1728                    self.parse_composite_constant(inst, &mut module)
1729                }
1730                Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),
1731                Op::ConstantTrue | Op::SpecConstantTrue => {
1732                    self.parse_bool_constant(inst, true, &mut module)
1733                }
1734                Op::ConstantFalse | Op::SpecConstantFalse => {
1735                    self.parse_bool_constant(inst, false, &mut module)
1736                }
1737                Op::Variable => self.parse_global_variable(inst, &mut module),
1738                Op::Function => {
1739                    self.switch(ModuleState::Function, inst.op)?;
1740                    inst.expect(5)?;
1741                    self.parse_function(&mut module)
1742                }
1743                _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), //TODO
1744            }?;
1745        }
1746
1747        if !self.upgrade_atomics.is_empty() {
1748            log::debug!("Upgrading atomic pointers...");
1749            module.upgrade_atomics(&self.upgrade_atomics)?;
1750        }
1751
1752        // Do entry point specific processing after all functions are parsed so that we can
1753        // cull unused problematic builtins of gl_PerVertex.
1754        for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {
1755            self.process_entry_point(&mut module, ep, fun_id)?;
1756        }
1757
1758        log::debug!("Patching...");
1759        {
1760            let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
1761                .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;
1762            nodes.reverse(); // we need dominated first
1763            let mut functions = mem::take(&mut module.functions);
1764            for fun_id in nodes {
1765                if fun_id > !(functions.len() as u32) {
1766                    // skip all the fake IDs registered for the entry points
1767                    continue;
1768                }
1769                let lookup = self.lookup_function.get_mut(&fun_id).unwrap();
1770                // take out the function from the old array
1771                let fun = mem::take(&mut functions[lookup.handle]);
1772                // add it to the newly formed arena, and adjust the lookup
1773                lookup.handle = module
1774                    .functions
1775                    .append(fun, functions.get_span(lookup.handle));
1776            }
1777        }
1778        // patch all the functions
1779        for (handle, fun) in module.functions.iter_mut() {
1780            self.patch_function(Some(handle), fun)?;
1781        }
1782        for ep in module.entry_points.iter_mut() {
1783            self.patch_function(None, &mut ep.function)?;
1784        }
1785
1786        // Check all the images and samplers to have consistent comparison property.
1787        for (handle, flags) in self.handle_sampling.drain() {
1788            if !image::patch_comparison_type(
1789                flags,
1790                module.global_variables.get_mut(handle),
1791                &mut module.types,
1792            ) {
1793                return Err(Error::InconsistentComparisonSampling(handle));
1794            }
1795        }
1796
1797        if !self.future_decor.is_empty() {
1798            log::debug!("Unused item decorations: {:?}", self.future_decor);
1799            self.future_decor.clear();
1800        }
1801        if !self.future_member_decor.is_empty() {
1802            log::debug!("Unused member decorations: {:?}", self.future_member_decor);
1803            self.future_member_decor.clear();
1804        }
1805
1806        Ok(module)
1807    }
1808
1809    fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {
1810        self.switch(ModuleState::Capability, inst.op)?;
1811        inst.expect(2)?;
1812        let capability = self.next()?;
1813        let cap =
1814            spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;
1815        if !SUPPORTED_CAPABILITIES.contains(&cap) {
1816            if self.options.strict_capabilities {
1817                return Err(Error::UnsupportedCapability(cap));
1818            } else {
1819                log::warn!("Unknown capability {cap:?}");
1820            }
1821        }
1822        Ok(())
1823    }
1824
1825    fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {
1826        self.switch(ModuleState::Extension, inst.op)?;
1827        inst.expect_at_least(2)?;
1828        let (name, left) = self.next_string(inst.wc - 1)?;
1829        if left != 0 {
1830            return Err(Error::InvalidOperand);
1831        }
1832        if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {
1833            return Err(Error::UnsupportedExtension(name));
1834        }
1835        Ok(())
1836    }
1837
1838    fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {
1839        self.switch(ModuleState::Extension, inst.op)?;
1840        inst.expect_at_least(3)?;
1841        let result_id = self.next()?;
1842        let (name, left) = self.next_string(inst.wc - 2)?;
1843        if left != 0 {
1844            return Err(Error::InvalidOperand);
1845        }
1846        if !SUPPORTED_EXT_SETS.contains(&name.as_str()) {
1847            return Err(Error::UnsupportedExtSet(name));
1848        }
1849        self.ext_glsl_id = Some(result_id);
1850        Ok(())
1851    }
1852
1853    fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {
1854        self.switch(ModuleState::MemoryModel, inst.op)?;
1855        inst.expect(3)?;
1856        let _addressing_model = self.next()?;
1857        let _memory_model = self.next()?;
1858        Ok(())
1859    }
1860
1861    fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {
1862        self.switch(ModuleState::EntryPoint, inst.op)?;
1863        inst.expect_at_least(4)?;
1864        let exec_model = self.next()?;
1865        let exec_model = spirv::ExecutionModel::from_u32(exec_model)
1866            .ok_or(Error::UnsupportedExecutionModel(exec_model))?;
1867        let function_id = self.next()?;
1868        let (name, left) = self.next_string(inst.wc - 3)?;
1869        let ep = EntryPoint {
1870            stage: match exec_model {
1871                spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,
1872                spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,
1873                spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,
1874                spirv::ExecutionModel::TaskEXT => crate::ShaderStage::Task,
1875                spirv::ExecutionModel::MeshEXT => crate::ShaderStage::Mesh,
1876                _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),
1877            },
1878            name,
1879            early_depth_test: None,
1880            workgroup_size: [0; 3],
1881            variable_ids: self.data.by_ref().take(left as usize).collect(),
1882        };
1883        self.lookup_entry_point.insert(function_id, ep);
1884        Ok(())
1885    }
1886
1887    fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
1888        use spirv::ExecutionMode;
1889
1890        self.switch(ModuleState::ExecutionMode, inst.op)?;
1891        inst.expect_at_least(3)?;
1892
1893        let ep_id = self.next()?;
1894        let mode_id = self.next()?;
1895        let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();
1896
1897        let ep = self
1898            .lookup_entry_point
1899            .get_mut(&ep_id)
1900            .ok_or(Error::InvalidId(ep_id))?;
1901        let mode =
1902            ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;
1903
1904        match mode {
1905            ExecutionMode::EarlyFragmentTests => {
1906                ep.early_depth_test = Some(crate::EarlyDepthTest::Force);
1907            }
1908            ExecutionMode::DepthUnchanged => {
1909                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1910                    if let &mut crate::EarlyDepthTest::Allow {
1911                        ref mut conservative,
1912                    } = early_depth_test
1913                    {
1914                        *conservative = crate::ConservativeDepth::Unchanged;
1915                    }
1916                } else {
1917                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1918                        conservative: crate::ConservativeDepth::Unchanged,
1919                    });
1920                }
1921            }
1922            ExecutionMode::DepthGreater => {
1923                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1924                    if let &mut crate::EarlyDepthTest::Allow {
1925                        ref mut conservative,
1926                    } = early_depth_test
1927                    {
1928                        *conservative = crate::ConservativeDepth::GreaterEqual;
1929                    }
1930                } else {
1931                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1932                        conservative: crate::ConservativeDepth::GreaterEqual,
1933                    });
1934                }
1935            }
1936            ExecutionMode::DepthLess => {
1937                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1938                    if let &mut crate::EarlyDepthTest::Allow {
1939                        ref mut conservative,
1940                    } = early_depth_test
1941                    {
1942                        *conservative = crate::ConservativeDepth::LessEqual;
1943                    }
1944                } else {
1945                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1946                        conservative: crate::ConservativeDepth::LessEqual,
1947                    });
1948                }
1949            }
1950            ExecutionMode::DepthReplacing => {
1951                // Ignored because it can be deduced from the IR.
1952            }
1953            ExecutionMode::OriginUpperLeft => {
1954                // Ignored because the other option (OriginLowerLeft) is not valid in Vulkan mode.
1955            }
1956            ExecutionMode::LocalSize => {
1957                ep.workgroup_size = [args[0], args[1], args[2]];
1958            }
1959            _ => {
1960                return Err(Error::UnsupportedExecutionMode(mode_id));
1961            }
1962        }
1963
1964        Ok(())
1965    }
1966
1967    fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {
1968        self.switch(ModuleState::Source, inst.op)?;
1969        inst.expect_at_least(3)?;
1970        let _id = self.next()?;
1971        let (_name, _) = self.next_string(inst.wc - 2)?;
1972        Ok(())
1973    }
1974
1975    fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {
1976        self.switch(ModuleState::Source, inst.op)?;
1977        for _ in 1..inst.wc {
1978            let _ = self.next()?;
1979        }
1980        Ok(())
1981    }
1982
1983    fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {
1984        self.switch(ModuleState::Source, inst.op)?;
1985        inst.expect_at_least(2)?;
1986        let (_name, _) = self.next_string(inst.wc - 1)?;
1987        Ok(())
1988    }
1989
1990    fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {
1991        self.switch(ModuleState::Name, inst.op)?;
1992        inst.expect_at_least(3)?;
1993        let id = self.next()?;
1994        let (name, left) = self.next_string(inst.wc - 2)?;
1995        if left != 0 {
1996            return Err(Error::InvalidOperand);
1997        }
1998        self.future_decor.entry(id).or_default().name = Some(name);
1999        Ok(())
2000    }
2001
2002    fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {
2003        self.switch(ModuleState::Name, inst.op)?;
2004        inst.expect_at_least(4)?;
2005        let id = self.next()?;
2006        let member = self.next()?;
2007        let (name, left) = self.next_string(inst.wc - 3)?;
2008        if left != 0 {
2009            return Err(Error::InvalidOperand);
2010        }
2011
2012        self.future_member_decor
2013            .entry((id, member))
2014            .or_default()
2015            .name = Some(name);
2016        Ok(())
2017    }
2018
2019    fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {
2020        self.switch(ModuleState::Name, inst.op)?;
2021        inst.expect_at_least(2)?;
2022        let (_info, left) = self.next_string(inst.wc - 1)?;
2023        //Note: string is ignored
2024        if left != 0 {
2025            return Err(Error::InvalidOperand);
2026        }
2027        Ok(())
2028    }
2029
2030    fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2031        self.switch(ModuleState::Annotation, inst.op)?;
2032        inst.expect_at_least(3)?;
2033        let id = self.next()?;
2034        let mut dec = self.future_decor.remove(&id).unwrap_or_default();
2035        self.next_decoration(inst, 2, &mut dec)?;
2036        self.future_decor.insert(id, dec);
2037        Ok(())
2038    }
2039
2040    fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2041        self.switch(ModuleState::Annotation, inst.op)?;
2042        inst.expect_at_least(4)?;
2043        let id = self.next()?;
2044        let member = self.next()?;
2045
2046        let mut dec = self
2047            .future_member_decor
2048            .remove(&(id, member))
2049            .unwrap_or_default();
2050        self.next_decoration(inst, 3, &mut dec)?;
2051        self.future_member_decor.insert((id, member), dec);
2052        Ok(())
2053    }
2054
2055    fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {
2056        self.switch(ModuleState::Type, inst.op)?;
2057        inst.expect(2)?;
2058        let id = self.next()?;
2059        self.lookup_void_type = Some(id);
2060        Ok(())
2061    }
2062
2063    fn parse_type_bool(
2064        &mut self,
2065        inst: Instruction,
2066        module: &mut crate::Module,
2067    ) -> Result<(), Error> {
2068        let start = self.data_offset;
2069        self.switch(ModuleState::Type, inst.op)?;
2070        inst.expect(2)?;
2071        let id = self.next()?;
2072        let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);
2073        self.lookup_type.insert(
2074            id,
2075            LookupType {
2076                handle: module.types.insert(
2077                    crate::Type {
2078                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2079                        inner,
2080                    },
2081                    self.span_from_with_op(start),
2082                ),
2083                base_id: None,
2084            },
2085        );
2086        Ok(())
2087    }
2088
2089    fn parse_type_int(
2090        &mut self,
2091        inst: Instruction,
2092        module: &mut crate::Module,
2093    ) -> Result<(), Error> {
2094        let start = self.data_offset;
2095        self.switch(ModuleState::Type, inst.op)?;
2096        inst.expect(4)?;
2097        let id = self.next()?;
2098        let width = self.next()?;
2099        let sign = self.next()?;
2100        let inner = crate::TypeInner::Scalar(crate::Scalar {
2101            kind: match sign {
2102                0 => crate::ScalarKind::Uint,
2103                1 => crate::ScalarKind::Sint,
2104                _ => return Err(Error::InvalidSign(sign)),
2105            },
2106            width: map_width(width)?,
2107        });
2108        self.lookup_type.insert(
2109            id,
2110            LookupType {
2111                handle: module.types.insert(
2112                    crate::Type {
2113                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2114                        inner,
2115                    },
2116                    self.span_from_with_op(start),
2117                ),
2118                base_id: None,
2119            },
2120        );
2121        Ok(())
2122    }
2123
2124    fn parse_type_float(
2125        &mut self,
2126        inst: Instruction,
2127        module: &mut crate::Module,
2128    ) -> Result<(), Error> {
2129        let start = self.data_offset;
2130        self.switch(ModuleState::Type, inst.op)?;
2131        inst.expect(3)?;
2132        let id = self.next()?;
2133        let width = self.next()?;
2134        let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));
2135        self.lookup_type.insert(
2136            id,
2137            LookupType {
2138                handle: module.types.insert(
2139                    crate::Type {
2140                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2141                        inner,
2142                    },
2143                    self.span_from_with_op(start),
2144                ),
2145                base_id: None,
2146            },
2147        );
2148        Ok(())
2149    }
2150
2151    fn parse_type_vector(
2152        &mut self,
2153        inst: Instruction,
2154        module: &mut crate::Module,
2155    ) -> Result<(), Error> {
2156        let start = self.data_offset;
2157        self.switch(ModuleState::Type, inst.op)?;
2158        inst.expect(4)?;
2159        let id = self.next()?;
2160        let type_id = self.next()?;
2161        let type_lookup = self.lookup_type.lookup(type_id)?;
2162        let scalar = match module.types[type_lookup.handle].inner {
2163            crate::TypeInner::Scalar(scalar) => scalar,
2164            _ => return Err(Error::InvalidInnerType(type_id)),
2165        };
2166        let component_count = self.next()?;
2167        let inner = crate::TypeInner::Vector {
2168            size: map_vector_size(component_count)?,
2169            scalar,
2170        };
2171        self.lookup_type.insert(
2172            id,
2173            LookupType {
2174                handle: module.types.insert(
2175                    crate::Type {
2176                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2177                        inner,
2178                    },
2179                    self.span_from_with_op(start),
2180                ),
2181                base_id: Some(type_id),
2182            },
2183        );
2184        Ok(())
2185    }
2186
2187    fn parse_type_matrix(
2188        &mut self,
2189        inst: Instruction,
2190        module: &mut crate::Module,
2191    ) -> Result<(), Error> {
2192        let start = self.data_offset;
2193        self.switch(ModuleState::Type, inst.op)?;
2194        inst.expect(4)?;
2195        let id = self.next()?;
2196        let vector_type_id = self.next()?;
2197        let num_columns = self.next()?;
2198        let decor = self.future_decor.remove(&id);
2199
2200        let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;
2201        let inner = match module.types[vector_type_lookup.handle].inner {
2202            crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {
2203                columns: map_vector_size(num_columns)?,
2204                rows: size,
2205                scalar,
2206            },
2207            _ => return Err(Error::InvalidInnerType(vector_type_id)),
2208        };
2209
2210        self.lookup_type.insert(
2211            id,
2212            LookupType {
2213                handle: module.types.insert(
2214                    crate::Type {
2215                        name: decor.and_then(|dec| dec.name),
2216                        inner,
2217                    },
2218                    self.span_from_with_op(start),
2219                ),
2220                base_id: Some(vector_type_id),
2221            },
2222        );
2223        Ok(())
2224    }
2225
2226    fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {
2227        self.switch(ModuleState::Type, inst.op)?;
2228        inst.expect_at_least(3)?;
2229        let id = self.next()?;
2230        let return_type_id = self.next()?;
2231        let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();
2232        self.lookup_function_type.insert(
2233            id,
2234            LookupFunctionType {
2235                parameter_type_ids,
2236                return_type_id,
2237            },
2238        );
2239        Ok(())
2240    }
2241
2242    fn parse_type_pointer(
2243        &mut self,
2244        inst: Instruction,
2245        module: &mut crate::Module,
2246    ) -> Result<(), Error> {
2247        let start = self.data_offset;
2248        self.switch(ModuleState::Type, inst.op)?;
2249        inst.expect(4)?;
2250        let id = self.next()?;
2251        let storage_class = self.next()?;
2252        let type_id = self.next()?;
2253
2254        let decor = self.future_decor.remove(&id);
2255        let base_lookup_ty = self.lookup_type.lookup(type_id)?;
2256        let base_inner = &module.types[base_lookup_ty.handle].inner;
2257
2258        let space = if let Some(space) = base_inner.pointer_space() {
2259            space
2260        } else if self
2261            .lookup_storage_buffer_types
2262            .contains_key(&base_lookup_ty.handle)
2263        {
2264            crate::AddressSpace::Storage {
2265                access: crate::StorageAccess::default(),
2266            }
2267        } else {
2268            match map_storage_class(storage_class)? {
2269                ExtendedClass::Global(space) => space,
2270                ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,
2271            }
2272        };
2273
2274        // We don't support pointers to runtime-sized arrays in the `Uniform`
2275        // storage class with the `BufferBlock` decoration. Runtime-sized arrays
2276        // should be in the StorageBuffer class.
2277        if let crate::TypeInner::Array {
2278            size: crate::ArraySize::Dynamic,
2279            ..
2280        } = *base_inner
2281        {
2282            match space {
2283                crate::AddressSpace::Storage { .. } => {}
2284                _ => {
2285                    return Err(Error::UnsupportedRuntimeArrayStorageClass);
2286                }
2287            }
2288        }
2289
2290        // Don't bother with pointer stuff for `Handle` types.
2291        let lookup_ty = if space == crate::AddressSpace::Handle {
2292            base_lookup_ty.clone()
2293        } else {
2294            LookupType {
2295                handle: module.types.insert(
2296                    crate::Type {
2297                        name: decor.and_then(|dec| dec.name),
2298                        inner: crate::TypeInner::Pointer {
2299                            base: base_lookup_ty.handle,
2300                            space,
2301                        },
2302                    },
2303                    self.span_from_with_op(start),
2304                ),
2305                base_id: Some(type_id),
2306            }
2307        };
2308        self.lookup_type.insert(id, lookup_ty);
2309        Ok(())
2310    }
2311
2312    fn parse_type_array(
2313        &mut self,
2314        inst: Instruction,
2315        module: &mut crate::Module,
2316    ) -> Result<(), Error> {
2317        let start = self.data_offset;
2318        self.switch(ModuleState::Type, inst.op)?;
2319        inst.expect(4)?;
2320        let id = self.next()?;
2321        let type_id = self.next()?;
2322        let length_id = self.next()?;
2323        let length_const = self.lookup_constant.lookup(length_id)?;
2324
2325        let size = resolve_constant(module.to_ctx(), &length_const.inner)
2326            .and_then(NonZeroU32::new)
2327            .ok_or(Error::InvalidArraySize(length_id))?;
2328
2329        let decor = self.future_decor.remove(&id).unwrap_or_default();
2330        let base = self.lookup_type.lookup(type_id)?.handle;
2331
2332        self.layouter.update(module.to_ctx()).unwrap();
2333
2334        // HACK if the underlying type is an image or a sampler, let's assume
2335        //      that we're dealing with a binding-array
2336        //
2337        // Note that it's not a strictly correct assumption, but rather a trade
2338        // off caused by an impedance mismatch between SPIR-V's and Naga's type
2339        // systems - Naga distinguishes between arrays and binding-arrays via
2340        // types (i.e. both kinds of arrays are just different types), while
2341        // SPIR-V distinguishes between them through usage - e.g. given:
2342        //
2343        // ```
2344        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
2345        // %uint_256 = OpConstant %uint 256
2346        // %image_array = OpTypeArray %image %uint_256
2347        // ```
2348        //
2349        // ```
2350        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
2351        // %uint_256 = OpConstant %uint 256
2352        // %image_array = OpTypeArray %image %uint_256
2353        // %image_array_ptr = OpTypePointer UniformConstant %image_array
2354        // ```
2355        //
2356        // ... in the first case, `%image_array` should technically correspond
2357        // to `TypeInner::Array`, while in the second case it should say
2358        // `TypeInner::BindingArray` (kinda, depending on whether `%image_array`
2359        // is ever used as a freestanding type or rather always through the
2360        // pointer-indirection).
2361        //
2362        // Anyway, at the moment we don't support other kinds of image / sampler
2363        // arrays than those binding-based, so this assumption is pretty safe
2364        // for now.
2365        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2366            module.types[base].inner
2367        {
2368            crate::TypeInner::BindingArray {
2369                base,
2370                size: crate::ArraySize::Constant(size),
2371            }
2372        } else {
2373            crate::TypeInner::Array {
2374                base,
2375                size: crate::ArraySize::Constant(size),
2376                stride: match decor.array_stride {
2377                    Some(stride) => stride.get(),
2378                    None => self.layouter[base].to_stride(),
2379                },
2380            }
2381        };
2382
2383        self.lookup_type.insert(
2384            id,
2385            LookupType {
2386                handle: module.types.insert(
2387                    crate::Type {
2388                        name: decor.name,
2389                        inner,
2390                    },
2391                    self.span_from_with_op(start),
2392                ),
2393                base_id: Some(type_id),
2394            },
2395        );
2396        Ok(())
2397    }
2398
2399    fn parse_type_runtime_array(
2400        &mut self,
2401        inst: Instruction,
2402        module: &mut crate::Module,
2403    ) -> Result<(), Error> {
2404        let start = self.data_offset;
2405        self.switch(ModuleState::Type, inst.op)?;
2406        inst.expect(3)?;
2407        let id = self.next()?;
2408        let type_id = self.next()?;
2409
2410        let decor = self.future_decor.remove(&id).unwrap_or_default();
2411        let base = self.lookup_type.lookup(type_id)?.handle;
2412
2413        self.layouter.update(module.to_ctx()).unwrap();
2414
2415        // HACK same case as in `parse_type_array()`
2416        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2417            module.types[base].inner
2418        {
2419            crate::TypeInner::BindingArray {
2420                base: self.lookup_type.lookup(type_id)?.handle,
2421                size: crate::ArraySize::Dynamic,
2422            }
2423        } else {
2424            crate::TypeInner::Array {
2425                base: self.lookup_type.lookup(type_id)?.handle,
2426                size: crate::ArraySize::Dynamic,
2427                stride: match decor.array_stride {
2428                    Some(stride) => stride.get(),
2429                    None => self.layouter[base].to_stride(),
2430                },
2431            }
2432        };
2433
2434        self.lookup_type.insert(
2435            id,
2436            LookupType {
2437                handle: module.types.insert(
2438                    crate::Type {
2439                        name: decor.name,
2440                        inner,
2441                    },
2442                    self.span_from_with_op(start),
2443                ),
2444                base_id: Some(type_id),
2445            },
2446        );
2447        Ok(())
2448    }
2449
2450    fn parse_type_struct(
2451        &mut self,
2452        inst: Instruction,
2453        module: &mut crate::Module,
2454    ) -> Result<(), Error> {
2455        let start = self.data_offset;
2456        self.switch(ModuleState::Type, inst.op)?;
2457        inst.expect_at_least(2)?;
2458        let id = self.next()?;
2459        let parent_decor = self.future_decor.remove(&id);
2460        let is_storage_buffer = parent_decor
2461            .as_ref()
2462            .is_some_and(|decor| decor.storage_buffer);
2463
2464        self.layouter.update(module.to_ctx()).unwrap();
2465
2466        let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
2467        let mut member_lookups = Vec::with_capacity(members.capacity());
2468        let mut storage_access = crate::StorageAccess::empty();
2469        let mut span = 0;
2470        let mut alignment = Alignment::ONE;
2471        for i in 0..u32::from(inst.wc) - 2 {
2472            let type_id = self.next()?;
2473            let ty = self.lookup_type.lookup(type_id)?.handle;
2474            let decor = self
2475                .future_member_decor
2476                .remove(&(id, i))
2477                .unwrap_or_default();
2478
2479            storage_access |= decor.flags.to_storage_access();
2480
2481            member_lookups.push(LookupMember {
2482                type_id,
2483                row_major: decor.matrix_major == Some(Majority::Row),
2484            });
2485
2486            let member_alignment = self.layouter[ty].alignment;
2487            span = member_alignment.round_up(span);
2488            alignment = member_alignment.max(alignment);
2489
2490            let binding = decor.io_binding().ok();
2491            if let Some(offset) = decor.offset {
2492                span = offset;
2493            }
2494            let offset = span;
2495
2496            span += self.layouter[ty].size;
2497
2498            let inner = &module.types[ty].inner;
2499            if let crate::TypeInner::Matrix {
2500                columns,
2501                rows,
2502                scalar,
2503            } = *inner
2504            {
2505                if let Some(stride) = decor.matrix_stride {
2506                    let expected_stride = Alignment::from(rows) * scalar.width as u32;
2507                    if stride.get() != expected_stride {
2508                        return Err(Error::UnsupportedMatrixStride {
2509                            stride: stride.get(),
2510                            columns: columns as u8,
2511                            rows: rows as u8,
2512                            width: scalar.width,
2513                        });
2514                    }
2515                }
2516            }
2517
2518            members.push(crate::StructMember {
2519                name: decor.name,
2520                ty,
2521                binding,
2522                offset,
2523            });
2524        }
2525
2526        span = alignment.round_up(span);
2527
2528        let inner = crate::TypeInner::Struct { span, members };
2529
2530        let ty_handle = module.types.insert(
2531            crate::Type {
2532                name: parent_decor.and_then(|dec| dec.name),
2533                inner,
2534            },
2535            self.span_from_with_op(start),
2536        );
2537
2538        if is_storage_buffer {
2539            self.lookup_storage_buffer_types
2540                .insert(ty_handle, storage_access);
2541        }
2542        for (i, member_lookup) in member_lookups.into_iter().enumerate() {
2543            self.lookup_member
2544                .insert((ty_handle, i as u32), member_lookup);
2545        }
2546        self.lookup_type.insert(
2547            id,
2548            LookupType {
2549                handle: ty_handle,
2550                base_id: None,
2551            },
2552        );
2553        Ok(())
2554    }
2555
2556    fn parse_type_image(
2557        &mut self,
2558        inst: Instruction,
2559        module: &mut crate::Module,
2560    ) -> Result<(), Error> {
2561        let start = self.data_offset;
2562        self.switch(ModuleState::Type, inst.op)?;
2563        inst.expect(9)?;
2564
2565        let id = self.next()?;
2566        let sample_type_id = self.next()?;
2567        let dim = self.next()?;
2568        let is_depth = self.next()?;
2569        let is_array = self.next()? != 0;
2570        let is_msaa = self.next()? != 0;
2571        let is_sampled = self.next()?;
2572        let format = self.next()?;
2573
2574        let dim = map_image_dim(dim)?;
2575        let decor = self.future_decor.remove(&id).unwrap_or_default();
2576
2577        // ensure there is a type for texture coordinate without extra components
2578        module.types.insert(
2579            crate::Type {
2580                name: None,
2581                inner: {
2582                    let scalar = crate::Scalar::F32;
2583                    match dim.required_coordinate_size() {
2584                        None => crate::TypeInner::Scalar(scalar),
2585                        Some(size) => crate::TypeInner::Vector { size, scalar },
2586                    }
2587                },
2588            },
2589            Default::default(),
2590        );
2591
2592        let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;
2593        let kind = module.types[base_handle]
2594            .inner
2595            .scalar_kind()
2596            .ok_or(Error::InvalidImageBaseType(base_handle))?;
2597
2598        let inner = crate::TypeInner::Image {
2599            class: if is_depth == 1 {
2600                crate::ImageClass::Depth { multi: is_msaa }
2601            } else if format != 0 {
2602                crate::ImageClass::Storage {
2603                    format: map_image_format(format)?,
2604                    access: crate::StorageAccess::default(),
2605                }
2606            } else if is_sampled == 2 {
2607                return Err(Error::InvalidImageWriteType);
2608            } else {
2609                crate::ImageClass::Sampled {
2610                    kind,
2611                    multi: is_msaa,
2612                }
2613            },
2614            dim,
2615            arrayed: is_array,
2616        };
2617
2618        let handle = module.types.insert(
2619            crate::Type {
2620                name: decor.name,
2621                inner,
2622            },
2623            self.span_from_with_op(start),
2624        );
2625
2626        self.lookup_type.insert(
2627            id,
2628            LookupType {
2629                handle,
2630                base_id: Some(sample_type_id),
2631            },
2632        );
2633        Ok(())
2634    }
2635
2636    fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {
2637        self.switch(ModuleState::Type, inst.op)?;
2638        inst.expect(3)?;
2639        let id = self.next()?;
2640        let image_id = self.next()?;
2641        self.lookup_type.insert(
2642            id,
2643            LookupType {
2644                handle: self.lookup_type.lookup(image_id)?.handle,
2645                base_id: Some(image_id),
2646            },
2647        );
2648        Ok(())
2649    }
2650
2651    fn parse_type_sampler(
2652        &mut self,
2653        inst: Instruction,
2654        module: &mut crate::Module,
2655    ) -> Result<(), Error> {
2656        let start = self.data_offset;
2657        self.switch(ModuleState::Type, inst.op)?;
2658        inst.expect(2)?;
2659        let id = self.next()?;
2660        let decor = self.future_decor.remove(&id).unwrap_or_default();
2661        let handle = module.types.insert(
2662            crate::Type {
2663                name: decor.name,
2664                inner: crate::TypeInner::Sampler { comparison: false },
2665            },
2666            self.span_from_with_op(start),
2667        );
2668        self.lookup_type.insert(
2669            id,
2670            LookupType {
2671                handle,
2672                base_id: None,
2673            },
2674        );
2675        Ok(())
2676    }
2677
2678    fn parse_constant(
2679        &mut self,
2680        inst: Instruction,
2681        module: &mut crate::Module,
2682    ) -> Result<(), Error> {
2683        let start = self.data_offset;
2684        self.switch(ModuleState::Type, inst.op)?;
2685        inst.expect_at_least(4)?;
2686        let type_id = self.next()?;
2687        let id = self.next()?;
2688        let type_lookup = self.lookup_type.lookup(type_id)?;
2689        let ty = type_lookup.handle;
2690
2691        let literal = match module.types[ty].inner {
2692            crate::TypeInner::Scalar(crate::Scalar {
2693                kind: crate::ScalarKind::Uint,
2694                width,
2695            }) => {
2696                let low = self.next()?;
2697                match width {
2698                    4 => crate::Literal::U32(low),
2699                    8 => {
2700                        inst.expect(5)?;
2701                        let high = self.next()?;
2702                        crate::Literal::U64((u64::from(high) << 32) | u64::from(low))
2703                    }
2704                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2705                }
2706            }
2707            crate::TypeInner::Scalar(crate::Scalar {
2708                kind: crate::ScalarKind::Sint,
2709                width,
2710            }) => {
2711                let low = self.next()?;
2712                match width {
2713                    4 => crate::Literal::I32(low as i32),
2714                    8 => {
2715                        inst.expect(5)?;
2716                        let high = self.next()?;
2717                        crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)
2718                    }
2719                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2720                }
2721            }
2722            crate::TypeInner::Scalar(crate::Scalar {
2723                kind: crate::ScalarKind::Float,
2724                width,
2725            }) => {
2726                let low = self.next()?;
2727                match width {
2728                    // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Literal
2729                    // If a numeric type’s bit width is less than 32-bits, the value appears in the low-order bits of the word.
2730                    2 => crate::Literal::F16(f16::from_bits(low as u16)),
2731                    4 => crate::Literal::F32(f32::from_bits(low)),
2732                    8 => {
2733                        inst.expect(5)?;
2734                        let high = self.next()?;
2735                        crate::Literal::F64(f64::from_bits(
2736                            (u64::from(high) << 32) | u64::from(low),
2737                        ))
2738                    }
2739                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2740                }
2741            }
2742            _ => return Err(Error::UnsupportedType(type_lookup.handle)),
2743        };
2744
2745        let span = self.span_from_with_op(start);
2746
2747        let init = module
2748            .global_expressions
2749            .append(crate::Expression::Literal(literal), span);
2750
2751        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2752    }
2753
2754    fn parse_composite_constant(
2755        &mut self,
2756        inst: Instruction,
2757        module: &mut crate::Module,
2758    ) -> Result<(), Error> {
2759        let start = self.data_offset;
2760        self.switch(ModuleState::Type, inst.op)?;
2761        inst.expect_at_least(3)?;
2762        let type_id = self.next()?;
2763        let id = self.next()?;
2764
2765        let type_lookup = self.lookup_type.lookup(type_id)?;
2766        let ty = type_lookup.handle;
2767
2768        let mut components = Vec::with_capacity(inst.wc as usize - 3);
2769        for _ in 0..components.capacity() {
2770            let start = self.data_offset;
2771            let component_id = self.next()?;
2772            let span = self.span_from_with_op(start);
2773            let constant = self.lookup_constant.lookup(component_id)?;
2774            let expr = module
2775                .global_expressions
2776                .append(constant.inner.to_expr(), span);
2777            components.push(expr);
2778        }
2779
2780        let span = self.span_from_with_op(start);
2781
2782        let init = module
2783            .global_expressions
2784            .append(crate::Expression::Compose { ty, components }, span);
2785
2786        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2787    }
2788
2789    fn parse_null_constant(
2790        &mut self,
2791        inst: Instruction,
2792        module: &mut crate::Module,
2793    ) -> Result<(), Error> {
2794        let start = self.data_offset;
2795        self.switch(ModuleState::Type, inst.op)?;
2796        inst.expect(3)?;
2797        let type_id = self.next()?;
2798        let id = self.next()?;
2799        let span = self.span_from_with_op(start);
2800
2801        let type_lookup = self.lookup_type.lookup(type_id)?;
2802        let ty = type_lookup.handle;
2803
2804        let init = module
2805            .global_expressions
2806            .append(crate::Expression::ZeroValue(ty), span);
2807
2808        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2809    }
2810
2811    fn parse_bool_constant(
2812        &mut self,
2813        inst: Instruction,
2814        value: bool,
2815        module: &mut crate::Module,
2816    ) -> Result<(), Error> {
2817        let start = self.data_offset;
2818        self.switch(ModuleState::Type, inst.op)?;
2819        inst.expect(3)?;
2820        let type_id = self.next()?;
2821        let id = self.next()?;
2822        let span = self.span_from_with_op(start);
2823
2824        let type_lookup = self.lookup_type.lookup(type_id)?;
2825        let ty = type_lookup.handle;
2826
2827        let init = module.global_expressions.append(
2828            crate::Expression::Literal(crate::Literal::Bool(value)),
2829            span,
2830        );
2831
2832        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2833    }
2834
2835    fn insert_parsed_constant(
2836        &mut self,
2837        module: &mut crate::Module,
2838        id: u32,
2839        type_id: u32,
2840        ty: Handle<crate::Type>,
2841        init: Handle<crate::Expression>,
2842        span: crate::Span,
2843    ) -> Result<(), Error> {
2844        let decor = self.future_decor.remove(&id).unwrap_or_default();
2845
2846        let inner = if let Some(id) = decor.specialization_constant_id {
2847            let o = crate::Override {
2848                name: decor.name,
2849                id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),
2850                ty,
2851                init: Some(init),
2852            };
2853            Constant::Override(module.overrides.append(o, span))
2854        } else {
2855            let c = crate::Constant {
2856                name: decor.name,
2857                ty,
2858                init,
2859            };
2860            Constant::Constant(module.constants.append(c, span))
2861        };
2862
2863        self.lookup_constant
2864            .insert(id, LookupConstant { inner, type_id });
2865        Ok(())
2866    }
2867
2868    fn parse_global_variable(
2869        &mut self,
2870        inst: Instruction,
2871        module: &mut crate::Module,
2872    ) -> Result<(), Error> {
2873        let start = self.data_offset;
2874        self.switch(ModuleState::Type, inst.op)?;
2875        inst.expect_at_least(4)?;
2876        let type_id = self.next()?;
2877        let id = self.next()?;
2878        let storage_class = self.next()?;
2879        let init = if inst.wc > 4 {
2880            inst.expect(5)?;
2881            let start = self.data_offset;
2882            let init_id = self.next()?;
2883            let span = self.span_from_with_op(start);
2884            let lconst = self.lookup_constant.lookup(init_id)?;
2885            let expr = module
2886                .global_expressions
2887                .append(lconst.inner.to_expr(), span);
2888            Some(expr)
2889        } else {
2890            None
2891        };
2892        let span = self.span_from_with_op(start);
2893        let dec = self.future_decor.remove(&id).unwrap_or_default();
2894
2895        let original_ty = self.lookup_type.lookup(type_id)?.handle;
2896        let mut ty = original_ty;
2897
2898        if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
2899            ty = base;
2900        }
2901
2902        if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
2903            // Inside `parse_type_array()` we guess that an array of images or
2904            // samplers must be a binding array, and here we validate that guess
2905            if dec.desc_set.is_none() || dec.desc_index.is_none() {
2906                return Err(Error::NonBindingArrayOfImageOrSamplers);
2907            }
2908        }
2909
2910        if let crate::TypeInner::Image {
2911            dim,
2912            arrayed,
2913            class: crate::ImageClass::Storage { format, access: _ },
2914        } = module.types[ty].inner
2915        {
2916            // Storage image types in IR have to contain the access, but not in the SPIR-V.
2917            // The same image type in SPIR-V can be used (and has to be used) for multiple images.
2918            // So we copy the type out and apply the variable access decorations.
2919            let access = dec.flags.to_storage_access();
2920
2921            ty = module.types.insert(
2922                crate::Type {
2923                    name: None,
2924                    inner: crate::TypeInner::Image {
2925                        dim,
2926                        arrayed,
2927                        class: crate::ImageClass::Storage { format, access },
2928                    },
2929                },
2930                Default::default(),
2931            );
2932        }
2933
2934        let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
2935            Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
2936            None => map_storage_class(storage_class)?,
2937        };
2938
2939        let (inner, var) = match ext_class {
2940            ExtendedClass::Global(mut space) => {
2941                if let crate::AddressSpace::Storage { ref mut access } = space {
2942                    *access &= dec.flags.to_storage_access();
2943                }
2944                let var = crate::GlobalVariable {
2945                    binding: dec.resource_binding(),
2946                    name: dec.name,
2947                    space,
2948                    ty,
2949                    init,
2950                };
2951                (Variable::Global, var)
2952            }
2953            ExtendedClass::Input => {
2954                let binding = dec.io_binding()?;
2955                let mut unsigned_ty = ty;
2956                if let crate::Binding::BuiltIn(built_in) = binding {
2957                    let needs_inner_uint = match built_in {
2958                        crate::BuiltIn::BaseInstance
2959                        | crate::BuiltIn::BaseVertex
2960                        | crate::BuiltIn::InstanceIndex
2961                        | crate::BuiltIn::SampleIndex
2962                        | crate::BuiltIn::VertexIndex
2963                        | crate::BuiltIn::PrimitiveIndex
2964                        | crate::BuiltIn::LocalInvocationIndex => {
2965                            Some(crate::TypeInner::Scalar(crate::Scalar::U32))
2966                        }
2967                        crate::BuiltIn::GlobalInvocationId
2968                        | crate::BuiltIn::LocalInvocationId
2969                        | crate::BuiltIn::WorkGroupId
2970                        | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {
2971                            size: crate::VectorSize::Tri,
2972                            scalar: crate::Scalar::U32,
2973                        }),
2974                        crate::BuiltIn::Barycentric => Some(crate::TypeInner::Vector {
2975                            size: crate::VectorSize::Tri,
2976                            scalar: crate::Scalar::F32,
2977                        }),
2978                        _ => None,
2979                    };
2980                    if let (Some(inner), Some(crate::ScalarKind::Sint)) =
2981                        (needs_inner_uint, module.types[ty].inner.scalar_kind())
2982                    {
2983                        unsigned_ty = module
2984                            .types
2985                            .insert(crate::Type { name: None, inner }, Default::default());
2986                    }
2987                }
2988
2989                let var = crate::GlobalVariable {
2990                    name: dec.name.clone(),
2991                    space: crate::AddressSpace::Private,
2992                    binding: None,
2993                    ty,
2994                    init: None,
2995                };
2996
2997                let inner = Variable::Input(crate::FunctionArgument {
2998                    name: dec.name,
2999                    ty: unsigned_ty,
3000                    binding: Some(binding),
3001                });
3002                (inner, var)
3003            }
3004            ExtendedClass::Output => {
3005                // For output interface blocks, this would be a structure.
3006                let binding = dec.io_binding().ok();
3007                let init = match binding {
3008                    Some(crate::Binding::BuiltIn(built_in)) => {
3009                        match null::generate_default_built_in(
3010                            Some(built_in),
3011                            ty,
3012                            &mut module.global_expressions,
3013                            span,
3014                        ) {
3015                            Ok(handle) => Some(handle),
3016                            Err(e) => {
3017                                log::warn!("Failed to initialize output built-in: {e}");
3018                                None
3019                            }
3020                        }
3021                    }
3022                    Some(crate::Binding::Location { .. }) => None,
3023                    None => match module.types[ty].inner {
3024                        crate::TypeInner::Struct { ref members, .. } => {
3025                            let mut components = Vec::with_capacity(members.len());
3026                            for member in members.iter() {
3027                                let built_in = match member.binding {
3028                                    Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),
3029                                    _ => None,
3030                                };
3031                                let handle = null::generate_default_built_in(
3032                                    built_in,
3033                                    member.ty,
3034                                    &mut module.global_expressions,
3035                                    span,
3036                                )?;
3037                                components.push(handle);
3038                            }
3039                            Some(
3040                                module
3041                                    .global_expressions
3042                                    .append(crate::Expression::Compose { ty, components }, span),
3043                            )
3044                        }
3045                        _ => None,
3046                    },
3047                };
3048
3049                let var = crate::GlobalVariable {
3050                    name: dec.name,
3051                    space: crate::AddressSpace::Private,
3052                    binding: None,
3053                    ty,
3054                    init,
3055                };
3056                let inner = Variable::Output(crate::FunctionResult { ty, binding });
3057                (inner, var)
3058            }
3059        };
3060
3061        let handle = module.global_variables.append(var, span);
3062
3063        if module.types[ty].inner.can_comparison_sample(module) {
3064            log::debug!("\t\ttracking {handle:?} for sampling properties");
3065
3066            self.handle_sampling
3067                .insert(handle, image::SamplingFlags::empty());
3068        }
3069
3070        self.lookup_variable.insert(
3071            id,
3072            LookupVariable {
3073                inner,
3074                handle,
3075                type_id,
3076            },
3077        );
3078        Ok(())
3079    }
3080
3081    /// Record an atomic access to some component of a global variable.
3082    ///
3083    /// Given `handle`, an expression referring to a scalar that has had an
3084    /// atomic operation applied to it, descend into the expression, noting
3085    /// which global variable it ultimately refers to, and which struct fields
3086    /// of that global's value it accesses.
3087    ///
3088    /// Return the handle of the type of the expression.
3089    ///
3090    /// If the expression doesn't actually refer to something in a global
3091    /// variable, we can't upgrade its type in a way that Naga validation would
3092    /// pass, so reject the input instead.
3093    fn record_atomic_access(
3094        &mut self,
3095        ctx: &BlockContext,
3096        handle: Handle<crate::Expression>,
3097    ) -> Result<Handle<crate::Type>, Error> {
3098        log::debug!("\t\tlocating global variable in {handle:?}");
3099        match ctx.expressions[handle] {
3100            crate::Expression::Access { base, index } => {
3101                log::debug!("\t\t  access {handle:?} {index:?}");
3102                let ty = self.record_atomic_access(ctx, base)?;
3103                let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {
3104                    unreachable!("Atomic operations on Access expressions only work for arrays");
3105                };
3106                Ok(base)
3107            }
3108            crate::Expression::AccessIndex { base, index } => {
3109                log::debug!("\t\t  access index {handle:?} {index:?}");
3110                let ty = self.record_atomic_access(ctx, base)?;
3111                match ctx.module.types[ty].inner {
3112                    crate::TypeInner::Struct { ref members, .. } => {
3113                        let index = index as usize;
3114                        self.upgrade_atomics.insert_field(ty, index);
3115                        Ok(members[index].ty)
3116                    }
3117                    crate::TypeInner::Array { base, .. } => {
3118                        Ok(base)
3119                    }
3120                    _ => unreachable!("Atomic operations on AccessIndex expressions only work for structs and arrays"),
3121                }
3122            }
3123            crate::Expression::GlobalVariable(h) => {
3124                log::debug!("\t\t  found {h:?}");
3125                self.upgrade_atomics.insert_global(h);
3126                Ok(ctx.module.global_variables[h].ty)
3127            }
3128            _ => Err(Error::AtomicUpgradeError(
3129                crate::front::atomic_upgrade::Error::GlobalVariableMissing,
3130            )),
3131        }
3132    }
3133}
3134
3135fn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {
3136    let constant = match *constant {
3137        Constant::Constant(constant) => constant,
3138        Constant::Override(_) => return None,
3139    };
3140    match gctx.global_expressions[gctx.constants[constant].init] {
3141        crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),
3142        crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),
3143        _ => None,
3144    }
3145}
3146
3147pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {
3148    if data.len() % 4 != 0 {
3149        return Err(Error::IncompleteData);
3150    }
3151
3152    let words = data
3153        .chunks(4)
3154        .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
3155    Frontend::new(words, options).parse()
3156}
3157
3158/// Helper function to check if `child` is in the scope of `parent`
3159fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
3160    loop {
3161        if child == parent {
3162            // The child is in the scope parent
3163            break true;
3164        } else if child == 0 {
3165            // Searched finished at the root the child isn't in the parent's body
3166            break false;
3167        }
3168
3169        child = block_ctx.bodies[child].parent;
3170    }
3171}
3172
3173#[cfg(test)]
3174mod test {
3175    use alloc::vec;
3176
3177    #[test]
3178    fn parse() {
3179        let bin = vec![
3180            // Magic number.           Version number: 1.0.
3181            0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
3182            // Generator number: 0.    Bound: 0.
3183            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved word: 0.
3184            0x00, 0x00, 0x00, 0x00, // OpMemoryModel.          Logical.
3185            0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // GLSL450.
3186            0x01, 0x00, 0x00, 0x00,
3187        ];
3188        let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
3189    }
3190}