Skip to main content

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