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