naga/front/spv/
next_block.rs

1//! Implementation of [`Frontend::next_block()`].
2//!
3//! This method is split out into its own module purely because it is so long.
4
5use alloc::{format, vec, vec::Vec};
6
7use crate::front::spv::{
8    convert::{map_binary_operator, map_relational_fun},
9    image, resolve_constant, BlockContext, Body, BodyFragment, Constant, Error, Frontend,
10    LookupExpression, LookupHelper as _, LookupLoadOverride, MergeBlockInformation, PhiExpression,
11    SignAnchor,
12};
13use crate::Handle;
14
15impl<I: Iterator<Item = u32>> Frontend<I> {
16    /// Add the next SPIR-V block's contents to `block_ctx`.
17    ///
18    /// Except for the function's entry block, `block_id` should be the label of
19    /// a block we've seen mentioned before, with an entry in
20    /// `block_ctx.body_for_label` to tell us which `Body` it contributes to.
21    pub(in crate::front::spv) fn next_block(
22        &mut self,
23        block_id: spirv::Word,
24        ctx: &mut BlockContext,
25    ) -> Result<(), Error> {
26        // Extend `body` with the correct form for a branch to `target`.
27        fn merger(body: &mut Body, target: &MergeBlockInformation) {
28            body.data.push(match *target {
29                MergeBlockInformation::LoopContinue => BodyFragment::Continue,
30                MergeBlockInformation::LoopMerge | MergeBlockInformation::SwitchMerge => {
31                    BodyFragment::Break
32                }
33
34                // Finishing a selection merge means just falling off the end of
35                // the `accept` or `reject` block of the `If` statement.
36                MergeBlockInformation::SelectionMerge => return,
37            })
38        }
39
40        let mut emitter = crate::proc::Emitter::default();
41        emitter.start(ctx.expressions);
42
43        // Find the `Body` to which this block contributes.
44        //
45        // If this is some SPIR-V structured control flow construct's merge
46        // block, then `body_idx` will refer to the same `Body` as the header,
47        // so that we simply pick up accumulating the `Body` where the header
48        // left off. Each of the statements in a block dominates the next, so
49        // we're sure to encounter their SPIR-V blocks in order, ensuring that
50        // the `Body` will be assembled in the proper order.
51        //
52        // Note that, unlike every other kind of SPIR-V block, we don't know the
53        // function's first block's label in advance. Thus, we assume that if
54        // this block has no entry in `ctx.body_for_label`, it must be the
55        // function's first block. This always has body index zero.
56        let mut body_idx = *ctx.body_for_label.entry(block_id).or_default();
57
58        // The Naga IR block this call builds. This will end up as
59        // `ctx.blocks[&block_id]`, and `ctx.bodies[body_idx]` will refer to it
60        // via a `BodyFragment::BlockId`.
61        let mut block = crate::Block::new();
62
63        // Stores the merge block as defined by a `OpSelectionMerge` otherwise is `None`
64        //
65        // This is used in `OpSwitch` to promote the `MergeBlockInformation` from
66        // `SelectionMerge` to `SwitchMerge` to allow `Break`s this isn't desirable for
67        // `LoopMerge`s because otherwise `Continue`s wouldn't be allowed
68        let mut selection_merge_block = None;
69
70        macro_rules! get_expr_handle {
71            ($id:expr, $lexp:expr) => {
72                self.get_expr_handle($id, $lexp, ctx, &mut emitter, &mut block, body_idx)
73            };
74        }
75        macro_rules! parse_expr_op {
76            ($op:expr, BINARY) => {
77                self.parse_expr_binary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
78            };
79
80            ($op:expr, SHIFT) => {
81                self.parse_expr_shift_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
82            };
83            ($op:expr, UNARY) => {
84                self.parse_expr_unary_op(ctx, &mut emitter, &mut block, block_id, body_idx, $op)
85            };
86            ($axis:expr, $ctrl:expr, DERIVATIVE) => {
87                self.parse_expr_derivative(
88                    ctx,
89                    &mut emitter,
90                    &mut block,
91                    block_id,
92                    body_idx,
93                    ($axis, $ctrl),
94                )
95            };
96        }
97
98        let terminator = loop {
99            use spirv::Op;
100            let start = self.data_offset;
101            let inst = self.next_inst()?;
102            let span = crate::Span::from(start..(start + 4 * (inst.wc as usize)));
103            log::debug!("\t\t{:?} [{}]", inst.op, inst.wc);
104
105            match inst.op {
106                Op::Line => {
107                    inst.expect(4)?;
108                    let _file_id = self.next()?;
109                    let _row_id = self.next()?;
110                    let _col_id = self.next()?;
111                }
112                Op::NoLine => inst.expect(1)?,
113                Op::Undef => {
114                    inst.expect(3)?;
115                    let type_id = self.next()?;
116                    let id = self.next()?;
117                    let type_lookup = self.lookup_type.lookup(type_id)?;
118                    let ty = type_lookup.handle;
119
120                    self.lookup_expression.insert(
121                        id,
122                        LookupExpression {
123                            handle: ctx
124                                .expressions
125                                .append(crate::Expression::ZeroValue(ty), span),
126                            type_id,
127                            block_id,
128                        },
129                    );
130                }
131                Op::Variable => {
132                    inst.expect_at_least(4)?;
133                    block.extend(emitter.finish(ctx.expressions));
134
135                    let result_type_id = self.next()?;
136                    let result_id = self.next()?;
137                    let _storage_class = self.next()?;
138                    let init = if inst.wc > 4 {
139                        inst.expect(5)?;
140                        let init_id = self.next()?;
141                        let lconst = self.lookup_constant.lookup(init_id)?;
142                        Some(ctx.expressions.append(lconst.inner.to_expr(), span))
143                    } else {
144                        None
145                    };
146
147                    let name = self
148                        .future_decor
149                        .remove(&result_id)
150                        .and_then(|decor| decor.name);
151                    if let Some(ref name) = name {
152                        log::debug!("\t\t\tid={result_id} name={name}");
153                    }
154                    let lookup_ty = self.lookup_type.lookup(result_type_id)?;
155                    let var_handle = ctx.local_arena.append(
156                        crate::LocalVariable {
157                            name,
158                            ty: match ctx.module.types[lookup_ty.handle].inner {
159                                crate::TypeInner::Pointer { base, .. } => base,
160                                _ => lookup_ty.handle,
161                            },
162                            init,
163                        },
164                        span,
165                    );
166
167                    self.lookup_expression.insert(
168                        result_id,
169                        LookupExpression {
170                            handle: ctx
171                                .expressions
172                                .append(crate::Expression::LocalVariable(var_handle), span),
173                            type_id: result_type_id,
174                            block_id,
175                        },
176                    );
177                    emitter.start(ctx.expressions);
178                }
179                Op::Phi => {
180                    inst.expect_at_least(3)?;
181                    block.extend(emitter.finish(ctx.expressions));
182
183                    let result_type_id = self.next()?;
184                    let result_id = self.next()?;
185
186                    let name = format!("phi_{result_id}");
187                    let local = ctx.local_arena.append(
188                        crate::LocalVariable {
189                            name: Some(name),
190                            ty: self.lookup_type.lookup(result_type_id)?.handle,
191                            init: None,
192                        },
193                        self.span_from(start),
194                    );
195                    let pointer = ctx
196                        .expressions
197                        .append(crate::Expression::LocalVariable(local), span);
198
199                    let in_count = (inst.wc - 3) / 2;
200                    let mut phi = PhiExpression {
201                        local,
202                        expressions: Vec::with_capacity(in_count as usize),
203                    };
204                    for _ in 0..in_count {
205                        let expr = self.next()?;
206                        let block = self.next()?;
207                        phi.expressions.push((expr, block));
208                    }
209
210                    ctx.phis.push(phi);
211                    emitter.start(ctx.expressions);
212
213                    // Associate the lookup with an actual value, which is emitted
214                    // into the current block.
215                    self.lookup_expression.insert(
216                        result_id,
217                        LookupExpression {
218                            handle: ctx
219                                .expressions
220                                .append(crate::Expression::Load { pointer }, span),
221                            type_id: result_type_id,
222                            block_id,
223                        },
224                    );
225                }
226                Op::AccessChain | Op::InBoundsAccessChain => {
227                    struct AccessExpression {
228                        base_handle: Handle<crate::Expression>,
229                        type_id: spirv::Word,
230                        load_override: Option<LookupLoadOverride>,
231                    }
232
233                    inst.expect_at_least(4)?;
234
235                    let result_type_id = self.next()?;
236                    let result_id = self.next()?;
237                    let base_id = self.next()?;
238                    log::trace!("\t\t\tlooking up expr {base_id:?}");
239
240                    let mut acex = {
241                        let lexp = self.lookup_expression.lookup(base_id)?;
242                        let lty = self.lookup_type.lookup(lexp.type_id)?;
243
244                        // HACK `OpAccessChain` and `OpInBoundsAccessChain`
245                        // require for the result type to be a pointer, but if
246                        // we're given a pointer to an image / sampler, it will
247                        // be *already* dereferenced, since we do that early
248                        // during `parse_type_pointer()`.
249                        //
250                        // This can happen only through `BindingArray`, since
251                        // that's the only case where one can obtain a pointer
252                        // to an image / sampler, and so let's match on that:
253                        let dereference = match ctx.module.types[lty.handle].inner {
254                            crate::TypeInner::BindingArray { .. } => false,
255                            _ => true,
256                        };
257
258                        let type_id = if dereference {
259                            lty.base_id.ok_or(Error::InvalidAccessType(lexp.type_id))?
260                        } else {
261                            lexp.type_id
262                        };
263
264                        AccessExpression {
265                            base_handle: get_expr_handle!(base_id, lexp),
266                            type_id,
267                            load_override: self.lookup_load_override.get(&base_id).cloned(),
268                        }
269                    };
270
271                    for _ in 4..inst.wc {
272                        let access_id = self.next()?;
273                        log::trace!("\t\t\tlooking up index expr {access_id:?}");
274                        let index_expr = self.lookup_expression.lookup(access_id)?.clone();
275                        let index_expr_handle = get_expr_handle!(access_id, &index_expr);
276                        let index_expr_data = &ctx.expressions[index_expr.handle];
277                        let index_maybe = match *index_expr_data {
278                            crate::Expression::Constant(const_handle) => Some(
279                                ctx.gctx()
280                                    .get_const_val(ctx.module.constants[const_handle].init)
281                                    .map_err(|_| {
282                                        Error::InvalidAccess(crate::Expression::Constant(
283                                            const_handle,
284                                        ))
285                                    })?,
286                            ),
287                            _ => None,
288                        };
289
290                        log::trace!("\t\t\tlooking up type {:?}", acex.type_id);
291                        let type_lookup = self.lookup_type.lookup(acex.type_id)?;
292                        let ty = &ctx.module.types[type_lookup.handle];
293                        acex = match ty.inner {
294                            // can only index a struct with a constant
295                            crate::TypeInner::Struct { ref members, .. } => {
296                                let index = index_maybe
297                                    .ok_or_else(|| Error::InvalidAccess(index_expr_data.clone()))?;
298
299                                let lookup_member = self
300                                    .lookup_member
301                                    .get(&(type_lookup.handle, index))
302                                    .ok_or(Error::InvalidAccessType(acex.type_id))?;
303                                let base_handle = ctx.expressions.append(
304                                    crate::Expression::AccessIndex {
305                                        base: acex.base_handle,
306                                        index,
307                                    },
308                                    span,
309                                );
310
311                                if let Some(crate::Binding::BuiltIn(built_in)) =
312                                    members[index as usize].binding
313                                {
314                                    self.gl_per_vertex_builtin_access.insert(built_in);
315                                }
316
317                                AccessExpression {
318                                    base_handle,
319                                    type_id: lookup_member.type_id,
320                                    load_override: if lookup_member.row_major {
321                                        debug_assert!(acex.load_override.is_none());
322                                        let sub_type_lookup =
323                                            self.lookup_type.lookup(lookup_member.type_id)?;
324                                        Some(match ctx.module.types[sub_type_lookup.handle].inner {
325                                            // load it transposed, to match column major expectations
326                                            crate::TypeInner::Matrix { .. } => {
327                                                let loaded = ctx.expressions.append(
328                                                    crate::Expression::Load {
329                                                        pointer: base_handle,
330                                                    },
331                                                    span,
332                                                );
333                                                let transposed = ctx.expressions.append(
334                                                    crate::Expression::Math {
335                                                        fun: crate::MathFunction::Transpose,
336                                                        arg: loaded,
337                                                        arg1: None,
338                                                        arg2: None,
339                                                        arg3: None,
340                                                    },
341                                                    span,
342                                                );
343                                                LookupLoadOverride::Loaded(transposed)
344                                            }
345                                            _ => LookupLoadOverride::Pending,
346                                        })
347                                    } else {
348                                        None
349                                    },
350                                }
351                            }
352                            crate::TypeInner::Matrix { .. } => {
353                                let load_override = match acex.load_override {
354                                    // We are indexing inside a row-major matrix
355                                    Some(LookupLoadOverride::Loaded(load_expr)) => {
356                                        let index = index_maybe.ok_or_else(|| {
357                                            Error::InvalidAccess(index_expr_data.clone())
358                                        })?;
359                                        let sub_handle = ctx.expressions.append(
360                                            crate::Expression::AccessIndex {
361                                                base: load_expr,
362                                                index,
363                                            },
364                                            span,
365                                        );
366                                        Some(LookupLoadOverride::Loaded(sub_handle))
367                                    }
368                                    _ => None,
369                                };
370                                let sub_expr = match index_maybe {
371                                    Some(index) => crate::Expression::AccessIndex {
372                                        base: acex.base_handle,
373                                        index,
374                                    },
375                                    None => crate::Expression::Access {
376                                        base: acex.base_handle,
377                                        index: index_expr_handle,
378                                    },
379                                };
380                                AccessExpression {
381                                    base_handle: ctx.expressions.append(sub_expr, span),
382                                    type_id: type_lookup
383                                        .base_id
384                                        .ok_or(Error::InvalidAccessType(acex.type_id))?,
385                                    load_override,
386                                }
387                            }
388                            // This must be a vector or an array.
389                            _ => {
390                                let base_handle = ctx.expressions.append(
391                                    crate::Expression::Access {
392                                        base: acex.base_handle,
393                                        index: index_expr_handle,
394                                    },
395                                    span,
396                                );
397                                let load_override = match acex.load_override {
398                                    // If there is a load override in place, then we always end up
399                                    // with a side-loaded value here.
400                                    Some(lookup_load_override) => {
401                                        let sub_expr = match lookup_load_override {
402                                            // We must be indexing into the array of row-major matrices.
403                                            // Let's load the result of indexing and transpose it.
404                                            LookupLoadOverride::Pending => {
405                                                let loaded = ctx.expressions.append(
406                                                    crate::Expression::Load {
407                                                        pointer: base_handle,
408                                                    },
409                                                    span,
410                                                );
411                                                ctx.expressions.append(
412                                                    crate::Expression::Math {
413                                                        fun: crate::MathFunction::Transpose,
414                                                        arg: loaded,
415                                                        arg1: None,
416                                                        arg2: None,
417                                                        arg3: None,
418                                                    },
419                                                    span,
420                                                )
421                                            }
422                                            // We are indexing inside a row-major matrix.
423                                            LookupLoadOverride::Loaded(load_expr) => {
424                                                ctx.expressions.append(
425                                                    crate::Expression::Access {
426                                                        base: load_expr,
427                                                        index: index_expr_handle,
428                                                    },
429                                                    span,
430                                                )
431                                            }
432                                        };
433                                        Some(LookupLoadOverride::Loaded(sub_expr))
434                                    }
435                                    None => None,
436                                };
437                                AccessExpression {
438                                    base_handle,
439                                    type_id: type_lookup
440                                        .base_id
441                                        .ok_or(Error::InvalidAccessType(acex.type_id))?,
442                                    load_override,
443                                }
444                            }
445                        };
446                    }
447
448                    if let Some(load_expr) = acex.load_override {
449                        self.lookup_load_override.insert(result_id, load_expr);
450                    }
451                    let lookup_expression = LookupExpression {
452                        handle: acex.base_handle,
453                        type_id: result_type_id,
454                        block_id,
455                    };
456                    self.lookup_expression.insert(result_id, lookup_expression);
457                }
458                Op::VectorExtractDynamic => {
459                    inst.expect(5)?;
460
461                    let result_type_id = self.next()?;
462                    let id = self.next()?;
463                    let composite_id = self.next()?;
464                    let index_id = self.next()?;
465
466                    let root_lexp = self.lookup_expression.lookup(composite_id)?;
467                    let root_handle = get_expr_handle!(composite_id, root_lexp);
468                    let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;
469                    let index_lexp = self.lookup_expression.lookup(index_id)?;
470                    let index_handle = get_expr_handle!(index_id, index_lexp);
471                    let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;
472
473                    let num_components = match ctx.module.types[root_type_lookup.handle].inner {
474                        crate::TypeInner::Vector { size, .. } => size as u32,
475                        _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),
476                    };
477
478                    let mut make_index = |ctx: &mut BlockContext, index: u32| {
479                        make_index_literal(
480                            ctx,
481                            index,
482                            &mut block,
483                            &mut emitter,
484                            index_type,
485                            index_lexp.type_id,
486                            span,
487                        )
488                    };
489
490                    let index_expr = make_index(ctx, 0)?;
491                    let mut handle = ctx.expressions.append(
492                        crate::Expression::Access {
493                            base: root_handle,
494                            index: index_expr,
495                        },
496                        span,
497                    );
498                    for index in 1..num_components {
499                        let index_expr = make_index(ctx, index)?;
500                        let access_expr = ctx.expressions.append(
501                            crate::Expression::Access {
502                                base: root_handle,
503                                index: index_expr,
504                            },
505                            span,
506                        );
507                        let cond = ctx.expressions.append(
508                            crate::Expression::Binary {
509                                op: crate::BinaryOperator::Equal,
510                                left: index_expr,
511                                right: index_handle,
512                            },
513                            span,
514                        );
515                        handle = ctx.expressions.append(
516                            crate::Expression::Select {
517                                condition: cond,
518                                accept: access_expr,
519                                reject: handle,
520                            },
521                            span,
522                        );
523                    }
524
525                    self.lookup_expression.insert(
526                        id,
527                        LookupExpression {
528                            handle,
529                            type_id: result_type_id,
530                            block_id,
531                        },
532                    );
533                }
534                Op::VectorInsertDynamic => {
535                    inst.expect(6)?;
536
537                    let result_type_id = self.next()?;
538                    let id = self.next()?;
539                    let composite_id = self.next()?;
540                    let object_id = self.next()?;
541                    let index_id = self.next()?;
542
543                    let object_lexp = self.lookup_expression.lookup(object_id)?;
544                    let object_handle = get_expr_handle!(object_id, object_lexp);
545                    let root_lexp = self.lookup_expression.lookup(composite_id)?;
546                    let root_handle = get_expr_handle!(composite_id, root_lexp);
547                    let root_type_lookup = self.lookup_type.lookup(root_lexp.type_id)?;
548                    let index_lexp = self.lookup_expression.lookup(index_id)?;
549                    let index_handle = get_expr_handle!(index_id, index_lexp);
550                    let index_type = self.lookup_type.lookup(index_lexp.type_id)?.handle;
551
552                    let num_components = match ctx.module.types[root_type_lookup.handle].inner {
553                        crate::TypeInner::Vector { size, .. } => size as u32,
554                        _ => return Err(Error::InvalidVectorType(root_type_lookup.handle)),
555                    };
556
557                    let mut components = Vec::with_capacity(num_components as usize);
558                    for index in 0..num_components {
559                        let index_expr = make_index_literal(
560                            ctx,
561                            index,
562                            &mut block,
563                            &mut emitter,
564                            index_type,
565                            index_lexp.type_id,
566                            span,
567                        )?;
568                        let access_expr = ctx.expressions.append(
569                            crate::Expression::Access {
570                                base: root_handle,
571                                index: index_expr,
572                            },
573                            span,
574                        );
575                        let cond = ctx.expressions.append(
576                            crate::Expression::Binary {
577                                op: crate::BinaryOperator::Equal,
578                                left: index_expr,
579                                right: index_handle,
580                            },
581                            span,
582                        );
583                        let handle = ctx.expressions.append(
584                            crate::Expression::Select {
585                                condition: cond,
586                                accept: object_handle,
587                                reject: access_expr,
588                            },
589                            span,
590                        );
591                        components.push(handle);
592                    }
593                    let handle = ctx.expressions.append(
594                        crate::Expression::Compose {
595                            ty: root_type_lookup.handle,
596                            components,
597                        },
598                        span,
599                    );
600
601                    self.lookup_expression.insert(
602                        id,
603                        LookupExpression {
604                            handle,
605                            type_id: result_type_id,
606                            block_id,
607                        },
608                    );
609                }
610                Op::CompositeExtract => {
611                    inst.expect_at_least(4)?;
612
613                    let result_type_id = self.next()?;
614                    let result_id = self.next()?;
615                    let base_id = self.next()?;
616                    log::trace!("\t\t\tlooking up expr {base_id:?}");
617                    let mut lexp = self.lookup_expression.lookup(base_id)?.clone();
618                    lexp.handle = get_expr_handle!(base_id, &lexp);
619                    for _ in 4..inst.wc {
620                        let index = self.next()?;
621                        log::trace!("\t\t\tlooking up type {:?}", lexp.type_id);
622                        let type_lookup = self.lookup_type.lookup(lexp.type_id)?;
623                        let type_id = match ctx.module.types[type_lookup.handle].inner {
624                            crate::TypeInner::Struct { .. } => {
625                                self.lookup_member
626                                    .get(&(type_lookup.handle, index))
627                                    .ok_or(Error::InvalidAccessType(lexp.type_id))?
628                                    .type_id
629                            }
630                            crate::TypeInner::Array { .. }
631                            | crate::TypeInner::Vector { .. }
632                            | crate::TypeInner::Matrix { .. } => type_lookup
633                                .base_id
634                                .ok_or(Error::InvalidAccessType(lexp.type_id))?,
635                            ref other => {
636                                log::warn!("composite type {other:?}");
637                                return Err(Error::UnsupportedType(type_lookup.handle));
638                            }
639                        };
640                        lexp = LookupExpression {
641                            handle: ctx.expressions.append(
642                                crate::Expression::AccessIndex {
643                                    base: lexp.handle,
644                                    index,
645                                },
646                                span,
647                            ),
648                            type_id,
649                            block_id,
650                        };
651                    }
652
653                    self.lookup_expression.insert(
654                        result_id,
655                        LookupExpression {
656                            handle: lexp.handle,
657                            type_id: result_type_id,
658                            block_id,
659                        },
660                    );
661                }
662                Op::CompositeInsert => {
663                    inst.expect_at_least(5)?;
664
665                    let result_type_id = self.next()?;
666                    let id = self.next()?;
667                    let object_id = self.next()?;
668                    let composite_id = self.next()?;
669                    let mut selections = Vec::with_capacity(inst.wc as usize - 5);
670                    for _ in 5..inst.wc {
671                        selections.push(self.next()?);
672                    }
673
674                    let object_lexp = self.lookup_expression.lookup(object_id)?.clone();
675                    let object_handle = get_expr_handle!(object_id, &object_lexp);
676                    let root_lexp = self.lookup_expression.lookup(composite_id)?.clone();
677                    let root_handle = get_expr_handle!(composite_id, &root_lexp);
678                    let handle = self.insert_composite(
679                        root_handle,
680                        result_type_id,
681                        object_handle,
682                        &selections,
683                        &ctx.module.types,
684                        ctx.expressions,
685                        span,
686                    )?;
687
688                    self.lookup_expression.insert(
689                        id,
690                        LookupExpression {
691                            handle,
692                            type_id: result_type_id,
693                            block_id,
694                        },
695                    );
696                }
697                Op::CompositeConstruct => {
698                    inst.expect_at_least(3)?;
699
700                    let result_type_id = self.next()?;
701                    let id = self.next()?;
702                    let mut components = Vec::with_capacity(inst.wc as usize - 2);
703                    for _ in 3..inst.wc {
704                        let comp_id = self.next()?;
705                        log::trace!("\t\t\tlooking up expr {comp_id:?}");
706                        let lexp = self.lookup_expression.lookup(comp_id)?;
707                        let handle = get_expr_handle!(comp_id, lexp);
708                        components.push(handle);
709                    }
710                    let ty = self.lookup_type.lookup(result_type_id)?.handle;
711                    let first = components[0];
712                    let expr = match ctx.module.types[ty].inner {
713                        // this is an optimization to detect the splat
714                        crate::TypeInner::Vector { size, .. }
715                            if components.len() == size as usize
716                                && components[1..].iter().all(|&c| c == first) =>
717                        {
718                            crate::Expression::Splat { size, value: first }
719                        }
720                        _ => crate::Expression::Compose { ty, components },
721                    };
722                    self.lookup_expression.insert(
723                        id,
724                        LookupExpression {
725                            handle: ctx.expressions.append(expr, span),
726                            type_id: result_type_id,
727                            block_id,
728                        },
729                    );
730                }
731                Op::Load => {
732                    inst.expect_at_least(4)?;
733
734                    let result_type_id = self.next()?;
735                    let result_id = self.next()?;
736                    let pointer_id = self.next()?;
737                    if inst.wc != 4 {
738                        inst.expect(5)?;
739                        let _memory_access = self.next()?;
740                    }
741
742                    let base_lexp = self.lookup_expression.lookup(pointer_id)?;
743                    let base_handle = get_expr_handle!(pointer_id, base_lexp);
744                    let type_lookup = self.lookup_type.lookup(base_lexp.type_id)?;
745                    let handle = match ctx.module.types[type_lookup.handle].inner {
746                        crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } => {
747                            base_handle
748                        }
749                        _ => match self.lookup_load_override.get(&pointer_id) {
750                            Some(&LookupLoadOverride::Loaded(handle)) => handle,
751                            //Note: we aren't handling `LookupLoadOverride::Pending` properly here
752                            _ => ctx.expressions.append(
753                                crate::Expression::Load {
754                                    pointer: base_handle,
755                                },
756                                span,
757                            ),
758                        },
759                    };
760
761                    self.lookup_expression.insert(
762                        result_id,
763                        LookupExpression {
764                            handle,
765                            type_id: result_type_id,
766                            block_id,
767                        },
768                    );
769                }
770                Op::Store => {
771                    inst.expect_at_least(3)?;
772
773                    let pointer_id = self.next()?;
774                    let value_id = self.next()?;
775                    if inst.wc != 3 {
776                        inst.expect(4)?;
777                        let _memory_access = self.next()?;
778                    }
779                    let base_expr = self.lookup_expression.lookup(pointer_id)?;
780                    let base_handle = get_expr_handle!(pointer_id, base_expr);
781                    let value_expr = self.lookup_expression.lookup(value_id)?;
782                    let value_handle = get_expr_handle!(value_id, value_expr);
783
784                    block.extend(emitter.finish(ctx.expressions));
785                    block.push(
786                        crate::Statement::Store {
787                            pointer: base_handle,
788                            value: value_handle,
789                        },
790                        span,
791                    );
792                    emitter.start(ctx.expressions);
793                }
794                // Arithmetic Instructions +, -, *, /, %
795                Op::SNegate | Op::FNegate => {
796                    inst.expect(4)?;
797                    self.parse_expr_unary_op_sign_adjusted(
798                        ctx,
799                        &mut emitter,
800                        &mut block,
801                        block_id,
802                        body_idx,
803                        crate::UnaryOperator::Negate,
804                    )?;
805                }
806                Op::IAdd
807                | Op::ISub
808                | Op::IMul
809                | Op::BitwiseOr
810                | Op::BitwiseXor
811                | Op::BitwiseAnd
812                | Op::SDiv
813                | Op::SRem => {
814                    inst.expect(5)?;
815                    let operator = map_binary_operator(inst.op)?;
816                    self.parse_expr_binary_op_sign_adjusted(
817                        ctx,
818                        &mut emitter,
819                        &mut block,
820                        block_id,
821                        body_idx,
822                        operator,
823                        SignAnchor::Result,
824                    )?;
825                }
826                Op::IEqual | Op::INotEqual => {
827                    inst.expect(5)?;
828                    let operator = map_binary_operator(inst.op)?;
829                    self.parse_expr_binary_op_sign_adjusted(
830                        ctx,
831                        &mut emitter,
832                        &mut block,
833                        block_id,
834                        body_idx,
835                        operator,
836                        SignAnchor::Operand,
837                    )?;
838                }
839                Op::FAdd => {
840                    inst.expect(5)?;
841                    parse_expr_op!(crate::BinaryOperator::Add, BINARY)?;
842                }
843                Op::FSub => {
844                    inst.expect(5)?;
845                    parse_expr_op!(crate::BinaryOperator::Subtract, BINARY)?;
846                }
847                Op::FMul => {
848                    inst.expect(5)?;
849                    parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;
850                }
851                Op::UDiv | Op::FDiv => {
852                    inst.expect(5)?;
853                    parse_expr_op!(crate::BinaryOperator::Divide, BINARY)?;
854                }
855                Op::UMod | Op::FRem => {
856                    inst.expect(5)?;
857                    parse_expr_op!(crate::BinaryOperator::Modulo, BINARY)?;
858                }
859                Op::SMod => {
860                    inst.expect(5)?;
861
862                    // x - y * int(floor(float(x) / float(y)))
863
864                    let start = self.data_offset;
865                    let result_type_id = self.next()?;
866                    let result_id = self.next()?;
867                    let p1_id = self.next()?;
868                    let p2_id = self.next()?;
869                    let span = self.span_from_with_op(start);
870
871                    let p1_lexp = self.lookup_expression.lookup(p1_id)?;
872                    let left = self.get_expr_handle(
873                        p1_id,
874                        p1_lexp,
875                        ctx,
876                        &mut emitter,
877                        &mut block,
878                        body_idx,
879                    );
880                    let p2_lexp = self.lookup_expression.lookup(p2_id)?;
881                    let right = self.get_expr_handle(
882                        p2_id,
883                        p2_lexp,
884                        ctx,
885                        &mut emitter,
886                        &mut block,
887                        body_idx,
888                    );
889
890                    let result_ty = self.lookup_type.lookup(result_type_id)?;
891                    let inner = &ctx.module.types[result_ty.handle].inner;
892                    let kind = inner.scalar_kind().unwrap();
893                    let size = inner.size(ctx.gctx()) as u8;
894
895                    let left_cast = ctx.expressions.append(
896                        crate::Expression::As {
897                            expr: left,
898                            kind: crate::ScalarKind::Float,
899                            convert: Some(size),
900                        },
901                        span,
902                    );
903                    let right_cast = ctx.expressions.append(
904                        crate::Expression::As {
905                            expr: right,
906                            kind: crate::ScalarKind::Float,
907                            convert: Some(size),
908                        },
909                        span,
910                    );
911                    let div = ctx.expressions.append(
912                        crate::Expression::Binary {
913                            op: crate::BinaryOperator::Divide,
914                            left: left_cast,
915                            right: right_cast,
916                        },
917                        span,
918                    );
919                    let floor = ctx.expressions.append(
920                        crate::Expression::Math {
921                            fun: crate::MathFunction::Floor,
922                            arg: div,
923                            arg1: None,
924                            arg2: None,
925                            arg3: None,
926                        },
927                        span,
928                    );
929                    let cast = ctx.expressions.append(
930                        crate::Expression::As {
931                            expr: floor,
932                            kind,
933                            convert: Some(size),
934                        },
935                        span,
936                    );
937                    let mult = ctx.expressions.append(
938                        crate::Expression::Binary {
939                            op: crate::BinaryOperator::Multiply,
940                            left: cast,
941                            right,
942                        },
943                        span,
944                    );
945                    let sub = ctx.expressions.append(
946                        crate::Expression::Binary {
947                            op: crate::BinaryOperator::Subtract,
948                            left,
949                            right: mult,
950                        },
951                        span,
952                    );
953                    self.lookup_expression.insert(
954                        result_id,
955                        LookupExpression {
956                            handle: sub,
957                            type_id: result_type_id,
958                            block_id,
959                        },
960                    );
961                }
962                Op::FMod => {
963                    inst.expect(5)?;
964
965                    // x - y * floor(x / y)
966
967                    let start = self.data_offset;
968                    let span = self.span_from_with_op(start);
969
970                    let result_type_id = self.next()?;
971                    let result_id = self.next()?;
972                    let p1_id = self.next()?;
973                    let p2_id = self.next()?;
974
975                    let p1_lexp = self.lookup_expression.lookup(p1_id)?;
976                    let left = self.get_expr_handle(
977                        p1_id,
978                        p1_lexp,
979                        ctx,
980                        &mut emitter,
981                        &mut block,
982                        body_idx,
983                    );
984                    let p2_lexp = self.lookup_expression.lookup(p2_id)?;
985                    let right = self.get_expr_handle(
986                        p2_id,
987                        p2_lexp,
988                        ctx,
989                        &mut emitter,
990                        &mut block,
991                        body_idx,
992                    );
993
994                    let div = ctx.expressions.append(
995                        crate::Expression::Binary {
996                            op: crate::BinaryOperator::Divide,
997                            left,
998                            right,
999                        },
1000                        span,
1001                    );
1002                    let floor = ctx.expressions.append(
1003                        crate::Expression::Math {
1004                            fun: crate::MathFunction::Floor,
1005                            arg: div,
1006                            arg1: None,
1007                            arg2: None,
1008                            arg3: None,
1009                        },
1010                        span,
1011                    );
1012                    let mult = ctx.expressions.append(
1013                        crate::Expression::Binary {
1014                            op: crate::BinaryOperator::Multiply,
1015                            left: floor,
1016                            right,
1017                        },
1018                        span,
1019                    );
1020                    let sub = ctx.expressions.append(
1021                        crate::Expression::Binary {
1022                            op: crate::BinaryOperator::Subtract,
1023                            left,
1024                            right: mult,
1025                        },
1026                        span,
1027                    );
1028                    self.lookup_expression.insert(
1029                        result_id,
1030                        LookupExpression {
1031                            handle: sub,
1032                            type_id: result_type_id,
1033                            block_id,
1034                        },
1035                    );
1036                }
1037                Op::VectorTimesScalar
1038                | Op::VectorTimesMatrix
1039                | Op::MatrixTimesScalar
1040                | Op::MatrixTimesVector
1041                | Op::MatrixTimesMatrix => {
1042                    inst.expect(5)?;
1043                    parse_expr_op!(crate::BinaryOperator::Multiply, BINARY)?;
1044                }
1045                Op::Transpose => {
1046                    inst.expect(4)?;
1047
1048                    let result_type_id = self.next()?;
1049                    let result_id = self.next()?;
1050                    let matrix_id = self.next()?;
1051                    let matrix_lexp = self.lookup_expression.lookup(matrix_id)?;
1052                    let matrix_handle = get_expr_handle!(matrix_id, matrix_lexp);
1053                    let expr = crate::Expression::Math {
1054                        fun: crate::MathFunction::Transpose,
1055                        arg: matrix_handle,
1056                        arg1: None,
1057                        arg2: None,
1058                        arg3: None,
1059                    };
1060                    self.lookup_expression.insert(
1061                        result_id,
1062                        LookupExpression {
1063                            handle: ctx.expressions.append(expr, span),
1064                            type_id: result_type_id,
1065                            block_id,
1066                        },
1067                    );
1068                }
1069                Op::Dot => {
1070                    inst.expect(5)?;
1071
1072                    let result_type_id = self.next()?;
1073                    let result_id = self.next()?;
1074                    let left_id = self.next()?;
1075                    let right_id = self.next()?;
1076                    let left_lexp = self.lookup_expression.lookup(left_id)?;
1077                    let left_handle = get_expr_handle!(left_id, left_lexp);
1078                    let right_lexp = self.lookup_expression.lookup(right_id)?;
1079                    let right_handle = get_expr_handle!(right_id, right_lexp);
1080                    let expr = crate::Expression::Math {
1081                        fun: crate::MathFunction::Dot,
1082                        arg: left_handle,
1083                        arg1: Some(right_handle),
1084                        arg2: None,
1085                        arg3: None,
1086                    };
1087                    self.lookup_expression.insert(
1088                        result_id,
1089                        LookupExpression {
1090                            handle: ctx.expressions.append(expr, span),
1091                            type_id: result_type_id,
1092                            block_id,
1093                        },
1094                    );
1095                }
1096                Op::BitFieldInsert => {
1097                    inst.expect(7)?;
1098
1099                    let start = self.data_offset;
1100                    let span = self.span_from_with_op(start);
1101
1102                    let result_type_id = self.next()?;
1103                    let result_id = self.next()?;
1104                    let base_id = self.next()?;
1105                    let insert_id = self.next()?;
1106                    let offset_id = self.next()?;
1107                    let count_id = self.next()?;
1108                    let base_lexp = self.lookup_expression.lookup(base_id)?;
1109                    let base_handle = get_expr_handle!(base_id, base_lexp);
1110                    let insert_lexp = self.lookup_expression.lookup(insert_id)?;
1111                    let insert_handle = get_expr_handle!(insert_id, insert_lexp);
1112                    let offset_lexp = self.lookup_expression.lookup(offset_id)?;
1113                    let offset_handle = get_expr_handle!(offset_id, offset_lexp);
1114                    let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;
1115                    let count_lexp = self.lookup_expression.lookup(count_id)?;
1116                    let count_handle = get_expr_handle!(count_id, count_lexp);
1117                    let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;
1118
1119                    let offset_kind = ctx.module.types[offset_lookup_ty.handle]
1120                        .inner
1121                        .scalar_kind()
1122                        .unwrap();
1123                    let count_kind = ctx.module.types[count_lookup_ty.handle]
1124                        .inner
1125                        .scalar_kind()
1126                        .unwrap();
1127
1128                    let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {
1129                        ctx.expressions.append(
1130                            crate::Expression::As {
1131                                expr: offset_handle,
1132                                kind: crate::ScalarKind::Uint,
1133                                convert: None,
1134                            },
1135                            span,
1136                        )
1137                    } else {
1138                        offset_handle
1139                    };
1140
1141                    let count_cast_handle = if count_kind != crate::ScalarKind::Uint {
1142                        ctx.expressions.append(
1143                            crate::Expression::As {
1144                                expr: count_handle,
1145                                kind: crate::ScalarKind::Uint,
1146                                convert: None,
1147                            },
1148                            span,
1149                        )
1150                    } else {
1151                        count_handle
1152                    };
1153
1154                    let expr = crate::Expression::Math {
1155                        fun: crate::MathFunction::InsertBits,
1156                        arg: base_handle,
1157                        arg1: Some(insert_handle),
1158                        arg2: Some(offset_cast_handle),
1159                        arg3: Some(count_cast_handle),
1160                    };
1161                    self.lookup_expression.insert(
1162                        result_id,
1163                        LookupExpression {
1164                            handle: ctx.expressions.append(expr, span),
1165                            type_id: result_type_id,
1166                            block_id,
1167                        },
1168                    );
1169                }
1170                Op::BitFieldSExtract | Op::BitFieldUExtract => {
1171                    inst.expect(6)?;
1172
1173                    let result_type_id = self.next()?;
1174                    let result_id = self.next()?;
1175                    let base_id = self.next()?;
1176                    let offset_id = self.next()?;
1177                    let count_id = self.next()?;
1178                    let base_lexp = self.lookup_expression.lookup(base_id)?;
1179                    let base_handle = get_expr_handle!(base_id, base_lexp);
1180                    let offset_lexp = self.lookup_expression.lookup(offset_id)?;
1181                    let offset_handle = get_expr_handle!(offset_id, offset_lexp);
1182                    let offset_lookup_ty = self.lookup_type.lookup(offset_lexp.type_id)?;
1183                    let count_lexp = self.lookup_expression.lookup(count_id)?;
1184                    let count_handle = get_expr_handle!(count_id, count_lexp);
1185                    let count_lookup_ty = self.lookup_type.lookup(count_lexp.type_id)?;
1186
1187                    let offset_kind = ctx.module.types[offset_lookup_ty.handle]
1188                        .inner
1189                        .scalar_kind()
1190                        .unwrap();
1191                    let count_kind = ctx.module.types[count_lookup_ty.handle]
1192                        .inner
1193                        .scalar_kind()
1194                        .unwrap();
1195
1196                    let offset_cast_handle = if offset_kind != crate::ScalarKind::Uint {
1197                        ctx.expressions.append(
1198                            crate::Expression::As {
1199                                expr: offset_handle,
1200                                kind: crate::ScalarKind::Uint,
1201                                convert: None,
1202                            },
1203                            span,
1204                        )
1205                    } else {
1206                        offset_handle
1207                    };
1208
1209                    let count_cast_handle = if count_kind != crate::ScalarKind::Uint {
1210                        ctx.expressions.append(
1211                            crate::Expression::As {
1212                                expr: count_handle,
1213                                kind: crate::ScalarKind::Uint,
1214                                convert: None,
1215                            },
1216                            span,
1217                        )
1218                    } else {
1219                        count_handle
1220                    };
1221
1222                    let expr = crate::Expression::Math {
1223                        fun: crate::MathFunction::ExtractBits,
1224                        arg: base_handle,
1225                        arg1: Some(offset_cast_handle),
1226                        arg2: Some(count_cast_handle),
1227                        arg3: None,
1228                    };
1229                    self.lookup_expression.insert(
1230                        result_id,
1231                        LookupExpression {
1232                            handle: ctx.expressions.append(expr, span),
1233                            type_id: result_type_id,
1234                            block_id,
1235                        },
1236                    );
1237                }
1238                Op::BitReverse | Op::BitCount => {
1239                    inst.expect(4)?;
1240
1241                    let result_type_id = self.next()?;
1242                    let result_id = self.next()?;
1243                    let base_id = self.next()?;
1244                    let base_lexp = self.lookup_expression.lookup(base_id)?;
1245                    let base_handle = get_expr_handle!(base_id, base_lexp);
1246                    let expr = crate::Expression::Math {
1247                        fun: match inst.op {
1248                            Op::BitReverse => crate::MathFunction::ReverseBits,
1249                            Op::BitCount => crate::MathFunction::CountOneBits,
1250                            _ => unreachable!(),
1251                        },
1252                        arg: base_handle,
1253                        arg1: None,
1254                        arg2: None,
1255                        arg3: None,
1256                    };
1257                    self.lookup_expression.insert(
1258                        result_id,
1259                        LookupExpression {
1260                            handle: ctx.expressions.append(expr, span),
1261                            type_id: result_type_id,
1262                            block_id,
1263                        },
1264                    );
1265                }
1266                Op::OuterProduct => {
1267                    inst.expect(5)?;
1268
1269                    let result_type_id = self.next()?;
1270                    let result_id = self.next()?;
1271                    let left_id = self.next()?;
1272                    let right_id = self.next()?;
1273                    let left_lexp = self.lookup_expression.lookup(left_id)?;
1274                    let left_handle = get_expr_handle!(left_id, left_lexp);
1275                    let right_lexp = self.lookup_expression.lookup(right_id)?;
1276                    let right_handle = get_expr_handle!(right_id, right_lexp);
1277                    let expr = crate::Expression::Math {
1278                        fun: crate::MathFunction::Outer,
1279                        arg: left_handle,
1280                        arg1: Some(right_handle),
1281                        arg2: None,
1282                        arg3: None,
1283                    };
1284                    self.lookup_expression.insert(
1285                        result_id,
1286                        LookupExpression {
1287                            handle: ctx.expressions.append(expr, span),
1288                            type_id: result_type_id,
1289                            block_id,
1290                        },
1291                    );
1292                }
1293                // Bitwise instructions
1294                Op::Not => {
1295                    inst.expect(4)?;
1296                    self.parse_expr_unary_op_sign_adjusted(
1297                        ctx,
1298                        &mut emitter,
1299                        &mut block,
1300                        block_id,
1301                        body_idx,
1302                        crate::UnaryOperator::BitwiseNot,
1303                    )?;
1304                }
1305                Op::ShiftRightLogical => {
1306                    inst.expect(5)?;
1307                    //TODO: convert input and result to unsigned
1308                    parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;
1309                }
1310                Op::ShiftRightArithmetic => {
1311                    inst.expect(5)?;
1312                    //TODO: convert input and result to signed
1313                    parse_expr_op!(crate::BinaryOperator::ShiftRight, SHIFT)?;
1314                }
1315                Op::ShiftLeftLogical => {
1316                    inst.expect(5)?;
1317                    parse_expr_op!(crate::BinaryOperator::ShiftLeft, SHIFT)?;
1318                }
1319                // Sampling
1320                Op::Image => {
1321                    inst.expect(4)?;
1322                    self.parse_image_uncouple(block_id)?;
1323                }
1324                Op::SampledImage => {
1325                    inst.expect(5)?;
1326                    self.parse_image_couple()?;
1327                }
1328                Op::ImageWrite => {
1329                    let extra = inst.expect_at_least(4)?;
1330                    let stmt =
1331                        self.parse_image_write(extra, ctx, &mut emitter, &mut block, body_idx)?;
1332                    block.extend(emitter.finish(ctx.expressions));
1333                    block.push(stmt, span);
1334                    emitter.start(ctx.expressions);
1335                }
1336                Op::ImageFetch | Op::ImageRead => {
1337                    let extra = inst.expect_at_least(5)?;
1338                    self.parse_image_load(
1339                        extra,
1340                        ctx,
1341                        &mut emitter,
1342                        &mut block,
1343                        block_id,
1344                        body_idx,
1345                    )?;
1346                }
1347                Op::ImageSampleImplicitLod | Op::ImageSampleExplicitLod => {
1348                    let extra = inst.expect_at_least(5)?;
1349                    let options = image::SamplingOptions {
1350                        compare: false,
1351                        project: false,
1352                        gather: false,
1353                    };
1354                    self.parse_image_sample(
1355                        extra,
1356                        options,
1357                        ctx,
1358                        &mut emitter,
1359                        &mut block,
1360                        block_id,
1361                        body_idx,
1362                    )?;
1363                }
1364                Op::ImageSampleProjImplicitLod | Op::ImageSampleProjExplicitLod => {
1365                    let extra = inst.expect_at_least(5)?;
1366                    let options = image::SamplingOptions {
1367                        compare: false,
1368                        project: true,
1369                        gather: false,
1370                    };
1371                    self.parse_image_sample(
1372                        extra,
1373                        options,
1374                        ctx,
1375                        &mut emitter,
1376                        &mut block,
1377                        block_id,
1378                        body_idx,
1379                    )?;
1380                }
1381                Op::ImageSampleDrefImplicitLod | Op::ImageSampleDrefExplicitLod => {
1382                    let extra = inst.expect_at_least(6)?;
1383                    let options = image::SamplingOptions {
1384                        compare: true,
1385                        project: false,
1386                        gather: false,
1387                    };
1388                    self.parse_image_sample(
1389                        extra,
1390                        options,
1391                        ctx,
1392                        &mut emitter,
1393                        &mut block,
1394                        block_id,
1395                        body_idx,
1396                    )?;
1397                }
1398                Op::ImageSampleProjDrefImplicitLod | Op::ImageSampleProjDrefExplicitLod => {
1399                    let extra = inst.expect_at_least(6)?;
1400                    let options = image::SamplingOptions {
1401                        compare: true,
1402                        project: true,
1403                        gather: false,
1404                    };
1405                    self.parse_image_sample(
1406                        extra,
1407                        options,
1408                        ctx,
1409                        &mut emitter,
1410                        &mut block,
1411                        block_id,
1412                        body_idx,
1413                    )?;
1414                }
1415                Op::ImageGather => {
1416                    let extra = inst.expect_at_least(6)?;
1417                    let options = image::SamplingOptions {
1418                        compare: false,
1419                        project: false,
1420                        gather: true,
1421                    };
1422                    self.parse_image_sample(
1423                        extra,
1424                        options,
1425                        ctx,
1426                        &mut emitter,
1427                        &mut block,
1428                        block_id,
1429                        body_idx,
1430                    )?;
1431                }
1432                Op::ImageDrefGather => {
1433                    let extra = inst.expect_at_least(6)?;
1434                    let options = image::SamplingOptions {
1435                        compare: true,
1436                        project: false,
1437                        gather: true,
1438                    };
1439                    self.parse_image_sample(
1440                        extra,
1441                        options,
1442                        ctx,
1443                        &mut emitter,
1444                        &mut block,
1445                        block_id,
1446                        body_idx,
1447                    )?;
1448                }
1449                Op::ImageQuerySize => {
1450                    inst.expect(4)?;
1451                    self.parse_image_query_size(
1452                        false,
1453                        ctx,
1454                        &mut emitter,
1455                        &mut block,
1456                        block_id,
1457                        body_idx,
1458                    )?;
1459                }
1460                Op::ImageQuerySizeLod => {
1461                    inst.expect(5)?;
1462                    self.parse_image_query_size(
1463                        true,
1464                        ctx,
1465                        &mut emitter,
1466                        &mut block,
1467                        block_id,
1468                        body_idx,
1469                    )?;
1470                }
1471                Op::ImageQueryLevels => {
1472                    inst.expect(4)?;
1473                    self.parse_image_query_other(crate::ImageQuery::NumLevels, ctx, block_id)?;
1474                }
1475                Op::ImageQuerySamples => {
1476                    inst.expect(4)?;
1477                    self.parse_image_query_other(crate::ImageQuery::NumSamples, ctx, block_id)?;
1478                }
1479                // other ops
1480                Op::Select => {
1481                    inst.expect(6)?;
1482                    let result_type_id = self.next()?;
1483                    let result_id = self.next()?;
1484                    let condition = self.next()?;
1485                    let o1_id = self.next()?;
1486                    let o2_id = self.next()?;
1487
1488                    let cond_lexp = self.lookup_expression.lookup(condition)?;
1489                    let cond_handle = get_expr_handle!(condition, cond_lexp);
1490                    let o1_lexp = self.lookup_expression.lookup(o1_id)?;
1491                    let o1_handle = get_expr_handle!(o1_id, o1_lexp);
1492                    let o2_lexp = self.lookup_expression.lookup(o2_id)?;
1493                    let o2_handle = get_expr_handle!(o2_id, o2_lexp);
1494
1495                    let expr = crate::Expression::Select {
1496                        condition: cond_handle,
1497                        accept: o1_handle,
1498                        reject: o2_handle,
1499                    };
1500                    self.lookup_expression.insert(
1501                        result_id,
1502                        LookupExpression {
1503                            handle: ctx.expressions.append(expr, span),
1504                            type_id: result_type_id,
1505                            block_id,
1506                        },
1507                    );
1508                }
1509                Op::VectorShuffle => {
1510                    inst.expect_at_least(5)?;
1511                    let result_type_id = self.next()?;
1512                    let result_id = self.next()?;
1513                    let v1_id = self.next()?;
1514                    let v2_id = self.next()?;
1515
1516                    let v1_lexp = self.lookup_expression.lookup(v1_id)?;
1517                    let v1_lty = self.lookup_type.lookup(v1_lexp.type_id)?;
1518                    let v1_handle = get_expr_handle!(v1_id, v1_lexp);
1519                    let n1 = match ctx.module.types[v1_lty.handle].inner {
1520                        crate::TypeInner::Vector { size, .. } => size as u32,
1521                        _ => return Err(Error::InvalidInnerType(v1_lexp.type_id)),
1522                    };
1523                    let v2_lexp = self.lookup_expression.lookup(v2_id)?;
1524                    let v2_lty = self.lookup_type.lookup(v2_lexp.type_id)?;
1525                    let v2_handle = get_expr_handle!(v2_id, v2_lexp);
1526                    let n2 = match ctx.module.types[v2_lty.handle].inner {
1527                        crate::TypeInner::Vector { size, .. } => size as u32,
1528                        _ => return Err(Error::InvalidInnerType(v2_lexp.type_id)),
1529                    };
1530
1531                    self.temp_bytes.clear();
1532                    let mut max_component = 0;
1533                    for _ in 5..inst.wc as usize {
1534                        let mut index = self.next()?;
1535                        if index == u32::MAX {
1536                            // treat Undefined as X
1537                            index = 0;
1538                        }
1539                        max_component = max_component.max(index);
1540                        self.temp_bytes.push(index as u8);
1541                    }
1542
1543                    // Check for swizzle first.
1544                    let expr = if max_component < n1 {
1545                        use crate::SwizzleComponent as Sc;
1546                        let size = match self.temp_bytes.len() {
1547                            2 => crate::VectorSize::Bi,
1548                            3 => crate::VectorSize::Tri,
1549                            _ => crate::VectorSize::Quad,
1550                        };
1551                        let mut pattern = [Sc::X; 4];
1552                        for (pat, index) in pattern.iter_mut().zip(self.temp_bytes.drain(..)) {
1553                            *pat = match index {
1554                                0 => Sc::X,
1555                                1 => Sc::Y,
1556                                2 => Sc::Z,
1557                                _ => Sc::W,
1558                            };
1559                        }
1560                        crate::Expression::Swizzle {
1561                            size,
1562                            vector: v1_handle,
1563                            pattern,
1564                        }
1565                    } else {
1566                        // Fall back to access + compose
1567                        let mut components = Vec::with_capacity(self.temp_bytes.len());
1568                        for index in self.temp_bytes.drain(..).map(|i| i as u32) {
1569                            let expr = if index < n1 {
1570                                crate::Expression::AccessIndex {
1571                                    base: v1_handle,
1572                                    index,
1573                                }
1574                            } else if index < n1 + n2 {
1575                                crate::Expression::AccessIndex {
1576                                    base: v2_handle,
1577                                    index: index - n1,
1578                                }
1579                            } else {
1580                                return Err(Error::InvalidAccessIndex(index));
1581                            };
1582                            components.push(ctx.expressions.append(expr, span));
1583                        }
1584                        crate::Expression::Compose {
1585                            ty: self.lookup_type.lookup(result_type_id)?.handle,
1586                            components,
1587                        }
1588                    };
1589
1590                    self.lookup_expression.insert(
1591                        result_id,
1592                        LookupExpression {
1593                            handle: ctx.expressions.append(expr, span),
1594                            type_id: result_type_id,
1595                            block_id,
1596                        },
1597                    );
1598                }
1599                Op::Bitcast
1600                | Op::ConvertSToF
1601                | Op::ConvertUToF
1602                | Op::ConvertFToU
1603                | Op::ConvertFToS
1604                | Op::FConvert
1605                | Op::UConvert
1606                | Op::SConvert => {
1607                    inst.expect(4)?;
1608                    let result_type_id = self.next()?;
1609                    let result_id = self.next()?;
1610                    let value_id = self.next()?;
1611
1612                    let value_lexp = self.lookup_expression.lookup(value_id)?;
1613                    let ty_lookup = self.lookup_type.lookup(result_type_id)?;
1614                    let scalar = match ctx.module.types[ty_lookup.handle].inner {
1615                        crate::TypeInner::Scalar(scalar)
1616                        | crate::TypeInner::Vector { scalar, .. }
1617                        | crate::TypeInner::Matrix { scalar, .. } => scalar,
1618                        _ => return Err(Error::InvalidAsType(ty_lookup.handle)),
1619                    };
1620
1621                    let expr = crate::Expression::As {
1622                        expr: get_expr_handle!(value_id, value_lexp),
1623                        kind: scalar.kind,
1624                        convert: if scalar.kind == crate::ScalarKind::Bool {
1625                            Some(crate::BOOL_WIDTH)
1626                        } else if inst.op == Op::Bitcast {
1627                            None
1628                        } else {
1629                            Some(scalar.width)
1630                        },
1631                    };
1632                    self.lookup_expression.insert(
1633                        result_id,
1634                        LookupExpression {
1635                            handle: ctx.expressions.append(expr, span),
1636                            type_id: result_type_id,
1637                            block_id,
1638                        },
1639                    );
1640                }
1641                Op::FunctionCall => {
1642                    inst.expect_at_least(4)?;
1643
1644                    let result_type_id = self.next()?;
1645                    let result_id = self.next()?;
1646                    let func_id = self.next()?;
1647
1648                    let mut arguments = Vec::with_capacity(inst.wc as usize - 4);
1649                    for _ in 0..arguments.capacity() {
1650                        let arg_id = self.next()?;
1651                        let lexp = self.lookup_expression.lookup(arg_id)?;
1652                        arguments.push(get_expr_handle!(arg_id, lexp));
1653                    }
1654
1655                    block.extend(emitter.finish(ctx.expressions));
1656
1657                    // We just need an unique handle here, nothing more.
1658                    let function = self.add_call(ctx.function_id, func_id);
1659
1660                    let result = if self.lookup_void_type == Some(result_type_id) {
1661                        None
1662                    } else {
1663                        let expr_handle = ctx
1664                            .expressions
1665                            .append(crate::Expression::CallResult(function), span);
1666                        self.lookup_expression.insert(
1667                            result_id,
1668                            LookupExpression {
1669                                handle: expr_handle,
1670                                type_id: result_type_id,
1671                                block_id,
1672                            },
1673                        );
1674                        Some(expr_handle)
1675                    };
1676                    block.push(
1677                        crate::Statement::Call {
1678                            function,
1679                            arguments,
1680                            result,
1681                        },
1682                        span,
1683                    );
1684                    emitter.start(ctx.expressions);
1685                }
1686                Op::ExtInst => {
1687                    use crate::MathFunction as Mf;
1688                    use spirv::GLOp as Glo;
1689
1690                    let base_wc = 5;
1691                    inst.expect_at_least(base_wc)?;
1692
1693                    let result_type_id = self.next()?;
1694                    let result_id = self.next()?;
1695                    let set_id = self.next()?;
1696                    if Some(set_id) == self.ext_non_semantic_id {
1697                        for _ in 0..inst.wc - 4 {
1698                            self.next()?;
1699                        }
1700                        continue;
1701                    } else if Some(set_id) != self.ext_glsl_id {
1702                        return Err(Error::UnsupportedExtInstSet(set_id));
1703                    }
1704                    let inst_id = self.next()?;
1705                    let gl_op = Glo::from_u32(inst_id).ok_or(Error::UnsupportedExtInst(inst_id))?;
1706
1707                    let fun = match gl_op {
1708                        Glo::Round => Mf::Round,
1709                        Glo::RoundEven => Mf::Round,
1710                        Glo::Trunc => Mf::Trunc,
1711                        Glo::FAbs | Glo::SAbs => Mf::Abs,
1712                        Glo::FSign | Glo::SSign => Mf::Sign,
1713                        Glo::Floor => Mf::Floor,
1714                        Glo::Ceil => Mf::Ceil,
1715                        Glo::Fract => Mf::Fract,
1716                        Glo::Sin => Mf::Sin,
1717                        Glo::Cos => Mf::Cos,
1718                        Glo::Tan => Mf::Tan,
1719                        Glo::Asin => Mf::Asin,
1720                        Glo::Acos => Mf::Acos,
1721                        Glo::Atan => Mf::Atan,
1722                        Glo::Sinh => Mf::Sinh,
1723                        Glo::Cosh => Mf::Cosh,
1724                        Glo::Tanh => Mf::Tanh,
1725                        Glo::Atan2 => Mf::Atan2,
1726                        Glo::Asinh => Mf::Asinh,
1727                        Glo::Acosh => Mf::Acosh,
1728                        Glo::Atanh => Mf::Atanh,
1729                        Glo::Radians => Mf::Radians,
1730                        Glo::Degrees => Mf::Degrees,
1731                        Glo::Pow => Mf::Pow,
1732                        Glo::Exp => Mf::Exp,
1733                        Glo::Log => Mf::Log,
1734                        Glo::Exp2 => Mf::Exp2,
1735                        Glo::Log2 => Mf::Log2,
1736                        Glo::Sqrt => Mf::Sqrt,
1737                        Glo::InverseSqrt => Mf::InverseSqrt,
1738                        Glo::MatrixInverse => Mf::Inverse,
1739                        Glo::Determinant => Mf::Determinant,
1740                        Glo::ModfStruct => Mf::Modf,
1741                        Glo::FMin | Glo::UMin | Glo::SMin | Glo::NMin => Mf::Min,
1742                        Glo::FMax | Glo::UMax | Glo::SMax | Glo::NMax => Mf::Max,
1743                        Glo::FClamp | Glo::UClamp | Glo::SClamp | Glo::NClamp => Mf::Clamp,
1744                        Glo::FMix => Mf::Mix,
1745                        Glo::Step => Mf::Step,
1746                        Glo::SmoothStep => Mf::SmoothStep,
1747                        Glo::Fma => Mf::Fma,
1748                        Glo::FrexpStruct => Mf::Frexp,
1749                        Glo::Ldexp => Mf::Ldexp,
1750                        Glo::Length => Mf::Length,
1751                        Glo::Distance => Mf::Distance,
1752                        Glo::Cross => Mf::Cross,
1753                        Glo::Normalize => Mf::Normalize,
1754                        Glo::FaceForward => Mf::FaceForward,
1755                        Glo::Reflect => Mf::Reflect,
1756                        Glo::Refract => Mf::Refract,
1757                        Glo::PackUnorm4x8 => Mf::Pack4x8unorm,
1758                        Glo::PackSnorm4x8 => Mf::Pack4x8snorm,
1759                        Glo::PackHalf2x16 => Mf::Pack2x16float,
1760                        Glo::PackUnorm2x16 => Mf::Pack2x16unorm,
1761                        Glo::PackSnorm2x16 => Mf::Pack2x16snorm,
1762                        Glo::UnpackUnorm4x8 => Mf::Unpack4x8unorm,
1763                        Glo::UnpackSnorm4x8 => Mf::Unpack4x8snorm,
1764                        Glo::UnpackHalf2x16 => Mf::Unpack2x16float,
1765                        Glo::UnpackUnorm2x16 => Mf::Unpack2x16unorm,
1766                        Glo::UnpackSnorm2x16 => Mf::Unpack2x16snorm,
1767                        Glo::FindILsb => Mf::FirstTrailingBit,
1768                        Glo::FindUMsb | Glo::FindSMsb => Mf::FirstLeadingBit,
1769                        // TODO: https://github.com/gfx-rs/naga/issues/2526
1770                        Glo::Modf | Glo::Frexp => return Err(Error::UnsupportedExtInst(inst_id)),
1771                        Glo::IMix
1772                        | Glo::PackDouble2x32
1773                        | Glo::UnpackDouble2x32
1774                        | Glo::InterpolateAtCentroid
1775                        | Glo::InterpolateAtSample
1776                        | Glo::InterpolateAtOffset => {
1777                            return Err(Error::UnsupportedExtInst(inst_id))
1778                        }
1779                    };
1780
1781                    let arg_count = fun.argument_count();
1782                    inst.expect(base_wc + arg_count as u16)?;
1783                    let arg = {
1784                        let arg_id = self.next()?;
1785                        let lexp = self.lookup_expression.lookup(arg_id)?;
1786                        get_expr_handle!(arg_id, lexp)
1787                    };
1788                    let arg1 = if arg_count > 1 {
1789                        let arg_id = self.next()?;
1790                        let lexp = self.lookup_expression.lookup(arg_id)?;
1791                        Some(get_expr_handle!(arg_id, lexp))
1792                    } else {
1793                        None
1794                    };
1795                    let arg2 = if arg_count > 2 {
1796                        let arg_id = self.next()?;
1797                        let lexp = self.lookup_expression.lookup(arg_id)?;
1798                        Some(get_expr_handle!(arg_id, lexp))
1799                    } else {
1800                        None
1801                    };
1802                    let arg3 = if arg_count > 3 {
1803                        let arg_id = self.next()?;
1804                        let lexp = self.lookup_expression.lookup(arg_id)?;
1805                        Some(get_expr_handle!(arg_id, lexp))
1806                    } else {
1807                        None
1808                    };
1809
1810                    let expr = crate::Expression::Math {
1811                        fun,
1812                        arg,
1813                        arg1,
1814                        arg2,
1815                        arg3,
1816                    };
1817                    self.lookup_expression.insert(
1818                        result_id,
1819                        LookupExpression {
1820                            handle: ctx.expressions.append(expr, span),
1821                            type_id: result_type_id,
1822                            block_id,
1823                        },
1824                    );
1825                }
1826                // Relational and Logical Instructions
1827                Op::LogicalNot => {
1828                    inst.expect(4)?;
1829                    parse_expr_op!(crate::UnaryOperator::LogicalNot, UNARY)?;
1830                }
1831                Op::LogicalOr => {
1832                    inst.expect(5)?;
1833                    parse_expr_op!(crate::BinaryOperator::LogicalOr, BINARY)?;
1834                }
1835                Op::LogicalAnd => {
1836                    inst.expect(5)?;
1837                    parse_expr_op!(crate::BinaryOperator::LogicalAnd, BINARY)?;
1838                }
1839                Op::SGreaterThan | Op::SGreaterThanEqual | Op::SLessThan | Op::SLessThanEqual => {
1840                    inst.expect(5)?;
1841                    self.parse_expr_int_comparison(
1842                        ctx,
1843                        &mut emitter,
1844                        &mut block,
1845                        block_id,
1846                        body_idx,
1847                        map_binary_operator(inst.op)?,
1848                        crate::ScalarKind::Sint,
1849                    )?;
1850                }
1851                Op::UGreaterThan | Op::UGreaterThanEqual | Op::ULessThan | Op::ULessThanEqual => {
1852                    inst.expect(5)?;
1853                    self.parse_expr_int_comparison(
1854                        ctx,
1855                        &mut emitter,
1856                        &mut block,
1857                        block_id,
1858                        body_idx,
1859                        map_binary_operator(inst.op)?,
1860                        crate::ScalarKind::Uint,
1861                    )?;
1862                }
1863                Op::FOrdEqual
1864                | Op::FUnordEqual
1865                | Op::FOrdNotEqual
1866                | Op::FUnordNotEqual
1867                | Op::FOrdLessThan
1868                | Op::FUnordLessThan
1869                | Op::FOrdGreaterThan
1870                | Op::FUnordGreaterThan
1871                | Op::FOrdLessThanEqual
1872                | Op::FUnordLessThanEqual
1873                | Op::FOrdGreaterThanEqual
1874                | Op::FUnordGreaterThanEqual
1875                | Op::LogicalEqual
1876                | Op::LogicalNotEqual => {
1877                    inst.expect(5)?;
1878                    let operator = map_binary_operator(inst.op)?;
1879                    parse_expr_op!(operator, BINARY)?;
1880                }
1881                Op::Any | Op::All | Op::IsNan | Op::IsInf | Op::IsFinite | Op::IsNormal => {
1882                    inst.expect(4)?;
1883                    let result_type_id = self.next()?;
1884                    let result_id = self.next()?;
1885                    let arg_id = self.next()?;
1886
1887                    let arg_lexp = self.lookup_expression.lookup(arg_id)?;
1888                    let arg_handle = get_expr_handle!(arg_id, arg_lexp);
1889
1890                    let expr = crate::Expression::Relational {
1891                        fun: map_relational_fun(inst.op)?,
1892                        argument: arg_handle,
1893                    };
1894                    self.lookup_expression.insert(
1895                        result_id,
1896                        LookupExpression {
1897                            handle: ctx.expressions.append(expr, span),
1898                            type_id: result_type_id,
1899                            block_id,
1900                        },
1901                    );
1902                }
1903                Op::Kill => {
1904                    inst.expect(1)?;
1905                    break Some(crate::Statement::Kill);
1906                }
1907                Op::Unreachable => {
1908                    inst.expect(1)?;
1909                    break None;
1910                }
1911                Op::Return => {
1912                    inst.expect(1)?;
1913                    break Some(crate::Statement::Return { value: None });
1914                }
1915                Op::ReturnValue => {
1916                    inst.expect(2)?;
1917                    let value_id = self.next()?;
1918                    let value_lexp = self.lookup_expression.lookup(value_id)?;
1919                    let value_handle = get_expr_handle!(value_id, value_lexp);
1920                    break Some(crate::Statement::Return {
1921                        value: Some(value_handle),
1922                    });
1923                }
1924                Op::Branch => {
1925                    inst.expect(2)?;
1926                    let target_id = self.next()?;
1927
1928                    // If this is a branch to a merge or continue block, then
1929                    // that ends the current body.
1930                    //
1931                    // Why can we count on finding an entry here when it's
1932                    // needed? SPIR-V requires dominators to appear before
1933                    // blocks they dominate, so we will have visited a
1934                    // structured control construct's header block before
1935                    // anything that could exit it.
1936                    if let Some(info) = ctx.mergers.get(&target_id) {
1937                        block.extend(emitter.finish(ctx.expressions));
1938                        ctx.blocks.insert(block_id, block);
1939                        let body = &mut ctx.bodies[body_idx];
1940                        body.data.push(BodyFragment::BlockId(block_id));
1941
1942                        merger(body, info);
1943
1944                        return Ok(());
1945                    }
1946
1947                    // If `target_id` has no entry in `ctx.body_for_label`, then
1948                    // this must be the only branch to it:
1949                    //
1950                    // - We've already established that it's not anybody's merge
1951                    //   block.
1952                    //
1953                    // - It can't be a switch case. Only switch header blocks
1954                    //   and other switch cases can branch to a switch case.
1955                    //   Switch header blocks must dominate all their cases, so
1956                    //   they must appear in the file before them, and when we
1957                    //   see `Op::Switch` we populate `ctx.body_for_label` for
1958                    //   every switch case.
1959                    //
1960                    // Thus, `target_id` must be a simple extension of the
1961                    // current block, which we dominate, so we know we'll
1962                    // encounter it later in the file.
1963                    ctx.body_for_label.entry(target_id).or_insert(body_idx);
1964
1965                    break None;
1966                }
1967                Op::BranchConditional => {
1968                    inst.expect_at_least(4)?;
1969
1970                    let condition = {
1971                        let condition_id = self.next()?;
1972                        let lexp = self.lookup_expression.lookup(condition_id)?;
1973                        get_expr_handle!(condition_id, lexp)
1974                    };
1975
1976                    // HACK(eddyb) Naga doesn't seem to have this helper,
1977                    // so it's declared on the fly here for convenience.
1978                    #[derive(Copy, Clone)]
1979                    struct BranchTarget {
1980                        label_id: spirv::Word,
1981                        merge_info: Option<MergeBlockInformation>,
1982                    }
1983                    let branch_target = |label_id| BranchTarget {
1984                        label_id,
1985                        merge_info: ctx.mergers.get(&label_id).copied(),
1986                    };
1987
1988                    let true_target = branch_target(self.next()?);
1989                    let false_target = branch_target(self.next()?);
1990
1991                    // Consume branch weights
1992                    for _ in 4..inst.wc {
1993                        let _ = self.next()?;
1994                    }
1995
1996                    // Handle `OpBranchConditional`s used at the end of a loop
1997                    // body's "continuing" section as a "conditional backedge",
1998                    // i.e. a `do`-`while` condition, or `break if` in WGSL.
1999
2000                    // HACK(eddyb) this has to go to the parent *twice*, because
2001                    // `OpLoopMerge` left the "continuing" section nested in the
2002                    // loop body in terms of `parent`, but not `BodyFragment`.
2003                    let parent_body_idx = ctx.bodies[body_idx].parent;
2004                    let parent_parent_body_idx = ctx.bodies[parent_body_idx].parent;
2005                    match ctx.bodies[parent_parent_body_idx].data[..] {
2006                        // The `OpLoopMerge`'s `continuing` block and the loop's
2007                        // backedge block may not be the same, but they'll both
2008                        // belong to the same body.
2009                        [.., BodyFragment::Loop {
2010                            body: loop_body_idx,
2011                            continuing: loop_continuing_idx,
2012                            break_if: ref mut break_if_slot @ None,
2013                        }] if body_idx == loop_continuing_idx => {
2014                            // Try both orderings of break-vs-backedge, because
2015                            // SPIR-V is symmetrical here, unlike WGSL `break if`.
2016                            let break_if_cond = [true, false].into_iter().find_map(|true_breaks| {
2017                                let (break_candidate, backedge_candidate) = if true_breaks {
2018                                    (true_target, false_target)
2019                                } else {
2020                                    (false_target, true_target)
2021                                };
2022
2023                                if break_candidate.merge_info
2024                                    != Some(MergeBlockInformation::LoopMerge)
2025                                {
2026                                    return None;
2027                                }
2028
2029                                // HACK(eddyb) since Naga doesn't explicitly track
2030                                // backedges, this is checking for the outcome of
2031                                // `OpLoopMerge` below (even if it looks weird).
2032                                let backedge_candidate_is_backedge =
2033                                    backedge_candidate.merge_info.is_none()
2034                                        && ctx.body_for_label.get(&backedge_candidate.label_id)
2035                                            == Some(&loop_body_idx);
2036                                if !backedge_candidate_is_backedge {
2037                                    return None;
2038                                }
2039
2040                                Some(if true_breaks {
2041                                    condition
2042                                } else {
2043                                    ctx.expressions.append(
2044                                        crate::Expression::Unary {
2045                                            op: crate::UnaryOperator::LogicalNot,
2046                                            expr: condition,
2047                                        },
2048                                        span,
2049                                    )
2050                                })
2051                            });
2052
2053                            if let Some(break_if_cond) = break_if_cond {
2054                                *break_if_slot = Some(break_if_cond);
2055
2056                                // This `OpBranchConditional` ends the "continuing"
2057                                // section of the loop body as normal, with the
2058                                // `break if` condition having been stashed above.
2059                                break None;
2060                            }
2061                        }
2062                        _ => {}
2063                    }
2064
2065                    block.extend(emitter.finish(ctx.expressions));
2066                    ctx.blocks.insert(block_id, block);
2067                    let body = &mut ctx.bodies[body_idx];
2068                    body.data.push(BodyFragment::BlockId(block_id));
2069
2070                    let same_target = true_target.label_id == false_target.label_id;
2071
2072                    // Start a body block for the `accept` branch.
2073                    let accept = ctx.bodies.len();
2074                    let mut accept_block = Body::with_parent(body_idx);
2075
2076                    // If the `OpBranchConditional` target is somebody else's
2077                    // merge or continue block, then put a `Break` or `Continue`
2078                    // statement in this new body block.
2079                    if let Some(info) = true_target.merge_info {
2080                        merger(
2081                            match same_target {
2082                                true => &mut ctx.bodies[body_idx],
2083                                false => &mut accept_block,
2084                            },
2085                            &info,
2086                        )
2087                    } else {
2088                        // Note the body index for the block we're branching to.
2089                        let prev = ctx.body_for_label.insert(
2090                            true_target.label_id,
2091                            match same_target {
2092                                true => body_idx,
2093                                false => accept,
2094                            },
2095                        );
2096                        debug_assert!(prev.is_none());
2097                    }
2098
2099                    if same_target {
2100                        return Ok(());
2101                    }
2102
2103                    ctx.bodies.push(accept_block);
2104
2105                    // Handle the `reject` branch just like the `accept` block.
2106                    let reject = ctx.bodies.len();
2107                    let mut reject_block = Body::with_parent(body_idx);
2108
2109                    if let Some(info) = false_target.merge_info {
2110                        merger(&mut reject_block, &info)
2111                    } else {
2112                        let prev = ctx.body_for_label.insert(false_target.label_id, reject);
2113                        debug_assert!(prev.is_none());
2114                    }
2115
2116                    ctx.bodies.push(reject_block);
2117
2118                    let body = &mut ctx.bodies[body_idx];
2119                    body.data.push(BodyFragment::If {
2120                        condition,
2121                        accept,
2122                        reject,
2123                    });
2124
2125                    return Ok(());
2126                }
2127                Op::Switch => {
2128                    inst.expect_at_least(3)?;
2129                    let selector = self.next()?;
2130                    let default_id = self.next()?;
2131
2132                    // If the previous instruction was a `OpSelectionMerge` then we must
2133                    // promote the `MergeBlockInformation` to a `SwitchMerge`
2134                    if let Some(merge) = selection_merge_block {
2135                        ctx.mergers
2136                            .insert(merge, MergeBlockInformation::SwitchMerge);
2137                    }
2138
2139                    let default = ctx.bodies.len();
2140                    ctx.bodies.push(Body::with_parent(body_idx));
2141                    ctx.body_for_label.entry(default_id).or_insert(default);
2142
2143                    let selector_lexp = &self.lookup_expression[&selector];
2144                    let selector_lty = self.lookup_type.lookup(selector_lexp.type_id)?;
2145                    let selector_handle = get_expr_handle!(selector, selector_lexp);
2146                    let selector = match ctx.module.types[selector_lty.handle].inner {
2147                        crate::TypeInner::Scalar(crate::Scalar {
2148                            kind: crate::ScalarKind::Uint,
2149                            width: _,
2150                        }) => {
2151                            // IR expects a signed integer, so do a bitcast
2152                            ctx.expressions.append(
2153                                crate::Expression::As {
2154                                    kind: crate::ScalarKind::Sint,
2155                                    expr: selector_handle,
2156                                    convert: None,
2157                                },
2158                                span,
2159                            )
2160                        }
2161                        crate::TypeInner::Scalar(crate::Scalar {
2162                            kind: crate::ScalarKind::Sint,
2163                            width: _,
2164                        }) => selector_handle,
2165                        ref other => unimplemented!("Unexpected selector {:?}", other),
2166                    };
2167
2168                    // Clear past switch cases to prevent them from entering this one
2169                    self.switch_cases.clear();
2170
2171                    for _ in 0..(inst.wc - 3) / 2 {
2172                        let literal = self.next()?;
2173                        let target = self.next()?;
2174
2175                        let case_body_idx = ctx.bodies.len();
2176
2177                        // Check if any previous case already used this target block id, if so
2178                        // group them together to reorder them later so that no weird
2179                        // fallthrough cases happen.
2180                        if let Some(&mut (_, ref mut literals)) = self.switch_cases.get_mut(&target)
2181                        {
2182                            literals.push(literal as i32);
2183                            continue;
2184                        }
2185
2186                        let mut body = Body::with_parent(body_idx);
2187
2188                        if let Some(info) = ctx.mergers.get(&target) {
2189                            merger(&mut body, info);
2190                        }
2191
2192                        ctx.bodies.push(body);
2193                        ctx.body_for_label.entry(target).or_insert(case_body_idx);
2194
2195                        // Register this target block id as already having been processed and
2196                        // the respective body index assigned and the first case value
2197                        self.switch_cases
2198                            .insert(target, (case_body_idx, vec![literal as i32]));
2199                    }
2200
2201                    // Loop through the collected target blocks creating a new case for each
2202                    // literal pointing to it, only one case will have the true body and all the
2203                    // others will be empty fallthrough so that they all execute the same body
2204                    // without duplicating code.
2205                    //
2206                    // Since `switch_cases` is an indexmap the order of insertion is preserved
2207                    // this is needed because spir-v defines fallthrough order in the switch
2208                    // instruction.
2209                    let mut cases = Vec::with_capacity((inst.wc as usize - 3) / 2);
2210                    for &(case_body_idx, ref literals) in self.switch_cases.values() {
2211                        let value = literals[0];
2212
2213                        for &literal in literals.iter().skip(1) {
2214                            let empty_body_idx = ctx.bodies.len();
2215                            let body = Body::with_parent(body_idx);
2216
2217                            ctx.bodies.push(body);
2218
2219                            cases.push((literal, empty_body_idx));
2220                        }
2221
2222                        cases.push((value, case_body_idx));
2223                    }
2224
2225                    block.extend(emitter.finish(ctx.expressions));
2226
2227                    let body = &mut ctx.bodies[body_idx];
2228                    ctx.blocks.insert(block_id, block);
2229                    // Make sure the vector has space for at least two more allocations
2230                    body.data.reserve(2);
2231                    body.data.push(BodyFragment::BlockId(block_id));
2232                    body.data.push(BodyFragment::Switch {
2233                        selector,
2234                        cases,
2235                        default,
2236                    });
2237
2238                    return Ok(());
2239                }
2240                Op::SelectionMerge => {
2241                    inst.expect(3)?;
2242                    let merge_block_id = self.next()?;
2243                    // TODO: Selection Control Mask
2244                    let _selection_control = self.next()?;
2245
2246                    // Indicate that the merge block is a continuation of the
2247                    // current `Body`.
2248                    ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);
2249
2250                    // Let subsequent branches to the merge block know that
2251                    // they've reached the end of the selection construct.
2252                    ctx.mergers
2253                        .insert(merge_block_id, MergeBlockInformation::SelectionMerge);
2254
2255                    selection_merge_block = Some(merge_block_id);
2256                }
2257                Op::LoopMerge => {
2258                    inst.expect_at_least(4)?;
2259                    let merge_block_id = self.next()?;
2260                    let continuing = self.next()?;
2261
2262                    // TODO: Loop Control Parameters
2263                    for _ in 0..inst.wc - 3 {
2264                        self.next()?;
2265                    }
2266
2267                    // Indicate that the merge block is a continuation of the
2268                    // current `Body`.
2269                    ctx.body_for_label.entry(merge_block_id).or_insert(body_idx);
2270                    // Let subsequent branches to the merge block know that
2271                    // they're `Break` statements.
2272                    ctx.mergers
2273                        .insert(merge_block_id, MergeBlockInformation::LoopMerge);
2274
2275                    let loop_body_idx = ctx.bodies.len();
2276                    ctx.bodies.push(Body::with_parent(body_idx));
2277
2278                    let continue_idx = ctx.bodies.len();
2279                    // The continue block inherits the scope of the loop body
2280                    ctx.bodies.push(Body::with_parent(loop_body_idx));
2281                    ctx.body_for_label.entry(continuing).or_insert(continue_idx);
2282                    // Let subsequent branches to the continue block know that
2283                    // they're `Continue` statements.
2284                    ctx.mergers
2285                        .insert(continuing, MergeBlockInformation::LoopContinue);
2286
2287                    // The loop header always belongs to the loop body
2288                    ctx.body_for_label.insert(block_id, loop_body_idx);
2289
2290                    let parent_body = &mut ctx.bodies[body_idx];
2291                    parent_body.data.push(BodyFragment::Loop {
2292                        body: loop_body_idx,
2293                        continuing: continue_idx,
2294                        break_if: None,
2295                    });
2296                    body_idx = loop_body_idx;
2297                }
2298                Op::DPdxCoarse => {
2299                    parse_expr_op!(
2300                        crate::DerivativeAxis::X,
2301                        crate::DerivativeControl::Coarse,
2302                        DERIVATIVE
2303                    )?;
2304                }
2305                Op::DPdyCoarse => {
2306                    parse_expr_op!(
2307                        crate::DerivativeAxis::Y,
2308                        crate::DerivativeControl::Coarse,
2309                        DERIVATIVE
2310                    )?;
2311                }
2312                Op::FwidthCoarse => {
2313                    parse_expr_op!(
2314                        crate::DerivativeAxis::Width,
2315                        crate::DerivativeControl::Coarse,
2316                        DERIVATIVE
2317                    )?;
2318                }
2319                Op::DPdxFine => {
2320                    parse_expr_op!(
2321                        crate::DerivativeAxis::X,
2322                        crate::DerivativeControl::Fine,
2323                        DERIVATIVE
2324                    )?;
2325                }
2326                Op::DPdyFine => {
2327                    parse_expr_op!(
2328                        crate::DerivativeAxis::Y,
2329                        crate::DerivativeControl::Fine,
2330                        DERIVATIVE
2331                    )?;
2332                }
2333                Op::FwidthFine => {
2334                    parse_expr_op!(
2335                        crate::DerivativeAxis::Width,
2336                        crate::DerivativeControl::Fine,
2337                        DERIVATIVE
2338                    )?;
2339                }
2340                Op::DPdx => {
2341                    parse_expr_op!(
2342                        crate::DerivativeAxis::X,
2343                        crate::DerivativeControl::None,
2344                        DERIVATIVE
2345                    )?;
2346                }
2347                Op::DPdy => {
2348                    parse_expr_op!(
2349                        crate::DerivativeAxis::Y,
2350                        crate::DerivativeControl::None,
2351                        DERIVATIVE
2352                    )?;
2353                }
2354                Op::Fwidth => {
2355                    parse_expr_op!(
2356                        crate::DerivativeAxis::Width,
2357                        crate::DerivativeControl::None,
2358                        DERIVATIVE
2359                    )?;
2360                }
2361                Op::ArrayLength => {
2362                    inst.expect(5)?;
2363                    let result_type_id = self.next()?;
2364                    let result_id = self.next()?;
2365                    let structure_id = self.next()?;
2366                    let member_index = self.next()?;
2367
2368                    // We're assuming that the validation pass, if it's run, will catch if the
2369                    // wrong types or parameters are supplied here.
2370
2371                    let structure_ptr = self.lookup_expression.lookup(structure_id)?;
2372                    let structure_handle = get_expr_handle!(structure_id, structure_ptr);
2373
2374                    let member_ptr = ctx.expressions.append(
2375                        crate::Expression::AccessIndex {
2376                            base: structure_handle,
2377                            index: member_index,
2378                        },
2379                        span,
2380                    );
2381
2382                    let length = ctx
2383                        .expressions
2384                        .append(crate::Expression::ArrayLength(member_ptr), span);
2385
2386                    self.lookup_expression.insert(
2387                        result_id,
2388                        LookupExpression {
2389                            handle: length,
2390                            type_id: result_type_id,
2391                            block_id,
2392                        },
2393                    );
2394                }
2395                Op::CopyMemory => {
2396                    inst.expect_at_least(3)?;
2397                    let target_id = self.next()?;
2398                    let source_id = self.next()?;
2399                    let _memory_access = if inst.wc != 3 {
2400                        inst.expect(4)?;
2401                        spirv::MemoryAccess::from_bits(self.next()?)
2402                            .ok_or(Error::InvalidParameter(Op::CopyMemory))?
2403                    } else {
2404                        spirv::MemoryAccess::NONE
2405                    };
2406
2407                    // TODO: check if the source and target types are the same?
2408                    let target = self.lookup_expression.lookup(target_id)?;
2409                    let target_handle = get_expr_handle!(target_id, target);
2410                    let source = self.lookup_expression.lookup(source_id)?;
2411                    let source_handle = get_expr_handle!(source_id, source);
2412
2413                    // This operation is practically the same as loading and then storing, I think.
2414                    let value_expr = ctx.expressions.append(
2415                        crate::Expression::Load {
2416                            pointer: source_handle,
2417                        },
2418                        span,
2419                    );
2420
2421                    block.extend(emitter.finish(ctx.expressions));
2422                    block.push(
2423                        crate::Statement::Store {
2424                            pointer: target_handle,
2425                            value: value_expr,
2426                        },
2427                        span,
2428                    );
2429
2430                    emitter.start(ctx.expressions);
2431                }
2432                Op::ControlBarrier => {
2433                    inst.expect(4)?;
2434                    let exec_scope_id = self.next()?;
2435                    let _mem_scope_raw = self.next()?;
2436                    let semantics_id = self.next()?;
2437                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
2438                    let semantics_const = self.lookup_constant.lookup(semantics_id)?;
2439
2440                    let exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
2441                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
2442                    let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)
2443                        .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;
2444
2445                    if exec_scope == spirv::Scope::Workgroup as u32
2446                        || exec_scope == spirv::Scope::Subgroup as u32
2447                    {
2448                        let mut flags = crate::Barrier::empty();
2449                        flags.set(
2450                            crate::Barrier::STORAGE,
2451                            semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,
2452                        );
2453                        flags.set(
2454                            crate::Barrier::WORK_GROUP,
2455                            semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,
2456                        );
2457                        flags.set(
2458                            crate::Barrier::SUB_GROUP,
2459                            semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,
2460                        );
2461                        flags.set(
2462                            crate::Barrier::TEXTURE,
2463                            semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,
2464                        );
2465
2466                        block.extend(emitter.finish(ctx.expressions));
2467                        block.push(crate::Statement::ControlBarrier(flags), span);
2468                        emitter.start(ctx.expressions);
2469                    } else {
2470                        log::warn!("Unsupported barrier execution scope: {exec_scope}");
2471                    }
2472                }
2473                Op::MemoryBarrier => {
2474                    inst.expect(3)?;
2475                    let mem_scope_id = self.next()?;
2476                    let semantics_id = self.next()?;
2477                    let mem_scope_const = self.lookup_constant.lookup(mem_scope_id)?;
2478                    let semantics_const = self.lookup_constant.lookup(semantics_id)?;
2479
2480                    let mem_scope = resolve_constant(ctx.gctx(), &mem_scope_const.inner)
2481                        .ok_or(Error::InvalidBarrierScope(mem_scope_id))?;
2482                    let semantics = resolve_constant(ctx.gctx(), &semantics_const.inner)
2483                        .ok_or(Error::InvalidBarrierMemorySemantics(semantics_id))?;
2484
2485                    let mut flags = if mem_scope == spirv::Scope::Device as u32 {
2486                        crate::Barrier::STORAGE
2487                    } else if mem_scope == spirv::Scope::Workgroup as u32 {
2488                        crate::Barrier::WORK_GROUP
2489                    } else if mem_scope == spirv::Scope::Subgroup as u32 {
2490                        crate::Barrier::SUB_GROUP
2491                    } else {
2492                        crate::Barrier::empty()
2493                    };
2494                    flags.set(
2495                        crate::Barrier::STORAGE,
2496                        semantics & spirv::MemorySemantics::UNIFORM_MEMORY.bits() != 0,
2497                    );
2498                    flags.set(
2499                        crate::Barrier::WORK_GROUP,
2500                        semantics & (spirv::MemorySemantics::WORKGROUP_MEMORY).bits() != 0,
2501                    );
2502                    flags.set(
2503                        crate::Barrier::SUB_GROUP,
2504                        semantics & spirv::MemorySemantics::SUBGROUP_MEMORY.bits() != 0,
2505                    );
2506                    flags.set(
2507                        crate::Barrier::TEXTURE,
2508                        semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0,
2509                    );
2510
2511                    block.extend(emitter.finish(ctx.expressions));
2512                    block.push(crate::Statement::MemoryBarrier(flags), span);
2513                    emitter.start(ctx.expressions);
2514                }
2515                Op::CopyObject => {
2516                    inst.expect(4)?;
2517                    let result_type_id = self.next()?;
2518                    let result_id = self.next()?;
2519                    let operand_id = self.next()?;
2520
2521                    let lookup = self.lookup_expression.lookup(operand_id)?;
2522                    let handle = get_expr_handle!(operand_id, lookup);
2523
2524                    self.lookup_expression.insert(
2525                        result_id,
2526                        LookupExpression {
2527                            handle,
2528                            type_id: result_type_id,
2529                            block_id,
2530                        },
2531                    );
2532                }
2533                Op::GroupNonUniformBallot => {
2534                    inst.expect(5)?;
2535                    block.extend(emitter.finish(ctx.expressions));
2536                    let result_type_id = self.next()?;
2537                    let result_id = self.next()?;
2538                    let exec_scope_id = self.next()?;
2539                    let predicate_id = self.next()?;
2540
2541                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
2542                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
2543                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
2544                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
2545
2546                    let predicate = if self
2547                        .lookup_constant
2548                        .lookup(predicate_id)
2549                        .ok()
2550                        .filter(|predicate_const| match predicate_const.inner {
2551                            Constant::Constant(constant) => matches!(
2552                                ctx.gctx().global_expressions[ctx.gctx().constants[constant].init],
2553                                crate::Expression::Literal(crate::Literal::Bool(true)),
2554                            ),
2555                            Constant::Override(_) => false,
2556                        })
2557                        .is_some()
2558                    {
2559                        None
2560                    } else {
2561                        let predicate_lookup = self.lookup_expression.lookup(predicate_id)?;
2562                        let predicate_handle = get_expr_handle!(predicate_id, predicate_lookup);
2563                        Some(predicate_handle)
2564                    };
2565
2566                    let result_handle = ctx
2567                        .expressions
2568                        .append(crate::Expression::SubgroupBallotResult, span);
2569                    self.lookup_expression.insert(
2570                        result_id,
2571                        LookupExpression {
2572                            handle: result_handle,
2573                            type_id: result_type_id,
2574                            block_id,
2575                        },
2576                    );
2577
2578                    block.push(
2579                        crate::Statement::SubgroupBallot {
2580                            result: result_handle,
2581                            predicate,
2582                        },
2583                        span,
2584                    );
2585                    emitter.start(ctx.expressions);
2586                }
2587                Op::GroupNonUniformAll
2588                | Op::GroupNonUniformAny
2589                | Op::GroupNonUniformIAdd
2590                | Op::GroupNonUniformFAdd
2591                | Op::GroupNonUniformIMul
2592                | Op::GroupNonUniformFMul
2593                | Op::GroupNonUniformSMax
2594                | Op::GroupNonUniformUMax
2595                | Op::GroupNonUniformFMax
2596                | Op::GroupNonUniformSMin
2597                | Op::GroupNonUniformUMin
2598                | Op::GroupNonUniformFMin
2599                | Op::GroupNonUniformBitwiseAnd
2600                | Op::GroupNonUniformBitwiseOr
2601                | Op::GroupNonUniformBitwiseXor
2602                | Op::GroupNonUniformLogicalAnd
2603                | Op::GroupNonUniformLogicalOr
2604                | Op::GroupNonUniformLogicalXor => {
2605                    block.extend(emitter.finish(ctx.expressions));
2606                    inst.expect(
2607                        if matches!(inst.op, Op::GroupNonUniformAll | Op::GroupNonUniformAny) {
2608                            5
2609                        } else {
2610                            6
2611                        },
2612                    )?;
2613                    let result_type_id = self.next()?;
2614                    let result_id = self.next()?;
2615                    let exec_scope_id = self.next()?;
2616                    let collective_op_id = match inst.op {
2617                        Op::GroupNonUniformAll | Op::GroupNonUniformAny => {
2618                            crate::CollectiveOperation::Reduce
2619                        }
2620                        _ => {
2621                            let group_op_id = self.next()?;
2622                            match spirv::GroupOperation::from_u32(group_op_id) {
2623                                Some(spirv::GroupOperation::Reduce) => {
2624                                    crate::CollectiveOperation::Reduce
2625                                }
2626                                Some(spirv::GroupOperation::InclusiveScan) => {
2627                                    crate::CollectiveOperation::InclusiveScan
2628                                }
2629                                Some(spirv::GroupOperation::ExclusiveScan) => {
2630                                    crate::CollectiveOperation::ExclusiveScan
2631                                }
2632                                _ => return Err(Error::UnsupportedGroupOperation(group_op_id)),
2633                            }
2634                        }
2635                    };
2636                    let argument_id = self.next()?;
2637
2638                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;
2639                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);
2640
2641                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
2642                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
2643                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
2644                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
2645
2646                    let op_id = match inst.op {
2647                        Op::GroupNonUniformAll => crate::SubgroupOperation::All,
2648                        Op::GroupNonUniformAny => crate::SubgroupOperation::Any,
2649                        Op::GroupNonUniformIAdd | Op::GroupNonUniformFAdd => {
2650                            crate::SubgroupOperation::Add
2651                        }
2652                        Op::GroupNonUniformIMul | Op::GroupNonUniformFMul => {
2653                            crate::SubgroupOperation::Mul
2654                        }
2655                        Op::GroupNonUniformSMax
2656                        | Op::GroupNonUniformUMax
2657                        | Op::GroupNonUniformFMax => crate::SubgroupOperation::Max,
2658                        Op::GroupNonUniformSMin
2659                        | Op::GroupNonUniformUMin
2660                        | Op::GroupNonUniformFMin => crate::SubgroupOperation::Min,
2661                        Op::GroupNonUniformBitwiseAnd | Op::GroupNonUniformLogicalAnd => {
2662                            crate::SubgroupOperation::And
2663                        }
2664                        Op::GroupNonUniformBitwiseOr | Op::GroupNonUniformLogicalOr => {
2665                            crate::SubgroupOperation::Or
2666                        }
2667                        Op::GroupNonUniformBitwiseXor | Op::GroupNonUniformLogicalXor => {
2668                            crate::SubgroupOperation::Xor
2669                        }
2670                        _ => unreachable!(),
2671                    };
2672
2673                    let result_type = self.lookup_type.lookup(result_type_id)?;
2674
2675                    let result_handle = ctx.expressions.append(
2676                        crate::Expression::SubgroupOperationResult {
2677                            ty: result_type.handle,
2678                        },
2679                        span,
2680                    );
2681                    self.lookup_expression.insert(
2682                        result_id,
2683                        LookupExpression {
2684                            handle: result_handle,
2685                            type_id: result_type_id,
2686                            block_id,
2687                        },
2688                    );
2689
2690                    block.push(
2691                        crate::Statement::SubgroupCollectiveOperation {
2692                            result: result_handle,
2693                            op: op_id,
2694                            collective_op: collective_op_id,
2695                            argument: argument_handle,
2696                        },
2697                        span,
2698                    );
2699                    emitter.start(ctx.expressions);
2700                }
2701                Op::GroupNonUniformBroadcastFirst
2702                | Op::GroupNonUniformBroadcast
2703                | Op::GroupNonUniformShuffle
2704                | Op::GroupNonUniformShuffleDown
2705                | Op::GroupNonUniformShuffleUp
2706                | Op::GroupNonUniformShuffleXor
2707                | Op::GroupNonUniformQuadBroadcast => {
2708                    inst.expect(if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {
2709                        5
2710                    } else {
2711                        6
2712                    })?;
2713                    block.extend(emitter.finish(ctx.expressions));
2714                    let result_type_id = self.next()?;
2715                    let result_id = self.next()?;
2716                    let exec_scope_id = self.next()?;
2717                    let argument_id = self.next()?;
2718
2719                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;
2720                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);
2721
2722                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
2723                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
2724                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
2725                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
2726
2727                    let mode = if matches!(inst.op, Op::GroupNonUniformBroadcastFirst) {
2728                        crate::GatherMode::BroadcastFirst
2729                    } else {
2730                        let index_id = self.next()?;
2731                        let index_lookup = self.lookup_expression.lookup(index_id)?;
2732                        let index_handle = get_expr_handle!(index_id, index_lookup);
2733                        match inst.op {
2734                            Op::GroupNonUniformBroadcast => {
2735                                crate::GatherMode::Broadcast(index_handle)
2736                            }
2737                            Op::GroupNonUniformShuffle => crate::GatherMode::Shuffle(index_handle),
2738                            Op::GroupNonUniformShuffleDown => {
2739                                crate::GatherMode::ShuffleDown(index_handle)
2740                            }
2741                            Op::GroupNonUniformShuffleUp => {
2742                                crate::GatherMode::ShuffleUp(index_handle)
2743                            }
2744                            Op::GroupNonUniformShuffleXor => {
2745                                crate::GatherMode::ShuffleXor(index_handle)
2746                            }
2747                            Op::GroupNonUniformQuadBroadcast => {
2748                                crate::GatherMode::QuadBroadcast(index_handle)
2749                            }
2750                            _ => unreachable!(),
2751                        }
2752                    };
2753
2754                    let result_type = self.lookup_type.lookup(result_type_id)?;
2755
2756                    let result_handle = ctx.expressions.append(
2757                        crate::Expression::SubgroupOperationResult {
2758                            ty: result_type.handle,
2759                        },
2760                        span,
2761                    );
2762                    self.lookup_expression.insert(
2763                        result_id,
2764                        LookupExpression {
2765                            handle: result_handle,
2766                            type_id: result_type_id,
2767                            block_id,
2768                        },
2769                    );
2770
2771                    block.push(
2772                        crate::Statement::SubgroupGather {
2773                            result: result_handle,
2774                            mode,
2775                            argument: argument_handle,
2776                        },
2777                        span,
2778                    );
2779                    emitter.start(ctx.expressions);
2780                }
2781                Op::GroupNonUniformQuadSwap => {
2782                    inst.expect(6)?;
2783                    block.extend(emitter.finish(ctx.expressions));
2784                    let result_type_id = self.next()?;
2785                    let result_id = self.next()?;
2786                    let exec_scope_id = self.next()?;
2787                    let argument_id = self.next()?;
2788                    let direction_id = self.next()?;
2789
2790                    let argument_lookup = self.lookup_expression.lookup(argument_id)?;
2791                    let argument_handle = get_expr_handle!(argument_id, argument_lookup);
2792
2793                    let exec_scope_const = self.lookup_constant.lookup(exec_scope_id)?;
2794                    let _exec_scope = resolve_constant(ctx.gctx(), &exec_scope_const.inner)
2795                        .filter(|exec_scope| *exec_scope == spirv::Scope::Subgroup as u32)
2796                        .ok_or(Error::InvalidBarrierScope(exec_scope_id))?;
2797
2798                    let direction_const = self.lookup_constant.lookup(direction_id)?;
2799                    let direction_const = resolve_constant(ctx.gctx(), &direction_const.inner)
2800                        .ok_or(Error::InvalidOperand)?;
2801                    let direction = match direction_const {
2802                        0 => crate::Direction::X,
2803                        1 => crate::Direction::Y,
2804                        2 => crate::Direction::Diagonal,
2805                        _ => unreachable!(),
2806                    };
2807
2808                    let result_type = self.lookup_type.lookup(result_type_id)?;
2809
2810                    let result_handle = ctx.expressions.append(
2811                        crate::Expression::SubgroupOperationResult {
2812                            ty: result_type.handle,
2813                        },
2814                        span,
2815                    );
2816                    self.lookup_expression.insert(
2817                        result_id,
2818                        LookupExpression {
2819                            handle: result_handle,
2820                            type_id: result_type_id,
2821                            block_id,
2822                        },
2823                    );
2824
2825                    block.push(
2826                        crate::Statement::SubgroupGather {
2827                            mode: crate::GatherMode::QuadSwap(direction),
2828                            result: result_handle,
2829                            argument: argument_handle,
2830                        },
2831                        span,
2832                    );
2833                    emitter.start(ctx.expressions);
2834                }
2835                Op::AtomicLoad => {
2836                    inst.expect(6)?;
2837                    let start = self.data_offset;
2838                    let result_type_id = self.next()?;
2839                    let result_id = self.next()?;
2840                    let pointer_id = self.next()?;
2841                    let _scope_id = self.next()?;
2842                    let _memory_semantics_id = self.next()?;
2843                    let span = self.span_from_with_op(start);
2844
2845                    log::trace!("\t\t\tlooking up expr {pointer_id:?}");
2846                    let p_lexp_handle =
2847                        get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
2848
2849                    // Create an expression for our result
2850                    let expr = crate::Expression::Load {
2851                        pointer: p_lexp_handle,
2852                    };
2853                    let handle = ctx.expressions.append(expr, span);
2854                    self.lookup_expression.insert(
2855                        result_id,
2856                        LookupExpression {
2857                            handle,
2858                            type_id: result_type_id,
2859                            block_id,
2860                        },
2861                    );
2862
2863                    // Store any associated global variables so we can upgrade their types later
2864                    self.record_atomic_access(ctx, p_lexp_handle)?;
2865                }
2866                Op::AtomicStore => {
2867                    inst.expect(5)?;
2868                    let start = self.data_offset;
2869                    let pointer_id = self.next()?;
2870                    let _scope_id = self.next()?;
2871                    let _memory_semantics_id = self.next()?;
2872                    let value_id = self.next()?;
2873                    let span = self.span_from_with_op(start);
2874
2875                    log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}");
2876                    let p_lexp_handle =
2877                        get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
2878
2879                    log::trace!("\t\t\tlooking up value expr {pointer_id:?}");
2880                    let v_lexp_handle =
2881                        get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
2882
2883                    block.extend(emitter.finish(ctx.expressions));
2884                    // Create a statement for the op itself
2885                    let stmt = crate::Statement::Store {
2886                        pointer: p_lexp_handle,
2887                        value: v_lexp_handle,
2888                    };
2889                    block.push(stmt, span);
2890                    emitter.start(ctx.expressions);
2891
2892                    // Store any associated global variables so we can upgrade their types later
2893                    self.record_atomic_access(ctx, p_lexp_handle)?;
2894                }
2895                Op::AtomicIIncrement | Op::AtomicIDecrement => {
2896                    inst.expect(6)?;
2897                    let start = self.data_offset;
2898                    let result_type_id = self.next()?;
2899                    let result_id = self.next()?;
2900                    let pointer_id = self.next()?;
2901                    let _scope_id = self.next()?;
2902                    let _memory_semantics_id = self.next()?;
2903                    let span = self.span_from_with_op(start);
2904
2905                    let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
2906                        pointer_id,
2907                        ctx,
2908                        &mut emitter,
2909                        &mut block,
2910                        body_idx,
2911                    )?;
2912
2913                    block.extend(emitter.finish(ctx.expressions));
2914                    // Create an expression for our result
2915                    let r_lexp_handle = {
2916                        let expr = crate::Expression::AtomicResult {
2917                            ty: p_base_ty_h,
2918                            comparison: false,
2919                        };
2920                        let handle = ctx.expressions.append(expr, span);
2921                        self.lookup_expression.insert(
2922                            result_id,
2923                            LookupExpression {
2924                                handle,
2925                                type_id: result_type_id,
2926                                block_id,
2927                            },
2928                        );
2929                        handle
2930                    };
2931                    emitter.start(ctx.expressions);
2932
2933                    // Create a literal "1" to use as our value
2934                    let one_lexp_handle = make_index_literal(
2935                        ctx,
2936                        1,
2937                        &mut block,
2938                        &mut emitter,
2939                        p_base_ty_h,
2940                        result_type_id,
2941                        span,
2942                    )?;
2943
2944                    // Create a statement for the op itself
2945                    let stmt = crate::Statement::Atomic {
2946                        pointer: p_exp_h,
2947                        fun: match inst.op {
2948                            Op::AtomicIIncrement => crate::AtomicFunction::Add,
2949                            _ => crate::AtomicFunction::Subtract,
2950                        },
2951                        value: one_lexp_handle,
2952                        result: Some(r_lexp_handle),
2953                    };
2954                    block.push(stmt, span);
2955
2956                    // Store any associated global variables so we can upgrade their types later
2957                    self.record_atomic_access(ctx, p_exp_h)?;
2958                }
2959                Op::AtomicCompareExchange => {
2960                    inst.expect(9)?;
2961
2962                    let start = self.data_offset;
2963                    let span = self.span_from_with_op(start);
2964                    let result_type_id = self.next()?;
2965                    let result_id = self.next()?;
2966                    let pointer_id = self.next()?;
2967                    let _memory_scope_id = self.next()?;
2968                    let _equal_memory_semantics_id = self.next()?;
2969                    let _unequal_memory_semantics_id = self.next()?;
2970                    let value_id = self.next()?;
2971                    let comparator_id = self.next()?;
2972
2973                    let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
2974                        pointer_id,
2975                        ctx,
2976                        &mut emitter,
2977                        &mut block,
2978                        body_idx,
2979                    )?;
2980
2981                    log::trace!("\t\t\tlooking up value expr {value_id:?}");
2982                    let v_lexp_handle =
2983                        get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
2984
2985                    log::trace!("\t\t\tlooking up comparator expr {value_id:?}");
2986                    let c_lexp_handle = get_expr_handle!(
2987                        comparator_id,
2988                        self.lookup_expression.lookup(comparator_id)?
2989                    );
2990
2991                    // We know from the SPIR-V spec that the result type must be an integer
2992                    // scalar, and we'll need the type itself to get a handle to the atomic
2993                    // result struct.
2994                    let crate::TypeInner::Scalar(scalar) = ctx.module.types[p_base_ty_h].inner
2995                    else {
2996                        return Err(
2997                            crate::front::atomic_upgrade::Error::CompareExchangeNonScalarBaseType
2998                                .into(),
2999                        );
3000                    };
3001
3002                    // Get a handle to the atomic result struct type.
3003                    let atomic_result_struct_ty_h = ctx.module.generate_predeclared_type(
3004                        crate::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),
3005                    );
3006
3007                    block.extend(emitter.finish(ctx.expressions));
3008
3009                    // Create an expression for our atomic result
3010                    let atomic_lexp_handle = {
3011                        let expr = crate::Expression::AtomicResult {
3012                            ty: atomic_result_struct_ty_h,
3013                            comparison: true,
3014                        };
3015                        ctx.expressions.append(expr, span)
3016                    };
3017
3018                    // Create an dot accessor to extract the value from the
3019                    // result struct __atomic_compare_exchange_result<T> and use that
3020                    // as the expression for the result_id
3021                    {
3022                        let expr = crate::Expression::AccessIndex {
3023                            base: atomic_lexp_handle,
3024                            index: 0,
3025                        };
3026                        let handle = ctx.expressions.append(expr, span);
3027                        // Use this dot accessor as the result id's expression
3028                        let _ = self.lookup_expression.insert(
3029                            result_id,
3030                            LookupExpression {
3031                                handle,
3032                                type_id: result_type_id,
3033                                block_id,
3034                            },
3035                        );
3036                    }
3037
3038                    emitter.start(ctx.expressions);
3039
3040                    // Create a statement for the op itself
3041                    let stmt = crate::Statement::Atomic {
3042                        pointer: p_exp_h,
3043                        fun: crate::AtomicFunction::Exchange {
3044                            compare: Some(c_lexp_handle),
3045                        },
3046                        value: v_lexp_handle,
3047                        result: Some(atomic_lexp_handle),
3048                    };
3049                    block.push(stmt, span);
3050
3051                    // Store any associated global variables so we can upgrade their types later
3052                    self.record_atomic_access(ctx, p_exp_h)?;
3053                }
3054                Op::AtomicExchange
3055                | Op::AtomicIAdd
3056                | Op::AtomicISub
3057                | Op::AtomicSMin
3058                | Op::AtomicUMin
3059                | Op::AtomicSMax
3060                | Op::AtomicUMax
3061                | Op::AtomicAnd
3062                | Op::AtomicOr
3063                | Op::AtomicXor
3064                | Op::AtomicFAddEXT => self.parse_atomic_expr_with_value(
3065                    inst,
3066                    &mut emitter,
3067                    ctx,
3068                    &mut block,
3069                    block_id,
3070                    body_idx,
3071                    match inst.op {
3072                        Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None },
3073                        Op::AtomicIAdd | Op::AtomicFAddEXT => crate::AtomicFunction::Add,
3074                        Op::AtomicISub => crate::AtomicFunction::Subtract,
3075                        Op::AtomicSMin => crate::AtomicFunction::Min,
3076                        Op::AtomicUMin => crate::AtomicFunction::Min,
3077                        Op::AtomicSMax => crate::AtomicFunction::Max,
3078                        Op::AtomicUMax => crate::AtomicFunction::Max,
3079                        Op::AtomicAnd => crate::AtomicFunction::And,
3080                        Op::AtomicOr => crate::AtomicFunction::InclusiveOr,
3081                        Op::AtomicXor => crate::AtomicFunction::ExclusiveOr,
3082                        _ => unreachable!(),
3083                    },
3084                )?,
3085
3086                _ => {
3087                    return Err(Error::UnsupportedInstruction(self.state, inst.op));
3088                }
3089            }
3090        };
3091
3092        block.extend(emitter.finish(ctx.expressions));
3093        if let Some(stmt) = terminator {
3094            block.push(stmt, crate::Span::default());
3095        }
3096
3097        // Save this block fragment in `block_ctx.blocks`, and mark it to be
3098        // incorporated into the current body at `Statement` assembly time.
3099        ctx.blocks.insert(block_id, block);
3100        let body = &mut ctx.bodies[body_idx];
3101        body.data.push(BodyFragment::BlockId(block_id));
3102        Ok(())
3103    }
3104}
3105
3106fn make_index_literal(
3107    ctx: &mut BlockContext,
3108    index: u32,
3109    block: &mut crate::Block,
3110    emitter: &mut crate::proc::Emitter,
3111    index_type: Handle<crate::Type>,
3112    index_type_id: spirv::Word,
3113    span: crate::Span,
3114) -> Result<Handle<crate::Expression>, Error> {
3115    block.extend(emitter.finish(ctx.expressions));
3116
3117    let literal = match ctx.module.types[index_type].inner.scalar_kind() {
3118        Some(crate::ScalarKind::Uint) => crate::Literal::U32(index),
3119        Some(crate::ScalarKind::Sint) => crate::Literal::I32(index as i32),
3120        _ => return Err(Error::InvalidIndexType(index_type_id)),
3121    };
3122    let expr = ctx
3123        .expressions
3124        .append(crate::Expression::Literal(literal), span);
3125
3126    emitter.start(ctx.expressions);
3127    Ok(expr)
3128}