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