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