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            }
1669            i += 1;
1670        }
1671        Ok(())
1672    }
1673
1674    fn patch_function(
1675        &mut self,
1676        handle: Option<Handle<crate::Function>>,
1677        fun: &mut crate::Function,
1678    ) -> Result<(), Error> {
1679        // Note: this search is a bit unfortunate
1680        let (fun_id, mut parameters_sampling) = match handle {
1681            Some(h) => {
1682                let (&fun_id, lookup) = self
1683                    .lookup_function
1684                    .iter_mut()
1685                    .find(|&(_, ref lookup)| lookup.handle == h)
1686                    .unwrap();
1687                (fun_id, mem::take(&mut lookup.parameters_sampling))
1688            }
1689            None => (0, Vec::new()),
1690        };
1691
1692        for (_, expr) in fun.expressions.iter_mut() {
1693            if let crate::Expression::CallResult(ref mut function) = *expr {
1694                let fun_id = self.deferred_function_calls[function.index()];
1695                *function = self.lookup_function.lookup(fun_id)?.handle;
1696            }
1697        }
1698
1699        self.patch_statements(
1700            &mut fun.body,
1701            &mut fun.expressions,
1702            &mut parameters_sampling,
1703        )?;
1704
1705        if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {
1706            lookup.parameters_sampling = parameters_sampling;
1707        }
1708        Ok(())
1709    }
1710
1711    pub fn parse(mut self) -> Result<crate::Module, Error> {
1712        let mut module = {
1713            if self.next()? != spirv::MAGIC_NUMBER {
1714                return Err(Error::InvalidHeader);
1715            }
1716            let version_raw = self.next()?;
1717            let generator = self.next()?;
1718            let _bound = self.next()?;
1719            let _schema = self.next()?;
1720            log::debug!("Generated by {generator} version {version_raw:x}");
1721            crate::Module::default()
1722        };
1723
1724        self.layouter.clear();
1725        self.dummy_functions = Arena::new();
1726        self.lookup_function.clear();
1727        self.function_call_graph.clear();
1728
1729        loop {
1730            use spirv::Op;
1731
1732            let inst = match self.next_inst() {
1733                Ok(inst) => inst,
1734                Err(Error::IncompleteData) => break,
1735                Err(other) => return Err(other),
1736            };
1737            log::debug!("\t{:?} [{}]", inst.op, inst.wc);
1738
1739            match inst.op {
1740                Op::Capability => self.parse_capability(inst),
1741                Op::Extension => self.parse_extension(inst),
1742                Op::ExtInstImport => self.parse_ext_inst_import(inst),
1743                Op::MemoryModel => self.parse_memory_model(inst),
1744                Op::EntryPoint => self.parse_entry_point(inst),
1745                Op::ExecutionMode => self.parse_execution_mode(inst),
1746                Op::String => self.parse_string(inst),
1747                Op::Source => self.parse_source(inst),
1748                Op::SourceExtension => self.parse_source_extension(inst),
1749                Op::Name => self.parse_name(inst),
1750                Op::MemberName => self.parse_member_name(inst),
1751                Op::ModuleProcessed => self.parse_module_processed(inst),
1752                Op::Decorate => self.parse_decorate(inst),
1753                Op::MemberDecorate => self.parse_member_decorate(inst),
1754                Op::TypeVoid => self.parse_type_void(inst),
1755                Op::TypeBool => self.parse_type_bool(inst, &mut module),
1756                Op::TypeInt => self.parse_type_int(inst, &mut module),
1757                Op::TypeFloat => self.parse_type_float(inst, &mut module),
1758                Op::TypeVector => self.parse_type_vector(inst, &mut module),
1759                Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),
1760                Op::TypeFunction => self.parse_type_function(inst),
1761                Op::TypePointer => self.parse_type_pointer(inst, &mut module),
1762                Op::TypeArray => self.parse_type_array(inst, &mut module),
1763                Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),
1764                Op::TypeStruct => self.parse_type_struct(inst, &mut module),
1765                Op::TypeImage => self.parse_type_image(inst, &mut module),
1766                Op::TypeSampledImage => self.parse_type_sampled_image(inst),
1767                Op::TypeSampler => self.parse_type_sampler(inst, &mut module),
1768                Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),
1769                Op::ConstantComposite | Op::SpecConstantComposite => {
1770                    self.parse_composite_constant(inst, &mut module)
1771                }
1772                Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),
1773                Op::ConstantTrue | Op::SpecConstantTrue => {
1774                    self.parse_bool_constant(inst, true, &mut module)
1775                }
1776                Op::ConstantFalse | Op::SpecConstantFalse => {
1777                    self.parse_bool_constant(inst, false, &mut module)
1778                }
1779                Op::Variable => self.parse_global_variable(inst, &mut module),
1780                Op::Function => {
1781                    self.switch(ModuleState::Function, inst.op)?;
1782                    inst.expect(5)?;
1783                    self.parse_function(&mut module)
1784                }
1785                Op::ExtInst => {
1786                    // Ignore the result type and result id
1787                    let _ = self.next()?;
1788                    let _ = self.next()?;
1789                    let set_id = self.next()?;
1790                    if Some(set_id) == self.ext_non_semantic_id {
1791                        // We've already skipped the instruction byte, result type, result id, and instruction set id
1792                        for _ in 0..inst.wc - 4 {
1793                            self.next()?;
1794                        }
1795                        Ok(())
1796                    } else {
1797                        return Err(Error::UnsupportedInstruction(self.state, inst.op));
1798                    }
1799                }
1800                _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), //TODO
1801            }?;
1802        }
1803
1804        if !self.upgrade_atomics.is_empty() {
1805            log::debug!("Upgrading atomic pointers...");
1806            module.upgrade_atomics(&self.upgrade_atomics)?;
1807        }
1808
1809        // Do entry point specific processing after all functions are parsed so that we can
1810        // cull unused problematic builtins of gl_PerVertex.
1811        for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {
1812            self.process_entry_point(&mut module, ep, fun_id)?;
1813        }
1814
1815        log::debug!("Patching...");
1816        {
1817            let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
1818                .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;
1819            nodes.reverse(); // we need dominated first
1820            let mut functions = module.functions.take();
1821            for fun_id in nodes {
1822                if fun_id > !(functions.len() as u32) {
1823                    // skip all the fake IDs registered for the entry points
1824                    continue;
1825                }
1826                let lookup = self.lookup_function.get_mut(&fun_id).unwrap();
1827                // take out the function from the old array
1828                let fun = mem::take(&mut functions[lookup.handle]);
1829                // add it to the newly formed arena, and adjust the lookup
1830                lookup.handle = module
1831                    .functions
1832                    .append(fun, functions.get_span(lookup.handle));
1833            }
1834        }
1835        // patch all the functions
1836        for (handle, fun) in module.functions.iter_mut() {
1837            self.patch_function(Some(handle), fun)?;
1838        }
1839        for ep in module.entry_points.iter_mut() {
1840            self.patch_function(None, &mut ep.function)?;
1841        }
1842
1843        // Check all the images and samplers to have consistent comparison property.
1844        for (handle, flags) in self.handle_sampling.drain() {
1845            if !image::patch_comparison_type(
1846                flags,
1847                module.global_variables.get_mut(handle),
1848                &mut module.types,
1849            ) {
1850                return Err(Error::InconsistentComparisonSampling(handle));
1851            }
1852        }
1853
1854        if !self.future_decor.is_empty() {
1855            log::debug!("Unused item decorations: {:?}", self.future_decor);
1856            self.future_decor.clear();
1857        }
1858        if !self.future_member_decor.is_empty() {
1859            log::debug!("Unused member decorations: {:?}", self.future_member_decor);
1860            self.future_member_decor.clear();
1861        }
1862
1863        Ok(module)
1864    }
1865
1866    fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {
1867        self.switch(ModuleState::Capability, inst.op)?;
1868        inst.expect(2)?;
1869        let capability = self.next()?;
1870        let cap =
1871            spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;
1872        if !SUPPORTED_CAPABILITIES.contains(&cap) {
1873            if self.options.strict_capabilities {
1874                return Err(Error::UnsupportedCapability(cap));
1875            } else {
1876                log::warn!("Unknown capability {cap:?}");
1877            }
1878        }
1879        Ok(())
1880    }
1881
1882    fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {
1883        self.switch(ModuleState::Extension, inst.op)?;
1884        inst.expect_at_least(2)?;
1885        let (name, left) = self.next_string(inst.wc - 1)?;
1886        if left != 0 {
1887            return Err(Error::InvalidOperand);
1888        }
1889        if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {
1890            return Err(Error::UnsupportedExtension(name));
1891        }
1892        Ok(())
1893    }
1894
1895    fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {
1896        self.switch(ModuleState::Extension, inst.op)?;
1897        inst.expect_at_least(3)?;
1898        let result_id = self.next()?;
1899        let (name, left) = self.next_string(inst.wc - 2)?;
1900        if left != 0 {
1901            return Err(Error::InvalidOperand);
1902        }
1903        if &name == "GLSL.std.450" {
1904            self.ext_glsl_id = Some(result_id);
1905        } else if &name == "NonSemantic.Shader.DebugInfo.100" {
1906            // We completely ignore this extension. All related instructions are
1907            // non-semantic and only for debug purposes, and the spec says they
1908            // are ignorable. Many compilers (dxc, slang, etc) will emit these
1909            // instructions depending on configuration.
1910            self.ext_non_semantic_id = Some(result_id);
1911        } else {
1912            return Err(Error::UnsupportedExtSet(name));
1913        }
1914        Ok(())
1915    }
1916
1917    fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {
1918        self.switch(ModuleState::MemoryModel, inst.op)?;
1919        inst.expect(3)?;
1920        let _addressing_model = self.next()?;
1921        let _memory_model = self.next()?;
1922        Ok(())
1923    }
1924
1925    fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {
1926        self.switch(ModuleState::EntryPoint, inst.op)?;
1927        inst.expect_at_least(4)?;
1928        let exec_model = self.next()?;
1929        let exec_model = spirv::ExecutionModel::from_u32(exec_model)
1930            .ok_or(Error::UnsupportedExecutionModel(exec_model))?;
1931        let function_id = self.next()?;
1932        let (name, left) = self.next_string(inst.wc - 3)?;
1933        let ep = EntryPoint {
1934            stage: match exec_model {
1935                spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,
1936                spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,
1937                spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,
1938                spirv::ExecutionModel::TaskEXT => crate::ShaderStage::Task,
1939                spirv::ExecutionModel::MeshEXT => crate::ShaderStage::Mesh,
1940                _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),
1941            },
1942            name,
1943            early_depth_test: None,
1944            workgroup_size: [0; 3],
1945            variable_ids: self.data.by_ref().take(left as usize).collect(),
1946        };
1947        self.lookup_entry_point.insert(function_id, ep);
1948        Ok(())
1949    }
1950
1951    fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
1952        use spirv::ExecutionMode;
1953
1954        self.switch(ModuleState::ExecutionMode, inst.op)?;
1955        inst.expect_at_least(3)?;
1956
1957        let ep_id = self.next()?;
1958        let mode_id = self.next()?;
1959        let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();
1960
1961        let ep = self
1962            .lookup_entry_point
1963            .get_mut(&ep_id)
1964            .ok_or(Error::InvalidId(ep_id))?;
1965        let mode =
1966            ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;
1967
1968        match mode {
1969            ExecutionMode::EarlyFragmentTests => {
1970                ep.early_depth_test = Some(crate::EarlyDepthTest::Force);
1971            }
1972            ExecutionMode::DepthUnchanged => {
1973                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1974                    if let &mut crate::EarlyDepthTest::Allow {
1975                        ref mut conservative,
1976                    } = early_depth_test
1977                    {
1978                        *conservative = crate::ConservativeDepth::Unchanged;
1979                    }
1980                } else {
1981                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1982                        conservative: crate::ConservativeDepth::Unchanged,
1983                    });
1984                }
1985            }
1986            ExecutionMode::DepthGreater => {
1987                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
1988                    if let &mut crate::EarlyDepthTest::Allow {
1989                        ref mut conservative,
1990                    } = early_depth_test
1991                    {
1992                        *conservative = crate::ConservativeDepth::GreaterEqual;
1993                    }
1994                } else {
1995                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
1996                        conservative: crate::ConservativeDepth::GreaterEqual,
1997                    });
1998                }
1999            }
2000            ExecutionMode::DepthLess => {
2001                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
2002                    if let &mut crate::EarlyDepthTest::Allow {
2003                        ref mut conservative,
2004                    } = early_depth_test
2005                    {
2006                        *conservative = crate::ConservativeDepth::LessEqual;
2007                    }
2008                } else {
2009                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
2010                        conservative: crate::ConservativeDepth::LessEqual,
2011                    });
2012                }
2013            }
2014            ExecutionMode::DepthReplacing => {
2015                // Ignored because it can be deduced from the IR.
2016            }
2017            ExecutionMode::OriginUpperLeft => {
2018                // Ignored because the other option (OriginLowerLeft) is not valid in Vulkan mode.
2019            }
2020            ExecutionMode::LocalSize => {
2021                ep.workgroup_size = [args[0], args[1], args[2]];
2022            }
2023            _ => {
2024                return Err(Error::UnsupportedExecutionMode(mode_id));
2025            }
2026        }
2027
2028        Ok(())
2029    }
2030
2031    fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {
2032        self.switch(ModuleState::Source, inst.op)?;
2033        inst.expect_at_least(3)?;
2034        let _id = self.next()?;
2035        let (_name, _) = self.next_string(inst.wc - 2)?;
2036        Ok(())
2037    }
2038
2039    fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {
2040        self.switch(ModuleState::Source, inst.op)?;
2041        for _ in 1..inst.wc {
2042            let _ = self.next()?;
2043        }
2044        Ok(())
2045    }
2046
2047    fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {
2048        self.switch(ModuleState::Source, inst.op)?;
2049        inst.expect_at_least(2)?;
2050        let (_name, _) = self.next_string(inst.wc - 1)?;
2051        Ok(())
2052    }
2053
2054    fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {
2055        self.switch(ModuleState::Name, inst.op)?;
2056        inst.expect_at_least(3)?;
2057        let id = self.next()?;
2058        let (name, left) = self.next_string(inst.wc - 2)?;
2059        if left != 0 {
2060            return Err(Error::InvalidOperand);
2061        }
2062        self.future_decor.entry(id).or_default().name = Some(name);
2063        Ok(())
2064    }
2065
2066    fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {
2067        self.switch(ModuleState::Name, inst.op)?;
2068        inst.expect_at_least(4)?;
2069        let id = self.next()?;
2070        let member = self.next()?;
2071        let (name, left) = self.next_string(inst.wc - 3)?;
2072        if left != 0 {
2073            return Err(Error::InvalidOperand);
2074        }
2075
2076        self.future_member_decor
2077            .entry((id, member))
2078            .or_default()
2079            .name = Some(name);
2080        Ok(())
2081    }
2082
2083    fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {
2084        self.switch(ModuleState::Name, inst.op)?;
2085        inst.expect_at_least(2)?;
2086        let (_info, left) = self.next_string(inst.wc - 1)?;
2087        //Note: string is ignored
2088        if left != 0 {
2089            return Err(Error::InvalidOperand);
2090        }
2091        Ok(())
2092    }
2093
2094    fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2095        self.switch(ModuleState::Annotation, inst.op)?;
2096        inst.expect_at_least(3)?;
2097        let id = self.next()?;
2098        let mut dec = self.future_decor.remove(&id).unwrap_or_default();
2099        self.next_decoration(inst, 2, &mut dec)?;
2100        self.future_decor.insert(id, dec);
2101        Ok(())
2102    }
2103
2104    fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
2105        self.switch(ModuleState::Annotation, inst.op)?;
2106        inst.expect_at_least(4)?;
2107        let id = self.next()?;
2108        let member = self.next()?;
2109
2110        let mut dec = self
2111            .future_member_decor
2112            .remove(&(id, member))
2113            .unwrap_or_default();
2114        self.next_decoration(inst, 3, &mut dec)?;
2115        self.future_member_decor.insert((id, member), dec);
2116        Ok(())
2117    }
2118
2119    fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {
2120        self.switch(ModuleState::Type, inst.op)?;
2121        inst.expect(2)?;
2122        let id = self.next()?;
2123        self.lookup_void_type = Some(id);
2124        Ok(())
2125    }
2126
2127    fn parse_type_bool(
2128        &mut self,
2129        inst: Instruction,
2130        module: &mut crate::Module,
2131    ) -> Result<(), Error> {
2132        let start = self.data_offset;
2133        self.switch(ModuleState::Type, inst.op)?;
2134        inst.expect(2)?;
2135        let id = self.next()?;
2136        let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);
2137        self.lookup_type.insert(
2138            id,
2139            LookupType {
2140                handle: module.types.insert(
2141                    crate::Type {
2142                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2143                        inner,
2144                    },
2145                    self.span_from_with_op(start),
2146                ),
2147                base_id: None,
2148            },
2149        );
2150        Ok(())
2151    }
2152
2153    fn parse_type_int(
2154        &mut self,
2155        inst: Instruction,
2156        module: &mut crate::Module,
2157    ) -> Result<(), Error> {
2158        let start = self.data_offset;
2159        self.switch(ModuleState::Type, inst.op)?;
2160        inst.expect(4)?;
2161        let id = self.next()?;
2162        let width = self.next()?;
2163        let sign = self.next()?;
2164        let inner = crate::TypeInner::Scalar(crate::Scalar {
2165            kind: match sign {
2166                0 => crate::ScalarKind::Uint,
2167                1 => crate::ScalarKind::Sint,
2168                _ => return Err(Error::InvalidSign(sign)),
2169            },
2170            width: map_width(width)?,
2171        });
2172        self.lookup_type.insert(
2173            id,
2174            LookupType {
2175                handle: module.types.insert(
2176                    crate::Type {
2177                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2178                        inner,
2179                    },
2180                    self.span_from_with_op(start),
2181                ),
2182                base_id: None,
2183            },
2184        );
2185        Ok(())
2186    }
2187
2188    fn parse_type_float(
2189        &mut self,
2190        inst: Instruction,
2191        module: &mut crate::Module,
2192    ) -> Result<(), Error> {
2193        let start = self.data_offset;
2194        self.switch(ModuleState::Type, inst.op)?;
2195        inst.expect(3)?;
2196        let id = self.next()?;
2197        let width = self.next()?;
2198        let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));
2199        self.lookup_type.insert(
2200            id,
2201            LookupType {
2202                handle: module.types.insert(
2203                    crate::Type {
2204                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2205                        inner,
2206                    },
2207                    self.span_from_with_op(start),
2208                ),
2209                base_id: None,
2210            },
2211        );
2212        Ok(())
2213    }
2214
2215    fn parse_type_vector(
2216        &mut self,
2217        inst: Instruction,
2218        module: &mut crate::Module,
2219    ) -> Result<(), Error> {
2220        let start = self.data_offset;
2221        self.switch(ModuleState::Type, inst.op)?;
2222        inst.expect(4)?;
2223        let id = self.next()?;
2224        let type_id = self.next()?;
2225        let type_lookup = self.lookup_type.lookup(type_id)?;
2226        let scalar = match module.types[type_lookup.handle].inner {
2227            crate::TypeInner::Scalar(scalar) => scalar,
2228            _ => return Err(Error::InvalidInnerType(type_id)),
2229        };
2230        let component_count = self.next()?;
2231        let inner = crate::TypeInner::Vector {
2232            size: map_vector_size(component_count)?,
2233            scalar,
2234        };
2235        self.lookup_type.insert(
2236            id,
2237            LookupType {
2238                handle: module.types.insert(
2239                    crate::Type {
2240                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
2241                        inner,
2242                    },
2243                    self.span_from_with_op(start),
2244                ),
2245                base_id: Some(type_id),
2246            },
2247        );
2248        Ok(())
2249    }
2250
2251    fn parse_type_matrix(
2252        &mut self,
2253        inst: Instruction,
2254        module: &mut crate::Module,
2255    ) -> Result<(), Error> {
2256        let start = self.data_offset;
2257        self.switch(ModuleState::Type, inst.op)?;
2258        inst.expect(4)?;
2259        let id = self.next()?;
2260        let vector_type_id = self.next()?;
2261        let num_columns = self.next()?;
2262        let decor = self.future_decor.remove(&id);
2263
2264        let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;
2265        let inner = match module.types[vector_type_lookup.handle].inner {
2266            crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {
2267                columns: map_vector_size(num_columns)?,
2268                rows: size,
2269                scalar,
2270            },
2271            _ => return Err(Error::InvalidInnerType(vector_type_id)),
2272        };
2273
2274        self.lookup_type.insert(
2275            id,
2276            LookupType {
2277                handle: module.types.insert(
2278                    crate::Type {
2279                        name: decor.and_then(|dec| dec.name),
2280                        inner,
2281                    },
2282                    self.span_from_with_op(start),
2283                ),
2284                base_id: Some(vector_type_id),
2285            },
2286        );
2287        Ok(())
2288    }
2289
2290    fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {
2291        self.switch(ModuleState::Type, inst.op)?;
2292        inst.expect_at_least(3)?;
2293        let id = self.next()?;
2294        let return_type_id = self.next()?;
2295        let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();
2296        self.lookup_function_type.insert(
2297            id,
2298            LookupFunctionType {
2299                parameter_type_ids,
2300                return_type_id,
2301            },
2302        );
2303        Ok(())
2304    }
2305
2306    fn parse_type_pointer(
2307        &mut self,
2308        inst: Instruction,
2309        module: &mut crate::Module,
2310    ) -> Result<(), Error> {
2311        let start = self.data_offset;
2312        self.switch(ModuleState::Type, inst.op)?;
2313        inst.expect(4)?;
2314        let id = self.next()?;
2315        let storage_class = self.next()?;
2316        let type_id = self.next()?;
2317
2318        let decor = self.future_decor.remove(&id);
2319        let base_lookup_ty = self.lookup_type.lookup(type_id)?;
2320        let base_inner = &module.types[base_lookup_ty.handle].inner;
2321
2322        let space = if let Some(space) = base_inner.pointer_space() {
2323            space
2324        } else if self
2325            .lookup_storage_buffer_types
2326            .contains_key(&base_lookup_ty.handle)
2327        {
2328            crate::AddressSpace::Storage {
2329                access: crate::StorageAccess::default(),
2330            }
2331        } else {
2332            match map_storage_class(storage_class)? {
2333                ExtendedClass::Global(space) => space,
2334                ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,
2335            }
2336        };
2337
2338        // We don't support pointers to runtime-sized arrays in the `Uniform`
2339        // storage class with the `BufferBlock` decoration. Runtime-sized arrays
2340        // should be in the StorageBuffer class.
2341        if let crate::TypeInner::Array {
2342            size: crate::ArraySize::Dynamic,
2343            ..
2344        } = *base_inner
2345        {
2346            match space {
2347                crate::AddressSpace::Storage { .. } => {}
2348                _ => {
2349                    return Err(Error::UnsupportedRuntimeArrayStorageClass);
2350                }
2351            }
2352        }
2353
2354        // Don't bother with pointer stuff for `Handle` types.
2355        let lookup_ty = if space == crate::AddressSpace::Handle {
2356            base_lookup_ty.clone()
2357        } else {
2358            LookupType {
2359                handle: module.types.insert(
2360                    crate::Type {
2361                        name: decor.and_then(|dec| dec.name),
2362                        inner: crate::TypeInner::Pointer {
2363                            base: base_lookup_ty.handle,
2364                            space,
2365                        },
2366                    },
2367                    self.span_from_with_op(start),
2368                ),
2369                base_id: Some(type_id),
2370            }
2371        };
2372        self.lookup_type.insert(id, lookup_ty);
2373        Ok(())
2374    }
2375
2376    fn parse_type_array(
2377        &mut self,
2378        inst: Instruction,
2379        module: &mut crate::Module,
2380    ) -> Result<(), Error> {
2381        let start = self.data_offset;
2382        self.switch(ModuleState::Type, inst.op)?;
2383        inst.expect(4)?;
2384        let id = self.next()?;
2385        let type_id = self.next()?;
2386        let length_id = self.next()?;
2387        let length_const = self.lookup_constant.lookup(length_id)?;
2388
2389        let size = resolve_constant(module.to_ctx(), &length_const.inner)
2390            .and_then(NonZeroU32::new)
2391            .ok_or(Error::InvalidArraySize(length_id))?;
2392
2393        let decor = self.future_decor.remove(&id).unwrap_or_default();
2394        let base = self.lookup_type.lookup(type_id)?.handle;
2395
2396        self.layouter.update(module.to_ctx()).unwrap();
2397
2398        // HACK if the underlying type is an image or a sampler, let's assume
2399        //      that we're dealing with a binding-array
2400        //
2401        // Note that it's not a strictly correct assumption, but rather a trade
2402        // off caused by an impedance mismatch between SPIR-V's and Naga's type
2403        // systems - Naga distinguishes between arrays and binding-arrays via
2404        // types (i.e. both kinds of arrays are just different types), while
2405        // SPIR-V distinguishes between them through usage - e.g. given:
2406        //
2407        // ```
2408        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
2409        // %uint_256 = OpConstant %uint 256
2410        // %image_array = OpTypeArray %image %uint_256
2411        // ```
2412        //
2413        // ```
2414        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
2415        // %uint_256 = OpConstant %uint 256
2416        // %image_array = OpTypeArray %image %uint_256
2417        // %image_array_ptr = OpTypePointer UniformConstant %image_array
2418        // ```
2419        //
2420        // ... in the first case, `%image_array` should technically correspond
2421        // to `TypeInner::Array`, while in the second case it should say
2422        // `TypeInner::BindingArray` (kinda, depending on whether `%image_array`
2423        // is ever used as a freestanding type or rather always through the
2424        // pointer-indirection).
2425        //
2426        // Anyway, at the moment we don't support other kinds of image / sampler
2427        // arrays than those binding-based, so this assumption is pretty safe
2428        // for now.
2429        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2430            module.types[base].inner
2431        {
2432            crate::TypeInner::BindingArray {
2433                base,
2434                size: crate::ArraySize::Constant(size),
2435            }
2436        } else {
2437            crate::TypeInner::Array {
2438                base,
2439                size: crate::ArraySize::Constant(size),
2440                stride: match decor.array_stride {
2441                    Some(stride) => stride.get(),
2442                    None => self.layouter[base].to_stride(),
2443                },
2444            }
2445        };
2446
2447        self.lookup_type.insert(
2448            id,
2449            LookupType {
2450                handle: module.types.insert(
2451                    crate::Type {
2452                        name: decor.name,
2453                        inner,
2454                    },
2455                    self.span_from_with_op(start),
2456                ),
2457                base_id: Some(type_id),
2458            },
2459        );
2460        Ok(())
2461    }
2462
2463    fn parse_type_runtime_array(
2464        &mut self,
2465        inst: Instruction,
2466        module: &mut crate::Module,
2467    ) -> Result<(), Error> {
2468        let start = self.data_offset;
2469        self.switch(ModuleState::Type, inst.op)?;
2470        inst.expect(3)?;
2471        let id = self.next()?;
2472        let type_id = self.next()?;
2473
2474        let decor = self.future_decor.remove(&id).unwrap_or_default();
2475        let base = self.lookup_type.lookup(type_id)?.handle;
2476
2477        self.layouter.update(module.to_ctx()).unwrap();
2478
2479        // HACK same case as in `parse_type_array()`
2480        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
2481            module.types[base].inner
2482        {
2483            crate::TypeInner::BindingArray {
2484                base: self.lookup_type.lookup(type_id)?.handle,
2485                size: crate::ArraySize::Dynamic,
2486            }
2487        } else {
2488            crate::TypeInner::Array {
2489                base: self.lookup_type.lookup(type_id)?.handle,
2490                size: crate::ArraySize::Dynamic,
2491                stride: match decor.array_stride {
2492                    Some(stride) => stride.get(),
2493                    None => self.layouter[base].to_stride(),
2494                },
2495            }
2496        };
2497
2498        self.lookup_type.insert(
2499            id,
2500            LookupType {
2501                handle: module.types.insert(
2502                    crate::Type {
2503                        name: decor.name,
2504                        inner,
2505                    },
2506                    self.span_from_with_op(start),
2507                ),
2508                base_id: Some(type_id),
2509            },
2510        );
2511        Ok(())
2512    }
2513
2514    fn parse_type_struct(
2515        &mut self,
2516        inst: Instruction,
2517        module: &mut crate::Module,
2518    ) -> Result<(), Error> {
2519        let start = self.data_offset;
2520        self.switch(ModuleState::Type, inst.op)?;
2521        inst.expect_at_least(2)?;
2522        let id = self.next()?;
2523        let parent_decor = self.future_decor.remove(&id);
2524        let is_storage_buffer = parent_decor
2525            .as_ref()
2526            .is_some_and(|decor| decor.storage_buffer);
2527
2528        self.layouter.update(module.to_ctx()).unwrap();
2529
2530        let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
2531        let mut member_lookups = Vec::with_capacity(members.capacity());
2532        let mut storage_access = crate::StorageAccess::empty();
2533        let mut span = 0;
2534        let mut alignment = Alignment::ONE;
2535        for i in 0..u32::from(inst.wc) - 2 {
2536            let type_id = self.next()?;
2537            let ty = self.lookup_type.lookup(type_id)?.handle;
2538            let decor = self
2539                .future_member_decor
2540                .remove(&(id, i))
2541                .unwrap_or_default();
2542
2543            storage_access |= decor.flags.to_storage_access();
2544
2545            member_lookups.push(LookupMember {
2546                type_id,
2547                row_major: decor.matrix_major == Some(Majority::Row),
2548            });
2549
2550            let member_alignment = self.layouter[ty].alignment;
2551            span = member_alignment.round_up(span);
2552            alignment = member_alignment.max(alignment);
2553
2554            let binding = decor.io_binding().ok();
2555            if let Some(offset) = decor.offset {
2556                span = offset;
2557            }
2558            let offset = span;
2559
2560            span += self.layouter[ty].size;
2561
2562            let inner = &module.types[ty].inner;
2563            if let crate::TypeInner::Matrix {
2564                columns,
2565                rows,
2566                scalar,
2567            } = *inner
2568            {
2569                if let Some(stride) = decor.matrix_stride {
2570                    let expected_stride = Alignment::from(rows) * scalar.width as u32;
2571                    if stride.get() != expected_stride {
2572                        return Err(Error::UnsupportedMatrixStride {
2573                            stride: stride.get(),
2574                            columns: columns as u8,
2575                            rows: rows as u8,
2576                            width: scalar.width,
2577                        });
2578                    }
2579                }
2580            }
2581
2582            members.push(crate::StructMember {
2583                name: decor.name,
2584                ty,
2585                binding,
2586                offset,
2587            });
2588        }
2589
2590        span = alignment.round_up(span);
2591
2592        let inner = crate::TypeInner::Struct { span, members };
2593
2594        let ty_handle = module.types.insert(
2595            crate::Type {
2596                name: parent_decor.and_then(|dec| dec.name),
2597                inner,
2598            },
2599            self.span_from_with_op(start),
2600        );
2601
2602        if is_storage_buffer {
2603            self.lookup_storage_buffer_types
2604                .insert(ty_handle, storage_access);
2605        }
2606        for (i, member_lookup) in member_lookups.into_iter().enumerate() {
2607            self.lookup_member
2608                .insert((ty_handle, i as u32), member_lookup);
2609        }
2610        self.lookup_type.insert(
2611            id,
2612            LookupType {
2613                handle: ty_handle,
2614                base_id: None,
2615            },
2616        );
2617        Ok(())
2618    }
2619
2620    fn parse_type_image(
2621        &mut self,
2622        inst: Instruction,
2623        module: &mut crate::Module,
2624    ) -> Result<(), Error> {
2625        let start = self.data_offset;
2626        self.switch(ModuleState::Type, inst.op)?;
2627        inst.expect(9)?;
2628
2629        let id = self.next()?;
2630        let sample_type_id = self.next()?;
2631        let dim = self.next()?;
2632        let is_depth = self.next()?;
2633        let is_array = self.next()? != 0;
2634        let is_msaa = self.next()? != 0;
2635        let is_sampled = self.next()?;
2636        let format = self.next()?;
2637
2638        let dim = map_image_dim(dim)?;
2639        let decor = self.future_decor.remove(&id).unwrap_or_default();
2640
2641        // ensure there is a type for texture coordinate without extra components
2642        module.types.insert(
2643            crate::Type {
2644                name: None,
2645                inner: {
2646                    let scalar = crate::Scalar::F32;
2647                    match dim.required_coordinate_size() {
2648                        None => crate::TypeInner::Scalar(scalar),
2649                        Some(size) => crate::TypeInner::Vector { size, scalar },
2650                    }
2651                },
2652            },
2653            Default::default(),
2654        );
2655
2656        let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;
2657        let kind = module.types[base_handle]
2658            .inner
2659            .scalar_kind()
2660            .ok_or(Error::InvalidImageBaseType(base_handle))?;
2661
2662        let inner = crate::TypeInner::Image {
2663            class: if is_depth == 1 {
2664                if is_sampled == 2 {
2665                    return Err(Error::InvalidImageDepthStorage);
2666                }
2667
2668                crate::ImageClass::Depth { multi: is_msaa }
2669            }
2670            // If we have an unknown format and storage texture, this is
2671            // StorageRead/WriteWithoutFormat. We don't currently support
2672            // this.
2673            else if is_sampled == 2 && format == 0 {
2674                return Err(Error::InvalidStorageImageWithoutFormat);
2675            }
2676            // If we have explicit class information (is_sampled = 2 = Storage), use it.
2677            //
2678            // If we have unknown class information (is_sampled = 0 = Unknown), infer the
2679            // class from the presence of an explicit format.
2680            else if format != 0 && (is_sampled == 0 || is_sampled == 2) {
2681                crate::ImageClass::Storage {
2682                    format: map_image_format(format)?,
2683                    access: crate::StorageAccess::default(),
2684                }
2685            }
2686            // We will hit this case either when sampled is 1, or if we have unknown
2687            // sampling information or when sampled is 0 and we have no explicit format.
2688            else {
2689                crate::ImageClass::Sampled {
2690                    kind,
2691                    multi: is_msaa,
2692                }
2693            },
2694            dim,
2695            arrayed: is_array,
2696        };
2697
2698        let handle = module.types.insert(
2699            crate::Type {
2700                name: decor.name,
2701                inner,
2702            },
2703            self.span_from_with_op(start),
2704        );
2705
2706        self.lookup_type.insert(
2707            id,
2708            LookupType {
2709                handle,
2710                base_id: Some(sample_type_id),
2711            },
2712        );
2713        Ok(())
2714    }
2715
2716    fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {
2717        self.switch(ModuleState::Type, inst.op)?;
2718        inst.expect(3)?;
2719        let id = self.next()?;
2720        let image_id = self.next()?;
2721        self.lookup_type.insert(
2722            id,
2723            LookupType {
2724                handle: self.lookup_type.lookup(image_id)?.handle,
2725                base_id: Some(image_id),
2726            },
2727        );
2728        Ok(())
2729    }
2730
2731    fn parse_type_sampler(
2732        &mut self,
2733        inst: Instruction,
2734        module: &mut crate::Module,
2735    ) -> Result<(), Error> {
2736        let start = self.data_offset;
2737        self.switch(ModuleState::Type, inst.op)?;
2738        inst.expect(2)?;
2739        let id = self.next()?;
2740        let decor = self.future_decor.remove(&id).unwrap_or_default();
2741        let handle = module.types.insert(
2742            crate::Type {
2743                name: decor.name,
2744                inner: crate::TypeInner::Sampler { comparison: false },
2745            },
2746            self.span_from_with_op(start),
2747        );
2748        self.lookup_type.insert(
2749            id,
2750            LookupType {
2751                handle,
2752                base_id: None,
2753            },
2754        );
2755        Ok(())
2756    }
2757
2758    fn parse_constant(
2759        &mut self,
2760        inst: Instruction,
2761        module: &mut crate::Module,
2762    ) -> Result<(), Error> {
2763        let start = self.data_offset;
2764        self.switch(ModuleState::Type, inst.op)?;
2765        inst.expect_at_least(4)?;
2766        let type_id = self.next()?;
2767        let id = self.next()?;
2768        let type_lookup = self.lookup_type.lookup(type_id)?;
2769        let ty = type_lookup.handle;
2770
2771        let literal = match module.types[ty].inner {
2772            crate::TypeInner::Scalar(crate::Scalar {
2773                kind: crate::ScalarKind::Uint,
2774                width,
2775            }) => {
2776                let low = self.next()?;
2777                match width {
2778                    4 => crate::Literal::U32(low),
2779                    8 => {
2780                        inst.expect(5)?;
2781                        let high = self.next()?;
2782                        crate::Literal::U64((u64::from(high) << 32) | u64::from(low))
2783                    }
2784                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2785                }
2786            }
2787            crate::TypeInner::Scalar(crate::Scalar {
2788                kind: crate::ScalarKind::Sint,
2789                width,
2790            }) => {
2791                let low = self.next()?;
2792                match width {
2793                    4 => crate::Literal::I32(low as i32),
2794                    8 => {
2795                        inst.expect(5)?;
2796                        let high = self.next()?;
2797                        crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)
2798                    }
2799                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2800                }
2801            }
2802            crate::TypeInner::Scalar(crate::Scalar {
2803                kind: crate::ScalarKind::Float,
2804                width,
2805            }) => {
2806                let low = self.next()?;
2807                match width {
2808                    // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Literal
2809                    // If a numeric type’s bit width is less than 32-bits, the value appears in the low-order bits of the word.
2810                    2 => crate::Literal::F16(f16::from_bits(low as u16)),
2811                    4 => crate::Literal::F32(f32::from_bits(low)),
2812                    8 => {
2813                        inst.expect(5)?;
2814                        let high = self.next()?;
2815                        crate::Literal::F64(f64::from_bits(
2816                            (u64::from(high) << 32) | u64::from(low),
2817                        ))
2818                    }
2819                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
2820                }
2821            }
2822            _ => return Err(Error::UnsupportedType(type_lookup.handle)),
2823        };
2824
2825        let span = self.span_from_with_op(start);
2826
2827        let init = module
2828            .global_expressions
2829            .append(crate::Expression::Literal(literal), span);
2830
2831        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2832    }
2833
2834    fn parse_composite_constant(
2835        &mut self,
2836        inst: Instruction,
2837        module: &mut crate::Module,
2838    ) -> Result<(), Error> {
2839        let start = self.data_offset;
2840        self.switch(ModuleState::Type, inst.op)?;
2841        inst.expect_at_least(3)?;
2842        let type_id = self.next()?;
2843        let id = self.next()?;
2844
2845        let type_lookup = self.lookup_type.lookup(type_id)?;
2846        let ty = type_lookup.handle;
2847
2848        let mut components = Vec::with_capacity(inst.wc as usize - 3);
2849        for _ in 0..components.capacity() {
2850            let start = self.data_offset;
2851            let component_id = self.next()?;
2852            let span = self.span_from_with_op(start);
2853            let constant = self.lookup_constant.lookup(component_id)?;
2854            let expr = module
2855                .global_expressions
2856                .append(constant.inner.to_expr(), span);
2857            components.push(expr);
2858        }
2859
2860        let span = self.span_from_with_op(start);
2861
2862        let init = module
2863            .global_expressions
2864            .append(crate::Expression::Compose { ty, components }, span);
2865
2866        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2867    }
2868
2869    fn parse_null_constant(
2870        &mut self,
2871        inst: Instruction,
2872        module: &mut crate::Module,
2873    ) -> Result<(), Error> {
2874        let start = self.data_offset;
2875        self.switch(ModuleState::Type, inst.op)?;
2876        inst.expect(3)?;
2877        let type_id = self.next()?;
2878        let id = self.next()?;
2879        let span = self.span_from_with_op(start);
2880
2881        let type_lookup = self.lookup_type.lookup(type_id)?;
2882        let ty = type_lookup.handle;
2883
2884        let init = module
2885            .global_expressions
2886            .append(crate::Expression::ZeroValue(ty), span);
2887
2888        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2889    }
2890
2891    fn parse_bool_constant(
2892        &mut self,
2893        inst: Instruction,
2894        value: bool,
2895        module: &mut crate::Module,
2896    ) -> Result<(), Error> {
2897        let start = self.data_offset;
2898        self.switch(ModuleState::Type, inst.op)?;
2899        inst.expect(3)?;
2900        let type_id = self.next()?;
2901        let id = self.next()?;
2902        let span = self.span_from_with_op(start);
2903
2904        let type_lookup = self.lookup_type.lookup(type_id)?;
2905        let ty = type_lookup.handle;
2906
2907        let init = module.global_expressions.append(
2908            crate::Expression::Literal(crate::Literal::Bool(value)),
2909            span,
2910        );
2911
2912        self.insert_parsed_constant(module, id, type_id, ty, init, span)
2913    }
2914
2915    fn insert_parsed_constant(
2916        &mut self,
2917        module: &mut crate::Module,
2918        id: u32,
2919        type_id: u32,
2920        ty: Handle<crate::Type>,
2921        init: Handle<crate::Expression>,
2922        span: crate::Span,
2923    ) -> Result<(), Error> {
2924        let decor = self.future_decor.remove(&id).unwrap_or_default();
2925
2926        let inner = if let Some(id) = decor.specialization_constant_id {
2927            let o = crate::Override {
2928                name: decor.name,
2929                id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),
2930                ty,
2931                init: Some(init),
2932            };
2933            Constant::Override(module.overrides.append(o, span))
2934        } else {
2935            let c = crate::Constant {
2936                name: decor.name,
2937                ty,
2938                init,
2939            };
2940            Constant::Constant(module.constants.append(c, span))
2941        };
2942
2943        self.lookup_constant
2944            .insert(id, LookupConstant { inner, type_id });
2945        Ok(())
2946    }
2947
2948    fn parse_global_variable(
2949        &mut self,
2950        inst: Instruction,
2951        module: &mut crate::Module,
2952    ) -> Result<(), Error> {
2953        let start = self.data_offset;
2954        self.switch(ModuleState::Type, inst.op)?;
2955        inst.expect_at_least(4)?;
2956        let type_id = self.next()?;
2957        let id = self.next()?;
2958        let storage_class = self.next()?;
2959        let init = if inst.wc > 4 {
2960            inst.expect(5)?;
2961            let start = self.data_offset;
2962            let init_id = self.next()?;
2963            let span = self.span_from_with_op(start);
2964            let lconst = self.lookup_constant.lookup(init_id)?;
2965            let expr = module
2966                .global_expressions
2967                .append(lconst.inner.to_expr(), span);
2968            Some(expr)
2969        } else {
2970            None
2971        };
2972        let span = self.span_from_with_op(start);
2973        let dec = self.future_decor.remove(&id).unwrap_or_default();
2974
2975        let original_ty = self.lookup_type.lookup(type_id)?.handle;
2976        let mut ty = original_ty;
2977
2978        if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
2979            ty = base;
2980        }
2981
2982        if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
2983            // Inside `parse_type_array()` we guess that an array of images or
2984            // samplers must be a binding array, and here we validate that guess
2985            if dec.desc_set.is_none() || dec.desc_index.is_none() {
2986                return Err(Error::NonBindingArrayOfImageOrSamplers);
2987            }
2988        }
2989
2990        if let crate::TypeInner::Image {
2991            dim,
2992            arrayed,
2993            class: crate::ImageClass::Storage { format, access: _ },
2994        } = module.types[ty].inner
2995        {
2996            // Storage image types in IR have to contain the access, but not in the SPIR-V.
2997            // The same image type in SPIR-V can be used (and has to be used) for multiple images.
2998            // So we copy the type out and apply the variable access decorations.
2999            let access = dec.flags.to_storage_access();
3000
3001            ty = module.types.insert(
3002                crate::Type {
3003                    name: None,
3004                    inner: crate::TypeInner::Image {
3005                        dim,
3006                        arrayed,
3007                        class: crate::ImageClass::Storage { format, access },
3008                    },
3009                },
3010                Default::default(),
3011            );
3012        }
3013
3014        let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
3015            Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
3016            None => map_storage_class(storage_class)?,
3017        };
3018
3019        let (inner, var) = match ext_class {
3020            ExtendedClass::Global(mut space) => {
3021                if let crate::AddressSpace::Storage { ref mut access } = space {
3022                    *access &= dec.flags.to_storage_access();
3023                }
3024                let var = crate::GlobalVariable {
3025                    binding: dec.resource_binding(),
3026                    name: dec.name,
3027                    space,
3028                    ty,
3029                    init,
3030                    memory_decorations: dec.flags.to_memory_decorations(),
3031                };
3032                (Variable::Global, var)
3033            }
3034            ExtendedClass::Input => {
3035                let binding = dec.io_binding()?;
3036                let mut unsigned_ty = ty;
3037                if let crate::Binding::BuiltIn(built_in) = binding {
3038                    let needs_inner_uint = match built_in {
3039                        crate::BuiltIn::BaseInstance
3040                        | crate::BuiltIn::BaseVertex
3041                        | crate::BuiltIn::InstanceIndex
3042                        | crate::BuiltIn::SampleIndex
3043                        | crate::BuiltIn::VertexIndex
3044                        | crate::BuiltIn::PrimitiveIndex
3045                        | crate::BuiltIn::LocalInvocationIndex => {
3046                            Some(crate::TypeInner::Scalar(crate::Scalar::U32))
3047                        }
3048                        crate::BuiltIn::GlobalInvocationId
3049                        | crate::BuiltIn::LocalInvocationId
3050                        | crate::BuiltIn::WorkGroupId
3051                        | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {
3052                            size: crate::VectorSize::Tri,
3053                            scalar: crate::Scalar::U32,
3054                        }),
3055                        crate::BuiltIn::Barycentric { perspective: false } => {
3056                            Some(crate::TypeInner::Vector {
3057                                size: crate::VectorSize::Tri,
3058                                scalar: crate::Scalar::F32,
3059                            })
3060                        }
3061                        _ => None,
3062                    };
3063                    if let (Some(inner), Some(crate::ScalarKind::Sint)) =
3064                        (needs_inner_uint, module.types[ty].inner.scalar_kind())
3065                    {
3066                        unsigned_ty = module
3067                            .types
3068                            .insert(crate::Type { name: None, inner }, Default::default());
3069                    }
3070                }
3071
3072                let var = crate::GlobalVariable {
3073                    name: dec.name.clone(),
3074                    space: crate::AddressSpace::Private,
3075                    binding: None,
3076                    ty,
3077                    init: None,
3078                    memory_decorations: crate::MemoryDecorations::empty(),
3079                };
3080
3081                let inner = Variable::Input(crate::FunctionArgument {
3082                    name: dec.name,
3083                    ty: unsigned_ty,
3084                    binding: Some(binding),
3085                });
3086                (inner, var)
3087            }
3088            ExtendedClass::Output => {
3089                // For output interface blocks, this would be a structure.
3090                let binding = dec.io_binding().ok();
3091                let init = match binding {
3092                    Some(crate::Binding::BuiltIn(built_in)) => {
3093                        match null::generate_default_built_in(
3094                            Some(built_in),
3095                            ty,
3096                            &mut module.global_expressions,
3097                            span,
3098                        ) {
3099                            Ok(handle) => Some(handle),
3100                            Err(e) => {
3101                                log::warn!("Failed to initialize output built-in: {e}");
3102                                None
3103                            }
3104                        }
3105                    }
3106                    Some(crate::Binding::Location { .. }) => None,
3107                    None => match module.types[ty].inner {
3108                        crate::TypeInner::Struct { ref members, .. } => {
3109                            let mut components = Vec::with_capacity(members.len());
3110                            for member in members.iter() {
3111                                let built_in = match member.binding {
3112                                    Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),
3113                                    _ => None,
3114                                };
3115                                let handle = null::generate_default_built_in(
3116                                    built_in,
3117                                    member.ty,
3118                                    &mut module.global_expressions,
3119                                    span,
3120                                )?;
3121                                components.push(handle);
3122                            }
3123                            Some(
3124                                module
3125                                    .global_expressions
3126                                    .append(crate::Expression::Compose { ty, components }, span),
3127                            )
3128                        }
3129                        _ => None,
3130                    },
3131                };
3132
3133                let var = crate::GlobalVariable {
3134                    name: dec.name,
3135                    space: crate::AddressSpace::Private,
3136                    binding: None,
3137                    ty,
3138                    init,
3139                    memory_decorations: crate::MemoryDecorations::empty(),
3140                };
3141                let inner = Variable::Output(crate::FunctionResult { ty, binding });
3142                (inner, var)
3143            }
3144        };
3145
3146        let handle = module.global_variables.append(var, span);
3147
3148        if module.types[ty].inner.can_comparison_sample(module) {
3149            log::debug!("\t\ttracking {handle:?} for sampling properties");
3150
3151            self.handle_sampling
3152                .insert(handle, image::SamplingFlags::empty());
3153        }
3154
3155        self.lookup_variable.insert(
3156            id,
3157            LookupVariable {
3158                inner,
3159                handle,
3160                type_id,
3161            },
3162        );
3163        Ok(())
3164    }
3165
3166    /// Record an atomic access to some component of a global variable.
3167    ///
3168    /// Given `handle`, an expression referring to a scalar that has had an
3169    /// atomic operation applied to it, descend into the expression, noting
3170    /// which global variable it ultimately refers to, and which struct fields
3171    /// of that global's value it accesses.
3172    ///
3173    /// Return the handle of the type of the expression.
3174    ///
3175    /// If the expression doesn't actually refer to something in a global
3176    /// variable, we can't upgrade its type in a way that Naga validation would
3177    /// pass, so reject the input instead.
3178    fn record_atomic_access(
3179        &mut self,
3180        ctx: &BlockContext,
3181        handle: Handle<crate::Expression>,
3182    ) -> Result<Handle<crate::Type>, Error> {
3183        log::debug!("\t\tlocating global variable in {handle:?}");
3184        match ctx.expressions[handle] {
3185            crate::Expression::Access { base, index } => {
3186                log::debug!("\t\t  access {handle:?} {index:?}");
3187                let ty = self.record_atomic_access(ctx, base)?;
3188                let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {
3189                    unreachable!("Atomic operations on Access expressions only work for arrays");
3190                };
3191                Ok(base)
3192            }
3193            crate::Expression::AccessIndex { base, index } => {
3194                log::debug!("\t\t  access index {handle:?} {index:?}");
3195                let ty = self.record_atomic_access(ctx, base)?;
3196                match ctx.module.types[ty].inner {
3197                    crate::TypeInner::Struct { ref members, .. } => {
3198                        let index = index as usize;
3199                        self.upgrade_atomics.insert_field(ty, index);
3200                        Ok(members[index].ty)
3201                    }
3202                    crate::TypeInner::Array { base, .. } => {
3203                        Ok(base)
3204                    }
3205                    _ => unreachable!("Atomic operations on AccessIndex expressions only work for structs and arrays"),
3206                }
3207            }
3208            crate::Expression::GlobalVariable(h) => {
3209                log::debug!("\t\t  found {h:?}");
3210                self.upgrade_atomics.insert_global(h);
3211                Ok(ctx.module.global_variables[h].ty)
3212            }
3213            _ => Err(Error::AtomicUpgradeError(
3214                crate::front::atomic_upgrade::Error::GlobalVariableMissing,
3215            )),
3216        }
3217    }
3218}
3219
3220fn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {
3221    let constant = match *constant {
3222        Constant::Constant(constant) => constant,
3223        Constant::Override(_) => return None,
3224    };
3225    match gctx.global_expressions[gctx.constants[constant].init] {
3226        crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),
3227        crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),
3228        _ => None,
3229    }
3230}
3231
3232pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {
3233    if !data.len().is_multiple_of(4) {
3234        return Err(Error::IncompleteData);
3235    }
3236
3237    let words = data
3238        .chunks(4)
3239        .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
3240    Frontend::new(words, options).parse()
3241}
3242
3243/// Helper function to check if `child` is in the scope of `parent`
3244fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
3245    loop {
3246        if child == parent {
3247            // The child is in the scope parent
3248            break true;
3249        } else if child == 0 {
3250            // Searched finished at the root the child isn't in the parent's body
3251            break false;
3252        }
3253
3254        child = block_ctx.bodies[child].parent;
3255    }
3256}
3257
3258#[cfg(test)]
3259mod test {
3260    use alloc::vec;
3261
3262    #[test]
3263    fn parse() {
3264        let bin = vec![
3265            // Magic number.           Version number: 1.0.
3266            0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
3267            // Generator number: 0.    Bound: 0.
3268            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved word: 0.
3269            0x00, 0x00, 0x00, 0x00, // OpMemoryModel.          Logical.
3270            0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // GLSL450.
3271            0x01, 0x00, 0x00, 0x00,
3272        ];
3273        let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
3274    }
3275}