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 kind = ctx.module.types[result_lookup_ty.handle]
1080            .inner
1081            .scalar_kind()
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.append(
1090                    crate::Expression::As {
1091                        expr: left,
1092                        kind,
1093                        convert: None,
1094                    },
1095                    span,
1096                )
1097            },
1098        };
1099
1100        self.lookup_expression.insert(
1101            result_id,
1102            LookupExpression {
1103                handle: ctx.expressions.append(expr, span),
1104                type_id: result_type_id,
1105                block_id,
1106            },
1107        );
1108        Ok(())
1109    }
1110
1111    /// A more complicated version of the binary op,
1112    /// where we force the operand to have the same type as the result.
1113    /// This is mostly needed for "i++" and "i--" coming from GLSL.
1114    #[allow(clippy::too_many_arguments)]
1115    fn parse_expr_binary_op_sign_adjusted(
1116        &mut self,
1117        ctx: &mut BlockContext,
1118        emitter: &mut crate::proc::Emitter,
1119        block: &mut crate::Block,
1120        block_id: spirv::Word,
1121        body_idx: usize,
1122        op: crate::BinaryOperator,
1123        // For arithmetic operations, we need the sign of operands to match the result.
1124        // For boolean operations, however, the operands need to match the signs, but
1125        // result is always different - a boolean.
1126        anchor: SignAnchor,
1127    ) -> Result<(), Error> {
1128        let start = self.data_offset;
1129        let result_type_id = self.next()?;
1130        let result_id = self.next()?;
1131        let p1_id = self.next()?;
1132        let p2_id = self.next()?;
1133        let span = self.span_from_with_op(start);
1134
1135        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1136        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1137        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1138        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1139
1140        let expected_type_id = match anchor {
1141            SignAnchor::Result => result_type_id,
1142            SignAnchor::Operand => p1_lexp.type_id,
1143        };
1144        let expected_lookup_ty = self.lookup_type.lookup(expected_type_id)?;
1145        let kind = ctx.module.types[expected_lookup_ty.handle]
1146            .inner
1147            .scalar_kind()
1148            .unwrap();
1149
1150        let expr = crate::Expression::Binary {
1151            op,
1152            left: if p1_lexp.type_id == expected_type_id {
1153                left
1154            } else {
1155                ctx.expressions.append(
1156                    crate::Expression::As {
1157                        expr: left,
1158                        kind,
1159                        convert: None,
1160                    },
1161                    span,
1162                )
1163            },
1164            right: if p2_lexp.type_id == expected_type_id {
1165                right
1166            } else {
1167                ctx.expressions.append(
1168                    crate::Expression::As {
1169                        expr: right,
1170                        kind,
1171                        convert: None,
1172                    },
1173                    span,
1174                )
1175            },
1176        };
1177
1178        self.lookup_expression.insert(
1179            result_id,
1180            LookupExpression {
1181                handle: ctx.expressions.append(expr, span),
1182                type_id: result_type_id,
1183                block_id,
1184            },
1185        );
1186        Ok(())
1187    }
1188
1189    /// A version of the binary op where one or both of the arguments might need to be casted to a
1190    /// specific integer kind (unsigned or signed), used for operations like OpINotEqual or
1191    /// OpUGreaterThan.
1192    #[allow(clippy::too_many_arguments)]
1193    fn parse_expr_int_comparison(
1194        &mut self,
1195        ctx: &mut BlockContext,
1196        emitter: &mut crate::proc::Emitter,
1197        block: &mut crate::Block,
1198        block_id: spirv::Word,
1199        body_idx: usize,
1200        op: crate::BinaryOperator,
1201        kind: crate::ScalarKind,
1202    ) -> Result<(), Error> {
1203        let start = self.data_offset;
1204        let result_type_id = self.next()?;
1205        let result_id = self.next()?;
1206        let p1_id = self.next()?;
1207        let p2_id = self.next()?;
1208        let span = self.span_from_with_op(start);
1209
1210        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1211        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1212        let p1_lookup_ty = self.lookup_type.lookup(p1_lexp.type_id)?;
1213        let p1_kind = ctx.module.types[p1_lookup_ty.handle]
1214            .inner
1215            .scalar_kind()
1216            .unwrap();
1217        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1218        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1219        let p2_lookup_ty = self.lookup_type.lookup(p2_lexp.type_id)?;
1220        let p2_kind = ctx.module.types[p2_lookup_ty.handle]
1221            .inner
1222            .scalar_kind()
1223            .unwrap();
1224
1225        let expr = crate::Expression::Binary {
1226            op,
1227            left: if p1_kind == kind {
1228                left
1229            } else {
1230                ctx.expressions.append(
1231                    crate::Expression::As {
1232                        expr: left,
1233                        kind,
1234                        convert: None,
1235                    },
1236                    span,
1237                )
1238            },
1239            right: if p2_kind == kind {
1240                right
1241            } else {
1242                ctx.expressions.append(
1243                    crate::Expression::As {
1244                        expr: right,
1245                        kind,
1246                        convert: None,
1247                    },
1248                    span,
1249                )
1250            },
1251        };
1252
1253        self.lookup_expression.insert(
1254            result_id,
1255            LookupExpression {
1256                handle: ctx.expressions.append(expr, span),
1257                type_id: result_type_id,
1258                block_id,
1259            },
1260        );
1261        Ok(())
1262    }
1263
1264    fn parse_expr_shift_op(
1265        &mut self,
1266        ctx: &mut BlockContext,
1267        emitter: &mut crate::proc::Emitter,
1268        block: &mut crate::Block,
1269        block_id: spirv::Word,
1270        body_idx: usize,
1271        op: crate::BinaryOperator,
1272    ) -> Result<(), Error> {
1273        let start = self.data_offset;
1274        let result_type_id = self.next()?;
1275        let result_id = self.next()?;
1276        let p1_id = self.next()?;
1277        let p2_id = self.next()?;
1278
1279        let span = self.span_from_with_op(start);
1280
1281        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1282        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1283        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1284        let p2_handle = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1285        // convert the shift to Uint
1286        let right = ctx.expressions.append(
1287            crate::Expression::As {
1288                expr: p2_handle,
1289                kind: crate::ScalarKind::Uint,
1290                convert: None,
1291            },
1292            span,
1293        );
1294
1295        let expr = crate::Expression::Binary { op, left, right };
1296        self.lookup_expression.insert(
1297            result_id,
1298            LookupExpression {
1299                handle: ctx.expressions.append(expr, span),
1300                type_id: result_type_id,
1301                block_id,
1302            },
1303        );
1304        Ok(())
1305    }
1306
1307    fn parse_expr_derivative(
1308        &mut self,
1309        ctx: &mut BlockContext,
1310        emitter: &mut crate::proc::Emitter,
1311        block: &mut crate::Block,
1312        block_id: spirv::Word,
1313        body_idx: usize,
1314        (axis, ctrl): (crate::DerivativeAxis, crate::DerivativeControl),
1315    ) -> Result<(), Error> {
1316        let start = self.data_offset;
1317        let result_type_id = self.next()?;
1318        let result_id = self.next()?;
1319        let arg_id = self.next()?;
1320
1321        let arg_lexp = self.lookup_expression.lookup(arg_id)?;
1322        let arg_handle = self.get_expr_handle(arg_id, arg_lexp, ctx, emitter, block, body_idx);
1323
1324        let expr = crate::Expression::Derivative {
1325            axis,
1326            ctrl,
1327            expr: arg_handle,
1328        };
1329        self.lookup_expression.insert(
1330            result_id,
1331            LookupExpression {
1332                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1333                type_id: result_type_id,
1334                block_id,
1335            },
1336        );
1337        Ok(())
1338    }
1339
1340    #[allow(clippy::too_many_arguments)]
1341    fn insert_composite(
1342        &self,
1343        root_expr: Handle<crate::Expression>,
1344        root_type_id: spirv::Word,
1345        object_expr: Handle<crate::Expression>,
1346        selections: &[spirv::Word],
1347        type_arena: &UniqueArena<crate::Type>,
1348        expressions: &mut Arena<crate::Expression>,
1349        span: crate::Span,
1350    ) -> Result<Handle<crate::Expression>, Error> {
1351        let selection = match selections.first() {
1352            Some(&index) => index,
1353            None => return Ok(object_expr),
1354        };
1355        let root_span = expressions.get_span(root_expr);
1356        let root_lookup = self.lookup_type.lookup(root_type_id)?;
1357
1358        let (count, child_type_id) = match type_arena[root_lookup.handle].inner {
1359            crate::TypeInner::Struct { ref members, .. } => {
1360                let child_member = self
1361                    .lookup_member
1362                    .get(&(root_lookup.handle, selection))
1363                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1364                (members.len(), child_member.type_id)
1365            }
1366            crate::TypeInner::Array { size, .. } => {
1367                let size = match size {
1368                    crate::ArraySize::Constant(size) => size.get(),
1369                    crate::ArraySize::Pending(_) => {
1370                        unreachable!();
1371                    }
1372                    // A runtime sized array is not a composite type
1373                    crate::ArraySize::Dynamic => {
1374                        return Err(Error::InvalidAccessType(root_type_id))
1375                    }
1376                };
1377
1378                let child_type_id = root_lookup
1379                    .base_id
1380                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1381
1382                (size as usize, child_type_id)
1383            }
1384            crate::TypeInner::Vector { size, .. }
1385            | crate::TypeInner::Matrix { columns: size, .. } => {
1386                let child_type_id = root_lookup
1387                    .base_id
1388                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1389                (size as usize, child_type_id)
1390            }
1391            _ => return Err(Error::InvalidAccessType(root_type_id)),
1392        };
1393
1394        let mut components = Vec::with_capacity(count);
1395        for index in 0..count as u32 {
1396            let expr = expressions.append(
1397                crate::Expression::AccessIndex {
1398                    base: root_expr,
1399                    index,
1400                },
1401                if index == selection { span } else { root_span },
1402            );
1403            components.push(expr);
1404        }
1405        components[selection as usize] = self.insert_composite(
1406            components[selection as usize],
1407            child_type_id,
1408            object_expr,
1409            &selections[1..],
1410            type_arena,
1411            expressions,
1412            span,
1413        )?;
1414
1415        Ok(expressions.append(
1416            crate::Expression::Compose {
1417                ty: root_lookup.handle,
1418                components,
1419            },
1420            span,
1421        ))
1422    }
1423
1424    /// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`].
1425    ///
1426    /// Return a [`Handle`] for a Naga [`Expression`] that holds the value of
1427    /// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it
1428    /// is a pointer.
1429    ///
1430    /// This may entail spilling `pointer_id`'s value to a temporary:
1431    /// see [`get_expr_handle`]'s documentation.
1432    ///
1433    /// [`Expression`]: crate::Expression
1434    /// [`Type`]: crate::Type
1435    /// [`Handle`]: crate::Handle
1436    /// [`get_expr_handle`]: Frontend::get_expr_handle
1437    fn get_exp_and_base_ty_handles(
1438        &self,
1439        pointer_id: spirv::Word,
1440        ctx: &mut BlockContext,
1441        emitter: &mut crate::proc::Emitter,
1442        block: &mut crate::Block,
1443        body_idx: usize,
1444    ) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {
1445        log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
1446        let p_lexp_handle;
1447        let p_lexp_ty_id;
1448        {
1449            let lexp = self.lookup_expression.lookup(pointer_id)?;
1450            p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);
1451            p_lexp_ty_id = lexp.type_id;
1452        };
1453
1454        log::trace!("\t\t\tlooking up pointer type {pointer_id:?}");
1455        let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
1456        let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
1457
1458        log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}");
1459        let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
1460
1461        Ok((p_lexp_handle, p_base_ty.handle))
1462    }
1463
1464    #[allow(clippy::too_many_arguments)]
1465    fn parse_atomic_expr_with_value(
1466        &mut self,
1467        inst: Instruction,
1468        emitter: &mut crate::proc::Emitter,
1469        ctx: &mut BlockContext,
1470        block: &mut crate::Block,
1471        block_id: spirv::Word,
1472        body_idx: usize,
1473        atomic_function: crate::AtomicFunction,
1474    ) -> Result<(), Error> {
1475        inst.expect(7)?;
1476        let start = self.data_offset;
1477        let result_type_id = self.next()?;
1478        let result_id = self.next()?;
1479        let pointer_id = self.next()?;
1480        let _scope_id = self.next()?;
1481        let _memory_semantics_id = self.next()?;
1482        let value_id = self.next()?;
1483        let span = self.span_from_with_op(start);
1484
1485        let (p_lexp_handle, p_base_ty_handle) =
1486            self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;
1487
1488        log::trace!("\t\t\tlooking up value expr {value_id:?}");
1489        let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;
1490
1491        block.extend(emitter.finish(ctx.expressions));
1492        // Create an expression for our result
1493        let r_lexp_handle = {
1494            let expr = crate::Expression::AtomicResult {
1495                ty: p_base_ty_handle,
1496                comparison: false,
1497            };
1498            let handle = ctx.expressions.append(expr, span);
1499            self.lookup_expression.insert(
1500                result_id,
1501                LookupExpression {
1502                    handle,
1503                    type_id: result_type_id,
1504                    block_id,
1505                },
1506            );
1507            handle
1508        };
1509        emitter.start(ctx.expressions);
1510
1511        // Create a statement for the op itself
1512        let stmt = crate::Statement::Atomic {
1513            pointer: p_lexp_handle,
1514            fun: atomic_function,
1515            value: v_lexp_handle,
1516            result: Some(r_lexp_handle),
1517        };
1518        block.push(stmt, span);
1519
1520        // Store any associated global variables so we can upgrade their types later
1521        self.record_atomic_access(ctx, p_lexp_handle)?;
1522
1523        Ok(())
1524    }
1525
1526    fn make_expression_storage(
1527        &mut self,
1528        globals: &Arena<crate::GlobalVariable>,
1529        constants: &Arena<crate::Constant>,
1530        overrides: &Arena<crate::Override>,
1531    ) -> Arena<crate::Expression> {
1532        let mut expressions = Arena::new();
1533        assert!(self.lookup_expression.is_empty());
1534        // register global variables
1535        for (&id, var) in self.lookup_variable.iter() {
1536            let span = globals.get_span(var.handle);
1537            let handle = expressions.append(crate::Expression::GlobalVariable(var.handle), span);
1538            self.lookup_expression.insert(
1539                id,
1540                LookupExpression {
1541                    type_id: var.type_id,
1542                    handle,
1543                    // Setting this to an invalid id will cause get_expr_handle
1544                    // to default to the main body making sure no load/stores
1545                    // are added.
1546                    block_id: 0,
1547                },
1548            );
1549        }
1550        // register constants
1551        for (&id, con) in self.lookup_constant.iter() {
1552            let (expr, span) = match con.inner {
1553                Constant::Constant(c) => (crate::Expression::Constant(c), constants.get_span(c)),
1554                Constant::Override(o) => (crate::Expression::Override(o), overrides.get_span(o)),
1555            };
1556            let handle = expressions.append(expr, span);
1557            self.lookup_expression.insert(
1558                id,
1559                LookupExpression {
1560                    type_id: con.type_id,
1561                    handle,
1562                    // Setting this to an invalid id will cause get_expr_handle
1563                    // to default to the main body making sure no load/stores
1564                    // are added.
1565                    block_id: 0,
1566                },
1567            );
1568        }
1569        // done
1570        expressions
1571    }
1572
1573    fn switch(&mut self, state: ModuleState, op: spirv::Op) -> Result<(), Error> {
1574        if state < self.state {
1575            Err(Error::UnsupportedInstruction(self.state, op))
1576        } else {
1577            self.state = state;
1578            Ok(())
1579        }
1580    }
1581
1582    /// Walk the statement tree and patch it in the following cases:
1583    /// 1. Function call targets are replaced by `deferred_function_calls` map
1584    fn patch_statements(
1585        &mut self,
1586        statements: &mut crate::Block,
1587        expressions: &mut Arena<crate::Expression>,
1588        fun_parameter_sampling: &mut [image::SamplingFlags],
1589    ) -> Result<(), Error> {
1590        use crate::Statement as S;
1591        let mut i = 0usize;
1592        while i < statements.len() {
1593            match statements[i] {
1594                S::Emit(_) => {}
1595                S::Block(ref mut block) => {
1596                    self.patch_statements(block, expressions, fun_parameter_sampling)?;
1597                }
1598                S::If {
1599                    condition: _,
1600                    ref mut accept,
1601                    ref mut reject,
1602                } => {
1603                    self.patch_statements(reject, expressions, fun_parameter_sampling)?;
1604                    self.patch_statements(accept, expressions, fun_parameter_sampling)?;
1605                }
1606                S::Switch {
1607                    selector: _,
1608                    ref mut cases,
1609                } => {
1610                    for case in cases.iter_mut() {
1611                        self.patch_statements(&mut case.body, expressions, fun_parameter_sampling)?;
1612                    }
1613                }
1614                S::Loop {
1615                    ref mut body,
1616                    ref mut continuing,
1617                    break_if: _,
1618                } => {
1619                    self.patch_statements(body, expressions, fun_parameter_sampling)?;
1620                    self.patch_statements(continuing, expressions, fun_parameter_sampling)?;
1621                }
1622                S::Break
1623                | S::Continue
1624                | S::Return { .. }
1625                | S::Kill
1626                | S::ControlBarrier(_)
1627                | S::MemoryBarrier(_)
1628                | S::Store { .. }
1629                | S::ImageStore { .. }
1630                | S::Atomic { .. }
1631                | S::ImageAtomic { .. }
1632                | S::RayQuery { .. }
1633                | S::SubgroupBallot { .. }
1634                | S::SubgroupCollectiveOperation { .. }
1635                | S::SubgroupGather { .. }
1636                | S::RayPipelineFunction(..) => {}
1637                S::Call {
1638                    function: ref mut callee,
1639                    ref arguments,
1640                    ..
1641                } => {
1642                    let fun_id = self.deferred_function_calls[callee.index()];
1643                    let fun_lookup = self.lookup_function.lookup(fun_id)?;
1644                    *callee = fun_lookup.handle;
1645
1646                    // Patch sampling flags
1647                    for (arg_index, arg) in arguments.iter().enumerate() {
1648                        let flags = match fun_lookup.parameters_sampling.get(arg_index) {
1649                            Some(&flags) if !flags.is_empty() => flags,
1650                            _ => continue,
1651                        };
1652
1653                        match expressions[*arg] {
1654                            crate::Expression::GlobalVariable(handle) => {
1655                                if let Some(sampling) = self.handle_sampling.get_mut(&handle) {
1656                                    *sampling |= flags
1657                                }
1658                            }
1659                            crate::Expression::FunctionArgument(i) => {
1660                                fun_parameter_sampling[i as usize] |= flags;
1661                            }
1662                            ref other => return Err(Error::InvalidGlobalVar(other.clone())),
1663                        }
1664                    }
1665                }
1666                S::WorkGroupUniformLoad { .. } => unreachable!(),
1667                S::CooperativeStore { .. } => unreachable!(),
1668                // TODO: the SPIR-V frontend does not read `NonSemantic.DebugPrintf` extended
1669                // instructions yet, so this statement is never produced here.
1670                S::DebugPrintf { .. } => unreachable!(),
1671            }
1672            i += 1;
1673        }
1674        Ok(())
1675    }
1676
1677    fn patch_function(
1678        &mut self,
1679        handle: Option<Handle<crate::Function>>,
1680        fun: &mut crate::Function,
1681    ) -> Result<(), Error> {
1682        // Note: this search is a bit unfortunate
1683        let (fun_id, mut parameters_sampling) = match handle {
1684            Some(h) => {
1685                let (&fun_id, lookup) = self
1686                    .lookup_function
1687                    .iter_mut()
1688                    .find(|&(_, ref lookup)| lookup.handle == h)
1689                    .unwrap();
1690                (fun_id, mem::take(&mut lookup.parameters_sampling))
1691            }
1692            None => (0, Vec::new()),
1693        };
1694
1695        for (_, expr) in fun.expressions.iter_mut() {
1696            if let crate::Expression::CallResult(ref mut function) = *expr {
1697                let fun_id = self.deferred_function_calls[function.index()];
1698                *function = self.lookup_function.lookup(fun_id)?.handle;
1699            }
1700        }
1701
1702        self.patch_statements(
1703            &mut fun.body,
1704            &mut fun.expressions,
1705            &mut parameters_sampling,
1706        )?;
1707
1708        if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {
1709            lookup.parameters_sampling = parameters_sampling;
1710        }
1711        Ok(())
1712    }
1713
1714    pub fn parse(mut self) -> Result<crate::Module, Error> {
1715        let mut module = {
1716            if self.next()? != spirv::MAGIC_NUMBER {
1717                return Err(Error::InvalidHeader);
1718            }
1719            let version_raw = self.next()?;
1720            let generator = self.next()?;
1721            let _bound = self.next()?;
1722            let _schema = self.next()?;
1723            log::debug!("Generated by {generator} version {version_raw:x}");
1724            crate::Module::default()
1725        };
1726
1727        self.layouter.clear();
1728        self.dummy_functions = Arena::new();
1729        self.lookup_function.clear();
1730        self.function_call_graph.clear();
1731
1732        loop {
1733            use spirv::Op;
1734
1735            let inst = match self.next_inst() {
1736                Ok(inst) => inst,
1737                Err(Error::IncompleteData) => break,
1738                Err(other) => return Err(other),
1739            };
1740            log::debug!("\t{:?} [{}]", inst.op, inst.wc);
1741
1742            match inst.op {
1743                Op::Capability => self.parse_capability(inst),
1744                Op::Extension => self.parse_extension(inst),
1745                Op::ExtInstImport => self.parse_ext_inst_import(inst),
1746                Op::MemoryModel => self.parse_memory_model(inst),
1747                Op::EntryPoint => self.parse_entry_point(inst),
1748                Op::ExecutionMode => self.parse_execution_mode(inst),
1749                Op::String => self.parse_string(inst),
1750                Op::Source => self.parse_source(inst),
1751                Op::SourceExtension => self.parse_source_extension(inst),
1752                Op::Name => self.parse_name(inst),
1753                Op::MemberName => self.parse_member_name(inst),
1754                Op::ModuleProcessed => self.parse_module_processed(inst),
1755                Op::Decorate => self.parse_decorate(inst),
1756                Op::MemberDecorate => self.parse_member_decorate(inst),
1757                Op::TypeVoid => self.parse_type_void(inst),
1758                Op::TypeBool => self.parse_type_bool(inst, &mut module),
1759                Op::TypeInt => self.parse_type_int(inst, &mut module),
1760                Op::TypeFloat => self.parse_type_float(inst, &mut module),
1761                Op::TypeVector => self.parse_type_vector(inst, &mut module),
1762                Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),
1763                Op::TypeFunction => self.parse_type_function(inst),
1764                Op::TypePointer => self.parse_type_pointer(inst, &mut module),
1765                Op::TypeArray => self.parse_type_array(inst, &mut module),
1766                Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),
1767                Op::TypeStruct => self.parse_type_struct(inst, &mut module),
1768                Op::TypeImage => self.parse_type_image(inst, &mut module),
1769                Op::TypeSampledImage => self.parse_type_sampled_image(inst),
1770                Op::TypeSampler => self.parse_type_sampler(inst, &mut module),
1771                Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),
1772                Op::ConstantComposite | Op::SpecConstantComposite => {
1773                    self.parse_composite_constant(inst, &mut module)
1774                }
1775                Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),
1776                Op::ConstantTrue | Op::SpecConstantTrue => {
1777                    self.parse_bool_constant(inst, true, &mut module)
1778                }
1779                Op::ConstantFalse | Op::SpecConstantFalse => {
1780                    self.parse_bool_constant(inst, false, &mut module)
1781                }
1782                Op::Variable => self.parse_global_variable(inst, &mut module),
1783                Op::Function => {
1784                    self.switch(ModuleState::Function, inst.op)?;
1785                    inst.expect(5)?;
1786                    self.parse_function(&mut module)
1787                }
1788                Op::ExtInst => {
1789                    // Ignore the result type and result id
1790                    let _ = self.next()?;
1791                    let _ = self.next()?;
1792                    let set_id = self.next()?;
1793                    if Some(set_id) == self.ext_non_semantic_id {
1794                        // We've already skipped the instruction byte, result type, result id, and instruction set id
1795                        for _ in 0..inst.wc - 4 {
1796                            self.next()?;
1797                        }
1798                        Ok(())
1799                    } else {
1800                        return Err(Error::UnsupportedInstruction(self.state, inst.op));
1801                    }
1802                }
1803                _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), //TODO
1804            }?;
1805        }
1806
1807        if !self.upgrade_atomics.is_empty() {
1808            log::debug!("Upgrading atomic pointers...");
1809            module.upgrade_atomics(&self.upgrade_atomics)?;
1810        }
1811
1812        // Do entry point specific processing after all functions are parsed so that we can
1813        // cull unused problematic builtins of gl_PerVertex.
1814        for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {
1815            self.process_entry_point(&mut module, ep, fun_id)?;
1816        }
1817
1818        log::debug!("Patching...");
1819        {
1820            let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
1821                .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;
1822            nodes.reverse(); // we need dominated first
1823            let mut functions = module.functions.take();
1824            for fun_id in nodes {
1825                if fun_id > !(functions.len() as u32) {
1826                    // skip all the fake IDs registered for the entry points
1827                    continue;
1828                }
1829                let lookup = self.lookup_function.get_mut(&fun_id).unwrap();
1830                // take out the function from the old array
1831                let fun = mem::take(&mut functions[lookup.handle]);
1832                // add it to the newly formed arena, and adjust the lookup
1833                lookup.handle = module
1834                    .functions
1835                    .append(fun, functions.get_span(lookup.handle));
1836            }
1837        }
1838        // patch all the functions
1839        for (handle, fun) in module.functions.iter_mut() {
1840            self.patch_function(Some(handle), fun)?;
1841        }
1842        for ep in module.entry_points.iter_mut() {
1843            self.patch_function(None, &mut ep.function)?;
1844        }
1845
1846        // Check all the images and samplers to have consistent comparison property.
1847        for (handle, flags) in self.handle_sampling.drain() {
1848            if !image::patch_comparison_type(
1849                flags,
1850                module.global_variables.get_mut(handle),
1851                &mut module.types,
1852            ) {
1853                return Err(Error::InconsistentComparisonSampling(handle));
1854            }
1855        }
1856
1857        if !self.future_decor.is_empty() {
1858            log::debug!("Unused item decorations: {:?}", self.future_decor);
1859            self.future_decor.clear();
1860        }
1861        if !self.future_member_decor.is_empty() {
1862            log::debug!("Unused member decorations: {:?}", self.future_member_decor);
1863            self.future_member_decor.clear();
1864        }
1865
1866        Ok(module)
1867    }
1868
1869    fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {
1870        self.switch(ModuleState::Capability, inst.op)?;
1871        inst.expect(2)?;
1872        let capability = self.next()?;
1873        let cap =
1874            spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;
1875        if !SUPPORTED_CAPABILITIES.contains(&cap) {
1876            if self.options.strict_capabilities {
1877                return Err(Error::UnsupportedCapability(cap));
1878            } else {
1879                log::warn!("Unknown capability {cap:?}");
1880            }
1881        }
1882        Ok(())
1883    }
1884
1885    fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {
1886        self.switch(ModuleState::Extension, inst.op)?;
1887        inst.expect_at_least(2)?;
1888        let (name, left) = self.next_string(inst.wc - 1)?;
1889        if left != 0 {
1890            return Err(Error::InvalidOperand);
1891        }
1892        if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {
1893            return Err(Error::UnsupportedExtension(name));
1894        }
1895        Ok(())
1896    }
1897
1898    fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {
1899        self.switch(ModuleState::Extension, inst.op)?;
1900        inst.expect_at_least(3)?;
1901        let result_id = self.next()?;
1902        let (name, left) = self.next_string(inst.wc - 2)?;
1903        if left != 0 {
1904            return Err(Error::InvalidOperand);
1905        }
1906        if &name == "GLSL.std.450" {
1907            self.ext_glsl_id = Some(result_id);
1908        } else if &name == "NonSemantic.Shader.DebugInfo.100" {
1909            // We completely ignore this extension. All related instructions are
1910            // non-semantic and only for debug purposes, and the spec says they
1911            // are ignorable. Many compilers (dxc, slang, etc) will emit these
1912            // instructions depending on configuration.
1913            self.ext_non_semantic_id = Some(result_id);
1914        } else {
1915            return Err(Error::UnsupportedExtSet(name));
1916        }
1917        Ok(())
1918    }
1919
1920    fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {
1921        self.switch(ModuleState::MemoryModel, inst.op)?;
1922        inst.expect(3)?;
1923        let _addressing_model = self.next()?;
1924        let _memory_model = self.next()?;
1925        Ok(())
1926    }
1927
1928    fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {
1929        self.switch(ModuleState::EntryPoint, inst.op)?;
1930        inst.expect_at_least(4)?;
1931        let exec_model = self.next()?;
1932        let exec_model = spirv::ExecutionModel::from_u32(exec_model)
1933            .ok_or(Error::UnsupportedExecutionModel(exec_model))?;
1934        let function_id = self.next()?;
1935        let (name, left) = self.next_string(inst.wc - 3)?;
1936        let ep = EntryPoint {
1937            stage: match exec_model {
1938                spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,
1939                spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,
1940                spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,
1941                spirv::ExecutionModel::TaskEXT => crate::ShaderStage::Task,
1942                spirv::ExecutionModel::MeshEXT => crate::ShaderStage::Mesh,
1943                _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),
1944            },
1945            name,
1946            early_depth_test: None,
1947            workgroup_size: [0; 3],
1948            variable_ids: self.data.by_ref().take(left as usize).collect(),
1949        };
1950        self.lookup_entry_point.insert(function_id, ep);
1951        Ok(())
1952    }
1953
1954    fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
1955        use spirv::ExecutionMode;
1956
1957        self.switch(ModuleState::ExecutionMode, inst.op)?;
1958        inst.expect_at_least(3)?;
1959
1960        let ep_id = self.next()?;
1961        let mode_id = self.next()?;
1962        let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();
1963
1964        let ep = self
1965            .lookup_entry_point
1966            .get_mut(&ep_id)
1967            .ok_or(Error::InvalidId(ep_id))?;
1968        let mode =
1969            ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;
1970
1971        match mode {
1972            ExecutionMode::EarlyFragmentTests => {
1973                ep.early_depth_test = Some(crate::EarlyDepthTest::Force);
1974            }
1975            ExecutionMode::DepthUnchanged => {
1976                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1977                    if let &mut crate::EarlyDepthTest::Allow {
1978                        ref mut conservative,
1979                    } = early_depth_test
1980                    {
1981                        *conservative = crate::ConservativeDepth::Unchanged;
1982                    }
1983                } else {
1984                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1985                        conservative: crate::ConservativeDepth::Unchanged,
1986                    });
1987                }
1988            }
1989            ExecutionMode::DepthGreater => {
1990                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1991                    if let &mut crate::EarlyDepthTest::Allow {
1992                        ref mut conservative,
1993                    } = early_depth_test
1994                    {
1995                        *conservative = crate::ConservativeDepth::GreaterEqual;
1996                    }
1997                } else {
1998                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1999                        conservative: crate::ConservativeDepth::GreaterEqual,
2000                    });
2001                }
2002            }
2003            ExecutionMode::DepthLess => {
2004                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
2005                    if let &mut crate::EarlyDepthTest::Allow {
2006                        ref mut conservative,
2007                    } = early_depth_test
2008                    {
2009                        *conservative = crate::ConservativeDepth::LessEqual;
2010                    }
2011                } else {
2012                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
2013                        conservative: crate::ConservativeDepth::LessEqual,
2014                    });
2015                }
2016            }
2017            ExecutionMode::DepthReplacing => {
2018                // Ignored because it can be deduced from the IR.
2019            }
2020            ExecutionMode::OriginUpperLeft => {
2021                // Ignored because the other option (OriginLowerLeft) is not valid in Vulkan mode.
2022            }
2023            ExecutionMode::LocalSize => {
2024                ep.workgroup_size = [args[0], args[1], args[2]];
2025            }
2026            _ => {
2027                return Err(Error::UnsupportedExecutionMode(mode_id));
2028            }
2029        }
2030
2031        Ok(())
2032    }
2033
2034    fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {
2035        self.switch(ModuleState::Source, inst.op)?;
2036        inst.expect_at_least(3)?;
2037        let _id = self.next()?;
2038        let (_name, _) = self.next_string(inst.wc - 2)?;
2039        Ok(())
2040    }
2041
2042    fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {
2043        self.switch(ModuleState::Source, inst.op)?;
2044        for _ in 1..inst.wc {
2045            let _ = self.next()?;
2046        }
2047        Ok(())
2048    }
2049
2050    fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {
2051        self.switch(ModuleState::Source, inst.op)?;
2052        inst.expect_at_least(2)?;
2053        let (_name, _) = self.next_string(inst.wc - 1)?;
2054        Ok(())
2055    }
2056
2057    fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {
2058        self.switch(ModuleState::Name, inst.op)?;
2059        inst.expect_at_least(3)?;
2060        let id = self.next()?;
2061        let (name, left) = self.next_string(inst.wc - 2)?;
2062        if left != 0 {
2063            return Err(Error::InvalidOperand);
2064        }
2065        self.future_decor.entry(id).or_default().name = Some(name);
2066        Ok(())
2067    }
2068
2069    fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {
2070        self.switch(ModuleState::Name, inst.op)?;
2071        inst.expect_at_least(4)?;
2072        let id = self.next()?;
2073        let member = self.next()?;
2074        let (name, left) = self.next_string(inst.wc - 3)?;
2075        if left != 0 {
2076            return Err(Error::InvalidOperand);
2077        }
2078
2079        self.future_member_decor
2080            .entry((id, member))
2081            .or_default()
2082            .name = Some(name);
2083        Ok(())
2084    }
2085
2086    fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {
2087        self.switch(ModuleState::Name, inst.op)?;
2088        inst.expect_at_least(2)?;
2089        let (_info, left) = self.next_string(inst.wc - 1)?;
2090        //Note: string is ignored
2091        if left != 0 {
2092            return Err(Error::InvalidOperand);
2093        }
2094        Ok(())
2095    }
2096
2097    fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2098        self.switch(ModuleState::Annotation, inst.op)?;
2099        inst.expect_at_least(3)?;
2100        let id = self.next()?;
2101        let mut dec = self.future_decor.remove(&id).unwrap_or_default();
2102        self.next_decoration(inst, 2, &mut dec)?;
2103        self.future_decor.insert(id, dec);
2104        Ok(())
2105    }
2106
2107    fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2108        self.switch(ModuleState::Annotation, inst.op)?;
2109        inst.expect_at_least(4)?;
2110        let id = self.next()?;
2111        let member = self.next()?;
2112
2113        let mut dec = self
2114            .future_member_decor
2115            .remove(&(id, member))
2116            .unwrap_or_default();
2117        self.next_decoration(inst, 3, &mut dec)?;
2118        self.future_member_decor.insert((id, member), dec);
2119        Ok(())
2120    }
2121
2122    fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {
2123        self.switch(ModuleState::Type, inst.op)?;
2124        inst.expect(2)?;
2125        let id = self.next()?;
2126        self.lookup_void_type = Some(id);
2127        Ok(())
2128    }
2129
2130    fn parse_type_bool(
2131        &mut self,
2132        inst: Instruction,
2133        module: &mut crate::Module,
2134    ) -> Result<(), Error> {
2135        let start = self.data_offset;
2136        self.switch(ModuleState::Type, inst.op)?;
2137        inst.expect(2)?;
2138        let id = self.next()?;
2139        let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);
2140        self.lookup_type.insert(
2141            id,
2142            LookupType {
2143                handle: module.types.insert(
2144                    crate::Type {
2145                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2146                        inner,
2147                    },
2148                    self.span_from_with_op(start),
2149                ),
2150                base_id: None,
2151            },
2152        );
2153        Ok(())
2154    }
2155
2156    fn parse_type_int(
2157        &mut self,
2158        inst: Instruction,
2159        module: &mut crate::Module,
2160    ) -> Result<(), Error> {
2161        let start = self.data_offset;
2162        self.switch(ModuleState::Type, inst.op)?;
2163        inst.expect(4)?;
2164        let id = self.next()?;
2165        let width = self.next()?;
2166        let sign = self.next()?;
2167        let inner = crate::TypeInner::Scalar(crate::Scalar {
2168            kind: match sign {
2169                0 => crate::ScalarKind::Uint,
2170                1 => crate::ScalarKind::Sint,
2171                _ => return Err(Error::InvalidSign(sign)),
2172            },
2173            width: map_width(width)?,
2174        });
2175        self.lookup_type.insert(
2176            id,
2177            LookupType {
2178                handle: module.types.insert(
2179                    crate::Type {
2180                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2181                        inner,
2182                    },
2183                    self.span_from_with_op(start),
2184                ),
2185                base_id: None,
2186            },
2187        );
2188        Ok(())
2189    }
2190
2191    fn parse_type_float(
2192        &mut self,
2193        inst: Instruction,
2194        module: &mut crate::Module,
2195    ) -> Result<(), Error> {
2196        let start = self.data_offset;
2197        self.switch(ModuleState::Type, inst.op)?;
2198        inst.expect(3)?;
2199        let id = self.next()?;
2200        let width = self.next()?;
2201        let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));
2202        self.lookup_type.insert(
2203            id,
2204            LookupType {
2205                handle: module.types.insert(
2206                    crate::Type {
2207                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2208                        inner,
2209                    },
2210                    self.span_from_with_op(start),
2211                ),
2212                base_id: None,
2213            },
2214        );
2215        Ok(())
2216    }
2217
2218    fn parse_type_vector(
2219        &mut self,
2220        inst: Instruction,
2221        module: &mut crate::Module,
2222    ) -> Result<(), Error> {
2223        let start = self.data_offset;
2224        self.switch(ModuleState::Type, inst.op)?;
2225        inst.expect(4)?;
2226        let id = self.next()?;
2227        let type_id = self.next()?;
2228        let type_lookup = self.lookup_type.lookup(type_id)?;
2229        let scalar = match module.types[type_lookup.handle].inner {
2230            crate::TypeInner::Scalar(scalar) => scalar,
2231            _ => return Err(Error::InvalidInnerType(type_id)),
2232        };
2233        let component_count = self.next()?;
2234        let inner = crate::TypeInner::Vector {
2235            size: map_vector_size(component_count)?,
2236            scalar,
2237        };
2238        self.lookup_type.insert(
2239            id,
2240            LookupType {
2241                handle: module.types.insert(
2242                    crate::Type {
2243                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2244                        inner,
2245                    },
2246                    self.span_from_with_op(start),
2247                ),
2248                base_id: Some(type_id),
2249            },
2250        );
2251        Ok(())
2252    }
2253
2254    fn parse_type_matrix(
2255        &mut self,
2256        inst: Instruction,
2257        module: &mut crate::Module,
2258    ) -> Result<(), Error> {
2259        let start = self.data_offset;
2260        self.switch(ModuleState::Type, inst.op)?;
2261        inst.expect(4)?;
2262        let id = self.next()?;
2263        let vector_type_id = self.next()?;
2264        let num_columns = self.next()?;
2265        let decor = self.future_decor.remove(&id);
2266
2267        let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;
2268        let inner = match module.types[vector_type_lookup.handle].inner {
2269            crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {
2270                columns: map_vector_size(num_columns)?,
2271                rows: size,
2272                scalar,
2273            },
2274            _ => return Err(Error::InvalidInnerType(vector_type_id)),
2275        };
2276
2277        self.lookup_type.insert(
2278            id,
2279            LookupType {
2280                handle: module.types.insert(
2281                    crate::Type {
2282                        name: decor.and_then(|dec| dec.name),
2283                        inner,
2284                    },
2285                    self.span_from_with_op(start),
2286                ),
2287                base_id: Some(vector_type_id),
2288            },
2289        );
2290        Ok(())
2291    }
2292
2293    fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {
2294        self.switch(ModuleState::Type, inst.op)?;
2295        inst.expect_at_least(3)?;
2296        let id = self.next()?;
2297        let return_type_id = self.next()?;
2298        let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();
2299        self.lookup_function_type.insert(
2300            id,
2301            LookupFunctionType {
2302                parameter_type_ids,
2303                return_type_id,
2304            },
2305        );
2306        Ok(())
2307    }
2308
2309    fn parse_type_pointer(
2310        &mut self,
2311        inst: Instruction,
2312        module: &mut crate::Module,
2313    ) -> Result<(), Error> {
2314        let start = self.data_offset;
2315        self.switch(ModuleState::Type, inst.op)?;
2316        inst.expect(4)?;
2317        let id = self.next()?;
2318        let storage_class = self.next()?;
2319        let type_id = self.next()?;
2320
2321        let decor = self.future_decor.remove(&id);
2322        let base_lookup_ty = self.lookup_type.lookup(type_id)?;
2323        let base_inner = &module.types[base_lookup_ty.handle].inner;
2324
2325        let space = if let Some(space) = base_inner.pointer_space() {
2326            space
2327        } else if self
2328            .lookup_storage_buffer_types
2329            .contains_key(&base_lookup_ty.handle)
2330        {
2331            crate::AddressSpace::Storage {
2332                access: crate::StorageAccess::default(),
2333            }
2334        } else {
2335            match map_storage_class(storage_class)? {
2336                ExtendedClass::Global(space) => space,
2337                ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,
2338            }
2339        };
2340
2341        // We don't support pointers to runtime-sized arrays in the `Uniform`
2342        // storage class with the `BufferBlock` decoration. Runtime-sized arrays
2343        // should be in the StorageBuffer class.
2344        if let crate::TypeInner::Array {
2345            size: crate::ArraySize::Dynamic,
2346            ..
2347        } = *base_inner
2348        {
2349            match space {
2350                crate::AddressSpace::Storage { .. } => {}
2351                _ => {
2352                    return Err(Error::UnsupportedRuntimeArrayStorageClass);
2353                }
2354            }
2355        }
2356
2357        // Don't bother with pointer stuff for `Handle` types.
2358        let lookup_ty = if space == crate::AddressSpace::Handle {
2359            base_lookup_ty.clone()
2360        } else {
2361            LookupType {
2362                handle: module.types.insert(
2363                    crate::Type {
2364                        name: decor.and_then(|dec| dec.name),
2365                        inner: crate::TypeInner::Pointer {
2366                            base: base_lookup_ty.handle,
2367                            space,
2368                        },
2369                    },
2370                    self.span_from_with_op(start),
2371                ),
2372                base_id: Some(type_id),
2373            }
2374        };
2375        self.lookup_type.insert(id, lookup_ty);
2376        Ok(())
2377    }
2378
2379    fn parse_type_array(
2380        &mut self,
2381        inst: Instruction,
2382        module: &mut crate::Module,
2383    ) -> Result<(), Error> {
2384        let start = self.data_offset;
2385        self.switch(ModuleState::Type, inst.op)?;
2386        inst.expect(4)?;
2387        let id = self.next()?;
2388        let type_id = self.next()?;
2389        let length_id = self.next()?;
2390        let length_const = self.lookup_constant.lookup(length_id)?;
2391
2392        let size = resolve_constant(module.to_ctx(), &length_const.inner)
2393            .and_then(NonZeroU32::new)
2394            .ok_or(Error::InvalidArraySize(length_id))?;
2395
2396        let decor = self.future_decor.remove(&id).unwrap_or_default();
2397        let base = self.lookup_type.lookup(type_id)?.handle;
2398
2399        self.layouter.update(module.to_ctx()).unwrap();
2400
2401        // HACK if the underlying type is an image or a sampler, let's assume
2402        //      that we're dealing with a binding-array
2403        //
2404        // Note that it's not a strictly correct assumption, but rather a trade
2405        // off caused by an impedance mismatch between SPIR-V's and Naga's type
2406        // systems - Naga distinguishes between arrays and binding-arrays via
2407        // types (i.e. both kinds of arrays are just different types), while
2408        // SPIR-V distinguishes between them through usage - e.g. given:
2409        //
2410        // ```
2411        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
2412        // %uint_256 = OpConstant %uint 256
2413        // %image_array = OpTypeArray %image %uint_256
2414        // ```
2415        //
2416        // ```
2417        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
2418        // %uint_256 = OpConstant %uint 256
2419        // %image_array = OpTypeArray %image %uint_256
2420        // %image_array_ptr = OpTypePointer UniformConstant %image_array
2421        // ```
2422        //
2423        // ... in the first case, `%image_array` should technically correspond
2424        // to `TypeInner::Array`, while in the second case it should say
2425        // `TypeInner::BindingArray` (kinda, depending on whether `%image_array`
2426        // is ever used as a freestanding type or rather always through the
2427        // pointer-indirection).
2428        //
2429        // Anyway, at the moment we don't support other kinds of image / sampler
2430        // arrays than those binding-based, so this assumption is pretty safe
2431        // for now.
2432        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2433            module.types[base].inner
2434        {
2435            crate::TypeInner::BindingArray {
2436                base,
2437                size: crate::ArraySize::Constant(size),
2438            }
2439        } else {
2440            crate::TypeInner::Array {
2441                base,
2442                size: crate::ArraySize::Constant(size),
2443                stride: match decor.array_stride {
2444                    Some(stride) => stride.get(),
2445                    None => self.layouter[base].to_stride(),
2446                },
2447            }
2448        };
2449
2450        self.lookup_type.insert(
2451            id,
2452            LookupType {
2453                handle: module.types.insert(
2454                    crate::Type {
2455                        name: decor.name,
2456                        inner,
2457                    },
2458                    self.span_from_with_op(start),
2459                ),
2460                base_id: Some(type_id),
2461            },
2462        );
2463        Ok(())
2464    }
2465
2466    fn parse_type_runtime_array(
2467        &mut self,
2468        inst: Instruction,
2469        module: &mut crate::Module,
2470    ) -> Result<(), Error> {
2471        let start = self.data_offset;
2472        self.switch(ModuleState::Type, inst.op)?;
2473        inst.expect(3)?;
2474        let id = self.next()?;
2475        let type_id = self.next()?;
2476
2477        let decor = self.future_decor.remove(&id).unwrap_or_default();
2478        let base = self.lookup_type.lookup(type_id)?.handle;
2479
2480        self.layouter.update(module.to_ctx()).unwrap();
2481
2482        // HACK same case as in `parse_type_array()`
2483        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2484            module.types[base].inner
2485        {
2486            crate::TypeInner::BindingArray {
2487                base: self.lookup_type.lookup(type_id)?.handle,
2488                size: crate::ArraySize::Dynamic,
2489            }
2490        } else {
2491            crate::TypeInner::Array {
2492                base: self.lookup_type.lookup(type_id)?.handle,
2493                size: crate::ArraySize::Dynamic,
2494                stride: match decor.array_stride {
2495                    Some(stride) => stride.get(),
2496                    None => self.layouter[base].to_stride(),
2497                },
2498            }
2499        };
2500
2501        self.lookup_type.insert(
2502            id,
2503            LookupType {
2504                handle: module.types.insert(
2505                    crate::Type {
2506                        name: decor.name,
2507                        inner,
2508                    },
2509                    self.span_from_with_op(start),
2510                ),
2511                base_id: Some(type_id),
2512            },
2513        );
2514        Ok(())
2515    }
2516
2517    fn parse_type_struct(
2518        &mut self,
2519        inst: Instruction,
2520        module: &mut crate::Module,
2521    ) -> Result<(), Error> {
2522        let start = self.data_offset;
2523        self.switch(ModuleState::Type, inst.op)?;
2524        inst.expect_at_least(2)?;
2525        let id = self.next()?;
2526        let parent_decor = self.future_decor.remove(&id);
2527        let is_storage_buffer = parent_decor
2528            .as_ref()
2529            .is_some_and(|decor| decor.storage_buffer);
2530
2531        self.layouter.update(module.to_ctx()).unwrap();
2532
2533        let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
2534        let mut member_lookups = Vec::with_capacity(members.capacity());
2535        let mut storage_access = crate::StorageAccess::empty();
2536        let mut span = 0;
2537        let mut alignment = Alignment::ONE;
2538        for i in 0..u32::from(inst.wc) - 2 {
2539            let type_id = self.next()?;
2540            let ty = self.lookup_type.lookup(type_id)?.handle;
2541            let decor = self
2542                .future_member_decor
2543                .remove(&(id, i))
2544                .unwrap_or_default();
2545
2546            storage_access |= decor.flags.to_storage_access();
2547
2548            member_lookups.push(LookupMember {
2549                type_id,
2550                row_major: decor.matrix_major == Some(Majority::Row),
2551            });
2552
2553            let member_alignment = self.layouter[ty].alignment;
2554            span = member_alignment.round_up(span);
2555            alignment = member_alignment.max(alignment);
2556
2557            let binding = decor.io_binding().ok();
2558            if let Some(offset) = decor.offset {
2559                span = offset;
2560            }
2561            let offset = span;
2562
2563            span += self.layouter[ty].size;
2564
2565            let inner = &module.types[ty].inner;
2566            if let crate::TypeInner::Matrix {
2567                columns,
2568                rows,
2569                scalar,
2570            } = *inner
2571            {
2572                if let Some(stride) = decor.matrix_stride {
2573                    let expected_stride = Alignment::from(rows) * scalar.width as u32;
2574                    if stride.get() != expected_stride {
2575                        return Err(Error::UnsupportedMatrixStride {
2576                            stride: stride.get(),
2577                            columns: columns as u8,
2578                            rows: rows as u8,
2579                            width: scalar.width,
2580                        });
2581                    }
2582                }
2583            }
2584
2585            members.push(crate::StructMember {
2586                name: decor.name,
2587                ty,
2588                binding,
2589                offset,
2590            });
2591        }
2592
2593        span = alignment.round_up(span);
2594
2595        let inner = crate::TypeInner::Struct { span, members };
2596
2597        let ty_handle = module.types.insert(
2598            crate::Type {
2599                name: parent_decor.and_then(|dec| dec.name),
2600                inner,
2601            },
2602            self.span_from_with_op(start),
2603        );
2604
2605        if is_storage_buffer {
2606            self.lookup_storage_buffer_types
2607                .insert(ty_handle, storage_access);
2608        }
2609        for (i, member_lookup) in member_lookups.into_iter().enumerate() {
2610            self.lookup_member
2611                .insert((ty_handle, i as u32), member_lookup);
2612        }
2613        self.lookup_type.insert(
2614            id,
2615            LookupType {
2616                handle: ty_handle,
2617                base_id: None,
2618            },
2619        );
2620        Ok(())
2621    }
2622
2623    fn parse_type_image(
2624        &mut self,
2625        inst: Instruction,
2626        module: &mut crate::Module,
2627    ) -> Result<(), Error> {
2628        let start = self.data_offset;
2629        self.switch(ModuleState::Type, inst.op)?;
2630        inst.expect(9)?;
2631
2632        let id = self.next()?;
2633        let sample_type_id = self.next()?;
2634        let dim = self.next()?;
2635        let is_depth = self.next()?;
2636        let is_array = self.next()? != 0;
2637        let is_msaa = self.next()? != 0;
2638        let is_sampled = self.next()?;
2639        let format = self.next()?;
2640
2641        let dim = map_image_dim(dim)?;
2642        let decor = self.future_decor.remove(&id).unwrap_or_default();
2643
2644        // ensure there is a type for texture coordinate without extra components
2645        module.types.insert(
2646            crate::Type {
2647                name: None,
2648                inner: {
2649                    let scalar = crate::Scalar::F32;
2650                    match dim.required_coordinate_size() {
2651                        None => crate::TypeInner::Scalar(scalar),
2652                        Some(size) => crate::TypeInner::Vector { size, scalar },
2653                    }
2654                },
2655            },
2656            Default::default(),
2657        );
2658
2659        let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;
2660        let kind = module.types[base_handle]
2661            .inner
2662            .scalar_kind()
2663            .ok_or(Error::InvalidImageBaseType(base_handle))?;
2664
2665        let inner = crate::TypeInner::Image {
2666            class: if is_depth == 1 {
2667                if is_sampled == 2 {
2668                    return Err(Error::InvalidImageDepthStorage);
2669                }
2670
2671                crate::ImageClass::Depth { multi: is_msaa }
2672            }
2673            // If we have an unknown format and storage texture, this is
2674            // StorageRead/WriteWithoutFormat. We don't currently support
2675            // this.
2676            else if is_sampled == 2 && format == 0 {
2677                return Err(Error::InvalidStorageImageWithoutFormat);
2678            }
2679            // If we have explicit class information (is_sampled = 2 = Storage), use it.
2680            //
2681            // If we have unknown class information (is_sampled = 0 = Unknown), infer the
2682            // class from the presence of an explicit format.
2683            else if format != 0 && (is_sampled == 0 || is_sampled == 2) {
2684                crate::ImageClass::Storage {
2685                    format: map_image_format(format)?,
2686                    access: crate::StorageAccess::default(),
2687                }
2688            }
2689            // We will hit this case either when sampled is 1, or if we have unknown
2690            // sampling information or when sampled is 0 and we have no explicit format.
2691            else {
2692                crate::ImageClass::Sampled {
2693                    kind,
2694                    multi: is_msaa,
2695                }
2696            },
2697            dim,
2698            arrayed: is_array,
2699        };
2700
2701        let handle = module.types.insert(
2702            crate::Type {
2703                name: decor.name,
2704                inner,
2705            },
2706            self.span_from_with_op(start),
2707        );
2708
2709        self.lookup_type.insert(
2710            id,
2711            LookupType {
2712                handle,
2713                base_id: Some(sample_type_id),
2714            },
2715        );
2716        Ok(())
2717    }
2718
2719    fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {
2720        self.switch(ModuleState::Type, inst.op)?;
2721        inst.expect(3)?;
2722        let id = self.next()?;
2723        let image_id = self.next()?;
2724        self.lookup_type.insert(
2725            id,
2726            LookupType {
2727                handle: self.lookup_type.lookup(image_id)?.handle,
2728                base_id: Some(image_id),
2729            },
2730        );
2731        Ok(())
2732    }
2733
2734    fn parse_type_sampler(
2735        &mut self,
2736        inst: Instruction,
2737        module: &mut crate::Module,
2738    ) -> Result<(), Error> {
2739        let start = self.data_offset;
2740        self.switch(ModuleState::Type, inst.op)?;
2741        inst.expect(2)?;
2742        let id = self.next()?;
2743        let decor = self.future_decor.remove(&id).unwrap_or_default();
2744        let handle = module.types.insert(
2745            crate::Type {
2746                name: decor.name,
2747                inner: crate::TypeInner::Sampler { comparison: false },
2748            },
2749            self.span_from_with_op(start),
2750        );
2751        self.lookup_type.insert(
2752            id,
2753            LookupType {
2754                handle,
2755                base_id: None,
2756            },
2757        );
2758        Ok(())
2759    }
2760
2761    fn parse_constant(
2762        &mut self,
2763        inst: Instruction,
2764        module: &mut crate::Module,
2765    ) -> Result<(), Error> {
2766        let start = self.data_offset;
2767        self.switch(ModuleState::Type, inst.op)?;
2768        inst.expect_at_least(4)?;
2769        let type_id = self.next()?;
2770        let id = self.next()?;
2771        let type_lookup = self.lookup_type.lookup(type_id)?;
2772        let ty = type_lookup.handle;
2773
2774        let literal = match module.types[ty].inner {
2775            crate::TypeInner::Scalar(crate::Scalar {
2776                kind: crate::ScalarKind::Uint,
2777                width,
2778            }) => {
2779                let low = self.next()?;
2780                match width {
2781                    4 => crate::Literal::U32(low),
2782                    8 => {
2783                        inst.expect(5)?;
2784                        let high = self.next()?;
2785                        crate::Literal::U64((u64::from(high) << 32) | u64::from(low))
2786                    }
2787                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2788                }
2789            }
2790            crate::TypeInner::Scalar(crate::Scalar {
2791                kind: crate::ScalarKind::Sint,
2792                width,
2793            }) => {
2794                let low = self.next()?;
2795                match width {
2796                    4 => crate::Literal::I32(low as i32),
2797                    8 => {
2798                        inst.expect(5)?;
2799                        let high = self.next()?;
2800                        crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)
2801                    }
2802                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2803                }
2804            }
2805            crate::TypeInner::Scalar(crate::Scalar {
2806                kind: crate::ScalarKind::Float,
2807                width,
2808            }) => {
2809                let low = self.next()?;
2810                match width {
2811                    // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Literal
2812                    // If a numeric type’s bit width is less than 32-bits, the value appears in the low-order bits of the word.
2813                    2 => crate::Literal::F16(f16::from_bits(low as u16)),
2814                    4 => crate::Literal::F32(f32::from_bits(low)),
2815                    8 => {
2816                        inst.expect(5)?;
2817                        let high = self.next()?;
2818                        crate::Literal::F64(f64::from_bits(
2819                            (u64::from(high) << 32) | u64::from(low),
2820                        ))
2821                    }
2822                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2823                }
2824            }
2825            _ => return Err(Error::UnsupportedType(type_lookup.handle)),
2826        };
2827
2828        let span = self.span_from_with_op(start);
2829
2830        let init = module
2831            .global_expressions
2832            .append(crate::Expression::Literal(literal), span);
2833
2834        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2835    }
2836
2837    fn parse_composite_constant(
2838        &mut self,
2839        inst: Instruction,
2840        module: &mut crate::Module,
2841    ) -> Result<(), Error> {
2842        let start = self.data_offset;
2843        self.switch(ModuleState::Type, inst.op)?;
2844        inst.expect_at_least(3)?;
2845        let type_id = self.next()?;
2846        let id = self.next()?;
2847
2848        let type_lookup = self.lookup_type.lookup(type_id)?;
2849        let ty = type_lookup.handle;
2850
2851        let mut components = Vec::with_capacity(inst.wc as usize - 3);
2852        for _ in 0..components.capacity() {
2853            let start = self.data_offset;
2854            let component_id = self.next()?;
2855            let span = self.span_from_with_op(start);
2856            let constant = self.lookup_constant.lookup(component_id)?;
2857            let expr = module
2858                .global_expressions
2859                .append(constant.inner.to_expr(), span);
2860            components.push(expr);
2861        }
2862
2863        let span = self.span_from_with_op(start);
2864
2865        let init = module
2866            .global_expressions
2867            .append(crate::Expression::Compose { ty, components }, span);
2868
2869        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2870    }
2871
2872    fn parse_null_constant(
2873        &mut self,
2874        inst: Instruction,
2875        module: &mut crate::Module,
2876    ) -> Result<(), Error> {
2877        let start = self.data_offset;
2878        self.switch(ModuleState::Type, inst.op)?;
2879        inst.expect(3)?;
2880        let type_id = self.next()?;
2881        let id = self.next()?;
2882        let span = self.span_from_with_op(start);
2883
2884        let type_lookup = self.lookup_type.lookup(type_id)?;
2885        let ty = type_lookup.handle;
2886
2887        let init = module
2888            .global_expressions
2889            .append(crate::Expression::ZeroValue(ty), span);
2890
2891        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2892    }
2893
2894    fn parse_bool_constant(
2895        &mut self,
2896        inst: Instruction,
2897        value: bool,
2898        module: &mut crate::Module,
2899    ) -> Result<(), Error> {
2900        let start = self.data_offset;
2901        self.switch(ModuleState::Type, inst.op)?;
2902        inst.expect(3)?;
2903        let type_id = self.next()?;
2904        let id = self.next()?;
2905        let span = self.span_from_with_op(start);
2906
2907        let type_lookup = self.lookup_type.lookup(type_id)?;
2908        let ty = type_lookup.handle;
2909
2910        let init = module.global_expressions.append(
2911            crate::Expression::Literal(crate::Literal::Bool(value)),
2912            span,
2913        );
2914
2915        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2916    }
2917
2918    fn insert_parsed_constant(
2919        &mut self,
2920        module: &mut crate::Module,
2921        id: u32,
2922        type_id: u32,
2923        ty: Handle<crate::Type>,
2924        init: Handle<crate::Expression>,
2925        span: crate::Span,
2926    ) -> Result<(), Error> {
2927        let decor = self.future_decor.remove(&id).unwrap_or_default();
2928
2929        let inner = if let Some(id) = decor.specialization_constant_id {
2930            let o = crate::Override {
2931                name: decor.name,
2932                id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),
2933                ty,
2934                init: Some(init),
2935            };
2936            Constant::Override(module.overrides.append(o, span))
2937        } else {
2938            let c = crate::Constant {
2939                name: decor.name,
2940                ty,
2941                init,
2942            };
2943            Constant::Constant(module.constants.append(c, span))
2944        };
2945
2946        self.lookup_constant
2947            .insert(id, LookupConstant { inner, type_id });
2948        Ok(())
2949    }
2950
2951    fn parse_global_variable(
2952        &mut self,
2953        inst: Instruction,
2954        module: &mut crate::Module,
2955    ) -> Result<(), Error> {
2956        let start = self.data_offset;
2957        self.switch(ModuleState::Type, inst.op)?;
2958        inst.expect_at_least(4)?;
2959        let type_id = self.next()?;
2960        let id = self.next()?;
2961        let storage_class = self.next()?;
2962        let init = if inst.wc > 4 {
2963            inst.expect(5)?;
2964            let start = self.data_offset;
2965            let init_id = self.next()?;
2966            let span = self.span_from_with_op(start);
2967            let lconst = self.lookup_constant.lookup(init_id)?;
2968            let expr = module
2969                .global_expressions
2970                .append(lconst.inner.to_expr(), span);
2971            Some(expr)
2972        } else {
2973            None
2974        };
2975        let span = self.span_from_with_op(start);
2976        let dec = self.future_decor.remove(&id).unwrap_or_default();
2977
2978        let original_ty = self.lookup_type.lookup(type_id)?.handle;
2979        let mut ty = original_ty;
2980
2981        if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
2982            ty = base;
2983        }
2984
2985        if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
2986            // Inside `parse_type_array()` we guess that an array of images or
2987            // samplers must be a binding array, and here we validate that guess
2988            if dec.desc_set.is_none() || dec.desc_index.is_none() {
2989                return Err(Error::NonBindingArrayOfImageOrSamplers);
2990            }
2991        }
2992
2993        if let crate::TypeInner::Image {
2994            dim,
2995            arrayed,
2996            class: crate::ImageClass::Storage { format, access: _ },
2997        } = module.types[ty].inner
2998        {
2999            // Storage image types in IR have to contain the access, but not in the SPIR-V.
3000            // The same image type in SPIR-V can be used (and has to be used) for multiple images.
3001            // So we copy the type out and apply the variable access decorations.
3002            let access = dec.flags.to_storage_access();
3003
3004            ty = module.types.insert(
3005                crate::Type {
3006                    name: None,
3007                    inner: crate::TypeInner::Image {
3008                        dim,
3009                        arrayed,
3010                        class: crate::ImageClass::Storage { format, access },
3011                    },
3012                },
3013                Default::default(),
3014            );
3015        }
3016
3017        let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
3018            Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
3019            None => map_storage_class(storage_class)?,
3020        };
3021
3022        let (inner, var) = match ext_class {
3023            ExtendedClass::Global(mut space) => {
3024                if let crate::AddressSpace::Storage { ref mut access } = space {
3025                    *access &= dec.flags.to_storage_access();
3026                }
3027                let var = crate::GlobalVariable {
3028                    binding: dec.resource_binding(),
3029                    name: dec.name,
3030                    space,
3031                    ty,
3032                    init,
3033                    memory_decorations: dec.flags.to_memory_decorations(),
3034                };
3035                (Variable::Global, var)
3036            }
3037            ExtendedClass::Input => {
3038                let binding = dec.io_binding()?;
3039                let mut unsigned_ty = ty;
3040                if let crate::Binding::BuiltIn(built_in) = binding {
3041                    let needs_inner_uint = match built_in {
3042                        crate::BuiltIn::BaseInstance
3043                        | crate::BuiltIn::BaseVertex
3044                        | crate::BuiltIn::InstanceIndex
3045                        | crate::BuiltIn::SampleIndex
3046                        | crate::BuiltIn::VertexIndex
3047                        | crate::BuiltIn::PrimitiveIndex
3048                        | crate::BuiltIn::LocalInvocationIndex => {
3049                            Some(crate::TypeInner::Scalar(crate::Scalar::U32))
3050                        }
3051                        crate::BuiltIn::GlobalInvocationId
3052                        | crate::BuiltIn::LocalInvocationId
3053                        | crate::BuiltIn::WorkGroupId
3054                        | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {
3055                            size: crate::VectorSize::Tri,
3056                            scalar: crate::Scalar::U32,
3057                        }),
3058                        crate::BuiltIn::Barycentric { perspective: false } => {
3059                            Some(crate::TypeInner::Vector {
3060                                size: crate::VectorSize::Tri,
3061                                scalar: crate::Scalar::F32,
3062                            })
3063                        }
3064                        _ => None,
3065                    };
3066                    if let (Some(inner), Some(crate::ScalarKind::Sint)) =
3067                        (needs_inner_uint, module.types[ty].inner.scalar_kind())
3068                    {
3069                        unsigned_ty = module
3070                            .types
3071                            .insert(crate::Type { name: None, inner }, Default::default());
3072                    }
3073                }
3074
3075                let var = crate::GlobalVariable {
3076                    name: dec.name.clone(),
3077                    space: crate::AddressSpace::Private,
3078                    binding: None,
3079                    ty,
3080                    init: None,
3081                    memory_decorations: crate::MemoryDecorations::empty(),
3082                };
3083
3084                let inner = Variable::Input(crate::FunctionArgument {
3085                    name: dec.name,
3086                    ty: unsigned_ty,
3087                    binding: Some(binding),
3088                });
3089                (inner, var)
3090            }
3091            ExtendedClass::Output => {
3092                // For output interface blocks, this would be a structure.
3093                let binding = dec.io_binding().ok();
3094                let init = match binding {
3095                    Some(crate::Binding::BuiltIn(built_in)) => {
3096                        match null::generate_default_built_in(
3097                            Some(built_in),
3098                            ty,
3099                            &mut module.global_expressions,
3100                            span,
3101                        ) {
3102                            Ok(handle) => Some(handle),
3103                            Err(e) => {
3104                                log::warn!("Failed to initialize output built-in: {e}");
3105                                None
3106                            }
3107                        }
3108                    }
3109                    Some(crate::Binding::Location { .. }) => None,
3110                    None => match module.types[ty].inner {
3111                        crate::TypeInner::Struct { ref members, .. } => {
3112                            let mut components = Vec::with_capacity(members.len());
3113                            for member in members.iter() {
3114                                let built_in = match member.binding {
3115                                    Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),
3116                                    _ => None,
3117                                };
3118                                let handle = null::generate_default_built_in(
3119                                    built_in,
3120                                    member.ty,
3121                                    &mut module.global_expressions,
3122                                    span,
3123                                )?;
3124                                components.push(handle);
3125                            }
3126                            Some(
3127                                module
3128                                    .global_expressions
3129                                    .append(crate::Expression::Compose { ty, components }, span),
3130                            )
3131                        }
3132                        _ => None,
3133                    },
3134                };
3135
3136                let var = crate::GlobalVariable {
3137                    name: dec.name,
3138                    space: crate::AddressSpace::Private,
3139                    binding: None,
3140                    ty,
3141                    init,
3142                    memory_decorations: crate::MemoryDecorations::empty(),
3143                };
3144                let inner = Variable::Output(crate::FunctionResult { ty, binding });
3145                (inner, var)
3146            }
3147        };
3148
3149        let handle = module.global_variables.append(var, span);
3150
3151        if module.types[ty].inner.can_comparison_sample(module) {
3152            log::debug!("\t\ttracking {handle:?} for sampling properties");
3153
3154            self.handle_sampling
3155                .insert(handle, image::SamplingFlags::empty());
3156        }
3157
3158        self.lookup_variable.insert(
3159            id,
3160            LookupVariable {
3161                inner,
3162                handle,
3163                type_id,
3164            },
3165        );
3166        Ok(())
3167    }
3168
3169    /// Record an atomic access to some component of a global variable.
3170    ///
3171    /// Given `handle`, an expression referring to a scalar that has had an
3172    /// atomic operation applied to it, descend into the expression, noting
3173    /// which global variable it ultimately refers to, and which struct fields
3174    /// of that global's value it accesses.
3175    ///
3176    /// Return the handle of the type of the expression.
3177    ///
3178    /// If the expression doesn't actually refer to something in a global
3179    /// variable, we can't upgrade its type in a way that Naga validation would
3180    /// pass, so reject the input instead.
3181    fn record_atomic_access(
3182        &mut self,
3183        ctx: &BlockContext,
3184        handle: Handle<crate::Expression>,
3185    ) -> Result<Handle<crate::Type>, Error> {
3186        log::debug!("\t\tlocating global variable in {handle:?}");
3187        match ctx.expressions[handle] {
3188            crate::Expression::Access { base, index } => {
3189                log::debug!("\t\t  access {handle:?} {index:?}");
3190                let ty = self.record_atomic_access(ctx, base)?;
3191                let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {
3192                    unreachable!("Atomic operations on Access expressions only work for arrays");
3193                };
3194                Ok(base)
3195            }
3196            crate::Expression::AccessIndex { base, index } => {
3197                log::debug!("\t\t  access index {handle:?} {index:?}");
3198                let ty = self.record_atomic_access(ctx, base)?;
3199                match ctx.module.types[ty].inner {
3200                    crate::TypeInner::Struct { ref members, .. } => {
3201                        let index = index as usize;
3202                        self.upgrade_atomics.insert_field(ty, index);
3203                        Ok(members[index].ty)
3204                    }
3205                    crate::TypeInner::Array { base, .. } => {
3206                        Ok(base)
3207                    }
3208                    _ => unreachable!("Atomic operations on AccessIndex expressions only work for structs and arrays"),
3209                }
3210            }
3211            crate::Expression::GlobalVariable(h) => {
3212                log::debug!("\t\t  found {h:?}");
3213                self.upgrade_atomics.insert_global(h);
3214                Ok(ctx.module.global_variables[h].ty)
3215            }
3216            _ => Err(Error::AtomicUpgradeError(
3217                crate::front::atomic_upgrade::Error::GlobalVariableMissing,
3218            )),
3219        }
3220    }
3221}
3222
3223fn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {
3224    let constant = match *constant {
3225        Constant::Constant(constant) => constant,
3226        Constant::Override(_) => return None,
3227    };
3228    match gctx.global_expressions[gctx.constants[constant].init] {
3229        crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),
3230        crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),
3231        _ => None,
3232    }
3233}
3234
3235pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {
3236    if !data.len().is_multiple_of(4) {
3237        return Err(Error::IncompleteData);
3238    }
3239
3240    let words = data
3241        .chunks(4)
3242        .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
3243    Frontend::new(words, options).parse()
3244}
3245
3246/// Helper function to check if `child` is in the scope of `parent`
3247fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
3248    loop {
3249        if child == parent {
3250            // The child is in the scope parent
3251            break true;
3252        } else if child == 0 {
3253            // Searched finished at the root the child isn't in the parent's body
3254            break false;
3255        }
3256
3257        child = block_ctx.bodies[child].parent;
3258    }
3259}
3260
3261#[cfg(test)]
3262mod test {
3263    use alloc::vec;
3264
3265    #[test]
3266    fn parse() {
3267        let bin = vec![
3268            // Magic number.           Version number: 1.0.
3269            0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
3270            // Generator number: 0.    Bound: 0.
3271            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved word: 0.
3272            0x00, 0x00, 0x00, 0x00, // OpMemoryModel.          Logical.
3273            0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // GLSL450.
3274            0x01, 0x00, 0x00, 0x00,
3275        ];
3276        let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
3277    }
3278}