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 null;
35
36pub use error::Error;
37
38use alloc::{borrow::ToOwned, format, string::String, vec, vec::Vec};
39use core::{convert::TryInto, mem, num::NonZeroU32};
40
41use half::f16;
42use petgraph::graphmap::GraphMap;
43
44use super::atomic_upgrade::Upgrades;
45use crate::{
46    arena::{Arena, Handle, UniqueArena},
47    path_like::PathLikeOwned,
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    // tricky ones
86    spirv::Capability::UniformBufferArrayDynamicIndexing,
87    spirv::Capability::StorageBufferArrayDynamicIndexing,
88];
89pub const SUPPORTED_EXTENSIONS: &[&str] = &[
90    "SPV_KHR_storage_buffer_storage_class",
91    "SPV_KHR_vulkan_memory_model",
92    "SPV_KHR_multiview",
93    "SPV_EXT_shader_atomic_float_add",
94    "SPV_KHR_16bit_storage",
95];
96pub const SUPPORTED_EXT_SETS: &[&str] = &["GLSL.std.450"];
97
98#[derive(Copy, Clone)]
99pub struct Instruction {
100    op: spirv::Op,
101    wc: u16,
102}
103
104impl Instruction {
105    const fn expect(self, count: u16) -> Result<(), Error> {
106        if self.wc == count {
107            Ok(())
108        } else {
109            Err(Error::InvalidOperandCount(self.op, self.wc))
110        }
111    }
112
113    fn expect_at_least(self, count: u16) -> Result<u16, Error> {
114        self.wc
115            .checked_sub(count)
116            .ok_or(Error::InvalidOperandCount(self.op, self.wc))
117    }
118}
119
120impl crate::TypeInner {
121    fn can_comparison_sample(&self, module: &crate::Module) -> bool {
122        match *self {
123            crate::TypeInner::Image {
124                class:
125                    crate::ImageClass::Sampled {
126                        kind: crate::ScalarKind::Float,
127                        multi: false,
128                    },
129                ..
130            } => true,
131            crate::TypeInner::Sampler { .. } => true,
132            crate::TypeInner::BindingArray { base, .. } => {
133                module.types[base].inner.can_comparison_sample(module)
134            }
135            _ => false,
136        }
137    }
138}
139
140#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
141pub enum ModuleState {
142    Empty,
143    Capability,
144    Extension,
145    ExtInstImport,
146    MemoryModel,
147    EntryPoint,
148    ExecutionMode,
149    Source,
150    Name,
151    ModuleProcessed,
152    Annotation,
153    Type,
154    Function,
155}
156
157trait LookupHelper {
158    type Target;
159    fn lookup(&self, key: spirv::Word) -> Result<&Self::Target, Error>;
160}
161
162impl<T> LookupHelper for FastHashMap<spirv::Word, T> {
163    type Target = T;
164    fn lookup(&self, key: spirv::Word) -> Result<&T, Error> {
165        self.get(&key).ok_or(Error::InvalidId(key))
166    }
167}
168
169impl crate::ImageDimension {
170    const fn required_coordinate_size(&self) -> Option<crate::VectorSize> {
171        match *self {
172            crate::ImageDimension::D1 => None,
173            crate::ImageDimension::D2 => Some(crate::VectorSize::Bi),
174            crate::ImageDimension::D3 => Some(crate::VectorSize::Tri),
175            crate::ImageDimension::Cube => Some(crate::VectorSize::Tri),
176        }
177    }
178}
179
180type MemberIndex = u32;
181
182bitflags::bitflags! {
183    #[derive(Clone, Copy, Debug, Default)]
184    struct DecorationFlags: u32 {
185        const NON_READABLE = 0x1;
186        const NON_WRITABLE = 0x2;
187    }
188}
189
190impl DecorationFlags {
191    fn to_storage_access(self) -> crate::StorageAccess {
192        let mut access = crate::StorageAccess::LOAD | crate::StorageAccess::STORE;
193        if self.contains(DecorationFlags::NON_READABLE) {
194            access &= !crate::StorageAccess::LOAD;
195        }
196        if self.contains(DecorationFlags::NON_WRITABLE) {
197            access &= !crate::StorageAccess::STORE;
198        }
199        access
200    }
201}
202
203#[derive(Debug, PartialEq)]
204enum Majority {
205    Column,
206    Row,
207}
208
209#[derive(Debug, Default)]
210struct Decoration {
211    name: Option<String>,
212    built_in: Option<spirv::Word>,
213    location: Option<spirv::Word>,
214    desc_set: Option<spirv::Word>,
215    desc_index: Option<spirv::Word>,
216    specialization_constant_id: Option<spirv::Word>,
217    storage_buffer: bool,
218    offset: Option<spirv::Word>,
219    array_stride: Option<NonZeroU32>,
220    matrix_stride: Option<NonZeroU32>,
221    matrix_major: Option<Majority>,
222    invariant: bool,
223    interpolation: Option<crate::Interpolation>,
224    sampling: Option<crate::Sampling>,
225    flags: DecorationFlags,
226}
227
228impl Decoration {
229    fn debug_name(&self) -> &str {
230        match self.name {
231            Some(ref name) => name.as_str(),
232            None => "?",
233        }
234    }
235
236    const fn resource_binding(&self) -> Option<crate::ResourceBinding> {
237        match *self {
238            Decoration {
239                desc_set: Some(group),
240                desc_index: Some(binding),
241                ..
242            } => Some(crate::ResourceBinding { group, binding }),
243            _ => None,
244        }
245    }
246
247    fn io_binding(&self) -> Result<crate::Binding, Error> {
248        match *self {
249            Decoration {
250                built_in: Some(built_in),
251                location: None,
252                invariant,
253                ..
254            } => Ok(crate::Binding::BuiltIn(map_builtin(built_in, invariant)?)),
255            Decoration {
256                built_in: None,
257                location: Some(location),
258                interpolation,
259                sampling,
260                ..
261            } => Ok(crate::Binding::Location {
262                location,
263                interpolation,
264                sampling,
265                blend_src: None,
266            }),
267            _ => Err(Error::MissingDecoration(spirv::Decoration::Location)),
268        }
269    }
270}
271
272#[derive(Debug)]
273struct LookupFunctionType {
274    parameter_type_ids: Vec<spirv::Word>,
275    return_type_id: spirv::Word,
276}
277
278struct LookupFunction {
279    handle: Handle<crate::Function>,
280    parameters_sampling: Vec<image::SamplingFlags>,
281}
282
283#[derive(Debug)]
284struct EntryPoint {
285    stage: crate::ShaderStage,
286    name: String,
287    early_depth_test: Option<crate::EarlyDepthTest>,
288    workgroup_size: [u32; 3],
289    variable_ids: Vec<spirv::Word>,
290}
291
292#[derive(Clone, Debug)]
293struct LookupType {
294    handle: Handle<crate::Type>,
295    base_id: Option<spirv::Word>,
296}
297
298#[derive(Debug)]
299enum Constant {
300    Constant(Handle<crate::Constant>),
301    Override(Handle<crate::Override>),
302}
303
304impl Constant {
305    const fn to_expr(&self) -> crate::Expression {
306        match *self {
307            Self::Constant(c) => crate::Expression::Constant(c),
308            Self::Override(o) => crate::Expression::Override(o),
309        }
310    }
311}
312
313#[derive(Debug)]
314struct LookupConstant {
315    inner: Constant,
316    type_id: spirv::Word,
317}
318
319#[derive(Debug)]
320enum Variable {
321    Global,
322    Input(crate::FunctionArgument),
323    Output(crate::FunctionResult),
324}
325
326#[derive(Debug)]
327struct LookupVariable {
328    inner: Variable,
329    handle: Handle<crate::GlobalVariable>,
330    type_id: spirv::Word,
331}
332
333/// Information about SPIR-V result ids, stored in `Frontend::lookup_expression`.
334#[derive(Clone, Debug)]
335struct LookupExpression {
336    /// The `Expression` constructed for this result.
337    ///
338    /// Note that, while a SPIR-V result id can be used in any block dominated
339    /// by its definition, a Naga `Expression` is only in scope for the rest of
340    /// its subtree. `Frontend::get_expr_handle` takes care of spilling the result
341    /// to a `LocalVariable` which can then be used anywhere.
342    handle: Handle<crate::Expression>,
343
344    /// The SPIR-V type of this result.
345    type_id: spirv::Word,
346
347    /// The label id of the block that defines this expression.
348    ///
349    /// This is zero for globals, constants, and function parameters, since they
350    /// originate outside any function's block.
351    block_id: spirv::Word,
352}
353
354#[derive(Debug)]
355struct LookupMember {
356    type_id: spirv::Word,
357    // This is true for either matrices, or arrays of matrices (yikes).
358    row_major: bool,
359}
360
361#[derive(Clone, Debug)]
362enum LookupLoadOverride {
363    /// For arrays of matrices, we track them but not loading yet.
364    Pending,
365    /// For matrices, vectors, and scalars, we pre-load the data.
366    Loaded(Handle<crate::Expression>),
367}
368
369#[derive(PartialEq)]
370enum ExtendedClass {
371    Global(crate::AddressSpace),
372    Input,
373    Output,
374}
375
376#[derive(Clone, Debug)]
377pub struct Options {
378    /// The IR coordinate space matches all the APIs except SPIR-V,
379    /// so by default we flip the Y coordinate of the `BuiltIn::Position`.
380    /// This flag can be used to avoid this.
381    pub adjust_coordinate_space: bool,
382    /// Only allow shaders with the known set of capabilities.
383    pub strict_capabilities: bool,
384    pub block_ctx_dump_prefix: Option<PathLikeOwned>,
385}
386
387impl Default for Options {
388    fn default() -> Self {
389        Options {
390            adjust_coordinate_space: true,
391            strict_capabilities: true,
392            block_ctx_dump_prefix: None,
393        }
394    }
395}
396
397/// An index into the `BlockContext::bodies` table.
398type BodyIndex = usize;
399
400/// An intermediate representation of a Naga [`Statement`].
401///
402/// `Body` and `BodyFragment` values form a tree: the `BodyIndex` fields of the
403/// variants are indices of the child `Body` values in [`BlockContext::bodies`].
404/// The `lower` function assembles the final `Statement` tree from this `Body`
405/// tree. See [`BlockContext`] for details.
406///
407/// [`Statement`]: crate::Statement
408#[derive(Debug)]
409enum BodyFragment {
410    BlockId(spirv::Word),
411    If {
412        condition: Handle<crate::Expression>,
413        accept: BodyIndex,
414        reject: BodyIndex,
415    },
416    Loop {
417        /// The body of the loop. Its [`Body::parent`] is the block containing
418        /// this `Loop` fragment.
419        body: BodyIndex,
420
421        /// The loop's continuing block. This is a grandchild: its
422        /// [`Body::parent`] is the loop body block, whose index is above.
423        continuing: BodyIndex,
424
425        /// If the SPIR-V loop's back-edge branch is conditional, this is the
426        /// expression that must be `false` for the back-edge to be taken, with
427        /// `true` being for the "loop merge" (which breaks out of the loop).
428        break_if: Option<Handle<crate::Expression>>,
429    },
430    Switch {
431        selector: Handle<crate::Expression>,
432        cases: Vec<(i32, BodyIndex)>,
433        default: BodyIndex,
434    },
435    Break,
436    Continue,
437}
438
439/// An intermediate representation of a Naga [`Block`].
440///
441/// This will be assembled into a `Block` once we've added spills for phi nodes
442/// and out-of-scope expressions. See [`BlockContext`] for details.
443///
444/// [`Block`]: crate::Block
445#[derive(Debug)]
446struct Body {
447    /// The index of the direct parent of this body
448    parent: usize,
449    data: Vec<BodyFragment>,
450}
451
452impl Body {
453    /// Creates a new empty `Body` with the specified `parent`
454    pub const fn with_parent(parent: usize) -> Self {
455        Body {
456            parent,
457            data: Vec::new(),
458        }
459    }
460}
461
462#[derive(Debug)]
463struct PhiExpression {
464    /// The local variable used for the phi node
465    local: Handle<crate::LocalVariable>,
466    /// List of (expression, block)
467    expressions: Vec<(spirv::Word, spirv::Word)>,
468}
469
470#[derive(Copy, Clone, Debug, PartialEq, Eq)]
471enum MergeBlockInformation {
472    LoopMerge,
473    LoopContinue,
474    SelectionMerge,
475    SwitchMerge,
476}
477
478/// Fragments of Naga IR, to be assembled into `Statements` once data flow is
479/// resolved.
480///
481/// We can't build a Naga `Statement` tree directly from SPIR-V blocks for three
482/// main reasons:
483///
484/// - We parse a function's SPIR-V blocks in the order they appear in the file.
485///   Within a function, SPIR-V requires that a block must precede any blocks it
486///   structurally dominates, but doesn't say much else about the order in which
487///   they must appear. So while we know we'll see control flow header blocks
488///   before their child constructs and merge blocks, those children and the
489///   merge blocks may appear in any order - perhaps even intermingled with
490///   children of other constructs.
491///
492/// - A SPIR-V expression can be used in any SPIR-V block dominated by its
493///   definition, whereas Naga expressions are scoped to the rest of their
494///   subtree. This means that discovering an expression use later in the
495///   function retroactively requires us to have spilled that expression into a
496///   local variable back before we left its scope. (The docs for
497///   [`Frontend::get_expr_handle`] explain this in more detail.)
498///
499/// - We translate SPIR-V OpPhi expressions as Naga local variables in which we
500///   store the appropriate value before jumping to the OpPhi's block.
501///
502/// All these cases require us to go back and amend previously generated Naga IR
503/// based on things we discover later. But modifying old blocks in arbitrary
504/// spots in a `Statement` tree is awkward.
505///
506/// Instead, as we iterate through the function's body, we accumulate
507/// control-flow-free fragments of Naga IR in the [`blocks`] table, while
508/// building a skeleton of the Naga `Statement` tree in [`bodies`]. We note any
509/// spills and temporaries we must introduce in [`phis`].
510///
511/// Finally, once we've processed the entire function, we add temporaries and
512/// spills to the fragmentary `Blocks` as directed by `phis`, and assemble them
513/// into the final Naga `Statement` tree as directed by `bodies`.
514///
515/// [`blocks`]: BlockContext::blocks
516/// [`bodies`]: BlockContext::bodies
517/// [`phis`]: BlockContext::phis
518#[derive(Debug)]
519struct BlockContext<'function> {
520    /// Phi nodes encountered when parsing the function, used to generate spills
521    /// to local variables.
522    phis: Vec<PhiExpression>,
523
524    /// Fragments of control-flow-free Naga IR.
525    ///
526    /// These will be stitched together into a proper [`Statement`] tree according
527    /// to `bodies`, once parsing is complete.
528    ///
529    /// [`Statement`]: crate::Statement
530    blocks: FastHashMap<spirv::Word, crate::Block>,
531
532    /// Map from each SPIR-V block's label id to the index of the [`Body`] in
533    /// [`bodies`] the block should append its contents to.
534    ///
535    /// Since each statement in a Naga [`Block`] dominates the next, we are sure
536    /// to encounter their SPIR-V blocks in order. Thus, by having this table
537    /// map a SPIR-V structured control flow construct's merge block to the same
538    /// body index as its header block, when we encounter the merge block, we
539    /// will simply pick up building the [`Body`] where the header left off.
540    ///
541    /// A function's first block is special: it is the only block we encounter
542    /// without having seen its label mentioned in advance. (It's simply the
543    /// first `OpLabel` after the `OpFunction`.) We thus assume that any block
544    /// missing an entry here must be the first block, which always has body
545    /// index zero.
546    ///
547    /// [`bodies`]: BlockContext::bodies
548    /// [`Block`]: crate::Block
549    body_for_label: FastHashMap<spirv::Word, BodyIndex>,
550
551    /// SPIR-V metadata about merge/continue blocks.
552    mergers: FastHashMap<spirv::Word, MergeBlockInformation>,
553
554    /// A table of `Body` values, each representing a block in the final IR.
555    ///
556    /// The first element is always the function's top-level block.
557    bodies: Vec<Body>,
558
559    /// The module we're building.
560    module: &'function mut crate::Module,
561
562    /// Id of the function currently being processed
563    function_id: spirv::Word,
564    /// Expression arena of the function currently being processed
565    expressions: &'function mut Arena<crate::Expression>,
566    /// Local variables arena of the function currently being processed
567    local_arena: &'function mut Arena<crate::LocalVariable>,
568    /// Arguments of the function currently being processed
569    arguments: &'function [crate::FunctionArgument],
570    /// Metadata about the usage of function parameters as sampling objects
571    parameter_sampling: &'function mut [image::SamplingFlags],
572}
573
574enum SignAnchor {
575    Result,
576    Operand,
577}
578
579pub struct Frontend<I> {
580    data: I,
581    data_offset: usize,
582    state: ModuleState,
583    layouter: Layouter,
584    temp_bytes: Vec<u8>,
585    ext_glsl_id: Option<spirv::Word>,
586    future_decor: FastHashMap<spirv::Word, Decoration>,
587    future_member_decor: FastHashMap<(spirv::Word, MemberIndex), Decoration>,
588    lookup_member: FastHashMap<(Handle<crate::Type>, MemberIndex), LookupMember>,
589    handle_sampling: FastHashMap<Handle<crate::GlobalVariable>, image::SamplingFlags>,
590
591    /// A record of what is accessed by [`Atomic`] statements we've
592    /// generated, so we can upgrade the types of their operands.
593    ///
594    /// [`Atomic`]: crate::Statement::Atomic
595    upgrade_atomics: Upgrades,
596
597    lookup_type: FastHashMap<spirv::Word, LookupType>,
598    lookup_void_type: Option<spirv::Word>,
599    lookup_storage_buffer_types: FastHashMap<Handle<crate::Type>, crate::StorageAccess>,
600    lookup_constant: FastHashMap<spirv::Word, LookupConstant>,
601    lookup_variable: FastHashMap<spirv::Word, LookupVariable>,
602    lookup_expression: FastHashMap<spirv::Word, LookupExpression>,
603    // Load overrides are used to work around row-major matrices
604    lookup_load_override: FastHashMap<spirv::Word, LookupLoadOverride>,
605    lookup_sampled_image: FastHashMap<spirv::Word, image::LookupSampledImage>,
606    lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,
607    lookup_function: FastHashMap<spirv::Word, LookupFunction>,
608    lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>,
609    // When parsing functions, each entry point function gets an entry here so that additional
610    // processing for them can be performed after all function parsing.
611    deferred_entry_points: Vec<(EntryPoint, spirv::Word)>,
612    //Note: each `OpFunctionCall` gets a single entry here, indexed by the
613    // dummy `Handle<crate::Function>` of the call site.
614    deferred_function_calls: Vec<spirv::Word>,
615    dummy_functions: Arena<crate::Function>,
616    // Graph of all function calls through the module.
617    // It's used to sort the functions (as nodes) topologically,
618    // so that in the IR any called function is already known.
619    function_call_graph: GraphMap<
620        spirv::Word,
621        (),
622        petgraph::Directed,
623        core::hash::BuildHasherDefault<rustc_hash::FxHasher>,
624    >,
625    options: Options,
626
627    /// Maps for a switch from a case target to the respective body and associated literals that
628    /// use that target block id.
629    ///
630    /// Used to preserve allocations between instruction parsing.
631    switch_cases: FastIndexMap<spirv::Word, (BodyIndex, Vec<i32>)>,
632
633    /// Tracks access to gl_PerVertex's builtins, it is used to cull unused builtins since initializing those can
634    /// affect performance and the mere presence of some of these builtins might cause backends to error since they
635    /// might be unsupported.
636    ///
637    /// The problematic builtins are: PointSize, ClipDistance and CullDistance.
638    ///
639    /// glslang declares those by default even though they are never written to
640    /// (see <https://github.com/KhronosGroup/glslang/issues/1868>)
641    gl_per_vertex_builtin_access: FastHashSet<crate::BuiltIn>,
642}
643
644impl<I: Iterator<Item = u32>> Frontend<I> {
645    pub fn new(data: I, options: &Options) -> Self {
646        Frontend {
647            data,
648            data_offset: 0,
649            state: ModuleState::Empty,
650            layouter: Layouter::default(),
651            temp_bytes: Vec::new(),
652            ext_glsl_id: None,
653            future_decor: FastHashMap::default(),
654            future_member_decor: FastHashMap::default(),
655            handle_sampling: FastHashMap::default(),
656            lookup_member: FastHashMap::default(),
657            upgrade_atomics: Default::default(),
658            lookup_type: FastHashMap::default(),
659            lookup_void_type: None,
660            lookup_storage_buffer_types: FastHashMap::default(),
661            lookup_constant: FastHashMap::default(),
662            lookup_variable: FastHashMap::default(),
663            lookup_expression: FastHashMap::default(),
664            lookup_load_override: FastHashMap::default(),
665            lookup_sampled_image: FastHashMap::default(),
666            lookup_function_type: FastHashMap::default(),
667            lookup_function: FastHashMap::default(),
668            lookup_entry_point: FastHashMap::default(),
669            deferred_entry_points: Vec::default(),
670            deferred_function_calls: Vec::default(),
671            dummy_functions: Arena::new(),
672            function_call_graph: GraphMap::new(),
673            options: options.clone(),
674            switch_cases: FastIndexMap::default(),
675            gl_per_vertex_builtin_access: FastHashSet::default(),
676        }
677    }
678
679    fn span_from(&self, from: usize) -> crate::Span {
680        crate::Span::from(from..self.data_offset)
681    }
682
683    fn span_from_with_op(&self, from: usize) -> crate::Span {
684        crate::Span::from((from - 4)..self.data_offset)
685    }
686
687    fn next(&mut self) -> Result<u32, Error> {
688        if let Some(res) = self.data.next() {
689            self.data_offset += 4;
690            Ok(res)
691        } else {
692            Err(Error::IncompleteData)
693        }
694    }
695
696    fn next_inst(&mut self) -> Result<Instruction, Error> {
697        let word = self.next()?;
698        let (wc, opcode) = ((word >> 16) as u16, (word & 0xffff) as u16);
699        if wc == 0 {
700            return Err(Error::InvalidWordCount);
701        }
702        let op = spirv::Op::from_u32(opcode as u32).ok_or(Error::UnknownInstruction(opcode))?;
703
704        Ok(Instruction { op, wc })
705    }
706
707    fn next_string(&mut self, mut count: u16) -> Result<(String, u16), Error> {
708        self.temp_bytes.clear();
709        loop {
710            if count == 0 {
711                return Err(Error::BadString);
712            }
713            count -= 1;
714            let chars = self.next()?.to_le_bytes();
715            let pos = chars.iter().position(|&c| c == 0).unwrap_or(4);
716            self.temp_bytes.extend_from_slice(&chars[..pos]);
717            if pos < 4 {
718                break;
719            }
720        }
721        core::str::from_utf8(&self.temp_bytes)
722            .map(|s| (s.to_owned(), count))
723            .map_err(|_| Error::BadString)
724    }
725
726    fn next_decoration(
727        &mut self,
728        inst: Instruction,
729        base_words: u16,
730        dec: &mut Decoration,
731    ) -> Result<(), Error> {
732        let raw = self.next()?;
733        let dec_typed = spirv::Decoration::from_u32(raw).ok_or(Error::InvalidDecoration(raw))?;
734        log::trace!("\t\t{}: {:?}", dec.debug_name(), dec_typed);
735        match dec_typed {
736            spirv::Decoration::BuiltIn => {
737                inst.expect(base_words + 2)?;
738                dec.built_in = Some(self.next()?);
739            }
740            spirv::Decoration::Location => {
741                inst.expect(base_words + 2)?;
742                dec.location = Some(self.next()?);
743            }
744            spirv::Decoration::DescriptorSet => {
745                inst.expect(base_words + 2)?;
746                dec.desc_set = Some(self.next()?);
747            }
748            spirv::Decoration::Binding => {
749                inst.expect(base_words + 2)?;
750                dec.desc_index = Some(self.next()?);
751            }
752            spirv::Decoration::BufferBlock => {
753                dec.storage_buffer = true;
754            }
755            spirv::Decoration::Offset => {
756                inst.expect(base_words + 2)?;
757                dec.offset = Some(self.next()?);
758            }
759            spirv::Decoration::ArrayStride => {
760                inst.expect(base_words + 2)?;
761                dec.array_stride = NonZeroU32::new(self.next()?);
762            }
763            spirv::Decoration::MatrixStride => {
764                inst.expect(base_words + 2)?;
765                dec.matrix_stride = NonZeroU32::new(self.next()?);
766            }
767            spirv::Decoration::Invariant => {
768                dec.invariant = true;
769            }
770            spirv::Decoration::NoPerspective => {
771                dec.interpolation = Some(crate::Interpolation::Linear);
772            }
773            spirv::Decoration::Flat => {
774                dec.interpolation = Some(crate::Interpolation::Flat);
775            }
776            spirv::Decoration::Centroid => {
777                dec.sampling = Some(crate::Sampling::Centroid);
778            }
779            spirv::Decoration::Sample => {
780                dec.sampling = Some(crate::Sampling::Sample);
781            }
782            spirv::Decoration::NonReadable => {
783                dec.flags |= DecorationFlags::NON_READABLE;
784            }
785            spirv::Decoration::NonWritable => {
786                dec.flags |= DecorationFlags::NON_WRITABLE;
787            }
788            spirv::Decoration::ColMajor => {
789                dec.matrix_major = Some(Majority::Column);
790            }
791            spirv::Decoration::RowMajor => {
792                dec.matrix_major = Some(Majority::Row);
793            }
794            spirv::Decoration::SpecId => {
795                dec.specialization_constant_id = Some(self.next()?);
796            }
797            other => {
798                log::warn!("Unknown decoration {other:?}");
799                for _ in base_words + 1..inst.wc {
800                    let _var = self.next()?;
801                }
802            }
803        }
804        Ok(())
805    }
806
807    /// Return the Naga [`Expression`] to use in `body_idx` to refer to the SPIR-V result `id`.
808    ///
809    /// Ideally, we would just have a map from each SPIR-V instruction id to the
810    /// [`Handle`] for the Naga [`Expression`] we generated for it.
811    /// Unfortunately, SPIR-V and Naga IR are different enough that such a
812    /// straightforward relationship isn't possible.
813    ///
814    /// In SPIR-V, an instruction's result id can be used by any instruction
815    /// dominated by that instruction. In Naga, an [`Expression`] is only in
816    /// scope for the remainder of its [`Block`]. In pseudocode:
817    ///
818    /// ```ignore
819    ///     loop {
820    ///         a = f();
821    ///         g(a);
822    ///         break;
823    ///     }
824    ///     h(a);
825    /// ```
826    ///
827    /// Suppose the calls to `f`, `g`, and `h` are SPIR-V instructions. In
828    /// SPIR-V, both the `g` and `h` instructions are allowed to refer to `a`,
829    /// because the loop body, including `f`, dominates both of them.
830    ///
831    /// But if `a` is a Naga [`Expression`], its scope ends at the end of the
832    /// block it's evaluated in: the loop body. Thus, while the [`Expression`]
833    /// we generate for `g` can refer to `a`, the one we generate for `h`
834    /// cannot.
835    ///
836    /// Instead, the SPIR-V front end must generate Naga IR like this:
837    ///
838    /// ```ignore
839    ///     var temp; // INTRODUCED
840    ///     loop {
841    ///         a = f();
842    ///         g(a);
843    ///         temp = a; // INTRODUCED
844    ///     }
845    ///     h(temp); // ADJUSTED
846    /// ```
847    ///
848    /// In other words, where `a` is in scope, [`Expression`]s can refer to it
849    /// directly; but once it is out of scope, we need to spill it to a
850    /// temporary and refer to that instead.
851    ///
852    /// Given a SPIR-V expression `id` and the index `body_idx` of the [body]
853    /// that wants to refer to it:
854    ///
855    /// - If the Naga [`Expression`] we generated for `id` is in scope in
856    ///   `body_idx`, then we simply return its `Handle<Expression>`.
857    ///
858    /// - Otherwise, introduce a new [`LocalVariable`], and add an entry to
859    ///   [`BlockContext::phis`] to arrange for `id`'s value to be spilled to
860    ///   it. Then emit a fresh [`Load`] of that temporary variable for use in
861    ///   `body_idx`'s block, and return its `Handle`.
862    ///
863    /// The SPIR-V domination rule ensures that the introduced [`LocalVariable`]
864    /// will always have been initialized before it is used.
865    ///
866    /// `lookup` must be the [`LookupExpression`] for `id`.
867    ///
868    /// `body_idx` argument must be the index of the [`Body`] that hopes to use
869    /// `id`'s [`Expression`].
870    ///
871    /// [`Expression`]: crate::Expression
872    /// [`Handle`]: crate::Handle
873    /// [`Block`]: crate::Block
874    /// [body]: BlockContext::bodies
875    /// [`LocalVariable`]: crate::LocalVariable
876    /// [`Load`]: crate::Expression::Load
877    fn get_expr_handle(
878        &self,
879        id: spirv::Word,
880        lookup: &LookupExpression,
881        ctx: &mut BlockContext,
882        emitter: &mut crate::proc::Emitter,
883        block: &mut crate::Block,
884        body_idx: BodyIndex,
885    ) -> Handle<crate::Expression> {
886        // What `Body` was `id` defined in?
887        let expr_body_idx = ctx
888            .body_for_label
889            .get(&lookup.block_id)
890            .copied()
891            .unwrap_or(0);
892
893        // Don't need to do a load/store if the expression is in the main body
894        // or if the expression is in the same body as where the query was
895        // requested. The body_idx might actually not be the final one if a loop
896        // or conditional occurs but in those cases we know that the new body
897        // will be a subscope of the body that was passed so we can still reuse
898        // the handle and not issue a load/store.
899        if is_parent(body_idx, expr_body_idx, ctx) {
900            lookup.handle
901        } else {
902            // Add a temporary variable of the same type which will be used to
903            // store the original expression and used in the current block
904            let ty = self.lookup_type[&lookup.type_id].handle;
905            let local = ctx.local_arena.append(
906                crate::LocalVariable {
907                    name: None,
908                    ty,
909                    init: None,
910                },
911                crate::Span::default(),
912            );
913
914            block.extend(emitter.finish(ctx.expressions));
915            let pointer = ctx.expressions.append(
916                crate::Expression::LocalVariable(local),
917                crate::Span::default(),
918            );
919            emitter.start(ctx.expressions);
920            let expr = ctx
921                .expressions
922                .append(crate::Expression::Load { pointer }, crate::Span::default());
923
924            // Add a slightly odd entry to the phi table, so that while `id`'s
925            // `Expression` is still in scope, the usual phi processing will
926            // spill its value to `local`, where we can find it later.
927            //
928            // This pretends that the block in which `id` is defined is the
929            // predecessor of some other block with a phi in it that cites id as
930            // one of its sources, and uses `local` as its variable. There is no
931            // such phi, but nobody needs to know that.
932            ctx.phis.push(PhiExpression {
933                local,
934                expressions: vec![(id, lookup.block_id)],
935            });
936
937            expr
938        }
939    }
940
941    fn parse_expr_unary_op(
942        &mut self,
943        ctx: &mut BlockContext,
944        emitter: &mut crate::proc::Emitter,
945        block: &mut crate::Block,
946        block_id: spirv::Word,
947        body_idx: usize,
948        op: crate::UnaryOperator,
949    ) -> Result<(), Error> {
950        let start = self.data_offset;
951        let result_type_id = self.next()?;
952        let result_id = self.next()?;
953        let p_id = self.next()?;
954
955        let p_lexp = self.lookup_expression.lookup(p_id)?;
956        let handle = self.get_expr_handle(p_id, p_lexp, ctx, emitter, block, body_idx);
957
958        let expr = crate::Expression::Unary { op, expr: handle };
959        self.lookup_expression.insert(
960            result_id,
961            LookupExpression {
962                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
963                type_id: result_type_id,
964                block_id,
965            },
966        );
967        Ok(())
968    }
969
970    fn parse_expr_binary_op(
971        &mut self,
972        ctx: &mut BlockContext,
973        emitter: &mut crate::proc::Emitter,
974        block: &mut crate::Block,
975        block_id: spirv::Word,
976        body_idx: usize,
977        op: crate::BinaryOperator,
978    ) -> Result<(), Error> {
979        let start = self.data_offset;
980        let result_type_id = self.next()?;
981        let result_id = self.next()?;
982        let p1_id = self.next()?;
983        let p2_id = self.next()?;
984
985        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
986        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
987        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
988        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
989
990        let expr = crate::Expression::Binary { op, left, right };
991        self.lookup_expression.insert(
992            result_id,
993            LookupExpression {
994                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
995                type_id: result_type_id,
996                block_id,
997            },
998        );
999        Ok(())
1000    }
1001
1002    /// A more complicated version of the unary op,
1003    /// where we force the operand to have the same type as the result.
1004    fn parse_expr_unary_op_sign_adjusted(
1005        &mut self,
1006        ctx: &mut BlockContext,
1007        emitter: &mut crate::proc::Emitter,
1008        block: &mut crate::Block,
1009        block_id: spirv::Word,
1010        body_idx: usize,
1011        op: crate::UnaryOperator,
1012    ) -> Result<(), Error> {
1013        let start = self.data_offset;
1014        let result_type_id = self.next()?;
1015        let result_id = self.next()?;
1016        let p1_id = self.next()?;
1017        let span = self.span_from_with_op(start);
1018
1019        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1020        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1021
1022        let result_lookup_ty = self.lookup_type.lookup(result_type_id)?;
1023        let kind = ctx.module.types[result_lookup_ty.handle]
1024            .inner
1025            .scalar_kind()
1026            .unwrap();
1027
1028        let expr = crate::Expression::Unary {
1029            op,
1030            expr: if p1_lexp.type_id == result_type_id {
1031                left
1032            } else {
1033                ctx.expressions.append(
1034                    crate::Expression::As {
1035                        expr: left,
1036                        kind,
1037                        convert: None,
1038                    },
1039                    span,
1040                )
1041            },
1042        };
1043
1044        self.lookup_expression.insert(
1045            result_id,
1046            LookupExpression {
1047                handle: ctx.expressions.append(expr, span),
1048                type_id: result_type_id,
1049                block_id,
1050            },
1051        );
1052        Ok(())
1053    }
1054
1055    /// A more complicated version of the binary op,
1056    /// where we force the operand to have the same type as the result.
1057    /// This is mostly needed for "i++" and "i--" coming from GLSL.
1058    #[allow(clippy::too_many_arguments)]
1059    fn parse_expr_binary_op_sign_adjusted(
1060        &mut self,
1061        ctx: &mut BlockContext,
1062        emitter: &mut crate::proc::Emitter,
1063        block: &mut crate::Block,
1064        block_id: spirv::Word,
1065        body_idx: usize,
1066        op: crate::BinaryOperator,
1067        // For arithmetic operations, we need the sign of operands to match the result.
1068        // For boolean operations, however, the operands need to match the signs, but
1069        // result is always different - a boolean.
1070        anchor: SignAnchor,
1071    ) -> Result<(), Error> {
1072        let start = self.data_offset;
1073        let result_type_id = self.next()?;
1074        let result_id = self.next()?;
1075        let p1_id = self.next()?;
1076        let p2_id = self.next()?;
1077        let span = self.span_from_with_op(start);
1078
1079        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1080        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1081        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1082        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1083
1084        let expected_type_id = match anchor {
1085            SignAnchor::Result => result_type_id,
1086            SignAnchor::Operand => p1_lexp.type_id,
1087        };
1088        let expected_lookup_ty = self.lookup_type.lookup(expected_type_id)?;
1089        let kind = ctx.module.types[expected_lookup_ty.handle]
1090            .inner
1091            .scalar_kind()
1092            .unwrap();
1093
1094        let expr = crate::Expression::Binary {
1095            op,
1096            left: if p1_lexp.type_id == expected_type_id {
1097                left
1098            } else {
1099                ctx.expressions.append(
1100                    crate::Expression::As {
1101                        expr: left,
1102                        kind,
1103                        convert: None,
1104                    },
1105                    span,
1106                )
1107            },
1108            right: if p2_lexp.type_id == expected_type_id {
1109                right
1110            } else {
1111                ctx.expressions.append(
1112                    crate::Expression::As {
1113                        expr: right,
1114                        kind,
1115                        convert: None,
1116                    },
1117                    span,
1118                )
1119            },
1120        };
1121
1122        self.lookup_expression.insert(
1123            result_id,
1124            LookupExpression {
1125                handle: ctx.expressions.append(expr, span),
1126                type_id: result_type_id,
1127                block_id,
1128            },
1129        );
1130        Ok(())
1131    }
1132
1133    /// A version of the binary op where one or both of the arguments might need to be casted to a
1134    /// specific integer kind (unsigned or signed), used for operations like OpINotEqual or
1135    /// OpUGreaterThan.
1136    #[allow(clippy::too_many_arguments)]
1137    fn parse_expr_int_comparison(
1138        &mut self,
1139        ctx: &mut BlockContext,
1140        emitter: &mut crate::proc::Emitter,
1141        block: &mut crate::Block,
1142        block_id: spirv::Word,
1143        body_idx: usize,
1144        op: crate::BinaryOperator,
1145        kind: crate::ScalarKind,
1146    ) -> Result<(), Error> {
1147        let start = self.data_offset;
1148        let result_type_id = self.next()?;
1149        let result_id = self.next()?;
1150        let p1_id = self.next()?;
1151        let p2_id = self.next()?;
1152        let span = self.span_from_with_op(start);
1153
1154        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1155        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1156        let p1_lookup_ty = self.lookup_type.lookup(p1_lexp.type_id)?;
1157        let p1_kind = ctx.module.types[p1_lookup_ty.handle]
1158            .inner
1159            .scalar_kind()
1160            .unwrap();
1161        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1162        let right = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1163        let p2_lookup_ty = self.lookup_type.lookup(p2_lexp.type_id)?;
1164        let p2_kind = ctx.module.types[p2_lookup_ty.handle]
1165            .inner
1166            .scalar_kind()
1167            .unwrap();
1168
1169        let expr = crate::Expression::Binary {
1170            op,
1171            left: if p1_kind == kind {
1172                left
1173            } else {
1174                ctx.expressions.append(
1175                    crate::Expression::As {
1176                        expr: left,
1177                        kind,
1178                        convert: None,
1179                    },
1180                    span,
1181                )
1182            },
1183            right: if p2_kind == kind {
1184                right
1185            } else {
1186                ctx.expressions.append(
1187                    crate::Expression::As {
1188                        expr: right,
1189                        kind,
1190                        convert: None,
1191                    },
1192                    span,
1193                )
1194            },
1195        };
1196
1197        self.lookup_expression.insert(
1198            result_id,
1199            LookupExpression {
1200                handle: ctx.expressions.append(expr, span),
1201                type_id: result_type_id,
1202                block_id,
1203            },
1204        );
1205        Ok(())
1206    }
1207
1208    fn parse_expr_shift_op(
1209        &mut self,
1210        ctx: &mut BlockContext,
1211        emitter: &mut crate::proc::Emitter,
1212        block: &mut crate::Block,
1213        block_id: spirv::Word,
1214        body_idx: usize,
1215        op: crate::BinaryOperator,
1216    ) -> Result<(), Error> {
1217        let start = self.data_offset;
1218        let result_type_id = self.next()?;
1219        let result_id = self.next()?;
1220        let p1_id = self.next()?;
1221        let p2_id = self.next()?;
1222
1223        let span = self.span_from_with_op(start);
1224
1225        let p1_lexp = self.lookup_expression.lookup(p1_id)?;
1226        let left = self.get_expr_handle(p1_id, p1_lexp, ctx, emitter, block, body_idx);
1227        let p2_lexp = self.lookup_expression.lookup(p2_id)?;
1228        let p2_handle = self.get_expr_handle(p2_id, p2_lexp, ctx, emitter, block, body_idx);
1229        // convert the shift to Uint
1230        let right = ctx.expressions.append(
1231            crate::Expression::As {
1232                expr: p2_handle,
1233                kind: crate::ScalarKind::Uint,
1234                convert: None,
1235            },
1236            span,
1237        );
1238
1239        let expr = crate::Expression::Binary { op, left, right };
1240        self.lookup_expression.insert(
1241            result_id,
1242            LookupExpression {
1243                handle: ctx.expressions.append(expr, span),
1244                type_id: result_type_id,
1245                block_id,
1246            },
1247        );
1248        Ok(())
1249    }
1250
1251    fn parse_expr_derivative(
1252        &mut self,
1253        ctx: &mut BlockContext,
1254        emitter: &mut crate::proc::Emitter,
1255        block: &mut crate::Block,
1256        block_id: spirv::Word,
1257        body_idx: usize,
1258        (axis, ctrl): (crate::DerivativeAxis, crate::DerivativeControl),
1259    ) -> Result<(), Error> {
1260        let start = self.data_offset;
1261        let result_type_id = self.next()?;
1262        let result_id = self.next()?;
1263        let arg_id = self.next()?;
1264
1265        let arg_lexp = self.lookup_expression.lookup(arg_id)?;
1266        let arg_handle = self.get_expr_handle(arg_id, arg_lexp, ctx, emitter, block, body_idx);
1267
1268        let expr = crate::Expression::Derivative {
1269            axis,
1270            ctrl,
1271            expr: arg_handle,
1272        };
1273        self.lookup_expression.insert(
1274            result_id,
1275            LookupExpression {
1276                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
1277                type_id: result_type_id,
1278                block_id,
1279            },
1280        );
1281        Ok(())
1282    }
1283
1284    #[allow(clippy::too_many_arguments)]
1285    fn insert_composite(
1286        &self,
1287        root_expr: Handle<crate::Expression>,
1288        root_type_id: spirv::Word,
1289        object_expr: Handle<crate::Expression>,
1290        selections: &[spirv::Word],
1291        type_arena: &UniqueArena<crate::Type>,
1292        expressions: &mut Arena<crate::Expression>,
1293        span: crate::Span,
1294    ) -> Result<Handle<crate::Expression>, Error> {
1295        let selection = match selections.first() {
1296            Some(&index) => index,
1297            None => return Ok(object_expr),
1298        };
1299        let root_span = expressions.get_span(root_expr);
1300        let root_lookup = self.lookup_type.lookup(root_type_id)?;
1301
1302        let (count, child_type_id) = match type_arena[root_lookup.handle].inner {
1303            crate::TypeInner::Struct { ref members, .. } => {
1304                let child_member = self
1305                    .lookup_member
1306                    .get(&(root_lookup.handle, selection))
1307                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1308                (members.len(), child_member.type_id)
1309            }
1310            crate::TypeInner::Array { size, .. } => {
1311                let size = match size {
1312                    crate::ArraySize::Constant(size) => size.get(),
1313                    crate::ArraySize::Pending(_) => {
1314                        unreachable!();
1315                    }
1316                    // A runtime sized array is not a composite type
1317                    crate::ArraySize::Dynamic => {
1318                        return Err(Error::InvalidAccessType(root_type_id))
1319                    }
1320                };
1321
1322                let child_type_id = root_lookup
1323                    .base_id
1324                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1325
1326                (size as usize, child_type_id)
1327            }
1328            crate::TypeInner::Vector { size, .. }
1329            | crate::TypeInner::Matrix { columns: size, .. } => {
1330                let child_type_id = root_lookup
1331                    .base_id
1332                    .ok_or(Error::InvalidAccessType(root_type_id))?;
1333                (size as usize, child_type_id)
1334            }
1335            _ => return Err(Error::InvalidAccessType(root_type_id)),
1336        };
1337
1338        let mut components = Vec::with_capacity(count);
1339        for index in 0..count as u32 {
1340            let expr = expressions.append(
1341                crate::Expression::AccessIndex {
1342                    base: root_expr,
1343                    index,
1344                },
1345                if index == selection { span } else { root_span },
1346            );
1347            components.push(expr);
1348        }
1349        components[selection as usize] = self.insert_composite(
1350            components[selection as usize],
1351            child_type_id,
1352            object_expr,
1353            &selections[1..],
1354            type_arena,
1355            expressions,
1356            span,
1357        )?;
1358
1359        Ok(expressions.append(
1360            crate::Expression::Compose {
1361                ty: root_lookup.handle,
1362                components,
1363            },
1364            span,
1365        ))
1366    }
1367
1368    /// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`].
1369    ///
1370    /// Return a [`Handle`] for a Naga [`Expression`] that holds the value of
1371    /// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it
1372    /// is a pointer.
1373    ///
1374    /// This may entail spilling `pointer_id`'s value to a temporary:
1375    /// see [`get_expr_handle`]'s documentation.
1376    ///
1377    /// [`Expression`]: crate::Expression
1378    /// [`Type`]: crate::Type
1379    /// [`Handle`]: crate::Handle
1380    /// [`get_expr_handle`]: Frontend::get_expr_handle
1381    fn get_exp_and_base_ty_handles(
1382        &self,
1383        pointer_id: spirv::Word,
1384        ctx: &mut BlockContext,
1385        emitter: &mut crate::proc::Emitter,
1386        block: &mut crate::Block,
1387        body_idx: usize,
1388    ) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {
1389        log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
1390        let p_lexp_handle;
1391        let p_lexp_ty_id;
1392        {
1393            let lexp = self.lookup_expression.lookup(pointer_id)?;
1394            p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);
1395            p_lexp_ty_id = lexp.type_id;
1396        };
1397
1398        log::trace!("\t\t\tlooking up pointer type {pointer_id:?}");
1399        let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
1400        let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
1401
1402        log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}");
1403        let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
1404
1405        Ok((p_lexp_handle, p_base_ty.handle))
1406    }
1407
1408    #[allow(clippy::too_many_arguments)]
1409    fn parse_atomic_expr_with_value(
1410        &mut self,
1411        inst: Instruction,
1412        emitter: &mut crate::proc::Emitter,
1413        ctx: &mut BlockContext,
1414        block: &mut crate::Block,
1415        block_id: spirv::Word,
1416        body_idx: usize,
1417        atomic_function: crate::AtomicFunction,
1418    ) -> Result<(), Error> {
1419        inst.expect(7)?;
1420        let start = self.data_offset;
1421        let result_type_id = self.next()?;
1422        let result_id = self.next()?;
1423        let pointer_id = self.next()?;
1424        let _scope_id = self.next()?;
1425        let _memory_semantics_id = self.next()?;
1426        let value_id = self.next()?;
1427        let span = self.span_from_with_op(start);
1428
1429        let (p_lexp_handle, p_base_ty_handle) =
1430            self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;
1431
1432        log::trace!("\t\t\tlooking up value expr {value_id:?}");
1433        let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;
1434
1435        block.extend(emitter.finish(ctx.expressions));
1436        // Create an expression for our result
1437        let r_lexp_handle = {
1438            let expr = crate::Expression::AtomicResult {
1439                ty: p_base_ty_handle,
1440                comparison: false,
1441            };
1442            let handle = ctx.expressions.append(expr, span);
1443            self.lookup_expression.insert(
1444                result_id,
1445                LookupExpression {
1446                    handle,
1447                    type_id: result_type_id,
1448                    block_id,
1449                },
1450            );
1451            handle
1452        };
1453        emitter.start(ctx.expressions);
1454
1455        // Create a statement for the op itself
1456        let stmt = crate::Statement::Atomic {
1457            pointer: p_lexp_handle,
1458            fun: atomic_function,
1459            value: v_lexp_handle,
1460            result: Some(r_lexp_handle),
1461        };
1462        block.push(stmt, span);
1463
1464        // Store any associated global variables so we can upgrade their types later
1465        self.record_atomic_access(ctx, p_lexp_handle)?;
1466
1467        Ok(())
1468    }
1469
1470    /// Add the next SPIR-V block's contents to `block_ctx`.
1471    ///
1472    /// Except for the function's entry block, `block_id` should be the label of
1473    /// a block we've seen mentioned before, with an entry in
1474    /// `block_ctx.body_for_label` to tell us which `Body` it contributes to.
1475    fn next_block(&mut self, block_id: spirv::Word, ctx: &mut BlockContext) -> Result<(), Error> {
1476        // Extend `body` with the correct form for a branch to `target`.
1477        fn merger(body: &mut Body, target: &MergeBlockInformation) {
1478            body.data.push(match *target {
1479                MergeBlockInformation::LoopContinue => BodyFragment::Continue,
1480                MergeBlockInformation::LoopMerge | MergeBlockInformation::SwitchMerge => {
1481                    BodyFragment::Break
1482                }
1483
1484                // Finishing a selection merge means just falling off the end of
1485                // the `accept` or `reject` block of the `If` statement.
1486                MergeBlockInformation::SelectionMerge => return,
1487            })
1488        }
1489
1490        let mut emitter = crate::proc::Emitter::default();
1491        emitter.start(ctx.expressions);
1492
1493        // Find the `Body` to which this block contributes.
1494        //
1495        // If this is some SPIR-V structured control flow construct's merge
1496        // block, then `body_idx` will refer to the same `Body` as the header,
1497        // so that we simply pick up accumulating the `Body` where the header
1498        // left off. Each of the statements in a block dominates the next, so
1499        // we're sure to encounter their SPIR-V blocks in order, ensuring that
1500        // the `Body` will be assembled in the proper order.
1501        //
1502        // Note that, unlike every other kind of SPIR-V block, we don't know the
1503        // function's first block's label in advance. Thus, we assume that if
1504        // this block has no entry in `ctx.body_for_label`, it must be the
1505        // function's first block. This always has body index zero.
1506        let mut body_idx = *ctx.body_for_label.entry(block_id).or_default();
1507
1508        // The Naga IR block this call builds. This will end up as
1509        // `ctx.blocks[&block_id]`, and `ctx.bodies[body_idx]` will refer to it
1510        // via a `BodyFragment::BlockId`.
1511        let mut block = crate::Block::new();
1512
1513        // Stores the merge block as defined by a `OpSelectionMerge` otherwise is `None`
1514        //
1515        // This is used in `OpSwitch` to promote the `MergeBlockInformation` from
1516        // `SelectionMerge` to `SwitchMerge` to allow `Break`s this isn't desirable for
1517        // `LoopMerge`s because otherwise `Continue`s wouldn't be allowed
1518        let mut selection_merge_block = None;
1519
1520        macro_rules! get_expr_handle {
1521            ($id:expr, $lexp:expr) => {
1522                self.get_expr_handle($id, $lexp, ctx, &mut emitter, &mut block, body_idx)
1523            };
1524        }
1525        macro_rules! parse_expr_op {
1526            ($op:expr, BINARY) => {
1527                self.parse_expr_binary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
1528            };
1529
1530            ($op:expr, SHIFT) => {
1531                self.parse_expr_shift_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
1532            };
1533            ($op:expr, UNARY) => {
1534                self.parse_expr_unary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
1535            };
1536            ($axis:expr, $ctrl:expr, DERIVATIVE) => {
1537                self.parse_expr_derivative(
1538                    ctx,
1539                    &mut emitter,
1540                    &mut block,
1541                    block_id,
1542                    body_idx,
1543                    ($axis, $ctrl),
1544                )
1545            };
1546        }
1547
1548        let terminator = loop {
1549            use spirv::Op;
1550            let start = self.data_offset;
1551            let inst = self.next_inst()?;
1552            let span = crate::Span::from(start..(start + 4 * (inst.wc as usize)));
1553            log::debug!("\t\t{:?} [{}]", inst.op, inst.wc);
1554
1555            match inst.op {
1556                Op::Line => {
1557                    inst.expect(4)?;
1558                    let _file_id = self.next()?;
1559                    let _row_id = self.next()?;
1560                    let _col_id = self.next()?;
1561                }
1562                Op::NoLine => inst.expect(1)?,
1563                Op::Undef => {
1564                    inst.expect(3)?;
1565                    let type_id = self.next()?;
1566                    let id = self.next()?;
1567                    let type_lookup = self.lookup_type.lookup(type_id)?;
1568                    let ty = type_lookup.handle;
1569
1570                    self.lookup_expression.insert(
1571                        id,
1572                        LookupExpression {
1573                            handle: ctx
1574                                .expressions
1575                                .append(crate::Expression::ZeroValue(ty), span),
1576                            type_id,
1577                            block_id,
1578                        },
1579                    );
1580                }
1581                Op::Variable => {
1582                    inst.expect_at_least(4)?;
1583                    block.extend(emitter.finish(ctx.expressions));
1584
1585                    let result_type_id = self.next()?;
1586                    let result_id = self.next()?;
1587                    let _storage_class = self.next()?;
1588                    let init = if inst.wc > 4 {
1589                        inst.expect(5)?;
1590                        let init_id = self.next()?;
1591                        let lconst = self.lookup_constant.lookup(init_id)?;
1592                        Some(ctx.expressions.append(lconst.inner.to_expr(), span))
1593                    } else {
1594                        None
1595                    };
1596
1597                    let name = self
1598                        .future_decor
1599                        .remove(&result_id)
1600                        .and_then(|decor| decor.name);
1601                    if let Some(ref name) = name {
1602                        log::debug!("\t\t\tid={result_id} name={name}");
1603                    }
1604                    let lookup_ty = self.lookup_type.lookup(result_type_id)?;
1605                    let var_handle = ctx.local_arena.append(
1606                        crate::LocalVariable {
1607                            name,
1608                            ty: match ctx.module.types[lookup_ty.handle].inner {
1609                                crate::TypeInner::Pointer { base, .. } => base,
1610                                _ => lookup_ty.handle,
1611                            },
1612                            init,
1613                        },
1614                        span,
1615                    );
1616
1617                    self.lookup_expression.insert(
1618                        result_id,
1619                        LookupExpression {
1620                            handle: ctx
1621                                .expressions
1622                                .append(crate::Expression::LocalVariable(var_handle), span),
1623                            type_id: result_type_id,
1624                            block_id,
1625                        },
1626                    );
1627                    emitter.start(ctx.expressions);
1628                }
1629                Op::Phi => {
1630                    inst.expect_at_least(3)?;
1631                    block.extend(emitter.finish(ctx.expressions));
1632
1633                    let result_type_id = self.next()?;
1634                    let result_id = self.next()?;
1635
1636                    let name = format!("phi_{result_id}");
1637                    let local = ctx.local_arena.append(
1638                        crate::LocalVariable {
1639                            name: Some(name),
1640                            ty: self.lookup_type.lookup(result_type_id)?.handle,
1641                            init: None,
1642                        },
1643                        self.span_from(start),
1644                    );
1645                    let pointer = ctx
1646                        .expressions
1647                        .append(crate::Expression::LocalVariable(local), span);
1648
1649                    let in_count = (inst.wc - 3) / 2;
1650                    let mut phi = PhiExpression {
1651                        local,
1652                        expressions: Vec::with_capacity(in_count as usize),
1653                    };
1654                    for _ in 0..in_count {
1655                        let expr = self.next()?;
1656                        let block = self.next()?;
1657                        phi.expressions.push((expr, block));
1658                    }
1659
1660                    ctx.phis.push(phi);
1661                    emitter.start(ctx.expressions);
1662
1663                    // Associate the lookup with an actual value, which is emitted
1664                    // into the current block.
1665                    self.lookup_expression.insert(
1666                        result_id,
1667                        LookupExpression {
1668                            handle: ctx
1669                                .expressions
1670                                .append(crate::Expression::Load { pointer }, span),
1671                            type_id: result_type_id,
1672                            block_id,
1673                        },
1674                    );
1675                }
1676                Op::AccessChain | Op::InBoundsAccessChain => {
1677                    struct AccessExpression {
1678                        base_handle: Handle<crate::Expression>,
1679                        type_id: spirv::Word,
1680                        load_override: Option<LookupLoadOverride>,
1681                    }
1682
1683                    inst.expect_at_least(4)?;
1684
1685                    let result_type_id = self.next()?;
1686                    let result_id = self.next()?;
1687                    let base_id = self.next()?;
1688                    log::trace!("\t\t\tlooking up expr {base_id:?}");
1689
1690                    let mut acex = {
1691                        let lexp = self.lookup_expression.lookup(base_id)?;
1692                        let lty = self.lookup_type.lookup(lexp.type_id)?;
1693
1694                        // HACK `OpAccessChain` and `OpInBoundsAccessChain`
1695                        // require for the result type to be a pointer, but if
1696                        // we're given a pointer to an image / sampler, it will
1697                        // be *already* dereferenced, since we do that early
1698                        // during `parse_type_pointer()`.
1699                        //
1700                        // This can happen only through `BindingArray`, since
1701                        // that's the only case where one can obtain a pointer
1702                        // to an image / sampler, and so let's match on that:
1703                        let dereference = match ctx.module.types[lty.handle].inner {
1704                            crate::TypeInner::BindingArray { .. } => false,
1705                            _ => true,
1706                        };
1707
1708                        let type_id = if dereference {
1709                            lty.base_id.ok_or(Error::InvalidAccessType(lexp.type_id))?
1710                        } else {
1711                            lexp.type_id
1712                        };
1713
1714                        AccessExpression {
1715                            base_handle: get_expr_handle!(base_id, lexp),
1716                            type_id,
1717                            load_override: self.lookup_load_override.get(&base_id).cloned(),
1718                        }
1719                    };
1720
1721                    for _ in 4..inst.wc {
1722                        let access_id = self.next()?;
1723                        log::trace!("\t\t\tlooking up index expr {access_id:?}");
1724                        let index_expr = self.lookup_expression.lookup(access_id)?.clone();
1725                        let index_expr_handle = get_expr_handle!(access_id, &index_expr);
1726                        let index_expr_data = &ctx.expressions[index_expr.handle];
1727                        let index_maybe = match *index_expr_data {
1728                            crate::Expression::Constant(const_handle) => Some(
1729                                ctx.gctx()
1730                                    .eval_expr_to_u32(ctx.module.constants[const_handle].init)
1731                                    .map_err(|_| {
1732                                        Error::InvalidAccess(crate::Expression::Constant(
1733                                            const_handle,
1734                                        ))
1735                                    })?,
1736                            ),
1737                            _ => None,
1738                        };
1739
1740                        log::trace!("\t\t\tlooking up type {:?}", acex.type_id);
1741                        let type_lookup = self.lookup_type.lookup(acex.type_id)?;
1742                        let ty = &ctx.module.types[type_lookup.handle];
1743                        acex = match ty.inner {
1744                            // can only index a struct with a constant
1745                            crate::TypeInner::Struct { ref members, .. } => {
1746                                let index = index_maybe
1747                                    .ok_or_else(|| Error::InvalidAccess(index_expr_data.clone()))?;
1748
1749                                let lookup_member = self
1750                                    .lookup_member
1751                                    .get(&(type_lookup.handle, index))
1752                                    .ok_or(Error::InvalidAccessType(acex.type_id))?;
1753                                let base_handle = ctx.expressions.append(
1754                                    crate::Expression::AccessIndex {
1755                                        base: acex.base_handle,
1756                                        index,
1757                                    },
1758                                    span,
1759                                );
1760
1761                                if let Some(crate::Binding::BuiltIn(built_in)) =
1762                                    members[index as usize].binding
1763                                {
1764                                    self.gl_per_vertex_builtin_access.insert(built_in);
1765                                }
1766
1767                                AccessExpression {
1768                                    base_handle,
1769                                    type_id: lookup_member.type_id,
1770                                    load_override: if lookup_member.row_major {
1771                                        debug_assert!(acex.load_override.is_none());
1772                                        let sub_type_lookup =
1773                                            self.lookup_type.lookup(lookup_member.type_id)?;
1774                                        Some(match ctx.module.types[sub_type_lookup.handle].inner {
1775                                            // load it transposed, to match column major expectations
1776                                            crate::TypeInner::Matrix { .. } => {
1777                                                let loaded = ctx.expressions.append(
1778                                                    crate::Expression::Load {
1779                                                        pointer: base_handle,
1780                                                    },
1781                                                    span,
1782                                                );
1783                                                let transposed = ctx.expressions.append(
1784                                                    crate::Expression::Math {
1785                                                        fun: crate::MathFunction::Transpose,
1786                                                        arg: loaded,
1787                                                        arg1: None,
1788                                                        arg2: None,
1789                                                        arg3: None,
1790                                                    },
1791                                                    span,
1792                                                );
1793                                                LookupLoadOverride::Loaded(transposed)
1794                                            }
1795                                            _ => LookupLoadOverride::Pending,
1796                                        })
1797                                    } else {
1798                                        None
1799                                    },
1800                                }
1801                            }
1802                            crate::TypeInner::Matrix { .. } => {
1803                                let load_override = match acex.load_override {
1804                                    // We are indexing inside a row-major matrix
1805                                    Some(LookupLoadOverride::Loaded(load_expr)) => {
1806                                        let index = index_maybe.ok_or_else(|| {
1807                                            Error::InvalidAccess(index_expr_data.clone())
1808                                        })?;
1809                                        let sub_handle = ctx.expressions.append(
1810                                            crate::Expression::AccessIndex {
1811                                                base: load_expr,
1812                                                index,
1813                                            },
1814                                            span,
1815                                        );
1816                                        Some(LookupLoadOverride::Loaded(sub_handle))
1817                                    }
1818                                    _ => None,
1819                                };
1820                                let sub_expr = match index_maybe {
1821                                    Some(index) => crate::Expression::AccessIndex {
1822                                        base: acex.base_handle,
1823                                        index,
1824                                    },
1825                                    None => crate::Expression::Access {
1826                                        base: acex.base_handle,
1827                                        index: index_expr_handle,
1828                                    },
1829                                };
1830                                AccessExpression {
1831                                    base_handle: ctx.expressions.append(sub_expr, span),
1832                                    type_id: type_lookup
1833                                        .base_id
1834                                        .ok_or(Error::InvalidAccessType(acex.type_id))?,
1835                                    load_override,
1836                                }
1837                            }
1838                            // This must be a vector or an array.
1839                            _ => {
1840                                let base_handle = ctx.expressions.append(
1841                                    crate::Expression::Access {
1842                                        base: acex.base_handle,
1843                                        index: index_expr_handle,
1844                                    },
1845                                    span,
1846                                );
1847                                let load_override = match acex.load_override {
1848                                    // If there is a load override in place, then we always end up
1849                                    // with a side-loaded value here.
1850                                    Some(lookup_load_override) => {
1851                                        let sub_expr = match lookup_load_override {
1852                                            // We must be indexing into the array of row-major matrices.
1853                                            // Let's load the result of indexing and transpose it.
1854                                            LookupLoadOverride::Pending => {
1855                                                let loaded = ctx.expressions.append(
1856                                                    crate::Expression::Load {
1857                                                        pointer: base_handle,
1858                                                    },
1859                                                    span,
1860                                                );
1861                                                ctx.expressions.append(
1862                                                    crate::Expression::Math {
1863                                                        fun: crate::MathFunction::Transpose,
1864                                                        arg: loaded,
1865                                                        arg1: None,
1866                                                        arg2: None,
1867                                                        arg3: None,
1868                                                    },
1869                                                    span,
1870                                                )
1871                                            }
1872                                            // We are indexing inside a row-major matrix.
1873                                            LookupLoadOverride::Loaded(load_expr) => {
1874                                                ctx.expressions.append(
1875                                                    crate::Expression::Access {
1876                                                        base: load_expr,
1877                                                        index: index_expr_handle,
1878                                                    },
1879                                                    span,
1880                                                )
1881                                            }
1882                                        };
1883                                        Some(LookupLoadOverride::Loaded(sub_expr))
1884                                    }
1885                                    None => None,
1886                                };
1887                                AccessExpression {
1888                                    base_handle,
1889                                    type_id: type_lookup
1890                                        .base_id
1891                                        .ok_or(Error::InvalidAccessType(acex.type_id))?,
1892                                    load_override,
1893                                }
1894                            }
1895                        };
1896                    }
1897
1898                    if let Some(load_expr) = acex.load_override {
1899                        self.lookup_load_override.insert(result_id, load_expr);
1900                    }
1901                    let lookup_expression = LookupExpression {
1902                        handle: acex.base_handle,
1903                        type_id: result_type_id,
1904                        block_id,
1905                    };
1906                    self.lookup_expression.insert(result_id, lookup_expression);
1907                }
1908                Op::VectorExtractDynamic => {
1909                    inst.expect(5)?;
1910
1911                    let result_type_id = self.next()?;
1912                    let id = self.next()?;
1913                    let composite_id = self.next()?;
1914                    let index_id = self.next()?;
1915
1916                    let root_lexp = self.lookup_expression.lookup(composite_id)?;
1917                    let root_handle = get_expr_handle!(composite_id, root_lexp);
1918                    let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;
1919                    let index_lexp = self.lookup_expression.lookup(index_id)?;
1920                    let index_handle = get_expr_handle!(index_id, index_lexp);
1921                    let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;
1922
1923                    let num_components = match ctx.module.types[root_type_lookup.handle].inner {
1924                        crate::TypeInner::Vector { size, .. } => size as u32,
1925                        _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),
1926                    };
1927
1928                    let mut make_index = |ctx: &mut BlockContext, index: u32| {
1929                        make_index_literal(
1930                            ctx,
1931                            index,
1932                            &mut block,
1933                            &mut emitter,
1934                            index_type,
1935                            index_lexp.type_id,
1936                            span,
1937                        )
1938                    };
1939
1940                    let index_expr = make_index(ctx, 0)?;
1941                    let mut handle = ctx.expressions.append(
1942                        crate::Expression::Access {
1943                            base: root_handle,
1944                            index: index_expr,
1945                        },
1946                        span,
1947                    );
1948                    for index in 1..num_components {
1949                        let index_expr = make_index(ctx, index)?;
1950                        let access_expr = ctx.expressions.append(
1951                            crate::Expression::Access {
1952                                base: root_handle,
1953                                index: index_expr,
1954                            },
1955                            span,
1956                        );
1957                        let cond = ctx.expressions.append(
1958                            crate::Expression::Binary {
1959                                op: crate::BinaryOperator::Equal,
1960                                left: index_expr,
1961                                right: index_handle,
1962                            },
1963                            span,
1964                        );
1965                        handle = ctx.expressions.append(
1966                            crate::Expression::Select {
1967                                condition: cond,
1968                                accept: access_expr,
1969                                reject: handle,
1970                            },
1971                            span,
1972                        );
1973                    }
1974
1975                    self.lookup_expression.insert(
1976                        id,
1977                        LookupExpression {
1978                            handle,
1979                            type_id: result_type_id,
1980                            block_id,
1981                        },
1982                    );
1983                }
1984                Op::VectorInsertDynamic => {
1985                    inst.expect(6)?;
1986
1987                    let result_type_id = self.next()?;
1988                    let id = self.next()?;
1989                    let composite_id = self.next()?;
1990                    let object_id = self.next()?;
1991                    let index_id = self.next()?;
1992
1993                    let object_lexp = self.lookup_expression.lookup(object_id)?;
1994                    let object_handle = get_expr_handle!(object_id, object_lexp);
1995                    let root_lexp = self.lookup_expression.lookup(composite_id)?;
1996                    let root_handle = get_expr_handle!(composite_id, root_lexp);
1997                    let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;
1998                    let index_lexp = self.lookup_expression.lookup(index_id)?;
1999                    let index_handle = get_expr_handle!(index_id, index_lexp);
2000                    let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;
2001
2002                    let num_components = match ctx.module.types[root_type_lookup.handle].inner {
2003                        crate::TypeInner::Vector { size, .. } => size as u32,
2004                        _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),
2005                    };
2006
2007                    let mut components = Vec::with_capacity(num_components as usize);
2008                    for index in 0..num_components {
2009                        let index_expr = make_index_literal(
2010                            ctx,
2011                            index,
2012                            &mut block,
2013                            &mut emitter,
2014                            index_type,
2015                            index_lexp.type_id,
2016                            span,
2017                        )?;
2018                        let access_expr = ctx.expressions.append(
2019                            crate::Expression::Access {
2020                                base: root_handle,
2021                                index: index_expr,
2022                            },
2023                            span,
2024                        );
2025                        let cond = ctx.expressions.append(
2026                            crate::Expression::Binary {
2027                                op: crate::BinaryOperator::Equal,
2028                                left: index_expr,
2029                                right: index_handle,
2030                            },
2031                            span,
2032                        );
2033                        let handle = ctx.expressions.append(
2034                            crate::Expression::Select {
2035                                condition: cond,
2036                                accept: object_handle,
2037                                reject: access_expr,
2038                            },
2039                            span,
2040                        );
2041                        components.push(handle);
2042                    }
2043                    let handle = ctx.expressions.append(
2044                        crate::Expression::Compose {
2045                            ty: root_type_lookup.handle,
2046                            components,
2047                        },
2048                        span,
2049                    );
2050
2051                    self.lookup_expression.insert(
2052                        id,
2053                        LookupExpression {
2054                            handle,
2055                            type_id: result_type_id,
2056                            block_id,
2057                        },
2058                    );
2059                }
2060                Op::CompositeExtract => {
2061                    inst.expect_at_least(4)?;
2062
2063                    let result_type_id = self.next()?;
2064                    let result_id = self.next()?;
2065                    let base_id = self.next()?;
2066                    log::trace!("\t\t\tlooking up expr {base_id:?}");
2067                    let mut lexp = self.lookup_expression.lookup(base_id)?.clone();
2068                    lexp.handle = get_expr_handle!(base_id, &lexp);
2069                    for _ in 4..inst.wc {
2070                        let index = self.next()?;
2071                        log::trace!("\t\t\tlooking up type {:?}", lexp.type_id);
2072                        let type_lookup = self.lookup_type.lookup(lexp.type_id)?;
2073                        let type_id = match ctx.module.types[type_lookup.handle].inner {
2074                            crate::TypeInner::Struct { .. } => {
2075                                self.lookup_member
2076                                    .get(&(type_lookup.handle, index))
2077                                    .ok_or(Error::InvalidAccessType(lexp.type_id))?
2078                                    .type_id
2079                            }
2080                            crate::TypeInner::Array { .. }
2081                            | crate::TypeInner::Vector { .. }
2082                            | crate::TypeInner::Matrix { .. } => type_lookup
2083                                .base_id
2084                                .ok_or(Error::InvalidAccessType(lexp.type_id))?,
2085                            ref other => {
2086                                log::warn!("composite type {other:?}");
2087                                return Err(Error::UnsupportedType(type_lookup.handle));
2088                            }
2089                        };
2090                        lexp = LookupExpression {
2091                            handle: ctx.expressions.append(
2092                                crate::Expression::AccessIndex {
2093                                    base: lexp.handle,
2094                                    index,
2095                                },
2096                                span,
2097                            ),
2098                            type_id,
2099                            block_id,
2100                        };
2101                    }
2102
2103                    self.lookup_expression.insert(
2104                        result_id,
2105                        LookupExpression {
2106                            handle: lexp.handle,
2107                            type_id: result_type_id,
2108                            block_id,
2109                        },
2110                    );
2111                }
2112                Op::CompositeInsert => {
2113                    inst.expect_at_least(5)?;
2114
2115                    let result_type_id = self.next()?;
2116                    let id = self.next()?;
2117                    let object_id = self.next()?;
2118                    let composite_id = self.next()?;
2119                    let mut selections = Vec::with_capacity(inst.wc as usize - 5);
2120                    for _ in 5..inst.wc {
2121                        selections.push(self.next()?);
2122                    }
2123
2124                    let object_lexp = self.lookup_expression.lookup(object_id)?.clone();
2125                    let object_handle = get_expr_handle!(object_id, &object_lexp);
2126                    let root_lexp = self.lookup_expression.lookup(composite_id)?.clone();
2127                    let root_handle = get_expr_handle!(composite_id, &root_lexp);
2128                    let handle = self.insert_composite(
2129                        root_handle,
2130                        result_type_id,
2131                        object_handle,
2132                        &selections,
2133                        &ctx.module.types,
2134                        ctx.expressions,
2135                        span,
2136                    )?;
2137
2138                    self.lookup_expression.insert(
2139                        id,
2140                        LookupExpression {
2141                            handle,
2142                            type_id: result_type_id,
2143                            block_id,
2144                        },
2145                    );
2146                }
2147                Op::CompositeConstruct => {
2148                    inst.expect_at_least(3)?;
2149
2150                    let result_type_id = self.next()?;
2151                    let id = self.next()?;
2152                    let mut components = Vec::with_capacity(inst.wc as usize - 2);
2153                    for _ in 3..inst.wc {
2154                        let comp_id = self.next()?;
2155                        log::trace!("\t\t\tlooking up expr {comp_id:?}");
2156                        let lexp = self.lookup_expression.lookup(comp_id)?;
2157                        let handle = get_expr_handle!(comp_id, lexp);
2158                        components.push(handle);
2159                    }
2160                    let ty = self.lookup_type.lookup(result_type_id)?.handle;
2161                    let first = components[0];
2162                    let expr = match ctx.module.types[ty].inner {
2163                        // this is an optimization to detect the splat
2164                        crate::TypeInner::Vector { size, .. }
2165                            if components.len() == size as usize
2166                                && components[1..].iter().all(|&c| c == first) =>
2167                        {
2168                            crate::Expression::Splat { size, value: first }
2169                        }
2170                        _ => crate::Expression::Compose { ty, components },
2171                    };
2172                    self.lookup_expression.insert(
2173                        id,
2174                        LookupExpression {
2175                            handle: ctx.expressions.append(expr, span),
2176                            type_id: result_type_id,
2177                            block_id,
2178                        },
2179                    );
2180                }
2181                Op::Load => {
2182                    inst.expect_at_least(4)?;
2183
2184                    let result_type_id = self.next()?;
2185                    let result_id = self.next()?;
2186                    let pointer_id = self.next()?;
2187                    if inst.wc != 4 {
2188                        inst.expect(5)?;
2189                        let _memory_access = self.next()?;
2190                    }
2191
2192                    let base_lexp = self.lookup_expression.lookup(pointer_id)?;
2193                    let base_handle = get_expr_handle!(pointer_id, base_lexp);
2194                    let type_lookup = self.lookup_type.lookup(base_lexp.type_id)?;
2195                    let handle = match ctx.module.types[type_lookup.handle].inner {
2196                        crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } => {
2197                            base_handle
2198                        }
2199                        _ => match self.lookup_load_override.get(&pointer_id) {
2200                            Some(&LookupLoadOverride::Loaded(handle)) => handle,
2201                            //Note: we aren't handling `LookupLoadOverride::Pending` properly here
2202                            _ => ctx.expressions.append(
2203                                crate::Expression::Load {
2204                                    pointer: base_handle,
2205                                },
2206                                span,
2207                            ),
2208                        },
2209                    };
2210
2211                    self.lookup_expression.insert(
2212                        result_id,
2213                        LookupExpression {
2214                            handle,
2215                            type_id: result_type_id,
2216                            block_id,
2217                        },
2218                    );
2219                }
2220                Op::Store => {
2221                    inst.expect_at_least(3)?;
2222
2223                    let pointer_id = self.next()?;
2224                    let value_id = self.next()?;
2225                    if inst.wc != 3 {
2226                        inst.expect(4)?;
2227                        let _memory_access = self.next()?;
2228                    }
2229                    let base_expr = self.lookup_expression.lookup(pointer_id)?;
2230                    let base_handle = get_expr_handle!(pointer_id, base_expr);
2231                    let value_expr = self.lookup_expression.lookup(value_id)?;
2232                    let value_handle = get_expr_handle!(value_id, value_expr);
2233
2234                    block.extend(emitter.finish(ctx.expressions));
2235                    block.push(
2236                        crate::Statement::Store {
2237                            pointer: base_handle,
2238                            value: value_handle,
2239                        },
2240                        span,
2241                    );
2242                    emitter.start(ctx.expressions);
2243                }
2244                // Arithmetic Instructions +, -, *, /, %
2245                Op::SNegate | Op::FNegate => {
2246                    inst.expect(4)?;
2247                    self.parse_expr_unary_op_sign_adjusted(
2248                        ctx,
2249                        &mut emitter,
2250                        &mut block,
2251                        block_id,
2252                        body_idx,
2253                        crate::UnaryOperator::Negate,
2254                    )?;
2255                }
2256                Op::IAdd
2257                | Op::ISub
2258                | Op::IMul
2259                | Op::BitwiseOr
2260                | Op::BitwiseXor
2261                | Op::BitwiseAnd
2262                | Op::SDiv
2263                | Op::SRem => {
2264                    inst.expect(5)?;
2265                    let operator = map_binary_operator(inst.op)?;
2266                    self.parse_expr_binary_op_sign_adjusted(
2267                        ctx,
2268                        &mut emitter,
2269                        &mut block,
2270                        block_id,
2271                        body_idx,
2272                        operator,
2273                        SignAnchor::Result,
2274                    )?;
2275                }
2276                Op::IEqual | Op::INotEqual => {
2277                    inst.expect(5)?;
2278                    let operator = map_binary_operator(inst.op)?;
2279                    self.parse_expr_binary_op_sign_adjusted(
2280                        ctx,
2281                        &mut emitter,
2282                        &mut block,
2283                        block_id,
2284                        body_idx,
2285                        operator,
2286                        SignAnchor::Operand,
2287                    )?;
2288                }
2289                Op::FAdd => {
2290                    inst.expect(5)?;
2291                    parse_expr_op!(crate::BinaryOperator::Add, BINARY)?;
2292                }
2293                Op::FSub => {
2294                    inst.expect(5)?;
2295                    parse_expr_op!(crate::BinaryOperator::Subtract, BINARY)?;
2296                }
2297                Op::FMul => {
2298                    inst.expect(5)?;
2299                    parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;
2300                }
2301                Op::UDiv | Op::FDiv => {
2302                    inst.expect(5)?;
2303                    parse_expr_op!(crate::BinaryOperator::Divide, BINARY)?;
2304                }
2305                Op::UMod | Op::FRem => {
2306                    inst.expect(5)?;
2307                    parse_expr_op!(crate::BinaryOperator::Modulo, BINARY)?;
2308                }
2309                Op::SMod => {
2310                    inst.expect(5)?;
2311
2312                    // x - y * int(floor(float(x) / float(y)))
2313
2314                    let start = self.data_offset;
2315                    let result_type_id = self.next()?;
2316                    let result_id = self.next()?;
2317                    let p1_id = self.next()?;
2318                    let p2_id = self.next()?;
2319                    let span = self.span_from_with_op(start);
2320
2321                    let p1_lexp = self.lookup_expression.lookup(p1_id)?;
2322                    let left = self.get_expr_handle(
2323                        p1_id,
2324                        p1_lexp,
2325                        ctx,
2326                        &mut emitter,
2327                        &mut block,
2328                        body_idx,
2329                    );
2330                    let p2_lexp = self.lookup_expression.lookup(p2_id)?;
2331                    let right = self.get_expr_handle(
2332                        p2_id,
2333                        p2_lexp,
2334                        ctx,
2335                        &mut emitter,
2336                        &mut block,
2337                        body_idx,
2338                    );
2339
2340                    let result_ty = self.lookup_type.lookup(result_type_id)?;
2341                    let inner = &ctx.module.types[result_ty.handle].inner;
2342                    let kind = inner.scalar_kind().unwrap();
2343                    let size = inner.size(ctx.gctx()) as u8;
2344
2345                    let left_cast = ctx.expressions.append(
2346                        crate::Expression::As {
2347                            expr: left,
2348                            kind: crate::ScalarKind::Float,
2349                            convert: Some(size),
2350                        },
2351                        span,
2352                    );
2353                    let right_cast = ctx.expressions.append(
2354                        crate::Expression::As {
2355                            expr: right,
2356                            kind: crate::ScalarKind::Float,
2357                            convert: Some(size),
2358                        },
2359                        span,
2360                    );
2361                    let div = ctx.expressions.append(
2362                        crate::Expression::Binary {
2363                            op: crate::BinaryOperator::Divide,
2364                            left: left_cast,
2365                            right: right_cast,
2366                        },
2367                        span,
2368                    );
2369                    let floor = ctx.expressions.append(
2370                        crate::Expression::Math {
2371                            fun: crate::MathFunction::Floor,
2372                            arg: div,
2373                            arg1: None,
2374                            arg2: None,
2375                            arg3: None,
2376                        },
2377                        span,
2378                    );
2379                    let cast = ctx.expressions.append(
2380                        crate::Expression::As {
2381                            expr: floor,
2382                            kind,
2383                            convert: Some(size),
2384                        },
2385                        span,
2386                    );
2387                    let mult = ctx.expressions.append(
2388                        crate::Expression::Binary {
2389                            op: crate::BinaryOperator::Multiply,
2390                            left: cast,
2391                            right,
2392                        },
2393                        span,
2394                    );
2395                    let sub = ctx.expressions.append(
2396                        crate::Expression::Binary {
2397                            op: crate::BinaryOperator::Subtract,
2398                            left,
2399                            right: mult,
2400                        },
2401                        span,
2402                    );
2403                    self.lookup_expression.insert(
2404                        result_id,
2405                        LookupExpression {
2406                            handle: sub,
2407                            type_id: result_type_id,
2408                            block_id,
2409                        },
2410                    );
2411                }
2412                Op::FMod => {
2413                    inst.expect(5)?;
2414
2415                    // x - y * floor(x / y)
2416
2417                    let start = self.data_offset;
2418                    let span = self.span_from_with_op(start);
2419
2420                    let result_type_id = self.next()?;
2421                    let result_id = self.next()?;
2422                    let p1_id = self.next()?;
2423                    let p2_id = self.next()?;
2424
2425                    let p1_lexp = self.lookup_expression.lookup(p1_id)?;
2426                    let left = self.get_expr_handle(
2427                        p1_id,
2428                        p1_lexp,
2429                        ctx,
2430                        &mut emitter,
2431                        &mut block,
2432                        body_idx,
2433                    );
2434                    let p2_lexp = self.lookup_expression.lookup(p2_id)?;
2435                    let right = self.get_expr_handle(
2436                        p2_id,
2437                        p2_lexp,
2438                        ctx,
2439                        &mut emitter,
2440                        &mut block,
2441                        body_idx,
2442                    );
2443
2444                    let div = ctx.expressions.append(
2445                        crate::Expression::Binary {
2446                            op: crate::BinaryOperator::Divide,
2447                            left,
2448                            right,
2449                        },
2450                        span,
2451                    );
2452                    let floor = ctx.expressions.append(
2453                        crate::Expression::Math {
2454                            fun: crate::MathFunction::Floor,
2455                            arg: div,
2456                            arg1: None,
2457                            arg2: None,
2458                            arg3: None,
2459                        },
2460                        span,
2461                    );
2462                    let mult = ctx.expressions.append(
2463                        crate::Expression::Binary {
2464                            op: crate::BinaryOperator::Multiply,
2465                            left: floor,
2466                            right,
2467                        },
2468                        span,
2469                    );
2470                    let sub = ctx.expressions.append(
2471                        crate::Expression::Binary {
2472                            op: crate::BinaryOperator::Subtract,
2473                            left,
2474                            right: mult,
2475                        },
2476                        span,
2477                    );
2478                    self.lookup_expression.insert(
2479                        result_id,
2480                        LookupExpression {
2481                            handle: sub,
2482                            type_id: result_type_id,
2483                            block_id,
2484                        },
2485                    );
2486                }
2487                Op::VectorTimesScalar
2488                | Op::VectorTimesMatrix
2489                | Op::MatrixTimesScalar
2490                | Op::MatrixTimesVector
2491                | Op::MatrixTimesMatrix => {
2492                    inst.expect(5)?;
2493                    parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;
2494                }
2495                Op::Transpose => {
2496                    inst.expect(4)?;
2497
2498                    let result_type_id = self.next()?;
2499                    let result_id = self.next()?;
2500                    let matrix_id = self.next()?;
2501                    let matrix_lexp = self.lookup_expression.lookup(matrix_id)?;
2502                    let matrix_handle = get_expr_handle!(matrix_id, matrix_lexp);
2503                    let expr = crate::Expression::Math {
2504                        fun: crate::MathFunction::Transpose,
2505                        arg: matrix_handle,
2506                        arg1: None,
2507                        arg2: None,
2508                        arg3: None,
2509                    };
2510                    self.lookup_expression.insert(
2511                        result_id,
2512                        LookupExpression {
2513                            handle: ctx.expressions.append(expr, span),
2514                            type_id: result_type_id,
2515                            block_id,
2516                        },
2517                    );
2518                }
2519                Op::Dot => {
2520                    inst.expect(5)?;
2521
2522                    let result_type_id = self.next()?;
2523                    let result_id = self.next()?;
2524                    let left_id = self.next()?;
2525                    let right_id = self.next()?;
2526                    let left_lexp = self.lookup_expression.lookup(left_id)?;
2527                    let left_handle = get_expr_handle!(left_id, left_lexp);
2528                    let right_lexp = self.lookup_expression.lookup(right_id)?;
2529                    let right_handle = get_expr_handle!(right_id, right_lexp);
2530                    let expr = crate::Expression::Math {
2531                        fun: crate::MathFunction::Dot,
2532                        arg: left_handle,
2533                        arg1: Some(right_handle),
2534                        arg2: None,
2535                        arg3: None,
2536                    };
2537                    self.lookup_expression.insert(
2538                        result_id,
2539                        LookupExpression {
2540                            handle: ctx.expressions.append(expr, span),
2541                            type_id: result_type_id,
2542                            block_id,
2543                        },
2544                    );
2545                }
2546                Op::BitFieldInsert => {
2547                    inst.expect(7)?;
2548
2549                    let start = self.data_offset;
2550                    let span = self.span_from_with_op(start);
2551
2552                    let result_type_id = self.next()?;
2553                    let result_id = self.next()?;
2554                    let base_id = self.next()?;
2555                    let insert_id = self.next()?;
2556                    let offset_id = self.next()?;
2557                    let count_id = self.next()?;
2558                    let base_lexp = self.lookup_expression.lookup(base_id)?;
2559                    let base_handle = get_expr_handle!(base_id, base_lexp);
2560                    let insert_lexp = self.lookup_expression.lookup(insert_id)?;
2561                    let insert_handle = get_expr_handle!(insert_id, insert_lexp);
2562                    let offset_lexp = self.lookup_expression.lookup(offset_id)?;
2563                    let offset_handle = get_expr_handle!(offset_id, offset_lexp);
2564                    let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;
2565                    let count_lexp = self.lookup_expression.lookup(count_id)?;
2566                    let count_handle = get_expr_handle!(count_id, count_lexp);
2567                    let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;
2568
2569                    let offset_kind = ctx.module.types[offset_lookup_ty.handle]
2570                        .inner
2571                        .scalar_kind()
2572                        .unwrap();
2573                    let count_kind = ctx.module.types[count_lookup_ty.handle]
2574                        .inner
2575                        .scalar_kind()
2576                        .unwrap();
2577
2578                    let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {
2579                        ctx.expressions.append(
2580                            crate::Expression::As {
2581                                expr: offset_handle,
2582                                kind: crate::ScalarKind::Uint,
2583                                convert: None,
2584                            },
2585                            span,
2586                        )
2587                    } else {
2588                        offset_handle
2589                    };
2590
2591                    let count_cast_handle = if count_kind != crate::ScalarKind::Uint {
2592                        ctx.expressions.append(
2593                            crate::Expression::As {
2594                                expr: count_handle,
2595                                kind: crate::ScalarKind::Uint,
2596                                convert: None,
2597                            },
2598                            span,
2599                        )
2600                    } else {
2601                        count_handle
2602                    };
2603
2604                    let expr = crate::Expression::Math {
2605                        fun: crate::MathFunction::InsertBits,
2606                        arg: base_handle,
2607                        arg1: Some(insert_handle),
2608                        arg2: Some(offset_cast_handle),
2609                        arg3: Some(count_cast_handle),
2610                    };
2611                    self.lookup_expression.insert(
2612                        result_id,
2613                        LookupExpression {
2614                            handle: ctx.expressions.append(expr, span),
2615                            type_id: result_type_id,
2616                            block_id,
2617                        },
2618                    );
2619                }
2620                Op::BitFieldSExtract | Op::BitFieldUExtract => {
2621                    inst.expect(6)?;
2622
2623                    let result_type_id = self.next()?;
2624                    let result_id = self.next()?;
2625                    let base_id = self.next()?;
2626                    let offset_id = self.next()?;
2627                    let count_id = self.next()?;
2628                    let base_lexp = self.lookup_expression.lookup(base_id)?;
2629                    let base_handle = get_expr_handle!(base_id, base_lexp);
2630                    let offset_lexp = self.lookup_expression.lookup(offset_id)?;
2631                    let offset_handle = get_expr_handle!(offset_id, offset_lexp);
2632                    let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;
2633                    let count_lexp = self.lookup_expression.lookup(count_id)?;
2634                    let count_handle = get_expr_handle!(count_id, count_lexp);
2635                    let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;
2636
2637                    let offset_kind = ctx.module.types[offset_lookup_ty.handle]
2638                        .inner
2639                        .scalar_kind()
2640                        .unwrap();
2641                    let count_kind = ctx.module.types[count_lookup_ty.handle]
2642                        .inner
2643                        .scalar_kind()
2644                        .unwrap();
2645
2646                    let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {
2647                        ctx.expressions.append(
2648                            crate::Expression::As {
2649                                expr: offset_handle,
2650                                kind: crate::ScalarKind::Uint,
2651                                convert: None,
2652                            },
2653                            span,
2654                        )
2655                    } else {
2656                        offset_handle
2657                    };
2658
2659                    let count_cast_handle = if count_kind != crate::ScalarKind::Uint {
2660                        ctx.expressions.append(
2661                            crate::Expression::As {
2662                                expr: count_handle,
2663                                kind: crate::ScalarKind::Uint,
2664                                convert: None,
2665                            },
2666                            span,
2667                        )
2668                    } else {
2669                        count_handle
2670                    };
2671
2672                    let expr = crate::Expression::Math {
2673                        fun: crate::MathFunction::ExtractBits,
2674                        arg: base_handle,
2675                        arg1: Some(offset_cast_handle),
2676                        arg2: Some(count_cast_handle),
2677                        arg3: None,
2678                    };
2679                    self.lookup_expression.insert(
2680                        result_id,
2681                        LookupExpression {
2682                            handle: ctx.expressions.append(expr, span),
2683                            type_id: result_type_id,
2684                            block_id,
2685                        },
2686                    );
2687                }
2688                Op::BitReverse | Op::BitCount => {
2689                    inst.expect(4)?;
2690
2691                    let result_type_id = self.next()?;
2692                    let result_id = self.next()?;
2693                    let base_id = self.next()?;
2694                    let base_lexp = self.lookup_expression.lookup(base_id)?;
2695                    let base_handle = get_expr_handle!(base_id, base_lexp);
2696                    let expr = crate::Expression::Math {
2697                        fun: match inst.op {
2698                            Op::BitReverse => crate::MathFunction::ReverseBits,
2699                            Op::BitCount => crate::MathFunction::CountOneBits,
2700                            _ => unreachable!(),
2701                        },
2702                        arg: base_handle,
2703                        arg1: None,
2704                        arg2: None,
2705                        arg3: None,
2706                    };
2707                    self.lookup_expression.insert(
2708                        result_id,
2709                        LookupExpression {
2710                            handle: ctx.expressions.append(expr, span),
2711                            type_id: result_type_id,
2712                            block_id,
2713                        },
2714                    );
2715                }
2716                Op::OuterProduct => {
2717                    inst.expect(5)?;
2718
2719                    let result_type_id = self.next()?;
2720                    let result_id = self.next()?;
2721                    let left_id = self.next()?;
2722                    let right_id = self.next()?;
2723                    let left_lexp = self.lookup_expression.lookup(left_id)?;
2724                    let left_handle = get_expr_handle!(left_id, left_lexp);
2725                    let right_lexp = self.lookup_expression.lookup(right_id)?;
2726                    let right_handle = get_expr_handle!(right_id, right_lexp);
2727                    let expr = crate::Expression::Math {
2728                        fun: crate::MathFunction::Outer,
2729                        arg: left_handle,
2730                        arg1: Some(right_handle),
2731                        arg2: None,
2732                        arg3: None,
2733                    };
2734                    self.lookup_expression.insert(
2735                        result_id,
2736                        LookupExpression {
2737                            handle: ctx.expressions.append(expr, span),
2738                            type_id: result_type_id,
2739                            block_id,
2740                        },
2741                    );
2742                }
2743                // Bitwise instructions
2744                Op::Not => {
2745                    inst.expect(4)?;
2746                    self.parse_expr_unary_op_sign_adjusted(
2747                        ctx,
2748                        &mut emitter,
2749                        &mut block,
2750                        block_id,
2751                        body_idx,
2752                        crate::UnaryOperator::BitwiseNot,
2753                    )?;
2754                }
2755                Op::ShiftRightLogical => {
2756                    inst.expect(5)?;
2757                    //TODO: convert input and result to unsigned
2758                    parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;
2759                }
2760                Op::ShiftRightArithmetic => {
2761                    inst.expect(5)?;
2762                    //TODO: convert input and result to signed
2763                    parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;
2764                }
2765                Op::ShiftLeftLogical => {
2766                    inst.expect(5)?;
2767                    parse_expr_op!(crate::BinaryOperator::ShiftLeft, SHIFT)?;
2768                }
2769                // Sampling
2770                Op::Image => {
2771                    inst.expect(4)?;
2772                    self.parse_image_uncouple(block_id)?;
2773                }
2774                Op::SampledImage => {
2775                    inst.expect(5)?;
2776                    self.parse_image_couple()?;
2777                }
2778                Op::ImageWrite => {
2779                    let extra = inst.expect_at_least(4)?;
2780                    let stmt =
2781                        self.parse_image_write(extra, ctx, &mut emitter, &mut block, body_idx)?;
2782                    block.extend(emitter.finish(ctx.expressions));
2783                    block.push(stmt, span);
2784                    emitter.start(ctx.expressions);
2785                }
2786                Op::ImageFetch | Op::ImageRead => {
2787                    let extra = inst.expect_at_least(5)?;
2788                    self.parse_image_load(
2789                        extra,
2790                        ctx,
2791                        &mut emitter,
2792                        &mut block,
2793                        block_id,
2794                        body_idx,
2795                    )?;
2796                }
2797                Op::ImageSampleImplicitLod | Op::ImageSampleExplicitLod => {
2798                    let extra = inst.expect_at_least(5)?;
2799                    let options = image::SamplingOptions {
2800                        compare: false,
2801                        project: false,
2802                    };
2803                    self.parse_image_sample(
2804                        extra,
2805                        options,
2806                        ctx,
2807                        &mut emitter,
2808                        &mut block,
2809                        block_id,
2810                        body_idx,
2811                    )?;
2812                }
2813                Op::ImageSampleProjImplicitLod | Op::ImageSampleProjExplicitLod => {
2814                    let extra = inst.expect_at_least(5)?;
2815                    let options = image::SamplingOptions {
2816                        compare: false,
2817                        project: true,
2818                    };
2819                    self.parse_image_sample(
2820                        extra,
2821                        options,
2822                        ctx,
2823                        &mut emitter,
2824                        &mut block,
2825                        block_id,
2826                        body_idx,
2827                    )?;
2828                }
2829                Op::ImageSampleDrefImplicitLod | Op::ImageSampleDrefExplicitLod => {
2830                    let extra = inst.expect_at_least(6)?;
2831                    let options = image::SamplingOptions {
2832                        compare: true,
2833                        project: false,
2834                    };
2835                    self.parse_image_sample(
2836                        extra,
2837                        options,
2838                        ctx,
2839                        &mut emitter,
2840                        &mut block,
2841                        block_id,
2842                        body_idx,
2843                    )?;
2844                }
2845                Op::ImageSampleProjDrefImplicitLod | Op::ImageSampleProjDrefExplicitLod => {
2846                    let extra = inst.expect_at_least(6)?;
2847                    let options = image::SamplingOptions {
2848                        compare: true,
2849                        project: true,
2850                    };
2851                    self.parse_image_sample(
2852                        extra,
2853                        options,
2854                        ctx,
2855                        &mut emitter,
2856                        &mut block,
2857                        block_id,
2858                        body_idx,
2859                    )?;
2860                }
2861                Op::ImageQuerySize => {
2862                    inst.expect(4)?;
2863                    self.parse_image_query_size(
2864                        false,
2865                        ctx,
2866                        &mut emitter,
2867                        &mut block,
2868                        block_id,
2869                        body_idx,
2870                    )?;
2871                }
2872                Op::ImageQuerySizeLod => {
2873                    inst.expect(5)?;
2874                    self.parse_image_query_size(
2875                        true,
2876                        ctx,
2877                        &mut emitter,
2878                        &mut block,
2879                        block_id,
2880                        body_idx,
2881                    )?;
2882                }
2883                Op::ImageQueryLevels => {
2884                    inst.expect(4)?;
2885                    self.parse_image_query_other(crate::ImageQuery::NumLevels, ctx, block_id)?;
2886                }
2887                Op::ImageQuerySamples => {
2888                    inst.expect(4)?;
2889                    self.parse_image_query_other(crate::ImageQuery::NumSamples, ctx, block_id)?;
2890                }
2891                // other ops
2892                Op::Select => {
2893                    inst.expect(6)?;
2894                    let result_type_id = self.next()?;
2895                    let result_id = self.next()?;
2896                    let condition = self.next()?;
2897                    let o1_id = self.next()?;
2898                    let o2_id = self.next()?;
2899
2900                    let cond_lexp = self.lookup_expression.lookup(condition)?;
2901                    let cond_handle = get_expr_handle!(condition, cond_lexp);
2902                    let o1_lexp = self.lookup_expression.lookup(o1_id)?;
2903                    let o1_handle = get_expr_handle!(o1_id, o1_lexp);
2904                    let o2_lexp = self.lookup_expression.lookup(o2_id)?;
2905                    let o2_handle = get_expr_handle!(o2_id, o2_lexp);
2906
2907                    let expr = crate::Expression::Select {
2908                        condition: cond_handle,
2909                        accept: o1_handle,
2910                        reject: o2_handle,
2911                    };
2912                    self.lookup_expression.insert(
2913                        result_id,
2914                        LookupExpression {
2915                            handle: ctx.expressions.append(expr, span),
2916                            type_id: result_type_id,
2917                            block_id,
2918                        },
2919                    );
2920                }
2921                Op::VectorShuffle => {
2922                    inst.expect_at_least(5)?;
2923                    let result_type_id = self.next()?;
2924                    let result_id = self.next()?;
2925                    let v1_id = self.next()?;
2926                    let v2_id = self.next()?;
2927
2928                    let v1_lexp = self.lookup_expression.lookup(v1_id)?;
2929                    let v1_lty = self.lookup_type.lookup(v1_lexp.type_id)?;
2930                    let v1_handle = get_expr_handle!(v1_id, v1_lexp);
2931                    let n1 = match ctx.module.types[v1_lty.handle].inner {
2932                        crate::TypeInner::Vector { size, .. } => size as u32,
2933                        _ => return Err(Error::InvalidInnerType(v1_lexp.type_id)),
2934                    };
2935                    let v2_lexp = self.lookup_expression.lookup(v2_id)?;
2936                    let v2_lty = self.lookup_type.lookup(v2_lexp.type_id)?;
2937                    let v2_handle = get_expr_handle!(v2_id, v2_lexp);
2938                    let n2 = match ctx.module.types[v2_lty.handle].inner {
2939                        crate::TypeInner::Vector { size, .. } => size as u32,
2940                        _ => return Err(Error::InvalidInnerType(v2_lexp.type_id)),
2941                    };
2942
2943                    self.temp_bytes.clear();
2944                    let mut max_component = 0;
2945                    for _ in 5..inst.wc as usize {
2946                        let mut index = self.next()?;
2947                        if index == u32::MAX {
2948                            // treat Undefined as X
2949                            index = 0;
2950                        }
2951                        max_component = max_component.max(index);
2952                        self.temp_bytes.push(index as u8);
2953                    }
2954
2955                    // Check for swizzle first.
2956                    let expr = if max_component < n1 {
2957                        use crate::SwizzleComponent as Sc;
2958                        let size = match self.temp_bytes.len() {
2959                            2 => crate::VectorSize::Bi,
2960                            3 => crate::VectorSize::Tri,
2961                            _ => crate::VectorSize::Quad,
2962                        };
2963                        let mut pattern = [Sc::X; 4];
2964                        for (pat, index) in pattern.iter_mut().zip(self.temp_bytes.drain(..)) {
2965                            *pat = match index {
2966                                0 => Sc::X,
2967                                1 => Sc::Y,
2968                                2 => Sc::Z,
2969                                _ => Sc::W,
2970                            };
2971                        }
2972                        crate::Expression::Swizzle {
2973                            size,
2974                            vector: v1_handle,
2975                            pattern,
2976                        }
2977                    } else {
2978                        // Fall back to access + compose
2979                        let mut components = Vec::with_capacity(self.temp_bytes.len());
2980                        for index in self.temp_bytes.drain(..).map(|i| i as u32) {
2981                            let expr = if index < n1 {
2982                                crate::Expression::AccessIndex {
2983                                    base: v1_handle,
2984                                    index,
2985                                }
2986                            } else if index < n1 + n2 {
2987                                crate::Expression::AccessIndex {
2988                                    base: v2_handle,
2989                                    index: index - n1,
2990                                }
2991                            } else {
2992                                return Err(Error::InvalidAccessIndex(index));
2993                            };
2994                            components.push(ctx.expressions.append(expr, span));
2995                        }
2996                        crate::Expression::Compose {
2997                            ty: self.lookup_type.lookup(result_type_id)?.handle,
2998                            components,
2999                        }
3000                    };
3001
3002                    self.lookup_expression.insert(
3003                        result_id,
3004                        LookupExpression {
3005                            handle: ctx.expressions.append(expr, span),
3006                            type_id: result_type_id,
3007                            block_id,
3008                        },
3009                    );
3010                }
3011                Op::Bitcast
3012                | Op::ConvertSToF
3013                | Op::ConvertUToF
3014                | Op::ConvertFToU
3015                | Op::ConvertFToS
3016                | Op::FConvert
3017                | Op::UConvert
3018                | Op::SConvert => {
3019                    inst.expect(4)?;
3020                    let result_type_id = self.next()?;
3021                    let result_id = self.next()?;
3022                    let value_id = self.next()?;
3023
3024                    let value_lexp = self.lookup_expression.lookup(value_id)?;
3025                    let ty_lookup = self.lookup_type.lookup(result_type_id)?;
3026                    let scalar = match ctx.module.types[ty_lookup.handle].inner {
3027                        crate::TypeInner::Scalar(scalar)
3028                        | crate::TypeInner::Vector { scalar, .. }
3029                        | crate::TypeInner::Matrix { scalar, .. } => scalar,
3030                        _ => return Err(Error::InvalidAsType(ty_lookup.handle)),
3031                    };
3032
3033                    let expr = crate::Expression::As {
3034                        expr: get_expr_handle!(value_id, value_lexp),
3035                        kind: scalar.kind,
3036                        convert: if scalar.kind == crate::ScalarKind::Bool {
3037                            Some(crate::BOOL_WIDTH)
3038                        } else if inst.op == Op::Bitcast {
3039                            None
3040                        } else {
3041                            Some(scalar.width)
3042                        },
3043                    };
3044                    self.lookup_expression.insert(
3045                        result_id,
3046                        LookupExpression {
3047                            handle: ctx.expressions.append(expr, span),
3048                            type_id: result_type_id,
3049                            block_id,
3050                        },
3051                    );
3052                }
3053                Op::FunctionCall => {
3054                    inst.expect_at_least(4)?;
3055
3056                    let result_type_id = self.next()?;
3057                    let result_id = self.next()?;
3058                    let func_id = self.next()?;
3059
3060                    let mut arguments = Vec::with_capacity(inst.wc as usize - 4);
3061                    for _ in 0..arguments.capacity() {
3062                        let arg_id = self.next()?;
3063                        let lexp = self.lookup_expression.lookup(arg_id)?;
3064                        arguments.push(get_expr_handle!(arg_id, lexp));
3065                    }
3066
3067                    block.extend(emitter.finish(ctx.expressions));
3068
3069                    // We just need an unique handle here, nothing more.
3070                    let function = self.add_call(ctx.function_id, func_id);
3071
3072                    let result = if self.lookup_void_type == Some(result_type_id) {
3073                        None
3074                    } else {
3075                        let expr_handle = ctx
3076                            .expressions
3077                            .append(crate::Expression::CallResult(function), span);
3078                        self.lookup_expression.insert(
3079                            result_id,
3080                            LookupExpression {
3081                                handle: expr_handle,
3082                                type_id: result_type_id,
3083                                block_id,
3084                            },
3085                        );
3086                        Some(expr_handle)
3087                    };
3088                    block.push(
3089                        crate::Statement::Call {
3090                            function,
3091                            arguments,
3092                            result,
3093                        },
3094                        span,
3095                    );
3096                    emitter.start(ctx.expressions);
3097                }
3098                Op::ExtInst => {
3099                    use crate::MathFunction as Mf;
3100                    use spirv::GLOp as Glo;
3101
3102                    let base_wc = 5;
3103                    inst.expect_at_least(base_wc)?;
3104
3105                    let result_type_id = self.next()?;
3106                    let result_id = self.next()?;
3107                    let set_id = self.next()?;
3108                    if Some(set_id) != self.ext_glsl_id {
3109                        return Err(Error::UnsupportedExtInstSet(set_id));
3110                    }
3111                    let inst_id = self.next()?;
3112                    let gl_op = Glo::from_u32(inst_id).ok_or(Error::UnsupportedExtInst(inst_id))?;
3113
3114                    let fun = match gl_op {
3115                        Glo::Round => Mf::Round,
3116                        Glo::RoundEven => Mf::Round,
3117                        Glo::Trunc => Mf::Trunc,
3118                        Glo::FAbs | Glo::SAbs => Mf::Abs,
3119                        Glo::FSign | Glo::SSign => Mf::Sign,
3120                        Glo::Floor => Mf::Floor,
3121                        Glo::Ceil => Mf::Ceil,
3122                        Glo::Fract => Mf::Fract,
3123                        Glo::Sin => Mf::Sin,
3124                        Glo::Cos => Mf::Cos,
3125                        Glo::Tan => Mf::Tan,
3126                        Glo::Asin => Mf::Asin,
3127                        Glo::Acos => Mf::Acos,
3128                        Glo::Atan => Mf::Atan,
3129                        Glo::Sinh => Mf::Sinh,
3130                        Glo::Cosh => Mf::Cosh,
3131                        Glo::Tanh => Mf::Tanh,
3132                        Glo::Atan2 => Mf::Atan2,
3133                        Glo::Asinh => Mf::Asinh,
3134                        Glo::Acosh => Mf::Acosh,
3135                        Glo::Atanh => Mf::Atanh,
3136                        Glo::Radians => Mf::Radians,
3137                        Glo::Degrees => Mf::Degrees,
3138                        Glo::Pow => Mf::Pow,
3139                        Glo::Exp => Mf::Exp,
3140                        Glo::Log => Mf::Log,
3141                        Glo::Exp2 => Mf::Exp2,
3142                        Glo::Log2 => Mf::Log2,
3143                        Glo::Sqrt => Mf::Sqrt,
3144                        Glo::InverseSqrt => Mf::InverseSqrt,
3145                        Glo::MatrixInverse => Mf::Inverse,
3146                        Glo::Determinant => Mf::Determinant,
3147                        Glo::ModfStruct => Mf::Modf,
3148                        Glo::FMin | Glo::UMin | Glo::SMin | Glo::NMin => Mf::Min,
3149                        Glo::FMax | Glo::UMax | Glo::SMax | Glo::NMax => Mf::Max,
3150                        Glo::FClamp | Glo::UClamp | Glo::SClamp | Glo::NClamp => Mf::Clamp,
3151                        Glo::FMix => Mf::Mix,
3152                        Glo::Step => Mf::Step,
3153                        Glo::SmoothStep => Mf::SmoothStep,
3154                        Glo::Fma => Mf::Fma,
3155                        Glo::FrexpStruct => Mf::Frexp,
3156                        Glo::Ldexp => Mf::Ldexp,
3157                        Glo::Length => Mf::Length,
3158                        Glo::Distance => Mf::Distance,
3159                        Glo::Cross => Mf::Cross,
3160                        Glo::Normalize => Mf::Normalize,
3161                        Glo::FaceForward => Mf::FaceForward,
3162                        Glo::Reflect => Mf::Reflect,
3163                        Glo::Refract => Mf::Refract,
3164                        Glo::PackUnorm4x8 => Mf::Pack4x8unorm,
3165                        Glo::PackSnorm4x8 => Mf::Pack4x8snorm,
3166                        Glo::PackHalf2x16 => Mf::Pack2x16float,
3167                        Glo::PackUnorm2x16 => Mf::Pack2x16unorm,
3168                        Glo::PackSnorm2x16 => Mf::Pack2x16snorm,
3169                        Glo::UnpackUnorm4x8 => Mf::Unpack4x8unorm,
3170                        Glo::UnpackSnorm4x8 => Mf::Unpack4x8snorm,
3171                        Glo::UnpackHalf2x16 => Mf::Unpack2x16float,
3172                        Glo::UnpackUnorm2x16 => Mf::Unpack2x16unorm,
3173                        Glo::UnpackSnorm2x16 => Mf::Unpack2x16snorm,
3174                        Glo::FindILsb => Mf::FirstTrailingBit,
3175                        Glo::FindUMsb | Glo::FindSMsb => Mf::FirstLeadingBit,
3176                        // TODO: https://github.com/gfx-rs/naga/issues/2526
3177                        Glo::Modf | Glo::Frexp => return Err(Error::UnsupportedExtInst(inst_id)),
3178                        Glo::IMix
3179                        | Glo::PackDouble2x32
3180                        | Glo::UnpackDouble2x32
3181                        | Glo::InterpolateAtCentroid
3182                        | Glo::InterpolateAtSample
3183                        | Glo::InterpolateAtOffset => {
3184                            return Err(Error::UnsupportedExtInst(inst_id))
3185                        }
3186                    };
3187
3188                    let arg_count = fun.argument_count();
3189                    inst.expect(base_wc + arg_count as u16)?;
3190                    let arg = {
3191                        let arg_id = self.next()?;
3192                        let lexp = self.lookup_expression.lookup(arg_id)?;
3193                        get_expr_handle!(arg_id, lexp)
3194                    };
3195                    let arg1 = if arg_count > 1 {
3196                        let arg_id = self.next()?;
3197                        let lexp = self.lookup_expression.lookup(arg_id)?;
3198                        Some(get_expr_handle!(arg_id, lexp))
3199                    } else {
3200                        None
3201                    };
3202                    let arg2 = if arg_count > 2 {
3203                        let arg_id = self.next()?;
3204                        let lexp = self.lookup_expression.lookup(arg_id)?;
3205                        Some(get_expr_handle!(arg_id, lexp))
3206                    } else {
3207                        None
3208                    };
3209                    let arg3 = if arg_count > 3 {
3210                        let arg_id = self.next()?;
3211                        let lexp = self.lookup_expression.lookup(arg_id)?;
3212                        Some(get_expr_handle!(arg_id, lexp))
3213                    } else {
3214                        None
3215                    };
3216
3217                    let expr = crate::Expression::Math {
3218                        fun,
3219                        arg,
3220                        arg1,
3221                        arg2,
3222                        arg3,
3223                    };
3224                    self.lookup_expression.insert(
3225                        result_id,
3226                        LookupExpression {
3227                            handle: ctx.expressions.append(expr, span),
3228                            type_id: result_type_id,
3229                            block_id,
3230                        },
3231                    );
3232                }
3233                // Relational and Logical Instructions
3234                Op::LogicalNot => {
3235                    inst.expect(4)?;
3236                    parse_expr_op!(crate::UnaryOperator::LogicalNot, UNARY)?;
3237                }
3238                Op::LogicalOr => {
3239                    inst.expect(5)?;
3240                    parse_expr_op!(crate::BinaryOperator::LogicalOr, BINARY)?;
3241                }
3242                Op::LogicalAnd => {
3243                    inst.expect(5)?;
3244                    parse_expr_op!(crate::BinaryOperator::LogicalAnd, BINARY)?;
3245                }
3246                Op::SGreaterThan | Op::SGreaterThanEqual | Op::SLessThan | Op::SLessThanEqual => {
3247                    inst.expect(5)?;
3248                    self.parse_expr_int_comparison(
3249                        ctx,
3250                        &mut emitter,
3251                        &mut block,
3252                        block_id,
3253                        body_idx,
3254                        map_binary_operator(inst.op)?,
3255                        crate::ScalarKind::Sint,
3256                    )?;
3257                }
3258                Op::UGreaterThan | Op::UGreaterThanEqual | Op::ULessThan | Op::ULessThanEqual => {
3259                    inst.expect(5)?;
3260                    self.parse_expr_int_comparison(
3261                        ctx,
3262                        &mut emitter,
3263                        &mut block,
3264                        block_id,
3265                        body_idx,
3266                        map_binary_operator(inst.op)?,
3267                        crate::ScalarKind::Uint,
3268                    )?;
3269                }
3270                Op::FOrdEqual
3271                | Op::FUnordEqual
3272                | Op::FOrdNotEqual
3273                | Op::FUnordNotEqual
3274                | Op::FOrdLessThan
3275                | Op::FUnordLessThan
3276                | Op::FOrdGreaterThan
3277                | Op::FUnordGreaterThan
3278                | Op::FOrdLessThanEqual
3279                | Op::FUnordLessThanEqual
3280                | Op::FOrdGreaterThanEqual
3281                | Op::FUnordGreaterThanEqual
3282                | Op::LogicalEqual
3283                | Op::LogicalNotEqual => {
3284                    inst.expect(5)?;
3285                    let operator = map_binary_operator(inst.op)?;
3286                    parse_expr_op!(operator, BINARY)?;
3287                }
3288                Op::Any | Op::All | Op::IsNan | Op::IsInf | Op::IsFinite | Op::IsNormal => {
3289                    inst.expect(4)?;
3290                    let result_type_id = self.next()?;
3291                    let result_id = self.next()?;
3292                    let arg_id = self.next()?;
3293
3294                    let arg_lexp = self.lookup_expression.lookup(arg_id)?;
3295                    let arg_handle = get_expr_handle!(arg_id, arg_lexp);
3296
3297                    let expr = crate::Expression::Relational {
3298                        fun: map_relational_fun(inst.op)?,
3299                        argument: arg_handle,
3300                    };
3301                    self.lookup_expression.insert(
3302                        result_id,
3303                        LookupExpression {
3304                            handle: ctx.expressions.append(expr, span),
3305                            type_id: result_type_id,
3306                            block_id,
3307                        },
3308                    );
3309                }
3310                Op::Kill => {
3311                    inst.expect(1)?;
3312                    break Some(crate::Statement::Kill);
3313                }
3314                Op::Unreachable => {
3315                    inst.expect(1)?;
3316                    break None;
3317                }
3318                Op::Return => {
3319                    inst.expect(1)?;
3320                    break Some(crate::Statement::Return { value: None });
3321                }
3322                Op::ReturnValue => {
3323                    inst.expect(2)?;
3324                    let value_id = self.next()?;
3325                    let value_lexp = self.lookup_expression.lookup(value_id)?;
3326                    let value_handle = get_expr_handle!(value_id, value_lexp);
3327                    break Some(crate::Statement::Return {
3328                        value: Some(value_handle),
3329                    });
3330                }
3331                Op::Branch => {
3332                    inst.expect(2)?;
3333                    let target_id = self.next()?;
3334
3335                    // If this is a branch to a merge or continue block, then
3336                    // that ends the current body.
3337                    //
3338                    // Why can we count on finding an entry here when it's
3339                    // needed? SPIR-V requires dominators to appear before
3340                    // blocks they dominate, so we will have visited a
3341                    // structured control construct's header block before
3342                    // anything that could exit it.
3343                    if let Some(info) = ctx.mergers.get(&target_id) {
3344                        block.extend(emitter.finish(ctx.expressions));
3345                        ctx.blocks.insert(block_id, block);
3346                        let body = &mut ctx.bodies[body_idx];
3347                        body.data.push(BodyFragment::BlockId(block_id));
3348
3349                        merger(body, info);
3350
3351                        return Ok(());
3352                    }
3353
3354                    // If `target_id` has no entry in `ctx.body_for_label`, then
3355                    // this must be the only branch to it:
3356                    //
3357                    // - We've already established that it's not anybody's merge
3358                    //   block.
3359                    //
3360                    // - It can't be a switch case. Only switch header blocks
3361                    //   and other switch cases can branch to a switch case.
3362                    //   Switch header blocks must dominate all their cases, so
3363                    //   they must appear in the file before them, and when we
3364                    //   see `Op::Switch` we populate `ctx.body_for_label` for
3365                    //   every switch case.
3366                    //
3367                    // Thus, `target_id` must be a simple extension of the
3368                    // current block, which we dominate, so we know we'll
3369                    // encounter it later in the file.
3370                    ctx.body_for_label.entry(target_id).or_insert(body_idx);
3371
3372                    break None;
3373                }
3374                Op::BranchConditional => {
3375                    inst.expect_at_least(4)?;
3376
3377                    let condition = {
3378                        let condition_id = self.next()?;
3379                        let lexp = self.lookup_expression.lookup(condition_id)?;
3380                        get_expr_handle!(condition_id, lexp)
3381                    };
3382
3383                    // HACK(eddyb) Naga doesn't seem to have this helper,
3384                    // so it's declared on the fly here for convenience.
3385                    #[derive(Copy, Clone)]
3386                    struct BranchTarget {
3387                        label_id: spirv::Word,
3388                        merge_info: Option<MergeBlockInformation>,
3389                    }
3390                    let branch_target = |label_id| BranchTarget {
3391                        label_id,
3392                        merge_info: ctx.mergers.get(&label_id).copied(),
3393                    };
3394
3395                    let true_target = branch_target(self.next()?);
3396                    let false_target = branch_target(self.next()?);
3397
3398                    // Consume branch weights
3399                    for _ in 4..inst.wc {
3400                        let _ = self.next()?;
3401                    }
3402
3403                    // Handle `OpBranchConditional`s used at the end of a loop
3404                    // body's "continuing" section as a "conditional backedge",
3405                    // i.e. a `do`-`while` condition, or `break if` in WGSL.
3406
3407                    // HACK(eddyb) this has to go to the parent *twice*, because
3408                    // `OpLoopMerge` left the "continuing" section nested in the
3409                    // loop body in terms of `parent`, but not `BodyFragment`.
3410                    let parent_body_idx = ctx.bodies[body_idx].parent;
3411                    let parent_parent_body_idx = ctx.bodies[parent_body_idx].parent;
3412                    match ctx.bodies[parent_parent_body_idx].data[..] {
3413                        // The `OpLoopMerge`'s `continuing` block and the loop's
3414                        // backedge block may not be the same, but they'll both
3415                        // belong to the same body.
3416                        [.., BodyFragment::Loop {
3417                            body: loop_body_idx,
3418                            continuing: loop_continuing_idx,
3419                            break_if: ref mut break_if_slot @ None,
3420                        }] if body_idx == loop_continuing_idx => {
3421                            // Try both orderings of break-vs-backedge, because
3422                            // SPIR-V is symmetrical here, unlike WGSL `break if`.
3423                            let break_if_cond = [true, false].into_iter().find_map(|true_breaks| {
3424                                let (break_candidate, backedge_candidate) = if true_breaks {
3425                                    (true_target, false_target)
3426                                } else {
3427                                    (false_target, true_target)
3428                                };
3429
3430                                if break_candidate.merge_info
3431                                    != Some(MergeBlockInformation::LoopMerge)
3432                                {
3433                                    return None;
3434                                }
3435
3436                                // HACK(eddyb) since Naga doesn't explicitly track
3437                                // backedges, this is checking for the outcome of
3438                                // `OpLoopMerge` below (even if it looks weird).
3439                                let backedge_candidate_is_backedge =
3440                                    backedge_candidate.merge_info.is_none()
3441                                        && ctx.body_for_label.get(&backedge_candidate.label_id)
3442                                            == Some(&loop_body_idx);
3443                                if !backedge_candidate_is_backedge {
3444                                    return None;
3445                                }
3446
3447                                Some(if true_breaks {
3448                                    condition
3449                                } else {
3450                                    ctx.expressions.append(
3451                                        crate::Expression::Unary {
3452                                            op: crate::UnaryOperator::LogicalNot,
3453                                            expr: condition,
3454                                        },
3455                                        span,
3456                                    )
3457                                })
3458                            });
3459
3460                            if let Some(break_if_cond) = break_if_cond {
3461                                *break_if_slot = Some(break_if_cond);
3462
3463                                // This `OpBranchConditional` ends the "continuing"
3464                                // section of the loop body as normal, with the
3465                                // `break if` condition having been stashed above.
3466                                break None;
3467                            }
3468                        }
3469                        _ => {}
3470                    }
3471
3472                    block.extend(emitter.finish(ctx.expressions));
3473                    ctx.blocks.insert(block_id, block);
3474                    let body = &mut ctx.bodies[body_idx];
3475                    body.data.push(BodyFragment::BlockId(block_id));
3476
3477                    let same_target = true_target.label_id == false_target.label_id;
3478
3479                    // Start a body block for the `accept` branch.
3480                    let accept = ctx.bodies.len();
3481                    let mut accept_block = Body::with_parent(body_idx);
3482
3483                    // If the `OpBranchConditional` target is somebody else's
3484                    // merge or continue block, then put a `Break` or `Continue`
3485                    // statement in this new body block.
3486                    if let Some(info) = true_target.merge_info {
3487                        merger(
3488                            match same_target {
3489                                true => &mut ctx.bodies[body_idx],
3490                                false => &mut accept_block,
3491                            },
3492                            &info,
3493                        )
3494                    } else {
3495                        // Note the body index for the block we're branching to.
3496                        let prev = ctx.body_for_label.insert(
3497                            true_target.label_id,
3498                            match same_target {
3499                                true => body_idx,
3500                                false => accept,
3501                            },
3502                        );
3503                        debug_assert!(prev.is_none());
3504                    }
3505
3506                    if same_target {
3507                        return Ok(());
3508                    }
3509
3510                    ctx.bodies.push(accept_block);
3511
3512                    // Handle the `reject` branch just like the `accept` block.
3513                    let reject = ctx.bodies.len();
3514                    let mut reject_block = Body::with_parent(body_idx);
3515
3516                    if let Some(info) = false_target.merge_info {
3517                        merger(&mut reject_block, &info)
3518                    } else {
3519                        let prev = ctx.body_for_label.insert(false_target.label_id, reject);
3520                        debug_assert!(prev.is_none());
3521                    }
3522
3523                    ctx.bodies.push(reject_block);
3524
3525                    let body = &mut ctx.bodies[body_idx];
3526                    body.data.push(BodyFragment::If {
3527                        condition,
3528                        accept,
3529                        reject,
3530                    });
3531
3532                    return Ok(());
3533                }
3534                Op::Switch => {
3535                    inst.expect_at_least(3)?;
3536                    let selector = self.next()?;
3537                    let default_id = self.next()?;
3538
3539                    // If the previous instruction was a `OpSelectionMerge` then we must
3540                    // promote the `MergeBlockInformation` to a `SwitchMerge`
3541                    if let Some(merge) = selection_merge_block {
3542                        ctx.mergers
3543                            .insert(merge, MergeBlockInformation::SwitchMerge);
3544                    }
3545
3546                    let default = ctx.bodies.len();
3547                    ctx.bodies.push(Body::with_parent(body_idx));
3548                    ctx.body_for_label.entry(default_id).or_insert(default);
3549
3550                    let selector_lexp = &self.lookup_expression[&selector];
3551                    let selector_lty = self.lookup_type.lookup(selector_lexp.type_id)?;
3552                    let selector_handle = get_expr_handle!(selector, selector_lexp);
3553                    let selector = match ctx.module.types[selector_lty.handle].inner {
3554                        crate::TypeInner::Scalar(crate::Scalar {
3555                            kind: crate::ScalarKind::Uint,
3556                            width: _,
3557                        }) => {
3558                            // IR expects a signed integer, so do a bitcast
3559                            ctx.expressions.append(
3560                                crate::Expression::As {
3561                                    kind: crate::ScalarKind::Sint,
3562                                    expr: selector_handle,
3563                                    convert: None,
3564                                },
3565                                span,
3566                            )
3567                        }
3568                        crate::TypeInner::Scalar(crate::Scalar {
3569                            kind: crate::ScalarKind::Sint,
3570                            width: _,
3571                        }) => selector_handle,
3572                        ref other => unimplemented!("Unexpected selector {:?}", other),
3573                    };
3574
3575                    // Clear past switch cases to prevent them from entering this one
3576                    self.switch_cases.clear();
3577
3578                    for _ in 0..(inst.wc - 3) / 2 {
3579                        let literal = self.next()?;
3580                        let target = self.next()?;
3581
3582                        let case_body_idx = ctx.bodies.len();
3583
3584                        // Check if any previous case already used this target block id, if so
3585                        // group them together to reorder them later so that no weird
3586                        // fallthrough cases happen.
3587                        if let Some(&mut (_, ref mut literals)) = self.switch_cases.get_mut(&target)
3588                        {
3589                            literals.push(literal as i32);
3590                            continue;
3591                        }
3592
3593                        let mut body = Body::with_parent(body_idx);
3594
3595                        if let Some(info) = ctx.mergers.get(&target) {
3596                            merger(&mut body, info);
3597                        }
3598
3599                        ctx.bodies.push(body);
3600                        ctx.body_for_label.entry(target).or_insert(case_body_idx);
3601
3602                        // Register this target block id as already having been processed and
3603                        // the respective body index assigned and the first case value
3604                        self.switch_cases
3605                            .insert(target, (case_body_idx, vec![literal as i32]));
3606                    }
3607
3608                    // Loop through the collected target blocks creating a new case for each
3609                    // literal pointing to it, only one case will have the true body and all the
3610                    // others will be empty fallthrough so that they all execute the same body
3611                    // without duplicating code.
3612                    //
3613                    // Since `switch_cases` is an indexmap the order of insertion is preserved
3614                    // this is needed because spir-v defines fallthrough order in the switch
3615                    // instruction.
3616                    let mut cases = Vec::with_capacity((inst.wc as usize - 3) / 2);
3617                    for &(case_body_idx, ref literals) in self.switch_cases.values() {
3618                        let value = literals[0];
3619
3620                        for &literal in literals.iter().skip(1) {
3621                            let empty_body_idx = ctx.bodies.len();
3622                            let body = Body::with_parent(body_idx);
3623
3624                            ctx.bodies.push(body);
3625
3626                            cases.push((literal, empty_body_idx));
3627                        }
3628
3629                        cases.push((value, case_body_idx));
3630                    }
3631
3632                    block.extend(emitter.finish(ctx.expressions));
3633
3634                    let body = &mut ctx.bodies[body_idx];
3635                    ctx.blocks.insert(block_id, block);
3636                    // Make sure the vector has space for at least two more allocations
3637                    body.data.reserve(2);
3638                    body.data.push(BodyFragment::BlockId(block_id));
3639                    body.data.push(BodyFragment::Switch {
3640                        selector,
3641                        cases,
3642                        default,
3643                    });
3644
3645                    return Ok(());
3646                }
3647                Op::SelectionMerge => {
3648                    inst.expect(3)?;
3649                    let merge_block_id = self.next()?;
3650                    // TODO: Selection Control Mask
3651                    let _selection_control = self.next()?;
3652
3653                    // Indicate that the merge block is a continuation of the
3654                    // current `Body`.
3655                    ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);
3656
3657                    // Let subsequent branches to the merge block know that
3658                    // they've reached the end of the selection construct.
3659                    ctx.mergers
3660                        .insert(merge_block_id, MergeBlockInformation::SelectionMerge);
3661
3662                    selection_merge_block = Some(merge_block_id);
3663                }
3664                Op::LoopMerge => {
3665                    inst.expect_at_least(4)?;
3666                    let merge_block_id = self.next()?;
3667                    let continuing = self.next()?;
3668
3669                    // TODO: Loop Control Parameters
3670                    for _ in 0..inst.wc - 3 {
3671                        self.next()?;
3672                    }
3673
3674                    // Indicate that the merge block is a continuation of the
3675                    // current `Body`.
3676                    ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);
3677                    // Let subsequent branches to the merge block know that
3678                    // they're `Break` statements.
3679                    ctx.mergers
3680                        .insert(merge_block_id, MergeBlockInformation::LoopMerge);
3681
3682                    let loop_body_idx = ctx.bodies.len();
3683                    ctx.bodies.push(Body::with_parent(body_idx));
3684
3685                    let continue_idx = ctx.bodies.len();
3686                    // The continue block inherits the scope of the loop body
3687                    ctx.bodies.push(Body::with_parent(loop_body_idx));
3688                    ctx.body_for_label.entry(continuing).or_insert(continue_idx);
3689                    // Let subsequent branches to the continue block know that
3690                    // they're `Continue` statements.
3691                    ctx.mergers
3692                        .insert(continuing, MergeBlockInformation::LoopContinue);
3693
3694                    // The loop header always belongs to the loop body
3695                    ctx.body_for_label.insert(block_id, loop_body_idx);
3696
3697                    let parent_body = &mut ctx.bodies[body_idx];
3698                    parent_body.data.push(BodyFragment::Loop {
3699                        body: loop_body_idx,
3700                        continuing: continue_idx,
3701                        break_if: None,
3702                    });
3703                    body_idx = loop_body_idx;
3704                }
3705                Op::DPdxCoarse => {
3706                    parse_expr_op!(
3707                        crate::DerivativeAxis::X,
3708                        crate::DerivativeControl::Coarse,
3709                        DERIVATIVE
3710                    )?;
3711                }
3712                Op::DPdyCoarse => {
3713                    parse_expr_op!(
3714                        crate::DerivativeAxis::Y,
3715                        crate::DerivativeControl::Coarse,
3716                        DERIVATIVE
3717                    )?;
3718                }
3719                Op::FwidthCoarse => {
3720                    parse_expr_op!(
3721                        crate::DerivativeAxis::Width,
3722                        crate::DerivativeControl::Coarse,
3723                        DERIVATIVE
3724                    )?;
3725                }
3726                Op::DPdxFine => {
3727                    parse_expr_op!(
3728                        crate::DerivativeAxis::X,
3729                        crate::DerivativeControl::Fine,
3730                        DERIVATIVE
3731                    )?;
3732                }
3733                Op::DPdyFine => {
3734                    parse_expr_op!(
3735                        crate::DerivativeAxis::Y,
3736                        crate::DerivativeControl::Fine,
3737                        DERIVATIVE
3738                    )?;
3739                }
3740                Op::FwidthFine => {
3741                    parse_expr_op!(
3742                        crate::DerivativeAxis::Width,
3743                        crate::DerivativeControl::Fine,
3744                        DERIVATIVE
3745                    )?;
3746                }
3747                Op::DPdx => {
3748                    parse_expr_op!(
3749                        crate::DerivativeAxis::X,
3750                        crate::DerivativeControl::None,
3751                        DERIVATIVE
3752                    )?;
3753                }
3754                Op::DPdy => {
3755                    parse_expr_op!(
3756                        crate::DerivativeAxis::Y,
3757                        crate::DerivativeControl::None,
3758                        DERIVATIVE
3759                    )?;
3760                }
3761                Op::Fwidth => {
3762                    parse_expr_op!(
3763                        crate::DerivativeAxis::Width,
3764                        crate::DerivativeControl::None,
3765                        DERIVATIVE
3766                    )?;
3767                }
3768                Op::ArrayLength => {
3769                    inst.expect(5)?;
3770                    let result_type_id = self.next()?;
3771                    let result_id = self.next()?;
3772                    let structure_id = self.next()?;
3773                    let member_index = self.next()?;
3774
3775                    // We're assuming that the validation pass, if it's run, will catch if the
3776                    // wrong types or parameters are supplied here.
3777
3778                    let structure_ptr = self.lookup_expression.lookup(structure_id)?;
3779                    let structure_handle = get_expr_handle!(structure_id, structure_ptr);
3780
3781                    let member_ptr = ctx.expressions.append(
3782                        crate::Expression::AccessIndex {
3783                            base: structure_handle,
3784                            index: member_index,
3785                        },
3786                        span,
3787                    );
3788
3789                    let length = ctx
3790                        .expressions
3791                        .append(crate::Expression::ArrayLength(member_ptr), span);
3792
3793                    self.lookup_expression.insert(
3794                        result_id,
3795                        LookupExpression {
3796                            handle: length,
3797                            type_id: result_type_id,
3798                            block_id,
3799                        },
3800                    );
3801                }
3802                Op::CopyMemory => {
3803                    inst.expect_at_least(3)?;
3804                    let target_id = self.next()?;
3805                    let source_id = self.next()?;
3806                    let _memory_access = if inst.wc != 3 {
3807                        inst.expect(4)?;
3808                        spirv::MemoryAccess::from_bits(self.next()?)
3809                            .ok_or(Error::InvalidParameter(Op::CopyMemory))?
3810                    } else {
3811                        spirv::MemoryAccess::NONE
3812                    };
3813
3814                    // TODO: check if the source and target types are the same?
3815                    let target = self.lookup_expression.lookup(target_id)?;
3816                    let target_handle = get_expr_handle!(target_id, target);
3817                    let source = self.lookup_expression.lookup(source_id)?;
3818                    let source_handle = get_expr_handle!(source_id, source);
3819
3820                    // This operation is practically the same as loading and then storing, I think.
3821                    let value_expr = ctx.expressions.append(
3822                        crate::Expression::Load {
3823                            pointer: source_handle,
3824                        },
3825                        span,
3826                    );
3827
3828                    block.extend(emitter.finish(ctx.expressions));
3829                    block.push(
3830                        crate::Statement::Store {
3831                            pointer: target_handle,
3832                            value: value_expr,
3833                        },
3834                        span,
3835                    );
3836
3837                    emitter.start(ctx.expressions);
3838                }
3839                Op::ControlBarrier => {
3840                    inst.expect(4)?;
3841                    let exec_scope_id = self.next()?;
3842                    let _mem_scope_raw = self.next()?;
3843                    let semantics_id = self.next()?;
3844                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
3845                    let semantics_const = self.lookup_constant.lookup(semantics_id)?;
3846
3847                    let exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
3848                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
3849                    let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)
3850                        .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;
3851
3852                    if exec_scope == spirv::Scope::Workgroup as u32
3853                        || exec_scope == spirv::Scope::Subgroup as u32
3854                    {
3855                        let mut flags = crate::Barrier::empty();
3856                        flags.set(
3857                            crate::Barrier::STORAGE,
3858                            semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,
3859                        );
3860                        flags.set(
3861                            crate::Barrier::WORK_GROUP,
3862                            semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,
3863                        );
3864                        flags.set(
3865                            crate::Barrier::SUB_GROUP,
3866                            semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,
3867                        );
3868                        flags.set(
3869                            crate::Barrier::TEXTURE,
3870                            semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,
3871                        );
3872                        block.push(crate::Statement::ControlBarrier(flags), span);
3873                    } else {
3874                        log::warn!("Unsupported barrier execution scope: {exec_scope}");
3875                    }
3876                }
3877                Op::MemoryBarrier => {
3878                    inst.expect(3)?;
3879                    let mem_scope_id = self.next()?;
3880                    let semantics_id = self.next()?;
3881                    let mem_scope_const = self.lookup_constant.lookup(mem_scope_id)?;
3882                    let semantics_const = self.lookup_constant.lookup(semantics_id)?;
3883
3884                    let mem_scope = resolve_constant(ctx.gctx(), &mem_scope_const.inner)
3885                        .ok_or(Error::InvalidBarrierScope(mem_scope_id))?;
3886                    let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)
3887                        .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;
3888
3889                    let mut flags = if mem_scope == spirv::Scope::Device as u32 {
3890                        crate::Barrier::STORAGE
3891                    } else if mem_scope == spirv::Scope::Workgroup as u32 {
3892                        crate::Barrier::WORK_GROUP
3893                    } else if mem_scope == spirv::Scope::Subgroup as u32 {
3894                        crate::Barrier::SUB_GROUP
3895                    } else {
3896                        crate::Barrier::empty()
3897                    };
3898                    flags.set(
3899                        crate::Barrier::STORAGE,
3900                        semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,
3901                    );
3902                    flags.set(
3903                        crate::Barrier::WORK_GROUP,
3904                        semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,
3905                    );
3906                    flags.set(
3907                        crate::Barrier::SUB_GROUP,
3908                        semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,
3909                    );
3910                    flags.set(
3911                        crate::Barrier::TEXTURE,
3912                        semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,
3913                    );
3914                    block.push(crate::Statement::MemoryBarrier(flags), span);
3915                }
3916                Op::CopyObject => {
3917                    inst.expect(4)?;
3918                    let result_type_id = self.next()?;
3919                    let result_id = self.next()?;
3920                    let operand_id = self.next()?;
3921
3922                    let lookup = self.lookup_expression.lookup(operand_id)?;
3923                    let handle = get_expr_handle!(operand_id, lookup);
3924
3925                    self.lookup_expression.insert(
3926                        result_id,
3927                        LookupExpression {
3928                            handle,
3929                            type_id: result_type_id,
3930                            block_id,
3931                        },
3932                    );
3933                }
3934                Op::GroupNonUniformBallot => {
3935                    inst.expect(5)?;
3936                    block.extend(emitter.finish(ctx.expressions));
3937                    let result_type_id = self.next()?;
3938                    let result_id = self.next()?;
3939                    let exec_scope_id = self.next()?;
3940                    let predicate_id = self.next()?;
3941
3942                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
3943                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
3944                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
3945                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
3946
3947                    let predicate = if self
3948                        .lookup_constant
3949                        .lookup(predicate_id)
3950                        .ok()
3951                        .filter(|predicate_const| match predicate_const.inner {
3952                            Constant::Constant(constant) => matches!(
3953                                ctx.gctx().global_expressions[ctx.gctx().constants[constant].init],
3954                                crate::Expression::Literal(crate::Literal::Bool(true)),
3955                            ),
3956                            Constant::Override(_) => false,
3957                        })
3958                        .is_some()
3959                    {
3960                        None
3961                    } else {
3962                        let predicate_lookup = self.lookup_expression.lookup(predicate_id)?;
3963                        let predicate_handle = get_expr_handle!(predicate_id, predicate_lookup);
3964                        Some(predicate_handle)
3965                    };
3966
3967                    let result_handle = ctx
3968                        .expressions
3969                        .append(crate::Expression::SubgroupBallotResult, span);
3970                    self.lookup_expression.insert(
3971                        result_id,
3972                        LookupExpression {
3973                            handle: result_handle,
3974                            type_id: result_type_id,
3975                            block_id,
3976                        },
3977                    );
3978
3979                    block.push(
3980                        crate::Statement::SubgroupBallot {
3981                            result: result_handle,
3982                            predicate,
3983                        },
3984                        span,
3985                    );
3986                    emitter.start(ctx.expressions);
3987                }
3988                Op::GroupNonUniformAll
3989                | Op::GroupNonUniformAny
3990                | Op::GroupNonUniformIAdd
3991                | Op::GroupNonUniformFAdd
3992                | Op::GroupNonUniformIMul
3993                | Op::GroupNonUniformFMul
3994                | Op::GroupNonUniformSMax
3995                | Op::GroupNonUniformUMax
3996                | Op::GroupNonUniformFMax
3997                | Op::GroupNonUniformSMin
3998                | Op::GroupNonUniformUMin
3999                | Op::GroupNonUniformFMin
4000                | Op::GroupNonUniformBitwiseAnd
4001                | Op::GroupNonUniformBitwiseOr
4002                | Op::GroupNonUniformBitwiseXor
4003                | Op::GroupNonUniformLogicalAnd
4004                | Op::GroupNonUniformLogicalOr
4005                | Op::GroupNonUniformLogicalXor => {
4006                    block.extend(emitter.finish(ctx.expressions));
4007                    inst.expect(
4008                        if matches!(inst.op, Op::GroupNonUniformAll | Op::GroupNonUniformAny) {
4009                            5
4010                        } else {
4011                            6
4012                        },
4013                    )?;
4014                    let result_type_id = self.next()?;
4015                    let result_id = self.next()?;
4016                    let exec_scope_id = self.next()?;
4017                    let collective_op_id = match inst.op {
4018                        Op::GroupNonUniformAll | Op::GroupNonUniformAny => {
4019                            crate::CollectiveOperation::Reduce
4020                        }
4021                        _ => {
4022                            let group_op_id = self.next()?;
4023                            match spirv::GroupOperation::from_u32(group_op_id) {
4024                                Some(spirv::GroupOperation::Reduce) => {
4025                                    crate::CollectiveOperation::Reduce
4026                                }
4027                                Some(spirv::GroupOperation::InclusiveScan) => {
4028                                    crate::CollectiveOperation::InclusiveScan
4029                                }
4030                                Some(spirv::GroupOperation::ExclusiveScan) => {
4031                                    crate::CollectiveOperation::ExclusiveScan
4032                                }
4033                                _ => return Err(Error::UnsupportedGroupOperation(group_op_id)),
4034                            }
4035                        }
4036                    };
4037                    let argument_id = self.next()?;
4038
4039                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;
4040                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);
4041
4042                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
4043                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
4044                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
4045                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
4046
4047                    let op_id = match inst.op {
4048                        Op::GroupNonUniformAll => crate::SubgroupOperation::All,
4049                        Op::GroupNonUniformAny => crate::SubgroupOperation::Any,
4050                        Op::GroupNonUniformIAdd | Op::GroupNonUniformFAdd => {
4051                            crate::SubgroupOperation::Add
4052                        }
4053                        Op::GroupNonUniformIMul | Op::GroupNonUniformFMul => {
4054                            crate::SubgroupOperation::Mul
4055                        }
4056                        Op::GroupNonUniformSMax
4057                        | Op::GroupNonUniformUMax
4058                        | Op::GroupNonUniformFMax => crate::SubgroupOperation::Max,
4059                        Op::GroupNonUniformSMin
4060                        | Op::GroupNonUniformUMin
4061                        | Op::GroupNonUniformFMin => crate::SubgroupOperation::Min,
4062                        Op::GroupNonUniformBitwiseAnd | Op::GroupNonUniformLogicalAnd => {
4063                            crate::SubgroupOperation::And
4064                        }
4065                        Op::GroupNonUniformBitwiseOr | Op::GroupNonUniformLogicalOr => {
4066                            crate::SubgroupOperation::Or
4067                        }
4068                        Op::GroupNonUniformBitwiseXor | Op::GroupNonUniformLogicalXor => {
4069                            crate::SubgroupOperation::Xor
4070                        }
4071                        _ => unreachable!(),
4072                    };
4073
4074                    let result_type = self.lookup_type.lookup(result_type_id)?;
4075
4076                    let result_handle = ctx.expressions.append(
4077                        crate::Expression::SubgroupOperationResult {
4078                            ty: result_type.handle,
4079                        },
4080                        span,
4081                    );
4082                    self.lookup_expression.insert(
4083                        result_id,
4084                        LookupExpression {
4085                            handle: result_handle,
4086                            type_id: result_type_id,
4087                            block_id,
4088                        },
4089                    );
4090
4091                    block.push(
4092                        crate::Statement::SubgroupCollectiveOperation {
4093                            result: result_handle,
4094                            op: op_id,
4095                            collective_op: collective_op_id,
4096                            argument: argument_handle,
4097                        },
4098                        span,
4099                    );
4100                    emitter.start(ctx.expressions);
4101                }
4102                Op::GroupNonUniformBroadcastFirst
4103                | Op::GroupNonUniformBroadcast
4104                | Op::GroupNonUniformShuffle
4105                | Op::GroupNonUniformShuffleDown
4106                | Op::GroupNonUniformShuffleUp
4107                | Op::GroupNonUniformShuffleXor
4108                | Op::GroupNonUniformQuadBroadcast => {
4109                    inst.expect(if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {
4110                        5
4111                    } else {
4112                        6
4113                    })?;
4114                    block.extend(emitter.finish(ctx.expressions));
4115                    let result_type_id = self.next()?;
4116                    let result_id = self.next()?;
4117                    let exec_scope_id = self.next()?;
4118                    let argument_id = self.next()?;
4119
4120                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;
4121                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);
4122
4123                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
4124                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
4125                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
4126                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
4127
4128                    let mode = if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {
4129                        crate::GatherMode::BroadcastFirst
4130                    } else {
4131                        let index_id = self.next()?;
4132                        let index_lookup = self.lookup_expression.lookup(index_id)?;
4133                        let index_handle = get_expr_handle!(index_id, index_lookup);
4134                        match inst.op {
4135                            Op::GroupNonUniformBroadcast => {
4136                                crate::GatherMode::Broadcast(index_handle)
4137                            }
4138                            Op::GroupNonUniformShuffle => crate::GatherMode::Shuffle(index_handle),
4139                            Op::GroupNonUniformShuffleDown => {
4140                                crate::GatherMode::ShuffleDown(index_handle)
4141                            }
4142                            Op::GroupNonUniformShuffleUp => {
4143                                crate::GatherMode::ShuffleUp(index_handle)
4144                            }
4145                            Op::GroupNonUniformShuffleXor => {
4146                                crate::GatherMode::ShuffleXor(index_handle)
4147                            }
4148                            Op::GroupNonUniformQuadBroadcast => {
4149                                crate::GatherMode::QuadBroadcast(index_handle)
4150                            }
4151                            _ => unreachable!(),
4152                        }
4153                    };
4154
4155                    let result_type = self.lookup_type.lookup(result_type_id)?;
4156
4157                    let result_handle = ctx.expressions.append(
4158                        crate::Expression::SubgroupOperationResult {
4159                            ty: result_type.handle,
4160                        },
4161                        span,
4162                    );
4163                    self.lookup_expression.insert(
4164                        result_id,
4165                        LookupExpression {
4166                            handle: result_handle,
4167                            type_id: result_type_id,
4168                            block_id,
4169                        },
4170                    );
4171
4172                    block.push(
4173                        crate::Statement::SubgroupGather {
4174                            result: result_handle,
4175                            mode,
4176                            argument: argument_handle,
4177                        },
4178                        span,
4179                    );
4180                    emitter.start(ctx.expressions);
4181                }
4182                Op::GroupNonUniformQuadSwap => {
4183                    inst.expect(6)?;
4184                    block.extend(emitter.finish(ctx.expressions));
4185                    let result_type_id = self.next()?;
4186                    let result_id = self.next()?;
4187                    let exec_scope_id = self.next()?;
4188                    let argument_id = self.next()?;
4189                    let direction_id = self.next()?;
4190
4191                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;
4192                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);
4193
4194                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
4195                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
4196                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
4197                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
4198
4199                    let direction_const = self.lookup_constant.lookup(direction_id)?;
4200                    let direction_const = resolve_constant(ctx.gctx(), &direction_const.inner)
4201                        .ok_or(Error::InvalidOperand)?;
4202                    let direction = match direction_const {
4203                        0 => crate::Direction::X,
4204                        1 => crate::Direction::Y,
4205                        2 => crate::Direction::Diagonal,
4206                        _ => unreachable!(),
4207                    };
4208
4209                    let result_type = self.lookup_type.lookup(result_type_id)?;
4210
4211                    let result_handle = ctx.expressions.append(
4212                        crate::Expression::SubgroupOperationResult {
4213                            ty: result_type.handle,
4214                        },
4215                        span,
4216                    );
4217                    self.lookup_expression.insert(
4218                        result_id,
4219                        LookupExpression {
4220                            handle: result_handle,
4221                            type_id: result_type_id,
4222                            block_id,
4223                        },
4224                    );
4225
4226                    block.push(
4227                        crate::Statement::SubgroupGather {
4228                            mode: crate::GatherMode::QuadSwap(direction),
4229                            result: result_handle,
4230                            argument: argument_handle,
4231                        },
4232                        span,
4233                    );
4234                    emitter.start(ctx.expressions);
4235                }
4236                Op::AtomicLoad => {
4237                    inst.expect(6)?;
4238                    let start = self.data_offset;
4239                    let result_type_id = self.next()?;
4240                    let result_id = self.next()?;
4241                    let pointer_id = self.next()?;
4242                    let _scope_id = self.next()?;
4243                    let _memory_semantics_id = self.next()?;
4244                    let span = self.span_from_with_op(start);
4245
4246                    log::trace!("\t\t\tlooking up expr {pointer_id:?}");
4247                    let p_lexp_handle =
4248                        get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
4249
4250                    // Create an expression for our result
4251                    let expr = crate::Expression::Load {
4252                        pointer: p_lexp_handle,
4253                    };
4254                    let handle = ctx.expressions.append(expr, span);
4255                    self.lookup_expression.insert(
4256                        result_id,
4257                        LookupExpression {
4258                            handle,
4259                            type_id: result_type_id,
4260                            block_id,
4261                        },
4262                    );
4263
4264                    // Store any associated global variables so we can upgrade their types later
4265                    self.record_atomic_access(ctx, p_lexp_handle)?;
4266                }
4267                Op::AtomicStore => {
4268                    inst.expect(5)?;
4269                    let start = self.data_offset;
4270                    let pointer_id = self.next()?;
4271                    let _scope_id = self.next()?;
4272                    let _memory_semantics_id = self.next()?;
4273                    let value_id = self.next()?;
4274                    let span = self.span_from_with_op(start);
4275
4276                    log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
4277                    let p_lexp_handle =
4278                        get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
4279
4280                    log::trace!("\t\t\tlooking up value expr {pointer_id:?}");
4281                    let v_lexp_handle =
4282                        get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
4283
4284                    block.extend(emitter.finish(ctx.expressions));
4285                    // Create a statement for the op itself
4286                    let stmt = crate::Statement::Store {
4287                        pointer: p_lexp_handle,
4288                        value: v_lexp_handle,
4289                    };
4290                    block.push(stmt, span);
4291                    emitter.start(ctx.expressions);
4292
4293                    // Store any associated global variables so we can upgrade their types later
4294                    self.record_atomic_access(ctx, p_lexp_handle)?;
4295                }
4296                Op::AtomicIIncrement | Op::AtomicIDecrement => {
4297                    inst.expect(6)?;
4298                    let start = self.data_offset;
4299                    let result_type_id = self.next()?;
4300                    let result_id = self.next()?;
4301                    let pointer_id = self.next()?;
4302                    let _scope_id = self.next()?;
4303                    let _memory_semantics_id = self.next()?;
4304                    let span = self.span_from_with_op(start);
4305
4306                    let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
4307                        pointer_id,
4308                        ctx,
4309                        &mut emitter,
4310                        &mut block,
4311                        body_idx,
4312                    )?;
4313
4314                    block.extend(emitter.finish(ctx.expressions));
4315                    // Create an expression for our result
4316                    let r_lexp_handle = {
4317                        let expr = crate::Expression::AtomicResult {
4318                            ty: p_base_ty_h,
4319                            comparison: false,
4320                        };
4321                        let handle = ctx.expressions.append(expr, span);
4322                        self.lookup_expression.insert(
4323                            result_id,
4324                            LookupExpression {
4325                                handle,
4326                                type_id: result_type_id,
4327                                block_id,
4328                            },
4329                        );
4330                        handle
4331                    };
4332                    emitter.start(ctx.expressions);
4333
4334                    // Create a literal "1" to use as our value
4335                    let one_lexp_handle = make_index_literal(
4336                        ctx,
4337                        1,
4338                        &mut block,
4339                        &mut emitter,
4340                        p_base_ty_h,
4341                        result_type_id,
4342                        span,
4343                    )?;
4344
4345                    // Create a statement for the op itself
4346                    let stmt = crate::Statement::Atomic {
4347                        pointer: p_exp_h,
4348                        fun: match inst.op {
4349                            Op::AtomicIIncrement => crate::AtomicFunction::Add,
4350                            _ => crate::AtomicFunction::Subtract,
4351                        },
4352                        value: one_lexp_handle,
4353                        result: Some(r_lexp_handle),
4354                    };
4355                    block.push(stmt, span);
4356
4357                    // Store any associated global variables so we can upgrade their types later
4358                    self.record_atomic_access(ctx, p_exp_h)?;
4359                }
4360                Op::AtomicCompareExchange => {
4361                    inst.expect(9)?;
4362
4363                    let start = self.data_offset;
4364                    let span = self.span_from_with_op(start);
4365                    let result_type_id = self.next()?;
4366                    let result_id = self.next()?;
4367                    let pointer_id = self.next()?;
4368                    let _memory_scope_id = self.next()?;
4369                    let _equal_memory_semantics_id = self.next()?;
4370                    let _unequal_memory_semantics_id = self.next()?;
4371                    let value_id = self.next()?;
4372                    let comparator_id = self.next()?;
4373
4374                    let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
4375                        pointer_id,
4376                        ctx,
4377                        &mut emitter,
4378                        &mut block,
4379                        body_idx,
4380                    )?;
4381
4382                    log::trace!("\t\t\tlooking up value expr {value_id:?}");
4383                    let v_lexp_handle =
4384                        get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
4385
4386                    log::trace!("\t\t\tlooking up comparator expr {value_id:?}");
4387                    let c_lexp_handle = get_expr_handle!(
4388                        comparator_id,
4389                        self.lookup_expression.lookup(comparator_id)?
4390                    );
4391
4392                    // We know from the SPIR-V spec that the result type must be an integer
4393                    // scalar, and we'll need the type itself to get a handle to the atomic
4394                    // result struct.
4395                    let crate::TypeInner::Scalar(scalar) = ctx.module.types[p_base_ty_h].inner
4396                    else {
4397                        return Err(
4398                            crate::front::atomic_upgrade::Error::CompareExchangeNonScalarBaseType
4399                                .into(),
4400                        );
4401                    };
4402
4403                    // Get a handle to the atomic result struct type.
4404                    let atomic_result_struct_ty_h = ctx.module.generate_predeclared_type(
4405                        crate::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),
4406                    );
4407
4408                    block.extend(emitter.finish(ctx.expressions));
4409
4410                    // Create an expression for our atomic result
4411                    let atomic_lexp_handle = {
4412                        let expr = crate::Expression::AtomicResult {
4413                            ty: atomic_result_struct_ty_h,
4414                            comparison: true,
4415                        };
4416                        ctx.expressions.append(expr, span)
4417                    };
4418
4419                    // Create an dot accessor to extract the value from the
4420                    // result struct __atomic_compare_exchange_result<T> and use that
4421                    // as the expression for the result_id
4422                    {
4423                        let expr = crate::Expression::AccessIndex {
4424                            base: atomic_lexp_handle,
4425                            index: 0,
4426                        };
4427                        let handle = ctx.expressions.append(expr, span);
4428                        // Use this dot accessor as the result id's expression
4429                        let _ = self.lookup_expression.insert(
4430                            result_id,
4431                            LookupExpression {
4432                                handle,
4433                                type_id: result_type_id,
4434                                block_id,
4435                            },
4436                        );
4437                    }
4438
4439                    emitter.start(ctx.expressions);
4440
4441                    // Create a statement for the op itself
4442                    let stmt = crate::Statement::Atomic {
4443                        pointer: p_exp_h,
4444                        fun: crate::AtomicFunction::Exchange {
4445                            compare: Some(c_lexp_handle),
4446                        },
4447                        value: v_lexp_handle,
4448                        result: Some(atomic_lexp_handle),
4449                    };
4450                    block.push(stmt, span);
4451
4452                    // Store any associated global variables so we can upgrade their types later
4453                    self.record_atomic_access(ctx, p_exp_h)?;
4454                }
4455                Op::AtomicExchange
4456                | Op::AtomicIAdd
4457                | Op::AtomicISub
4458                | Op::AtomicSMin
4459                | Op::AtomicUMin
4460                | Op::AtomicSMax
4461                | Op::AtomicUMax
4462                | Op::AtomicAnd
4463                | Op::AtomicOr
4464                | Op::AtomicXor
4465                | Op::AtomicFAddEXT => self.parse_atomic_expr_with_value(
4466                    inst,
4467                    &mut emitter,
4468                    ctx,
4469                    &mut block,
4470                    block_id,
4471                    body_idx,
4472                    match inst.op {
4473                        Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None },
4474                        Op::AtomicIAdd | Op::AtomicFAddEXT => crate::AtomicFunction::Add,
4475                        Op::AtomicISub => crate::AtomicFunction::Subtract,
4476                        Op::AtomicSMin => crate::AtomicFunction::Min,
4477                        Op::AtomicUMin => crate::AtomicFunction::Min,
4478                        Op::AtomicSMax => crate::AtomicFunction::Max,
4479                        Op::AtomicUMax => crate::AtomicFunction::Max,
4480                        Op::AtomicAnd => crate::AtomicFunction::And,
4481                        Op::AtomicOr => crate::AtomicFunction::InclusiveOr,
4482                        Op::AtomicXor => crate::AtomicFunction::ExclusiveOr,
4483                        _ => unreachable!(),
4484                    },
4485                )?,
4486
4487                _ => {
4488                    return Err(Error::UnsupportedInstruction(self.state, inst.op));
4489                }
4490            }
4491        };
4492
4493        block.extend(emitter.finish(ctx.expressions));
4494        if let Some(stmt) = terminator {
4495            block.push(stmt, crate::Span::default());
4496        }
4497
4498        // Save this block fragment in `block_ctx.blocks`, and mark it to be
4499        // incorporated into the current body at `Statement` assembly time.
4500        ctx.blocks.insert(block_id, block);
4501        let body = &mut ctx.bodies[body_idx];
4502        body.data.push(BodyFragment::BlockId(block_id));
4503        Ok(())
4504    }
4505
4506    fn make_expression_storage(
4507        &mut self,
4508        globals: &Arena<crate::GlobalVariable>,
4509        constants: &Arena<crate::Constant>,
4510        overrides: &Arena<crate::Override>,
4511    ) -> Arena<crate::Expression> {
4512        let mut expressions = Arena::new();
4513        #[allow(clippy::panic)]
4514        {
4515            assert!(self.lookup_expression.is_empty());
4516        }
4517        // register global variables
4518        for (&id, var) in self.lookup_variable.iter() {
4519            let span = globals.get_span(var.handle);
4520            let handle = expressions.append(crate::Expression::GlobalVariable(var.handle), span);
4521            self.lookup_expression.insert(
4522                id,
4523                LookupExpression {
4524                    type_id: var.type_id,
4525                    handle,
4526                    // Setting this to an invalid id will cause get_expr_handle
4527                    // to default to the main body making sure no load/stores
4528                    // are added.
4529                    block_id: 0,
4530                },
4531            );
4532        }
4533        // register constants
4534        for (&id, con) in self.lookup_constant.iter() {
4535            let (expr, span) = match con.inner {
4536                Constant::Constant(c) => (crate::Expression::Constant(c), constants.get_span(c)),
4537                Constant::Override(o) => (crate::Expression::Override(o), overrides.get_span(o)),
4538            };
4539            let handle = expressions.append(expr, span);
4540            self.lookup_expression.insert(
4541                id,
4542                LookupExpression {
4543                    type_id: con.type_id,
4544                    handle,
4545                    // Setting this to an invalid id will cause get_expr_handle
4546                    // to default to the main body making sure no load/stores
4547                    // are added.
4548                    block_id: 0,
4549                },
4550            );
4551        }
4552        // done
4553        expressions
4554    }
4555
4556    fn switch(&mut self, state: ModuleState, op: spirv::Op) -> Result<(), Error> {
4557        if state < self.state {
4558            Err(Error::UnsupportedInstruction(self.state, op))
4559        } else {
4560            self.state = state;
4561            Ok(())
4562        }
4563    }
4564
4565    /// Walk the statement tree and patch it in the following cases:
4566    /// 1. Function call targets are replaced by `deferred_function_calls` map
4567    fn patch_statements(
4568        &mut self,
4569        statements: &mut crate::Block,
4570        expressions: &mut Arena<crate::Expression>,
4571        fun_parameter_sampling: &mut [image::SamplingFlags],
4572    ) -> Result<(), Error> {
4573        use crate::Statement as S;
4574        let mut i = 0usize;
4575        while i < statements.len() {
4576            match statements[i] {
4577                S::Emit(_) => {}
4578                S::Block(ref mut block) => {
4579                    self.patch_statements(block, expressions, fun_parameter_sampling)?;
4580                }
4581                S::If {
4582                    condition: _,
4583                    ref mut accept,
4584                    ref mut reject,
4585                } => {
4586                    self.patch_statements(reject, expressions, fun_parameter_sampling)?;
4587                    self.patch_statements(accept, expressions, fun_parameter_sampling)?;
4588                }
4589                S::Switch {
4590                    selector: _,
4591                    ref mut cases,
4592                } => {
4593                    for case in cases.iter_mut() {
4594                        self.patch_statements(&mut case.body, expressions, fun_parameter_sampling)?;
4595                    }
4596                }
4597                S::Loop {
4598                    ref mut body,
4599                    ref mut continuing,
4600                    break_if: _,
4601                } => {
4602                    self.patch_statements(body, expressions, fun_parameter_sampling)?;
4603                    self.patch_statements(continuing, expressions, fun_parameter_sampling)?;
4604                }
4605                S::Break
4606                | S::Continue
4607                | S::Return { .. }
4608                | S::Kill
4609                | S::ControlBarrier(_)
4610                | S::MemoryBarrier(_)
4611                | S::Store { .. }
4612                | S::ImageStore { .. }
4613                | S::Atomic { .. }
4614                | S::ImageAtomic { .. }
4615                | S::RayQuery { .. }
4616                | S::SubgroupBallot { .. }
4617                | S::SubgroupCollectiveOperation { .. }
4618                | S::SubgroupGather { .. } => {}
4619                S::Call {
4620                    function: ref mut callee,
4621                    ref arguments,
4622                    ..
4623                } => {
4624                    let fun_id = self.deferred_function_calls[callee.index()];
4625                    let fun_lookup = self.lookup_function.lookup(fun_id)?;
4626                    *callee = fun_lookup.handle;
4627
4628                    // Patch sampling flags
4629                    for (arg_index, arg) in arguments.iter().enumerate() {
4630                        let flags = match fun_lookup.parameters_sampling.get(arg_index) {
4631                            Some(&flags) if !flags.is_empty() => flags,
4632                            _ => continue,
4633                        };
4634
4635                        match expressions[*arg] {
4636                            crate::Expression::GlobalVariable(handle) => {
4637                                if let Some(sampling) = self.handle_sampling.get_mut(&handle) {
4638                                    *sampling |= flags
4639                                }
4640                            }
4641                            crate::Expression::FunctionArgument(i) => {
4642                                fun_parameter_sampling[i as usize] |= flags;
4643                            }
4644                            ref other => return Err(Error::InvalidGlobalVar(other.clone())),
4645                        }
4646                    }
4647                }
4648                S::WorkGroupUniformLoad { .. } => unreachable!(),
4649            }
4650            i += 1;
4651        }
4652        Ok(())
4653    }
4654
4655    fn patch_function(
4656        &mut self,
4657        handle: Option<Handle<crate::Function>>,
4658        fun: &mut crate::Function,
4659    ) -> Result<(), Error> {
4660        // Note: this search is a bit unfortunate
4661        let (fun_id, mut parameters_sampling) = match handle {
4662            Some(h) => {
4663                let (&fun_id, lookup) = self
4664                    .lookup_function
4665                    .iter_mut()
4666                    .find(|&(_, ref lookup)| lookup.handle == h)
4667                    .unwrap();
4668                (fun_id, mem::take(&mut lookup.parameters_sampling))
4669            }
4670            None => (0, Vec::new()),
4671        };
4672
4673        for (_, expr) in fun.expressions.iter_mut() {
4674            if let crate::Expression::CallResult(ref mut function) = *expr {
4675                let fun_id = self.deferred_function_calls[function.index()];
4676                *function = self.lookup_function.lookup(fun_id)?.handle;
4677            }
4678        }
4679
4680        self.patch_statements(
4681            &mut fun.body,
4682            &mut fun.expressions,
4683            &mut parameters_sampling,
4684        )?;
4685
4686        if let Some(lookup) = self.lookup_function.get_mut(&fun_id) {
4687            lookup.parameters_sampling = parameters_sampling;
4688        }
4689        Ok(())
4690    }
4691
4692    pub fn parse(mut self) -> Result<crate::Module, Error> {
4693        let mut module = {
4694            if self.next()? != spirv::MAGIC_NUMBER {
4695                return Err(Error::InvalidHeader);
4696            }
4697            let version_raw = self.next()?;
4698            let generator = self.next()?;
4699            let _bound = self.next()?;
4700            let _schema = self.next()?;
4701            log::info!("Generated by {generator} version {version_raw:x}");
4702            crate::Module::default()
4703        };
4704
4705        self.layouter.clear();
4706        self.dummy_functions = Arena::new();
4707        self.lookup_function.clear();
4708        self.function_call_graph.clear();
4709
4710        loop {
4711            use spirv::Op;
4712
4713            let inst = match self.next_inst() {
4714                Ok(inst) => inst,
4715                Err(Error::IncompleteData) => break,
4716                Err(other) => return Err(other),
4717            };
4718            log::debug!("\t{:?} [{}]", inst.op, inst.wc);
4719
4720            match inst.op {
4721                Op::Capability => self.parse_capability(inst),
4722                Op::Extension => self.parse_extension(inst),
4723                Op::ExtInstImport => self.parse_ext_inst_import(inst),
4724                Op::MemoryModel => self.parse_memory_model(inst),
4725                Op::EntryPoint => self.parse_entry_point(inst),
4726                Op::ExecutionMode => self.parse_execution_mode(inst),
4727                Op::String => self.parse_string(inst),
4728                Op::Source => self.parse_source(inst),
4729                Op::SourceExtension => self.parse_source_extension(inst),
4730                Op::Name => self.parse_name(inst),
4731                Op::MemberName => self.parse_member_name(inst),
4732                Op::ModuleProcessed => self.parse_module_processed(inst),
4733                Op::Decorate => self.parse_decorate(inst),
4734                Op::MemberDecorate => self.parse_member_decorate(inst),
4735                Op::TypeVoid => self.parse_type_void(inst),
4736                Op::TypeBool => self.parse_type_bool(inst, &mut module),
4737                Op::TypeInt => self.parse_type_int(inst, &mut module),
4738                Op::TypeFloat => self.parse_type_float(inst, &mut module),
4739                Op::TypeVector => self.parse_type_vector(inst, &mut module),
4740                Op::TypeMatrix => self.parse_type_matrix(inst, &mut module),
4741                Op::TypeFunction => self.parse_type_function(inst),
4742                Op::TypePointer => self.parse_type_pointer(inst, &mut module),
4743                Op::TypeArray => self.parse_type_array(inst, &mut module),
4744                Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module),
4745                Op::TypeStruct => self.parse_type_struct(inst, &mut module),
4746                Op::TypeImage => self.parse_type_image(inst, &mut module),
4747                Op::TypeSampledImage => self.parse_type_sampled_image(inst),
4748                Op::TypeSampler => self.parse_type_sampler(inst, &mut module),
4749                Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module),
4750                Op::ConstantComposite | Op::SpecConstantComposite => {
4751                    self.parse_composite_constant(inst, &mut module)
4752                }
4753                Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module),
4754                Op::ConstantTrue | Op::SpecConstantTrue => {
4755                    self.parse_bool_constant(inst, true, &mut module)
4756                }
4757                Op::ConstantFalse | Op::SpecConstantFalse => {
4758                    self.parse_bool_constant(inst, false, &mut module)
4759                }
4760                Op::Variable => self.parse_global_variable(inst, &mut module),
4761                Op::Function => {
4762                    self.switch(ModuleState::Function, inst.op)?;
4763                    inst.expect(5)?;
4764                    self.parse_function(&mut module)
4765                }
4766                _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), //TODO
4767            }?;
4768        }
4769
4770        if !self.upgrade_atomics.is_empty() {
4771            log::info!("Upgrading atomic pointers...");
4772            module.upgrade_atomics(&self.upgrade_atomics)?;
4773        }
4774
4775        // Do entry point specific processing after all functions are parsed so that we can
4776        // cull unused problematic builtins of gl_PerVertex.
4777        for (ep, fun_id) in mem::take(&mut self.deferred_entry_points) {
4778            self.process_entry_point(&mut module, ep, fun_id)?;
4779        }
4780
4781        log::info!("Patching...");
4782        {
4783            let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
4784                .map_err(|cycle| Error::FunctionCallCycle(cycle.node_id()))?;
4785            nodes.reverse(); // we need dominated first
4786            let mut functions = mem::take(&mut module.functions);
4787            for fun_id in nodes {
4788                if fun_id > !(functions.len() as u32) {
4789                    // skip all the fake IDs registered for the entry points
4790                    continue;
4791                }
4792                let lookup = self.lookup_function.get_mut(&fun_id).unwrap();
4793                // take out the function from the old array
4794                let fun = mem::take(&mut functions[lookup.handle]);
4795                // add it to the newly formed arena, and adjust the lookup
4796                lookup.handle = module
4797                    .functions
4798                    .append(fun, functions.get_span(lookup.handle));
4799            }
4800        }
4801        // patch all the functions
4802        for (handle, fun) in module.functions.iter_mut() {
4803            self.patch_function(Some(handle), fun)?;
4804        }
4805        for ep in module.entry_points.iter_mut() {
4806            self.patch_function(None, &mut ep.function)?;
4807        }
4808
4809        // Check all the images and samplers to have consistent comparison property.
4810        for (handle, flags) in self.handle_sampling.drain() {
4811            if !image::patch_comparison_type(
4812                flags,
4813                module.global_variables.get_mut(handle),
4814                &mut module.types,
4815            ) {
4816                return Err(Error::InconsistentComparisonSampling(handle));
4817            }
4818        }
4819
4820        if !self.future_decor.is_empty() {
4821            log::warn!("Unused item decorations: {:?}", self.future_decor);
4822            self.future_decor.clear();
4823        }
4824        if !self.future_member_decor.is_empty() {
4825            log::warn!("Unused member decorations: {:?}", self.future_member_decor);
4826            self.future_member_decor.clear();
4827        }
4828
4829        Ok(module)
4830    }
4831
4832    fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> {
4833        self.switch(ModuleState::Capability, inst.op)?;
4834        inst.expect(2)?;
4835        let capability = self.next()?;
4836        let cap =
4837            spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?;
4838        if !SUPPORTED_CAPABILITIES.contains(&cap) {
4839            if self.options.strict_capabilities {
4840                return Err(Error::UnsupportedCapability(cap));
4841            } else {
4842                log::warn!("Unknown capability {cap:?}");
4843            }
4844        }
4845        Ok(())
4846    }
4847
4848    fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> {
4849        self.switch(ModuleState::Extension, inst.op)?;
4850        inst.expect_at_least(2)?;
4851        let (name, left) = self.next_string(inst.wc - 1)?;
4852        if left != 0 {
4853            return Err(Error::InvalidOperand);
4854        }
4855        if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) {
4856            return Err(Error::UnsupportedExtension(name));
4857        }
4858        Ok(())
4859    }
4860
4861    fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> {
4862        self.switch(ModuleState::Extension, inst.op)?;
4863        inst.expect_at_least(3)?;
4864        let result_id = self.next()?;
4865        let (name, left) = self.next_string(inst.wc - 2)?;
4866        if left != 0 {
4867            return Err(Error::InvalidOperand);
4868        }
4869        if !SUPPORTED_EXT_SETS.contains(&name.as_str()) {
4870            return Err(Error::UnsupportedExtSet(name));
4871        }
4872        self.ext_glsl_id = Some(result_id);
4873        Ok(())
4874    }
4875
4876    fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> {
4877        self.switch(ModuleState::MemoryModel, inst.op)?;
4878        inst.expect(3)?;
4879        let _addressing_model = self.next()?;
4880        let _memory_model = self.next()?;
4881        Ok(())
4882    }
4883
4884    fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> {
4885        self.switch(ModuleState::EntryPoint, inst.op)?;
4886        inst.expect_at_least(4)?;
4887        let exec_model = self.next()?;
4888        let exec_model = spirv::ExecutionModel::from_u32(exec_model)
4889            .ok_or(Error::UnsupportedExecutionModel(exec_model))?;
4890        let function_id = self.next()?;
4891        let (name, left) = self.next_string(inst.wc - 3)?;
4892        let ep = EntryPoint {
4893            stage: match exec_model {
4894                spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex,
4895                spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment,
4896                spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute,
4897                _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)),
4898            },
4899            name,
4900            early_depth_test: None,
4901            workgroup_size: [0; 3],
4902            variable_ids: self.data.by_ref().take(left as usize).collect(),
4903        };
4904        self.lookup_entry_point.insert(function_id, ep);
4905        Ok(())
4906    }
4907
4908    fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
4909        use spirv::ExecutionMode;
4910
4911        self.switch(ModuleState::ExecutionMode, inst.op)?;
4912        inst.expect_at_least(3)?;
4913
4914        let ep_id = self.next()?;
4915        let mode_id = self.next()?;
4916        let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect();
4917
4918        let ep = self
4919            .lookup_entry_point
4920            .get_mut(&ep_id)
4921            .ok_or(Error::InvalidId(ep_id))?;
4922        let mode =
4923            ExecutionMode::from_u32(mode_id).ok_or(Error::UnsupportedExecutionMode(mode_id))?;
4924
4925        match mode {
4926            ExecutionMode::EarlyFragmentTests => {
4927                ep.early_depth_test = Some(crate::EarlyDepthTest::Force);
4928            }
4929            ExecutionMode::DepthUnchanged => {
4930                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
4931                    if let &mut crate::EarlyDepthTest::Allow {
4932                        ref mut conservative,
4933                    } = early_depth_test
4934                    {
4935                        *conservative = crate::ConservativeDepth::Unchanged;
4936                    }
4937                } else {
4938                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
4939                        conservative: crate::ConservativeDepth::Unchanged,
4940                    });
4941                }
4942            }
4943            ExecutionMode::DepthGreater => {
4944                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
4945                    if let &mut crate::EarlyDepthTest::Allow {
4946                        ref mut conservative,
4947                    } = early_depth_test
4948                    {
4949                        *conservative = crate::ConservativeDepth::GreaterEqual;
4950                    }
4951                } else {
4952                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
4953                        conservative: crate::ConservativeDepth::GreaterEqual,
4954                    });
4955                }
4956            }
4957            ExecutionMode::DepthLess => {
4958                if let &mut Some(ref mut early_depth_test) = &mut ep.early_depth_test {
4959                    if let &mut crate::EarlyDepthTest::Allow {
4960                        ref mut conservative,
4961                    } = early_depth_test
4962                    {
4963                        *conservative = crate::ConservativeDepth::LessEqual;
4964                    }
4965                } else {
4966                    ep.early_depth_test = Some(crate::EarlyDepthTest::Allow {
4967                        conservative: crate::ConservativeDepth::LessEqual,
4968                    });
4969                }
4970            }
4971            ExecutionMode::DepthReplacing => {
4972                // Ignored because it can be deduced from the IR.
4973            }
4974            ExecutionMode::OriginUpperLeft => {
4975                // Ignored because the other option (OriginLowerLeft) is not valid in Vulkan mode.
4976            }
4977            ExecutionMode::LocalSize => {
4978                ep.workgroup_size = [args[0], args[1], args[2]];
4979            }
4980            _ => {
4981                return Err(Error::UnsupportedExecutionMode(mode_id));
4982            }
4983        }
4984
4985        Ok(())
4986    }
4987
4988    fn parse_string(&mut self, inst: Instruction) -> Result<(), Error> {
4989        self.switch(ModuleState::Source, inst.op)?;
4990        inst.expect_at_least(3)?;
4991        let _id = self.next()?;
4992        let (_name, _) = self.next_string(inst.wc - 2)?;
4993        Ok(())
4994    }
4995
4996    fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> {
4997        self.switch(ModuleState::Source, inst.op)?;
4998        for _ in 1..inst.wc {
4999            let _ = self.next()?;
5000        }
5001        Ok(())
5002    }
5003
5004    fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> {
5005        self.switch(ModuleState::Source, inst.op)?;
5006        inst.expect_at_least(2)?;
5007        let (_name, _) = self.next_string(inst.wc - 1)?;
5008        Ok(())
5009    }
5010
5011    fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> {
5012        self.switch(ModuleState::Name, inst.op)?;
5013        inst.expect_at_least(3)?;
5014        let id = self.next()?;
5015        let (name, left) = self.next_string(inst.wc - 2)?;
5016        if left != 0 {
5017            return Err(Error::InvalidOperand);
5018        }
5019        self.future_decor.entry(id).or_default().name = Some(name);
5020        Ok(())
5021    }
5022
5023    fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> {
5024        self.switch(ModuleState::Name, inst.op)?;
5025        inst.expect_at_least(4)?;
5026        let id = self.next()?;
5027        let member = self.next()?;
5028        let (name, left) = self.next_string(inst.wc - 3)?;
5029        if left != 0 {
5030            return Err(Error::InvalidOperand);
5031        }
5032
5033        self.future_member_decor
5034            .entry((id, member))
5035            .or_default()
5036            .name = Some(name);
5037        Ok(())
5038    }
5039
5040    fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error> {
5041        self.switch(ModuleState::Name, inst.op)?;
5042        inst.expect_at_least(2)?;
5043        let (_info, left) = self.next_string(inst.wc - 1)?;
5044        //Note: string is ignored
5045        if left != 0 {
5046            return Err(Error::InvalidOperand);
5047        }
5048        Ok(())
5049    }
5050
5051    fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
5052        self.switch(ModuleState::Annotation, inst.op)?;
5053        inst.expect_at_least(3)?;
5054        let id = self.next()?;
5055        let mut dec = self.future_decor.remove(&id).unwrap_or_default();
5056        self.next_decoration(inst, 2, &mut dec)?;
5057        self.future_decor.insert(id, dec);
5058        Ok(())
5059    }
5060
5061    fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> {
5062        self.switch(ModuleState::Annotation, inst.op)?;
5063        inst.expect_at_least(4)?;
5064        let id = self.next()?;
5065        let member = self.next()?;
5066
5067        let mut dec = self
5068            .future_member_decor
5069            .remove(&(id, member))
5070            .unwrap_or_default();
5071        self.next_decoration(inst, 3, &mut dec)?;
5072        self.future_member_decor.insert((id, member), dec);
5073        Ok(())
5074    }
5075
5076    fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> {
5077        self.switch(ModuleState::Type, inst.op)?;
5078        inst.expect(2)?;
5079        let id = self.next()?;
5080        self.lookup_void_type = Some(id);
5081        Ok(())
5082    }
5083
5084    fn parse_type_bool(
5085        &mut self,
5086        inst: Instruction,
5087        module: &mut crate::Module,
5088    ) -> Result<(), Error> {
5089        let start = self.data_offset;
5090        self.switch(ModuleState::Type, inst.op)?;
5091        inst.expect(2)?;
5092        let id = self.next()?;
5093        let inner = crate::TypeInner::Scalar(crate::Scalar::BOOL);
5094        self.lookup_type.insert(
5095            id,
5096            LookupType {
5097                handle: module.types.insert(
5098                    crate::Type {
5099                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5100                        inner,
5101                    },
5102                    self.span_from_with_op(start),
5103                ),
5104                base_id: None,
5105            },
5106        );
5107        Ok(())
5108    }
5109
5110    fn parse_type_int(
5111        &mut self,
5112        inst: Instruction,
5113        module: &mut crate::Module,
5114    ) -> Result<(), Error> {
5115        let start = self.data_offset;
5116        self.switch(ModuleState::Type, inst.op)?;
5117        inst.expect(4)?;
5118        let id = self.next()?;
5119        let width = self.next()?;
5120        let sign = self.next()?;
5121        let inner = crate::TypeInner::Scalar(crate::Scalar {
5122            kind: match sign {
5123                0 => crate::ScalarKind::Uint,
5124                1 => crate::ScalarKind::Sint,
5125                _ => return Err(Error::InvalidSign(sign)),
5126            },
5127            width: map_width(width)?,
5128        });
5129        self.lookup_type.insert(
5130            id,
5131            LookupType {
5132                handle: module.types.insert(
5133                    crate::Type {
5134                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5135                        inner,
5136                    },
5137                    self.span_from_with_op(start),
5138                ),
5139                base_id: None,
5140            },
5141        );
5142        Ok(())
5143    }
5144
5145    fn parse_type_float(
5146        &mut self,
5147        inst: Instruction,
5148        module: &mut crate::Module,
5149    ) -> Result<(), Error> {
5150        let start = self.data_offset;
5151        self.switch(ModuleState::Type, inst.op)?;
5152        inst.expect(3)?;
5153        let id = self.next()?;
5154        let width = self.next()?;
5155        let inner = crate::TypeInner::Scalar(crate::Scalar::float(map_width(width)?));
5156        self.lookup_type.insert(
5157            id,
5158            LookupType {
5159                handle: module.types.insert(
5160                    crate::Type {
5161                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5162                        inner,
5163                    },
5164                    self.span_from_with_op(start),
5165                ),
5166                base_id: None,
5167            },
5168        );
5169        Ok(())
5170    }
5171
5172    fn parse_type_vector(
5173        &mut self,
5174        inst: Instruction,
5175        module: &mut crate::Module,
5176    ) -> Result<(), Error> {
5177        let start = self.data_offset;
5178        self.switch(ModuleState::Type, inst.op)?;
5179        inst.expect(4)?;
5180        let id = self.next()?;
5181        let type_id = self.next()?;
5182        let type_lookup = self.lookup_type.lookup(type_id)?;
5183        let scalar = match module.types[type_lookup.handle].inner {
5184            crate::TypeInner::Scalar(scalar) => scalar,
5185            _ => return Err(Error::InvalidInnerType(type_id)),
5186        };
5187        let component_count = self.next()?;
5188        let inner = crate::TypeInner::Vector {
5189            size: map_vector_size(component_count)?,
5190            scalar,
5191        };
5192        self.lookup_type.insert(
5193            id,
5194            LookupType {
5195                handle: module.types.insert(
5196                    crate::Type {
5197                        name: self.future_decor.remove(&id).and_then(|dec| dec.name),
5198                        inner,
5199                    },
5200                    self.span_from_with_op(start),
5201                ),
5202                base_id: Some(type_id),
5203            },
5204        );
5205        Ok(())
5206    }
5207
5208    fn parse_type_matrix(
5209        &mut self,
5210        inst: Instruction,
5211        module: &mut crate::Module,
5212    ) -> Result<(), Error> {
5213        let start = self.data_offset;
5214        self.switch(ModuleState::Type, inst.op)?;
5215        inst.expect(4)?;
5216        let id = self.next()?;
5217        let vector_type_id = self.next()?;
5218        let num_columns = self.next()?;
5219        let decor = self.future_decor.remove(&id);
5220
5221        let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?;
5222        let inner = match module.types[vector_type_lookup.handle].inner {
5223            crate::TypeInner::Vector { size, scalar } => crate::TypeInner::Matrix {
5224                columns: map_vector_size(num_columns)?,
5225                rows: size,
5226                scalar,
5227            },
5228            _ => return Err(Error::InvalidInnerType(vector_type_id)),
5229        };
5230
5231        self.lookup_type.insert(
5232            id,
5233            LookupType {
5234                handle: module.types.insert(
5235                    crate::Type {
5236                        name: decor.and_then(|dec| dec.name),
5237                        inner,
5238                    },
5239                    self.span_from_with_op(start),
5240                ),
5241                base_id: Some(vector_type_id),
5242            },
5243        );
5244        Ok(())
5245    }
5246
5247    fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> {
5248        self.switch(ModuleState::Type, inst.op)?;
5249        inst.expect_at_least(3)?;
5250        let id = self.next()?;
5251        let return_type_id = self.next()?;
5252        let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect();
5253        self.lookup_function_type.insert(
5254            id,
5255            LookupFunctionType {
5256                parameter_type_ids,
5257                return_type_id,
5258            },
5259        );
5260        Ok(())
5261    }
5262
5263    fn parse_type_pointer(
5264        &mut self,
5265        inst: Instruction,
5266        module: &mut crate::Module,
5267    ) -> Result<(), Error> {
5268        let start = self.data_offset;
5269        self.switch(ModuleState::Type, inst.op)?;
5270        inst.expect(4)?;
5271        let id = self.next()?;
5272        let storage_class = self.next()?;
5273        let type_id = self.next()?;
5274
5275        let decor = self.future_decor.remove(&id);
5276        let base_lookup_ty = self.lookup_type.lookup(type_id)?;
5277        let base_inner = &module.types[base_lookup_ty.handle].inner;
5278
5279        let space = if let Some(space) = base_inner.pointer_space() {
5280            space
5281        } else if self
5282            .lookup_storage_buffer_types
5283            .contains_key(&base_lookup_ty.handle)
5284        {
5285            crate::AddressSpace::Storage {
5286                access: crate::StorageAccess::default(),
5287            }
5288        } else {
5289            match map_storage_class(storage_class)? {
5290                ExtendedClass::Global(space) => space,
5291                ExtendedClass::Input | ExtendedClass::Output => crate::AddressSpace::Private,
5292            }
5293        };
5294
5295        // We don't support pointers to runtime-sized arrays in the `Uniform`
5296        // storage class with the `BufferBlock` decoration. Runtime-sized arrays
5297        // should be in the StorageBuffer class.
5298        if let crate::TypeInner::Array {
5299            size: crate::ArraySize::Dynamic,
5300            ..
5301        } = *base_inner
5302        {
5303            match space {
5304                crate::AddressSpace::Storage { .. } => {}
5305                _ => {
5306                    return Err(Error::UnsupportedRuntimeArrayStorageClass);
5307                }
5308            }
5309        }
5310
5311        // Don't bother with pointer stuff for `Handle` types.
5312        let lookup_ty = if space == crate::AddressSpace::Handle {
5313            base_lookup_ty.clone()
5314        } else {
5315            LookupType {
5316                handle: module.types.insert(
5317                    crate::Type {
5318                        name: decor.and_then(|dec| dec.name),
5319                        inner: crate::TypeInner::Pointer {
5320                            base: base_lookup_ty.handle,
5321                            space,
5322                        },
5323                    },
5324                    self.span_from_with_op(start),
5325                ),
5326                base_id: Some(type_id),
5327            }
5328        };
5329        self.lookup_type.insert(id, lookup_ty);
5330        Ok(())
5331    }
5332
5333    fn parse_type_array(
5334        &mut self,
5335        inst: Instruction,
5336        module: &mut crate::Module,
5337    ) -> Result<(), Error> {
5338        let start = self.data_offset;
5339        self.switch(ModuleState::Type, inst.op)?;
5340        inst.expect(4)?;
5341        let id = self.next()?;
5342        let type_id = self.next()?;
5343        let length_id = self.next()?;
5344        let length_const = self.lookup_constant.lookup(length_id)?;
5345
5346        let size = resolve_constant(module.to_ctx(), &length_const.inner)
5347            .and_then(NonZeroU32::new)
5348            .ok_or(Error::InvalidArraySize(length_id))?;
5349
5350        let decor = self.future_decor.remove(&id).unwrap_or_default();
5351        let base = self.lookup_type.lookup(type_id)?.handle;
5352
5353        self.layouter.update(module.to_ctx()).unwrap();
5354
5355        // HACK if the underlying type is an image or a sampler, let's assume
5356        //      that we're dealing with a binding-array
5357        //
5358        // Note that it's not a strictly correct assumption, but rather a trade
5359        // off caused by an impedance mismatch between SPIR-V's and Naga's type
5360        // systems - Naga distinguishes between arrays and binding-arrays via
5361        // types (i.e. both kinds of arrays are just different types), while
5362        // SPIR-V distinguishes between them through usage - e.g. given:
5363        //
5364        // ```
5365        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
5366        // %uint_256 = OpConstant %uint 256
5367        // %image_array = OpTypeArray %image %uint_256
5368        // ```
5369        //
5370        // ```
5371        // %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
5372        // %uint_256 = OpConstant %uint 256
5373        // %image_array = OpTypeArray %image %uint_256
5374        // %image_array_ptr = OpTypePointer UniformConstant %image_array
5375        // ```
5376        //
5377        // ... in the first case, `%image_array` should technically correspond
5378        // to `TypeInner::Array`, while in the second case it should say
5379        // `TypeInner::BindingArray` (kinda, depending on whether `%image_array`
5380        // is ever used as a freestanding type or rather always through the
5381        // pointer-indirection).
5382        //
5383        // Anyway, at the moment we don't support other kinds of image / sampler
5384        // arrays than those binding-based, so this assumption is pretty safe
5385        // for now.
5386        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
5387            module.types[base].inner
5388        {
5389            crate::TypeInner::BindingArray {
5390                base,
5391                size: crate::ArraySize::Constant(size),
5392            }
5393        } else {
5394            crate::TypeInner::Array {
5395                base,
5396                size: crate::ArraySize::Constant(size),
5397                stride: match decor.array_stride {
5398                    Some(stride) => stride.get(),
5399                    None => self.layouter[base].to_stride(),
5400                },
5401            }
5402        };
5403
5404        self.lookup_type.insert(
5405            id,
5406            LookupType {
5407                handle: module.types.insert(
5408                    crate::Type {
5409                        name: decor.name,
5410                        inner,
5411                    },
5412                    self.span_from_with_op(start),
5413                ),
5414                base_id: Some(type_id),
5415            },
5416        );
5417        Ok(())
5418    }
5419
5420    fn parse_type_runtime_array(
5421        &mut self,
5422        inst: Instruction,
5423        module: &mut crate::Module,
5424    ) -> Result<(), Error> {
5425        let start = self.data_offset;
5426        self.switch(ModuleState::Type, inst.op)?;
5427        inst.expect(3)?;
5428        let id = self.next()?;
5429        let type_id = self.next()?;
5430
5431        let decor = self.future_decor.remove(&id).unwrap_or_default();
5432        let base = self.lookup_type.lookup(type_id)?.handle;
5433
5434        self.layouter.update(module.to_ctx()).unwrap();
5435
5436        // HACK same case as in `parse_type_array()`
5437        let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
5438            module.types[base].inner
5439        {
5440            crate::TypeInner::BindingArray {
5441                base: self.lookup_type.lookup(type_id)?.handle,
5442                size: crate::ArraySize::Dynamic,
5443            }
5444        } else {
5445            crate::TypeInner::Array {
5446                base: self.lookup_type.lookup(type_id)?.handle,
5447                size: crate::ArraySize::Dynamic,
5448                stride: match decor.array_stride {
5449                    Some(stride) => stride.get(),
5450                    None => self.layouter[base].to_stride(),
5451                },
5452            }
5453        };
5454
5455        self.lookup_type.insert(
5456            id,
5457            LookupType {
5458                handle: module.types.insert(
5459                    crate::Type {
5460                        name: decor.name,
5461                        inner,
5462                    },
5463                    self.span_from_with_op(start),
5464                ),
5465                base_id: Some(type_id),
5466            },
5467        );
5468        Ok(())
5469    }
5470
5471    fn parse_type_struct(
5472        &mut self,
5473        inst: Instruction,
5474        module: &mut crate::Module,
5475    ) -> Result<(), Error> {
5476        let start = self.data_offset;
5477        self.switch(ModuleState::Type, inst.op)?;
5478        inst.expect_at_least(2)?;
5479        let id = self.next()?;
5480        let parent_decor = self.future_decor.remove(&id);
5481        let is_storage_buffer = parent_decor
5482            .as_ref()
5483            .is_some_and(|decor| decor.storage_buffer);
5484
5485        self.layouter.update(module.to_ctx()).unwrap();
5486
5487        let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
5488        let mut member_lookups = Vec::with_capacity(members.capacity());
5489        let mut storage_access = crate::StorageAccess::empty();
5490        let mut span = 0;
5491        let mut alignment = Alignment::ONE;
5492        for i in 0..u32::from(inst.wc) - 2 {
5493            let type_id = self.next()?;
5494            let ty = self.lookup_type.lookup(type_id)?.handle;
5495            let decor = self
5496                .future_member_decor
5497                .remove(&(id, i))
5498                .unwrap_or_default();
5499
5500            storage_access |= decor.flags.to_storage_access();
5501
5502            member_lookups.push(LookupMember {
5503                type_id,
5504                row_major: decor.matrix_major == Some(Majority::Row),
5505            });
5506
5507            let member_alignment = self.layouter[ty].alignment;
5508            span = member_alignment.round_up(span);
5509            alignment = member_alignment.max(alignment);
5510
5511            let binding = decor.io_binding().ok();
5512            if let Some(offset) = decor.offset {
5513                span = offset;
5514            }
5515            let offset = span;
5516
5517            span += self.layouter[ty].size;
5518
5519            let inner = &module.types[ty].inner;
5520            if let crate::TypeInner::Matrix {
5521                columns,
5522                rows,
5523                scalar,
5524            } = *inner
5525            {
5526                if let Some(stride) = decor.matrix_stride {
5527                    let expected_stride = Alignment::from(rows) * scalar.width as u32;
5528                    if stride.get() != expected_stride {
5529                        return Err(Error::UnsupportedMatrixStride {
5530                            stride: stride.get(),
5531                            columns: columns as u8,
5532                            rows: rows as u8,
5533                            width: scalar.width,
5534                        });
5535                    }
5536                }
5537            }
5538
5539            members.push(crate::StructMember {
5540                name: decor.name,
5541                ty,
5542                binding,
5543                offset,
5544            });
5545        }
5546
5547        span = alignment.round_up(span);
5548
5549        let inner = crate::TypeInner::Struct { span, members };
5550
5551        let ty_handle = module.types.insert(
5552            crate::Type {
5553                name: parent_decor.and_then(|dec| dec.name),
5554                inner,
5555            },
5556            self.span_from_with_op(start),
5557        );
5558
5559        if is_storage_buffer {
5560            self.lookup_storage_buffer_types
5561                .insert(ty_handle, storage_access);
5562        }
5563        for (i, member_lookup) in member_lookups.into_iter().enumerate() {
5564            self.lookup_member
5565                .insert((ty_handle, i as u32), member_lookup);
5566        }
5567        self.lookup_type.insert(
5568            id,
5569            LookupType {
5570                handle: ty_handle,
5571                base_id: None,
5572            },
5573        );
5574        Ok(())
5575    }
5576
5577    fn parse_type_image(
5578        &mut self,
5579        inst: Instruction,
5580        module: &mut crate::Module,
5581    ) -> Result<(), Error> {
5582        let start = self.data_offset;
5583        self.switch(ModuleState::Type, inst.op)?;
5584        inst.expect(9)?;
5585
5586        let id = self.next()?;
5587        let sample_type_id = self.next()?;
5588        let dim = self.next()?;
5589        let is_depth = self.next()?;
5590        let is_array = self.next()? != 0;
5591        let is_msaa = self.next()? != 0;
5592        let _is_sampled = self.next()?;
5593        let format = self.next()?;
5594
5595        let dim = map_image_dim(dim)?;
5596        let decor = self.future_decor.remove(&id).unwrap_or_default();
5597
5598        // ensure there is a type for texture coordinate without extra components
5599        module.types.insert(
5600            crate::Type {
5601                name: None,
5602                inner: {
5603                    let scalar = crate::Scalar::F32;
5604                    match dim.required_coordinate_size() {
5605                        None => crate::TypeInner::Scalar(scalar),
5606                        Some(size) => crate::TypeInner::Vector { size, scalar },
5607                    }
5608                },
5609            },
5610            Default::default(),
5611        );
5612
5613        let base_handle = self.lookup_type.lookup(sample_type_id)?.handle;
5614        let kind = module.types[base_handle]
5615            .inner
5616            .scalar_kind()
5617            .ok_or(Error::InvalidImageBaseType(base_handle))?;
5618
5619        let inner = crate::TypeInner::Image {
5620            class: if is_depth == 1 {
5621                crate::ImageClass::Depth { multi: is_msaa }
5622            } else if format != 0 {
5623                crate::ImageClass::Storage {
5624                    format: map_image_format(format)?,
5625                    access: crate::StorageAccess::default(),
5626                }
5627            } else {
5628                crate::ImageClass::Sampled {
5629                    kind,
5630                    multi: is_msaa,
5631                }
5632            },
5633            dim,
5634            arrayed: is_array,
5635        };
5636
5637        let handle = module.types.insert(
5638            crate::Type {
5639                name: decor.name,
5640                inner,
5641            },
5642            self.span_from_with_op(start),
5643        );
5644
5645        self.lookup_type.insert(
5646            id,
5647            LookupType {
5648                handle,
5649                base_id: Some(sample_type_id),
5650            },
5651        );
5652        Ok(())
5653    }
5654
5655    fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> {
5656        self.switch(ModuleState::Type, inst.op)?;
5657        inst.expect(3)?;
5658        let id = self.next()?;
5659        let image_id = self.next()?;
5660        self.lookup_type.insert(
5661            id,
5662            LookupType {
5663                handle: self.lookup_type.lookup(image_id)?.handle,
5664                base_id: Some(image_id),
5665            },
5666        );
5667        Ok(())
5668    }
5669
5670    fn parse_type_sampler(
5671        &mut self,
5672        inst: Instruction,
5673        module: &mut crate::Module,
5674    ) -> Result<(), Error> {
5675        let start = self.data_offset;
5676        self.switch(ModuleState::Type, inst.op)?;
5677        inst.expect(2)?;
5678        let id = self.next()?;
5679        let decor = self.future_decor.remove(&id).unwrap_or_default();
5680        let handle = module.types.insert(
5681            crate::Type {
5682                name: decor.name,
5683                inner: crate::TypeInner::Sampler { comparison: false },
5684            },
5685            self.span_from_with_op(start),
5686        );
5687        self.lookup_type.insert(
5688            id,
5689            LookupType {
5690                handle,
5691                base_id: None,
5692            },
5693        );
5694        Ok(())
5695    }
5696
5697    fn parse_constant(
5698        &mut self,
5699        inst: Instruction,
5700        module: &mut crate::Module,
5701    ) -> Result<(), Error> {
5702        let start = self.data_offset;
5703        self.switch(ModuleState::Type, inst.op)?;
5704        inst.expect_at_least(4)?;
5705        let type_id = self.next()?;
5706        let id = self.next()?;
5707        let type_lookup = self.lookup_type.lookup(type_id)?;
5708        let ty = type_lookup.handle;
5709
5710        let literal = match module.types[ty].inner {
5711            crate::TypeInner::Scalar(crate::Scalar {
5712                kind: crate::ScalarKind::Uint,
5713                width,
5714            }) => {
5715                let low = self.next()?;
5716                match width {
5717                    4 => crate::Literal::U32(low),
5718                    8 => {
5719                        inst.expect(5)?;
5720                        let high = self.next()?;
5721                        crate::Literal::U64((u64::from(high) << 32) | u64::from(low))
5722                    }
5723                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
5724                }
5725            }
5726            crate::TypeInner::Scalar(crate::Scalar {
5727                kind: crate::ScalarKind::Sint,
5728                width,
5729            }) => {
5730                let low = self.next()?;
5731                match width {
5732                    4 => crate::Literal::I32(low as i32),
5733                    8 => {
5734                        inst.expect(5)?;
5735                        let high = self.next()?;
5736                        crate::Literal::I64(((u64::from(high) << 32) | u64::from(low)) as i64)
5737                    }
5738                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
5739                }
5740            }
5741            crate::TypeInner::Scalar(crate::Scalar {
5742                kind: crate::ScalarKind::Float,
5743                width,
5744            }) => {
5745                let low = self.next()?;
5746                match width {
5747                    // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Literal
5748                    // If a numeric type’s bit width is less than 32-bits, the value appears in the low-order bits of the word.
5749                    2 => crate::Literal::F16(f16::from_bits(low as u16)),
5750                    4 => crate::Literal::F32(f32::from_bits(low)),
5751                    8 => {
5752                        inst.expect(5)?;
5753                        let high = self.next()?;
5754                        crate::Literal::F64(f64::from_bits(
5755                            (u64::from(high) << 32) | u64::from(low),
5756                        ))
5757                    }
5758                    _ => return Err(Error::InvalidTypeWidth(width as u32)),
5759                }
5760            }
5761            _ => return Err(Error::UnsupportedType(type_lookup.handle)),
5762        };
5763
5764        let span = self.span_from_with_op(start);
5765
5766        let init = module
5767            .global_expressions
5768            .append(crate::Expression::Literal(literal), span);
5769
5770        self.insert_parsed_constant(module, id, type_id, ty, init, span)
5771    }
5772
5773    fn parse_composite_constant(
5774        &mut self,
5775        inst: Instruction,
5776        module: &mut crate::Module,
5777    ) -> Result<(), Error> {
5778        let start = self.data_offset;
5779        self.switch(ModuleState::Type, inst.op)?;
5780        inst.expect_at_least(3)?;
5781        let type_id = self.next()?;
5782        let id = self.next()?;
5783
5784        let type_lookup = self.lookup_type.lookup(type_id)?;
5785        let ty = type_lookup.handle;
5786
5787        let mut components = Vec::with_capacity(inst.wc as usize - 3);
5788        for _ in 0..components.capacity() {
5789            let start = self.data_offset;
5790            let component_id = self.next()?;
5791            let span = self.span_from_with_op(start);
5792            let constant = self.lookup_constant.lookup(component_id)?;
5793            let expr = module
5794                .global_expressions
5795                .append(constant.inner.to_expr(), span);
5796            components.push(expr);
5797        }
5798
5799        let span = self.span_from_with_op(start);
5800
5801        let init = module
5802            .global_expressions
5803            .append(crate::Expression::Compose { ty, components }, span);
5804
5805        self.insert_parsed_constant(module, id, type_id, ty, init, span)
5806    }
5807
5808    fn parse_null_constant(
5809        &mut self,
5810        inst: Instruction,
5811        module: &mut crate::Module,
5812    ) -> Result<(), Error> {
5813        let start = self.data_offset;
5814        self.switch(ModuleState::Type, inst.op)?;
5815        inst.expect(3)?;
5816        let type_id = self.next()?;
5817        let id = self.next()?;
5818        let span = self.span_from_with_op(start);
5819
5820        let type_lookup = self.lookup_type.lookup(type_id)?;
5821        let ty = type_lookup.handle;
5822
5823        let init = module
5824            .global_expressions
5825            .append(crate::Expression::ZeroValue(ty), span);
5826
5827        self.insert_parsed_constant(module, id, type_id, ty, init, span)
5828    }
5829
5830    fn parse_bool_constant(
5831        &mut self,
5832        inst: Instruction,
5833        value: bool,
5834        module: &mut crate::Module,
5835    ) -> Result<(), Error> {
5836        let start = self.data_offset;
5837        self.switch(ModuleState::Type, inst.op)?;
5838        inst.expect(3)?;
5839        let type_id = self.next()?;
5840        let id = self.next()?;
5841        let span = self.span_from_with_op(start);
5842
5843        let type_lookup = self.lookup_type.lookup(type_id)?;
5844        let ty = type_lookup.handle;
5845
5846        let init = module.global_expressions.append(
5847            crate::Expression::Literal(crate::Literal::Bool(value)),
5848            span,
5849        );
5850
5851        self.insert_parsed_constant(module, id, type_id, ty, init, span)
5852    }
5853
5854    fn insert_parsed_constant(
5855        &mut self,
5856        module: &mut crate::Module,
5857        id: u32,
5858        type_id: u32,
5859        ty: Handle<crate::Type>,
5860        init: Handle<crate::Expression>,
5861        span: crate::Span,
5862    ) -> Result<(), Error> {
5863        let decor = self.future_decor.remove(&id).unwrap_or_default();
5864
5865        let inner = if let Some(id) = decor.specialization_constant_id {
5866            let o = crate::Override {
5867                name: decor.name,
5868                id: Some(id.try_into().map_err(|_| Error::SpecIdTooHigh(id))?),
5869                ty,
5870                init: Some(init),
5871            };
5872            Constant::Override(module.overrides.append(o, span))
5873        } else {
5874            let c = crate::Constant {
5875                name: decor.name,
5876                ty,
5877                init,
5878            };
5879            Constant::Constant(module.constants.append(c, span))
5880        };
5881
5882        self.lookup_constant
5883            .insert(id, LookupConstant { inner, type_id });
5884        Ok(())
5885    }
5886
5887    fn parse_global_variable(
5888        &mut self,
5889        inst: Instruction,
5890        module: &mut crate::Module,
5891    ) -> Result<(), Error> {
5892        let start = self.data_offset;
5893        self.switch(ModuleState::Type, inst.op)?;
5894        inst.expect_at_least(4)?;
5895        let type_id = self.next()?;
5896        let id = self.next()?;
5897        let storage_class = self.next()?;
5898        let init = if inst.wc > 4 {
5899            inst.expect(5)?;
5900            let start = self.data_offset;
5901            let init_id = self.next()?;
5902            let span = self.span_from_with_op(start);
5903            let lconst = self.lookup_constant.lookup(init_id)?;
5904            let expr = module
5905                .global_expressions
5906                .append(lconst.inner.to_expr(), span);
5907            Some(expr)
5908        } else {
5909            None
5910        };
5911        let span = self.span_from_with_op(start);
5912        let dec = self.future_decor.remove(&id).unwrap_or_default();
5913
5914        let original_ty = self.lookup_type.lookup(type_id)?.handle;
5915        let mut ty = original_ty;
5916
5917        if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
5918            ty = base;
5919        }
5920
5921        if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
5922            // Inside `parse_type_array()` we guess that an array of images or
5923            // samplers must be a binding array, and here we validate that guess
5924            if dec.desc_set.is_none() || dec.desc_index.is_none() {
5925                return Err(Error::NonBindingArrayOfImageOrSamplers);
5926            }
5927        }
5928
5929        if let crate::TypeInner::Image {
5930            dim,
5931            arrayed,
5932            class: crate::ImageClass::Storage { format, access: _ },
5933        } = module.types[ty].inner
5934        {
5935            // Storage image types in IR have to contain the access, but not in the SPIR-V.
5936            // The same image type in SPIR-V can be used (and has to be used) for multiple images.
5937            // So we copy the type out and apply the variable access decorations.
5938            let access = dec.flags.to_storage_access();
5939
5940            ty = module.types.insert(
5941                crate::Type {
5942                    name: None,
5943                    inner: crate::TypeInner::Image {
5944                        dim,
5945                        arrayed,
5946                        class: crate::ImageClass::Storage { format, access },
5947                    },
5948                },
5949                Default::default(),
5950            );
5951        }
5952
5953        let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
5954            Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
5955            None => map_storage_class(storage_class)?,
5956        };
5957
5958        let (inner, var) = match ext_class {
5959            ExtendedClass::Global(mut space) => {
5960                if let crate::AddressSpace::Storage { ref mut access } = space {
5961                    *access &= dec.flags.to_storage_access();
5962                }
5963                let var = crate::GlobalVariable {
5964                    binding: dec.resource_binding(),
5965                    name: dec.name,
5966                    space,
5967                    ty,
5968                    init,
5969                };
5970                (Variable::Global, var)
5971            }
5972            ExtendedClass::Input => {
5973                let binding = dec.io_binding()?;
5974                let mut unsigned_ty = ty;
5975                if let crate::Binding::BuiltIn(built_in) = binding {
5976                    let needs_inner_uint = match built_in {
5977                        crate::BuiltIn::BaseInstance
5978                        | crate::BuiltIn::BaseVertex
5979                        | crate::BuiltIn::InstanceIndex
5980                        | crate::BuiltIn::SampleIndex
5981                        | crate::BuiltIn::VertexIndex
5982                        | crate::BuiltIn::PrimitiveIndex
5983                        | crate::BuiltIn::LocalInvocationIndex => {
5984                            Some(crate::TypeInner::Scalar(crate::Scalar::U32))
5985                        }
5986                        crate::BuiltIn::GlobalInvocationId
5987                        | crate::BuiltIn::LocalInvocationId
5988                        | crate::BuiltIn::WorkGroupId
5989                        | crate::BuiltIn::WorkGroupSize => Some(crate::TypeInner::Vector {
5990                            size: crate::VectorSize::Tri,
5991                            scalar: crate::Scalar::U32,
5992                        }),
5993                        _ => None,
5994                    };
5995                    if let (Some(inner), Some(crate::ScalarKind::Sint)) =
5996                        (needs_inner_uint, module.types[ty].inner.scalar_kind())
5997                    {
5998                        unsigned_ty = module
5999                            .types
6000                            .insert(crate::Type { name: None, inner }, Default::default());
6001                    }
6002                }
6003
6004                let var = crate::GlobalVariable {
6005                    name: dec.name.clone(),
6006                    space: crate::AddressSpace::Private,
6007                    binding: None,
6008                    ty,
6009                    init: None,
6010                };
6011
6012                let inner = Variable::Input(crate::FunctionArgument {
6013                    name: dec.name,
6014                    ty: unsigned_ty,
6015                    binding: Some(binding),
6016                });
6017                (inner, var)
6018            }
6019            ExtendedClass::Output => {
6020                // For output interface blocks, this would be a structure.
6021                let binding = dec.io_binding().ok();
6022                let init = match binding {
6023                    Some(crate::Binding::BuiltIn(built_in)) => {
6024                        match null::generate_default_built_in(
6025                            Some(built_in),
6026                            ty,
6027                            &mut module.global_expressions,
6028                            span,
6029                        ) {
6030                            Ok(handle) => Some(handle),
6031                            Err(e) => {
6032                                log::warn!("Failed to initialize output built-in: {e}");
6033                                None
6034                            }
6035                        }
6036                    }
6037                    Some(crate::Binding::Location { .. }) => None,
6038                    None => match module.types[ty].inner {
6039                        crate::TypeInner::Struct { ref members, .. } => {
6040                            let mut components = Vec::with_capacity(members.len());
6041                            for member in members.iter() {
6042                                let built_in = match member.binding {
6043                                    Some(crate::Binding::BuiltIn(built_in)) => Some(built_in),
6044                                    _ => None,
6045                                };
6046                                let handle = null::generate_default_built_in(
6047                                    built_in,
6048                                    member.ty,
6049                                    &mut module.global_expressions,
6050                                    span,
6051                                )?;
6052                                components.push(handle);
6053                            }
6054                            Some(
6055                                module
6056                                    .global_expressions
6057                                    .append(crate::Expression::Compose { ty, components }, span),
6058                            )
6059                        }
6060                        _ => None,
6061                    },
6062                };
6063
6064                let var = crate::GlobalVariable {
6065                    name: dec.name,
6066                    space: crate::AddressSpace::Private,
6067                    binding: None,
6068                    ty,
6069                    init,
6070                };
6071                let inner = Variable::Output(crate::FunctionResult { ty, binding });
6072                (inner, var)
6073            }
6074        };
6075
6076        let handle = module.global_variables.append(var, span);
6077
6078        if module.types[ty].inner.can_comparison_sample(module) {
6079            log::debug!("\t\ttracking {handle:?} for sampling properties");
6080
6081            self.handle_sampling
6082                .insert(handle, image::SamplingFlags::empty());
6083        }
6084
6085        self.lookup_variable.insert(
6086            id,
6087            LookupVariable {
6088                inner,
6089                handle,
6090                type_id,
6091            },
6092        );
6093        Ok(())
6094    }
6095
6096    /// Record an atomic access to some component of a global variable.
6097    ///
6098    /// Given `handle`, an expression referring to a scalar that has had an
6099    /// atomic operation applied to it, descend into the expression, noting
6100    /// which global variable it ultimately refers to, and which struct fields
6101    /// of that global's value it accesses.
6102    ///
6103    /// Return the handle of the type of the expression.
6104    ///
6105    /// If the expression doesn't actually refer to something in a global
6106    /// variable, we can't upgrade its type in a way that Naga validation would
6107    /// pass, so reject the input instead.
6108    fn record_atomic_access(
6109        &mut self,
6110        ctx: &BlockContext,
6111        handle: Handle<crate::Expression>,
6112    ) -> Result<Handle<crate::Type>, Error> {
6113        log::debug!("\t\tlocating global variable in {handle:?}");
6114        match ctx.expressions[handle] {
6115            crate::Expression::Access { base, index } => {
6116                log::debug!("\t\t  access {handle:?} {index:?}");
6117                let ty = self.record_atomic_access(ctx, base)?;
6118                let crate::TypeInner::Array { base, .. } = ctx.module.types[ty].inner else {
6119                    unreachable!("Atomic operations on Access expressions only work for arrays");
6120                };
6121                Ok(base)
6122            }
6123            crate::Expression::AccessIndex { base, index } => {
6124                log::debug!("\t\t  access index {handle:?} {index:?}");
6125                let ty = self.record_atomic_access(ctx, base)?;
6126                match ctx.module.types[ty].inner {
6127                    crate::TypeInner::Struct { ref members, .. } => {
6128                        let index = index as usize;
6129                        self.upgrade_atomics.insert_field(ty, index);
6130                        Ok(members[index].ty)
6131                    }
6132                    crate::TypeInner::Array { base, .. } => {
6133                        Ok(base)
6134                    }
6135                    _ => unreachable!("Atomic operations on AccessIndex expressions only work for structs and arrays"),
6136                }
6137            }
6138            crate::Expression::GlobalVariable(h) => {
6139                log::debug!("\t\t  found {h:?}");
6140                self.upgrade_atomics.insert_global(h);
6141                Ok(ctx.module.global_variables[h].ty)
6142            }
6143            _ => Err(Error::AtomicUpgradeError(
6144                crate::front::atomic_upgrade::Error::GlobalVariableMissing,
6145            )),
6146        }
6147    }
6148}
6149
6150fn make_index_literal(
6151    ctx: &mut BlockContext,
6152    index: u32,
6153    block: &mut crate::Block,
6154    emitter: &mut crate::proc::Emitter,
6155    index_type: Handle<crate::Type>,
6156    index_type_id: spirv::Word,
6157    span: crate::Span,
6158) -> Result<Handle<crate::Expression>, Error> {
6159    block.extend(emitter.finish(ctx.expressions));
6160
6161    let literal = match ctx.module.types[index_type].inner.scalar_kind() {
6162        Some(crate::ScalarKind::Uint) => crate::Literal::U32(index),
6163        Some(crate::ScalarKind::Sint) => crate::Literal::I32(index as i32),
6164        _ => return Err(Error::InvalidIndexType(index_type_id)),
6165    };
6166    let expr = ctx
6167        .expressions
6168        .append(crate::Expression::Literal(literal), span);
6169
6170    emitter.start(ctx.expressions);
6171    Ok(expr)
6172}
6173
6174fn resolve_constant(gctx: crate::proc::GlobalCtx, constant: &Constant) -> Option<u32> {
6175    let constant = match *constant {
6176        Constant::Constant(constant) => constant,
6177        Constant::Override(_) => return None,
6178    };
6179    match gctx.global_expressions[gctx.constants[constant].init] {
6180        crate::Expression::Literal(crate::Literal::U32(id)) => Some(id),
6181        crate::Expression::Literal(crate::Literal::I32(id)) => Some(id as u32),
6182        _ => None,
6183    }
6184}
6185
6186pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> {
6187    if data.len() % 4 != 0 {
6188        return Err(Error::IncompleteData);
6189    }
6190
6191    let words = data
6192        .chunks(4)
6193        .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
6194    Frontend::new(words, options).parse()
6195}
6196
6197/// Helper function to check if `child` is in the scope of `parent`
6198fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
6199    loop {
6200        if child == parent {
6201            // The child is in the scope parent
6202            break true;
6203        } else if child == 0 {
6204            // Searched finished at the root the child isn't in the parent's body
6205            break false;
6206        }
6207
6208        child = block_ctx.bodies[child].parent;
6209    }
6210}
6211
6212#[cfg(test)]
6213mod test {
6214    use alloc::vec;
6215
6216    #[test]
6217    fn parse() {
6218        let bin = vec![
6219            // Magic number.           Version number: 1.0.
6220            0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
6221            // Generator number: 0.    Bound: 0.
6222            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved word: 0.
6223            0x00, 0x00, 0x00, 0x00, // OpMemoryModel.          Logical.
6224            0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // GLSL450.
6225            0x01, 0x00, 0x00, 0x00,
6226        ];
6227        let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
6228    }
6229}