naga/front/spv/
next_block.rs

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