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