Repository: gfx-rs/wgpu (language: rust)

Issue:
Handle signed arithmetic edge cases that would be UB in Metal, HLSL and SPIRV
The following operations would overflow with signed integer operands. The results are what the WGSL spec requires.

```
div: T::MIN / -1 = T::MIN
rem: T::MIN % -1 = 0
abs: abs(T::MIN) = T::MIN
neg: -T::MIN = T::MIN
```

Related: https://github.com/gfx-rs/wgpu/issues/6666 / https://github.com/gfx-rs/wgpu/pull/6959


Discussion / hints:
Have we looked into which of these cases need attention in the other backend languages?
I haven't but suspect HLSL/GLSL would need them.
### Metal

#### Spec notes

* div: "Division on integer types that result in a value that lies outside of the range bounded by the maximum and minimum representable values of the integer type, such as TYPE_MIN/-1 for signed integer types or division by zero, does not cause an exception but results in an unspecified value"
  * So not undefined behaviour, but we must handle this to comply with the WGSL spec
* rem: If one or both operands are negative, the results are undefined.
* abs: Just says "Returns |x|" - Perhaps this is covered by general "The result of signed integer overflow is undefined" statement elsewhere in the spec, but that is from a different section.
* neg: Again nothing specific but presumably this is indeed covered by "the result of signed integer overflow is undefined" as it's from the same section (on operators)

#### What does Tint do?

For this wgsl:
```wgsl
fn do_stuff(x: i32) {
    var div_res = x / -1;
    var mod_res = x % -1;
    var abs_res = abs(x);
    var neg_res = -x;
}
```

Tint outputs:
```metal
int tint_unary_minus(const int v) {
  return select(-v, v, v == -2147483648);
}

int tint_div(int lhs, int rhs) {
  return (lhs / select(rhs, 1, bool((rhs == 0) | bool((lhs == (-2147483647 - 1)) & (rhs == -1)))));
}

int tint_mod(int lhs, int rhs) {
  int const rhs_or_one = select(rhs, 1, bool((rhs == 0) | bool((lhs == (-2147483647 - 1)) & (rhs == -1))));
  if (any(((uint((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
    return as_type<int>((as_type<uint>(lhs) - as_type<uint>(as_type<int>((as_type<uint>((lhs / rhs_or_one)) * as_type<uint>(rhs_or_one))))));
  } else {
    return (lhs % rhs_or_one);
  }
}

void do_stuff(int x) {
  int div_res = tint_div(x, -1);
  int mod_res = tint_mod(x, -1);
  int abs_res = abs(x);
  int neg_res = tint_unary_minus(x);
}
```

Interestingly they handle the unary minus but not abs(). 

Also interesting to note the `-{i32::MAX} - 1` construct rather than `{i32::MIN}`. Presumably this means metal parses `-1` as `<unary neg> <unsigned literal>` instead of `<signed literal>`. Which is something to look out for.

### HLSL

#### Spec notes

🙃 

#### What does Tint do?

```hlsl
int tint_div(int lhs, int rhs) {
  return (lhs / (((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))) ? 1 : rhs));
}

int tint_mod(int lhs, int rhs) {
  int rhs_or_one = (((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))) ? 1 : rhs);
  if (any(((uint((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
  } else {
    return (lhs % rhs_or_one);
  }
}

void do_stuff(int x) {
  int div_res = tint_div(x, -1);
  int mod_res = tint_mod(x, -1);
  int abs_res = abs(x);
  int neg_res = -(x);
}
```

It's interesting they don't wrap abs or neg. But then nor do they handle signed addition, subtraction, or multiplication for HLSL, whereas they do for MSL, which seems consistent. 

### SPIRV

#### Spec notes

* OpSDiv: Behavior is undefined if Operand 2 is -1 and Operand 1 is the minimum representable value for the operands' type, causing signed overflow.
* OpSRem: Behavior is undefined if Operand 2 is -1 and Operand 1 is the minimum representable value for the operands' type, causing signed overflow.
* GLOp::SAbs: Doesn't mention anything regarding overflow except "This instruction can be decorated with NoSignedWrap.", which presumably means the absence of that decoration we safely overflow to INT_MIN, which is what we want
* SNegate: same as above

#### What does tint do?

Won't post the whole thing but we can see they have wrapper functions for div and rem, and just use OpSNegate and SAbs directly, which agrees with my read of the spec.

```
    %div_res = OpVariable %_ptr_Function_int Function
    %mod_res = OpVariable %_ptr_Function_int Function
    %abs_res = OpVariable %_ptr_Function_int Function
    %neg_res = OpVariable %_ptr_Function_int Function
          %7 = OpFunctionCall %int %tint_div_i32 %x %int_n1
               OpStore %div_res %7
         %12 = OpFunctionCall %int %tint_mod_i32 %x %int_n1
               OpStore %mod_res %12
         %15 = OpExtInst %int %16 SAbs %x
               OpStore %abs_res %15
         %18 = OpSNegate %int %x
               OpStore %neg_res %18
```

### GLSL

I might leave GLSL for a follow-up, but for posterity here's what tint does:

```glsl
int tint_mod_i32(int lhs, int rhs) {
  uint v = uint((lhs == (-2147483647 - 1)));
  bool v_1 = bool((v & uint((rhs == -1))));
  uint v_2 = uint((rhs == 0));
  int v_3 = mix(rhs, 1, bool((v_2 | uint(v_1))));
  return (lhs - ((lhs / v_3) * v_3));
}
int tint_div_i32(int lhs, int rhs) {
  uint v_4 = uint((lhs == (-2147483647 - 1)));
  bool v_5 = bool((v_4 & uint((rhs == -1))));
  uint v_6 = uint((rhs == 0));
  return (lhs / mix(rhs, 1, bool((v_6 | uint(v_5)))));
}
void do_stuff(int x) {
  int div_res = tint_div_i32(x, -1);
  int mod_res = tint_mod_i32(x, -1);
  int abs_res = abs(x);
  int neg_res = -(x);
}
```

They don't worry about signed addition, subtraction, or multiplication either.
> ## HLSL
> ### Spec notes
> 🙃

I asked on the [directx discord](https://discord.com/invite/directx) about signed integer overflow and we'll see what they say

Sad news

> Signed integer overflow in DXIL is undefined, in DXBC it probably was well defined.

So we do need to handle it, it seems.

Fix patch:
diff --git a/naga/src/back/hlsl/help.rs b/naga/src/back/hlsl/help.rs
index a273812213..7789a4310d 100644
--- a/naga/src/back/hlsl/help.rs
+++ b/naga/src/back/hlsl/help.rs
@@ -28,7 +28,10 @@ int dim_1d = NagaDimensions1D(image_1d);
 
 use super::{
     super::FunctionCtx,
-    writer::{EXTRACT_BITS_FUNCTION, INSERT_BITS_FUNCTION},
+    writer::{
+        ABS_FUNCTION, DIV_FUNCTION, EXTRACT_BITS_FUNCTION, INSERT_BITS_FUNCTION, MOD_FUNCTION,
+        NEG_FUNCTION,
+    },
     BackendResult,
 };
 use crate::{arena::Handle, proc::NameKey, ScalarKind};
@@ -75,6 +78,23 @@ pub(super) struct WrappedZeroValue {
     pub(super) ty: Handle<crate::Type>,
 }
 
+#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
+pub(super) struct WrappedUnaryOp {
+    pub(super) op: crate::UnaryOperator,
+    // This can only represent scalar or vector types. If we ever need to wrap
+    // unary ops with other types, we'll need a better representation.
+    pub(super) ty: (Option<crate::VectorSize>, crate::Scalar),
+}
+
+#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
+pub(super) struct WrappedBinaryOp {
+    pub(super) op: crate::BinaryOperator,
+    // This can only represent scalar or vector types. If we ever need to wrap
+    // binary ops with other types, we'll need a better representation.
+    pub(super) left_ty: (Option<crate::VectorSize>, crate::Scalar),
+    pub(super) right_ty: (Option<crate::VectorSize>, crate::Scalar),
+}
+
 /// HLSL backend requires its own `ImageQuery` enum.
 ///
 /// It is used inside `WrappedImageQuery` and should be unique per ImageQuery function.
@@ -991,6 +1011,8 @@ impl<W: Write> super::Writer<'_, W> {
                 arg3: _arg3,
             } = *expression
             {
+                let arg_ty = func_ctx.resolve_type(arg, &module.types);
+
                 match fun {
                     crate::MathFunction::ExtractBits => {
                         // The behavior of our extractBits polyfill is undefined if offset + count > bit_width. We need
@@ -1005,7 +1027,6 @@ impl<W: Write> super::Writer<'_, W> {
                         // c = min(count, w - o)
                         //
                         // bitfieldExtract(x, o, c)
-                        let arg_ty = func_ctx.resolve_type(arg, &module.types);
                         let scalar = arg_ty.scalar().unwrap();
                         let components = arg_ty.components();
 
@@ -1048,7 +1069,6 @@ impl<W: Write> super::Writer<'_, W> {
                     crate::MathFunction::InsertBits => {
                         // The behavior of our insertBits polyfill has the same constraints as the extractBits polyfill.
 
-                        let arg_ty = func_ctx.resolve_type(arg, &module.types);
                         let scalar = arg_ty.scalar().unwrap();
                         let components = arg_ty.components();
 
@@ -1105,6 +1125,226 @@ impl<W: Write> super::Writer<'_, W> {
                         // End of function body
                         writeln!(self.out, "}}")?;
                     }
+                    // Taking the absolute value of the minimum value of a two's
+                    // complement signed integer type causes overflow, which is
+                    // undefined behaviour in HLSL. To avoid this, when the value is
+                    // negative we bitcast the value to unsigned and negate it, then
+                    // bitcast back to signed.
+                    // This adheres to the WGSL spec in that the absolute of the type's
+                    // minimum value should equal to the minimum value.
+                    //
+                    // TODO(#7109): asint()/asuint() only support 32-bit integers, so we
+                    // must find another solution for different bit-widths.
+                    crate::MathFunction::Abs
+                        if matches!(arg_ty.scalar(), Some(crate::Scalar::I32)) =>
+                    {
+                        let scalar = arg_ty.scalar().unwrap();
+                        let components = arg_ty.components();
+
+                        let wrapped = WrappedMath {
+                            fun,
+                            scalar,
+                            components,
+                        };
+
+                        if !self.wrapped.math.insert(wrapped) {
+                            continue;
+                        }
+
+                        self.write_value_type(module, arg_ty)?;
+                        write!(self.out, " {ABS_FUNCTION}(")?;
+                        self.write_value_type(module, arg_ty)?;
+                        writeln!(self.out, " val) {{")?;
+
+                        let level = crate::back::Level(1);
+                        writeln!(
+                            self.out,
+                            "{level}return val >= 0 ? val : asint(-asuint(val));"
+                        )?;
+                        writeln!(self.out, "}}")?;
+                        writeln!(self.out)?;
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    pub(super) fn write_wrapped_unary_ops(
+        &mut self,
+        module: &crate::Module,
+        func_ctx: &FunctionCtx,
+    ) -> BackendResult {
+        for (_, expression) in func_ctx.expressions.iter() {
+            if let crate::Expression::Unary { op, expr } = *expression {
+                let expr_ty = func_ctx.resolve_type(expr, &module.types);
+                let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() else {
+                    continue;
+                };
+                let wrapped = WrappedUnaryOp {
+                    op,
+                    ty: (vector_size, scalar),
+                };
+
+                // Negating the minimum value of a two's complement signed integer type
+                // causes overflow, which is undefined behaviour in HLSL. To avoid this
+                // we bitcast the value to unsigned and negate it, then bitcast back to
+                // signed. This adheres to the WGSL spec in that the negative of the
+                // type's minimum value should equal to the minimum value.
+                //
+                // TODO(#7109): asint()/asuint() only support 32-bit integers, so we must
+                // find another solution for different bit-widths.
+                match (op, scalar) {
+                    (crate::UnaryOperator::Negate, crate::Scalar::I32) => {
+                        if !self.wrapped.unary_op.insert(wrapped) {
+                            continue;
+                        }
+
+                        self.write_value_type(module, expr_ty)?;
+                        write!(self.out, " {NEG_FUNCTION}(")?;
+                        self.write_value_type(module, expr_ty)?;
+                        writeln!(self.out, " val) {{")?;
+
+                        let level = crate::back::Level(1);
+                        writeln!(self.out, "{level}return asint(-asuint(val));",)?;
+                        writeln!(self.out, "}}")?;
+                        writeln!(self.out)?;
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    pub(super) fn write_wrapped_binary_ops(
+        &mut self,
+        module: &crate::Module,
+        func_ctx: &FunctionCtx,
+    ) -> BackendResult {
+        for (expr_handle, expression) in func_ctx.expressions.iter() {
+            if let crate::Expression::Binary { op, left, right } = *expression {
+                let expr_ty = func_ctx.resolve_type(expr_handle, &module.types);
+                let left_ty = func_ctx.resolve_type(left, &module.types);
+                let right_ty = func_ctx.resolve_type(right, &module.types);
+
+                match (op, expr_ty.scalar()) {
+                    // Signed integer division of the type's minimum representable value
+                    // divided by -1, or signed or unsigned division by zero, is
+                    // undefined behaviour in HLSL. We override the divisor to 1 in these
+                    // cases.
+                    // This adheres to the WGSL spec in that:
+                    // * TYPE_MIN / -1 == TYPE_MIN
+                    // * x / 0 == x
+                    (
+                        crate::BinaryOperator::Divide,
+                        Some(
+                            scalar @ crate::Scalar {
+                                kind: ScalarKind::Sint | ScalarKind::Uint,
+                                ..
+                            },
+                        ),
+                    ) => {
+                        let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {
+                            continue;
+                        };
+                        let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {
+                            continue;
+                        };
+                        let wrapped = WrappedBinaryOp {
+                            op,
+                            left_ty: left_wrapped_ty,
+                            right_ty: right_wrapped_ty,
+                        };
+                        if !self.wrapped.binary_op.insert(wrapped) {
+                            continue;
+                        }
+
+                        self.write_value_type(module, expr_ty)?;
+                        write!(self.out, " {DIV_FUNCTION}(")?;
+                        self.write_value_type(module, left_ty)?;
+                        write!(self.out, " lhs, ")?;
+                        self.write_value_type(module, right_ty)?;
+                        writeln!(self.out, " rhs) {{")?;
+                        let level = crate::back::Level(1);
+                        match scalar.kind {
+                            ScalarKind::Sint => {
+                                let min = -1i64 << (scalar.width as u32 * 8 - 1);
+                                writeln!(self.out, "{level}return lhs / (((lhs == {min} & rhs == -1) | (rhs == 0)) ? 1 : rhs);")?
+                            }
+                            ScalarKind::Uint => {
+                                writeln!(self.out, "{level}return lhs / (rhs == 0u ? 1u : rhs);")?
+                            }
+                            _ => unreachable!(),
+                        }
+                        writeln!(self.out, "}}")?;
+                        writeln!(self.out)?;
+                    }
+                    // The modulus operator is only defined for integers in HLSL when
+                    // either both sides are positive or both sides are negative. To
+                    // avoid this undefined behaviour we use the following equation:
+                    //
+                    // dividend - (dividend / divisor) * divisor
+                    //
+                    // overriding the divisor to 1 if either it is 0, or it is -1
+                    // and the dividend is the minimum representable value.
+                    //
+                    // This adheres to the WGSL spec in that:
+                    // * min_value % -1 == 0
+                    // * x % 0 == 0
+                    (
+                        crate::BinaryOperator::Modulo,
+                        Some(
+                            scalar @ crate::Scalar {
+                                kind: ScalarKind::Sint | ScalarKind::Uint,
+                                ..
+                            },
+                        ),
+                    ) => {
+                        let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {
+                            continue;
+                        };
+                        let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {
+                            continue;
+                        };
+                        let wrapped = WrappedBinaryOp {
+                            op,
+                            left_ty: left_wrapped_ty,
+                            right_ty: right_wrapped_ty,
+                        };
+                        if !self.wrapped.binary_op.insert(wrapped) {
+                            continue;
+                        }
+
+                        self.write_value_type(module, expr_ty)?;
+                        write!(self.out, " {MOD_FUNCTION}(")?;
+                        self.write_value_type(module, left_ty)?;
+                        write!(self.out, " lhs, ")?;
+                        self.write_value_type(module, right_ty)?;
+                        writeln!(self.out, " rhs) {{")?;
+                        let level = crate::back::Level(1);
+                        match scalar.kind {
+                            ScalarKind::Sint => {
+                                let min = -1i64 << (scalar.width as u32 * 8 - 1);
+                                write!(self.out, "{level}")?;
+                                self.write_value_type(module, right_ty)?;
+                                writeln!(self.out, " divisor = ((lhs == {min} & rhs == -1) | (rhs == 0)) ? 1 : rhs;")?;
+                                writeln!(
+                                    self.out,
+                                    "{level}return lhs - (lhs / divisor) * divisor;"
+                                )?
+                            }
+                            ScalarKind::Uint => {
+                                writeln!(self.out, "{level}return lhs % (rhs == 0u ? 1u : rhs);")?
+                            }
+                            _ => unreachable!(),
+                        }
+                        writeln!(self.out, "}}")?;
+                        writeln!(self.out)?;
+                    }
                     _ => {}
                 }
             }
@@ -1120,6 +1360,8 @@ impl<W: Write> super::Writer<'_, W> {
         func_ctx: &FunctionCtx,
     ) -> BackendResult {
         self.write_wrapped_math_functions(module, func_ctx)?;
+        self.write_wrapped_unary_ops(module, func_ctx)?;
+        self.write_wrapped_binary_ops(module, func_ctx)?;
         self.write_wrapped_expression_functions(module, func_ctx.expressions, Some(func_ctx))?;
         self.write_wrapped_zero_value_functions(module, func_ctx.expressions)?;
 
diff --git a/naga/src/back/hlsl/keywords.rs b/naga/src/back/hlsl/keywords.rs
index 2c876c3198..e5be7b6d81 100644
--- a/naga/src/back/hlsl/keywords.rs
+++ b/naga/src/back/hlsl/keywords.rs
@@ -822,6 +822,10 @@ pub const RESERVED: &[&str] = &[
     super::writer::INSERT_BITS_FUNCTION,
     super::writer::SAMPLER_HEAP_VAR,
     super::writer::COMPARISON_SAMPLER_HEAP_VAR,
+    super::writer::ABS_FUNCTION,
+    super::writer::DIV_FUNCTION,
+    super::writer::MOD_FUNCTION,
+    super::writer::NEG_FUNCTION,
 ];
 
 // DXC scalar types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/ASTContextHLSL.cpp#L48-L254
diff --git a/naga/src/back/hlsl/mod.rs b/naga/src/back/hlsl/mod.rs
index 572bb4ca59..e5361393b3 100644
--- a/naga/src/back/hlsl/mod.rs
+++ b/naga/src/back/hlsl/mod.rs
@@ -365,6 +365,8 @@ struct Wrapped {
     struct_matrix_access: crate::FastHashSet<help::WrappedStructMatrixAccess>,
     mat_cx2s: crate::FastHashSet<help::WrappedMatCx2>,
     math: crate::FastHashSet<help::WrappedMath>,
+    unary_op: crate::FastHashSet<help::WrappedUnaryOp>,
+    binary_op: crate::FastHashSet<help::WrappedBinaryOp>,
     /// If true, the sampler heaps have been written out.
     sampler_heaps: bool,
     // Mapping from SamplerIndexBufferKey to the name the namer returned.
@@ -379,6 +381,8 @@ impl Wrapped {
         self.struct_matrix_access.clear();
         self.mat_cx2s.clear();
         self.math.clear();
+        self.unary_op.clear();
+        self.binary_op.clear();
     }
 }
 
diff --git a/naga/src/back/hlsl/writer.rs b/naga/src/back/hlsl/writer.rs
index b6b047b1f2..a8283388ce 100644
--- a/naga/src/back/hlsl/writer.rs
+++ b/naga/src/back/hlsl/writer.rs
@@ -27,6 +27,10 @@ pub(crate) const EXTRACT_BITS_FUNCTION: &str = "naga_extractBits";
 pub(crate) const INSERT_BITS_FUNCTION: &str = "naga_insertBits";
 pub(crate) const SAMPLER_HEAP_VAR: &str = "nagaSamplerHeap";
 pub(crate) const COMPARISON_SAMPLER_HEAP_VAR: &str = "nagaComparisonSamplerHeap";
+pub(crate) const ABS_FUNCTION: &str = "naga_abs";
+pub(crate) const DIV_FUNCTION: &str = "naga_div";
+pub(crate) const MOD_FUNCTION: &str = "naga_mod";
+pub(crate) const NEG_FUNCTION: &str = "naga_neg";
 
 struct EpStructMember {
     name: String,
@@ -2631,7 +2635,10 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
                 crate::Literal::F64(value) => write!(self.out, "{value:?}L")?,
                 crate::Literal::F32(value) => write!(self.out, "{value:?}")?,
                 crate::Literal::U32(value) => write!(self.out, "{value}u")?,
-                crate::Literal::I32(value) => write!(self.out, "{value}")?,
+                // HLSL has no suffix for explicit i32 literals, but not using any suffix
+                // makes the type ambiguous which prevents overload resolution from
+                // working. So we explicitly use the int() constructor syntax.
+                crate::Literal::I32(value) => write!(self.out, "int({value})")?,
                 crate::Literal::U64(value) => write!(self.out, "{value}uL")?,
                 crate::Literal::I64(value) => write!(self.out, "{value}L")?,
                 crate::Literal::Bool(value) => write!(self.out, "{value}")?,
@@ -2754,6 +2761,30 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
                 )?;
             }
             Expression::Override(_) => return Err(Error::Override),
+            // Avoid undefined behaviour for addition, subtraction, and
+            // multiplication of signed integers by casting operands to
+            // unsigned, performing the operation, then casting the result back
+            // to signed.
+            // TODO(#7109): This relies on the asint()/asuint() functions which only work
+            // for 32-bit types, so we must find another solution for different bit widths.
+            Expression::Binary {
+                op:
+                    op @ crate::BinaryOperator::Add
+                    | op @ crate::BinaryOperator::Subtract
+                    | op @ crate::BinaryOperator::Multiply,
+                left,
+                right,
+            } if matches!(
+                func_ctx.resolve_type(expr, &module.types).scalar(),
+                Some(Scalar::I32)
+            ) =>
+            {
+                write!(self.out, "asint(asuint(",)?;
+                self.write_expr(module, left, func_ctx)?;
+                write!(self.out, ") {} asuint(", back::binary_operation_str(op))?;
+                self.write_expr(module, right, func_ctx)?;
+                write!(self.out, "))")?;
+            }
             // All of the multiplication can be expressed as `mul`,
             // except vector * vector, which needs to use the "*" operator.
             Expression::Binary {
@@ -2771,18 +2802,48 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
                 write!(self.out, ")")?;
             }
 
-            // TODO: handle undefined behavior of BinaryOperator::Modulo
+            // WGSL says that floating-point division by zero should return
+            // infinity. Microsoft's Direct3D 11 functional specification
+            // (https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm)
+            // says:
             //
-            // sint:
-            // if right == 0 return 0
-            // if left == min(type_of(left)) && right == -1 return 0
-            // if sign(left) != sign(right) return result as defined by WGSL
+            //     Divide by 0 produces +/- INF, except 0/0 which results in NaN.
             //
-            // uint:
-            // if right == 0 return 0
+            // which is what we want. The DXIL specification for the FDiv
+            // instruction corroborates this:
             //
-            // float:
-            // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798
+            // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#fdiv
+            Expression::Binary {
+                op: crate::BinaryOperator::Divide,
+                left,
+                right,
+            } if matches!(
+                func_ctx.resolve_type(expr, &module.types).scalar_kind(),
+                Some(ScalarKind::Sint | ScalarKind::Uint)
+            ) =>
+            {
+                write!(self.out, "{DIV_FUNCTION}(")?;
+                self.write_expr(module, left, func_ctx)?;
+                write!(self.out, ", ")?;
+                self.write_expr(module, right, func_ctx)?;
+                write!(self.out, ")")?;
+            }
+
+            Expression::Binary {
+                op: crate::BinaryOperator::Modulo,
+                left,
+                right,
+            } if matches!(
+                func_ctx.resolve_type(expr, &module.types).scalar_kind(),
+                Some(ScalarKind::Sint | ScalarKind::Uint)
+            ) =>
+            {
+                write!(self.out, "{MOD_FUNCTION}(")?;
+                self.write_expr(module, left, func_ctx)?;
+                write!(self.out, ", ")?;
+                self.write_expr(module, right, func_ctx)?;
+                write!(self.out, ")")?;
+            }
 
             // While HLSL supports float operands with the % operator it is only
             // defined in cases where both sides are either positive or negative.
@@ -3283,7 +3344,12 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
             Expression::Unary { op, expr } => {
                 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-operators#unary-operators
                 let op_str = match op {
-                    crate::UnaryOperator::Negate => "-",
+                    crate::UnaryOperator::Negate => {
+                        match func_ctx.resolve_type(expr, &module.types).scalar() {
+                            Some(Scalar::I32) => NEG_FUNCTION,
+                            _ => "-",
+                        }
+                    }
                     crate::UnaryOperator::LogicalNot => "!",
                     crate::UnaryOperator::BitwiseNot => "~",
                 };
@@ -3382,7 +3448,10 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
 
                 let fun = match fun {
                     // comparison
-                    Mf::Abs => Function::Regular("abs"),
+                    Mf::Abs => match func_ctx.resolve_type(arg, &module.types).scalar() {
+                        Some(Scalar::I32) => Function::Regular(ABS_FUNCTION),
+                        _ => Function::Regular("abs"),
+                    },
                     Mf::Min => Function::Regular("min"),
                     Mf::Max => Function::Regular("max"),
                     Mf::Clamp => Function::Regular("clamp"),
@@ -3668,11 +3737,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
                     // as non-32bit types are DXC only.
                     Function::MissingIntOverload(fun_name) => {
                         let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
-                        if let Some(Scalar {
-                            kind: ScalarKind::Sint,
-                            width: 4,
-                        }) = scalar_kind
-                        {
+                        if let Some(Scalar::I32) = scalar_kind {
                             write!(self.out, "asint({fun_name}(asuint(")?;
                             self.write_expr(module, arg, func_ctx)?;
                             write!(self.out, ")))")?;
@@ -3686,11 +3751,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
                     // as non-32bit types are DXC only.
                     Function::MissingIntReturnType(fun_name) => {
                         let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
-                        if let Some(Scalar {
-                            kind: ScalarKind::Sint,
-                            width: 4,
-                        }) = scalar_kind
-                        {
+                        if let Some(Scalar::I32) = scalar_kind {
                             write!(self.out, "asint({fun_name}(")?;
                             self.write_expr(module, arg, func_ctx)?;
                             write!(self.out, "))")?;
diff --git a/naga/src/back/msl/keywords.rs b/naga/src/back/msl/keywords.rs
index a4eabab234..674d37067e 100644
--- a/naga/src/back/msl/keywords.rs
+++ b/naga/src/back/msl/keywords.rs
@@ -341,5 +341,9 @@ pub const RESERVED: &[&str] = &[
     "DefaultConstructible",
     super::writer::FREXP_FUNCTION,
     super::writer::MODF_FUNCTION,
+    super::writer::ABS_FUNCTION,
+    super::writer::DIV_FUNCTION,
+    super::writer::MOD_FUNCTION,
+    super::writer::NEG_FUNCTION,
     super::writer::ARGUMENT_BUFFER_WRAPPER_STRUCT,
 ];
diff --git a/naga/src/back/msl/writer.rs b/naga/src/back/msl/writer.rs
index 5815499a45..bc77a7f6d0 100644
--- a/naga/src/back/msl/writer.rs
+++ b/naga/src/back/msl/writer.rs
@@ -2,8 +2,7 @@ use super::{sampler as sm, Error, LocationMode, Options, PipelineOptions, Transl
 use crate::{
     arena::{Handle, HandleSet},
     back::{self, Baked},
-    proc::index,
-    proc::{self, NameKey, TypeResolution},
+    proc::{self, index, ExpressionKindTracker, NameKey, TypeResolution},
     valid, FastHashMap, FastHashSet,
 };
 #[cfg(test)]
@@ -37,6 +36,10 @@ const RAY_QUERY_FUN_MAP_INTERSECTION: &str = "_map_intersection_type";
 pub(crate) const ATOMIC_COMP_EXCH_FUNCTION: &str = "naga_atomic_compare_exchange_weak_explicit";
 pub(crate) const MODF_FUNCTION: &str = "naga_modf";
 pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
+pub(crate) const ABS_FUNCTION: &str = "naga_abs";
+pub(crate) const DIV_FUNCTION: &str = "naga_div";
+pub(crate) const MOD_FUNCTION: &str = "naga_mod";
+pub(crate) const NEG_FUNCTION: &str = "naga_neg";
 /// For some reason, Metal does not let you have `metal::texture<..>*` as a buffer argument.
 /// However, if you put that texture inside a struct, everything is totally fine. This
 /// baffles me to no end.
@@ -370,6 +373,23 @@ impl TypedGlobalVariable<'_> {
     }
 }
 
+#[derive(Eq, PartialEq, Hash)]
+enum WrappedFunction {
+    UnaryOp {
+        op: crate::UnaryOperator,
+        ty: (Option<crate::VectorSize>, crate::Scalar),
+    },
+    BinaryOp {
+        op: crate::BinaryOperator,
+        left_ty: (Option<crate::VectorSize>, crate::Scalar),
+        right_ty: (Option<crate::VectorSize>, crate::Scalar),
+    },
+    Math {
+        fun: crate::MathFunction,
+        arg_ty: (Option<crate::VectorSize>, crate::Scalar),
+    },
+}
+
 pub struct Writer<W> {
     out: W,
     names: FastHashMap<NameKey, String>,
@@ -377,6 +397,7 @@ pub struct Writer<W> {
     /// Set of expressions that need to be baked to avoid unnecessary repetition in output
     need_bake_expressions: back::NeedBakeExpressions,
     namer: proc::Namer,
+    wrapped_functions: FastHashSet<WrappedFunction>,
     #[cfg(test)]
     put_expression_stack_pointers: FastHashSet<*const ()>,
     #[cfg(test)]
@@ -676,6 +697,7 @@ impl<W: Write> Writer<W> {
             named_expressions: Default::default(),
             need_bake_expressions: Default::default(),
             namer: proc::Namer::default(),
+            wrapped_functions: FastHashSet::default(),
             #[cfg(test)]
             put_expression_stack_pointers: Default::default(),
             #[cfg(test)]
@@ -1749,7 +1771,12 @@ impl<W: Write> Writer<W> {
             },
             crate::Expression::Unary { op, expr } => {
                 let op_str = match op {
-                    crate::UnaryOperator::Negate => "-",
+                    crate::UnaryOperator::Negate => {
+                        match context.resolve_type(expr).scalar_kind() {
+                            Some(crate::ScalarKind::Sint) => NEG_FUNCTION,
+                            _ => "-",
+                        }
+                    }
                     crate::UnaryOperator::LogicalNot => "!",
                     crate::UnaryOperator::BitwiseNot => "~",
                 };
@@ -1763,20 +1790,27 @@ impl<W: Write> Writer<W> {
                     .scalar_kind()
                     .ok_or(Error::UnsupportedBinaryOp(op))?;
 
-                // TODO: handle undefined behavior of BinaryOperator::Modulo
-                //
-                // sint:
-                // if right == 0 return 0
-                // if left == min(type_of(left)) && right == -1 return 0
-                // if sign(left) == -1 || sign(right) == -1 return result as defined by WGSL
-                //
-                // uint:
-                // if right == 0 return 0
-                //
-                // float:
-                // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798
-
-                if op == crate::BinaryOperator::Modulo && kind == crate::ScalarKind::Float {
+                if op == crate::BinaryOperator::Divide
+                    && (kind == crate::ScalarKind::Sint || kind == crate::ScalarKind::Uint)
+                {
+                    write!(self.out, "{DIV_FUNCTION}(")?;
+                    self.put_expression(left, context, true)?;
+                    write!(self.out, ", ")?;
+                    self.put_expression(right, context, true)?;
+                    write!(self.out, ")")?;
+                } else if op == crate::BinaryOperator::Modulo
+                    && (kind == crate::ScalarKind::Sint || kind == crate::ScalarKind::Uint)
+                {
+                    write!(self.out, "{MOD_FUNCTION}(")?;
+                    self.put_expression(left, context, true)?;
+                    write!(self.out, ", ")?;
+                    self.put_expression(right, context, true)?;
+                    write!(self.out, ")")?;
+                } else if op == crate::BinaryOperator::Modulo && kind == crate::ScalarKind::Float {
+                    // TODO: handle undefined behavior of BinaryOperator::Modulo
+                    //
+                    // float:
+                    // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798
                     write!(self.out, "{NAMESPACE}::fmod(")?;
                     self.put_expression(left, context, true)?;
                     write!(self.out, ", ")?;
@@ -2038,6 +2072,11 @@ impl<W: Write> Writer<W> {
                 }
 
                 match fun {
+                    Mf::Abs if arg_type.scalar_kind() == Some(crate::ScalarKind::Sint) => {
+                        write!(self.out, "{ABS_FUNCTION}(")?;
+                        self.put_expression(arg, context, true)?;
+                        write!(self.out, ")")?;
+                    }
                     Mf::Distance if scalar_argument => {
                         write!(self.out, "{NAMESPACE}::abs(")?;
                         self.put_expression(arg, context, false)?;
@@ -3752,6 +3791,7 @@ impl<W: Write> Writer<W> {
             &[CLAMPED_LOD_LOAD_PREFIX],
             &mut self.names,
         );
+        self.wrapped_functions.clear();
         self.struct_member_pads.clear();
 
         writeln!(
@@ -5018,6 +5058,269 @@ template <typename A>
         }
     }
 
+    pub(super) fn write_wrapped_functions(
+        &mut self,
+        module: &crate::Module,
+        func_ctx: &back::FunctionCtx,
+    ) -> BackendResult {
+        for (expr_handle, expr) in func_ctx.expressions.iter() {
+            match *expr {
+                crate::Expression::Unary { op, expr: operand } => {
+                    let operand_ty = func_ctx.resolve_type(operand, &module.types);
+                    match op {
+                        // Negating the TYPE_MIN of a two's complement signed integer
+                        // type causes overflow, which is undefined behaviour in MSL. To
+                        // avoid this we bitcast the value to unsigned and negate it,
+                        // then bitcast back to signed.
+                        // This adheres to the WGSL spec in that the negative of the
+                        // type's minimum value should equal to the minimum value.
+                        crate::UnaryOperator::Negate
+                            if operand_ty.scalar_kind() == Some(crate::ScalarKind::Sint) =>
+                        {
+                            let Some((vector_size, scalar)) = operand_ty.vector_size_and_scalar()
+                            else {
+                                continue;
+                            };
+                            let wrapped = WrappedFunction::UnaryOp {
+                                op,
+                                ty: (vector_size, scalar),
+                            };
+                            if !self.wrapped_functions.insert(wrapped) {
+                                continue;
+                            }
+
+                            let unsigned_scalar = crate::Scalar {
+                                kind: crate::ScalarKind::Uint,
+                                ..scalar
+                            };
+                            let mut type_name = String::new();
+                            let mut unsigned_type_name = String::new();
+                            match vector_size {
+                                None => {
+                                    put_numeric_type(&mut type_name, scalar, &[])?;
+                                    put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])?
+                                }
+                                Some(size) => {
+                                    put_numeric_type(&mut type_name, scalar, &[size])?;
+                                    put_numeric_type(
+                                        &mut unsigned_type_name,
+                                        unsigned_scalar,
+                                        &[size],
+                                    )?;
+                                }
+                            };
+
+                            writeln!(self.out, "{type_name} {NEG_FUNCTION}({type_name} val) {{")?;
+                            let level = back::Level(1);
+                            writeln!(self.out, "{level}return as_type<{type_name}>(-as_type<{unsigned_type_name}>(val));")?;
+                            writeln!(self.out, "}}")?;
+                            writeln!(self.out)?;
+                        }
+                        _ => {}
+                    }
+                }
+                crate::Expression::Binary { op, left, right } => {
+                    let expr_ty = func_ctx.resolve_type(expr_handle, &module.types);
+                    let left_ty = func_ctx.resolve_type(left, &module.types);
+                    let right_ty = func_ctx.resolve_type(right, &module.types);
+                    match (op, expr_ty.scalar_kind()) {
+                        // Signed integer division of TYPE_MIN / -1, or signed or
+                        // unsigned division by zero, gives an unspecified value in MSL.
+                        // We override the divisor to 1 in these cases.
+                        // This adheres to the WGSL spec in that:
+                        // * TYPE_MIN / -1 == TYPE_MIN
+                        // * x / 0 == x
+                        (
+                            crate::BinaryOperator::Divide,
+                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint),
+                        ) => {
+                            let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {
+                                continue;
+                            };
+                            let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {
+                                continue;
+                            };
+                            let wrapped = WrappedFunction::BinaryOp {
+                                op,
+                                left_ty: left_wrapped_ty,
+                                right_ty: right_wrapped_ty,
+                            };
+                            if !self.wrapped_functions.insert(wrapped) {
+                                continue;
+                            }
+
+                            let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar()
+                            else {
+                                continue;
+                            };
+                            let mut type_name = String::new();
+                            match vector_size {
+                                None => put_numeric_type(&mut type_name, scalar, &[])?,
+                                Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?,
+                            };
+                            writeln!(
+                                self.out,
+                                "{type_name} {DIV_FUNCTION}({type_name} lhs, {type_name} rhs) {{"
+                            )?;
+                            let level = back::Level(1);
+                            match scalar.kind {
+                                crate::ScalarKind::Sint => {
+                                    let min = -1i64 << (scalar.width as u32 * 8 - 1);
+                                    writeln!(self.out, "{level}return lhs / metal::select(rhs, 1, (lhs == {min} & rhs == -1) | (rhs == 0));")?
+                                }
+                                crate::ScalarKind::Uint => writeln!(
+                                    self.out,
+                                    "{level}return lhs / metal::select(rhs, 1u, rhs == 0u);"
+                                )?,
+                                _ => unreachable!(),
+                            }
+                            writeln!(self.out, "}}")?;
+                            writeln!(self.out)?;
+                        }
+                        // Integer modulo where one or both operands are negative, or the
+                        // divisor is zero, is undefined behaviour in MSL. To avoid this
+                        // we use the following equation:
+                        //
+                        // dividend - (dividend / divisor) * divisor
+                        //
+                        // overriding the divisor to 1 if either it is 0, or it is -1
+                        // and the dividend is TYPE_MIN.
+                        //
+                        // This adheres to the WGSL spec in that:
+                        // * TYPE_MIN % -1 == 0
+                        // * x % 0 == 0
+                        (
+                            crate::BinaryOperator::Modulo,
+                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint),
+                        ) => {
+                            let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {
+                                continue;
+                            };
+                            let Some((right_vector_size, right_scalar)) =
+                                right_ty.vector_size_and_scalar()
+                            else {
+                                continue;
+                            };
+                            let wrapped = WrappedFunction::BinaryOp {
+                                op,
+                                left_ty: left_wrapped_ty,
+                                right_ty: (right_vector_size, right_scalar),
+                            };
+                            if !self.wrapped_functions.insert(wrapped) {
+                                continue;
+                            }
+
+                            let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar()
+                            else {
+                                continue;
+                            };
+                            let mut type_name = String::new();
+                            match vector_size {
+                                None => put_numeric_type(&mut type_name, scalar, &[])?,
+                                Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?,
+                            };
+                            let mut rhs_type_name = String::new();
+                            match right_vector_size {
+                                None => put_numeric_type(&mut rhs_type_name, right_scalar, &[])?,
+                                Some(size) => {
+                                    put_numeric_type(&mut rhs_type_name, right_scalar, &[size])?
+                                }
+                            };
+
+                            writeln!(
+                                self.out,
+                                "{type_name} {MOD_FUNCTION}({type_name} lhs, {type_name} rhs) {{"
+                            )?;
+                            let level = back::Level(1);
+                            match scalar.kind {
+                                crate::ScalarKind::Sint => {
+                                    let min = -1i64 << (scalar.width as u32 * 8 - 1);
+                                    writeln!(self.out, "{level}{rhs_type_name} divisor = metal::select(rhs, 1, (lhs == {min} & rhs == -1) | (rhs == 0));")?;
+                                    writeln!(
+                                        self.out,
+                                        "{level}return lhs - (lhs / divisor) * divisor;"
+                                    )?
+                                }
+                                crate::ScalarKind::Uint => writeln!(
+                                    self.out,
+                                    "{level}return lhs % metal::select(rhs, 1u, rhs == 0u);"
+                                )?,
+                                _ => unreachable!(),
+                            }
+                            writeln!(self.out, "}}")?;
+                            writeln!(self.out)?;
+                        }
+                        _ => {}
+                    }
+                }
+                crate::Expression::Math {
+                    fun,
+                    arg,
+                    arg1: _,
+                    arg2: _,
+                    arg3: _,
+                } => {
+                    let arg_ty = func_ctx.resolve_type(arg, &module.types);
+                    match fun {
+                        // Taking the absolute value of the TYPE_MIN of a two's
+                        // complement signed integer type causes overflow, which is
+                        // undefined behaviour in MSL. To avoid this, when the value is
+                        // negative we bitcast the value to unsigned and negate it, then
+                        // bitcast back to signed.
+                        // This adheres to the WGSL spec in that the absolute of the
+                        // type's minimum value should equal to the minimum value.
+                        crate::MathFunction::Abs
+                            if arg_ty.scalar_kind() == Some(crate::ScalarKind::Sint) =>
+                        {
+                            let Some((vector_size, scalar)) = arg_ty.vector_size_and_scalar()
+                            else {
+                                continue;
+                            };
+                            let wrapped = WrappedFunction::Math {
+                                fun,
+                                arg_ty: (vector_size, scalar),
+                            };
+                            if !self.wrapped_functions.insert(wrapped) {
+                                continue;
+                            }
+
+                            let unsigned_scalar = crate::Scalar {
+                                kind: crate::ScalarKind::Uint,
+                                ..scalar
+                            };
+                            let mut type_name = String::new();
+                            let mut unsigned_type_name = String::new();
+                            match vector_size {
+                                None => {
+                                    put_numeric_type(&mut type_name, scalar, &[])?;
+                                    put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])?
+                                }
+                                Some(size) => {
+                                    put_numeric_type(&mut type_name, scalar, &[size])?;
+                                    put_numeric_type(
+                                        &mut unsigned_type_name,
+                                        unsigned_scalar,
+                                        &[size],
+                                    )?;
+                                }
+                            };
+
+                            writeln!(self.out, "{type_name} {ABS_FUNCTION}({type_name} val) {{")?;
+                            let level = back::Level(1);
+                            writeln!(self.out, "{level}return metal::select(as_type<{type_name}>(-as_type<{unsigned_type_name}>(val)), val, val >= 0);")?;
+                            writeln!(self.out, "}}")?;
+                            writeln!(self.out)?;
+                        }
+                        _ => {}
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        Ok(())
+    }
+
     // Returns the array of mapped entry point names.
     fn write_functions(
         &mut self,
@@ -5129,6 +5432,17 @@ template <typename A>
                 fun_handle
             );
 
+            let ctx = back::FunctionCtx {
+                ty: back::FunctionType::Function(fun_handle),
+                info: &mod_info[fun_handle],
+                expressions: &fun.expressions,
+                named_expressions: &fun.named_expressions,
+                expr_kind_tracker: ExpressionKindTracker::from_arena(&fun.expressions),
+            };
+
+            writeln!(self.out)?;
+            self.write_wrapped_functions(module, &ctx)?;
+
             let fun_info = &mod_info[fun_handle];
             pass_through_globals.clear();
             let mut needs_buffer_sizes = false;
@@ -5141,7 +5455,6 @@ template <typename A>
                 }
             }
 
-            writeln!(self.out)?;
             let fun_name = &self.names[&NameKey::Function(fun_handle)];
             match fun.result {
                 Some(ref result) => {
@@ -5279,6 +5592,16 @@ template <typename A>
                 ep_index
             );
 
+            let ctx = back::FunctionCtx {
+                ty: back::FunctionType::EntryPoint(ep_index as u16),
+                info: fun_info,
+                expressions: &fun.expressions,
+                named_expressions: &fun.named_expressions,
+                expr_kind_tracker: ExpressionKindTracker::from_arena(&fun.expressions),
+            };
+
+            self.write_wrapped_functions(module, &ctx)?;
+
             let (em_str, in_mode, out_mode, can_vertex_pull) = match ep.stage {
                 crate::ShaderStage::Vertex => (
                     "vertex",
diff --git a/naga/src/back/spv/block.rs b/naga/src/back/spv/block.rs
index 1e87e2e6d1..6b26c1c2aa 100644
--- a/naga/src/back/spv/block.rs
+++ b/naga/src/back/spv/block.rs
@@ -4,7 +4,8 @@ Implementations for `BlockContext` methods.
 
 use super::{
     index::BoundsCheckResult, selection::Selection, Block, BlockContext, Dimension, Error,
-    Instruction, LocalType, LookupType, NumericType, ResultMember, Writer, WriterFlags,
+    Instruction, LocalType, LookupType, NumericType, ResultMember, WrappedFunction, Writer,
+    WriterFlags,
 };
 use crate::{arena::Handle, proc::index::GuardedIndex, Statement};
 use spirv::Word;
@@ -578,201 +579,227 @@ impl BlockContext<'_> {
                 let id = self.gen_id();
                 let left_id = self.cached[left];
                 let right_id = self.cached[right];
+                let left_type_id = self.get_expression_type_id(&self.fun_info[left].ty);
+                let right_type_id = self.get_expression_type_id(&self.fun_info[right].ty);
 
-                let left_ty_inner = self.fun_info[left].ty.inner_with(&self.ir_module.types);
-                let right_ty_inner = self.fun_info[right].ty.inner_with(&self.ir_module.types);
+                if let Some(function_id) =
+                    self.writer
+                        .wrapped_functions
+                        .get(&WrappedFunction::BinaryOp {
+                            op,
+                            left_type_id,
+                            right_type_id,
+                        })
+                {
+                    block.body.push(Instruction::function_call(
+                        result_type_id,
+                        id,
+                        *function_id,
+                        &[left_id, right_id],
+                    ));
+                } else {
+                    let left_ty_inner = self.fun_info[left].ty.inner_with(&self.ir_module.types);
+                    let right_ty_inner = self.fun_info[right].ty.inner_with(&self.ir_module.types);
 
-                let left_dimension = get_dimension(left_ty_inner);
-                let right_dimension = get_dimension(right_ty_inner);
+                    let left_dimension = get_dimension(left_ty_inner);
+                    let right_dimension = get_dimension(right_ty_inner);
 
-                let mut reverse_operands = false;
+                    let mut reverse_operands = false;
 
-                let spirv_op = match op {
-                    crate::BinaryOperator::Add => match *left_ty_inner {
-                        crate::TypeInner::Scalar(scalar)
-                        | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
-                            crate::ScalarKind::Float => spirv::Op::FAdd,
-                            _ => spirv::Op::IAdd,
-                        },
-                        crate::TypeInner::Matrix {
-                            columns,
-                            rows,
-                            scalar,
-                        } => {
-                            self.write_matrix_matrix_column_op(
-                                block,
-                                id,
-                                result_type_id,
-                                left_id,
-                                right_id,
+                    let spirv_op = match op {
+                        crate::BinaryOperator::Add => match *left_ty_inner {
+                            crate::TypeInner::Scalar(scalar)
+                            | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
+                                crate::ScalarKind::Float => spirv::Op::FAdd,
+                                _ => spirv::Op::IAdd,
+                            },
+                            crate::TypeInner::Matrix {
                                 columns,
                                 rows,
-                                scalar.width,
-                                spirv::Op::FAdd,
-                            );
+                                scalar,
+                            } => {
+                                self.write_matrix_matrix_column_op(
+                                    block,
+                                    id,
+                                    result_type_id,
+                                    left_id,
+                                    right_id,
+                                    columns,
+                                    rows,
+                                    scalar.width,
+                                    spirv::Op::FAdd,
+                                );
 
-                            self.cached[expr_handle] = id;
-                            return Ok(());
-                        }
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::Subtract => match *left_ty_inner {
-                        crate::TypeInner::Scalar(scalar)
-                        | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
-                            crate::ScalarKind::Float => spirv::Op::FSub,
-                            _ => spirv::Op::ISub,
+                                self.cached[expr_handle] = id;
+                                return Ok(());
+                            }
+                            _ => unimplemented!(),
                         },
-                        crate::TypeInner::Matrix {
-                            columns,
-                            rows,
-                            scalar,
-                        } => {
-                            self.write_matrix_matrix_column_op(
-                                block,
-                                id,
-                                result_type_id,
-                                left_id,
-                                right_id,
+                        crate::BinaryOperator::Subtract => match *left_ty_inner {
+                            crate::TypeInner::Scalar(scalar)
+                            | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
+                                crate::ScalarKind::Float => spirv::Op::FSub,
+                                _ => spirv::Op::ISub,
+                            },
+                            crate::TypeInner::Matrix {
                                 columns,
                                 rows,
-                                scalar.width,
-                                spirv::Op::FSub,
-                            );
+                                scalar,
+                            } => {
+                                self.write_matrix_matrix_column_op(
+                                    block,
+                                    id,
+                                    result_type_id,
+                                    left_id,
+                                    right_id,
+                                    columns,
+                                    rows,
+                                    scalar.width,
+                                    spirv::Op::FSub,
+                                );
 
-                            self.cached[expr_handle] = id;
-                            return Ok(());
-                        }
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::Multiply => match (left_dimension, right_dimension) {
-                        (Dimension::Scalar, Dimension::Vector) => {
-                            self.write_vector_scalar_mult(
-                                block,
-                                id,
-                                result_type_id,
-                                right_id,
-                                left_id,
-                                right_ty_inner,
-                            );
+                                self.cached[expr_handle] = id;
+                                return Ok(());
+                            }
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::Multiply => {
+                            match (left_dimension, right_dimension) {
+                                (Dimension::Scalar, Dimension::Vector) => {
+                                    self.write_vector_scalar_mult(
+                                        block,
+                                        id,
+                                        result_type_id,
+                                        right_id,
+                                        left_id,
+                                        right_ty_inner,
+                                    );
 
-                            self.cached[expr_handle] = id;
-                            return Ok(());
-                        }
-                        (Dimension::Vector, Dimension::Scalar) => {
-                            self.write_vector_scalar_mult(
-                                block,
-                                id,
-                                result_type_id,
-                                left_id,
-                                right_id,
-                                left_ty_inner,
-                            );
+                                    self.cached[expr_handle] = id;
+                                    return Ok(());
+                                }
+                                (Dimension::Vector, Dimension::Scalar) => {
+                                    self.write_vector_scalar_mult(
+                                        block,
+                                        id,
+                                        result_type_id,
+                                        left_id,
+                                        right_id,
+                                        left_ty_inner,
+                                    );
 
-                            self.cached[expr_handle] = id;
-                            return Ok(());
-                        }
-                        (Dimension::Vector, Dimension::Matrix) => spirv::Op::VectorTimesMatrix,
-                        (Dimension::Matrix, Dimension::Scalar) => spirv::Op::MatrixTimesScalar,
-                        (Dimension::Scalar, Dimension::Matrix) => {
-                            reverse_operands = true;
-                            spirv::Op::MatrixTimesScalar
-                        }
-                        (Dimension::Matrix, Dimension::Vector) => spirv::Op::MatrixTimesVector,
-                        (Dimension::Matrix, Dimension::Matrix) => spirv::Op::MatrixTimesMatrix,
-                        (Dimension::Vector, Dimension::Vector)
-                        | (Dimension::Scalar, Dimension::Scalar)
-                            if left_ty_inner.scalar_kind() == Some(crate::ScalarKind::Float) =>
-                        {
-                            spirv::Op::FMul
-                        }
-                        (Dimension::Vector, Dimension::Vector)
-                        | (Dimension::Scalar, Dimension::Scalar) => spirv::Op::IMul,
-                    },
-                    crate::BinaryOperator::Divide => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint) => spirv::Op::SDiv,
-                        Some(crate::ScalarKind::Uint) => spirv::Op::UDiv,
-                        Some(crate::ScalarKind::Float) => spirv::Op::FDiv,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::Modulo => match left_ty_inner.scalar_kind() {
-                        // TODO: handle undefined behavior
-                        // if right == 0 return 0
-                        // if left == min(type_of(left)) && right == -1 return 0
-                        Some(crate::ScalarKind::Sint) => spirv::Op::SRem,
-                        // TODO: handle undefined behavior
-                        // if right == 0 return 0
-                        Some(crate::ScalarKind::Uint) => spirv::Op::UMod,
-                        // TODO: handle undefined behavior
-                        // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798
-                        Some(crate::ScalarKind::Float) => spirv::Op::FRem,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::Equal => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
-                            spirv::Op::IEqual
-                        }
-                        Some(crate::ScalarKind::Float) => spirv::Op::FOrdEqual,
-                        Some(crate::ScalarKind::Bool) => spirv::Op::LogicalEqual,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::NotEqual => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
-                            spirv::Op::INotEqual
+                                    self.cached[expr_handle] = id;
+                                    return Ok(());
+                                }
+                                (Dimension::Vector, Dimension::Matrix) => {
+                                    spirv::Op::VectorTimesMatrix
+                                }
+                                (Dimension::Matrix, Dimension::Scalar) => {
+                                    spirv::Op::MatrixTimesScalar
+                                }
+                                (Dimension::Scalar, Dimension::Matrix) => {
+                                    reverse_operands = true;
+                                    spirv::Op::MatrixTimesScalar
+                                }
+                                (Dimension::Matrix, Dimension::Vector) => {
+                                    spirv::Op::MatrixTimesVector
+                                }
+                                (Dimension::Matrix, Dimension::Matrix) => {
+                                    spirv::Op::MatrixTimesMatrix
+                                }
+                                (Dimension::Vector, Dimension::Vector)
+                                | (Dimension::Scalar, Dimension::Scalar)
+                                    if left_ty_inner.scalar_kind()
+                                        == Some(crate::ScalarKind::Float) =>
+                                {
+                                    spirv::Op::FMul
+                                }
+                                (Dimension::Vector, Dimension::Vector)
+                                | (Dimension::Scalar, Dimension::Scalar) => spirv::Op::IMul,
+                            }
                         }
-                        Some(crate::ScalarKind::Float) => spirv::Op::FOrdNotEqual,
-                        Some(crate::ScalarKind::Bool) => spirv::Op::LogicalNotEqual,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::Less => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint) => spirv::Op::SLessThan,
-                        Some(crate::ScalarKind::Uint) => spirv::Op::ULessThan,
-                        Some(crate::ScalarKind::Float) => spirv::Op::FOrdLessThan,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::LessEqual => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint) => spirv::Op::SLessThanEqual,
-                        Some(crate::ScalarKind::Uint) => spirv::Op::ULessThanEqual,
-                        Some(crate::ScalarKind::Float) => spirv::Op::FOrdLessThanEqual,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::Greater => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint) => spirv::Op::SGreaterThan,
-                        Some(crate::ScalarKind::Uint) => spirv::Op::UGreaterThan,
-                        Some(crate::ScalarKind::Float) => spirv::Op::FOrdGreaterThan,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::GreaterEqual => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint) => spirv::Op::SGreaterThanEqual,
-                        Some(crate::ScalarKind::Uint) => spirv::Op::UGreaterThanEqual,
-                        Some(crate::ScalarKind::Float) => spirv::Op::FOrdGreaterThanEqual,
-                        _ => unimplemented!(),
-                    },
-                    crate::BinaryOperator::And => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Bool) => spirv::Op::LogicalAnd,
-                        _ => spirv::Op::BitwiseAnd,
-                    },
-                    crate::BinaryOperator::ExclusiveOr => spirv::Op::BitwiseXor,
-                    crate::BinaryOperator::InclusiveOr => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Bool) => spirv::Op::LogicalOr,
-                        _ => spirv::Op::BitwiseOr,
-                    },
-                    crate::BinaryOperator::LogicalAnd => spirv::Op::LogicalAnd,
-                    crate::BinaryOperator::LogicalOr => spirv::Op::LogicalOr,
-                    crate::BinaryOperator::ShiftLeft => spirv::Op::ShiftLeftLogical,
-                    crate::BinaryOperator::ShiftRight => match left_ty_inner.scalar_kind() {
-                        Some(crate::ScalarKind::Sint) => spirv::Op::ShiftRightArithmetic,
-                        Some(crate::ScalarKind::Uint) => spirv::Op::ShiftRightLogical,
-                        _ => unimplemented!(),
-                    },
-                };
+                        crate::BinaryOperator::Divide => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint) => spirv::Op::SDiv,
+                            Some(crate::ScalarKind::Uint) => spirv::Op::UDiv,
+                            Some(crate::ScalarKind::Float) => spirv::Op::FDiv,
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::Modulo => match left_ty_inner.scalar_kind() {
+                            // TODO: handle undefined behavior
+                            // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798
+                            Some(crate::ScalarKind::Float) => spirv::Op::FRem,
+                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
+                                unreachable!("Should have been handled by wrapped function")
+                            }
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::Equal => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
+                                spirv::Op::IEqual
+                            }
+                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdEqual,
+                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalEqual,
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::NotEqual => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
+                                spirv::Op::INotEqual
+                            }
+                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdNotEqual,
+                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalNotEqual,
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::Less => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint) => spirv::Op::SLessThan,
+                            Some(crate::ScalarKind::Uint) => spirv::Op::ULessThan,
+                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdLessThan,
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::LessEqual => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint) => spirv::Op::SLessThanEqual,
+                            Some(crate::ScalarKind::Uint) => spirv::Op::ULessThanEqual,
+                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdLessThanEqual,
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::Greater => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint) => spirv::Op::SGreaterThan,
+                            Some(crate::ScalarKind::Uint) => spirv::Op::UGreaterThan,
+                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdGreaterThan,
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::GreaterEqual => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint) => spirv::Op::SGreaterThanEqual,
+                            Some(crate::ScalarKind::Uint) => spirv::Op::UGreaterThanEqual,
+                            Some(crate::ScalarKind::Float) => spirv::Op::FOrdGreaterThanEqual,
+                            _ => unimplemented!(),
+                        },
+                        crate::BinaryOperator::And => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalAnd,
+                            _ => spirv::Op::BitwiseAnd,
+                        },
+                        crate::BinaryOperator::ExclusiveOr => spirv::Op::BitwiseXor,
+                        crate::BinaryOperator::InclusiveOr => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Bool) => spirv::Op::LogicalOr,
+                            _ => spirv::Op::BitwiseOr,
+                        },
+                        crate::BinaryOperator::LogicalAnd => spirv::Op::LogicalAnd,
+                        crate::BinaryOperator::LogicalOr => spirv::Op::LogicalOr,
+                        crate::BinaryOperator::ShiftLeft => spirv::Op::ShiftLeftLogical,
+                        crate::BinaryOperator::ShiftRight => match left_ty_inner.scalar_kind() {
+                            Some(crate::ScalarKind::Sint) => spirv::Op::ShiftRightArithmetic,
+                            Some(crate::ScalarKind::Uint) => spirv::Op::ShiftRightLogical,
+                            _ => unimplemented!(),
+                        },
+                    };
 
-                block.body.push(Instruction::binary(
-                    spirv_op,
-                    result_type_id,
-                    id,
-                    if reverse_operands { right_id } else { left_id },
-                    if reverse_operands { left_id } else { right_id },
-                ));
+                    block.body.push(Instruction::binary(
+                        spirv_op,
+                        result_type_id,
+                        id,
+                        if reverse_operands { right_id } else { left_id },
+                        if reverse_operands { left_id } else { right_id },
+                    ));
+                }
                 id
             }
             crate::Expression::Math {
diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs
index e6164098d3..5a1c90326a 100644
--- a/naga/src/back/spv/mod.rs
+++ b/naga/src/back/spv/mod.rs
@@ -302,6 +302,26 @@ impl NumericType {
             _ => None,
         }
     }
+
+    const fn scalar(self) -> crate::Scalar {
+        match self {
+            NumericType::Scalar(scalar)
+            | NumericType::Vector { scalar, .. }
+            | NumericType::Matrix { scalar, .. } => scalar,
+        }
+    }
+
+    const fn with_scalar(self, scalar: crate::Scalar) -> Self {
+        match self {
+            NumericType::Scalar(_) => NumericType::Scalar(scalar),
+            NumericType::Vector { size, .. } => NumericType::Vector { size, scalar },
+            NumericType::Matrix { columns, rows, .. } => NumericType::Matrix {
+                columns,
+                rows,
+                scalar,
+            },
+        }
+    }
 }
 
 /// A SPIR-V type constructed during code generation.
@@ -475,6 +495,18 @@ enum Dimension {
     Matrix,
 }
 
+/// Key used to look up an operation which we have wrapped in a helper
+/// function, which should be called instead of directly emitting code
+/// for the expression. See [`Writer::wrapped_functions`].
+#[derive(Debug, Eq, PartialEq, Hash)]
+enum WrappedFunction {
+    BinaryOp {
+        op: crate::BinaryOperator,
+        left_type_id: Word,
+        right_type_id: Word,
+    },
+}
+
 /// A map from evaluated [`Expression`](crate::Expression)s to their SPIR-V ids.
 ///
 /// When we emit code to evaluate a given `Expression`, we record the
@@ -752,6 +784,10 @@ pub struct Writer {
     lookup_type: crate::FastHashMap<LookupType, Word>,
     lookup_function: crate::FastHashMap<Handle<crate::Function>, Word>,
     lookup_function_type: crate::FastHashMap<LookupFunctionType, Word>,
+    /// Operations which have been wrapped in a helper function. The value is
+    /// the ID of the function, which should be called instead of emitting code
+    /// for the operation directly.
+    wrapped_functions: crate::FastHashMap<WrappedFunction, Word>,
     /// Indexed by const-expression handle indexes
     constant_ids: HandleVec<crate::Expression, Word>,
     cached_constants: crate::FastHashMap<CachedConstant, Word>,
diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs
index a8bac54052..097791ef33 100644
--- a/naga/src/back/spv/writer.rs
+++ b/naga/src/back/spv/writer.rs
@@ -8,7 +8,7 @@ use super::{
 };
 use crate::{
     arena::{Handle, HandleVec, UniqueArena},
-    back::spv::BindingInfo,
+    back::spv::{BindingInfo, WrappedFunction},
     proc::{Alignment, TypeResolution},
     valid::{FunctionInfo, ModuleInfo},
 };
@@ -74,6 +74,7 @@ impl Writer {
             lookup_type: crate::FastHashMap::default(),
             lookup_function: crate::FastHashMap::default(),
             lookup_function_type: crate::FastHashMap::default(),
+            wrapped_functions: crate::FastHashMap::default(),
             constant_ids: HandleVec::new(),
             cached_constants: crate::FastHashMap::default(),
             global_variables: HandleVec::new(),
@@ -127,6 +128,7 @@ impl Writer {
             lookup_type: take(&mut self.lookup_type).recycle(),
             lookup_function: take(&mut self.lookup_function).recycle(),
             lookup_function_type: take(&mut self.lookup_function_type).recycle(),
+            wrapped_functions: take(&mut self.wrapped_functions).recycle(),
             constant_ids: take(&mut self.constant_ids).recycle(),
             cached_constants: take(&mut self.cached_constants).recycle(),
             global_variables: take(&mut self.global_variables).recycle(),
@@ -221,6 +223,10 @@ impl Writer {
         self.get_type_id(lookup_ty)
     }
 
+    pub(super) fn get_localtype_id(&mut self, local: LocalType) -> Word {
+        self.get_type_id(LookupType::Local(local))
+    }
+
     pub(super) fn get_pointer_id(
         &mut self,
         handle: Handle<crate::Type>,
@@ -305,6 +311,243 @@ impl Writer {
             .push(Instruction::decorate(id, decoration, operands));
     }
 
+    /// Emits code for any wrapper functions required by the expressions in ir_function.
+    /// The IDs of any emitted functions will be stored in [`Self::wrapped_functions`].
+    fn write_wrapped_functions(
+        &mut self,
+        ir_function: &crate::Function,
+        info: &FunctionInfo,
+        ir_module: &crate::Module,
+    ) -> Result<(), Error> {
+        log::trace!("Generating wrapped functions for {:?}", ir_function.name);
+
+        for (expr_handle, expr) in ir_function.expressions.iter() {
+            match *expr {
+                crate::Expression::Binary { op, left, right } => {
+                    let expr_ty_inner = info[expr_handle].ty.inner_with(&ir_module.types);
+                    if let Some(expr_ty) = NumericType::from_inner(expr_ty_inner) {
+                        match (op, expr_ty.scalar().kind) {
+                            // Division and modulo are undefined behaviour when the
+                            // dividend is the minimum representable value and the divisor
+                            // is negative one, or when the divisor is zero. These wrapped
+                            // functions override the divisor to one in these cases,
+                            // matching the WGSL spec.
+                            (
+                                crate::BinaryOperator::Divide | crate::BinaryOperator::Modulo,
+                                crate::ScalarKind::Sint | crate::ScalarKind::Uint,
+                            ) => {
+                                self.write_wrapped_binary_op(
+                                    op,
+                                    expr_ty,
+                                    &info[left].ty,
+                                    &info[right].ty,
+                                )?;
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Write a SPIR-V function that performs the operator `op` with Naga IR semantics.
+    ///
+    /// Define a function that performs an integer division or modulo operation,
+    /// except that using a divisor of zero or causing signed overflow with a
+    /// divisor of -1 returns the numerator unchanged, rather than exhibiting
+    /// undefined behavior.
+    ///
+    /// Store the generated function's id in the [`wrapped_functions`] table.
+    ///
+    /// The operator `op` must be either [`Divide`] or [`Modulo`].
+    ///
+    /// # Panics
+    ///
+    /// The `return_type`, `left_type` or `right_type` arguments must all be
+    /// integer scalars or vectors. If not, this function panics.
+    ///
+    /// [`wrapped_functions`]: Writer::wrapped_functions
+    /// [`Divide`]: crate::BinaryOperator::Divide
+    /// [`Modulo`]: crate::BinaryOperator::Modulo
+    fn write_wrapped_binary_op(
+        &mut self,
+        op: crate::BinaryOperator,
+        return_type: NumericType,
+        left_type: &TypeResolution,
+        right_type: &TypeResolution,
+    ) -> Result<(), Error> {
+        let return_type_id = self.get_localtype_id(LocalType::Numeric(return_type));
+        let left_type_id = self.get_expression_type_id(left_type);
+        let right_type_id = self.get_expression_type_id(right_type);
+
+        // Check if we've already emitted this function.
+        let wrapped = WrappedFunction::BinaryOp {
+            op,
+            left_type_id,
+            right_type_id,
+        };
+        let function_id = match self.wrapped_functions.entry(wrapped) {
+            Entry::Occupied(_) => return Ok(()),
+            Entry::Vacant(e) => *e.insert(self.id_gen.next()),
+        };
+
+        let scalar = return_type.scalar();
+
+        if self.flags.contains(WriterFlags::DEBUG) {
+            let function_name = match op {
+                crate::BinaryOperator::Divide => "naga_div",
+                crate::BinaryOperator::Modulo => "naga_mod",
+                _ => unreachable!(),
+            };
+            self.debugs
+                .push(Instruction::name(function_id, function_name));
+        }
+        let mut function = Function::default();
+
+        let function_type_id = self.get_function_type(LookupFunctionType {
+            parameter_type_ids: vec![left_type_id, right_type_id],
+            return_type_id,
+        });
+        function.signature = Some(Instruction::function(
+            return_type_id,
+            function_id,
+            spirv::FunctionControl::empty(),
+            function_type_id,
+        ));
+
+        let lhs_id = self.id_gen.next();
+        let rhs_id = self.id_gen.next();
+        if self.flags.contains(WriterFlags::DEBUG) {
+            self.debugs.push(Instruction::name(lhs_id, "lhs"));
+            self.debugs.push(Instruction::name(rhs_id, "rhs"));
+        }
+        let left_par = Instruction::function_parameter(left_type_id, lhs_id);
+        let right_par = Instruction::function_parameter(right_type_id, rhs_id);
+        for instruction in [left_par, right_par] {
+            function.parameters.push(FunctionArgument {
+                instruction,
+                handle_id: 0,
+            });
+        }
+
+        let label_id = self.id_gen.next();
+        let mut block = Block::new(label_id);
+
+        let bool_type = return_type.with_scalar(crate::Scalar::BOOL);
+        let bool_type_id = self.get_type_id(LookupType::Local(LocalType::Numeric(bool_type)));
+
+        let maybe_splat_const = |writer: &mut Self, const_id| match return_type {
+            NumericType::Scalar(_) => const_id,
+            NumericType::Vector { size, .. } => {
+                let constituent_ids = [const_id; crate::VectorSize::MAX];
+                writer.get_constant_composite(
+                    LookupType::Local(LocalType::Numeric(return_type)),
+                    &constituent_ids[..size as usize],
+                )
+            }
+            NumericType::Matrix { .. } => unreachable!(),
+        };
+
+        let const_zero_id = self.get_constant_scalar_with(0, scalar)?;
+        let composite_zero_id = maybe_splat_const(self, const_zero_id);
+        let rhs_eq_zero_id = self.id_gen.next();
+        block.body.push(Instruction::binary(
+            spirv::Op::IEqual,
+            bool_type_id,
+            rhs_eq_zero_id,
+            rhs_id,
+            composite_zero_id,
+        ));
+        let divisor_selector_id = match scalar.kind {
+            crate::ScalarKind::Sint => {
+                let (const_min_id, const_neg_one_id) = match scalar.width {
+                    4 => Ok((
+                        self.get_constant_scalar(crate::Literal::I32(i32::MIN)),
+                        self.get_constant_scalar(crate::Literal::I32(-1i32)),
+                    )),
+                    8 => Ok((
+                        self.get_constant_scalar(crate::Literal::I64(i64::MIN)),
+                        self.get_constant_scalar(crate::Literal::I64(-1i64)),
+                    )),
+                    _ => Err(Error::Validation("Unexpected scalar width")),
+                }?;
+                let composite_min_id = maybe_splat_const(self, const_min_id);
+                let composite_neg_one_id = maybe_splat_const(self, const_neg_one_id);
+
+                let lhs_eq_int_min_id = self.id_gen.next();
+                block.body.push(Instruction::binary(
+                    spirv::Op::IEqual,
+                    bool_type_id,
+                    lhs_eq_int_min_id,
+                    lhs_id,
+                    composite_min_id,
+                ));
+                let rhs_eq_neg_one_id = self.id_gen.next();
+                block.body.push(Instruction::binary(
+                    spirv::Op::IEqual,
+                    bool_type_id,
+                    rhs_eq_neg_one_id,
+                    rhs_id,
+                    composite_neg_one_id,
+                ));
+                let lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
+                block.body.push(Instruction::binary(
+                    spirv::Op::LogicalAnd,
+                    bool_type_id,
+                    lhs_eq_int_min_and_rhs_eq_neg_one_id,
+                    lhs_eq_int_min_id,
+                    rhs_eq_neg_one_id,
+                ));
+                let rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
+                block.body.push(Instruction::binary(
+                    spirv::Op::LogicalOr,
+                    bool_type_id,
+                    rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id,
+                    rhs_eq_zero_id,
+                    lhs_eq_int_min_and_rhs_eq_neg_one_id,
+                ));
+                rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id
+            }
+            crate::ScalarKind::Uint => rhs_eq_zero_id,
+            _ => unreachable!(),
+        };
+
+        let const_one_id = self.get_constant_scalar_with(1, scalar)?;
+        let composite_one_id = maybe_splat_const(self, const_one_id);
+        let divisor_id = self.id_gen.next();
+        block.body.push(Instruction::select(
+            right_type_id,
+            divisor_id,
+            divisor_selector_id,
+            composite_one_id,
+            rhs_id,
+        ));
+        let op = match (op, scalar.kind) {
+            (crate::BinaryOperator::Divide, crate::ScalarKind::Sint) => spirv::Op::SDiv,
+            (crate::BinaryOperator::Divide, crate::ScalarKind::Uint) => spirv::Op::UDiv,
+            (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint) => spirv::Op::SRem,
+            (crate::BinaryOperator::Modulo, crate::ScalarKind::Uint) => spirv::Op::UMod,
+            _ => unreachable!(),
+        };
+        let return_id = self.id_gen.next();
+        block.body.push(Instruction::binary(
+            op,
+            return_type_id,
+            return_id,
+            lhs_id,
+            divisor_id,
+        ));
+
+        function.consume(block, Instruction::return_value(return_id));
+        function.to_words(&mut self.logical_layout.function_definitions);
+        Instruction::function_end().to_words(&mut self.logical_layout.function_definitions);
+        Ok(())
+    }
+
     fn write_function(
         &mut self,
         ir_function: &crate::Function,
@@ -313,6 +556,8 @@ impl Writer {
         mut interface: Option<FunctionInterface>,
         debug_info: &Option<DebugInfoInner>,
     ) -> Result<Word, Error> {
+        self.write_wrapped_functions(ir_function, info, ir_module)?;
+
         log::trace!("Generating code for {:?}", ir_function.name);
         let mut function = Function::default();
 
@@ -919,7 +1164,7 @@ impl Writer {
             }
             LocalType::Image(image) => {
                 let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));
-                let type_id = self.get_type_id(LookupType::Local(local_type));
+                let type_id = self.get_localtype_id(local_type);
                 Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)
             }
             LocalType::Sampler => Instruction::type_sampler(id),
diff --git a/naga/src/proc/type_methods.rs b/naga/src/proc/type_methods.rs
index 6c5520c9d5..351f4c5368 100644
--- a/naga/src/proc/type_methods.rs
+++ b/naga/src/proc/type_methods.rs
@@ -259,4 +259,26 @@ impl crate::TypeInner {
             _ => return None,
         })
     }
+
+    /// If the type is a Vector or a Scalar return a tuple of the vector size (or None
+    /// for Scalars), and the scalar kind. Returns (None, None) for other types.
+    pub const fn vector_size_and_scalar(
+        &self,
+    ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
+        match *self {
+            crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
+            crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
+            crate::TypeInner::Matrix { .. }
+            | crate::TypeInner::Atomic(_)
+            | crate::TypeInner::Pointer { .. }
+            | crate::TypeInner::ValuePointer { .. }
+            | crate::TypeInner::Array { .. }
+            | crate::TypeInner::Struct { .. }
+            | crate::TypeInner::Image { .. }
+            | crate::TypeInner::Sampler { .. }
+            | crate::TypeInner::AccelerationStructure
+            | crate::TypeInner::RayQuery
+            | crate::TypeInner::BindingArray { .. } => None,
+        }
+    }
 }


Test patch:
diff --git a/naga/tests/in/operators.wgsl b/naga/tests/in/operators.wgsl
index 293b84ab11..5b621988e9 100644
--- a/naga/tests/in/operators.wgsl
+++ b/naga/tests/in/operators.wgsl
@@ -293,13 +293,23 @@ fn main(@builtin(workgroup_id) id: vec3<u32>) {
 }
 
 fn negation_avoids_prefix_decrement() {
-    let x = 1;
-    let p0 = -x;
-    let p1 = - -x;
-    let p2 = -(-x);
-    let p3 = -(- x);
-    let p4 = - - -x;
-    let p5 = - - - - x;
-    let p6 = - - -(- -x);
-    let p7 = (- - - - -x);
+    let i = 1;
+    let i0 = -i;
+    let i1 = - -i;
+    let i2 = -(-i);
+    let i3 = -(- i);
+    let i4 = - - -i;
+    let i5 = - - - - i;
+    let i6 = - - -(- -i);
+    let i7 = (- - - - -i);
+
+    let f = 1.0;
+    let f0 = -f;
+    let f1 = - -f;
+    let f2 = -(-f);
+    let f3 = -(- f);
+    let f4 = - - -f;
+    let f5 = - - - - f;
+    let f6 = - - -(- -f);
+    let f7 = (- - - - -f);
 }
diff --git a/naga/tests/out/glsl/operators.main.Compute.glsl b/naga/tests/out/glsl/operators.main.Compute.glsl
index 1c2824495f..3e38d43202 100644
--- a/naga/tests/out/glsl/operators.main.Compute.glsl
+++ b/naga/tests/out/glsl/operators.main.Compute.glsl
@@ -240,14 +240,22 @@ void assignment() {
 }
 
 void negation_avoids_prefix_decrement() {
-    int p0_ = -(1);
-    int p1_ = -(-(1));
-    int p2_ = -(-(1));
-    int p3_ = -(-(1));
-    int p4_ = -(-(-(1)));
-    int p5_ = -(-(-(-(1))));
-    int p6_ = -(-(-(-(-(1)))));
-    int p7_ = -(-(-(-(-(1)))));
+    int i0_ = -(1);
+    int i1_ = -(-(1));
+    int i2_ = -(-(1));
+    int i3_ = -(-(1));
+    int i4_ = -(-(-(1)));
+    int i5_ = -(-(-(-(1))));
+    int i6_ = -(-(-(-(-(1)))));
+    int i7_ = -(-(-(-(-(1)))));
+    float f0_ = -(1.0);
+    float f1_ = -(-(1.0));
+    float f2_ = -(-(1.0));
+    float f3_ = -(-(1.0));
+    float f4_ = -(-(-(1.0)));
+    float f5_ = -(-(-(-(1.0))));
+    float f6_ = -(-(-(-(-(1.0)))));
+    float f7_ = -(-(-(-(-(1.0)))));
     return;
 }
 
diff --git a/naga/tests/out/hlsl/6772-unpack-expr-accesses.hlsl b/naga/tests/out/hlsl/6772-unpack-expr-accesses.hlsl
index c6d6c3f150..be62edc89f 100644
--- a/naga/tests/out/hlsl/6772-unpack-expr-accesses.hlsl
+++ b/naga/tests/out/hlsl/6772-unpack-expr-accesses.hlsl
@@ -1,7 +1,7 @@
 [numthreads(1, 1, 1)]
 void main()
 {
-    int phony = (int4(12u, 12u >> 8, 12u >> 16, 12u >> 24) << 24 >> 24)[2];
+    int phony = (int4(12u, 12u >> 8, 12u >> 16, 12u >> 24) << 24 >> 24)[int(2)];
     uint phony_1 = (uint4(12u, 12u >> 8, 12u >> 16, 12u >> 24) << 24 >> 24).y;
     return;
 }
diff --git a/naga/tests/out/hlsl/abstract-types-return.hlsl b/naga/tests/out/hlsl/abstract-types-return.hlsl
index fe2de29aee..77a09e8c9a 100644
--- a/naga/tests/out/hlsl/abstract-types-return.hlsl
+++ b/naga/tests/out/hlsl/abstract-types-return.hlsl
@@ -1,6 +1,6 @@
 int return_i32_ai()
 {
-    return 1;
+    return int(1);
 }
 
 uint return_u32_ai()
diff --git a/naga/tests/out/hlsl/access.hlsl b/naga/tests/out/hlsl/access.hlsl
index dcb62ae2af..167046fb30 100644
--- a/naga/tests/out/hlsl/access.hlsl
+++ b/naga/tests/out/hlsl/access.hlsl
@@ -93,7 +93,7 @@ GlobalConst ConstructGlobalConst(uint arg0, uint3 arg1, int arg2) {
     return ret;
 }
 
-static GlobalConst global_const = ConstructGlobalConst(0u, uint3(0u, 0u, 0u), 0);
+static GlobalConst global_const = ConstructGlobalConst(0u, uint3(0u, 0u, 0u), int(0));
 RWByteAddressBuffer bar : register(u0);
 cbuffer baz : register(b1) { Baz baz; }
 RWByteAddressBuffer qux : register(u2);
@@ -135,11 +135,11 @@ void SetMatScalarmOnBaz(Baz obj, float scalar, uint mat_idx, uint vec_idx) {
 
 void test_matrix_within_struct_accesses()
 {
-    int idx = 1;
+    int idx = int(1);
     Baz t = ConstructBaz(float3x2((1.0).xx, (2.0).xx, (3.0).xx));
 
     int _e3 = idx;
-    idx = (_e3 - 1);
+    idx = asint(asuint(_e3) - asuint(int(1)));
     float3x2 l0_ = GetMatmOnBaz(baz);
     float2 l1_ = GetMatmOnBaz(baz)[0];
     int _e14 = idx;
@@ -153,7 +153,7 @@ void test_matrix_within_struct_accesses()
     int _e38 = idx;
     float l6_ = GetMatmOnBaz(baz)[_e36][_e38];
     int _e51 = idx;
-    idx = (_e51 + 1);
+    idx = asint(asuint(_e51) + asuint(int(1)));
     SetMatmOnBaz(t, float3x2((6.0).xx, (5.0).xx, (4.0).xx));
     t.m_0 = (9.0).xx;
     int _e66 = idx;
@@ -182,11 +182,11 @@ ret_ZeroValuearray2_float4x2_ ZeroValuearray2_float4x2_() {
 
 void test_matrix_within_array_within_struct_accesses()
 {
-    int idx_1 = 1;
+    int idx_1 = int(1);
     MatCx2InArray t_1 = ConstructMatCx2InArray(ZeroValuearray2_float4x2_());
 
     int _e3 = idx_1;
-    idx_1 = (_e3 - 1);
+    idx_1 = asint(asuint(_e3) - asuint(int(1)));
     float4x2 l0_1[2] = ((float4x2[2])nested_mat_cx2_.am);
     float4x2 l1_1 = ((float4x2)nested_mat_cx2_.am[0]);
     float2 l2_1 = nested_mat_cx2_.am[0]._0;
@@ -201,7 +201,7 @@ void test_matrix_within_array_within_struct_accesses()
     int _e48 = idx_1;
     float l7_ = __get_col_of_mat4x2(nested_mat_cx2_.am[0], _e46)[_e48];
     int _e55 = idx_1;
-    idx_1 = (_e55 + 1);
+    idx_1 = asint(asuint(_e55) + asuint(int(1)));
     t_1.am = (__mat4x2[2])ZeroValuearray2_float4x2_();
     t_1.am[0] = (__mat4x2)float4x2((8.0).xx, (7.0).xx, (6.0).xx, (5.0).xx);
     t_1.am[0]._0 = (9.0).xx;
@@ -294,7 +294,7 @@ S ConstructS(int arg0) {
 
 int member_ptr()
 {
-    S s = ConstructS(42);
+    S s = ConstructS(int(42));
 
     int _e4 = s.m;
     return _e4;
@@ -370,8 +370,8 @@ float4 foo_vert(uint vi : SV_VertexID) : SV_Position
     int a_2 = asint(bar.Load(0+(((NagaBufferLengthRW(bar) - 160) / 8) - 2u)*8+160));
     int2 c = asint(qux.Load2(0));
     const float _e33 = read_from_private(foo);
-    c2_ = Constructarray5_int_(a_2, int(b), 3, 4, 5);
-    c2_[min(uint((vi + 1u)), 4u)] = 42;
+    c2_ = Constructarray5_int_(a_2, int(b), int(3), int(4), int(5));
+    c2_[min(uint((vi + 1u)), 4u)] = int(42);
     int value_1 = c2_[min(uint(vi), 4u)];
     const float _e47 = test_arr_as_arg(ZeroValuearray5_array10_float__());
     return float4(mul(float4((value_1).xxxx), _matrix), 2.0);
@@ -396,7 +396,7 @@ float4 foo_frag() : SV_Target0
         bar.Store2(144+0, asuint(_value2[0]));
         bar.Store2(144+8, asuint(_value2[1]));
     }
-    bar.Store(0+8+160, asuint(1));
+    bar.Store(0+8+160, asuint(int(1)));
     qux.Store2(0, asuint(ZeroValueint2()));
     return (0.0).xxxx;
 }
diff --git a/naga/tests/out/hlsl/atomicOps.hlsl b/naga/tests/out/hlsl/atomicOps.hlsl
index 640972a2fa..52d11aa325 100644
--- a/naga/tests/out/hlsl/atomicOps.hlsl
+++ b/naga/tests/out/hlsl/atomicOps.hlsl
@@ -20,13 +20,13 @@ void cs_main(uint3 id : SV_GroupThreadID, uint3 __local_invocation_id : SV_Group
     }
     GroupMemoryBarrierWithGroupSync();
     storage_atomic_scalar.Store(0, asuint(1u));
-    storage_atomic_arr.Store(4, asuint(1));
+    storage_atomic_arr.Store(4, asuint(int(1)));
     storage_struct.Store(0, asuint(1u));
-    storage_struct.Store(4+4, asuint(1));
+    storage_struct.Store(4+4, asuint(int(1)));
     workgroup_atomic_scalar = 1u;
-    workgroup_atomic_arr[1] = 1;
+    workgroup_atomic_arr[1] = int(1);
     workgroup_struct.atomic_scalar = 1u;
-    workgroup_struct.atomic_arr[1] = 1;
+    workgroup_struct.atomic_arr[1] = int(1);
     GroupMemoryBarrierWithGroupSync();
     uint l0_ = asuint(storage_atomic_scalar.Load(0));
     int l1_ = asint(storage_atomic_arr.Load(4));
@@ -38,74 +38,74 @@ void cs_main(uint3 id : SV_GroupThreadID, uint3 __local_invocation_id : SV_Group
     int l7_ = workgroup_struct.atomic_arr[1];
     GroupMemoryBarrierWithGroupSync();
     uint _e51; storage_atomic_scalar.InterlockedAdd(0, 1u, _e51);
-    int _e55; storage_atomic_arr.InterlockedAdd(4, 1, _e55);
+    int _e55; storage_atomic_arr.InterlockedAdd(4, int(1), _e55);
     uint _e59; storage_struct.InterlockedAdd(0, 1u, _e59);
-    int _e64; storage_struct.InterlockedAdd(4+4, 1, _e64);
+    int _e64; storage_struct.InterlockedAdd(4+4, int(1), _e64);
     uint _e67; InterlockedAdd(workgroup_atomic_scalar, 1u, _e67);
-    int _e71; InterlockedAdd(workgroup_atomic_arr[1], 1, _e71);
+    int _e71; InterlockedAdd(workgroup_atomic_arr[1], int(1), _e71);
     uint _e75; InterlockedAdd(workgroup_struct.atomic_scalar, 1u, _e75);
-    int _e80; InterlockedAdd(workgroup_struct.atomic_arr[1], 1, _e80);
+    int _e80; InterlockedAdd(workgroup_struct.atomic_arr[1], int(1), _e80);
     GroupMemoryBarrierWithGroupSync();
     uint _e83; storage_atomic_scalar.InterlockedAdd(0, -1u, _e83);
-    int _e87; storage_atomic_arr.InterlockedAdd(4, -1, _e87);
+    int _e87; storage_atomic_arr.InterlockedAdd(4, -int(1), _e87);
     uint _e91; storage_struct.InterlockedAdd(0, -1u, _e91);
-    int _e96; storage_struct.InterlockedAdd(4+4, -1, _e96);
+    int _e96; storage_struct.InterlockedAdd(4+4, -int(1), _e96);
     uint _e99; InterlockedAdd(workgroup_atomic_scalar, -1u, _e99);
-    int _e103; InterlockedAdd(workgroup_atomic_arr[1], -1, _e103);
+    int _e103; InterlockedAdd(workgroup_atomic_arr[1], -int(1), _e103);
     uint _e107; InterlockedAdd(workgroup_struct.atomic_scalar, -1u, _e107);
-    int _e112; InterlockedAdd(workgroup_struct.atomic_arr[1], -1, _e112);
+    int _e112; InterlockedAdd(workgroup_struct.atomic_arr[1], -int(1), _e112);
     GroupMemoryBarrierWithGroupSync();
     uint _e115; storage_atomic_scalar.InterlockedMax(0, 1u, _e115);
-    int _e119; storage_atomic_arr.InterlockedMax(4, 1, _e119);
+    int _e119; storage_atomic_arr.InterlockedMax(4, int(1), _e119);
     uint _e123; storage_struct.InterlockedMax(0, 1u, _e123);
-    int _e128; storage_struct.InterlockedMax(4+4, 1, _e128);
+    int _e128; storage_struct.InterlockedMax(4+4, int(1), _e128);
     uint _e131; InterlockedMax(workgroup_atomic_scalar, 1u, _e131);
-    int _e135; InterlockedMax(workgroup_atomic_arr[1], 1, _e135);
+    int _e135; InterlockedMax(workgroup_atomic_arr[1], int(1), _e135);
     uint _e139; InterlockedMax(workgroup_struct.atomic_scalar, 1u, _e139);
-    int _e144; InterlockedMax(workgroup_struct.atomic_arr[1], 1, _e144);
+    int _e144; InterlockedMax(workgroup_struct.atomic_arr[1], int(1), _e144);
     GroupMemoryBarrierWithGroupSync();
     uint _e147; storage_atomic_scalar.InterlockedMin(0, 1u, _e147);
-    int _e151; storage_atomic_arr.InterlockedMin(4, 1, _e151);
+    int _e151; storage_atomic_arr.InterlockedMin(4, int(1), _e151);
     uint _e155; storage_struct.InterlockedMin(0, 1u, _e155);
-    int _e160; storage_struct.InterlockedMin(4+4, 1, _e160);
+    int _e160; storage_struct.InterlockedMin(4+4, int(1), _e160);
     uint _e163; InterlockedMin(workgroup_atomic_scalar, 1u, _e163);
-    int _e167; InterlockedMin(workgroup_atomic_arr[1], 1, _e167);
+    int _e167; InterlockedMin(workgroup_atomic_arr[1], int(1), _e167);
     uint _e171; InterlockedMin(workgroup_struct.atomic_scalar, 1u, _e171);
-    int _e176; InterlockedMin(workgroup_struct.atomic_arr[1], 1, _e176);
+    int _e176; InterlockedMin(workgroup_struct.atomic_arr[1], int(1), _e176);
     GroupMemoryBarrierWithGroupSync();
     uint _e179; storage_atomic_scalar.InterlockedAnd(0, 1u, _e179);
-    int _e183; storage_atomic_arr.InterlockedAnd(4, 1, _e183);
+    int _e183; storage_atomic_arr.InterlockedAnd(4, int(1), _e183);
     uint _e187; storage_struct.InterlockedAnd(0, 1u, _e187);
-    int _e192; storage_struct.InterlockedAnd(4+4, 1, _e192);
+    int _e192; storage_struct.InterlockedAnd(4+4, int(1), _e192);
     uint _e195; InterlockedAnd(workgroup_atomic_scalar, 1u, _e195);
-    int _e199; InterlockedAnd(workgroup_atomic_arr[1], 1, _e199);
+    int _e199; InterlockedAnd(workgroup_atomic_arr[1], int(1), _e199);
     uint _e203; InterlockedAnd(workgroup_struct.atomic_scalar, 1u, _e203);
-    int _e208; InterlockedAnd(workgroup_struct.atomic_arr[1], 1, _e208);
+    int _e208; InterlockedAnd(workgroup_struct.atomic_arr[1], int(1), _e208);
     GroupMemoryBarrierWithGroupSync();
     uint _e211; storage_atomic_scalar.InterlockedOr(0, 1u, _e211);
-    int _e215; storage_atomic_arr.InterlockedOr(4, 1, _e215);
+    int _e215; storage_atomic_arr.InterlockedOr(4, int(1), _e215);
     uint _e219; storage_struct.InterlockedOr(0, 1u, _e219);
-    int _e224; storage_struct.InterlockedOr(4+4, 1, _e224);
+    int _e224; storage_struct.InterlockedOr(4+4, int(1), _e224);
     uint _e227; InterlockedOr(workgroup_atomic_scalar, 1u, _e227);
-    int _e231; InterlockedOr(workgroup_atomic_arr[1], 1, _e231);
+    int _e231; InterlockedOr(workgroup_atomic_arr[1], int(1), _e231);
     uint _e235; InterlockedOr(workgroup_struct.atomic_scalar, 1u, _e235);
-    int _e240; InterlockedOr(workgroup_struct.atomic_arr[1], 1, _e240);
+    int _e240; InterlockedOr(workgroup_struct.atomic_arr[1], int(1), _e240);
     GroupMemoryBarrierWithGroupSync();
     uint _e243; storage_atomic_scalar.InterlockedXor(0, 1u, _e243);
-    int _e247; storage_atomic_arr.InterlockedXor(4, 1, _e247);
+    int _e247; storage_atomic_arr.InterlockedXor(4, int(1), _e247);
     uint _e251; storage_struct.InterlockedXor(0, 1u, _e251);
-    int _e256; storage_struct.InterlockedXor(4+4, 1, _e256);
+    int _e256; storage_struct.InterlockedXor(4+4, int(1), _e256);
     uint _e259; InterlockedXor(workgroup_atomic_scalar, 1u, _e259);
-    int _e263; InterlockedXor(workgroup_atomic_arr[1], 1, _e263);
+    int _e263; InterlockedXor(workgroup_atomic_arr[1], int(1), _e263);
     uint _e267; InterlockedXor(workgroup_struct.atomic_scalar, 1u, _e267);
-    int _e272; InterlockedXor(workgroup_struct.atomic_arr[1], 1, _e272);
+    int _e272; InterlockedXor(workgroup_struct.atomic_arr[1], int(1), _e272);
     uint _e275; storage_atomic_scalar.InterlockedExchange(0, 1u, _e275);
-    int _e279; storage_atomic_arr.InterlockedExchange(4, 1, _e279);
+    int _e279; storage_atomic_arr.InterlockedExchange(4, int(1), _e279);
     uint _e283; storage_struct.InterlockedExchange(0, 1u, _e283);
-    int _e288; storage_struct.InterlockedExchange(4+4, 1, _e288);
+    int _e288; storage_struct.InterlockedExchange(4+4, int(1), _e288);
     uint _e291; InterlockedExchange(workgroup_atomic_scalar, 1u, _e291);
-    int _e295; InterlockedExchange(workgroup_atomic_arr[1], 1, _e295);
+    int _e295; InterlockedExchange(workgroup_atomic_arr[1], int(1), _e295);
     uint _e299; InterlockedExchange(workgroup_struct.atomic_scalar, 1u, _e299);
-    int _e304; InterlockedExchange(workgroup_struct.atomic_arr[1], 1, _e304);
+    int _e304; InterlockedExchange(workgroup_struct.atomic_arr[1], int(1), _e304);
     return;
 }
diff --git a/naga/tests/out/hlsl/atomicTexture-int64.hlsl b/naga/tests/out/hlsl/atomicTexture-int64.hlsl
index 056489c790..ed9e57dd62 100644
--- a/naga/tests/out/hlsl/atomicTexture-int64.hlsl
+++ b/naga/tests/out/hlsl/atomicTexture-int64.hlsl
@@ -10,8 +10,8 @@ RWTexture2D<uint64_t> image : register(u0);
 [numthreads(2, 1, 1)]
 void cs_main(uint3 id : SV_GroupThreadID)
 {
-    InterlockedMax(image[int2(0, 0)],1uL);
+    InterlockedMax(image[int2(int(0), int(0))],1uL);
     GroupMemoryBarrierWithGroupSync();
-    InterlockedMin(image[int2(0, 0)],1uL);
+    InterlockedMin(image[int2(int(0), int(0))],1uL);
     return;
 }
diff --git a/naga/tests/out/hlsl/atomicTexture.hlsl b/naga/tests/out/hlsl/atomicTexture.hlsl
index 241cdab678..d598be925d 100644
--- a/naga/tests/out/hlsl/atomicTexture.hlsl
+++ b/naga/tests/out/hlsl/atomicTexture.hlsl
@@ -11,17 +11,17 @@ RWTexture2D<int> image_s : register(u1);
 [numthreads(2, 1, 1)]
 void cs_main(uint3 id : SV_GroupThreadID)
 {
-    InterlockedMax(image_u[int2(0, 0)],1u);
-    InterlockedMin(image_u[int2(0, 0)],1u);
-    InterlockedAdd(image_u[int2(0, 0)],1u);
-    InterlockedAnd(image_u[int2(0, 0)],1u);
-    InterlockedOr(image_u[int2(0, 0)],1u);
-    InterlockedXor(image_u[int2(0, 0)],1u);
-    InterlockedMax(image_s[int2(0, 0)],1);
-    InterlockedMin(image_s[int2(0, 0)],1);
-    InterlockedAdd(image_s[int2(0, 0)],1);
-    InterlockedAnd(image_s[int2(0, 0)],1);
-    InterlockedOr(image_s[int2(0, 0)],1);
-    InterlockedXor(image_s[int2(0, 0)],1);
+    InterlockedMax(image_u[int2(int(0), int(0))],1u);
+    InterlockedMin(image_u[int2(int(0), int(0))],1u);
+    InterlockedAdd(image_u[int2(int(0), int(0))],1u);
+    InterlockedAnd(image_u[int2(int(0), int(0))],1u);
+    InterlockedOr(image_u[int2(int(0), int(0))],1u);
+    InterlockedXor(image_u[int2(int(0), int(0))],1u);
+    InterlockedMax(image_s[int2(int(0), int(0))],int(1));
+    InterlockedMin(image_s[int2(int(0), int(0))],int(1));
+    InterlockedAdd(image_s[int2(int(0), int(0))],int(1));
+    InterlockedAnd(image_s[int2(int(0), int(0))],int(1));
+    InterlockedOr(image_s[int2(int(0), int(0))],int(1));
+    InterlockedXor(image_s[int2(int(0), int(0))],int(1));
     return;
 }
diff --git a/naga/tests/out/hlsl/binding-arrays.hlsl b/naga/tests/out/hlsl/binding-arrays.hlsl
index 3f0a533b6c..2f16123199 100644
--- a/naga/tests/out/hlsl/binding-arrays.hlsl
+++ b/naga/tests/out/hlsl/binding-arrays.hlsl
@@ -62,7 +62,7 @@ float4 main(FragmentInput_main fragmentinput_main) : SV_Target0
     uint uniform_index = uni.index;
     uint non_uniform_index = fragment_in.index;
     float2 uv = (0.0).xx;
-    int2 pix = (0).xx;
+    int2 pix = (int(0)).xx;
     uint2 _e22 = u2_;
     u2_ = (_e22 + NagaDimensions2D(texture_array_unbounded[0]));
     uint2 _e27 = u2_;
@@ -87,13 +87,13 @@ float4 main(FragmentInput_main fragmentinput_main) : SV_Target0
     float4 _e76 = texture_array_depth[NonUniformResourceIndex(non_uniform_index)].GatherCmp(nagaComparisonSamplerHeap[NonUniformResourceIndex(nagaGroup0SamplerIndexArray[samp_comp + non_uniform_index])], uv, 0.0);
     float4 _e77 = v4_;
     v4_ = (_e77 + _e76);
-    float4 _e82 = texture_array_unbounded[0].Load(int3(pix, 0));
+    float4 _e82 = texture_array_unbounded[0].Load(int3(pix, int(0)));
     float4 _e83 = v4_;
     v4_ = (_e83 + _e82);
-    float4 _e88 = texture_array_unbounded[uniform_index].Load(int3(pix, 0));
+    float4 _e88 = texture_array_unbounded[uniform_index].Load(int3(pix, int(0)));
     float4 _e89 = v4_;
     v4_ = (_e89 + _e88);
-    float4 _e94 = texture_array_unbounded[NonUniformResourceIndex(non_uniform_index)].Load(int3(pix, 0));
+    float4 _e94 = texture_array_unbounded[NonUniformResourceIndex(non_uniform_index)].Load(int3(pix, int(0)));
     float4 _e95 = v4_;
     v4_ = (_e95 + _e94);
     uint _e100 = u1_;
diff --git a/naga/tests/out/hlsl/bitcast.hlsl b/naga/tests/out/hlsl/bitcast.hlsl
index b21408dda5..420f39dd94 100644
--- a/naga/tests/out/hlsl/bitcast.hlsl
+++ b/naga/tests/out/hlsl/bitcast.hlsl
@@ -1,9 +1,9 @@
 [numthreads(1, 1, 1)]
 void main()
 {
-    int2 i2_ = (0).xx;
-    int3 i3_ = (0).xxx;
-    int4 i4_ = (0).xxxx;
+    int2 i2_ = (int(0)).xx;
+    int3 i3_ = (int(0)).xxx;
+    int4 i4_ = (int(0)).xxxx;
     uint2 u2_ = (0u).xx;
     uint3 u3_ = (0u).xxx;
     uint4 u4_ = (0u).xxxx;
diff --git a/naga/tests/out/hlsl/bits.hlsl b/naga/tests/out/hlsl/bits.hlsl
index 1987e15514..f75cba90b6 100644
--- a/naga/tests/out/hlsl/bits.hlsl
+++ b/naga/tests/out/hlsl/bits.hlsl
@@ -177,10 +177,10 @@ uint4 naga_extractBits(
 [numthreads(1, 1, 1)]
 void main()
 {
-    int i = 0;
-    int2 i2_ = (0).xx;
-    int3 i3_ = (0).xxx;
-    int4 i4_ = (0).xxxx;
+    int i = int(0);
+    int2 i2_ = (int(0)).xx;
+    int3 i3_ = (int(0)).xxx;
+    int4 i4_ = (int(0)).xxxx;
     uint u = 0u;
     uint2 u2_ = (0u).xx;
     uint3 u3_ = (0u).xxx;
diff --git a/naga/tests/out/hlsl/boids.hlsl b/naga/tests/out/hlsl/boids.hlsl
index 8934a9bca2..58d444706b 100644
--- a/naga/tests/out/hlsl/boids.hlsl
+++ b/naga/tests/out/hlsl/boids.hlsl
@@ -27,8 +27,8 @@ void main(uint3 global_invocation_id : SV_DispatchThreadID)
     float2 cMass = float2(0.0, 0.0);
     float2 cVel = float2(0.0, 0.0);
     float2 colVel = float2(0.0, 0.0);
-    int cMassCount = 0;
-    int cVelCount = 0;
+    int cMassCount = int(0);
+    int cVelCount = int(0);
     float2 pos = (float2)0;
     float2 vel = (float2)0;
     uint i = 0u;
@@ -73,7 +73,7 @@ void main(uint3 global_invocation_id : SV_DispatchThreadID)
             float2 _e61 = pos;
             cMass = (_e60 + _e61);
             int _e63 = cMassCount;
-            cMassCount = (_e63 + 1);
+            cMassCount = asint(asuint(_e63) + asuint(int(1)));
         }
         float2 _e66 = pos;
         float2 _e67 = vPos;
@@ -92,18 +92,18 @@ void main(uint3 global_invocation_id : SV_DispatchThreadID)
             float2 _e86 = vel;
             cVel = (_e85 + _e86);
             int _e88 = cVelCount;
-            cVelCount = (_e88 + 1);
+            cVelCount = asint(asuint(_e88) + asuint(int(1)));
         }
     }
     int _e94 = cMassCount;
-    if ((_e94 > 0)) {
+    if ((_e94 > int(0))) {
         float2 _e97 = cMass;
         int _e98 = cMassCount;
         float2 _e102 = vPos;
         cMass = ((_e97 / (float(_e98)).xx) - _e102);
     }
     int _e104 = cVelCount;
-    if ((_e104 > 0)) {
+    if ((_e104 > int(0))) {
         float2 _e107 = cVel;
         int _e108 = cVelCount;
         cVel = (_e107 / (float(_e108)).xx);
diff --git a/naga/tests/out/hlsl/collatz.hlsl b/naga/tests/out/hlsl/collatz.hlsl
index 3a250a4f25..926ddaf932 100644
--- a/naga/tests/out/hlsl/collatz.hlsl
+++ b/naga/tests/out/hlsl/collatz.hlsl
@@ -1,5 +1,13 @@
 RWByteAddressBuffer v_indices : register(u0);
 
+uint naga_mod(uint lhs, uint rhs) {
+    return lhs % (rhs == 0u ? 1u : rhs);
+}
+
+uint naga_div(uint lhs, uint rhs) {
+    return lhs / (rhs == 0u ? 1u : rhs);
+}
+
 uint collatz_iterations(uint n_base)
 {
     uint n = (uint)0;
@@ -17,9 +25,9 @@ uint collatz_iterations(uint n_base)
         }
         {
             uint _e7 = n;
-            if (((_e7 % 2u) == 0u)) {
+            if ((naga_mod(_e7, 2u) == 0u)) {
                 uint _e12 = n;
-                n = (_e12 / 2u);
+                n = naga_div(_e12, 2u);
             } else {
                 uint _e16 = n;
                 n = ((3u * _e16) + 1u);
diff --git a/naga/tests/out/hlsl/const-exprs.hlsl b/naga/tests/out/hlsl/const-exprs.hlsl
index 9d62504f78..ab2160cb19 100644
--- a/naga/tests/out/hlsl/const-exprs.hlsl
+++ b/naga/tests/out/hlsl/const-exprs.hlsl
@@ -1,45 +1,45 @@
 static const uint TWO = 2u;
-static const int THREE = 3;
-static const int FOUR = 4;
-static const int FOUR_ALIAS = 4;
-static const int TEST_CONSTANT_ADDITION = 8;
-static const int TEST_CONSTANT_ALIAS_ADDITION = 8;
+static const int THREE = int(3);
+static const int FOUR = int(4);
+static const int FOUR_ALIAS = int(4);
+static const int TEST_CONSTANT_ADDITION = int(8);
+static const int TEST_CONSTANT_ALIAS_ADDITION = int(8);
 static const float PI = 3.141;
 static const float phi_sun = 6.282;
 static const float4 DIV = float4(0.44444445, 0.0, 0.0, 0.0);
-static const int TEXTURE_KIND_REGULAR = 0;
-static const int TEXTURE_KIND_WARP = 1;
-static const int TEXTURE_KIND_SKY = 2;
+static const int TEXTURE_KIND_REGULAR = int(0);
+static const int TEXTURE_KIND_WARP = int(1);
+static const int TEXTURE_KIND_SKY = int(2);
 static const float2 add_vec = float2(4.0, 5.0);
 static const bool2 compare_vec = bool2(true, false);
 
 void swizzle_of_compose()
 {
-    int4 out_ = int4(4, 3, 2, 1);
+    int4 out_ = int4(int(4), int(3), int(2), int(1));
 
     return;
 }
 
 void index_of_compose()
 {
-    int out_1 = 2;
+    int out_1 = int(2);
 
     return;
 }
 
 void compose_three_deep()
 {
-    int out_2 = 6;
+    int out_2 = int(6);
 
     return;
 }
 
 void non_constant_initializers()
 {
-    int w = 30;
+    int w = int(30);
     int x = (int)0;
     int y = (int)0;
-    int z = 70;
+    int z = int(70);
     int4 out_3 = (int4)0;
 
     int _e2 = w;
@@ -56,14 +56,14 @@ void non_constant_initializers()
 
 void splat_of_constant()
 {
-    int4 out_4 = int4(-4, -4, -4, -4);
+    int4 out_4 = int4(int(-4), int(-4), int(-4), int(-4));
 
     return;
 }
 
 void compose_of_constant()
 {
-    int4 out_5 = int4(-4, -4, -4, -4);
+    int4 out_5 = int4(int(-4), int(-4), int(-4), int(-4));
 
     return;
 }
@@ -95,9 +95,9 @@ uint map_texture_kind(int texture_kind)
 
 void compose_vector_zero_val_binop()
 {
-    int3 a = int3(1, 1, 1);
-    int3 b = int3(0, 1, 2);
-    int3 c = int3(1, 0, 2);
+    int3 a = int3(int(1), int(1), int(1));
+    int3 b = int3(int(0), int(1), int(2));
+    int3 c = int3(int(1), int(0), int(2));
 
     return;
 }
diff --git a/naga/tests/out/hlsl/constructors.hlsl b/naga/tests/out/hlsl/constructors.hlsl
index c354737114..5cc3702630 100644
--- a/naga/tests/out/hlsl/constructors.hlsl
+++ b/naga/tests/out/hlsl/constructors.hlsl
@@ -62,7 +62,7 @@ static const uint2 cz4_ = ZeroValueuint2();
 static const float2x2 cz5_ = ZeroValuefloat2x2();
 static const Foo cz6_[3] = ZeroValuearray3_Foo_();
 static const Foo cz7_ = ZeroValueFoo();
-static const int cp3_[4] = Constructarray4_int_(0, 1, 2, 3);
+static const int cp3_[4] = Constructarray4_int_(int(0), int(1), int(2), int(3));
 
 Foo ConstructFoo(float4 arg0, int arg1) {
     Foo ret = (Foo)0;
@@ -80,12 +80,12 @@ void main()
 {
     Foo foo = (Foo)0;
 
-    foo = ConstructFoo((1.0).xxxx, 1);
+    foo = ConstructFoo((1.0).xxxx, int(1));
     float2x2 m0_ = float2x2(float2(1.0, 0.0), float2(0.0, 1.0));
     float4x4 m1_ = float4x4(float4(1.0, 0.0, 0.0, 0.0), float4(0.0, 1.0, 0.0, 0.0), float4(0.0, 0.0, 1.0, 0.0), float4(0.0, 0.0, 0.0, 1.0));
     uint2 cit0_ = (0u).xx;
     float2x2 cit1_ = float2x2((0.0).xx, (0.0).xx);
-    int cit2_[4] = Constructarray4_int_(0, 1, 2, 3);
+    int cit2_[4] = Constructarray4_int_(int(0), int(1), int(2), int(3));
     uint2 ic4_ = uint2(0u, 0u);
     float2x3 ic5_ = float2x3(float3(0.0, 0.0, 0.0), float3(0.0, 0.0, 0.0));
     return;
diff --git a/naga/tests/out/hlsl/control-flow.hlsl b/naga/tests/out/hlsl/control-flow.hlsl
index 7a8cb73779..ad36cc6201 100644
--- a/naga/tests/out/hlsl/control-flow.hlsl
+++ b/naga/tests/out/hlsl/control-flow.hlsl
@@ -7,7 +7,7 @@ void switch_default_break(int i)
 
 void switch_case_break()
 {
-    switch(0) {
+    switch(int(0)) {
         case 0: {
             break;
         }
@@ -125,7 +125,7 @@ void loop_switch_continue_nesting(int x_1, int y, int z)
 
 void loop_switch_omit_continue_variable_checks(int x_2, int y_1, int z_1, int w)
 {
-    int pos_1 = 0;
+    int pos_1 = int(0);
 
     uint2 loop_bound_4 = uint2(0u, 0u);
     while(true) {
@@ -134,7 +134,7 @@ void loop_switch_omit_continue_variable_checks(int x_2, int y_1, int z_1, int w)
         bool should_continue_5 = false;
         switch(x_2) {
             case 1: {
-                pos_1 = 1;
+                pos_1 = int(1);
                 break;
             }
             default: {
@@ -160,7 +160,7 @@ void loop_switch_omit_continue_variable_checks(int x_2, int y_1, int z_1, int w)
                     default: {
                         switch(z_1) {
                             case 1: {
-                                pos_1 = 2;
+                                pos_1 = int(2);
                                 break;
                             }
                             default: {
@@ -194,30 +194,30 @@ void main(uint3 global_id : SV_DispatchThreadID)
     DeviceMemoryBarrierWithGroupSync();
     GroupMemoryBarrierWithGroupSync();
     do {
-        pos = 1;
+        pos = int(1);
     } while(false);
     int _e4 = pos;
     switch(_e4) {
         case 1: {
-            pos = 0;
+            pos = int(0);
             break;
         }
         case 2: {
-            pos = 1;
+            pos = int(1);
             break;
         }
         case 3:
         case 4: {
-            pos = 2;
+            pos = int(2);
             break;
         }
         case 5: {
-            pos = 3;
+            pos = int(3);
             break;
         }
         default:
         case 6: {
-            pos = 4;
+            pos = int(4);
             break;
         }
     }
@@ -232,22 +232,22 @@ void main(uint3 global_id : SV_DispatchThreadID)
     int _e11 = pos;
     switch(_e11) {
         case 1: {
-            pos = 0;
+            pos = int(0);
             break;
         }
         case 2: {
-            pos = 1;
+            pos = int(1);
             return;
         }
         case 3: {
-            pos = 2;
+            pos = int(2);
             return;
         }
         case 4: {
             return;
         }
         default: {
-            pos = 3;
+            pos = int(3);
             return;
         }
     }
diff --git a/naga/tests/out/hlsl/empty-global-name.hlsl b/naga/tests/out/hlsl/empty-global-name.hlsl
index 64227b0bc7..4f45d94749 100644
--- a/naga/tests/out/hlsl/empty-global-name.hlsl
+++ b/naga/tests/out/hlsl/empty-global-name.hlsl
@@ -7,7 +7,7 @@ RWByteAddressBuffer unnamed : register(u0);
 void function()
 {
     int _e3 = asint(unnamed.Load(0));
-    unnamed.Store(0, asuint((_e3 + 1)));
+    unnamed.Store(0, asuint(asint(asuint(_e3) + asuint(int(1)))));
     return;
 }
 
diff --git a/naga/tests/out/hlsl/fetch_depth.hlsl b/naga/tests/out/hlsl/fetch_depth.hlsl
index 04a1f51353..71788135f1 100644
--- a/naga/tests/out/hlsl/fetch_depth.hlsl
+++ b/naga/tests/out/hlsl/fetch_depth.hlsl
@@ -13,7 +13,7 @@ Texture2D<float> global_2 : register(t2);
 void function()
 {
     uint2 _e6 = asuint(global_1.Load2(0));
-    float _e7 = global_2.Load(int3(_e6, 0)).x;
+    float _e7 = global_2.Load(int3(_e6, int(0))).x;
     global.Store(0, asuint((_e7).xxxx.x));
     return;
 }
diff --git a/naga/tests/out/hlsl/fragment-output.hlsl b/naga/tests/out/hlsl/fragment-output.hlsl
index f1f0a82ae8..bc11c8518e 100644
--- a/naga/tests/out/hlsl/fragment-output.hlsl
+++ b/naga/tests/out/hlsl/fragment-output.hlsl
@@ -21,10 +21,10 @@ FragmentOutputVec4Vec3_ main_vec4vec3_()
     FragmentOutputVec4Vec3_ output = (FragmentOutputVec4Vec3_)0;
 
     output.vec4f = (0.0).xxxx;
-    output.vec4i = (0).xxxx;
+    output.vec4i = (int(0)).xxxx;
     output.vec4u = (0u).xxxx;
     output.vec3f = (0.0).xxx;
-    output.vec3i = (0).xxx;
+    output.vec3i = (int(0)).xxx;
     output.vec3u = (0u).xxx;
     FragmentOutputVec4Vec3_ _e19 = output;
     const FragmentOutputVec4Vec3_ fragmentoutputvec4vec3_ = _e19;
@@ -36,10 +36,10 @@ FragmentOutputVec2Scalar main_vec2scalar()
     FragmentOutputVec2Scalar output_1 = (FragmentOutputVec2Scalar)0;
 
     output_1.vec2f = (0.0).xx;
-    output_1.vec2i = (0).xx;
+    output_1.vec2i = (int(0)).xx;
     output_1.vec2u = (0u).xx;
     output_1.scalarf = 0.0;
-    output_1.scalari = 0;
+    output_1.scalari = int(0);
     output_1.scalaru = 0u;
     FragmentOutputVec2Scalar _e16 = output_1;
     const FragmentOutputVec2Scalar fragmentoutputvec2scalar = _e16;
diff --git a/naga/tests/out/hlsl/functions.hlsl b/naga/tests/out/hlsl/functions.hlsl
index 6d7e210307..c2341fb43a 100644
--- a/naga/tests/out/hlsl/functions.hlsl
+++ b/naga/tests/out/hlsl/functions.hlsl
@@ -8,13 +8,13 @@ float2 test_fma()
 
 int test_integer_dot_product()
 {
-    int2 a_2_ = (1).xx;
-    int2 b_2_ = (1).xx;
+    int2 a_2_ = (int(1)).xx;
+    int2 b_2_ = (int(1)).xx;
     int c_2_ = dot(a_2_, b_2_);
     uint3 a_3_ = (1u).xxx;
     uint3 b_3_ = (1u).xxx;
     uint c_3_ = dot(a_3_, b_3_);
-    int c_4_ = dot((4).xxxx, (2).xxxx);
+    int c_4_ = dot((int(4)).xxxx, (int(2)).xxxx);
     return c_4_;
 }
 
diff --git a/naga/tests/out/hlsl/globals.hlsl b/naga/tests/out/hlsl/globals.hlsl
index d320899b89..af2951a334 100644
--- a/naga/tests/out/hlsl/globals.hlsl
+++ b/naga/tests/out/hlsl/globals.hlsl
@@ -84,7 +84,7 @@ FooStruct ConstructFooStruct(float3 arg0, float arg1) {
 
 void test_msl_packed_vec3_()
 {
-    int idx = 1;
+    int idx = int(1);
 
     alignment.Store3(0, asuint((1.0).xxx));
     alignment.Store(0+0, asuint(1.0));
diff --git a/naga/tests/out/hlsl/image.hlsl b/naga/tests/out/hlsl/image.hlsl
index 30bfd86aeb..97eb0ab748 100644
--- a/naga/tests/out/hlsl/image.hlsl
+++ b/naga/tests/out/hlsl/image.hlsl
@@ -24,6 +24,11 @@ Texture2D<float> image_2d_depth : register(t2, space1);
 Texture2DArray<float> image_2d_array_depth : register(t3, space1);
 TextureCube<float> image_cube_depth : register(t4, space1);
 
+int2 naga_mod(int2 lhs, int2 rhs) {
+    int2 divisor = ((lhs == -2147483648 & rhs == -1) | (rhs == 0)) ? 1 : rhs;
+    return lhs - (lhs / divisor) * divisor;
+}
+
 uint2 NagaRWDimensions2D(RWTexture2D<uint4> tex)
 {
     uint4 ret;
@@ -35,18 +40,18 @@ uint2 NagaRWDimensions2D(RWTexture2D<uint4> tex)
 void main(uint3 local_id : SV_GroupThreadID)
 {
     uint2 dim = NagaRWDimensions2D(image_storage_src);
-    int2 itc = (int2((dim * local_id.xy)) % int2(10, 20));
+    int2 itc = naga_mod(int2((dim * local_id.xy)), int2(int(10), int(20)));
     uint4 value1_ = image_mipmapped_src.Load(int3(itc, int(local_id.z)));
     uint4 value2_ = image_multisampled_src.Load(itc, int(local_id.z));
     uint4 value4_ = image_storage_src.Load(itc);
-    uint4 value5_ = image_array_src.Load(int4(itc, local_id.z, (int(local_id.z) + 1)));
-    uint4 value6_ = image_array_src.Load(int4(itc, int(local_id.z), (int(local_id.z) + 1)));
+    uint4 value5_ = image_array_src.Load(int4(itc, local_id.z, asint(asuint(int(local_id.z)) + asuint(int(1)))));
+    uint4 value6_ = image_array_src.Load(int4(itc, int(local_id.z), asint(asuint(int(local_id.z)) + asuint(int(1)))));
     uint4 value7_ = image_1d_src.Load(int2(int(local_id.x), int(local_id.z)));
     uint4 value1u = image_mipmapped_src.Load(int3(uint2(itc), int(local_id.z)));
     uint4 value2u = image_multisampled_src.Load(uint2(itc), int(local_id.z));
     uint4 value4u = image_storage_src.Load(uint2(itc));
-    uint4 value5u = image_array_src.Load(int4(uint2(itc), local_id.z, (int(local_id.z) + 1)));
-    uint4 value6u = image_array_src.Load(int4(uint2(itc), int(local_id.z), (int(local_id.z) + 1)));
+    uint4 value5u = image_array_src.Load(int4(uint2(itc), local_id.z, asint(asuint(int(local_id.z)) + asuint(int(1)))));
+    uint4 value6u = image_array_src.Load(int4(uint2(itc), int(local_id.z), asint(asuint(int(local_id.z)) + asuint(int(1)))));
     uint4 value7u = image_1d_src.Load(int2(uint(local_id.x), int(local_id.z)));
     image_dst[itc.x] = ((((value1_ + value2_) + value4_) + value5_) + value6_);
     image_dst[uint(itc.x)] = ((((value1u + value2u) + value4u) + value5u) + value6u);
@@ -57,7 +62,7 @@ void main(uint3 local_id : SV_GroupThreadID)
 void depth_load(uint3 local_id_1 : SV_GroupThreadID)
 {
     uint2 dim_1 = NagaRWDimensions2D(image_storage_src);
-    int2 itc_1 = (int2((dim_1 * local_id_1.xy)) % int2(10, 20));
+    int2 itc_1 = naga_mod(int2((dim_1 * local_id_1.xy)), int2(int(10), int(20)));
     float val = image_depth_multisampled_src.Load(itc_1, int(local_id_1.z)).x;
     image_dst[itc_1.x] = (uint(val)).xxxx;
     return;
@@ -159,15 +164,15 @@ float4 queries() : SV_Position
     uint dim_1d = NagaDimensions1D(image_1d);
     uint dim_1d_lod = NagaMipDimensions1D(image_1d, int(dim_1d));
     uint2 dim_2d = NagaDimensions2D(image_2d);
-    uint2 dim_2d_lod = NagaMipDimensions2D(image_2d, 1);
+    uint2 dim_2d_lod = NagaMipDimensions2D(image_2d, int(1));
     uint2 dim_2d_array = NagaDimensions2DArray(image_2d_array);
-    uint2 dim_2d_array_lod = NagaMipDimensions2DArray(image_2d_array, 1);
+    uint2 dim_2d_array_lod = NagaMipDimensions2DArray(image_2d_array, int(1));
     uint2 dim_cube = NagaDimensionsCube(image_cube);
-    uint2 dim_cube_lod = NagaMipDimensionsCube(image_cube, 1);
+    uint2 dim_cube_lod = NagaMipDimensionsCube(image_cube, int(1));
     uint2 dim_cube_array = NagaDimensionsCubeArray(image_cube_array);
-    uint2 dim_cube_array_lod = NagaMipDimensionsCubeArray(image_cube_array, 1);
+    uint2 dim_cube_array_lod = NagaMipDimensionsCubeArray(image_cube_array, int(1));
     uint3 dim_3d = NagaDimensions3D(image_3d);
-    uint3 dim_3d_lod = NagaMipDimensions3D(image_3d, 1);
+    uint3 dim_3d_lod = NagaMipDimensions3D(image_3d, int(1));
     uint2 dim_2s_ms = NagaMSDimensions2D(image_aa);
     uint sum = ((((((((((dim_1d + dim_2d.y) + dim_2d_lod.y) + dim_2d_array.y) + dim_2d_array_lod.y) + dim_cube.y) + dim_cube_lod.y) + dim_cube_array.y) + dim_cube_array_lod.y) + dim_3d.z) + dim_3d_lod.z);
     return (float(sum)).xxxx;
@@ -256,46 +261,46 @@ float4 texture_sample() : SV_Target0
     float4 _e14 = image_2d.Sample(sampler_reg, tc);
     float4 _e15 = a;
     a = (_e15 + _e14);
-    float4 _e19 = image_2d.Sample(sampler_reg, tc, int2(int2(3, 1)));
+    float4 _e19 = image_2d.Sample(sampler_reg, tc, int2(int2(int(3), int(1))));
     float4 _e20 = a;
     a = (_e20 + _e19);
     float4 _e24 = image_2d.SampleLevel(sampler_reg, tc, 2.3);
     float4 _e25 = a;
     a = (_e25 + _e24);
-    float4 _e29 = image_2d.SampleLevel(sampler_reg, tc, 2.3, int2(int2(3, 1)));
+    float4 _e29 = image_2d.SampleLevel(sampler_reg, tc, 2.3, int2(int2(int(3), int(1))));
     float4 _e30 = a;
     a = (_e30 + _e29);
-    float4 _e35 = image_2d.SampleBias(sampler_reg, tc, 2.0, int2(int2(3, 1)));
+    float4 _e35 = image_2d.SampleBias(sampler_reg, tc, 2.0, int2(int2(int(3), int(1))));
     float4 _e36 = a;
     a = (_e36 + _e35);
     float4 _e41 = image_2d_array.Sample(sampler_reg, float3(tc, 0u));
     float4 _e42 = a;
     a = (_e42 + _e41);
-    float4 _e47 = image_2d_array.Sample(sampler_reg, float3(tc, 0u), int2(int2(3, 1)));
+    float4 _e47 = image_2d_array.Sample(sampler_reg, float3(tc, 0u), int2(int2(int(3), int(1))));
     float4 _e48 = a;
     a = (_e48 + _e47);
     float4 _e53 = image_2d_array.SampleLevel(sampler_reg, float3(tc, 0u), 2.3);
     float4 _e54 = a;
     a = (_e54 + _e53);
-    float4 _e59 = image_2d_array.SampleLevel(sampler_reg, float3(tc, 0u), 2.3, int2(int2(3, 1)));
+    float4 _e59 = image_2d_array.SampleLevel(sampler_reg, float3(tc, 0u), 2.3, int2(int2(int(3), int(1))));
     float4 _e60 = a;
     a = (_e60 + _e59);
-    float4 _e66 = image_2d_array.SampleBias(sampler_reg, float3(tc, 0u), 2.0, int2(int2(3, 1)));
+    float4 _e66 = image_2d_array.SampleBias(sampler_reg, float3(tc, 0u), 2.0, int2(int2(int(3), int(1))));
     float4 _e67 = a;
     a = (_e67 + _e66);
-    float4 _e72 = image_2d_array.Sample(sampler_reg, float3(tc, 0));
+    float4 _e72 = image_2d_array.Sample(sampler_reg, float3(tc, int(0)));
     float4 _e73 = a;
     a = (_e73 + _e72);
-    float4 _e78 = image_2d_array.Sample(sampler_reg, float3(tc, 0), int2(int2(3, 1)));
+    float4 _e78 = image_2d_array.Sample(sampler_reg, float3(tc, int(0)), int2(int2(int(3), int(1))));
     float4 _e79 = a;
     a = (_e79 + _e78);
-    float4 _e84 = image_2d_array.SampleLevel(sampler_reg, float3(tc, 0), 2.3);
+    float4 _e84 = image_2d_array.SampleLevel(sampler_reg, float3(tc, int(0)), 2.3);
     float4 _e85 = a;
     a = (_e85 + _e84);
-    float4 _e90 = image_2d_array.SampleLevel(sampler_reg, float3(tc, 0), 2.3, int2(int2(3, 1)));
+    float4 _e90 = image_2d_array.SampleLevel(sampler_reg, float3(tc, int(0)), 2.3, int2(int2(int(3), int(1))));
     float4 _e91 = a;
     a = (_e91 + _e90);
-    float4 _e97 = image_2d_array.SampleBias(sampler_reg, float3(tc, 0), 2.0, int2(int2(3, 1)));
+    float4 _e97 = image_2d_array.SampleBias(sampler_reg, float3(tc, int(0)), 2.0, int2(int2(int(3), int(1))));
     float4 _e98 = a;
     a = (_e98 + _e97);
     float4 _e103 = image_cube_array.Sample(sampler_reg, float4(tc3_, 0u));
@@ -307,13 +312,13 @@ float4 texture_sample() : SV_Target0
     float4 _e116 = image_cube_array.SampleBias(sampler_reg, float4(tc3_, 0u), 2.0);
     float4 _e117 = a;
     a = (_e117 + _e116);
-    float4 _e122 = image_cube_array.Sample(sampler_reg, float4(tc3_, 0));
+    float4 _e122 = image_cube_array.Sample(sampler_reg, float4(tc3_, int(0)));
     float4 _e123 = a;
     a = (_e123 + _e122);
-    float4 _e128 = image_cube_array.SampleLevel(sampler_reg, float4(tc3_, 0), 2.3);
+    float4 _e128 = image_cube_array.SampleLevel(sampler_reg, float4(tc3_, int(0)), 2.3);
     float4 _e129 = a;
     a = (_e129 + _e128);
-    float4 _e135 = image_cube_array.SampleBias(sampler_reg, float4(tc3_, 0), 2.0);
+    float4 _e135 = image_cube_array.SampleBias(sampler_reg, float4(tc3_, int(0)), 2.0);
     float4 _e136 = a;
     a = (_e136 + _e135);
     float4 _e138 = a;
@@ -332,7 +337,7 @@ float texture_sample_comparison() : SV_Target0
     float _e14 = image_2d_array_depth.SampleCmp(sampler_cmp, float3(tc_1, 0u), 0.5);
     float _e15 = a_1;
     a_1 = (_e15 + _e14);
-    float _e20 = image_2d_array_depth.SampleCmp(sampler_cmp, float3(tc_1, 0), 0.5);
+    float _e20 = image_2d_array_depth.SampleCmp(sampler_cmp, float3(tc_1, int(0)), 0.5);
     float _e21 = a_1;
     a_1 = (_e21 + _e20);
     float _e25 = image_cube_depth.SampleCmp(sampler_cmp, tc3_1, 0.5);
@@ -344,7 +349,7 @@ float texture_sample_comparison() : SV_Target0
     float _e36 = image_2d_array_depth.SampleCmpLevelZero(sampler_cmp, float3(tc_1, 0u), 0.5);
     float _e37 = a_1;
     a_1 = (_e37 + _e36);
-    float _e42 = image_2d_array_depth.SampleCmpLevelZero(sampler_cmp, float3(tc_1, 0), 0.5);
+    float _e42 = image_2d_array_depth.SampleCmpLevelZero(sampler_cmp, float3(tc_1, int(0)), 0.5);
     float _e43 = a_1;
     a_1 = (_e43 + _e42);
     float _e47 = image_cube_depth.SampleCmpLevelZero(sampler_cmp, tc3_1, 0.5);
@@ -358,9 +363,9 @@ float4 gather() : SV_Target0
 {
     float2 tc_2 = (0.5).xx;
     float4 s2d = image_2d.GatherGreen(sampler_reg, tc_2);
-    float4 s2d_offset = image_2d.GatherAlpha(sampler_reg, tc_2, int2(int2(3, 1)));
+    float4 s2d_offset = image_2d.GatherAlpha(sampler_reg, tc_2, int2(int2(int(3), int(1))));
     float4 s2d_depth = image_2d_depth.GatherCmp(sampler_cmp, tc_2, 0.5);
-    float4 s2d_depth_offset = image_2d_depth.GatherCmp(sampler_cmp, tc_2, 0.5, int2(int2(3, 1)));
+    float4 s2d_depth_offset = image_2d_depth.GatherCmp(sampler_cmp, tc_2, 0.5, int2(int2(int(3), int(1))));
     uint4 u = image_2d_u32_.Gather(sampler_reg, tc_2);
     int4 i = image_2d_i32_.Gather(sampler_reg, tc_2);
     float4 f = (float4(u) + float4(i));
@@ -372,6 +377,6 @@ float4 depth_no_comparison() : SV_Target0
     float2 tc_3 = (0.5).xx;
     float s2d_1 = image_2d_depth.Sample(sampler_reg, tc_3);
     float4 s2d_gather = image_2d_depth.Gather(sampler_reg, tc_3);
-    float s2d_level = image_2d_depth.SampleLevel(sampler_reg, tc_3, 1);
+    float s2d_level = image_2d_depth.SampleLevel(sampler_reg, tc_3, int(1));
     return (((s2d_1).xxxx + s2d_gather) + (s2d_level).xxxx);
 }
diff --git a/naga/tests/out/hlsl/int64.hlsl b/naga/tests/out/hlsl/int64.hlsl
index 26441380b1..84ff643070 100644
--- a/naga/tests/out/hlsl/int64.hlsl
+++ b/naga/tests/out/hlsl/int64.hlsl
@@ -75,7 +75,7 @@ int64_t int64_function(int64_t x)
     int _e26 = input_uniform.val_i32_;
     int64_t _e27 = val;
     int64_t _e31 = val;
-    val = (_e31 + int64_t((_e26 + int(_e27))));
+    val = (_e31 + int64_t(asint(asuint(_e26) + asuint(int(_e27)))));
     float _e35 = input_uniform.val_f32_;
     int64_t _e36 = val;
     int64_t _e40 = val;
@@ -162,7 +162,7 @@ uint64_t uint64_function(uint64_t x_1)
     int _e26 = input_uniform.val_i32_;
     uint64_t _e27 = val_1;
     uint64_t _e31 = val_1;
-    val_1 = (_e31 + uint64_t((_e26 + int(_e27))));
+    val_1 = (_e31 + uint64_t(asint(asuint(_e26) + asuint(int(_e27)))));
     float _e35 = input_uniform.val_f32_;
     uint64_t _e36 = val_1;
     uint64_t _e40 = val_1;
diff --git a/naga/tests/out/hlsl/math-functions.hlsl b/naga/tests/out/hlsl/math-functions.hlsl
index b8ace15b3c..b31d804a7b 100644
--- a/naga/tests/out/hlsl/math-functions.hlsl
+++ b/naga/tests/out/hlsl/math-functions.hlsl
@@ -76,21 +76,21 @@ void main()
     float4 d = radians(v);
     float4 e = saturate(v);
     float4 g = refract(v, v, 1.0);
-    int4 sign_b = int4(-1, -1, -1, -1);
+    int4 sign_b = int4(int(-1), int(-1), int(-1), int(-1));
     float4 sign_d = float4(-1.0, -1.0, -1.0, -1.0);
     int const_dot = dot(ZeroValueint2(), ZeroValueint2());
-    int2 flb_b = int2(-1, -1);
+    int2 flb_b = int2(int(-1), int(-1));
     uint2 flb_c = uint2(0u, 0u);
-    int2 ftb_c = int2(0, 0);
+    int2 ftb_c = int2(int(0), int(0));
     uint2 ftb_d = uint2(0u, 0u);
     uint2 ctz_e = uint2(32u, 32u);
-    int2 ctz_f = int2(32, 32);
+    int2 ctz_f = int2(int(32), int(32));
     uint2 ctz_g = uint2(0u, 0u);
-    int2 ctz_h = int2(0, 0);
-    int2 clz_c = int2(0, 0);
+    int2 ctz_h = int2(int(0), int(0));
+    int2 clz_c = int2(int(0), int(0));
     uint2 clz_d = uint2(31u, 31u);
-    float lde_a = ldexp(1.0, 2);
-    float2 lde_b = ldexp(float2(1.0, 2.0), int2(3, 4));
+    float lde_a = ldexp(1.0, int(2));
+    float2 lde_b = ldexp(float2(1.0, 2.0), int2(int(3), int(4)));
     _modf_result_f32_ modf_a = naga_modf(1.5);
     float modf_b = naga_modf(1.5).fract;
     float modf_c = naga_modf(1.5).whole;
diff --git a/naga/tests/out/hlsl/operators.hlsl b/naga/tests/out/hlsl/operators.hlsl
index a4a381f7e7..0e74dc54fa 100644
--- a/naga/tests/out/hlsl/operators.hlsl
+++ b/naga/tests/out/hlsl/operators.hlsl
@@ -1,25 +1,30 @@
 static const float4 v_f32_one = float4(1.0, 1.0, 1.0, 1.0);
 static const float4 v_f32_zero = float4(0.0, 0.0, 0.0, 0.0);
 static const float4 v_f32_half = float4(0.5, 0.5, 0.5, 0.5);
-static const int4 v_i32_one = int4(1, 1, 1, 1);
+static const int4 v_i32_one = int4(int(1), int(1), int(1), int(1));
 
 float4 builtins()
 {
-    int s1_ = (true ? 1 : 0);
+    int s1_ = (true ? int(1) : int(0));
     float4 s2_ = (true ? v_f32_one : v_f32_zero);
     float4 s3_ = (bool4(false, false, false, false) ? v_f32_zero : v_f32_one);
     float4 m1_ = lerp(v_f32_zero, v_f32_one, v_f32_half);
     float4 m2_ = lerp(v_f32_zero, v_f32_one, 0.1);
-    float b1_ = asfloat(1);
+    float b1_ = asfloat(int(1));
     float4 b2_ = asfloat(v_i32_one);
-    int4 v_i32_zero = int4(0, 0, 0, 0);
-    return (((((float4(((s1_).xxxx + v_i32_zero)) + s2_) + m1_) + m2_) + (b1_).xxxx) + b2_);
+    int4 v_i32_zero = int4(int(0), int(0), int(0), int(0));
+    return (((((float4(asint(asuint((s1_).xxxx) + asuint(v_i32_zero))) + s2_) + m1_) + m2_) + (b1_).xxxx) + b2_);
+}
+
+int4 naga_mod(int4 lhs, int4 rhs) {
+    int4 divisor = ((lhs == -2147483648 & rhs == -1) | (rhs == 0)) ? 1 : rhs;
+    return lhs - (lhs / divisor) * divisor;
 }
 
 float4 splat(float m, int n)
 {
     float2 a_2 = ((((2.0).xx + (m).xx) - (4.0).xx) / (8.0).xx);
-    int4 b = ((n).xxxx % (2).xxxx);
+    int4 b = naga_mod((n).xxxx, (int(2)).xxxx);
     return (a_2.xyxy + float4(b));
 }
 
@@ -56,6 +61,52 @@ void logical()
     return;
 }
 
+int2 naga_neg(int2 val) {
+    return asint(-asuint(val));
+}
+
+int naga_div(int lhs, int rhs) {
+    return lhs / (((lhs == -2147483648 & rhs == -1) | (rhs == 0)) ? 1 : rhs);
+}
+
+uint naga_div(uint lhs, uint rhs) {
+    return lhs / (rhs == 0u ? 1u : rhs);
+}
+
+int2 naga_div(int2 lhs, int2 rhs) {
+    return lhs / (((lhs == -2147483648 & rhs == -1) | (rhs == 0)) ? 1 : rhs);
+}
+
+uint3 naga_div(uint3 lhs, uint3 rhs) {
+    return lhs / (rhs == 0u ? 1u : rhs);
+}
+
+int naga_mod(int lhs, int rhs) {
+    int divisor = ((lhs == -2147483648 & rhs == -1) | (rhs == 0)) ? 1 : rhs;
+    return lhs - (lhs / divisor) * divisor;
+}
+
+uint naga_mod(uint lhs, uint rhs) {
+    return lhs % (rhs == 0u ? 1u : rhs);
+}
+
+int2 naga_mod(int2 lhs, int2 rhs) {
+    int2 divisor = ((lhs == -2147483648 & rhs == -1) | (rhs == 0)) ? 1 : rhs;
+    return lhs - (lhs / divisor) * divisor;
+}
+
+uint3 naga_mod(uint3 lhs, uint3 rhs) {
+    return lhs % (rhs == 0u ? 1u : rhs);
+}
+
+uint2 naga_div(uint2 lhs, uint2 rhs) {
+    return lhs / (rhs == 0u ? 1u : rhs);
+}
+
+uint2 naga_mod(uint2 lhs, uint2 rhs) {
+    return lhs % (rhs == 0u ? 1u : rhs);
+}
+
 float3x3 ZeroValuefloat3x3() {
     return (float3x3)0;
 }
@@ -71,67 +122,67 @@ float3x4 ZeroValuefloat3x4() {
 void arithmetic()
 {
     float neg0_1 = -(1.0);
-    int2 neg1_1 = -((1).xx);
+    int2 neg1_1 = naga_neg((int(1)).xx);
     float2 neg2_ = -((1.0).xx);
-    int add0_ = (2 + 1);
+    int add0_ = asint(asuint(int(2)) + asuint(int(1)));
     uint add1_ = (2u + 1u);
     float add2_ = (2.0 + 1.0);
-    int2 add3_ = ((2).xx + (1).xx);
+    int2 add3_ = asint(asuint((int(2)).xx) + asuint((int(1)).xx));
     uint3 add4_ = ((2u).xxx + (1u).xxx);
     float4 add5_ = ((2.0).xxxx + (1.0).xxxx);
-    int sub0_ = (2 - 1);
+    int sub0_ = asint(asuint(int(2)) - asuint(int(1)));
     uint sub1_ = (2u - 1u);
     float sub2_ = (2.0 - 1.0);
-    int2 sub3_ = ((2).xx - (1).xx);
+    int2 sub3_ = asint(asuint((int(2)).xx) - asuint((int(1)).xx));
     uint3 sub4_ = ((2u).xxx - (1u).xxx);
     float4 sub5_ = ((2.0).xxxx - (1.0).xxxx);
-    int mul0_ = (2 * 1);
+    int mul0_ = asint(asuint(int(2)) * asuint(int(1)));
     uint mul1_ = (2u * 1u);
     float mul2_ = (2.0 * 1.0);
-    int2 mul3_ = ((2).xx * (1).xx);
+    int2 mul3_ = asint(asuint((int(2)).xx) * asuint((int(1)).xx));
     uint3 mul4_ = ((2u).xxx * (1u).xxx);
     float4 mul5_ = ((2.0).xxxx * (1.0).xxxx);
-    int div0_ = (2 / 1);
-    uint div1_ = (2u / 1u);
+    int div0_ = naga_div(int(2), int(1));
+    uint div1_ = naga_div(2u, 1u);
     float div2_ = (2.0 / 1.0);
-    int2 div3_ = ((2).xx / (1).xx);
-    uint3 div4_ = ((2u).xxx / (1u).xxx);
+    int2 div3_ = naga_div((int(2)).xx, (int(1)).xx);
+    uint3 div4_ = naga_div((2u).xxx, (1u).xxx);
     float4 div5_ = ((2.0).xxxx / (1.0).xxxx);
-    int rem0_ = (2 % 1);
-    uint rem1_ = (2u % 1u);
+    int rem0_ = naga_mod(int(2), int(1));
+    uint rem1_ = naga_mod(2u, 1u);
     float rem2_ = fmod(2.0, 1.0);
-    int2 rem3_ = ((2).xx % (1).xx);
-    uint3 rem4_ = ((2u).xxx % (1u).xxx);
+    int2 rem3_ = naga_mod((int(2)).xx, (int(1)).xx);
+    uint3 rem4_ = naga_mod((2u).xxx, (1u).xxx);
     float4 rem5_ = fmod((2.0).xxxx, (1.0).xxxx);
     {
-        int2 add0_1 = ((2).xx + (1).xx);
-        int2 add1_1 = ((2).xx + (1).xx);
+        int2 add0_1 = asint(asuint((int(2)).xx) + asuint((int(1)).xx));
+        int2 add1_1 = asint(asuint((int(2)).xx) + asuint((int(1)).xx));
         uint2 add2_1 = ((2u).xx + (1u).xx);
         uint2 add3_1 = ((2u).xx + (1u).xx);
         float2 add4_1 = ((2.0).xx + (1.0).xx);
         float2 add5_1 = ((2.0).xx + (1.0).xx);
-        int2 sub0_1 = ((2).xx - (1).xx);
-        int2 sub1_1 = ((2).xx - (1).xx);
+        int2 sub0_1 = asint(asuint((int(2)).xx) - asuint((int(1)).xx));
+        int2 sub1_1 = asint(asuint((int(2)).xx) - asuint((int(1)).xx));
         uint2 sub2_1 = ((2u).xx - (1u).xx);
         uint2 sub3_1 = ((2u).xx - (1u).xx);
         float2 sub4_1 = ((2.0).xx - (1.0).xx);
         float2 sub5_1 = ((2.0).xx - (1.0).xx);
-        int2 mul0_1 = ((2).xx * 1);
-        int2 mul1_1 = (2 * (1).xx);
+        int2 mul0_1 = asint(asuint((int(2)).xx) * asuint(int(1)));
+        int2 mul1_1 = asint(asuint(int(2)) * asuint((int(1)).xx));
         uint2 mul2_1 = ((2u).xx * 1u);
         uint2 mul3_1 = (2u * (1u).xx);
         float2 mul4_1 = ((2.0).xx * 1.0);
         float2 mul5_1 = (2.0 * (1.0).xx);
-        int2 div0_1 = ((2).xx / (1).xx);
-        int2 div1_1 = ((2).xx / (1).xx);
-        uint2 div2_1 = ((2u).xx / (1u).xx);
-        uint2 div3_1 = ((2u).xx / (1u).xx);
+        int2 div0_1 = naga_div((int(2)).xx, (int(1)).xx);
+        int2 div1_1 = naga_div((int(2)).xx, (int(1)).xx);
+        uint2 div2_1 = naga_div((2u).xx, (1u).xx);
+        uint2 div3_1 = naga_div((2u).xx, (1u).xx);
         float2 div4_1 = ((2.0).xx / (1.0).xx);
         float2 div5_1 = ((2.0).xx / (1.0).xx);
-        int2 rem0_1 = ((2).xx % (1).xx);
-        int2 rem1_1 = ((2).xx % (1).xx);
-        uint2 rem2_1 = ((2u).xx % (1u).xx);
-        uint2 rem3_1 = ((2u).xx % (1u).xx);
+        int2 rem0_1 = naga_mod((int(2)).xx, (int(1)).xx);
+        int2 rem1_1 = naga_mod((int(2)).xx, (int(1)).xx);
+        uint2 rem2_1 = naga_mod((2u).xx, (1u).xx);
+        uint2 rem3_1 = naga_mod((2u).xx, (1u).xx);
         float2 rem4_1 = fmod((2.0).xx, (1.0).xx);
         float2 rem5_1 = fmod((2.0).xx, (1.0).xx);
     }
@@ -147,69 +198,69 @@ void arithmetic()
 
 void bit()
 {
-    int flip0_ = ~(1);
+    int flip0_ = ~(int(1));
     uint flip1_ = ~(1u);
-    int2 flip2_ = ~((1).xx);
+    int2 flip2_ = ~((int(1)).xx);
     uint3 flip3_ = ~((1u).xxx);
-    int or0_ = (2 | 1);
+    int or0_ = (int(2) | int(1));
     uint or1_ = (2u | 1u);
-    int2 or2_ = ((2).xx | (1).xx);
+    int2 or2_ = ((int(2)).xx | (int(1)).xx);
     uint3 or3_ = ((2u).xxx | (1u).xxx);
-    int and0_ = (2 & 1);
+    int and0_ = (int(2) & int(1));
     uint and1_ = (2u & 1u);
-    int2 and2_ = ((2).xx & (1).xx);
+    int2 and2_ = ((int(2)).xx & (int(1)).xx);
     uint3 and3_ = ((2u).xxx & (1u).xxx);
-    int xor0_ = (2 ^ 1);
+    int xor0_ = (int(2) ^ int(1));
     uint xor1_ = (2u ^ 1u);
-    int2 xor2_ = ((2).xx ^ (1).xx);
+    int2 xor2_ = ((int(2)).xx ^ (int(1)).xx);
     uint3 xor3_ = ((2u).xxx ^ (1u).xxx);
-    int shl0_ = (2 << 1u);
+    int shl0_ = (int(2) << 1u);
     uint shl1_ = (2u << 1u);
-    int2 shl2_ = ((2).xx << (1u).xx);
+    int2 shl2_ = ((int(2)).xx << (1u).xx);
     uint3 shl3_ = ((2u).xxx << (1u).xxx);
-    int shr0_ = (2 >> 1u);
+    int shr0_ = (int(2) >> 1u);
     uint shr1_ = (2u >> 1u);
-    int2 shr2_ = ((2).xx >> (1u).xx);
+    int2 shr2_ = ((int(2)).xx >> (1u).xx);
     uint3 shr3_ = ((2u).xxx >> (1u).xxx);
     return;
 }
 
 void comparison()
 {
-    bool eq0_ = (2 == 1);
+    bool eq0_ = (int(2) == int(1));
     bool eq1_ = (2u == 1u);
     bool eq2_ = (2.0 == 1.0);
-    bool2 eq3_ = ((2).xx == (1).xx);
+    bool2 eq3_ = ((int(2)).xx == (int(1)).xx);
     bool3 eq4_ = ((2u).xxx == (1u).xxx);
     bool4 eq5_ = ((2.0).xxxx == (1.0).xxxx);
-    bool neq0_ = (2 != 1);
+    bool neq0_ = (int(2) != int(1));
     bool neq1_ = (2u != 1u);
     bool neq2_ = (2.0 != 1.0);
-    bool2 neq3_ = ((2).xx != (1).xx);
+    bool2 neq3_ = ((int(2)).xx != (int(1)).xx);
     bool3 neq4_ = ((2u).xxx != (1u).xxx);
     bool4 neq5_ = ((2.0).xxxx != (1.0).xxxx);
-    bool lt0_ = (2 < 1);
+    bool lt0_ = (int(2) < int(1));
     bool lt1_ = (2u < 1u);
     bool lt2_ = (2.0 < 1.0);
-    bool2 lt3_ = ((2).xx < (1).xx);
+    bool2 lt3_ = ((int(2)).xx < (int(1)).xx);
     bool3 lt4_ = ((2u).xxx < (1u).xxx);
     bool4 lt5_ = ((2.0).xxxx < (1.0).xxxx);
-    bool lte0_ = (2 <= 1);
+    bool lte0_ = (int(2) <= int(1));
     bool lte1_ = (2u <= 1u);
     bool lte2_ = (2.0 <= 1.0);
-    bool2 lte3_ = ((2).xx <= (1).xx);
+    bool2 lte3_ = ((int(2)).xx <= (int(1)).xx);
     bool3 lte4_ = ((2u).xxx <= (1u).xxx);
     bool4 lte5_ = ((2.0).xxxx <= (1.0).xxxx);
-    bool gt0_ = (2 > 1);
+    bool gt0_ = (int(2) > int(1));
     bool gt1_ = (2u > 1u);
     bool gt2_ = (2.0 > 1.0);
-    bool2 gt3_ = ((2).xx > (1).xx);
+    bool2 gt3_ = ((int(2)).xx > (int(1)).xx);
     bool3 gt4_ = ((2u).xxx > (1u).xxx);
     bool4 gt5_ = ((2.0).xxxx > (1.0).xxxx);
-    bool gte0_ = (2 >= 1);
+    bool gte0_ = (int(2) >= int(1));
     bool gte1_ = (2u >= 1u);
     bool gte2_ = (2.0 >= 1.0);
-    bool2 gte3_ = ((2).xx >= (1).xx);
+    bool2 gte3_ = ((int(2)).xx >= (int(1)).xx);
     bool3 gte4_ = ((2u).xxx >= (1u).xxx);
     bool4 gte5_ = ((2.0).xxxx >= (1.0).xxxx);
     return;
@@ -224,50 +275,62 @@ void assignment()
     int a_1 = (int)0;
     int3 vec0_ = ZeroValueint3();
 
-    a_1 = 1;
+    a_1 = int(1);
     int _e5 = a_1;
-    a_1 = (_e5 + 1);
+    a_1 = asint(asuint(_e5) + asuint(int(1)));
     int _e7 = a_1;
-    a_1 = (_e7 - 1);
+    a_1 = asint(asuint(_e7) - asuint(int(1)));
     int _e9 = a_1;
     int _e10 = a_1;
-    a_1 = (_e10 * _e9);
+    a_1 = asint(asuint(_e10) * asuint(_e9));
     int _e12 = a_1;
     int _e13 = a_1;
-    a_1 = (_e13 / _e12);
+    a_1 = naga_div(_e13, _e12);
     int _e15 = a_1;
-    a_1 = (_e15 % 1);
+    a_1 = naga_mod(_e15, int(1));
     int _e17 = a_1;
-    a_1 = (_e17 & 0);
+    a_1 = (_e17 & int(0));
     int _e19 = a_1;
-    a_1 = (_e19 | 0);
+    a_1 = (_e19 | int(0));
     int _e21 = a_1;
-    a_1 = (_e21 ^ 0);
+    a_1 = (_e21 ^ int(0));
     int _e23 = a_1;
     a_1 = (_e23 << 2u);
     int _e25 = a_1;
     a_1 = (_e25 >> 1u);
     int _e28 = a_1;
-    a_1 = (_e28 + 1);
+    a_1 = asint(asuint(_e28) + asuint(int(1)));
     int _e31 = a_1;
-    a_1 = (_e31 - 1);
-    int _e37 = vec0_[1];
-    vec0_[1] = (_e37 + 1);
-    int _e41 = vec0_[1];
-    vec0_[1] = (_e41 - 1);
+    a_1 = asint(asuint(_e31) - asuint(int(1)));
+    int _e37 = vec0_[int(1)];
+    vec0_[int(1)] = asint(asuint(_e37) + asuint(int(1)));
+    int _e41 = vec0_[int(1)];
+    vec0_[int(1)] = asint(asuint(_e41) - asuint(int(1)));
     return;
 }
 
+int naga_neg(int val) {
+    return asint(-asuint(val));
+}
+
 void negation_avoids_prefix_decrement()
 {
-    int p0_ = -(1);
-    int p1_ = -(-(1));
-    int p2_ = -(-(1));
-    int p3_ = -(-(1));
-    int p4_ = -(-(-(1)));
-    int p5_ = -(-(-(-(1))));
-    int p6_ = -(-(-(-(-(1)))));
-    int p7_ = -(-(-(-(-(1)))));
+    int i0_ = naga_neg(int(1));
+    int i1_ = naga_neg(naga_neg(int(1)));
+    int i2_ = naga_neg(naga_neg(int(1)));
+    int i3_ = naga_neg(naga_neg(int(1)));
+    int i4_ = naga_neg(naga_neg(naga_neg(int(1))));
+    int i5_ = naga_neg(naga_neg(naga_neg(naga_neg(int(1)))));
+    int i6_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(int(1))))));
+    int i7_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(int(1))))));
+    float f0_ = -(1.0);
+    float f1_ = -(-(1.0));
+    float f2_ = -(-(1.0));
+    float f3_ = -(-(1.0));
+    float f4_ = -(-(-(1.0)));
+    float f5_ = -(-(-(-(1.0))));
+    float f6_ = -(-(-(-(-(1.0)))));
+    float f7_ = -(-(-(-(-(1.0)))));
     return;
 }
 
diff --git a/naga/tests/out/hlsl/phony_assignment.hlsl b/naga/tests/out/hlsl/phony_assignment.hlsl
index dbe587e5c8..02348c87df 100644
--- a/naga/tests/out/hlsl/phony_assignment.hlsl
+++ b/naga/tests/out/hlsl/phony_assignment.hlsl
@@ -2,7 +2,7 @@ cbuffer binding : register(b0) { float binding; }
 
 int five()
 {
-    return 5;
+    return int(5);
 }
 
 [numthreads(1, 1, 1)]
diff --git a/naga/tests/out/hlsl/skybox.hlsl b/naga/tests/out/hlsl/skybox.hlsl
index f1d77c3c50..9c890953b2 100644
--- a/naga/tests/out/hlsl/skybox.hlsl
+++ b/naga/tests/out/hlsl/skybox.hlsl
@@ -32,6 +32,10 @@ struct FragmentInput_fs_main {
     float4 position_1 : SV_Position;
 };
 
+int naga_div(int lhs, int rhs) {
+    return lhs / (((lhs == -2147483648 & rhs == -1) | (rhs == 0)) ? 1 : rhs);
+}
+
 VertexOutput ConstructVertexOutput(float4 arg0, float3 arg1) {
     VertexOutput ret = (VertexOutput)0;
     ret.position = arg0;
@@ -44,8 +48,8 @@ VertexOutput_vs_main vs_main(uint vertex_index : SV_VertexID)
     int tmp1_ = (int)0;
     int tmp2_ = (int)0;
 
-    tmp1_ = (int((_NagaConstants.first_vertex + vertex_index)) / 2);
-    tmp2_ = (int((_NagaConstants.first_vertex + vertex_index)) & 1);
+    tmp1_ = naga_div(int((_NagaConstants.first_vertex + vertex_index)), int(2));
+    tmp2_ = (int((_NagaConstants.first_vertex + vertex_index)) & int(1));
     int _e9 = tmp1_;
     int _e15 = tmp2_;
     float4 pos = float4(((float(_e9) * 4.0) - 1.0), ((float(_e15) * 4.0) - 1.0), 0.0, 1.0);
diff --git a/naga/tests/out/hlsl/unnamed-gl-per-vertex.hlsl b/naga/tests/out/hlsl/unnamed-gl-per-vertex.hlsl
index 6564f71f72..9b2ac0864e 100644
--- a/naga/tests/out/hlsl/unnamed-gl-per-vertex.hlsl
+++ b/naga/tests/out/hlsl/unnamed-gl-per-vertex.hlsl
@@ -26,7 +26,7 @@ static int global_1 = (int)0;
 void function()
 {
     int _e9 = global_1;
-    global.member = float4(((_e9 == 0) ? -4.0 : 1.0), ((_e9 == 2) ? 4.0 : -1.0), 0.0, 1.0);
+    global.member = float4(((_e9 == int(0)) ? -4.0 : 1.0), ((_e9 == int(2)) ? 4.0 : -1.0), 0.0, 1.0);
     return;
 }
 
diff --git a/naga/tests/out/hlsl/workgroup-uniform-load.hlsl b/naga/tests/out/hlsl/workgroup-uniform-load.hlsl
index a8dba16b9a..0bfb55cb6c 100644
--- a/naga/tests/out/hlsl/workgroup-uniform-load.hlsl
+++ b/naga/tests/out/hlsl/workgroup-uniform-load.hlsl
@@ -12,7 +12,7 @@ void test_workgroupUniformLoad(uint3 workgroup_id : SV_GroupID, uint3 __local_in
     GroupMemoryBarrierWithGroupSync();
     int _e4 = arr_i32_[min(uint(workgroup_id.x), 127u)];
     GroupMemoryBarrierWithGroupSync();
-    if ((_e4 > 10)) {
+    if ((_e4 > int(10))) {
         GroupMemoryBarrierWithGroupSync();
         return;
     } else {
diff --git a/naga/tests/out/msl/collatz.msl b/naga/tests/out/msl/collatz.msl
index eef2c83c34..e190d16da6 100644
--- a/naga/tests/out/msl/collatz.msl
+++ b/naga/tests/out/msl/collatz.msl
@@ -13,6 +13,14 @@ struct PrimeIndices {
     type_1 data;
 };
 
+uint naga_mod(uint lhs, uint rhs) {
+    return lhs % metal::select(rhs, 1u, rhs == 0u);
+}
+
+uint naga_div(uint lhs, uint rhs) {
+    return lhs / metal::select(rhs, 1u, rhs == 0u);
+}
+
 uint collatz_iterations(
     uint n_base
 ) {
@@ -30,9 +38,9 @@ uint collatz_iterations(
         }
         {
             uint _e7 = n;
-            if ((_e7 % 2u) == 0u) {
+            if (naga_mod(_e7, 2u) == 0u) {
                 uint _e12 = n;
-                n = _e12 / 2u;
+                n = naga_div(_e12, 2u);
             } else {
                 uint _e16 = n;
                 n = (3u * _e16) + 1u;
diff --git a/naga/tests/out/msl/image.msl b/naga/tests/out/msl/image.msl
index 1a8567a0ca..b2b3bda4e5 100644
--- a/naga/tests/out/msl/image.msl
+++ b/naga/tests/out/msl/image.msl
@@ -4,6 +4,11 @@
 
 using metal::uint;
 
+metal::int2 naga_mod(metal::int2 lhs, metal::int2 rhs) {
+    metal::int2 divisor = metal::select(rhs, 1, (lhs == -2147483648 & rhs == -1) | (rhs == 0));
+    return lhs - (lhs / divisor) * divisor;
+}
+
 
 struct main_Input {
 };
@@ -17,7 +22,7 @@ kernel void main_(
 , metal::texture1d<uint, metal::access::write> image_dst [[user(fake0)]]
 ) {
     metal::uint2 dim = metal::uint2(image_storage_src.get_width(), image_storage_src.get_height());
-    metal::int2 itc = static_cast<metal::int2>(dim * local_id.xy) % metal::int2(10, 20);
+    metal::int2 itc = naga_mod(static_cast<metal::int2>(dim * local_id.xy), metal::int2(10, 20));
     metal::uint4 value1_ = image_mipmapped_src.read(metal::uint2(itc), static_cast<int>(local_id.z));
     metal::uint4 value2_ = image_multisampled_src.read(metal::uint2(itc), static_cast<int>(local_id.z));
     metal::uint4 value4_ = image_storage_src.read(metal::uint2(itc));
@@ -45,7 +50,7 @@ kernel void depth_load(
 , metal::texture1d<uint, metal::access::write> image_dst [[user(fake0)]]
 ) {
     metal::uint2 dim_1 = metal::uint2(image_storage_src.get_width(), image_storage_src.get_height());
-    metal::int2 itc_1 = static_cast<metal::int2>(dim_1 * local_id_1.xy) % metal::int2(10, 20);
+    metal::int2 itc_1 = naga_mod(static_cast<metal::int2>(dim_1 * local_id_1.xy), metal::int2(10, 20));
     float val = image_depth_multisampled_src.read(metal::uint2(itc_1), static_cast<int>(local_id_1.z));
     image_dst.write(metal::uint4(static_cast<uint>(val)), uint(itc_1.x));
     return;
diff --git a/naga/tests/out/msl/int64.msl b/naga/tests/out/msl/int64.msl
index 369eb01c6f..d563b41d7b 100644
--- a/naga/tests/out/msl/int64.msl
+++ b/naga/tests/out/msl/int64.msl
@@ -34,6 +34,10 @@ struct StorageCompatible {
 };
 constant ulong constant_variable = 20uL;
 
+long naga_abs(long val) {
+    return metal::select(as_type<long>(-as_type<ulong>(val)), val, val >= 0);
+}
+
 long int64_function(
     long x,
     constant UniformCompatible& input_uniform,
@@ -91,7 +95,7 @@ long int64_function(
     output_arrays.val_i64_array_2_ = _e116;
     long _e117 = val;
     long _e119 = val;
-    val = as_type<long>(as_type<ulong>(_e119) + as_type<ulong>(metal::abs(_e117)));
+    val = as_type<long>(as_type<ulong>(_e119) + as_type<ulong>(naga_abs(_e117)));
     long _e121 = val;
     long _e122 = val;
     long _e123 = val;
diff --git a/naga/tests/out/msl/operators.msl b/naga/tests/out/msl/operators.msl
index 9b9b916cb2..1d2d81834a 100644
--- a/naga/tests/out/msl/operators.msl
+++ b/naga/tests/out/msl/operators.msl
@@ -22,12 +22,17 @@ metal::float4 builtins(
     return ((((static_cast<metal::float4>(as_type<metal::int4>(as_type<metal::uint4>(metal::int4(s1_)) + as_type<metal::uint4>(v_i32_zero))) + s2_) + m1_) + m2_) + metal::float4(b1_)) + b2_;
 }
 
+metal::int4 naga_mod(metal::int4 lhs, metal::int4 rhs) {
+    metal::int4 divisor = metal::select(rhs, 1, (lhs == -2147483648 & rhs == -1) | (rhs == 0));
+    return lhs - (lhs / divisor) * divisor;
+}
+
 metal::float4 splat(
     float m,
     int n
 ) {
     metal::float2 a_2 = ((metal::float2(2.0) + metal::float2(m)) - metal::float2(4.0)) / metal::float2(8.0);
-    metal::int4 b = metal::int4(n) % metal::int4(2);
+    metal::int4 b = naga_mod(metal::int4(n), metal::int4(2));
     return a_2.xyxy + static_cast<metal::float4>(b);
 }
 
@@ -64,10 +69,56 @@ void logical(
     return;
 }
 
+metal::int2 naga_neg(metal::int2 val) {
+    return as_type<metal::int2>(-as_type<metal::uint2>(val));
+}
+
+int naga_div(int lhs, int rhs) {
+    return lhs / metal::select(rhs, 1, (lhs == -2147483648 & rhs == -1) | (rhs == 0));
+}
+
+uint naga_div(uint lhs, uint rhs) {
+    return lhs / metal::select(rhs, 1u, rhs == 0u);
+}
+
+metal::int2 naga_div(metal::int2 lhs, metal::int2 rhs) {
+    return lhs / metal::select(rhs, 1, (lhs == -2147483648 & rhs == -1) | (rhs == 0));
+}
+
+metal::uint3 naga_div(metal::uint3 lhs, metal::uint3 rhs) {
+    return lhs / metal::select(rhs, 1u, rhs == 0u);
+}
+
+int naga_mod(int lhs, int rhs) {
+    int divisor = metal::select(rhs, 1, (lhs == -2147483648 & rhs == -1) | (rhs == 0));
+    return lhs - (lhs / divisor) * divisor;
+}
+
+uint naga_mod(uint lhs, uint rhs) {
+    return lhs % metal::select(rhs, 1u, rhs == 0u);
+}
+
+metal::int2 naga_mod(metal::int2 lhs, metal::int2 rhs) {
+    metal::int2 divisor = metal::select(rhs, 1, (lhs == -2147483648 & rhs == -1) | (rhs == 0));
+    return lhs - (lhs / divisor) * divisor;
+}
+
+metal::uint3 naga_mod(metal::uint3 lhs, metal::uint3 rhs) {
+    return lhs % metal::select(rhs, 1u, rhs == 0u);
+}
+
+metal::uint2 naga_div(metal::uint2 lhs, metal::uint2 rhs) {
+    return lhs / metal::select(rhs, 1u, rhs == 0u);
+}
+
+metal::uint2 naga_mod(metal::uint2 lhs, metal::uint2 rhs) {
+    return lhs % metal::select(rhs, 1u, rhs == 0u);
+}
+
 void arithmetic(
 ) {
     float neg0_1 = -(1.0);
-    metal::int2 neg1_1 = -(metal::int2(1));
+    metal::int2 neg1_1 = naga_neg(metal::int2(1));
     metal::float2 neg2_ = -(metal::float2(1.0));
     int add0_ = as_type<int>(as_type<uint>(2) + as_type<uint>(1));
     uint add1_ = 2u + 1u;
@@ -87,17 +138,17 @@ void arithmetic(
     metal::int2 mul3_ = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) * as_type<metal::uint2>(metal::int2(1)));
     metal::uint3 mul4_ = metal::uint3(2u) * metal::uint3(1u);
     metal::float4 mul5_ = metal::float4(2.0) * metal::float4(1.0);
-    int div0_ = 2 / 1;
-    uint div1_ = 2u / 1u;
+    int div0_ = naga_div(2, 1);
+    uint div1_ = naga_div(2u, 1u);
     float div2_ = 2.0 / 1.0;
-    metal::int2 div3_ = metal::int2(2) / metal::int2(1);
-    metal::uint3 div4_ = metal::uint3(2u) / metal::uint3(1u);
+    metal::int2 div3_ = naga_div(metal::int2(2), metal::int2(1));
+    metal::uint3 div4_ = naga_div(metal::uint3(2u), metal::uint3(1u));
     metal::float4 div5_ = metal::float4(2.0) / metal::float4(1.0);
-    int rem0_ = 2 % 1;
-    uint rem1_ = 2u % 1u;
+    int rem0_ = naga_mod(2, 1);
+    uint rem1_ = naga_mod(2u, 1u);
     float rem2_ = metal::fmod(2.0, 1.0);
-    metal::int2 rem3_ = metal::int2(2) % metal::int2(1);
-    metal::uint3 rem4_ = metal::uint3(2u) % metal::uint3(1u);
+    metal::int2 rem3_ = naga_mod(metal::int2(2), metal::int2(1));
+    metal::uint3 rem4_ = naga_mod(metal::uint3(2u), metal::uint3(1u));
     metal::float4 rem5_ = metal::fmod(metal::float4(2.0), metal::float4(1.0));
     {
         metal::int2 add0_1 = as_type<metal::int2>(as_type<metal::uint2>(metal::int2(2)) + as_type<metal::uint2>(metal::int2(1)));
@@ -118,16 +169,16 @@ void arithmetic(
         metal::uint2 mul3_1 = 2u * metal::uint2(1u);
         metal::float2 mul4_1 = metal::float2(2.0) * 1.0;
         metal::float2 mul5_1 = 2.0 * metal::float2(1.0);
-        metal::int2 div0_1 = metal::int2(2) / metal::int2(1);
-        metal::int2 div1_1 = metal::int2(2) / metal::int2(1);
-        metal::uint2 div2_1 = metal::uint2(2u) / metal::uint2(1u);
-        metal::uint2 div3_1 = metal::uint2(2u) / metal::uint2(1u);
+        metal::int2 div0_1 = naga_div(metal::int2(2), metal::int2(1));
+        metal::int2 div1_1 = naga_div(metal::int2(2), metal::int2(1));
+        metal::uint2 div2_1 = naga_div(metal::uint2(2u), metal::uint2(1u));
+        metal::uint2 div3_1 = naga_div(metal::uint2(2u), metal::uint2(1u));
         metal::float2 div4_1 = metal::float2(2.0) / metal::float2(1.0);
         metal::float2 div5_1 = metal::float2(2.0) / metal::float2(1.0);
-        metal::int2 rem0_1 = metal::int2(2) % metal::int2(1);
-        metal::int2 rem1_1 = metal::int2(2) % metal::int2(1);
-        metal::uint2 rem2_1 = metal::uint2(2u) % metal::uint2(1u);
-        metal::uint2 rem3_1 = metal::uint2(2u) % metal::uint2(1u);
+        metal::int2 rem0_1 = naga_mod(metal::int2(2), metal::int2(1));
+        metal::int2 rem1_1 = naga_mod(metal::int2(2), metal::int2(1));
+        metal::uint2 rem2_1 = naga_mod(metal::uint2(2u), metal::uint2(1u));
+        metal::uint2 rem3_1 = naga_mod(metal::uint2(2u), metal::uint2(1u));
         metal::float2 rem4_1 = metal::fmod(metal::float2(2.0), metal::float2(1.0));
         metal::float2 rem5_1 = metal::fmod(metal::float2(2.0), metal::float2(1.0));
     }
@@ -225,9 +276,9 @@ void assignment(
     a_1 = as_type<int>(as_type<uint>(_e10) * as_type<uint>(_e9));
     int _e12 = a_1;
     int _e13 = a_1;
-    a_1 = _e13 / _e12;
+    a_1 = naga_div(_e13, _e12);
     int _e15 = a_1;
-    a_1 = _e15 % 1;
+    a_1 = naga_mod(_e15, 1);
     int _e17 = a_1;
     a_1 = _e17 & 0;
     int _e19 = a_1;
@@ -249,16 +300,28 @@ void assignment(
     return;
 }
 
+int naga_neg(int val) {
+    return as_type<int>(-as_type<uint>(val));
+}
+
 void negation_avoids_prefix_decrement(
 ) {
-    int p0_ = -(1);
-    int p1_ = -(-(1));
-    int p2_ = -(-(1));
-    int p3_ = -(-(1));
-    int p4_ = -(-(-(1)));
-    int p5_ = -(-(-(-(1))));
-    int p6_ = -(-(-(-(-(1)))));
-    int p7_ = -(-(-(-(-(1)))));
+    int i0_ = naga_neg(1);
+    int i1_ = naga_neg(naga_neg(1));
+    int i2_ = naga_neg(naga_neg(1));
+    int i3_ = naga_neg(naga_neg(1));
+    int i4_ = naga_neg(naga_neg(naga_neg(1)));
+    int i5_ = naga_neg(naga_neg(naga_neg(naga_neg(1))));
+    int i6_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(1)))));
+    int i7_ = naga_neg(naga_neg(naga_neg(naga_neg(naga_neg(1)))));
+    float f0_ = -(1.0);
+    float f1_ = -(-(1.0));
+    float f2_ = -(-(1.0));
+    float f3_ = -(-(1.0));
+    float f4_ = -(-(-(1.0)));
+    float f5_ = -(-(-(-(1.0))));
+    float f6_ = -(-(-(-(-(1.0)))));
+    float f7_ = -(-(-(-(-(1.0)))));
     return;
 }
 
diff --git a/naga/tests/out/msl/skybox.msl b/naga/tests/out/msl/skybox.msl
index 7b10ea23e7..b5f51c946b 100644
--- a/naga/tests/out/msl/skybox.msl
+++ b/naga/tests/out/msl/skybox.msl
@@ -12,6 +12,10 @@ struct Data {
     metal::float4x4 proj_inv;
     metal::float4x4 view;
 };
+int naga_div(int lhs, int rhs) {
+    return lhs / metal::select(rhs, 1, (lhs == -2147483648 & rhs == -1) | (rhs == 0));
+}
+
 
 struct vs_mainInput {
 };
@@ -25,7 +29,7 @@ vertex vs_mainOutput vs_main(
 ) {
     int tmp1_ = {};
     int tmp2_ = {};
-    tmp1_ = static_cast<int>(vertex_index) / 2;
+    tmp1_ = naga_div(static_cast<int>(vertex_index), 2);
     tmp2_ = static_cast<int>(vertex_index) & 1;
     int _e9 = tmp1_;
     int _e15 = tmp2_;
diff --git a/naga/tests/out/spv/collatz.spvasm b/naga/tests/out/spv/collatz.spvasm
index 5c77a52269..6c6cfb4f91 100644
--- a/naga/tests/out/spv/collatz.spvasm
+++ b/naga/tests/out/spv/collatz.spvasm
@@ -1,28 +1,34 @@
 ; SPIR-V
 ; Version: 1.0
 ; Generator: rspirv
-; Bound: 62
+; Bound: 77
 OpCapability Shader
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %51 "main" %48
-OpExecutionMode %51 LocalSize 1 1 1
+OpEntryPoint GLCompute %66 "main" %63
+OpExecutionMode %66 LocalSize 1 1 1
 OpMemberName %5 0 "data"
 OpName %5 "PrimeIndices"
 OpName %7 "v_indices"
-OpName %10 "n_base"
-OpName %11 "collatz_iterations"
-OpName %17 "n"
-OpName %20 "i"
-OpName %48 "global_id"
-OpName %51 "main"
+OpName %9 "naga_mod"
+OpName %11 "lhs"
+OpName %12 "rhs"
+OpName %20 "naga_div"
+OpName %21 "lhs"
+OpName %22 "rhs"
+OpName %28 "n_base"
+OpName %29 "collatz_iterations"
+OpName %33 "n"
+OpName %36 "i"
+OpName %63 "global_id"
+OpName %66 "main"
 OpDecorate %4 ArrayStride 4
 OpMemberDecorate %5 0 Offset 0
 OpDecorate %5 Block
 OpDecorate %7 DescriptorSet 0
 OpDecorate %7 Binding 0
-OpDecorate %48 BuiltIn GlobalInvocationId
+OpDecorate %63 BuiltIn GlobalInvocationId
 %2 = OpTypeVoid
 %3 = OpTypeInt 32 0
 %4 = OpTypeRuntimeArray %3
@@ -30,81 +36,100 @@ OpDecorate %48 BuiltIn GlobalInvocationId
 %6 = OpTypeVector %3 3
 %8 = OpTypePointer StorageBuffer %5
 %7 = OpVariable  %8  StorageBuffer
-%12 = OpTypeFunction %3 %3
-%13 = OpConstant  %3  0
-%14 = OpConstant  %3  1
-%15 = OpConstant  %3  2
-%16 = OpConstant  %3  3
-%18 = OpTypePointer Function %3
-%19 = OpConstantNull  %3
-%27 = OpTypeBool
-%49 = OpTypePointer Input %6
-%48 = OpVariable  %49  Input
-%52 = OpTypeFunction %2
-%54 = OpTypePointer StorageBuffer %4
-%56 = OpTypePointer StorageBuffer %3
-%11 = OpFunction  %3  None %12
-%10 = OpFunctionParameter  %3
-%9 = OpLabel
-%17 = OpVariable  %18  Function %19
-%20 = OpVariable  %18  Function %13
-OpBranch %21
-%21 = OpLabel
-OpStore %17 %10
-OpBranch %22
-%22 = OpLabel
-OpLoopMerge %23 %25 None
-OpBranch %24
-%24 = OpLabel
-%26 = OpLoad  %3  %17
-%28 = OpUGreaterThan  %27  %26 %14
-OpSelectionMerge %29 None
-OpBranchConditional %28 %29 %30
-%30 = OpLabel
-OpBranch %23
-%29 = OpLabel
-OpBranch %31
-%31 = OpLabel
-%33 = OpLoad  %3  %17
-%34 = OpUMod  %3  %33 %15
-%35 = OpIEqual  %27  %34 %13
-OpSelectionMerge %36 None
-OpBranchConditional %35 %37 %38
-%37 = OpLabel
-%39 = OpLoad  %3  %17
-%40 = OpUDiv  %3  %39 %15
-OpStore %17 %40
-OpBranch %36
-%38 = OpLabel
-%41 = OpLoad  %3  %17
-%42 = OpIMul  %3  %16 %41
-%43 = OpIAdd  %3  %42 %14
-OpStore %17 %43
-OpBranch %36
-%36 = OpLabel
-%44 = OpLoad  %3  %20
-%45 = OpIAdd  %3  %44 %14
-OpStore %20 %45
-OpBranch %32
-%32 = OpLabel
-OpBranch %25
-%25 = OpLabel
-OpBranch %22
+%10 = OpTypeFunction %3 %3 %3
+%14 = OpTypeBool
+%15 = OpConstant  %3  0
+%17 = OpConstant  %3  1
+%30 = OpTypeFunction %3 %3
+%31 = OpConstant  %3  2
+%32 = OpConstant  %3  3
+%34 = OpTypePointer Function %3
+%35 = OpConstantNull  %3
+%64 = OpTypePointer Input %6
+%63 = OpVariable  %64  Input
+%67 = OpTypeFunction %2
+%69 = OpTypePointer StorageBuffer %4
+%71 = OpTypePointer StorageBuffer %3
+%9 = OpFunction  %3  None %10
+%11 = OpFunctionParameter  %3
+%12 = OpFunctionParameter  %3
+%13 = OpLabel
+%16 = OpIEqual  %14  %12 %15
+%18 = OpSelect  %3  %16 %17 %12
+%19 = OpUMod  %3  %11 %18
+OpReturnValue %19
+OpFunctionEnd
+%20 = OpFunction  %3  None %10
+%21 = OpFunctionParameter  %3
+%22 = OpFunctionParameter  %3
 %23 = OpLabel
-%46 = OpLoad  %3  %20
-OpReturnValue %46
+%24 = OpIEqual  %14  %22 %15
+%25 = OpSelect  %3  %24 %17 %22
+%26 = OpUDiv  %3  %21 %25
+OpReturnValue %26
 OpFunctionEnd
-%51 = OpFunction  %2  None %52
-%47 = OpLabel
-%50 = OpLoad  %6  %48
-OpBranch %53
+%29 = OpFunction  %3  None %30
+%28 = OpFunctionParameter  %3
+%27 = OpLabel
+%33 = OpVariable  %34  Function %35
+%36 = OpVariable  %34  Function %15
+OpBranch %37
+%37 = OpLabel
+OpStore %33 %28
+OpBranch %38
+%38 = OpLabel
+OpLoopMerge %39 %41 None
+OpBranch %40
+%40 = OpLabel
+%42 = OpLoad  %3  %33
+%43 = OpUGreaterThan  %14  %42 %17
+OpSelectionMerge %44 None
+OpBranchConditional %43 %44 %45
+%45 = OpLabel
+OpBranch %39
+%44 = OpLabel
+OpBranch %46
+%46 = OpLabel
+%48 = OpLoad  %3  %33
+%49 = OpFunctionCall  %3  %9 %48 %31
+%50 = OpIEqual  %14  %49 %15
+OpSelectionMerge %51 None
+OpBranchConditional %50 %52 %53
+%52 = OpLabel
+%54 = OpLoad  %3  %33
+%55 = OpFunctionCall  %3  %20 %54 %31
+OpStore %33 %55
+OpBranch %51
 %53 = OpLabel
-%55 = OpCompositeExtract  %3  %50 0
-%57 = OpCompositeExtract  %3  %50 0
-%58 = OpAccessChain  %56  %7 %13 %57
-%59 = OpLoad  %3  %58
-%60 = OpFunctionCall  %3  %11 %59
-%61 = OpAccessChain  %56  %7 %13 %55
-OpStore %61 %60
+%56 = OpLoad  %3  %33
+%57 = OpIMul  %3  %32 %56
+%58 = OpIAdd  %3  %57 %17
+OpStore %33 %58
+OpBranch %51
+%51 = OpLabel
+%59 = OpLoad  %3  %36
+%60 = OpIAdd  %3  %59 %17
+OpStore %36 %60
+OpBranch %47
+%47 = OpLabel
+OpBranch %41
+%41 = OpLabel
+OpBranch %38
+%39 = OpLabel
+%61 = OpLoad  %3  %36
+OpReturnValue %61
+OpFunctionEnd
+%66 = OpFunction  %2  None %67
+%62 = OpLabel
+%65 = OpLoad  %6  %63
+OpBranch %68
+%68 = OpLabel
+%70 = OpCompositeExtract  %3  %65 0
+%72 = OpCompositeExtract  %3  %65 0
+%73 = OpAccessChain  %71  %7 %15 %72
+%74 = OpLoad  %3  %73
+%75 = OpFunctionCall  %3  %29 %74
+%76 = OpAccessChain  %71  %7 %15 %70
+OpStore %76 %75
 OpReturn
 OpFunctionEnd
\ No newline at end of file
diff --git a/naga/tests/out/spv/debug-symbol-large-source.spvasm b/naga/tests/out/spv/debug-symbol-large-source.spvasm
index 15e95cf2a5..c761e6cdac 100644
--- a/naga/tests/out/spv/debug-symbol-large-source.spvasm
+++ b/naga/tests/out/spv/debug-symbol-large-source.spvasm
@@ -1,19 +1,19 @@
 ; SPIR-V
 ; Version: 1.1
 ; Generator: rspirv
-; Bound: 644
+; Bound: 659
 OpCapability Shader
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %345 "gen_terrain_compute" %342
-OpEntryPoint Vertex %415 "gen_terrain_vertex" %406 %409 %411 %413
-OpEntryPoint Fragment %465 "gen_terrain_fragment" %455 %457 %460 %463 %464
-OpEntryPoint Vertex %558 "vs_main" %549 %552 %554 %555 %557
-OpEntryPoint Fragment %583 "fs_main" %576 %578 %580 %582
-OpExecutionMode %345 LocalSize 64 1 1
-OpExecutionMode %465 OriginUpperLeft
-OpExecutionMode %583 OriginUpperLeft
+OpEntryPoint GLCompute %353 "gen_terrain_compute" %350
+OpEntryPoint Vertex %430 "gen_terrain_vertex" %421 %424 %426 %428
+OpEntryPoint Fragment %480 "gen_terrain_fragment" %470 %472 %475 %478 %479
+OpEntryPoint Vertex %573 "vs_main" %564 %567 %569 %570 %572
+OpEntryPoint Fragment %598 "fs_main" %591 %593 %595 %597
+OpExecutionMode %353 LocalSize 64 1 1
+OpExecutionMode %480 OriginUpperLeft
+OpExecutionMode %598 OriginUpperLeft
 %3 = OpString "debug-symbol-large-source.wgsl"
 OpSource Unknown 0 %3 "//This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다
 //This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다 This is a test 这是测试 これはテストだ 테스트입니다
@@ -7541,38 +7541,44 @@ OpName %257 "terrain_point"
 OpName %268 "p"
 OpName %269 "min_max_height"
 OpName %270 "terrain_vertex"
-OpName %300 "vert_index"
-OpName %301 "chunk_size"
-OpName %302 "chunk_corner"
-OpName %303 "index_to_p"
-OpName %319 "p"
-OpName %320 "color23"
-OpName %342 "gid"
-OpName %345 "gen_terrain_compute"
-OpName %406 "vindex"
-OpName %409 "index"
-OpName %411 "position"
-OpName %413 "uv"
-OpName %415 "gen_terrain_vertex"
-OpName %455 "index"
-OpName %457 "position"
-OpName %460 "uv"
-OpName %463 "vert_component"
-OpName %464 "index"
-OpName %465 "gen_terrain_fragment"
-OpName %468 "vert_component"
-OpName %469 "index"
-OpName %549 "position"
-OpName %552 "normal"
-OpName %554 "clip_position"
-OpName %555 "normal"
-OpName %557 "world_pos"
-OpName %558 "vs_main"
-OpName %576 "clip_position"
-OpName %578 "normal"
-OpName %580 "world_pos"
-OpName %583 "fs_main"
-OpName %592 "color"
+OpName %299 "naga_div"
+OpName %301 "lhs"
+OpName %302 "rhs"
+OpName %308 "vert_index"
+OpName %309 "chunk_size"
+OpName %310 "chunk_corner"
+OpName %311 "index_to_p"
+OpName %327 "p"
+OpName %328 "color23"
+OpName %350 "gid"
+OpName %353 "gen_terrain_compute"
+OpName %413 "naga_mod"
+OpName %414 "lhs"
+OpName %415 "rhs"
+OpName %421 "vindex"
+OpName %424 "index"
+OpName %426 "position"
+OpName %428 "uv"
+OpName %430 "gen_terrain_vertex"
+OpName %470 "index"
+OpName %472 "position"
+OpName %475 "uv"
+OpName %478 "vert_component"
+OpName %479 "index"
+OpName %480 "gen_terrain_fragment"
+OpName %483 "vert_component"
+OpName %484 "index"
+OpName %564 "position"
+OpName %567 "normal"
+OpName %569 "clip_position"
+OpName %570 "normal"
+OpName %572 "world_pos"
+OpName %573 "vs_main"
+OpName %591 "clip_position"
+OpName %593 "normal"
+OpName %595 "world_pos"
+OpName %598 "fs_main"
+OpName %607 "color"
 OpMemberDecorate %13 0 Offset 0
 OpMemberDecorate %13 1 Offset 8
 OpMemberDecorate %13 2 Offset 16
@@ -7631,27 +7637,27 @@ OpDecorate %49 DescriptorSet 2
 OpDecorate %49 Binding 2
 OpDecorate %50 DescriptorSet 2
 OpDecorate %50 Binding 3
-OpDecorate %342 BuiltIn GlobalInvocationId
-OpDecorate %406 BuiltIn VertexIndex
-OpDecorate %409 Location 0
-OpDecorate %409 Flat
-OpDecorate %411 BuiltIn Position
-OpDecorate %413 Location 1
-OpDecorate %455 Location 0
-OpDecorate %455 Flat
-OpDecorate %457 BuiltIn FragCoord
-OpDecorate %460 Location 1
-OpDecorate %463 Location 0
-OpDecorate %464 Location 1
-OpDecorate %549 Location 0
-OpDecorate %552 Location 1
-OpDecorate %554 BuiltIn Position
-OpDecorate %555 Location 0
-OpDecorate %557 Location 1
-OpDecorate %576 BuiltIn FragCoord
-OpDecorate %578 Location 0
-OpDecorate %580 Location 1
-OpDecorate %582 Location 0
+OpDecorate %350 BuiltIn GlobalInvocationId
+OpDecorate %421 BuiltIn VertexIndex
+OpDecorate %424 Location 0
+OpDecorate %424 Flat
+OpDecorate %426 BuiltIn Position
+OpDecorate %428 Location 1
+OpDecorate %470 Location 0
+OpDecorate %470 Flat
+OpDecorate %472 BuiltIn FragCoord
+OpDecorate %475 Location 1
+OpDecorate %478 Location 0
+OpDecorate %479 Location 1
+OpDecorate %564 Location 0
+OpDecorate %567 Location 1
+OpDecorate %569 BuiltIn Position
+OpDecorate %570 Location 0
+OpDecorate %572 Location 1
+OpDecorate %591 BuiltIn FragCoord
+OpDecorate %593 Location 0
+OpDecorate %595 Location 1
+OpDecorate %597 Location 0
 %2 = OpTypeVoid
 %4 = OpTypeFloat 32
 %5 = OpTypeVector %4 3
@@ -7754,72 +7760,73 @@ OpDecorate %582 Location 0
 %275 = OpConstant  %4  -0.1
 %276 = OpConstantComposite  %6  %275 %74
 %277 = OpConstantComposite  %6  %74 %275
-%304 = OpTypeFunction %6 %8 %10 %11
-%321 = OpTypeFunction %5 %6
-%322 = OpConstant  %4  23.0
-%323 = OpConstant  %4  32.0
-%324 = OpConstantComposite  %6  %322 %323
-%325 = OpConstant  %4  -43.0
-%326 = OpConstant  %4  3.0
-%327 = OpConstantComposite  %6  %325 %326
-%343 = OpTypePointer Input %19
-%342 = OpVariable  %343  Input
-%346 = OpTypeFunction %2
-%347 = OpTypePointer Uniform %13
-%349 = OpConstant  %8  6
-%350 = OpConstant  %8  2
-%351 = OpConstant  %8  3
-%352 = OpConstant  %8  4
-%355 = OpTypePointer Uniform %10
-%358 = OpTypePointer Uniform %11
-%362 = OpTypePointer StorageBuffer %15
-%363 = OpTypePointer StorageBuffer %14
-%364 = OpTypePointer Uniform %6
-%371 = OpTypePointer Uniform %8
-%392 = OpTypePointer StorageBuffer %17
-%393 = OpTypePointer StorageBuffer %8
-%407 = OpTypePointer Input %8
-%406 = OpVariable  %407  Input
-%410 = OpTypePointer Output %8
-%409 = OpVariable  %410  Output
-%412 = OpTypePointer Output %7
-%411 = OpVariable  %412  Output
-%414 = OpTypePointer Output %6
-%413 = OpVariable  %414  Output
-%416 = OpTypePointer Uniform %20
-%418 = OpConstant  %4  -1.0
-%419 = OpConstantComposite  %6  %418 %418
-%434 = OpTypePointer Uniform %8
-%455 = OpVariable  %407  Input
-%458 = OpTypePointer Input %7
-%457 = OpVariable  %458  Input
-%461 = OpTypePointer Input %6
-%460 = OpVariable  %461  Input
-%463 = OpVariable  %410  Output
-%464 = OpVariable  %410  Output
-%467 = OpConstant  %4  6.0
-%550 = OpTypePointer Input %5
-%549 = OpVariable  %550  Input
-%552 = OpVariable  %550  Input
-%554 = OpVariable  %412  Output
-%556 = OpTypePointer Output %5
-%555 = OpVariable  %556  Output
-%557 = OpVariable  %556  Output
-%559 = OpTypePointer Uniform %24
-%562 = OpTypePointer Uniform %23
-%576 = OpVariable  %458  Input
-%578 = OpVariable  %550  Input
-%580 = OpVariable  %550  Input
-%582 = OpVariable  %412  Output
-%585 = OpTypePointer Uniform %25
-%587 = OpConstantComposite  %5  %272 %272 %272
-%588 = OpConstant  %4  0.7
-%589 = OpConstantComposite  %5  %78 %272 %588
-%590 = OpConstant  %4  0.2
-%591 = OpConstantComposite  %5  %590 %590 %590
-%593 = OpConstantNull  %5
-%608 = OpTypePointer Uniform %5
-%617 = OpTypePointer Uniform %7
+%300 = OpTypeFunction %8 %8 %8
+%312 = OpTypeFunction %6 %8 %10 %11
+%329 = OpTypeFunction %5 %6
+%330 = OpConstant  %4  23.0
+%331 = OpConstant  %4  32.0
+%332 = OpConstantComposite  %6  %330 %331
+%333 = OpConstant  %4  -43.0
+%334 = OpConstant  %4  3.0
+%335 = OpConstantComposite  %6  %333 %334
+%351 = OpTypePointer Input %19
+%350 = OpVariable  %351  Input
+%354 = OpTypeFunction %2
+%355 = OpTypePointer Uniform %13
+%357 = OpConstant  %8  6
+%358 = OpConstant  %8  2
+%359 = OpConstant  %8  3
+%360 = OpConstant  %8  4
+%363 = OpTypePointer Uniform %10
+%366 = OpTypePointer Uniform %11
+%370 = OpTypePointer StorageBuffer %15
+%371 = OpTypePointer StorageBuffer %14
+%372 = OpTypePointer Uniform %6
+%379 = OpTypePointer Uniform %8
+%400 = OpTypePointer StorageBuffer %17
+%401 = OpTypePointer StorageBuffer %8
+%422 = OpTypePointer Input %8
+%421 = OpVariable  %422  Input
+%425 = OpTypePointer Output %8
+%424 = OpVariable  %425  Output
+%427 = OpTypePointer Output %7
+%426 = OpVariable  %427  Output
+%429 = OpTypePointer Output %6
+%428 = OpVariable  %429  Output
+%431 = OpTypePointer Uniform %20
+%433 = OpConstant  %4  -1.0
+%434 = OpConstantComposite  %6  %433 %433
+%449 = OpTypePointer Uniform %8
+%470 = OpVariable  %422  Input
+%473 = OpTypePointer Input %7
+%472 = OpVariable  %473  Input
+%476 = OpTypePointer Input %6
+%475 = OpVariable  %476  Input
+%478 = OpVariable  %425  Output
+%479 = OpVariable  %425  Output
+%482 = OpConstant  %4  6.0
+%565 = OpTypePointer Input %5
+%564 = OpVariable  %565  Input
+%567 = OpVariable  %565  Input
+%569 = OpVariable  %427  Output
+%571 = OpTypePointer Output %5
+%570 = OpVariable  %571  Output
+%572 = OpVariable  %571  Output
+%574 = OpTypePointer Uniform %24
+%577 = OpTypePointer Uniform %23
+%591 = OpVariable  %473  Input
+%593 = OpVariable  %565  Input
+%595 = OpVariable  %565  Input
+%597 = OpVariable  %427  Output
+%600 = OpTypePointer Uniform %25
+%602 = OpConstantComposite  %5  %272 %272 %272
+%603 = OpConstant  %4  0.7
+%604 = OpConstantComposite  %5  %78 %272 %603
+%605 = OpConstant  %4  0.2
+%606 = OpConstantComposite  %5  %605 %605 %605
+%608 = OpConstantNull  %5
+%623 = OpTypePointer Uniform %5
+%632 = OpTypePointer Uniform %7
 %53 = OpFunction  %5  None %54
 %52 = OpFunctionParameter  %5
 %51 = OpLabel
@@ -8146,480 +8153,498 @@ OpLine %3 7239 12
 %298 = OpCompositeConstruct  %14  %279 %297
 OpReturnValue %298
 OpFunctionEnd
-%303 = OpFunction  %6  None %304
-%300 = OpFunctionParameter  %8
-%301 = OpFunctionParameter  %10
-%302 = OpFunctionParameter  %11
-%299 = OpLabel
-OpBranch %305
-%305 = OpLabel
+%299 = OpFunction  %8  None %300
+%301 = OpFunctionParameter  %8
+%302 = OpFunctionParameter  %8
+%303 = OpLabel
+%304 = OpIEqual  %112  %302 %135
+%305 = OpSelect  %8  %304 %126 %302
+%306 = OpUDiv  %8  %301 %305
+OpReturnValue %306
+OpFunctionEnd
+%311 = OpFunction  %6  None %312
+%308 = OpFunctionParameter  %8
+%309 = OpFunctionParameter  %10
+%310 = OpFunctionParameter  %11
+%307 = OpLabel
+OpBranch %313
+%313 = OpLabel
 OpLine %3 7244 9
-%306 = OpConvertUToF  %4  %300
-%307 = OpCompositeExtract  %8  %301 0
+%314 = OpConvertUToF  %4  %308
+%315 = OpCompositeExtract  %8  %309 0
 OpLine %3 7244 9
-%308 = OpIAdd  %8  %307 %126
-%309 = OpConvertUToF  %4  %308
-%310 = OpFRem  %4  %306 %309
-%311 = OpCompositeExtract  %8  %301 0
+%316 = OpIAdd  %8  %315 %126
+%317 = OpConvertUToF  %4  %316
+%318 = OpFRem  %4  %314 %317
+%319 = OpCompositeExtract  %8  %309 0
 OpLine %3 7243 12
-%312 = OpIAdd  %8  %311 %126
-%313 = OpUDiv  %8  %300 %312
-%314 = OpConvertUToF  %4  %313
-%315 = OpCompositeConstruct  %6  %310 %314
-%316 = OpConvertSToF  %6  %302
-%317 = OpFAdd  %6  %315 %316
-OpReturnValue %317
+%320 = OpIAdd  %8  %319 %126
+%321 = OpFunctionCall  %8  %299 %308 %320
+%322 = OpConvertUToF  %4  %321
+%323 = OpCompositeConstruct  %6  %318 %322
+%324 = OpConvertSToF  %6  %310
+%325 = OpFAdd  %6  %323 %324
+OpReturnValue %325
 OpFunctionEnd
-%320 = OpFunction  %5  None %321
-%319 = OpFunctionParameter  %6
-%318 = OpLabel
-OpBranch %328
-%328 = OpLabel
+%328 = OpFunction  %5  None %329
+%327 = OpFunctionParameter  %6
+%326 = OpLabel
+OpBranch %336
+%336 = OpLabel
 OpLine %3 7413 9
-%329 = OpFunctionCall  %4  %67 %319
+%337 = OpFunctionCall  %4  %67 %327
 OpLine %3 7413 9
-%330 = OpFMul  %4  %329 %78
+%338 = OpFMul  %4  %337 %78
 OpLine %3 7413 9
-%331 = OpFAdd  %4  %330 %78
+%339 = OpFAdd  %4  %338 %78
 OpLine %3 7414 17
-%332 = OpFAdd  %6  %319 %324
+%340 = OpFAdd  %6  %327 %332
 OpLine %3 7414 9
-%333 = OpFunctionCall  %4  %67 %332
+%341 = OpFunctionCall  %4  %67 %340
 OpLine %3 7414 9
-%334 = OpFMul  %4  %333 %78
+%342 = OpFMul  %4  %341 %78
 OpLine %3 7414 9
-%335 = OpFAdd  %4  %334 %78
+%343 = OpFAdd  %4  %342 %78
 OpLine %3 7415 17
-%336 = OpFAdd  %6  %319 %327
+%344 = OpFAdd  %6  %327 %335
 OpLine %3 7415 9
-%337 = OpFunctionCall  %4  %67 %336
+%345 = OpFunctionCall  %4  %67 %344
 OpLine %3 7415 9
-%338 = OpFMul  %4  %337 %78
+%346 = OpFMul  %4  %345 %78
 OpLine %3 7412 12
-%339 = OpFAdd  %4  %338 %78
-%340 = OpCompositeConstruct  %5  %331 %335 %339
-OpReturnValue %340
+%347 = OpFAdd  %4  %346 %78
+%348 = OpCompositeConstruct  %5  %339 %343 %347
+OpReturnValue %348
 OpFunctionEnd
-%345 = OpFunction  %2  None %346
-%341 = OpLabel
-%344 = OpLoad  %19  %342
-%348 = OpAccessChain  %347  %29 %135
-OpBranch %353
-%353 = OpLabel
+%353 = OpFunction  %2  None %354
+%349 = OpLabel
+%352 = OpLoad  %19  %350
+%356 = OpAccessChain  %355  %29 %135
+OpBranch %361
+%361 = OpLabel
 OpLine %3 7254 22
-%354 = OpCompositeExtract  %8  %344 0
+%362 = OpCompositeExtract  %8  %352 0
 OpLine %3 7256 36
-%356 = OpAccessChain  %355  %348 %135
-%357 = OpLoad  %10  %356
+%364 = OpAccessChain  %363  %356 %135
+%365 = OpLoad  %10  %364
 OpLine %3 7256 59
-%359 = OpAccessChain  %358  %348 %126
-%360 = OpLoad  %11  %359
+%367 = OpAccessChain  %366  %356 %126
+%368 = OpLoad  %11  %367
 OpLine %3 7256 13
-%361 = OpFunctionCall  %6  %303 %354 %357 %360
+%369 = OpFunctionCall  %6  %311 %362 %365 %368
 OpLine %3 7258 5
 OpLine %3 7258 51
-%365 = OpAccessChain  %364  %348 %350
-%366 = OpLoad  %6  %365
+%373 = OpAccessChain  %372  %356 %358
+%374 = OpLoad  %6  %373
 OpLine %3 7258 33
-%367 = OpFunctionCall  %14  %270 %361 %366
+%375 = OpFunctionCall  %14  %270 %369 %374
 OpLine %3 7258 5
-%368 = OpAccessChain  %363  %32 %135 %354
-OpStore %368 %367
+%376 = OpAccessChain  %371  %32 %135 %362
+OpStore %376 %375
 OpLine %3 7261 23
-%369 = OpCompositeExtract  %8  %344 0
+%377 = OpCompositeExtract  %8  %352 0
 OpLine %3 7261 23
-%370 = OpIMul  %8  %369 %349
+%378 = OpIMul  %8  %377 %357
 OpLine %3 7263 24
-%372 = OpAccessChain  %371  %348 %135 %135
-%373 = OpLoad  %8  %372
+%380 = OpAccessChain  %379  %356 %135 %135
+%381 = OpLoad  %8  %380
 OpLine %3 7263 24
-%374 = OpAccessChain  %371  %348 %135 %126
-%375 = OpLoad  %8  %374
-%376 = OpIMul  %8  %373 %375
+%382 = OpAccessChain  %379  %356 %135 %126
+%383 = OpLoad  %8  %382
+%384 = OpIMul  %8  %381 %383
 OpLine %3 7263 8
-%377 = OpIMul  %8  %376 %349
-%378 = OpUGreaterThanEqual  %112  %370 %377
+%385 = OpIMul  %8  %384 %357
+%386 = OpUGreaterThanEqual  %112  %378 %385
 OpLine %3 7263 5
-OpSelectionMerge %379 None
-OpBranchConditional %378 %380 %379
-%380 = OpLabel
+OpSelectionMerge %387 None
+OpBranchConditional %386 %388 %387
+%388 = OpLabel
 OpReturn
-%379 = OpLabel
+%387 = OpLabel
 OpLine %3 7265 28
-%381 = OpCompositeExtract  %8  %344 0
+%389 = OpCompositeExtract  %8  %352 0
 OpLine %3 7265 15
-%382 = OpAccessChain  %371  %348 %135 %135
-%383 = OpLoad  %8  %382
-%384 = OpUDiv  %8  %381 %383
-%385 = OpIAdd  %8  %354 %384
+%390 = OpAccessChain  %379  %356 %135 %135
+%391 = OpLoad  %8  %390
+%392 = OpFunctionCall  %8  %299 %389 %391
+%393 = OpIAdd  %8  %362 %392
 OpLine %3 7266 15
-%386 = OpIAdd  %8  %385 %126
+%394 = OpIAdd  %8  %393 %126
 OpLine %3 7267 15
-%387 = OpAccessChain  %371  %348 %135 %135
-%388 = OpLoad  %8  %387
-%389 = OpIAdd  %8  %385 %388
+%395 = OpAccessChain  %379  %356 %135 %135
+%396 = OpLoad  %8  %395
+%397 = OpIAdd  %8  %393 %396
 OpLine %3 7267 15
-%390 = OpIAdd  %8  %389 %126
+%398 = OpIAdd  %8  %397 %126
 OpLine %3 7268 15
-%391 = OpIAdd  %8  %390 %126
+%399 = OpIAdd  %8  %398 %126
 OpLine %3 7270 5
 OpLine %3 7270 5
-%394 = OpAccessChain  %393  %34 %135 %370
-OpStore %394 %385
+%402 = OpAccessChain  %401  %34 %135 %378
+OpStore %402 %393
 OpLine %3 7271 5
 OpLine %3 7271 5
-%395 = OpIAdd  %8  %370 %126
+%403 = OpIAdd  %8  %378 %126
 OpLine %3 7271 5
-%396 = OpAccessChain  %393  %34 %135 %395
-OpStore %396 %390
+%404 = OpAccessChain  %401  %34 %135 %403
+OpStore %404 %398
 OpLine %3 7272 5
 OpLine %3 7272 5
-%397 = OpIAdd  %8  %370 %350
+%405 = OpIAdd  %8  %378 %358
 OpLine %3 7272 5
-%398 = OpAccessChain  %393  %34 %135 %397
-OpStore %398 %391
+%406 = OpAccessChain  %401  %34 %135 %405
+OpStore %406 %399
 OpLine %3 7273 5
 OpLine %3 7273 5
-%399 = OpIAdd  %8  %370 %351
+%407 = OpIAdd  %8  %378 %359
 OpLine %3 7273 5
-%400 = OpAccessChain  %393  %34 %135 %399
-OpStore %400 %385
+%408 = OpAccessChain  %401  %34 %135 %407
+OpStore %408 %393
 OpLine %3 7274 5
 OpLine %3 7274 5
-%401 = OpIAdd  %8  %370 %352
+%409 = OpIAdd  %8  %378 %360
 OpLine %3 7274 5
-%402 = OpAccessChain  %393  %34 %135 %401
-OpStore %402 %391
+%410 = OpAccessChain  %401  %34 %135 %409
+OpStore %410 %399
 OpLine %3 7275 5
 OpLine %3 7275 5
-%403 = OpIAdd  %8  %370 %205
+%411 = OpIAdd  %8  %378 %205
 OpLine %3 7275 5
-%404 = OpAccessChain  %393  %34 %135 %403
-OpStore %404 %386
+%412 = OpAccessChain  %401  %34 %135 %411
+OpStore %412 %394
 OpReturn
 OpFunctionEnd
-%415 = OpFunction  %2  None %346
-%405 = OpLabel
-%408 = OpLoad  %8  %406
-%417 = OpAccessChain  %416  %36 %135
-OpBranch %420
+%413 = OpFunction  %8  None %300
+%414 = OpFunctionParameter  %8
+%415 = OpFunctionParameter  %8
+%416 = OpLabel
+%417 = OpIEqual  %112  %415 %135
+%418 = OpSelect  %8  %417 %126 %415
+%419 = OpUMod  %8  %414 %418
+OpReturnValue %419
+OpFunctionEnd
+%430 = OpFunction  %2  None %354
 %420 = OpLabel
+%423 = OpLoad  %8  %421
+%432 = OpAccessChain  %431  %36 %135
+OpBranch %435
+%435 = OpLabel
 OpLine %3 7304 19
-%421 = OpIAdd  %8  %408 %350
+%436 = OpIAdd  %8  %423 %358
 OpLine %3 7304 18
-%422 = OpUDiv  %8  %421 %351
+%437 = OpFunctionCall  %8  %299 %436 %359
 OpLine %3 7304 13
-%423 = OpUMod  %8  %422 %350
-%424 = OpConvertUToF  %4  %423
+%438 = OpFunctionCall  %8  %413 %437 %358
+%439 = OpConvertUToF  %4  %438
 OpLine %3 7305 19
-%425 = OpIAdd  %8  %408 %126
+%440 = OpIAdd  %8  %423 %126
 OpLine %3 7305 18
-%426 = OpUDiv  %8  %425 %351
+%441 = OpFunctionCall  %8  %299 %440 %359
 OpLine %3 7305 13
-%427 = OpUMod  %8  %426 %350
-%428 = OpConvertUToF  %4  %427
+%442 = OpFunctionCall  %8  %413 %441 %358
+%443 = OpConvertUToF  %4  %442
 OpLine %3 7306 14
-%429 = OpCompositeConstruct  %6  %424 %428
+%444 = OpCompositeConstruct  %6  %439 %443
 OpLine %3 7308 30
-%430 = OpVectorTimesScalar  %6  %429 %81
+%445 = OpVectorTimesScalar  %6  %444 %81
 OpLine %3 7308 30
-%431 = OpFAdd  %6  %419 %430
+%446 = OpFAdd  %6  %434 %445
 OpLine %3 7308 20
-%432 = OpCompositeConstruct  %7  %431 %74 %56
+%447 = OpCompositeConstruct  %7  %446 %74 %56
 OpLine %3 7311 21
-%433 = OpCompositeExtract  %4  %429 0
+%448 = OpCompositeExtract  %4  %444 0
 OpLine %3 7311 21
-%435 = OpAccessChain  %434  %417 %351
-%436 = OpLoad  %8  %435
-%437 = OpConvertUToF  %4  %436
-%438 = OpFMul  %4  %433 %437
-%439 = OpCompositeExtract  %4  %429 1
+%450 = OpAccessChain  %449  %432 %359
+%451 = OpLoad  %8  %450
+%452 = OpConvertUToF  %4  %451
+%453 = OpFMul  %4  %448 %452
+%454 = OpCompositeExtract  %4  %444 1
 OpLine %3 7311 17
-%440 = OpAccessChain  %434  %417 %351
-%441 = OpLoad  %8  %440
-%442 = OpConvertUToF  %4  %441
-%443 = OpFMul  %4  %439 %442
-%444 = OpFAdd  %4  %438 %443
-%445 = OpConvertFToU  %8  %444
+%455 = OpAccessChain  %449  %432 %359
+%456 = OpLoad  %8  %455
+%457 = OpConvertUToF  %4  %456
+%458 = OpFMul  %4  %454 %457
+%459 = OpFAdd  %4  %453 %458
+%460 = OpConvertFToU  %8  %459
 OpLine %3 7311 17
-%446 = OpAccessChain  %434  %417 %352
-%447 = OpLoad  %8  %446
-%448 = OpIAdd  %8  %445 %447
+%461 = OpAccessChain  %449  %432 %360
+%462 = OpLoad  %8  %461
+%463 = OpIAdd  %8  %460 %462
 OpLine %3 7313 12
-%449 = OpCompositeConstruct  %21  %448 %432 %429
-%450 = OpCompositeExtract  %8  %449 0
-OpStore %409 %450
-%451 = OpCompositeExtract  %7  %449 1
-OpStore %411 %451
-%452 = OpCompositeExtract  %6  %449 2
-OpStore %413 %452
+%464 = OpCompositeConstruct  %21  %463 %447 %444
+%465 = OpCompositeExtract  %8  %464 0
+OpStore %424 %465
+%466 = OpCompositeExtract  %7  %464 1
+OpStore %426 %466
+%467 = OpCompositeExtract  %6  %464 2
+OpStore %428 %467
 OpReturn
 OpFunctionEnd
-%465 = OpFunction  %2  None %346
-%453 = OpLabel
-%468 = OpVariable  %215  Function %74
-%469 = OpVariable  %218  Function %135
-%456 = OpLoad  %8  %455
-%459 = OpLoad  %7  %457
-%462 = OpLoad  %6  %460
-%454 = OpCompositeConstruct  %21  %456 %459 %462
-%466 = OpAccessChain  %416  %36 %135
-OpBranch %470
-%470 = OpLabel
+%480 = OpFunction  %2  None %354
+%468 = OpLabel
+%483 = OpVariable  %215  Function %74
+%484 = OpVariable  %218  Function %135
+%471 = OpLoad  %8  %470
+%474 = OpLoad  %7  %472
+%477 = OpLoad  %6  %475
+%469 = OpCompositeConstruct  %21  %471 %474 %477
+%481 = OpAccessChain  %431  %36 %135
+OpBranch %485
+%485 = OpLabel
 OpLine %3 7324 17
-%471 = OpCompositeExtract  %6  %454 2
-%472 = OpCompositeExtract  %4  %471 0
+%486 = OpCompositeExtract  %6  %469 2
+%487 = OpCompositeExtract  %4  %486 0
 OpLine %3 7324 17
-%473 = OpAccessChain  %434  %466 %351
-%474 = OpLoad  %8  %473
-%475 = OpConvertUToF  %4  %474
-%476 = OpFMul  %4  %472 %475
-%477 = OpCompositeExtract  %6  %454 2
-%478 = OpCompositeExtract  %4  %477 1
+%488 = OpAccessChain  %449  %481 %359
+%489 = OpLoad  %8  %488
+%490 = OpConvertUToF  %4  %489
+%491 = OpFMul  %4  %487 %490
+%492 = OpCompositeExtract  %6  %469 2
+%493 = OpCompositeExtract  %4  %492 1
 OpLine %3 7324 70
-%479 = OpAccessChain  %434  %466 %351
-%480 = OpLoad  %8  %479
+%494 = OpAccessChain  %449  %481 %359
+%495 = OpLoad  %8  %494
 OpLine %3 7324 13
-%481 = OpAccessChain  %434  %466 %351
-%482 = OpLoad  %8  %481
-%483 = OpIMul  %8  %480 %482
-%484 = OpConvertUToF  %4  %483
-%485 = OpFMul  %4  %478 %484
-%486 = OpFAdd  %4  %476 %485
-%487 = OpConvertFToU  %8  %486
+%496 = OpAccessChain  %449  %481 %359
+%497 = OpLoad  %8  %496
+%498 = OpIMul  %8  %495 %497
+%499 = OpConvertUToF  %4  %498
+%500 = OpFMul  %4  %493 %499
+%501 = OpFAdd  %4  %491 %500
+%502 = OpConvertFToU  %8  %501
 OpLine %3 7324 13
-%488 = OpAccessChain  %434  %466 %352
-%489 = OpLoad  %8  %488
-%490 = OpIAdd  %8  %487 %489
+%503 = OpAccessChain  %449  %481 %360
+%504 = OpLoad  %8  %503
+%505 = OpIAdd  %8  %502 %504
 OpLine %3 7325 32
-%491 = OpConvertUToF  %4  %490
+%506 = OpConvertUToF  %4  %505
 OpLine %3 7325 22
-%492 = OpFDiv  %4  %491 %467
-%493 = OpExtInst  %4  %1 Floor %492
-%494 = OpConvertFToU  %8  %493
+%507 = OpFDiv  %4  %506 %482
+%508 = OpExtInst  %4  %1 Floor %507
+%509 = OpConvertFToU  %8  %508
 OpLine %3 7326 22
-%495 = OpUMod  %8  %490 %349
+%510 = OpFunctionCall  %8  %413 %505 %357
 OpLine %3 7328 36
-%496 = OpAccessChain  %355  %466 %135
-%497 = OpLoad  %10  %496
+%511 = OpAccessChain  %363  %481 %135
+%512 = OpLoad  %10  %511
 OpLine %3 7328 57
-%498 = OpAccessChain  %358  %466 %126
-%499 = OpLoad  %11  %498
+%513 = OpAccessChain  %366  %481 %126
+%514 = OpLoad  %11  %513
 OpLine %3 7328 13
-%500 = OpFunctionCall  %6  %303 %494 %497 %499
+%515 = OpFunctionCall  %6  %311 %509 %512 %514
 OpLine %3 7329 31
-%501 = OpAccessChain  %364  %466 %350
-%502 = OpLoad  %6  %501
+%516 = OpAccessChain  %372  %481 %358
+%517 = OpLoad  %6  %516
 OpLine %3 7329 13
-%503 = OpFunctionCall  %14  %270 %500 %502
+%518 = OpFunctionCall  %14  %270 %515 %517
 OpLine %3 7333 5
-OpSelectionMerge %504 None
-OpSwitch %495 %511 0 %505 1 %506 2 %507 3 %508 4 %509 5 %510
-%505 = OpLabel
+OpSelectionMerge %519 None
+OpSwitch %510 %526 0 %520 1 %521 2 %522 3 %523 4 %524 5 %525
+%520 = OpLabel
 OpLine %3 7334 37
-%512 = OpCompositeExtract  %5  %503 0
-%513 = OpCompositeExtract  %4  %512 0
+%527 = OpCompositeExtract  %5  %518 0
+%528 = OpCompositeExtract  %4  %527 0
 OpLine %3 7334 20
-OpStore %468 %513
-OpBranch %504
-%506 = OpLabel
+OpStore %483 %528
+OpBranch %519
+%521 = OpLabel
 OpLine %3 7335 37
-%514 = OpCompositeExtract  %5  %503 0
-%515 = OpCompositeExtract  %4  %514 1
+%529 = OpCompositeExtract  %5  %518 0
+%530 = OpCompositeExtract  %4  %529 1
 OpLine %3 7335 20
-OpStore %468 %515
-OpBranch %504
-%507 = OpLabel
+OpStore %483 %530
+OpBranch %519
+%522 = OpLabel
 OpLine %3 7336 37
-%516 = OpCompositeExtract  %5  %503 0
-%517 = OpCompositeExtract  %4  %516 2
+%531 = OpCompositeExtract  %5  %518 0
+%532 = OpCompositeExtract  %4  %531 2
 OpLine %3 7336 20
-OpStore %468 %517
-OpBranch %504
-%508 = OpLabel
+OpStore %483 %532
+OpBranch %519
+%523 = OpLabel
 OpLine %3 7337 37
-%518 = OpCompositeExtract  %5  %503 1
-%519 = OpCompositeExtract  %4  %518 0
+%533 = OpCompositeExtract  %5  %518 1
+%534 = OpCompositeExtract  %4  %533 0
 OpLine %3 7337 20
-OpStore %468 %519
-OpBranch %504
-%509 = OpLabel
+OpStore %483 %534
+OpBranch %519
+%524 = OpLabel
 OpLine %3 7338 37
-%520 = OpCompositeExtract  %5  %503 1
-%521 = OpCompositeExtract  %4  %520 1
+%535 = OpCompositeExtract  %5  %518 1
+%536 = OpCompositeExtract  %4  %535 1
 OpLine %3 7338 20
-OpStore %468 %521
-OpBranch %504
-%510 = OpLabel
+OpStore %483 %536
+OpBranch %519
+%525 = OpLabel
 OpLine %3 7339 37
-%522 = OpCompositeExtract  %5  %503 1
-%523 = OpCompositeExtract  %4  %522 2
+%537 = OpCompositeExtract  %5  %518 1
+%538 = OpCompositeExtract  %4  %537 2
 OpLine %3 7339 20
-OpStore %468 %523
-OpBranch %504
-%511 = OpLabel
-OpBranch %504
-%504 = OpLabel
+OpStore %483 %538
+OpBranch %519
+%526 = OpLabel
+OpBranch %519
+%519 = OpLabel
 OpLine %3 7343 15
-%524 = OpAccessChain  %371  %466 %135 %135
-%525 = OpLoad  %8  %524
-%526 = OpUDiv  %8  %494 %525
-%527 = OpIAdd  %8  %494 %526
+%539 = OpAccessChain  %379  %481 %135 %135
+%540 = OpLoad  %8  %539
+%541 = OpFunctionCall  %8  %299 %509 %540
+%542 = OpIAdd  %8  %509 %541
 OpLine %3 7344 15
-%528 = OpIAdd  %8  %527 %126
+%543 = OpIAdd  %8  %542 %126
 OpLine %3 7345 15
-%529 = OpAccessChain  %371  %466 %135 %135
-%530 = OpLoad  %8  %529
-%531 = OpIAdd  %8  %527 %530
+%544 = OpAccessChain  %379  %481 %135 %135
+%545 = OpLoad  %8  %544
+%546 = OpIAdd  %8  %542 %545
 OpLine %3 7345 15
-%532 = OpIAdd  %8  %531 %126
+%547 = OpIAdd  %8  %546 %126
 OpLine %3 7346 15
-%533 = OpIAdd  %8  %532 %126
+%548 = OpIAdd  %8  %547 %126
 OpLine %3 7349 5
-OpSelectionMerge %534 None
-OpSwitch %495 %539 0 %535 3 %535 2 %536 4 %536 1 %537 5 %538
-%535 = OpLabel
+OpSelectionMerge %549 None
+OpSwitch %510 %554 0 %550 3 %550 2 %551 4 %551 1 %552 5 %553
+%550 = OpLabel
 OpLine %3 7350 24
-OpStore %469 %527
-OpBranch %534
-%536 = OpLabel
+OpStore %484 %542
+OpBranch %549
+%551 = OpLabel
 OpLine %3 7351 24
-OpStore %469 %533
-OpBranch %534
-%537 = OpLabel
+OpStore %484 %548
+OpBranch %549
+%552 = OpLabel
 OpLine %3 7352 20
-OpStore %469 %532
-OpBranch %534
-%538 = OpLabel
+OpStore %484 %547
+OpBranch %549
+%553 = OpLabel
 OpLine %3 7353 20
-OpStore %469 %528
-OpBranch %534
-%539 = OpLabel
-OpBranch %534
-%534 = OpLabel
+OpStore %484 %543
+OpBranch %549
+%554 = OpLabel
+OpBranch %549
+%549 = OpLabel
 OpLine %3 7356 13
-%540 = OpCompositeExtract  %8  %454 0
+%555 = OpCompositeExtract  %8  %469 0
 OpLine %3 7356 5
-OpStore %469 %540
+OpStore %484 %555
 OpLine %3 7365 27
-%541 = OpLoad  %4  %468
-%542 = OpBitcast  %8  %541
+%556 = OpLoad  %4  %483
+%557 = OpBitcast  %8  %556
 OpLine %3 7366 12
-%543 = OpLoad  %8  %469
-%544 = OpCompositeConstruct  %22  %542 %543
-%545 = OpCompositeExtract  %8  %544 0
-OpStore %463 %545
-%546 = OpCompositeExtract  %8  %544 1
-OpStore %464 %546
+%558 = OpLoad  %8  %484
+%559 = OpCompositeConstruct  %22  %557 %558
+%560 = OpCompositeExtract  %8  %559 0
+OpStore %478 %560
+%561 = OpCompositeExtract  %8  %559 1
+OpStore %479 %561
 OpReturn
 OpFunctionEnd
-%558 = OpFunction  %2  None %346
-%547 = OpLabel
-%551 = OpLoad  %5  %549
-%553 = OpLoad  %5  %552
-%548 = OpCompositeConstruct  %14  %551 %553
-%560 = OpAccessChain  %559  %39 %135
-OpBranch %561
-%561 = OpLabel
+%573 = OpFunction  %2  None %354
+%562 = OpLabel
+%566 = OpLoad  %5  %564
+%568 = OpLoad  %5  %567
+%563 = OpCompositeConstruct  %14  %566 %568
+%575 = OpAccessChain  %574  %39 %135
+OpBranch %576
+%576 = OpLabel
 OpLine %3 7397 25
-%563 = OpAccessChain  %562  %560 %126
-%564 = OpLoad  %23  %563
-%565 = OpCompositeExtract  %5  %548 0
+%578 = OpAccessChain  %577  %575 %126
+%579 = OpLoad  %23  %578
+%580 = OpCompositeExtract  %5  %563 0
 OpLine %3 7397 25
-%566 = OpCompositeConstruct  %7  %565 %56
-%567 = OpMatrixTimesVector  %7  %564 %566
+%581 = OpCompositeConstruct  %7  %580 %56
+%582 = OpMatrixTimesVector  %7  %579 %581
 OpLine %3 7398 18
-%568 = OpCompositeExtract  %5  %548 1
+%583 = OpCompositeExtract  %5  %563 1
 OpLine %3 7399 12
-%569 = OpCompositeExtract  %5  %548 0
-%570 = OpCompositeConstruct  %26  %567 %568 %569
-%571 = OpCompositeExtract  %7  %570 0
-OpStore %554 %571
-%572 = OpCompositeExtract  %5  %570 1
-OpStore %555 %572
-%573 = OpCompositeExtract  %5  %570 2
-OpStore %557 %573
+%584 = OpCompositeExtract  %5  %563 0
+%585 = OpCompositeConstruct  %26  %582 %583 %584
+%586 = OpCompositeExtract  %7  %585 0
+OpStore %569 %586
+%587 = OpCompositeExtract  %5  %585 1
+OpStore %570 %587
+%588 = OpCompositeExtract  %5  %585 2
+OpStore %572 %588
 OpReturn
 OpFunctionEnd
-%583 = OpFunction  %2  None %346
-%574 = OpLabel
-%592 = OpVariable  %95  Function %593
-%577 = OpLoad  %7  %576
-%579 = OpLoad  %5  %578
-%581 = OpLoad  %5  %580
-%575 = OpCompositeConstruct  %26  %577 %579 %581
-%584 = OpAccessChain  %559  %39 %135
-%586 = OpAccessChain  %585  %42 %135
-OpBranch %594
-%594 = OpLabel
+%598 = OpFunction  %2  None %354
+%589 = OpLabel
+%607 = OpVariable  %95  Function %608
+%592 = OpLoad  %7  %591
+%594 = OpLoad  %5  %593
+%596 = OpLoad  %5  %595
+%590 = OpCompositeConstruct  %26  %592 %594 %596
+%599 = OpAccessChain  %574  %39 %135
+%601 = OpAccessChain  %600  %42 %135
+OpBranch %609
+%609 = OpLabel
 OpLine %3 7421 28
 OpLine %3 7421 17
-%595 = OpCompositeExtract  %5  %575 2
-%596 = OpExtInst  %5  %1 Fract %595
-%597 = OpExtInst  %5  %1 SmoothStep %80 %587 %596
+%610 = OpCompositeExtract  %5  %590 2
+%611 = OpExtInst  %5  %1 Fract %610
+%612 = OpExtInst  %5  %1 SmoothStep %80 %602 %611
 OpLine %3 7421 5
-OpStore %592 %597
+OpStore %607 %612
 OpLine %3 7422 17
 OpLine %3 7422 13
-%598 = OpAccessChain  %125  %592 %135
-%599 = OpLoad  %4  %598
-%600 = OpAccessChain  %125  %592 %126
-%601 = OpLoad  %4  %600
-%602 = OpFMul  %4  %599 %601
-%603 = OpAccessChain  %125  %592 %350
-%604 = OpLoad  %4  %603
-%605 = OpFMul  %4  %602 %604
-%606 = OpCompositeConstruct  %5  %605 %605 %605
-%607 = OpExtInst  %5  %1 FMix %589 %591 %606
+%613 = OpAccessChain  %125  %607 %135
+%614 = OpLoad  %4  %613
+%615 = OpAccessChain  %125  %607 %126
+%616 = OpLoad  %4  %615
+%617 = OpFMul  %4  %614 %616
+%618 = OpAccessChain  %125  %607 %358
+%619 = OpLoad  %4  %618
+%620 = OpFMul  %4  %617 %619
+%621 = OpCompositeConstruct  %5  %620 %620 %620
+%622 = OpExtInst  %5  %1 FMix %604 %606 %621
 OpLine %3 7422 5
-OpStore %592 %607
+OpStore %607 %622
 OpLine %3 7425 25
-%609 = OpAccessChain  %608  %586 %126
-%610 = OpLoad  %5  %609
-%611 = OpVectorTimesScalar  %5  %610 %272
+%624 = OpAccessChain  %623  %601 %126
+%625 = OpLoad  %5  %624
+%626 = OpVectorTimesScalar  %5  %625 %272
 OpLine %3 7427 21
-%612 = OpAccessChain  %608  %586 %135
-%613 = OpLoad  %5  %612
-%614 = OpCompositeExtract  %5  %575 2
-%615 = OpFSub  %5  %613 %614
-%616 = OpExtInst  %5  %1 Normalize %615
+%627 = OpAccessChain  %623  %601 %135
+%628 = OpLoad  %5  %627
+%629 = OpCompositeExtract  %5  %590 2
+%630 = OpFSub  %5  %628 %629
+%631 = OpExtInst  %5  %1 Normalize %630
 OpLine %3 7428 20
-%618 = OpAccessChain  %617  %584 %135
-%619 = OpLoad  %7  %618
-%620 = OpVectorShuffle  %5  %619 %619 0 1 2
-%621 = OpCompositeExtract  %5  %575 2
-%622 = OpFSub  %5  %620 %621
-%623 = OpExtInst  %5  %1 Normalize %622
+%633 = OpAccessChain  %632  %599 %135
+%634 = OpLoad  %7  %633
+%635 = OpVectorShuffle  %5  %634 %634 0 1 2
+%636 = OpCompositeExtract  %5  %590 2
+%637 = OpFSub  %5  %635 %636
+%638 = OpExtInst  %5  %1 Normalize %637
 OpLine %3 7429 20
-%624 = OpFAdd  %5  %623 %616
-%625 = OpExtInst  %5  %1 Normalize %624
+%639 = OpFAdd  %5  %638 %631
+%640 = OpExtInst  %5  %1 Normalize %639
 OpLine %3 7431 32
-%626 = OpCompositeExtract  %5  %575 1
-%627 = OpDot  %4  %626 %616
+%641 = OpCompositeExtract  %5  %590 1
+%642 = OpDot  %4  %641 %631
 OpLine %3 7431 28
-%628 = OpExtInst  %4  %1 FMax %627 %74
+%643 = OpExtInst  %4  %1 FMax %642 %74
 OpLine %3 7432 25
-%629 = OpAccessChain  %608  %586 %126
-%630 = OpLoad  %5  %629
-%631 = OpVectorTimesScalar  %5  %630 %628
+%644 = OpAccessChain  %623  %601 %126
+%645 = OpLoad  %5  %644
+%646 = OpVectorTimesScalar  %5  %645 %643
 OpLine %3 7434 37
-%632 = OpCompositeExtract  %5  %575 1
-%633 = OpDot  %4  %632 %625
+%647 = OpCompositeExtract  %5  %590 1
+%648 = OpDot  %4  %647 %640
 OpLine %3 7434 33
-%634 = OpExtInst  %4  %1 FMax %633 %74
+%649 = OpExtInst  %4  %1 FMax %648 %74
 OpLine %3 7434 29
-%635 = OpExtInst  %4  %1 Pow %634 %323
+%650 = OpExtInst  %4  %1 Pow %649 %331
 OpLine %3 7435 26
-%636 = OpAccessChain  %608  %586 %126
-%637 = OpLoad  %5  %636
-%638 = OpVectorTimesScalar  %5  %637 %635
+%651 = OpAccessChain  %623  %601 %126
+%652 = OpLoad  %5  %651
+%653 = OpVectorTimesScalar  %5  %652 %650
 OpLine %3 7437 18
-%639 = OpFAdd  %5  %611 %631
-%640 = OpFAdd  %5  %639 %638
-%641 = OpLoad  %5  %592
-%642 = OpFMul  %5  %640 %641
+%654 = OpFAdd  %5  %626 %646
+%655 = OpFAdd  %5  %654 %653
+%656 = OpLoad  %5  %607
+%657 = OpFMul  %5  %655 %656
 OpLine %3 7439 12
-%643 = OpCompositeConstruct  %7  %642 %56
-OpStore %582 %643
+%658 = OpCompositeConstruct  %7  %657 %56
+OpStore %597 %658
 OpReturn
 OpFunctionEnd
\ No newline at end of file
diff --git a/naga/tests/out/spv/debug-symbol-terrain.spvasm b/naga/tests/out/spv/debug-symbol-terrain.spvasm
index 38d8984d0f..e8d7d141e7 100644
--- a/naga/tests/out/spv/debug-symbol-terrain.spvasm
+++ b/naga/tests/out/spv/debug-symbol-terrain.spvasm
@@ -1,19 +1,19 @@
 ; SPIR-V
 ; Version: 1.1
 ; Generator: rspirv
-; Bound: 644
+; Bound: 659
 OpCapability Shader
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %345 "gen_terrain_compute" %342
-OpEntryPoint Vertex %415 "gen_terrain_vertex" %406 %409 %411 %413
-OpEntryPoint Fragment %465 "gen_terrain_fragment" %455 %457 %460 %463 %464
-OpEntryPoint Vertex %558 "vs_main" %549 %552 %554 %555 %557
-OpEntryPoint Fragment %583 "fs_main" %576 %578 %580 %582
-OpExecutionMode %345 LocalSize 64 1 1
-OpExecutionMode %465 OriginUpperLeft
-OpExecutionMode %583 OriginUpperLeft
+OpEntryPoint GLCompute %353 "gen_terrain_compute" %350
+OpEntryPoint Vertex %430 "gen_terrain_vertex" %421 %424 %426 %428
+OpEntryPoint Fragment %480 "gen_terrain_fragment" %470 %472 %475 %478 %479
+OpEntryPoint Vertex %573 "vs_main" %564 %567 %569 %570 %572
+OpEntryPoint Fragment %598 "fs_main" %591 %593 %595 %597
+OpExecutionMode %353 LocalSize 64 1 1
+OpExecutionMode %480 OriginUpperLeft
+OpExecutionMode %598 OriginUpperLeft
 %3 = OpString "debug-symbol-terrain.wgsl"
 OpSource Unknown 0 %3 "// Taken from https://github.com/sotrh/learn-wgpu/blob/11820796f5e1dbce42fb1119f04ddeb4b167d2a0/code/intermediate/tutorial13-terrain/src/terrain.wgsl
 // ============================
@@ -376,38 +376,44 @@ OpName %257 "terrain_point"
 OpName %268 "p"
 OpName %269 "min_max_height"
 OpName %270 "terrain_vertex"
-OpName %300 "vert_index"
-OpName %301 "chunk_size"
-OpName %302 "chunk_corner"
-OpName %303 "index_to_p"
-OpName %319 "p"
-OpName %320 "color23"
-OpName %342 "gid"
-OpName %345 "gen_terrain_compute"
-OpName %406 "vindex"
-OpName %409 "index"
-OpName %411 "position"
-OpName %413 "uv"
-OpName %415 "gen_terrain_vertex"
-OpName %455 "index"
-OpName %457 "position"
-OpName %460 "uv"
-OpName %463 "vert_component"
-OpName %464 "index"
-OpName %465 "gen_terrain_fragment"
-OpName %468 "vert_component"
-OpName %469 "index"
-OpName %549 "position"
-OpName %552 "normal"
-OpName %554 "clip_position"
-OpName %555 "normal"
-OpName %557 "world_pos"
-OpName %558 "vs_main"
-OpName %576 "clip_position"
-OpName %578 "normal"
-OpName %580 "world_pos"
-OpName %583 "fs_main"
-OpName %592 "color"
+OpName %299 "naga_div"
+OpName %301 "lhs"
+OpName %302 "rhs"
+OpName %308 "vert_index"
+OpName %309 "chunk_size"
+OpName %310 "chunk_corner"
+OpName %311 "index_to_p"
+OpName %327 "p"
+OpName %328 "color23"
+OpName %350 "gid"
+OpName %353 "gen_terrain_compute"
+OpName %413 "naga_mod"
+OpName %414 "lhs"
+OpName %415 "rhs"
+OpName %421 "vindex"
+OpName %424 "index"
+OpName %426 "position"
+OpName %428 "uv"
+OpName %430 "gen_terrain_vertex"
+OpName %470 "index"
+OpName %472 "position"
+OpName %475 "uv"
+OpName %478 "vert_component"
+OpName %479 "index"
+OpName %480 "gen_terrain_fragment"
+OpName %483 "vert_component"
+OpName %484 "index"
+OpName %564 "position"
+OpName %567 "normal"
+OpName %569 "clip_position"
+OpName %570 "normal"
+OpName %572 "world_pos"
+OpName %573 "vs_main"
+OpName %591 "clip_position"
+OpName %593 "normal"
+OpName %595 "world_pos"
+OpName %598 "fs_main"
+OpName %607 "color"
 OpMemberDecorate %13 0 Offset 0
 OpMemberDecorate %13 1 Offset 8
 OpMemberDecorate %13 2 Offset 16
@@ -466,27 +472,27 @@ OpDecorate %49 DescriptorSet 2
 OpDecorate %49 Binding 2
 OpDecorate %50 DescriptorSet 2
 OpDecorate %50 Binding 3
-OpDecorate %342 BuiltIn GlobalInvocationId
-OpDecorate %406 BuiltIn VertexIndex
-OpDecorate %409 Location 0
-OpDecorate %409 Flat
-OpDecorate %411 BuiltIn Position
-OpDecorate %413 Location 1
-OpDecorate %455 Location 0
-OpDecorate %455 Flat
-OpDecorate %457 BuiltIn FragCoord
-OpDecorate %460 Location 1
-OpDecorate %463 Location 0
-OpDecorate %464 Location 1
-OpDecorate %549 Location 0
-OpDecorate %552 Location 1
-OpDecorate %554 BuiltIn Position
-OpDecorate %555 Location 0
-OpDecorate %557 Location 1
-OpDecorate %576 BuiltIn FragCoord
-OpDecorate %578 Location 0
-OpDecorate %580 Location 1
-OpDecorate %582 Location 0
+OpDecorate %350 BuiltIn GlobalInvocationId
+OpDecorate %421 BuiltIn VertexIndex
+OpDecorate %424 Location 0
+OpDecorate %424 Flat
+OpDecorate %426 BuiltIn Position
+OpDecorate %428 Location 1
+OpDecorate %470 Location 0
+OpDecorate %470 Flat
+OpDecorate %472 BuiltIn FragCoord
+OpDecorate %475 Location 1
+OpDecorate %478 Location 0
+OpDecorate %479 Location 1
+OpDecorate %564 Location 0
+OpDecorate %567 Location 1
+OpDecorate %569 BuiltIn Position
+OpDecorate %570 Location 0
+OpDecorate %572 Location 1
+OpDecorate %591 BuiltIn FragCoord
+OpDecorate %593 Location 0
+OpDecorate %595 Location 1
+OpDecorate %597 Location 0
 %2 = OpTypeVoid
 %4 = OpTypeFloat 32
 %5 = OpTypeVector %4 3
@@ -589,72 +595,73 @@ OpDecorate %582 Location 0
 %275 = OpConstant  %4  -0.1
 %276 = OpConstantComposite  %6  %275 %74
 %277 = OpConstantComposite  %6  %74 %275
-%304 = OpTypeFunction %6 %8 %10 %11
-%321 = OpTypeFunction %5 %6
-%322 = OpConstant  %4  23.0
-%323 = OpConstant  %4  32.0
-%324 = OpConstantComposite  %6  %322 %323
-%325 = OpConstant  %4  -43.0
-%326 = OpConstant  %4  3.0
-%327 = OpConstantComposite  %6  %325 %326
-%343 = OpTypePointer Input %19
-%342 = OpVariable  %343  Input
-%346 = OpTypeFunction %2
-%347 = OpTypePointer Uniform %13
-%349 = OpConstant  %8  6
-%350 = OpConstant  %8  2
-%351 = OpConstant  %8  3
-%352 = OpConstant  %8  4
-%355 = OpTypePointer Uniform %10
-%358 = OpTypePointer Uniform %11
-%362 = OpTypePointer StorageBuffer %15
-%363 = OpTypePointer StorageBuffer %14
-%364 = OpTypePointer Uniform %6
-%371 = OpTypePointer Uniform %8
-%392 = OpTypePointer StorageBuffer %17
-%393 = OpTypePointer StorageBuffer %8
-%407 = OpTypePointer Input %8
-%406 = OpVariable  %407  Input
-%410 = OpTypePointer Output %8
-%409 = OpVariable  %410  Output
-%412 = OpTypePointer Output %7
-%411 = OpVariable  %412  Output
-%414 = OpTypePointer Output %6
-%413 = OpVariable  %414  Output
-%416 = OpTypePointer Uniform %20
-%418 = OpConstant  %4  -1.0
-%419 = OpConstantComposite  %6  %418 %418
-%434 = OpTypePointer Uniform %8
-%455 = OpVariable  %407  Input
-%458 = OpTypePointer Input %7
-%457 = OpVariable  %458  Input
-%461 = OpTypePointer Input %6
-%460 = OpVariable  %461  Input
-%463 = OpVariable  %410  Output
-%464 = OpVariable  %410  Output
-%467 = OpConstant  %4  6.0
-%550 = OpTypePointer Input %5
-%549 = OpVariable  %550  Input
-%552 = OpVariable  %550  Input
-%554 = OpVariable  %412  Output
-%556 = OpTypePointer Output %5
-%555 = OpVariable  %556  Output
-%557 = OpVariable  %556  Output
-%559 = OpTypePointer Uniform %24
-%562 = OpTypePointer Uniform %23
-%576 = OpVariable  %458  Input
-%578 = OpVariable  %550  Input
-%580 = OpVariable  %550  Input
-%582 = OpVariable  %412  Output
-%585 = OpTypePointer Uniform %25
-%587 = OpConstantComposite  %5  %272 %272 %272
-%588 = OpConstant  %4  0.7
-%589 = OpConstantComposite  %5  %78 %272 %588
-%590 = OpConstant  %4  0.2
-%591 = OpConstantComposite  %5  %590 %590 %590
-%593 = OpConstantNull  %5
-%608 = OpTypePointer Uniform %5
-%617 = OpTypePointer Uniform %7
+%300 = OpTypeFunction %8 %8 %8
+%312 = OpTypeFunction %6 %8 %10 %11
+%329 = OpTypeFunction %5 %6
+%330 = OpConstant  %4  23.0
+%331 = OpConstant  %4  32.0
+%332 = OpConstantComposite  %6  %330 %331
+%333 = OpConstant  %4  -43.0
+%334 = OpConstant  %4  3.0
+%335 = OpConstantComposite  %6  %333 %334
+%351 = OpTypePointer Input %19
+%350 = OpVariable  %351  Input
+%354 = OpTypeFunction %2
+%355 = OpTypePointer Uniform %13
+%357 = OpConstant  %8  6
+%358 = OpConstant  %8  2
+%359 = OpConstant  %8  3
+%360 = OpConstant  %8  4
+%363 = OpTypePointer Uniform %10
+%366 = OpTypePointer Uniform %11
+%370 = OpTypePointer StorageBuffer %15
+%371 = OpTypePointer StorageBuffer %14
+%372 = OpTypePointer Uniform %6
+%379 = OpTypePointer Uniform %8
+%400 = OpTypePointer StorageBuffer %17
+%401 = OpTypePointer StorageBuffer %8
+%422 = OpTypePointer Input %8
+%421 = OpVariable  %422  Input
+%425 = OpTypePointer Output %8
+%424 = OpVariable  %425  Output
+%427 = OpTypePointer Output %7
+%426 = OpVariable  %427  Output
+%429 = OpTypePointer Output %6
+%428 = OpVariable  %429  Output
+%431 = OpTypePointer Uniform %20
+%433 = OpConstant  %4  -1.0
+%434 = OpConstantComposite  %6  %433 %433
+%449 = OpTypePointer Uniform %8
+%470 = OpVariable  %422  Input
+%473 = OpTypePointer Input %7
+%472 = OpVariable  %473  Input
+%476 = OpTypePointer Input %6
+%475 = OpVariable  %476  Input
+%478 = OpVariable  %425  Output
+%479 = OpVariable  %425  Output
+%482 = OpConstant  %4  6.0
+%565 = OpTypePointer Input %5
+%564 = OpVariable  %565  Input
+%567 = OpVariable  %565  Input
+%569 = OpVariable  %427  Output
+%571 = OpTypePointer Output %5
+%570 = OpVariable  %571  Output
+%572 = OpVariable  %571  Output
+%574 = OpTypePointer Uniform %24
+%577 = OpTypePointer Uniform %23
+%591 = OpVariable  %473  Input
+%593 = OpVariable  %565  Input
+%595 = OpVariable  %565  Input
+%597 = OpVariable  %427  Output
+%600 = OpTypePointer Uniform %25
+%602 = OpConstantComposite  %5  %272 %272 %272
+%603 = OpConstant  %4  0.7
+%604 = OpConstantComposite  %5  %78 %272 %603
+%605 = OpConstant  %4  0.2
+%606 = OpConstantComposite  %5  %605 %605 %605
+%608 = OpConstantNull  %5
+%623 = OpTypePointer Uniform %5
+%632 = OpTypePointer Uniform %7
 %53 = OpFunction  %5  None %54
 %52 = OpFunctionParameter  %5
 %51 = OpLabel
@@ -981,480 +988,498 @@ OpLine %3 96 12
 %298 = OpCompositeConstruct  %14  %279 %297
 OpReturnValue %298
 OpFunctionEnd
-%303 = OpFunction  %6  None %304
-%300 = OpFunctionParameter  %8
-%301 = OpFunctionParameter  %10
-%302 = OpFunctionParameter  %11
-%299 = OpLabel
-OpBranch %305
-%305 = OpLabel
+%299 = OpFunction  %8  None %300
+%301 = OpFunctionParameter  %8
+%302 = OpFunctionParameter  %8
+%303 = OpLabel
+%304 = OpIEqual  %112  %302 %135
+%305 = OpSelect  %8  %304 %126 %302
+%306 = OpUDiv  %8  %301 %305
+OpReturnValue %306
+OpFunctionEnd
+%311 = OpFunction  %6  None %312
+%308 = OpFunctionParameter  %8
+%309 = OpFunctionParameter  %10
+%310 = OpFunctionParameter  %11
+%307 = OpLabel
+OpBranch %313
+%313 = OpLabel
 OpLine %3 101 9
-%306 = OpConvertUToF  %4  %300
-%307 = OpCompositeExtract  %8  %301 0
+%314 = OpConvertUToF  %4  %308
+%315 = OpCompositeExtract  %8  %309 0
 OpLine %3 101 9
-%308 = OpIAdd  %8  %307 %126
-%309 = OpConvertUToF  %4  %308
-%310 = OpFRem  %4  %306 %309
-%311 = OpCompositeExtract  %8  %301 0
+%316 = OpIAdd  %8  %315 %126
+%317 = OpConvertUToF  %4  %316
+%318 = OpFRem  %4  %314 %317
+%319 = OpCompositeExtract  %8  %309 0
 OpLine %3 100 12
-%312 = OpIAdd  %8  %311 %126
-%313 = OpUDiv  %8  %300 %312
-%314 = OpConvertUToF  %4  %313
-%315 = OpCompositeConstruct  %6  %310 %314
-%316 = OpConvertSToF  %6  %302
-%317 = OpFAdd  %6  %315 %316
-OpReturnValue %317
+%320 = OpIAdd  %8  %319 %126
+%321 = OpFunctionCall  %8  %299 %308 %320
+%322 = OpConvertUToF  %4  %321
+%323 = OpCompositeConstruct  %6  %318 %322
+%324 = OpConvertSToF  %6  %310
+%325 = OpFAdd  %6  %323 %324
+OpReturnValue %325
 OpFunctionEnd
-%320 = OpFunction  %5  None %321
-%319 = OpFunctionParameter  %6
-%318 = OpLabel
-OpBranch %328
-%328 = OpLabel
+%328 = OpFunction  %5  None %329
+%327 = OpFunctionParameter  %6
+%326 = OpLabel
+OpBranch %336
+%336 = OpLabel
 OpLine %3 270 9
-%329 = OpFunctionCall  %4  %67 %319
+%337 = OpFunctionCall  %4  %67 %327
 OpLine %3 270 9
-%330 = OpFMul  %4  %329 %78
+%338 = OpFMul  %4  %337 %78
 OpLine %3 270 9
-%331 = OpFAdd  %4  %330 %78
+%339 = OpFAdd  %4  %338 %78
 OpLine %3 271 17
-%332 = OpFAdd  %6  %319 %324
+%340 = OpFAdd  %6  %327 %332
 OpLine %3 271 9
-%333 = OpFunctionCall  %4  %67 %332
+%341 = OpFunctionCall  %4  %67 %340
 OpLine %3 271 9
-%334 = OpFMul  %4  %333 %78
+%342 = OpFMul  %4  %341 %78
 OpLine %3 271 9
-%335 = OpFAdd  %4  %334 %78
+%343 = OpFAdd  %4  %342 %78
 OpLine %3 272 17
-%336 = OpFAdd  %6  %319 %327
+%344 = OpFAdd  %6  %327 %335
 OpLine %3 272 9
-%337 = OpFunctionCall  %4  %67 %336
+%345 = OpFunctionCall  %4  %67 %344
 OpLine %3 272 9
-%338 = OpFMul  %4  %337 %78
+%346 = OpFMul  %4  %345 %78
 OpLine %3 269 12
-%339 = OpFAdd  %4  %338 %78
-%340 = OpCompositeConstruct  %5  %331 %335 %339
-OpReturnValue %340
+%347 = OpFAdd  %4  %346 %78
+%348 = OpCompositeConstruct  %5  %339 %343 %347
+OpReturnValue %348
 OpFunctionEnd
-%345 = OpFunction  %2  None %346
-%341 = OpLabel
-%344 = OpLoad  %19  %342
-%348 = OpAccessChain  %347  %29 %135
-OpBranch %353
-%353 = OpLabel
+%353 = OpFunction  %2  None %354
+%349 = OpLabel
+%352 = OpLoad  %19  %350
+%356 = OpAccessChain  %355  %29 %135
+OpBranch %361
+%361 = OpLabel
 OpLine %3 111 22
-%354 = OpCompositeExtract  %8  %344 0
+%362 = OpCompositeExtract  %8  %352 0
 OpLine %3 113 36
-%356 = OpAccessChain  %355  %348 %135
-%357 = OpLoad  %10  %356
+%364 = OpAccessChain  %363  %356 %135
+%365 = OpLoad  %10  %364
 OpLine %3 113 59
-%359 = OpAccessChain  %358  %348 %126
-%360 = OpLoad  %11  %359
+%367 = OpAccessChain  %366  %356 %126
+%368 = OpLoad  %11  %367
 OpLine %3 113 13
-%361 = OpFunctionCall  %6  %303 %354 %357 %360
+%369 = OpFunctionCall  %6  %311 %362 %365 %368
 OpLine %3 115 5
 OpLine %3 115 51
-%365 = OpAccessChain  %364  %348 %350
-%366 = OpLoad  %6  %365
+%373 = OpAccessChain  %372  %356 %358
+%374 = OpLoad  %6  %373
 OpLine %3 115 33
-%367 = OpFunctionCall  %14  %270 %361 %366
+%375 = OpFunctionCall  %14  %270 %369 %374
 OpLine %3 115 5
-%368 = OpAccessChain  %363  %32 %135 %354
-OpStore %368 %367
+%376 = OpAccessChain  %371  %32 %135 %362
+OpStore %376 %375
 OpLine %3 118 23
-%369 = OpCompositeExtract  %8  %344 0
+%377 = OpCompositeExtract  %8  %352 0
 OpLine %3 118 23
-%370 = OpIMul  %8  %369 %349
+%378 = OpIMul  %8  %377 %357
 OpLine %3 120 25
-%372 = OpAccessChain  %371  %348 %135 %135
-%373 = OpLoad  %8  %372
+%380 = OpAccessChain  %379  %356 %135 %135
+%381 = OpLoad  %8  %380
 OpLine %3 120 25
-%374 = OpAccessChain  %371  %348 %135 %126
-%375 = OpLoad  %8  %374
-%376 = OpIMul  %8  %373 %375
+%382 = OpAccessChain  %379  %356 %135 %126
+%383 = OpLoad  %8  %382
+%384 = OpIMul  %8  %381 %383
 OpLine %3 120 9
-%377 = OpIMul  %8  %376 %349
-%378 = OpUGreaterThanEqual  %112  %370 %377
+%385 = OpIMul  %8  %384 %357
+%386 = OpUGreaterThanEqual  %112  %378 %385
 OpLine %3 120 5
-OpSelectionMerge %379 None
-OpBranchConditional %378 %380 %379
-%380 = OpLabel
+OpSelectionMerge %387 None
+OpBranchConditional %386 %388 %387
+%388 = OpLabel
 OpReturn
-%379 = OpLabel
+%387 = OpLabel
 OpLine %3 122 28
-%381 = OpCompositeExtract  %8  %344 0
+%389 = OpCompositeExtract  %8  %352 0
 OpLine %3 122 15
-%382 = OpAccessChain  %371  %348 %135 %135
-%383 = OpLoad  %8  %382
-%384 = OpUDiv  %8  %381 %383
-%385 = OpIAdd  %8  %354 %384
+%390 = OpAccessChain  %379  %356 %135 %135
+%391 = OpLoad  %8  %390
+%392 = OpFunctionCall  %8  %299 %389 %391
+%393 = OpIAdd  %8  %362 %392
 OpLine %3 123 15
-%386 = OpIAdd  %8  %385 %126
+%394 = OpIAdd  %8  %393 %126
 OpLine %3 124 15
-%387 = OpAccessChain  %371  %348 %135 %135
-%388 = OpLoad  %8  %387
-%389 = OpIAdd  %8  %385 %388
+%395 = OpAccessChain  %379  %356 %135 %135
+%396 = OpLoad  %8  %395
+%397 = OpIAdd  %8  %393 %396
 OpLine %3 124 15
-%390 = OpIAdd  %8  %389 %126
+%398 = OpIAdd  %8  %397 %126
 OpLine %3 125 15
-%391 = OpIAdd  %8  %390 %126
+%399 = OpIAdd  %8  %398 %126
 OpLine %3 127 5
 OpLine %3 127 5
-%394 = OpAccessChain  %393  %34 %135 %370
-OpStore %394 %385
+%402 = OpAccessChain  %401  %34 %135 %378
+OpStore %402 %393
 OpLine %3 128 5
 OpLine %3 128 5
-%395 = OpIAdd  %8  %370 %126
+%403 = OpIAdd  %8  %378 %126
 OpLine %3 128 5
-%396 = OpAccessChain  %393  %34 %135 %395
-OpStore %396 %390
+%404 = OpAccessChain  %401  %34 %135 %403
+OpStore %404 %398
 OpLine %3 129 5
 OpLine %3 129 5
-%397 = OpIAdd  %8  %370 %350
+%405 = OpIAdd  %8  %378 %358
 OpLine %3 129 5
-%398 = OpAccessChain  %393  %34 %135 %397
-OpStore %398 %391
+%406 = OpAccessChain  %401  %34 %135 %405
+OpStore %406 %399
 OpLine %3 130 5
 OpLine %3 130 5
-%399 = OpIAdd  %8  %370 %351
+%407 = OpIAdd  %8  %378 %359
 OpLine %3 130 5
-%400 = OpAccessChain  %393  %34 %135 %399
-OpStore %400 %385
+%408 = OpAccessChain  %401  %34 %135 %407
+OpStore %408 %393
 OpLine %3 131 5
 OpLine %3 131 5
-%401 = OpIAdd  %8  %370 %352
+%409 = OpIAdd  %8  %378 %360
 OpLine %3 131 5
-%402 = OpAccessChain  %393  %34 %135 %401
-OpStore %402 %391
+%410 = OpAccessChain  %401  %34 %135 %409
+OpStore %410 %399
 OpLine %3 132 5
 OpLine %3 132 5
-%403 = OpIAdd  %8  %370 %205
+%411 = OpIAdd  %8  %378 %205
 OpLine %3 132 5
-%404 = OpAccessChain  %393  %34 %135 %403
-OpStore %404 %386
+%412 = OpAccessChain  %401  %34 %135 %411
+OpStore %412 %394
 OpReturn
 OpFunctionEnd
-%415 = OpFunction  %2  None %346
-%405 = OpLabel
-%408 = OpLoad  %8  %406
-%417 = OpAccessChain  %416  %36 %135
-OpBranch %420
+%413 = OpFunction  %8  None %300
+%414 = OpFunctionParameter  %8
+%415 = OpFunctionParameter  %8
+%416 = OpLabel
+%417 = OpIEqual  %112  %415 %135
+%418 = OpSelect  %8  %417 %126 %415
+%419 = OpUMod  %8  %414 %418
+OpReturnValue %419
+OpFunctionEnd
+%430 = OpFunction  %2  None %354
 %420 = OpLabel
+%423 = OpLoad  %8  %421
+%432 = OpAccessChain  %431  %36 %135
+OpBranch %435
+%435 = OpLabel
 OpLine %3 161 19
-%421 = OpIAdd  %8  %408 %350
+%436 = OpIAdd  %8  %423 %358
 OpLine %3 161 18
-%422 = OpUDiv  %8  %421 %351
+%437 = OpFunctionCall  %8  %299 %436 %359
 OpLine %3 161 13
-%423 = OpUMod  %8  %422 %350
-%424 = OpConvertUToF  %4  %423
+%438 = OpFunctionCall  %8  %413 %437 %358
+%439 = OpConvertUToF  %4  %438
 OpLine %3 162 19
-%425 = OpIAdd  %8  %408 %126
+%440 = OpIAdd  %8  %423 %126
 OpLine %3 162 18
-%426 = OpUDiv  %8  %425 %351
+%441 = OpFunctionCall  %8  %299 %440 %359
 OpLine %3 162 13
-%427 = OpUMod  %8  %426 %350
-%428 = OpConvertUToF  %4  %427
+%442 = OpFunctionCall  %8  %413 %441 %358
+%443 = OpConvertUToF  %4  %442
 OpLine %3 163 14
-%429 = OpCompositeConstruct  %6  %424 %428
+%444 = OpCompositeConstruct  %6  %439 %443
 OpLine %3 165 30
-%430 = OpVectorTimesScalar  %6  %429 %81
+%445 = OpVectorTimesScalar  %6  %444 %81
 OpLine %3 165 30
-%431 = OpFAdd  %6  %419 %430
+%446 = OpFAdd  %6  %434 %445
 OpLine %3 165 20
-%432 = OpCompositeConstruct  %7  %431 %74 %56
+%447 = OpCompositeConstruct  %7  %446 %74 %56
 OpLine %3 168 21
-%433 = OpCompositeExtract  %4  %429 0
+%448 = OpCompositeExtract  %4  %444 0
 OpLine %3 168 21
-%435 = OpAccessChain  %434  %417 %351
-%436 = OpLoad  %8  %435
-%437 = OpConvertUToF  %4  %436
-%438 = OpFMul  %4  %433 %437
-%439 = OpCompositeExtract  %4  %429 1
+%450 = OpAccessChain  %449  %432 %359
+%451 = OpLoad  %8  %450
+%452 = OpConvertUToF  %4  %451
+%453 = OpFMul  %4  %448 %452
+%454 = OpCompositeExtract  %4  %444 1
 OpLine %3 168 17
-%440 = OpAccessChain  %434  %417 %351
-%441 = OpLoad  %8  %440
-%442 = OpConvertUToF  %4  %441
-%443 = OpFMul  %4  %439 %442
-%444 = OpFAdd  %4  %438 %443
-%445 = OpConvertFToU  %8  %444
+%455 = OpAccessChain  %449  %432 %359
+%456 = OpLoad  %8  %455
+%457 = OpConvertUToF  %4  %456
+%458 = OpFMul  %4  %454 %457
+%459 = OpFAdd  %4  %453 %458
+%460 = OpConvertFToU  %8  %459
 OpLine %3 168 17
-%446 = OpAccessChain  %434  %417 %352
-%447 = OpLoad  %8  %446
-%448 = OpIAdd  %8  %445 %447
+%461 = OpAccessChain  %449  %432 %360
+%462 = OpLoad  %8  %461
+%463 = OpIAdd  %8  %460 %462
 OpLine %3 170 12
-%449 = OpCompositeConstruct  %21  %448 %432 %429
-%450 = OpCompositeExtract  %8  %449 0
-OpStore %409 %450
-%451 = OpCompositeExtract  %7  %449 1
-OpStore %411 %451
-%452 = OpCompositeExtract  %6  %449 2
-OpStore %413 %452
+%464 = OpCompositeConstruct  %21  %463 %447 %444
+%465 = OpCompositeExtract  %8  %464 0
+OpStore %424 %465
+%466 = OpCompositeExtract  %7  %464 1
+OpStore %426 %466
+%467 = OpCompositeExtract  %6  %464 2
+OpStore %428 %467
 OpReturn
 OpFunctionEnd
-%465 = OpFunction  %2  None %346
-%453 = OpLabel
-%468 = OpVariable  %215  Function %74
-%469 = OpVariable  %218  Function %135
-%456 = OpLoad  %8  %455
-%459 = OpLoad  %7  %457
-%462 = OpLoad  %6  %460
-%454 = OpCompositeConstruct  %21  %456 %459 %462
-%466 = OpAccessChain  %416  %36 %135
-OpBranch %470
-%470 = OpLabel
+%480 = OpFunction  %2  None %354
+%468 = OpLabel
+%483 = OpVariable  %215  Function %74
+%484 = OpVariable  %218  Function %135
+%471 = OpLoad  %8  %470
+%474 = OpLoad  %7  %472
+%477 = OpLoad  %6  %475
+%469 = OpCompositeConstruct  %21  %471 %474 %477
+%481 = OpAccessChain  %431  %36 %135
+OpBranch %485
+%485 = OpLabel
 OpLine %3 181 17
-%471 = OpCompositeExtract  %6  %454 2
-%472 = OpCompositeExtract  %4  %471 0
+%486 = OpCompositeExtract  %6  %469 2
+%487 = OpCompositeExtract  %4  %486 0
 OpLine %3 181 17
-%473 = OpAccessChain  %434  %466 %351
-%474 = OpLoad  %8  %473
-%475 = OpConvertUToF  %4  %474
-%476 = OpFMul  %4  %472 %475
-%477 = OpCompositeExtract  %6  %454 2
-%478 = OpCompositeExtract  %4  %477 1
+%488 = OpAccessChain  %449  %481 %359
+%489 = OpLoad  %8  %488
+%490 = OpConvertUToF  %4  %489
+%491 = OpFMul  %4  %487 %490
+%492 = OpCompositeExtract  %6  %469 2
+%493 = OpCompositeExtract  %4  %492 1
 OpLine %3 181 70
-%479 = OpAccessChain  %434  %466 %351
-%480 = OpLoad  %8  %479
+%494 = OpAccessChain  %449  %481 %359
+%495 = OpLoad  %8  %494
 OpLine %3 181 13
-%481 = OpAccessChain  %434  %466 %351
-%482 = OpLoad  %8  %481
-%483 = OpIMul  %8  %480 %482
-%484 = OpConvertUToF  %4  %483
-%485 = OpFMul  %4  %478 %484
-%486 = OpFAdd  %4  %476 %485
-%487 = OpConvertFToU  %8  %486
+%496 = OpAccessChain  %449  %481 %359
+%497 = OpLoad  %8  %496
+%498 = OpIMul  %8  %495 %497
+%499 = OpConvertUToF  %4  %498
+%500 = OpFMul  %4  %493 %499
+%501 = OpFAdd  %4  %491 %500
+%502 = OpConvertFToU  %8  %501
 OpLine %3 181 13
-%488 = OpAccessChain  %434  %466 %352
-%489 = OpLoad  %8  %488
-%490 = OpIAdd  %8  %487 %489
+%503 = OpAccessChain  %449  %481 %360
+%504 = OpLoad  %8  %503
+%505 = OpIAdd  %8  %502 %504
 OpLine %3 182 32
-%491 = OpConvertUToF  %4  %490
+%506 = OpConvertUToF  %4  %505
 OpLine %3 182 22
-%492 = OpFDiv  %4  %491 %467
-%493 = OpExtInst  %4  %1 Floor %492
-%494 = OpConvertFToU  %8  %493
+%507 = OpFDiv  %4  %506 %482
+%508 = OpExtInst  %4  %1 Floor %507
+%509 = OpConvertFToU  %8  %508
 OpLine %3 183 22
-%495 = OpUMod  %8  %490 %349
+%510 = OpFunctionCall  %8  %413 %505 %357
 OpLine %3 185 36
-%496 = OpAccessChain  %355  %466 %135
-%497 = OpLoad  %10  %496
+%511 = OpAccessChain  %363  %481 %135
+%512 = OpLoad  %10  %511
 OpLine %3 185 57
-%498 = OpAccessChain  %358  %466 %126
-%499 = OpLoad  %11  %498
+%513 = OpAccessChain  %366  %481 %126
+%514 = OpLoad  %11  %513
 OpLine %3 185 13
-%500 = OpFunctionCall  %6  %303 %494 %497 %499
+%515 = OpFunctionCall  %6  %311 %509 %512 %514
 OpLine %3 186 31
-%501 = OpAccessChain  %364  %466 %350
-%502 = OpLoad  %6  %501
+%516 = OpAccessChain  %372  %481 %358
+%517 = OpLoad  %6  %516
 OpLine %3 186 13
-%503 = OpFunctionCall  %14  %270 %500 %502
+%518 = OpFunctionCall  %14  %270 %515 %517
 OpLine %3 190 5
-OpSelectionMerge %504 None
-OpSwitch %495 %511 0 %505 1 %506 2 %507 3 %508 4 %509 5 %510
-%505 = OpLabel
+OpSelectionMerge %519 None
+OpSwitch %510 %526 0 %520 1 %521 2 %522 3 %523 4 %524 5 %525
+%520 = OpLabel
 OpLine %3 191 37
-%512 = OpCompositeExtract  %5  %503 0
-%513 = OpCompositeExtract  %4  %512 0
+%527 = OpCompositeExtract  %5  %518 0
+%528 = OpCompositeExtract  %4  %527 0
 OpLine %3 191 20
-OpStore %468 %513
-OpBranch %504
-%506 = OpLabel
+OpStore %483 %528
+OpBranch %519
+%521 = OpLabel
 OpLine %3 192 37
-%514 = OpCompositeExtract  %5  %503 0
-%515 = OpCompositeExtract  %4  %514 1
+%529 = OpCompositeExtract  %5  %518 0
+%530 = OpCompositeExtract  %4  %529 1
 OpLine %3 192 20
-OpStore %468 %515
-OpBranch %504
-%507 = OpLabel
+OpStore %483 %530
+OpBranch %519
+%522 = OpLabel
 OpLine %3 193 37
-%516 = OpCompositeExtract  %5  %503 0
-%517 = OpCompositeExtract  %4  %516 2
+%531 = OpCompositeExtract  %5  %518 0
+%532 = OpCompositeExtract  %4  %531 2
 OpLine %3 193 20
-OpStore %468 %517
-OpBranch %504
-%508 = OpLabel
+OpStore %483 %532
+OpBranch %519
+%523 = OpLabel
 OpLine %3 194 37
-%518 = OpCompositeExtract  %5  %503 1
-%519 = OpCompositeExtract  %4  %518 0
+%533 = OpCompositeExtract  %5  %518 1
+%534 = OpCompositeExtract  %4  %533 0
 OpLine %3 194 20
-OpStore %468 %519
-OpBranch %504
-%509 = OpLabel
+OpStore %483 %534
+OpBranch %519
+%524 = OpLabel
 OpLine %3 195 37
-%520 = OpCompositeExtract  %5  %503 1
-%521 = OpCompositeExtract  %4  %520 1
+%535 = OpCompositeExtract  %5  %518 1
+%536 = OpCompositeExtract  %4  %535 1
 OpLine %3 195 20
-OpStore %468 %521
-OpBranch %504
-%510 = OpLabel
+OpStore %483 %536
+OpBranch %519
+%525 = OpLabel
 OpLine %3 196 37
-%522 = OpCompositeExtract  %5  %503 1
-%523 = OpCompositeExtract  %4  %522 2
+%537 = OpCompositeExtract  %5  %518 1
+%538 = OpCompositeExtract  %4  %537 2
 OpLine %3 196 20
-OpStore %468 %523
-OpBranch %504
-%511 = OpLabel
-OpBranch %504
-%504 = OpLabel
+OpStore %483 %538
+OpBranch %519
+%526 = OpLabel
+OpBranch %519
+%519 = OpLabel
 OpLine %3 200 15
-%524 = OpAccessChain  %371  %466 %135 %135
-%525 = OpLoad  %8  %524
-%526 = OpUDiv  %8  %494 %525
-%527 = OpIAdd  %8  %494 %526
+%539 = OpAccessChain  %379  %481 %135 %135
+%540 = OpLoad  %8  %539
+%541 = OpFunctionCall  %8  %299 %509 %540
+%542 = OpIAdd  %8  %509 %541
 OpLine %3 201 15
-%528 = OpIAdd  %8  %527 %126
+%543 = OpIAdd  %8  %542 %126
 OpLine %3 202 15
-%529 = OpAccessChain  %371  %466 %135 %135
-%530 = OpLoad  %8  %529
-%531 = OpIAdd  %8  %527 %530
+%544 = OpAccessChain  %379  %481 %135 %135
+%545 = OpLoad  %8  %544
+%546 = OpIAdd  %8  %542 %545
 OpLine %3 202 15
-%532 = OpIAdd  %8  %531 %126
+%547 = OpIAdd  %8  %546 %126
 OpLine %3 203 15
-%533 = OpIAdd  %8  %532 %126
+%548 = OpIAdd  %8  %547 %126
 OpLine %3 206 5
-OpSelectionMerge %534 None
-OpSwitch %495 %539 0 %535 3 %535 2 %536 4 %536 1 %537 5 %538
-%535 = OpLabel
+OpSelectionMerge %549 None
+OpSwitch %510 %554 0 %550 3 %550 2 %551 4 %551 1 %552 5 %553
+%550 = OpLabel
 OpLine %3 207 24
-OpStore %469 %527
-OpBranch %534
-%536 = OpLabel
+OpStore %484 %542
+OpBranch %549
+%551 = OpLabel
 OpLine %3 208 24
-OpStore %469 %533
-OpBranch %534
-%537 = OpLabel
+OpStore %484 %548
+OpBranch %549
+%552 = OpLabel
 OpLine %3 209 20
-OpStore %469 %532
-OpBranch %534
-%538 = OpLabel
+OpStore %484 %547
+OpBranch %549
+%553 = OpLabel
 OpLine %3 210 20
-OpStore %469 %528
-OpBranch %534
-%539 = OpLabel
-OpBranch %534
-%534 = OpLabel
+OpStore %484 %543
+OpBranch %549
+%554 = OpLabel
+OpBranch %549
+%549 = OpLabel
 OpLine %3 213 13
-%540 = OpCompositeExtract  %8  %454 0
+%555 = OpCompositeExtract  %8  %469 0
 OpLine %3 213 5
-OpStore %469 %540
+OpStore %484 %555
 OpLine %3 222 27
-%541 = OpLoad  %4  %468
-%542 = OpBitcast  %8  %541
+%556 = OpLoad  %4  %483
+%557 = OpBitcast  %8  %556
 OpLine %3 223 12
-%543 = OpLoad  %8  %469
-%544 = OpCompositeConstruct  %22  %542 %543
-%545 = OpCompositeExtract  %8  %544 0
-OpStore %463 %545
-%546 = OpCompositeExtract  %8  %544 1
-OpStore %464 %546
+%558 = OpLoad  %8  %484
+%559 = OpCompositeConstruct  %22  %557 %558
+%560 = OpCompositeExtract  %8  %559 0
+OpStore %478 %560
+%561 = OpCompositeExtract  %8  %559 1
+OpStore %479 %561
 OpReturn
 OpFunctionEnd
-%558 = OpFunction  %2  None %346
-%547 = OpLabel
-%551 = OpLoad  %5  %549
-%553 = OpLoad  %5  %552
-%548 = OpCompositeConstruct  %14  %551 %553
-%560 = OpAccessChain  %559  %39 %135
-OpBranch %561
-%561 = OpLabel
+%573 = OpFunction  %2  None %354
+%562 = OpLabel
+%566 = OpLoad  %5  %564
+%568 = OpLoad  %5  %567
+%563 = OpCompositeConstruct  %14  %566 %568
+%575 = OpAccessChain  %574  %39 %135
+OpBranch %576
+%576 = OpLabel
 OpLine %3 254 25
-%563 = OpAccessChain  %562  %560 %126
-%564 = OpLoad  %23  %563
-%565 = OpCompositeExtract  %5  %548 0
+%578 = OpAccessChain  %577  %575 %126
+%579 = OpLoad  %23  %578
+%580 = OpCompositeExtract  %5  %563 0
 OpLine %3 254 25
-%566 = OpCompositeConstruct  %7  %565 %56
-%567 = OpMatrixTimesVector  %7  %564 %566
+%581 = OpCompositeConstruct  %7  %580 %56
+%582 = OpMatrixTimesVector  %7  %579 %581
 OpLine %3 255 18
-%568 = OpCompositeExtract  %5  %548 1
+%583 = OpCompositeExtract  %5  %563 1
 OpLine %3 256 12
-%569 = OpCompositeExtract  %5  %548 0
-%570 = OpCompositeConstruct  %26  %567 %568 %569
-%571 = OpCompositeExtract  %7  %570 0
-OpStore %554 %571
-%572 = OpCompositeExtract  %5  %570 1
-OpStore %555 %572
-%573 = OpCompositeExtract  %5  %570 2
-OpStore %557 %573
+%584 = OpCompositeExtract  %5  %563 0
+%585 = OpCompositeConstruct  %26  %582 %583 %584
+%586 = OpCompositeExtract  %7  %585 0
+OpStore %569 %586
+%587 = OpCompositeExtract  %5  %585 1
+OpStore %570 %587
+%588 = OpCompositeExtract  %5  %585 2
+OpStore %572 %588
 OpReturn
 OpFunctionEnd
-%583 = OpFunction  %2  None %346
-%574 = OpLabel
-%592 = OpVariable  %95  Function %593
-%577 = OpLoad  %7  %576
-%579 = OpLoad  %5  %578
-%581 = OpLoad  %5  %580
-%575 = OpCompositeConstruct  %26  %577 %579 %581
-%584 = OpAccessChain  %559  %39 %135
-%586 = OpAccessChain  %585  %42 %135
-OpBranch %594
-%594 = OpLabel
+%598 = OpFunction  %2  None %354
+%589 = OpLabel
+%607 = OpVariable  %95  Function %608
+%592 = OpLoad  %7  %591
+%594 = OpLoad  %5  %593
+%596 = OpLoad  %5  %595
+%590 = OpCompositeConstruct  %26  %592 %594 %596
+%599 = OpAccessChain  %574  %39 %135
+%601 = OpAccessChain  %600  %42 %135
+OpBranch %609
+%609 = OpLabel
 OpLine %3 278 28
 OpLine %3 278 17
-%595 = OpCompositeExtract  %5  %575 2
-%596 = OpExtInst  %5  %1 Fract %595
-%597 = OpExtInst  %5  %1 SmoothStep %80 %587 %596
+%610 = OpCompositeExtract  %5  %590 2
+%611 = OpExtInst  %5  %1 Fract %610
+%612 = OpExtInst  %5  %1 SmoothStep %80 %602 %611
 OpLine %3 278 5
-OpStore %592 %597
+OpStore %607 %612
 OpLine %3 279 17
 OpLine %3 279 13
-%598 = OpAccessChain  %125  %592 %135
-%599 = OpLoad  %4  %598
-%600 = OpAccessChain  %125  %592 %126
-%601 = OpLoad  %4  %600
-%602 = OpFMul  %4  %599 %601
-%603 = OpAccessChain  %125  %592 %350
-%604 = OpLoad  %4  %603
-%605 = OpFMul  %4  %602 %604
-%606 = OpCompositeConstruct  %5  %605 %605 %605
-%607 = OpExtInst  %5  %1 FMix %589 %591 %606
+%613 = OpAccessChain  %125  %607 %135
+%614 = OpLoad  %4  %613
+%615 = OpAccessChain  %125  %607 %126
+%616 = OpLoad  %4  %615
+%617 = OpFMul  %4  %614 %616
+%618 = OpAccessChain  %125  %607 %358
+%619 = OpLoad  %4  %618
+%620 = OpFMul  %4  %617 %619
+%621 = OpCompositeConstruct  %5  %620 %620 %620
+%622 = OpExtInst  %5  %1 FMix %604 %606 %621
 OpLine %3 279 5
-OpStore %592 %607
+OpStore %607 %622
 OpLine %3 282 25
-%609 = OpAccessChain  %608  %586 %126
-%610 = OpLoad  %5  %609
-%611 = OpVectorTimesScalar  %5  %610 %272
+%624 = OpAccessChain  %623  %601 %126
+%625 = OpLoad  %5  %624
+%626 = OpVectorTimesScalar  %5  %625 %272
 OpLine %3 284 21
-%612 = OpAccessChain  %608  %586 %135
-%613 = OpLoad  %5  %612
-%614 = OpCompositeExtract  %5  %575 2
-%615 = OpFSub  %5  %613 %614
-%616 = OpExtInst  %5  %1 Normalize %615
+%627 = OpAccessChain  %623  %601 %135
+%628 = OpLoad  %5  %627
+%629 = OpCompositeExtract  %5  %590 2
+%630 = OpFSub  %5  %628 %629
+%631 = OpExtInst  %5  %1 Normalize %630
 OpLine %3 285 20
-%618 = OpAccessChain  %617  %584 %135
-%619 = OpLoad  %7  %618
-%620 = OpVectorShuffle  %5  %619 %619 0 1 2
-%621 = OpCompositeExtract  %5  %575 2
-%622 = OpFSub  %5  %620 %621
-%623 = OpExtInst  %5  %1 Normalize %622
+%633 = OpAccessChain  %632  %599 %135
+%634 = OpLoad  %7  %633
+%635 = OpVectorShuffle  %5  %634 %634 0 1 2
+%636 = OpCompositeExtract  %5  %590 2
+%637 = OpFSub  %5  %635 %636
+%638 = OpExtInst  %5  %1 Normalize %637
 OpLine %3 286 20
-%624 = OpFAdd  %5  %623 %616
-%625 = OpExtInst  %5  %1 Normalize %624
+%639 = OpFAdd  %5  %638 %631
+%640 = OpExtInst  %5  %1 Normalize %639
 OpLine %3 288 32
-%626 = OpCompositeExtract  %5  %575 1
-%627 = OpDot  %4  %626 %616
+%641 = OpCompositeExtract  %5  %590 1
+%642 = OpDot  %4  %641 %631
 OpLine %3 288 28
-%628 = OpExtInst  %4  %1 FMax %627 %74
+%643 = OpExtInst  %4  %1 FMax %642 %74
 OpLine %3 289 25
-%629 = OpAccessChain  %608  %586 %126
-%630 = OpLoad  %5  %629
-%631 = OpVectorTimesScalar  %5  %630 %628
+%644 = OpAccessChain  %623  %601 %126
+%645 = OpLoad  %5  %644
+%646 = OpVectorTimesScalar  %5  %645 %643
 OpLine %3 291 37
-%632 = OpCompositeExtract  %5  %575 1
-%633 = OpDot  %4  %632 %625
+%647 = OpCompositeExtract  %5  %590 1
+%648 = OpDot  %4  %647 %640
 OpLine %3 291 33
-%634 = OpExtInst  %4  %1 FMax %633 %74
+%649 = OpExtInst  %4  %1 FMax %648 %74
 OpLine %3 291 29
-%635 = OpExtInst  %4  %1 Pow %634 %323
+%650 = OpExtInst  %4  %1 Pow %649 %331
 OpLine %3 292 26
-%636 = OpAccessChain  %608  %586 %126
-%637 = OpLoad  %5  %636
-%638 = OpVectorTimesScalar  %5  %637 %635
+%651 = OpAccessChain  %623  %601 %126
+%652 = OpLoad  %5  %651
+%653 = OpVectorTimesScalar  %5  %652 %650
 OpLine %3 294 18
-%639 = OpFAdd  %5  %611 %631
-%640 = OpFAdd  %5  %639 %638
-%641 = OpLoad  %5  %592
-%642 = OpFMul  %5  %640 %641
+%654 = OpFAdd  %5  %626 %646
+%655 = OpFAdd  %5  %654 %653
+%656 = OpLoad  %5  %607
+%657 = OpFMul  %5  %655 %656
 OpLine %3 296 12
-%643 = OpCompositeConstruct  %7  %642 %56
-OpStore %582 %643
+%658 = OpCompositeConstruct  %7  %657 %56
+OpStore %597 %658
 OpReturn
 OpFunctionEnd
\ No newline at end of file
diff --git a/naga/tests/out/spv/image.spvasm b/naga/tests/out/spv/image.spvasm
index 5ec2925dc6..7a34bed86e 100644
--- a/naga/tests/out/spv/image.spvasm
+++ b/naga/tests/out/spv/image.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.1
 ; Generator: rspirv
-; Bound: 526
+; Bound: 546
 OpCapability Shader
 OpCapability Image1D
 OpCapability Sampled1D
@@ -9,20 +9,20 @@ OpCapability SampledCubeArray
 OpCapability ImageQuery
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %78 "main" %75
-OpEntryPoint GLCompute %169 "depth_load" %167
-OpEntryPoint Vertex %189 "queries" %187
-OpEntryPoint Vertex %241 "levels_queries" %240
-OpEntryPoint Fragment %272 "texture_sample" %271
-OpEntryPoint Fragment %419 "texture_sample_comparison" %417
-OpEntryPoint Fragment %475 "gather" %474
-OpEntryPoint Fragment %509 "depth_no_comparison" %508
-OpExecutionMode %78 LocalSize 16 1 1
-OpExecutionMode %169 LocalSize 16 1 1
-OpExecutionMode %272 OriginUpperLeft
-OpExecutionMode %419 OriginUpperLeft
-OpExecutionMode %475 OriginUpperLeft
-OpExecutionMode %509 OriginUpperLeft
+OpEntryPoint GLCompute %99 "main" %96
+OpEntryPoint GLCompute %190 "depth_load" %188
+OpEntryPoint Vertex %210 "queries" %208
+OpEntryPoint Vertex %262 "levels_queries" %261
+OpEntryPoint Fragment %293 "texture_sample" %292
+OpEntryPoint Fragment %439 "texture_sample_comparison" %437
+OpEntryPoint Fragment %495 "gather" %494
+OpEntryPoint Fragment %529 "depth_no_comparison" %528
+OpExecutionMode %99 LocalSize 16 1 1
+OpExecutionMode %190 LocalSize 16 1 1
+OpExecutionMode %293 OriginUpperLeft
+OpExecutionMode %439 OriginUpperLeft
+OpExecutionMode %495 OriginUpperLeft
+OpExecutionMode %529 OriginUpperLeft
 OpName %31 "image_mipmapped_src"
 OpName %33 "image_multisampled_src"
 OpName %35 "image_depth_multisampled_src"
@@ -45,18 +45,21 @@ OpName %66 "sampler_cmp"
 OpName %68 "image_2d_depth"
 OpName %70 "image_2d_array_depth"
 OpName %72 "image_cube_depth"
-OpName %75 "local_id"
-OpName %78 "main"
-OpName %167 "local_id"
-OpName %169 "depth_load"
-OpName %189 "queries"
-OpName %241 "levels_queries"
-OpName %272 "texture_sample"
-OpName %286 "a"
-OpName %419 "texture_sample_comparison"
-OpName %424 "a"
-OpName %475 "gather"
-OpName %509 "depth_no_comparison"
+OpName %74 "naga_mod"
+OpName %76 "lhs"
+OpName %77 "rhs"
+OpName %96 "local_id"
+OpName %99 "main"
+OpName %188 "local_id"
+OpName %190 "depth_load"
+OpName %210 "queries"
+OpName %262 "levels_queries"
+OpName %293 "texture_sample"
+OpName %306 "a"
+OpName %439 "texture_sample_comparison"
+OpName %444 "a"
+OpName %495 "gather"
+OpName %529 "depth_no_comparison"
 OpDecorate %31 DescriptorSet 0
 OpDecorate %31 Binding 0
 OpDecorate %33 DescriptorSet 0
@@ -104,14 +107,14 @@ OpDecorate %70 DescriptorSet 1
 OpDecorate %70 Binding 3
 OpDecorate %72 DescriptorSet 1
 OpDecorate %72 Binding 4
-OpDecorate %75 BuiltIn LocalInvocationId
-OpDecorate %167 BuiltIn LocalInvocationId
-OpDecorate %187 BuiltIn Position
-OpDecorate %240 BuiltIn Position
-OpDecorate %271 Location 0
-OpDecorate %417 Location 0
-OpDecorate %474 Location 0
-OpDecorate %508 Location 0
+OpDecorate %96 BuiltIn LocalInvocationId
+OpDecorate %188 BuiltIn LocalInvocationId
+OpDecorate %208 BuiltIn Position
+OpDecorate %261 BuiltIn Position
+OpDecorate %292 Location 0
+OpDecorate %437 Location 0
+OpDecorate %494 Location 0
+OpDecorate %528 Location 0
 %2 = OpTypeVoid
 %4 = OpTypeInt 32 0
 %3 = OpTypeImage %4 2D 0 0 0 1 Unknown
@@ -184,517 +187,539 @@ OpDecorate %508 Location 0
 %70 = OpVariable  %71  UniformConstant
 %73 = OpTypePointer UniformConstant %27
 %72 = OpVariable  %73  UniformConstant
-%76 = OpTypePointer Input %12
-%75 = OpVariable  %76  Input
-%79 = OpTypeFunction %2
-%86 = OpConstant  %14  10
-%87 = OpConstant  %14  20
-%88 = OpConstantComposite  %13  %86 %87
-%90 = OpTypeVector %4 2
-%98 = OpTypeVector %4 4
-%109 = OpTypeVector %14 3
-%167 = OpVariable  %76  Input
-%188 = OpTypePointer Output %23
-%187 = OpVariable  %188  Output
-%198 = OpConstant  %4  0
-%240 = OpVariable  %188  Output
-%271 = OpVariable  %188  Output
-%278 = OpConstant  %7  0.5
-%279 = OpTypeVector %7 2
-%280 = OpConstantComposite  %279  %278 %278
-%281 = OpTypeVector %7 3
-%282 = OpConstantComposite  %281  %278 %278 %278
-%283 = OpConstant  %7  2.3
-%284 = OpConstant  %7  2.0
-%285 = OpConstant  %14  0
-%287 = OpTypePointer Function %23
-%288 = OpConstantNull  %23
-%291 = OpTypeSampledImage %15
-%296 = OpTypeSampledImage %16
-%317 = OpTypeSampledImage %18
-%378 = OpTypeSampledImage %20
-%418 = OpTypePointer Output %7
-%417 = OpVariable  %418  Output
-%425 = OpTypePointer Function %7
-%426 = OpConstantNull  %7
-%428 = OpTypeSampledImage %25
-%433 = OpTypeSampledImage %26
-%446 = OpTypeSampledImage %27
-%453 = OpConstant  %7  0.0
-%474 = OpVariable  %188  Output
-%485 = OpConstant  %4  1
-%488 = OpConstant  %4  3
-%493 = OpTypeSampledImage %3
-%496 = OpTypeVector %14 4
-%497 = OpTypeSampledImage %17
-%508 = OpVariable  %188  Output
-%78 = OpFunction  %2  None %79
-%74 = OpLabel
-%77 = OpLoad  %12  %75
-%80 = OpLoad  %3  %31
-%81 = OpLoad  %5  %33
-%82 = OpLoad  %8  %37
-%83 = OpLoad  %9  %39
-%84 = OpLoad  %11  %43
-%85 = OpLoad  %10  %45
-OpBranch %89
-%89 = OpLabel
-%91 = OpImageQuerySize  %90  %82
-%92 = OpVectorShuffle  %90  %77 %77 0 1
-%93 = OpIMul  %90  %91 %92
-%94 = OpBitcast  %13  %93
-%95 = OpSRem  %13  %94 %88
-%96 = OpCompositeExtract  %4  %77 2
-%97 = OpBitcast  %14  %96
-%99 = OpImageFetch  %98  %80 %95 Lod %97
-%100 = OpCompositeExtract  %4  %77 2
-%101 = OpBitcast  %14  %100
-%102 = OpImageFetch  %98  %81 %95 Sample %101
-%103 = OpImageRead  %98  %82 %95
-%104 = OpCompositeExtract  %4  %77 2
-%105 = OpCompositeExtract  %4  %77 2
-%106 = OpBitcast  %14  %105
-%107 = OpIAdd  %14  %106 %29
-%108 = OpBitcast  %14  %104
-%110 = OpCompositeConstruct  %109  %95 %108
-%111 = OpImageFetch  %98  %83 %110 Lod %107
-%112 = OpCompositeExtract  %4  %77 2
-%113 = OpBitcast  %14  %112
-%114 = OpCompositeExtract  %4  %77 2
-%115 = OpBitcast  %14  %114
-%116 = OpIAdd  %14  %115 %29
-%117 = OpCompositeConstruct  %109  %95 %113
-%118 = OpImageFetch  %98  %83 %117 Lod %116
-%119 = OpCompositeExtract  %4  %77 0
-%120 = OpBitcast  %14  %119
-%121 = OpCompositeExtract  %4  %77 2
+%75 = OpTypeFunction %13 %13 %13
+%80 = OpTypeBool
+%79 = OpTypeVector %80 2
+%81 = OpConstant  %14  0
+%82 = OpConstantComposite  %13  %81 %81
+%84 = OpConstant  %14  -2147483648
+%85 = OpConstant  %14  -1
+%86 = OpConstantComposite  %13  %84 %84
+%87 = OpConstantComposite  %13  %85 %85
+%92 = OpConstantComposite  %13  %29 %29
+%97 = OpTypePointer Input %12
+%96 = OpVariable  %97  Input
+%100 = OpTypeFunction %2
+%107 = OpConstant  %14  10
+%108 = OpConstant  %14  20
+%109 = OpConstantComposite  %13  %107 %108
+%111 = OpTypeVector %4 2
+%119 = OpTypeVector %4 4
+%130 = OpTypeVector %14 3
+%188 = OpVariable  %97  Input
+%209 = OpTypePointer Output %23
+%208 = OpVariable  %209  Output
+%219 = OpConstant  %4  0
+%261 = OpVariable  %209  Output
+%292 = OpVariable  %209  Output
+%299 = OpConstant  %7  0.5
+%300 = OpTypeVector %7 2
+%301 = OpConstantComposite  %300  %299 %299
+%302 = OpTypeVector %7 3
+%303 = OpConstantComposite  %302  %299 %299 %299
+%304 = OpConstant  %7  2.3
+%305 = OpConstant  %7  2.0
+%307 = OpTypePointer Function %23
+%308 = OpConstantNull  %23
+%311 = OpTypeSampledImage %15
+%316 = OpTypeSampledImage %16
+%337 = OpTypeSampledImage %18
+%398 = OpTypeSampledImage %20
+%438 = OpTypePointer Output %7
+%437 = OpVariable  %438  Output
+%445 = OpTypePointer Function %7
+%446 = OpConstantNull  %7
+%448 = OpTypeSampledImage %25
+%453 = OpTypeSampledImage %26
+%466 = OpTypeSampledImage %27
+%473 = OpConstant  %7  0.0
+%494 = OpVariable  %209  Output
+%505 = OpConstant  %4  1
+%508 = OpConstant  %4  3
+%513 = OpTypeSampledImage %3
+%516 = OpTypeVector %14 4
+%517 = OpTypeSampledImage %17
+%528 = OpVariable  %209  Output
+%74 = OpFunction  %13  None %75
+%76 = OpFunctionParameter  %13
+%77 = OpFunctionParameter  %13
+%78 = OpLabel
+%83 = OpIEqual  %79  %77 %82
+%88 = OpIEqual  %79  %76 %86
+%89 = OpIEqual  %79  %77 %87
+%90 = OpLogicalAnd  %79  %88 %89
+%91 = OpLogicalOr  %79  %83 %90
+%93 = OpSelect  %13  %91 %92 %77
+%94 = OpSRem  %13  %76 %93
+OpReturnValue %94
+OpFunctionEnd
+%99 = OpFunction  %2  None %100
+%95 = OpLabel
+%98 = OpLoad  %12  %96
+%101 = OpLoad  %3  %31
+%102 = OpLoad  %5  %33
+%103 = OpLoad  %8  %37
+%104 = OpLoad  %9  %39
+%105 = OpLoad  %11  %43
+%106 = OpLoad  %10  %45
+OpBranch %110
+%110 = OpLabel
+%112 = OpImageQuerySize  %111  %103
+%113 = OpVectorShuffle  %111  %98 %98 0 1
+%114 = OpIMul  %111  %112 %113
+%115 = OpBitcast  %13  %114
+%116 = OpFunctionCall  %13  %74 %115 %109
+%117 = OpCompositeExtract  %4  %98 2
+%118 = OpBitcast  %14  %117
+%120 = OpImageFetch  %119  %101 %116 Lod %118
+%121 = OpCompositeExtract  %4  %98 2
 %122 = OpBitcast  %14  %121
-%123 = OpImageFetch  %98  %84 %120 Lod %122
-%124 = OpBitcast  %90  %95
-%125 = OpCompositeExtract  %4  %77 2
-%126 = OpBitcast  %14  %125
-%127 = OpImageFetch  %98  %80 %124 Lod %126
-%128 = OpBitcast  %90  %95
-%129 = OpCompositeExtract  %4  %77 2
-%130 = OpBitcast  %14  %129
-%131 = OpImageFetch  %98  %81 %128 Sample %130
-%132 = OpBitcast  %90  %95
-%133 = OpImageRead  %98  %82 %132
-%134 = OpBitcast  %90  %95
-%135 = OpCompositeExtract  %4  %77 2
-%136 = OpCompositeExtract  %4  %77 2
-%137 = OpBitcast  %14  %136
-%138 = OpIAdd  %14  %137 %29
-%139 = OpCompositeConstruct  %12  %134 %135
-%140 = OpImageFetch  %98  %83 %139 Lod %138
-%141 = OpBitcast  %90  %95
-%142 = OpCompositeExtract  %4  %77 2
+%123 = OpImageFetch  %119  %102 %116 Sample %122
+%124 = OpImageRead  %119  %103 %116
+%125 = OpCompositeExtract  %4  %98 2
+%126 = OpCompositeExtract  %4  %98 2
+%127 = OpBitcast  %14  %126
+%128 = OpIAdd  %14  %127 %29
+%129 = OpBitcast  %14  %125
+%131 = OpCompositeConstruct  %130  %116 %129
+%132 = OpImageFetch  %119  %104 %131 Lod %128
+%133 = OpCompositeExtract  %4  %98 2
+%134 = OpBitcast  %14  %133
+%135 = OpCompositeExtract  %4  %98 2
+%136 = OpBitcast  %14  %135
+%137 = OpIAdd  %14  %136 %29
+%138 = OpCompositeConstruct  %130  %116 %134
+%139 = OpImageFetch  %119  %104 %138 Lod %137
+%140 = OpCompositeExtract  %4  %98 0
+%141 = OpBitcast  %14  %140
+%142 = OpCompositeExtract  %4  %98 2
 %143 = OpBitcast  %14  %142
-%144 = OpCompositeExtract  %4  %77 2
-%145 = OpBitcast  %14  %144
-%146 = OpIAdd  %14  %145 %29
-%147 = OpBitcast  %4  %143
-%148 = OpCompositeConstruct  %12  %141 %147
-%149 = OpImageFetch  %98  %83 %148 Lod %146
-%150 = OpCompositeExtract  %4  %77 0
-%152 = OpCompositeExtract  %4  %77 2
-%153 = OpBitcast  %14  %152
-%154 = OpImageFetch  %98  %84 %150 Lod %153
-%155 = OpCompositeExtract  %14  %95 0
-%156 = OpIAdd  %98  %99 %102
-%157 = OpIAdd  %98  %156 %103
-%158 = OpIAdd  %98  %157 %111
-%159 = OpIAdd  %98  %158 %118
-OpImageWrite %85 %155 %159
-%160 = OpCompositeExtract  %14  %95 0
-%161 = OpBitcast  %4  %160
-%162 = OpIAdd  %98  %127 %131
-%163 = OpIAdd  %98  %162 %133
-%164 = OpIAdd  %98  %163 %140
-%165 = OpIAdd  %98  %164 %149
-OpImageWrite %85 %161 %165
+%144 = OpImageFetch  %119  %105 %141 Lod %143
+%145 = OpBitcast  %111  %116
+%146 = OpCompositeExtract  %4  %98 2
+%147 = OpBitcast  %14  %146
+%148 = OpImageFetch  %119  %101 %145 Lod %147
+%149 = OpBitcast  %111  %116
+%150 = OpCompositeExtract  %4  %98 2
+%151 = OpBitcast  %14  %150
+%152 = OpImageFetch  %119  %102 %149 Sample %151
+%153 = OpBitcast  %111  %116
+%154 = OpImageRead  %119  %103 %153
+%155 = OpBitcast  %111  %116
+%156 = OpCompositeExtract  %4  %98 2
+%157 = OpCompositeExtract  %4  %98 2
+%158 = OpBitcast  %14  %157
+%159 = OpIAdd  %14  %158 %29
+%160 = OpCompositeConstruct  %12  %155 %156
+%161 = OpImageFetch  %119  %104 %160 Lod %159
+%162 = OpBitcast  %111  %116
+%163 = OpCompositeExtract  %4  %98 2
+%164 = OpBitcast  %14  %163
+%165 = OpCompositeExtract  %4  %98 2
+%166 = OpBitcast  %14  %165
+%167 = OpIAdd  %14  %166 %29
+%168 = OpBitcast  %4  %164
+%169 = OpCompositeConstruct  %12  %162 %168
+%170 = OpImageFetch  %119  %104 %169 Lod %167
+%171 = OpCompositeExtract  %4  %98 0
+%173 = OpCompositeExtract  %4  %98 2
+%174 = OpBitcast  %14  %173
+%175 = OpImageFetch  %119  %105 %171 Lod %174
+%176 = OpCompositeExtract  %14  %116 0
+%177 = OpIAdd  %119  %120 %123
+%178 = OpIAdd  %119  %177 %124
+%179 = OpIAdd  %119  %178 %132
+%180 = OpIAdd  %119  %179 %139
+OpImageWrite %106 %176 %180
+%181 = OpCompositeExtract  %14  %116 0
+%182 = OpBitcast  %4  %181
+%183 = OpIAdd  %119  %148 %152
+%184 = OpIAdd  %119  %183 %154
+%185 = OpIAdd  %119  %184 %161
+%186 = OpIAdd  %119  %185 %170
+OpImageWrite %106 %182 %186
 OpReturn
 OpFunctionEnd
-%169 = OpFunction  %2  None %79
-%166 = OpLabel
-%168 = OpLoad  %12  %167
-%170 = OpLoad  %6  %35
-%171 = OpLoad  %8  %37
-%172 = OpLoad  %10  %45
-OpBranch %173
-%173 = OpLabel
-%174 = OpImageQuerySize  %90  %171
-%175 = OpVectorShuffle  %90  %168 %168 0 1
-%176 = OpIMul  %90  %174 %175
-%177 = OpBitcast  %13  %176
-%178 = OpSRem  %13  %177 %88
-%179 = OpCompositeExtract  %4  %168 2
-%180 = OpBitcast  %14  %179
-%181 = OpImageFetch  %23  %170 %178 Sample %180
-%182 = OpCompositeExtract  %7  %181 0
-%183 = OpCompositeExtract  %14  %178 0
-%184 = OpConvertFToU  %4  %182
-%185 = OpCompositeConstruct  %98  %184 %184 %184 %184
-OpImageWrite %172 %183 %185
+%190 = OpFunction  %2  None %100
+%187 = OpLabel
+%189 = OpLoad  %12  %188
+%191 = OpLoad  %6  %35
+%192 = OpLoad  %8  %37
+%193 = OpLoad  %10  %45
+OpBranch %194
+%194 = OpLabel
+%195 = OpImageQuerySize  %111  %192
+%196 = OpVectorShuffle  %111  %189 %189 0 1
+%197 = OpIMul  %111  %195 %196
+%198 = OpBitcast  %13  %197
+%199 = OpFunctionCall  %13  %74 %198 %109
+%200 = OpCompositeExtract  %4  %189 2
+%201 = OpBitcast  %14  %200
+%202 = OpImageFetch  %23  %191 %199 Sample %201
+%203 = OpCompositeExtract  %7  %202 0
+%204 = OpCompositeExtract  %14  %199 0
+%205 = OpConvertFToU  %4  %203
+%206 = OpCompositeConstruct  %119  %205 %205 %205 %205
+OpImageWrite %193 %204 %206
 OpReturn
 OpFunctionEnd
-%189 = OpFunction  %2  None %79
-%186 = OpLabel
-%190 = OpLoad  %15  %47
-%191 = OpLoad  %16  %49
-%192 = OpLoad  %18  %54
-%193 = OpLoad  %19  %56
-%194 = OpLoad  %20  %58
-%195 = OpLoad  %21  %60
-%196 = OpLoad  %22  %62
-OpBranch %197
-%197 = OpLabel
-%199 = OpImageQuerySizeLod  %4  %190 %198
-%200 = OpBitcast  %14  %199
-%201 = OpImageQuerySizeLod  %4  %190 %200
-%202 = OpImageQuerySizeLod  %90  %191 %198
-%203 = OpImageQuerySizeLod  %90  %191 %29
-%204 = OpImageQuerySizeLod  %12  %192 %198
-%205 = OpVectorShuffle  %90  %204 %204 0 1
-%206 = OpImageQuerySizeLod  %12  %192 %29
-%207 = OpVectorShuffle  %90  %206 %206 0 1
-%208 = OpImageQuerySizeLod  %90  %193 %198
-%209 = OpImageQuerySizeLod  %90  %193 %29
-%210 = OpImageQuerySizeLod  %12  %194 %198
-%211 = OpVectorShuffle  %90  %210 %210 0 0
-%212 = OpImageQuerySizeLod  %12  %194 %29
-%213 = OpVectorShuffle  %90  %212 %212 0 0
-%214 = OpImageQuerySizeLod  %12  %195 %198
-%215 = OpImageQuerySizeLod  %12  %195 %29
-%216 = OpImageQuerySize  %90  %196
-%217 = OpCompositeExtract  %4  %202 1
-%218 = OpIAdd  %4  %199 %217
-%219 = OpCompositeExtract  %4  %203 1
-%220 = OpIAdd  %4  %218 %219
-%221 = OpCompositeExtract  %4  %205 1
-%222 = OpIAdd  %4  %220 %221
-%223 = OpCompositeExtract  %4  %207 1
-%224 = OpIAdd  %4  %222 %223
-%225 = OpCompositeExtract  %4  %208 1
-%226 = OpIAdd  %4  %224 %225
-%227 = OpCompositeExtract  %4  %209 1
-%228 = OpIAdd  %4  %226 %227
-%229 = OpCompositeExtract  %4  %211 1
-%230 = OpIAdd  %4  %228 %229
-%231 = OpCompositeExtract  %4  %213 1
-%232 = OpIAdd  %4  %230 %231
-%233 = OpCompositeExtract  %4  %214 2
-%234 = OpIAdd  %4  %232 %233
-%235 = OpCompositeExtract  %4  %215 2
-%236 = OpIAdd  %4  %234 %235
-%237 = OpConvertUToF  %7  %236
-%238 = OpCompositeConstruct  %23  %237 %237 %237 %237
-OpStore %187 %238
+%210 = OpFunction  %2  None %100
+%207 = OpLabel
+%211 = OpLoad  %15  %47
+%212 = OpLoad  %16  %49
+%213 = OpLoad  %18  %54
+%214 = OpLoad  %19  %56
+%215 = OpLoad  %20  %58
+%216 = OpLoad  %21  %60
+%217 = OpLoad  %22  %62
+OpBranch %218
+%218 = OpLabel
+%220 = OpImageQuerySizeLod  %4  %211 %219
+%221 = OpBitcast  %14  %220
+%222 = OpImageQuerySizeLod  %4  %211 %221
+%223 = OpImageQuerySizeLod  %111  %212 %219
+%224 = OpImageQuerySizeLod  %111  %212 %29
+%225 = OpImageQuerySizeLod  %12  %213 %219
+%226 = OpVectorShuffle  %111  %225 %225 0 1
+%227 = OpImageQuerySizeLod  %12  %213 %29
+%228 = OpVectorShuffle  %111  %227 %227 0 1
+%229 = OpImageQuerySizeLod  %111  %214 %219
+%230 = OpImageQuerySizeLod  %111  %214 %29
+%231 = OpImageQuerySizeLod  %12  %215 %219
+%232 = OpVectorShuffle  %111  %231 %231 0 0
+%233 = OpImageQuerySizeLod  %12  %215 %29
+%234 = OpVectorShuffle  %111  %233 %233 0 0
+%235 = OpImageQuerySizeLod  %12  %216 %219
+%236 = OpImageQuerySizeLod  %12  %216 %29
+%237 = OpImageQuerySize  %111  %217
+%238 = OpCompositeExtract  %4  %223 1
+%239 = OpIAdd  %4  %220 %238
+%240 = OpCompositeExtract  %4  %224 1
+%241 = OpIAdd  %4  %239 %240
+%242 = OpCompositeExtract  %4  %226 1
+%243 = OpIAdd  %4  %241 %242
+%244 = OpCompositeExtract  %4  %228 1
+%245 = OpIAdd  %4  %243 %244
+%246 = OpCompositeExtract  %4  %229 1
+%247 = OpIAdd  %4  %245 %246
+%248 = OpCompositeExtract  %4  %230 1
+%249 = OpIAdd  %4  %247 %248
+%250 = OpCompositeExtract  %4  %232 1
+%251 = OpIAdd  %4  %249 %250
+%252 = OpCompositeExtract  %4  %234 1
+%253 = OpIAdd  %4  %251 %252
+%254 = OpCompositeExtract  %4  %235 2
+%255 = OpIAdd  %4  %253 %254
+%256 = OpCompositeExtract  %4  %236 2
+%257 = OpIAdd  %4  %255 %256
+%258 = OpConvertUToF  %7  %257
+%259 = OpCompositeConstruct  %23  %258 %258 %258 %258
+OpStore %208 %259
 OpReturn
 OpFunctionEnd
-%241 = OpFunction  %2  None %79
-%239 = OpLabel
-%242 = OpLoad  %16  %49
-%243 = OpLoad  %18  %54
-%244 = OpLoad  %19  %56
-%245 = OpLoad  %20  %58
-%246 = OpLoad  %21  %60
-%247 = OpLoad  %22  %62
-OpBranch %248
-%248 = OpLabel
-%249 = OpImageQueryLevels  %4  %242
-%250 = OpImageQuerySizeLod  %12  %243 %198
-%251 = OpCompositeExtract  %4  %250 2
-%252 = OpImageQueryLevels  %4  %243
-%253 = OpImageQuerySizeLod  %12  %243 %198
-%254 = OpCompositeExtract  %4  %253 2
-%255 = OpImageQueryLevels  %4  %244
-%256 = OpImageQueryLevels  %4  %245
-%257 = OpImageQuerySizeLod  %12  %245 %198
-%258 = OpCompositeExtract  %4  %257 2
-%259 = OpImageQueryLevels  %4  %246
-%260 = OpImageQuerySamples  %4  %247
-%261 = OpIAdd  %4  %251 %258
-%262 = OpIAdd  %4  %261 %260
-%263 = OpIAdd  %4  %262 %249
-%264 = OpIAdd  %4  %263 %252
-%265 = OpIAdd  %4  %264 %259
-%266 = OpIAdd  %4  %265 %255
-%267 = OpIAdd  %4  %266 %256
-%268 = OpConvertUToF  %7  %267
-%269 = OpCompositeConstruct  %23  %268 %268 %268 %268
-OpStore %240 %269
+%262 = OpFunction  %2  None %100
+%260 = OpLabel
+%263 = OpLoad  %16  %49
+%264 = OpLoad  %18  %54
+%265 = OpLoad  %19  %56
+%266 = OpLoad  %20  %58
+%267 = OpLoad  %21  %60
+%268 = OpLoad  %22  %62
+OpBranch %269
+%269 = OpLabel
+%270 = OpImageQueryLevels  %4  %263
+%271 = OpImageQuerySizeLod  %12  %264 %219
+%272 = OpCompositeExtract  %4  %271 2
+%273 = OpImageQueryLevels  %4  %264
+%274 = OpImageQuerySizeLod  %12  %264 %219
+%275 = OpCompositeExtract  %4  %274 2
+%276 = OpImageQueryLevels  %4  %265
+%277 = OpImageQueryLevels  %4  %266
+%278 = OpImageQuerySizeLod  %12  %266 %219
+%279 = OpCompositeExtract  %4  %278 2
+%280 = OpImageQueryLevels  %4  %267
+%281 = OpImageQuerySamples  %4  %268
+%282 = OpIAdd  %4  %272 %279
+%283 = OpIAdd  %4  %282 %281
+%284 = OpIAdd  %4  %283 %270
+%285 = OpIAdd  %4  %284 %273
+%286 = OpIAdd  %4  %285 %280
+%287 = OpIAdd  %4  %286 %276
+%288 = OpIAdd  %4  %287 %277
+%289 = OpConvertUToF  %7  %288
+%290 = OpCompositeConstruct  %23  %289 %289 %289 %289
+OpStore %261 %290
 OpReturn
 OpFunctionEnd
-%272 = OpFunction  %2  None %79
-%270 = OpLabel
-%286 = OpVariable  %287  Function %288
-%273 = OpLoad  %15  %47
-%274 = OpLoad  %16  %49
-%275 = OpLoad  %18  %54
-%276 = OpLoad  %20  %58
-%277 = OpLoad  %24  %64
-OpBranch %289
-%289 = OpLabel
-%290 = OpCompositeExtract  %7  %280 0
-%292 = OpSampledImage  %291  %273 %277
-%293 = OpImageSampleImplicitLod  %23  %292 %290
-%294 = OpLoad  %23  %286
-%295 = OpFAdd  %23  %294 %293
-OpStore %286 %295
-%297 = OpSampledImage  %296  %274 %277
-%298 = OpImageSampleImplicitLod  %23  %297 %280
-%299 = OpLoad  %23  %286
-%300 = OpFAdd  %23  %299 %298
-OpStore %286 %300
-%301 = OpSampledImage  %296  %274 %277
-%302 = OpImageSampleImplicitLod  %23  %301 %280 ConstOffset %30
-%303 = OpLoad  %23  %286
-%304 = OpFAdd  %23  %303 %302
-OpStore %286 %304
-%305 = OpSampledImage  %296  %274 %277
-%306 = OpImageSampleExplicitLod  %23  %305 %280 Lod %283
-%307 = OpLoad  %23  %286
-%308 = OpFAdd  %23  %307 %306
-OpStore %286 %308
-%309 = OpSampledImage  %296  %274 %277
-%310 = OpImageSampleExplicitLod  %23  %309 %280 Lod|ConstOffset %283 %30
-%311 = OpLoad  %23  %286
-%312 = OpFAdd  %23  %311 %310
-OpStore %286 %312
-%313 = OpSampledImage  %296  %274 %277
-%314 = OpImageSampleImplicitLod  %23  %313 %280 Bias|ConstOffset %284 %30
-%315 = OpLoad  %23  %286
-%316 = OpFAdd  %23  %315 %314
-OpStore %286 %316
-%318 = OpConvertUToF  %7  %198
-%319 = OpCompositeConstruct  %281  %280 %318
-%320 = OpSampledImage  %317  %275 %277
-%321 = OpImageSampleImplicitLod  %23  %320 %319
-%322 = OpLoad  %23  %286
-%323 = OpFAdd  %23  %322 %321
-OpStore %286 %323
-%324 = OpConvertUToF  %7  %198
-%325 = OpCompositeConstruct  %281  %280 %324
-%326 = OpSampledImage  %317  %275 %277
-%327 = OpImageSampleImplicitLod  %23  %326 %325 ConstOffset %30
-%328 = OpLoad  %23  %286
-%329 = OpFAdd  %23  %328 %327
-OpStore %286 %329
-%330 = OpConvertUToF  %7  %198
-%331 = OpCompositeConstruct  %281  %280 %330
-%332 = OpSampledImage  %317  %275 %277
-%333 = OpImageSampleExplicitLod  %23  %332 %331 Lod %283
-%334 = OpLoad  %23  %286
-%335 = OpFAdd  %23  %334 %333
-OpStore %286 %335
-%336 = OpConvertUToF  %7  %198
-%337 = OpCompositeConstruct  %281  %280 %336
-%338 = OpSampledImage  %317  %275 %277
-%339 = OpImageSampleExplicitLod  %23  %338 %337 Lod|ConstOffset %283 %30
-%340 = OpLoad  %23  %286
-%341 = OpFAdd  %23  %340 %339
-OpStore %286 %341
-%342 = OpConvertUToF  %7  %198
-%343 = OpCompositeConstruct  %281  %280 %342
-%344 = OpSampledImage  %317  %275 %277
-%345 = OpImageSampleImplicitLod  %23  %344 %343 Bias|ConstOffset %284 %30
-%346 = OpLoad  %23  %286
-%347 = OpFAdd  %23  %346 %345
-OpStore %286 %347
-%348 = OpConvertSToF  %7  %285
-%349 = OpCompositeConstruct  %281  %280 %348
-%350 = OpSampledImage  %317  %275 %277
-%351 = OpImageSampleImplicitLod  %23  %350 %349
-%352 = OpLoad  %23  %286
-%353 = OpFAdd  %23  %352 %351
-OpStore %286 %353
-%354 = OpConvertSToF  %7  %285
-%355 = OpCompositeConstruct  %281  %280 %354
-%356 = OpSampledImage  %317  %275 %277
-%357 = OpImageSampleImplicitLod  %23  %356 %355 ConstOffset %30
-%358 = OpLoad  %23  %286
-%359 = OpFAdd  %23  %358 %357
-OpStore %286 %359
-%360 = OpConvertSToF  %7  %285
-%361 = OpCompositeConstruct  %281  %280 %360
-%362 = OpSampledImage  %317  %275 %277
-%363 = OpImageSampleExplicitLod  %23  %362 %361 Lod %283
-%364 = OpLoad  %23  %286
-%365 = OpFAdd  %23  %364 %363
-OpStore %286 %365
-%366 = OpConvertSToF  %7  %285
-%367 = OpCompositeConstruct  %281  %280 %366
-%368 = OpSampledImage  %317  %275 %277
-%369 = OpImageSampleExplicitLod  %23  %368 %367 Lod|ConstOffset %283 %30
-%370 = OpLoad  %23  %286
-%371 = OpFAdd  %23  %370 %369
-OpStore %286 %371
-%372 = OpConvertSToF  %7  %285
-%373 = OpCompositeConstruct  %281  %280 %372
-%374 = OpSampledImage  %317  %275 %277
-%375 = OpImageSampleImplicitLod  %23  %374 %373 Bias|ConstOffset %284 %30
-%376 = OpLoad  %23  %286
-%377 = OpFAdd  %23  %376 %375
-OpStore %286 %377
-%379 = OpConvertUToF  %7  %198
-%380 = OpCompositeConstruct  %23  %282 %379
-%381 = OpSampledImage  %378  %276 %277
-%382 = OpImageSampleImplicitLod  %23  %381 %380
-%383 = OpLoad  %23  %286
-%384 = OpFAdd  %23  %383 %382
-OpStore %286 %384
-%385 = OpConvertUToF  %7  %198
-%386 = OpCompositeConstruct  %23  %282 %385
-%387 = OpSampledImage  %378  %276 %277
-%388 = OpImageSampleExplicitLod  %23  %387 %386 Lod %283
-%389 = OpLoad  %23  %286
-%390 = OpFAdd  %23  %389 %388
-OpStore %286 %390
-%391 = OpConvertUToF  %7  %198
-%392 = OpCompositeConstruct  %23  %282 %391
-%393 = OpSampledImage  %378  %276 %277
-%394 = OpImageSampleImplicitLod  %23  %393 %392 Bias %284
-%395 = OpLoad  %23  %286
-%396 = OpFAdd  %23  %395 %394
-OpStore %286 %396
-%397 = OpConvertSToF  %7  %285
-%398 = OpCompositeConstruct  %23  %282 %397
-%399 = OpSampledImage  %378  %276 %277
-%400 = OpImageSampleImplicitLod  %23  %399 %398
-%401 = OpLoad  %23  %286
-%402 = OpFAdd  %23  %401 %400
-OpStore %286 %402
-%403 = OpConvertSToF  %7  %285
-%404 = OpCompositeConstruct  %23  %282 %403
-%405 = OpSampledImage  %378  %276 %277
-%406 = OpImageSampleExplicitLod  %23  %405 %404 Lod %283
-%407 = OpLoad  %23  %286
-%408 = OpFAdd  %23  %407 %406
-OpStore %286 %408
-%409 = OpConvertSToF  %7  %285
-%410 = OpCompositeConstruct  %23  %282 %409
-%411 = OpSampledImage  %378  %276 %277
-%412 = OpImageSampleImplicitLod  %23  %411 %410 Bias %284
-%413 = OpLoad  %23  %286
-%414 = OpFAdd  %23  %413 %412
-OpStore %286 %414
-%415 = OpLoad  %23  %286
-OpStore %271 %415
+%293 = OpFunction  %2  None %100
+%291 = OpLabel
+%306 = OpVariable  %307  Function %308
+%294 = OpLoad  %15  %47
+%295 = OpLoad  %16  %49
+%296 = OpLoad  %18  %54
+%297 = OpLoad  %20  %58
+%298 = OpLoad  %24  %64
+OpBranch %309
+%309 = OpLabel
+%310 = OpCompositeExtract  %7  %301 0
+%312 = OpSampledImage  %311  %294 %298
+%313 = OpImageSampleImplicitLod  %23  %312 %310
+%314 = OpLoad  %23  %306
+%315 = OpFAdd  %23  %314 %313
+OpStore %306 %315
+%317 = OpSampledImage  %316  %295 %298
+%318 = OpImageSampleImplicitLod  %23  %317 %301
+%319 = OpLoad  %23  %306
+%320 = OpFAdd  %23  %319 %318
+OpStore %306 %320
+%321 = OpSampledImage  %316  %295 %298
+%322 = OpImageSampleImplicitLod  %23  %321 %301 ConstOffset %30
+%323 = OpLoad  %23  %306
+%324 = OpFAdd  %23  %323 %322
+OpStore %306 %324
+%325 = OpSampledImage  %316  %295 %298
+%326 = OpImageSampleExplicitLod  %23  %325 %301 Lod %304
+%327 = OpLoad  %23  %306
+%328 = OpFAdd  %23  %327 %326
+OpStore %306 %328
+%329 = OpSampledImage  %316  %295 %298
+%330 = OpImageSampleExplicitLod  %23  %329 %301 Lod|ConstOffset %304 %30
+%331 = OpLoad  %23  %306
+%332 = OpFAdd  %23  %331 %330
+OpStore %306 %332
+%333 = OpSampledImage  %316  %295 %298
+%334 = OpImageSampleImplicitLod  %23  %333 %301 Bias|ConstOffset %305 %30
+%335 = OpLoad  %23  %306
+%336 = OpFAdd  %23  %335 %334
+OpStore %306 %336
+%338 = OpConvertUToF  %7  %219
+%339 = OpCompositeConstruct  %302  %301 %338
+%340 = OpSampledImage  %337  %296 %298
+%341 = OpImageSampleImplicitLod  %23  %340 %339
+%342 = OpLoad  %23  %306
+%343 = OpFAdd  %23  %342 %341
+OpStore %306 %343
+%344 = OpConvertUToF  %7  %219
+%345 = OpCompositeConstruct  %302  %301 %344
+%346 = OpSampledImage  %337  %296 %298
+%347 = OpImageSampleImplicitLod  %23  %346 %345 ConstOffset %30
+%348 = OpLoad  %23  %306
+%349 = OpFAdd  %23  %348 %347
+OpStore %306 %349
+%350 = OpConvertUToF  %7  %219
+%351 = OpCompositeConstruct  %302  %301 %350
+%352 = OpSampledImage  %337  %296 %298
+%353 = OpImageSampleExplicitLod  %23  %352 %351 Lod %304
+%354 = OpLoad  %23  %306
+%355 = OpFAdd  %23  %354 %353
+OpStore %306 %355
+%356 = OpConvertUToF  %7  %219
+%357 = OpCompositeConstruct  %302  %301 %356
+%358 = OpSampledImage  %337  %296 %298
+%359 = OpImageSampleExplicitLod  %23  %358 %357 Lod|ConstOffset %304 %30
+%360 = OpLoad  %23  %306
+%361 = OpFAdd  %23  %360 %359
+OpStore %306 %361
+%362 = OpConvertUToF  %7  %219
+%363 = OpCompositeConstruct  %302  %301 %362
+%364 = OpSampledImage  %337  %296 %298
+%365 = OpImageSampleImplicitLod  %23  %364 %363 Bias|ConstOffset %305 %30
+%366 = OpLoad  %23  %306
+%367 = OpFAdd  %23  %366 %365
+OpStore %306 %367
+%368 = OpConvertSToF  %7  %81
+%369 = OpCompositeConstruct  %302  %301 %368
+%370 = OpSampledImage  %337  %296 %298
+%371 = OpImageSampleImplicitLod  %23  %370 %369
+%372 = OpLoad  %23  %306
+%373 = OpFAdd  %23  %372 %371
+OpStore %306 %373
+%374 = OpConvertSToF  %7  %81
+%375 = OpCompositeConstruct  %302  %301 %374
+%376 = OpSampledImage  %337  %296 %298
+%377 = OpImageSampleImplicitLod  %23  %376 %375 ConstOffset %30
+%378 = OpLoad  %23  %306
+%379 = OpFAdd  %23  %378 %377
+OpStore %306 %379
+%380 = OpConvertSToF  %7  %81
+%381 = OpCompositeConstruct  %302  %301 %380
+%382 = OpSampledImage  %337  %296 %298
+%383 = OpImageSampleExplicitLod  %23  %382 %381 Lod %304
+%384 = OpLoad  %23  %306
+%385 = OpFAdd  %23  %384 %383
+OpStore %306 %385
+%386 = OpConvertSToF  %7  %81
+%387 = OpCompositeConstruct  %302  %301 %386
+%388 = OpSampledImage  %337  %296 %298
+%389 = OpImageSampleExplicitLod  %23  %388 %387 Lod|ConstOffset %304 %30
+%390 = OpLoad  %23  %306
+%391 = OpFAdd  %23  %390 %389
+OpStore %306 %391
+%392 = OpConvertSToF  %7  %81
+%393 = OpCompositeConstruct  %302  %301 %392
+%394 = OpSampledImage  %337  %296 %298
+%395 = OpImageSampleImplicitLod  %23  %394 %393 Bias|ConstOffset %305 %30
+%396 = OpLoad  %23  %306
+%397 = OpFAdd  %23  %396 %395
+OpStore %306 %397
+%399 = OpConvertUToF  %7  %219
+%400 = OpCompositeConstruct  %23  %303 %399
+%401 = OpSampledImage  %398  %297 %298
+%402 = OpImageSampleImplicitLod  %23  %401 %400
+%403 = OpLoad  %23  %306
+%404 = OpFAdd  %23  %403 %402
+OpStore %306 %404
+%405 = OpConvertUToF  %7  %219
+%406 = OpCompositeConstruct  %23  %303 %405
+%407 = OpSampledImage  %398  %297 %298
+%408 = OpImageSampleExplicitLod  %23  %407 %406 Lod %304
+%409 = OpLoad  %23  %306
+%410 = OpFAdd  %23  %409 %408
+OpStore %306 %410
+%411 = OpConvertUToF  %7  %219
+%412 = OpCompositeConstruct  %23  %303 %411
+%413 = OpSampledImage  %398  %297 %298
+%414 = OpImageSampleImplicitLod  %23  %413 %412 Bias %305
+%415 = OpLoad  %23  %306
+%416 = OpFAdd  %23  %415 %414
+OpStore %306 %416
+%417 = OpConvertSToF  %7  %81
+%418 = OpCompositeConstruct  %23  %303 %417
+%419 = OpSampledImage  %398  %297 %298
+%420 = OpImageSampleImplicitLod  %23  %419 %418
+%421 = OpLoad  %23  %306
+%422 = OpFAdd  %23  %421 %420
+OpStore %306 %422
+%423 = OpConvertSToF  %7  %81
+%424 = OpCompositeConstruct  %23  %303 %423
+%425 = OpSampledImage  %398  %297 %298
+%426 = OpImageSampleExplicitLod  %23  %425 %424 Lod %304
+%427 = OpLoad  %23  %306
+%428 = OpFAdd  %23  %427 %426
+OpStore %306 %428
+%429 = OpConvertSToF  %7  %81
+%430 = OpCompositeConstruct  %23  %303 %429
+%431 = OpSampledImage  %398  %297 %298
+%432 = OpImageSampleImplicitLod  %23  %431 %430 Bias %305
+%433 = OpLoad  %23  %306
+%434 = OpFAdd  %23  %433 %432
+OpStore %306 %434
+%435 = OpLoad  %23  %306
+OpStore %292 %435
 OpReturn
 OpFunctionEnd
-%419 = OpFunction  %2  None %79
-%416 = OpLabel
-%424 = OpVariable  %425  Function %426
-%420 = OpLoad  %24  %66
-%421 = OpLoad  %25  %68
-%422 = OpLoad  %26  %70
-%423 = OpLoad  %27  %72
-OpBranch %427
-%427 = OpLabel
-%429 = OpSampledImage  %428  %421 %420
-%430 = OpImageSampleDrefImplicitLod  %7  %429 %280 %278
-%431 = OpLoad  %7  %424
-%432 = OpFAdd  %7  %431 %430
-OpStore %424 %432
-%434 = OpConvertUToF  %7  %198
-%435 = OpCompositeConstruct  %281  %280 %434
-%436 = OpSampledImage  %433  %422 %420
-%437 = OpImageSampleDrefImplicitLod  %7  %436 %435 %278
-%438 = OpLoad  %7  %424
-%439 = OpFAdd  %7  %438 %437
-OpStore %424 %439
-%440 = OpConvertSToF  %7  %285
-%441 = OpCompositeConstruct  %281  %280 %440
-%442 = OpSampledImage  %433  %422 %420
-%443 = OpImageSampleDrefImplicitLod  %7  %442 %441 %278
-%444 = OpLoad  %7  %424
-%445 = OpFAdd  %7  %444 %443
-OpStore %424 %445
-%447 = OpSampledImage  %446  %423 %420
-%448 = OpImageSampleDrefImplicitLod  %7  %447 %282 %278
-%449 = OpLoad  %7  %424
-%450 = OpFAdd  %7  %449 %448
-OpStore %424 %450
-%451 = OpSampledImage  %428  %421 %420
-%452 = OpImageSampleDrefExplicitLod  %7  %451 %280 %278 Lod %453
-%454 = OpLoad  %7  %424
-%455 = OpFAdd  %7  %454 %452
-OpStore %424 %455
-%456 = OpConvertUToF  %7  %198
-%457 = OpCompositeConstruct  %281  %280 %456
-%458 = OpSampledImage  %433  %422 %420
-%459 = OpImageSampleDrefExplicitLod  %7  %458 %457 %278 Lod %453
-%460 = OpLoad  %7  %424
-%461 = OpFAdd  %7  %460 %459
-OpStore %424 %461
-%462 = OpConvertSToF  %7  %285
-%463 = OpCompositeConstruct  %281  %280 %462
-%464 = OpSampledImage  %433  %422 %420
-%465 = OpImageSampleDrefExplicitLod  %7  %464 %463 %278 Lod %453
-%466 = OpLoad  %7  %424
-%467 = OpFAdd  %7  %466 %465
-OpStore %424 %467
-%468 = OpSampledImage  %446  %423 %420
-%469 = OpImageSampleDrefExplicitLod  %7  %468 %282 %278 Lod %453
-%470 = OpLoad  %7  %424
-%471 = OpFAdd  %7  %470 %469
-OpStore %424 %471
-%472 = OpLoad  %7  %424
-OpStore %417 %472
+%439 = OpFunction  %2  None %100
+%436 = OpLabel
+%444 = OpVariable  %445  Function %446
+%440 = OpLoad  %24  %66
+%441 = OpLoad  %25  %68
+%442 = OpLoad  %26  %70
+%443 = OpLoad  %27  %72
+OpBranch %447
+%447 = OpLabel
+%449 = OpSampledImage  %448  %441 %440
+%450 = OpImageSampleDrefImplicitLod  %7  %449 %301 %299
+%451 = OpLoad  %7  %444
+%452 = OpFAdd  %7  %451 %450
+OpStore %444 %452
+%454 = OpConvertUToF  %7  %219
+%455 = OpCompositeConstruct  %302  %301 %454
+%456 = OpSampledImage  %453  %442 %440
+%457 = OpImageSampleDrefImplicitLod  %7  %456 %455 %299
+%458 = OpLoad  %7  %444
+%459 = OpFAdd  %7  %458 %457
+OpStore %444 %459
+%460 = OpConvertSToF  %7  %81
+%461 = OpCompositeConstruct  %302  %301 %460
+%462 = OpSampledImage  %453  %442 %440
+%463 = OpImageSampleDrefImplicitLod  %7  %462 %461 %299
+%464 = OpLoad  %7  %444
+%465 = OpFAdd  %7  %464 %463
+OpStore %444 %465
+%467 = OpSampledImage  %466  %443 %440
+%468 = OpImageSampleDrefImplicitLod  %7  %467 %303 %299
+%469 = OpLoad  %7  %444
+%470 = OpFAdd  %7  %469 %468
+OpStore %444 %470
+%471 = OpSampledImage  %448  %441 %440
+%472 = OpImageSampleDrefExplicitLod  %7  %471 %301 %299 Lod %473
+%474 = OpLoad  %7  %444
+%475 = OpFAdd  %7  %474 %472
+OpStore %444 %475
+%476 = OpConvertUToF  %7  %219
+%477 = OpCompositeConstruct  %302  %301 %476
+%478 = OpSampledImage  %453  %442 %440
+%479 = OpImageSampleDrefExplicitLod  %7  %478 %477 %299 Lod %473
+%480 = OpLoad  %7  %444
+%481 = OpFAdd  %7  %480 %479
+OpStore %444 %481
+%482 = OpConvertSToF  %7  %81
+%483 = OpCompositeConstruct  %302  %301 %482
+%484 = OpSampledImage  %453  %442 %440
+%485 = OpImageSampleDrefExplicitLod  %7  %484 %483 %299 Lod %473
+%486 = OpLoad  %7  %444
+%487 = OpFAdd  %7  %486 %485
+OpStore %444 %487
+%488 = OpSampledImage  %466  %443 %440
+%489 = OpImageSampleDrefExplicitLod  %7  %488 %303 %299 Lod %473
+%490 = OpLoad  %7  %444
+%491 = OpFAdd  %7  %490 %489
+OpStore %444 %491
+%492 = OpLoad  %7  %444
+OpStore %437 %492
 OpReturn
 OpFunctionEnd
-%475 = OpFunction  %2  None %79
-%473 = OpLabel
-%476 = OpLoad  %16  %49
-%477 = OpLoad  %3  %51
-%478 = OpLoad  %17  %52
-%479 = OpLoad  %24  %64
-%480 = OpLoad  %24  %66
-%481 = OpLoad  %25  %68
-OpBranch %482
-%482 = OpLabel
-%483 = OpSampledImage  %296  %476 %479
-%484 = OpImageGather  %23  %483 %280 %485
-%486 = OpSampledImage  %296  %476 %479
-%487 = OpImageGather  %23  %486 %280 %488 ConstOffset %30
-%489 = OpSampledImage  %428  %481 %480
-%490 = OpImageDrefGather  %23  %489 %280 %278
-%491 = OpSampledImage  %428  %481 %480
-%492 = OpImageDrefGather  %23  %491 %280 %278 ConstOffset %30
-%494 = OpSampledImage  %493  %477 %479
-%495 = OpImageGather  %98  %494 %280 %198
-%498 = OpSampledImage  %497  %478 %479
-%499 = OpImageGather  %496  %498 %280 %198
-%500 = OpConvertUToF  %23  %495
-%501 = OpConvertSToF  %23  %499
-%502 = OpFAdd  %23  %500 %501
-%503 = OpFAdd  %23  %484 %487
-%504 = OpFAdd  %23  %503 %490
-%505 = OpFAdd  %23  %504 %492
-%506 = OpFAdd  %23  %505 %502
-OpStore %474 %506
+%495 = OpFunction  %2  None %100
+%493 = OpLabel
+%496 = OpLoad  %16  %49
+%497 = OpLoad  %3  %51
+%498 = OpLoad  %17  %52
+%499 = OpLoad  %24  %64
+%500 = OpLoad  %24  %66
+%501 = OpLoad  %25  %68
+OpBranch %502
+%502 = OpLabel
+%503 = OpSampledImage  %316  %496 %499
+%504 = OpImageGather  %23  %503 %301 %505
+%506 = OpSampledImage  %316  %496 %499
+%507 = OpImageGather  %23  %506 %301 %508 ConstOffset %30
+%509 = OpSampledImage  %448  %501 %500
+%510 = OpImageDrefGather  %23  %509 %301 %299
+%511 = OpSampledImage  %448  %501 %500
+%512 = OpImageDrefGather  %23  %511 %301 %299 ConstOffset %30
+%514 = OpSampledImage  %513  %497 %499
+%515 = OpImageGather  %119  %514 %301 %219
+%518 = OpSampledImage  %517  %498 %499
+%519 = OpImageGather  %516  %518 %301 %219
+%520 = OpConvertUToF  %23  %515
+%521 = OpConvertSToF  %23  %519
+%522 = OpFAdd  %23  %520 %521
+%523 = OpFAdd  %23  %504 %507
+%524 = OpFAdd  %23  %523 %510
+%525 = OpFAdd  %23  %524 %512
+%526 = OpFAdd  %23  %525 %522
+OpStore %494 %526
 OpReturn
 OpFunctionEnd
-%509 = OpFunction  %2  None %79
-%507 = OpLabel
-%510 = OpLoad  %24  %64
-%511 = OpLoad  %25  %68
-OpBranch %512
-%512 = OpLabel
-%513 = OpSampledImage  %428  %511 %510
-%514 = OpImageSampleImplicitLod  %23  %513 %280
-%515 = OpCompositeExtract  %7  %514 0
-%516 = OpSampledImage  %428  %511 %510
-%517 = OpImageGather  %23  %516 %280 %198
-%518 = OpSampledImage  %428  %511 %510
-%520 = OpConvertSToF  %7  %29
-%519 = OpImageSampleExplicitLod  %23  %518 %280 Lod %520
-%521 = OpCompositeExtract  %7  %519 0
-%522 = OpCompositeConstruct  %23  %515 %515 %515 %515
-%523 = OpFAdd  %23  %522 %517
-%524 = OpCompositeConstruct  %23  %521 %521 %521 %521
-%525 = OpFAdd  %23  %523 %524
-OpStore %508 %525
+%529 = OpFunction  %2  None %100
+%527 = OpLabel
+%530 = OpLoad  %24  %64
+%531 = OpLoad  %25  %68
+OpBranch %532
+%532 = OpLabel
+%533 = OpSampledImage  %448  %531 %530
+%534 = OpImageSampleImplicitLod  %23  %533 %301
+%535 = OpCompositeExtract  %7  %534 0
+%536 = OpSampledImage  %448  %531 %530
+%537 = OpImageGather  %23  %536 %301 %219
+%538 = OpSampledImage  %448  %531 %530
+%540 = OpConvertSToF  %7  %29
+%539 = OpImageSampleExplicitLod  %23  %538 %301 Lod %540
+%541 = OpCompositeExtract  %7  %539 0
+%542 = OpCompositeConstruct  %23  %535 %535 %535 %535
+%543 = OpFAdd  %23  %542 %537
+%544 = OpCompositeConstruct  %23  %541 %541 %541 %541
+%545 = OpFAdd  %23  %543 %544
+OpStore %528 %545
 OpReturn
 OpFunctionEnd
\ No newline at end of file
diff --git a/naga/tests/out/spv/operators.spvasm b/naga/tests/out/spv/operators.spvasm
index 766f45528b..4ceea6ab71 100644
--- a/naga/tests/out/spv/operators.spvasm
+++ b/naga/tests/out/spv/operators.spvasm
@@ -1,13 +1,13 @@
 ; SPIR-V
 ; Version: 1.1
 ; Generator: rspirv
-; Bound: 389
+; Bound: 528
 OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %374 "main" %371
-OpExecutionMode %374 LocalSize 1 1 1
-OpDecorate %371 BuiltIn WorkgroupId
+OpEntryPoint GLCompute %513 "main" %510
+OpExecutionMode %513 LocalSize 1 1 1
+OpDecorate %510 BuiltIn WorkgroupId
 %2 = OpTypeVoid
 %3 = OpTypeFloat 32
 %4 = OpTypeVector %3 4
@@ -38,55 +38,73 @@ OpDecorate %371 BuiltIn WorkgroupId
 %31 = OpConstantComposite  %7  %30 %30 %30 %30
 %32 = OpConstant  %3  0.1
 %33 = OpConstantComposite  %6  %29 %29 %29 %29
-%57 = OpTypeFunction %4 %3 %5
-%58 = OpConstant  %3  2.0
-%59 = OpConstantComposite  %9  %58 %58
-%60 = OpConstant  %3  4.0
-%61 = OpConstantComposite  %9  %60 %60
-%62 = OpConstant  %3  8.0
-%63 = OpConstantComposite  %9  %62 %62
-%64 = OpConstant  %5  2
-%65 = OpConstantComposite  %6  %64 %64 %64 %64
-%78 = OpTypeFunction %9
-%79 = OpConstantComposite  %9  %17 %17
-%80 = OpConstant  %3  3.0
+%54 = OpTypeFunction %6 %6 %6
+%58 = OpConstantComposite  %6  %29 %29 %29 %29
+%60 = OpConstant  %5  -2147483648
+%61 = OpConstant  %5  -1
+%62 = OpConstantComposite  %6  %60 %60 %60 %60
+%63 = OpConstantComposite  %6  %61 %61 %61 %61
+%68 = OpConstantComposite  %6  %23 %23 %23 %23
+%75 = OpTypeFunction %4 %3 %5
+%76 = OpConstant  %3  2.0
+%77 = OpConstantComposite  %9  %76 %76
+%78 = OpConstant  %3  4.0
+%79 = OpConstantComposite  %9  %78 %78
+%80 = OpConstant  %3  8.0
 %81 = OpConstantComposite  %9  %80 %80
-%83 = OpTypePointer Function %9
-%95 = OpTypeFunction %10 %10
-%97 = OpTypeVector %8 3
-%98 = OpConstantComposite  %10  %19 %19 %19
-%100 = OpConstantComposite  %10  %17 %17 %17
-%104 = OpTypeFunction %2
-%105 = OpTypeVector %8 2
-%106 = OpConstantComposite  %105  %28 %28
-%107 = OpConstantComposite  %97  %28 %28 %28
-%108 = OpConstantComposite  %97  %30 %30 %30
-%109 = OpConstantComposite  %7  %28 %28 %28 %28
-%110 = OpConstantComposite  %7  %30 %30 %30 %30
-%122 = OpConstant  %16  1
-%123 = OpConstant  %16  2
-%124 = OpTypeVector %5 2
-%125 = OpConstantComposite  %124  %23 %23
-%126 = OpConstantComposite  %124  %64 %64
-%127 = OpConstantComposite  %15  %123 %123 %123
-%128 = OpConstantComposite  %15  %122 %122 %122
-%129 = OpConstantComposite  %4  %58 %58 %58 %58
-%130 = OpConstantComposite  %4  %17 %17 %17 %17
-%131 = OpTypeVector %16 2
-%132 = OpConstantComposite  %131  %123 %123
-%133 = OpConstantComposite  %131  %122 %122
-%134 = OpConstantNull  %11
-%135 = OpConstantNull  %12
-%136 = OpConstantComposite  %10  %58 %58 %58
-%137 = OpConstantNull  %13
-%301 = OpConstantNull  %14
-%303 = OpTypePointer Function %5
-%304 = OpConstantNull  %5
-%306 = OpTypePointer Function %14
-%334 = OpTypePointer Function %5
-%372 = OpTypePointer Input %15
-%371 = OpVariable  %372  Input
-%375 = OpConstantComposite  %10  %17 %17 %17
+%82 = OpConstant  %5  2
+%83 = OpConstantComposite  %6  %82 %82 %82 %82
+%96 = OpTypeFunction %9
+%97 = OpConstantComposite  %9  %17 %17
+%98 = OpConstant  %3  3.0
+%99 = OpConstantComposite  %9  %98 %98
+%101 = OpTypePointer Function %9
+%113 = OpTypeFunction %10 %10
+%115 = OpTypeVector %8 3
+%116 = OpConstantComposite  %10  %19 %19 %19
+%118 = OpConstantComposite  %10  %17 %17 %17
+%122 = OpTypeFunction %2
+%123 = OpTypeVector %8 2
+%124 = OpConstantComposite  %123  %28 %28
+%125 = OpConstantComposite  %115  %28 %28 %28
+%126 = OpConstantComposite  %115  %30 %30 %30
+%127 = OpConstantComposite  %7  %28 %28 %28 %28
+%128 = OpConstantComposite  %7  %30 %30 %30 %30
+%139 = OpTypeFunction %5 %5 %5
+%151 = OpTypeFunction %16 %16 %16
+%155 = OpConstant  %16  0
+%157 = OpConstant  %16  1
+%160 = OpTypeVector %5 2
+%162 = OpTypeFunction %160 %160 %160
+%166 = OpConstantComposite  %160  %29 %29
+%168 = OpConstantComposite  %160  %60 %60
+%169 = OpConstantComposite  %160  %61 %61
+%174 = OpConstantComposite  %160  %23 %23
+%178 = OpTypeFunction %15 %15 %15
+%182 = OpConstantComposite  %15  %155 %155 %155
+%184 = OpConstantComposite  %15  %157 %157 %157
+%223 = OpTypeVector %16 2
+%225 = OpTypeFunction %223 %223 %223
+%229 = OpConstantComposite  %223  %155 %155
+%231 = OpConstantComposite  %223  %157 %157
+%243 = OpConstant  %16  2
+%244 = OpConstantComposite  %160  %82 %82
+%245 = OpConstantComposite  %15  %243 %243 %243
+%246 = OpConstantComposite  %4  %76 %76 %76 %76
+%247 = OpConstantComposite  %4  %17 %17 %17 %17
+%248 = OpConstantComposite  %223  %243 %243
+%249 = OpConstantNull  %11
+%250 = OpConstantNull  %12
+%251 = OpConstantComposite  %10  %76 %76 %76
+%252 = OpConstantNull  %13
+%416 = OpConstantNull  %14
+%418 = OpTypePointer Function %5
+%419 = OpConstantNull  %5
+%421 = OpTypePointer Function %14
+%449 = OpTypePointer Function %5
+%511 = OpTypePointer Input %15
+%510 = OpVariable  %511  Input
+%514 = OpConstantComposite  %10  %17 %17 %17
 %26 = OpFunction  %4  None %27
 %25 = OpLabel
 OpBranch %34
@@ -111,340 +129,483 @@ OpBranch %34
 %52 = OpFAdd  %4  %51 %43
 OpReturnValue %52
 OpFunctionEnd
-%56 = OpFunction  %4  None %57
-%54 = OpFunctionParameter  %3
-%55 = OpFunctionParameter  %5
-%53 = OpLabel
-OpBranch %66
-%66 = OpLabel
-%67 = OpCompositeConstruct  %9  %54 %54
-%68 = OpFAdd  %9  %59 %67
-%69 = OpFSub  %9  %68 %61
-%70 = OpFDiv  %9  %69 %63
-%71 = OpCompositeConstruct  %6  %55 %55 %55 %55
-%72 = OpSRem  %6  %71 %65
-%73 = OpVectorShuffle  %4  %70 %70 0 1 0 1
-%74 = OpConvertSToF  %4  %72
-%75 = OpFAdd  %4  %73 %74
-OpReturnValue %75
+%53 = OpFunction  %6  None %54
+%55 = OpFunctionParameter  %6
+%56 = OpFunctionParameter  %6
+%57 = OpLabel
+%59 = OpIEqual  %7  %56 %58
+%64 = OpIEqual  %7  %55 %62
+%65 = OpIEqual  %7  %56 %63
+%66 = OpLogicalAnd  %7  %64 %65
+%67 = OpLogicalOr  %7  %59 %66
+%69 = OpSelect  %6  %67 %68 %56
+%70 = OpSRem  %6  %55 %69
+OpReturnValue %70
 OpFunctionEnd
-%77 = OpFunction  %9  None %78
-%76 = OpLabel
-%82 = OpVariable  %83  Function %59
+%74 = OpFunction  %4  None %75
+%72 = OpFunctionParameter  %3
+%73 = OpFunctionParameter  %5
+%71 = OpLabel
 OpBranch %84
 %84 = OpLabel
-%85 = OpLoad  %9  %82
-%86 = OpFAdd  %9  %85 %79
-OpStore %82 %86
-%87 = OpLoad  %9  %82
-%88 = OpFSub  %9  %87 %81
-OpStore %82 %88
-%89 = OpLoad  %9  %82
-%90 = OpFDiv  %9  %89 %61
-OpStore %82 %90
-%91 = OpLoad  %9  %82
-OpReturnValue %91
+%85 = OpCompositeConstruct  %9  %72 %72
+%86 = OpFAdd  %9  %77 %85
+%87 = OpFSub  %9  %86 %79
+%88 = OpFDiv  %9  %87 %81
+%89 = OpCompositeConstruct  %6  %73 %73 %73 %73
+%90 = OpFunctionCall  %6  %53 %89 %83
+%91 = OpVectorShuffle  %4  %88 %88 0 1 0 1
+%92 = OpConvertSToF  %4  %90
+%93 = OpFAdd  %4  %91 %92
+OpReturnValue %93
 OpFunctionEnd
-%94 = OpFunction  %10  None %95
-%93 = OpFunctionParameter  %10
-%92 = OpLabel
-OpBranch %96
-%96 = OpLabel
-%99 = OpFUnordNotEqual  %97  %93 %98
-%101 = OpSelect  %10  %99 %100 %98
-OpReturnValue %101
-OpFunctionEnd
-%103 = OpFunction  %2  None %104
+%95 = OpFunction  %9  None %96
+%94 = OpLabel
+%100 = OpVariable  %101  Function %77
+OpBranch %102
 %102 = OpLabel
-OpBranch %111
-%111 = OpLabel
-%112 = OpLogicalNot  %8  %28
-%113 = OpLogicalNot  %105  %106
-%114 = OpLogicalOr  %8  %28 %30
-%115 = OpLogicalAnd  %8  %28 %30
-%116 = OpLogicalOr  %8  %28 %30
-%117 = OpLogicalOr  %97  %107 %108
-%118 = OpLogicalAnd  %8  %28 %30
-%119 = OpLogicalAnd  %7  %109 %110
-OpReturn
+%103 = OpLoad  %9  %100
+%104 = OpFAdd  %9  %103 %97
+OpStore %100 %104
+%105 = OpLoad  %9  %100
+%106 = OpFSub  %9  %105 %99
+OpStore %100 %106
+%107 = OpLoad  %9  %100
+%108 = OpFDiv  %9  %107 %79
+OpStore %100 %108
+%109 = OpLoad  %9  %100
+OpReturnValue %109
 OpFunctionEnd
-%121 = OpFunction  %2  None %104
+%112 = OpFunction  %10  None %113
+%111 = OpFunctionParameter  %10
+%110 = OpLabel
+OpBranch %114
+%114 = OpLabel
+%117 = OpFUnordNotEqual  %115  %111 %116
+%119 = OpSelect  %10  %117 %118 %116
+OpReturnValue %119
+OpFunctionEnd
+%121 = OpFunction  %2  None %122
 %120 = OpLabel
-OpBranch %138
-%138 = OpLabel
-%139 = OpFNegate  %3  %17
-%140 = OpSNegate  %124  %125
-%141 = OpFNegate  %9  %79
-%142 = OpIAdd  %5  %64 %23
-%143 = OpIAdd  %16  %123 %122
-%144 = OpFAdd  %3  %58 %17
-%145 = OpIAdd  %124  %126 %125
-%146 = OpIAdd  %15  %127 %128
-%147 = OpFAdd  %4  %129 %130
-%148 = OpISub  %5  %64 %23
-%149 = OpISub  %16  %123 %122
-%150 = OpFSub  %3  %58 %17
-%151 = OpISub  %124  %126 %125
-%152 = OpISub  %15  %127 %128
-%153 = OpFSub  %4  %129 %130
-%154 = OpIMul  %5  %64 %23
-%155 = OpIMul  %16  %123 %122
-%156 = OpFMul  %3  %58 %17
-%157 = OpIMul  %124  %126 %125
-%158 = OpIMul  %15  %127 %128
-%159 = OpFMul  %4  %129 %130
-%160 = OpSDiv  %5  %64 %23
-%161 = OpUDiv  %16  %123 %122
-%162 = OpFDiv  %3  %58 %17
-%163 = OpSDiv  %124  %126 %125
-%164 = OpUDiv  %15  %127 %128
-%165 = OpFDiv  %4  %129 %130
-%166 = OpSRem  %5  %64 %23
-%167 = OpUMod  %16  %123 %122
-%168 = OpFRem  %3  %58 %17
-%169 = OpSRem  %124  %126 %125
-%170 = OpUMod  %15  %127 %128
-%171 = OpFRem  %4  %129 %130
-OpBranch %172
-%172 = OpLabel
-%174 = OpIAdd  %124  %126 %125
-%175 = OpIAdd  %124  %126 %125
-%176 = OpIAdd  %131  %132 %133
-%177 = OpIAdd  %131  %132 %133
-%178 = OpFAdd  %9  %59 %79
-%179 = OpFAdd  %9  %59 %79
-%180 = OpISub  %124  %126 %125
-%181 = OpISub  %124  %126 %125
-%182 = OpISub  %131  %132 %133
-%183 = OpISub  %131  %132 %133
-%184 = OpFSub  %9  %59 %79
-%185 = OpFSub  %9  %59 %79
-%187 = OpCompositeConstruct  %124  %23 %23
-%186 = OpIMul  %124  %126 %187
-%189 = OpCompositeConstruct  %124  %64 %64
-%188 = OpIMul  %124  %125 %189
-%191 = OpCompositeConstruct  %131  %122 %122
-%190 = OpIMul  %131  %132 %191
-%193 = OpCompositeConstruct  %131  %123 %123
-%192 = OpIMul  %131  %133 %193
-%194 = OpVectorTimesScalar  %9  %59 %17
-%195 = OpVectorTimesScalar  %9  %79 %58
-%196 = OpSDiv  %124  %126 %125
-%197 = OpSDiv  %124  %126 %125
-%198 = OpUDiv  %131  %132 %133
-%199 = OpUDiv  %131  %132 %133
-%200 = OpFDiv  %9  %59 %79
-%201 = OpFDiv  %9  %59 %79
-%202 = OpSRem  %124  %126 %125
-%203 = OpSRem  %124  %126 %125
-%204 = OpUMod  %131  %132 %133
-%205 = OpUMod  %131  %132 %133
-%206 = OpFRem  %9  %59 %79
-%207 = OpFRem  %9  %59 %79
-OpBranch %173
-%173 = OpLabel
-%209 = OpCompositeExtract  %10  %134 0
-%210 = OpCompositeExtract  %10  %134 0
-%211 = OpFAdd  %10  %209 %210
-%212 = OpCompositeExtract  %10  %134 1
-%213 = OpCompositeExtract  %10  %134 1
-%214 = OpFAdd  %10  %212 %213
-%215 = OpCompositeExtract  %10  %134 2
-%216 = OpCompositeExtract  %10  %134 2
-%217 = OpFAdd  %10  %215 %216
-%208 = OpCompositeConstruct  %11  %211 %214 %217
-%219 = OpCompositeExtract  %10  %134 0
-%220 = OpCompositeExtract  %10  %134 0
-%221 = OpFSub  %10  %219 %220
-%222 = OpCompositeExtract  %10  %134 1
-%223 = OpCompositeExtract  %10  %134 1
-%224 = OpFSub  %10  %222 %223
-%225 = OpCompositeExtract  %10  %134 2
-%226 = OpCompositeExtract  %10  %134 2
-%227 = OpFSub  %10  %225 %226
-%218 = OpCompositeConstruct  %11  %221 %224 %227
-%228 = OpMatrixTimesScalar  %11  %134 %17
-%229 = OpMatrixTimesScalar  %11  %134 %58
-%230 = OpMatrixTimesVector  %10  %135 %130
-%231 = OpVectorTimesMatrix  %4  %136 %135
-%232 = OpMatrixTimesMatrix  %11  %135 %137
+OpBranch %129
+%129 = OpLabel
+%130 = OpLogicalNot  %8  %28
+%131 = OpLogicalNot  %123  %124
+%132 = OpLogicalOr  %8  %28 %30
+%133 = OpLogicalAnd  %8  %28 %30
+%134 = OpLogicalOr  %8  %28 %30
+%135 = OpLogicalOr  %115  %125 %126
+%136 = OpLogicalAnd  %8  %28 %30
+%137 = OpLogicalAnd  %7  %127 %128
+OpReturn
+OpFunctionEnd
+%138 = OpFunction  %5  None %139
+%140 = OpFunctionParameter  %5
+%141 = OpFunctionParameter  %5
+%142 = OpLabel
+%143 = OpIEqual  %8  %141 %29
+%144 = OpIEqual  %8  %140 %60
+%145 = OpIEqual  %8  %141 %61
+%146 = OpLogicalAnd  %8  %144 %145
+%147 = OpLogicalOr  %8  %143 %146
+%148 = OpSelect  %5  %147 %23 %141
+%149 = OpSDiv  %5  %140 %148
+OpReturnValue %149
+OpFunctionEnd
+%150 = OpFunction  %16  None %151
+%152 = OpFunctionParameter  %16
+%153 = OpFunctionParameter  %16
+%154 = OpLabel
+%156 = OpIEqual  %8  %153 %155
+%158 = OpSelect  %16  %156 %157 %153
+%159 = OpUDiv  %16  %152 %158
+OpReturnValue %159
+OpFunctionEnd
+%161 = OpFunction  %160  None %162
+%163 = OpFunctionParameter  %160
+%164 = OpFunctionParameter  %160
+%165 = OpLabel
+%167 = OpIEqual  %123  %164 %166
+%170 = OpIEqual  %123  %163 %168
+%171 = OpIEqual  %123  %164 %169
+%172 = OpLogicalAnd  %123  %170 %171
+%173 = OpLogicalOr  %123  %167 %172
+%175 = OpSelect  %160  %173 %174 %164
+%176 = OpSDiv  %160  %163 %175
+OpReturnValue %176
+OpFunctionEnd
+%177 = OpFunction  %15  None %178
+%179 = OpFunctionParameter  %15
+%180 = OpFunctionParameter  %15
+%181 = OpLabel
+%183 = OpIEqual  %115  %180 %182
+%185 = OpSelect  %15  %183 %184 %180
+%186 = OpUDiv  %15  %179 %185
+OpReturnValue %186
+OpFunctionEnd
+%187 = OpFunction  %5  None %139
+%188 = OpFunctionParameter  %5
+%189 = OpFunctionParameter  %5
+%190 = OpLabel
+%191 = OpIEqual  %8  %189 %29
+%192 = OpIEqual  %8  %188 %60
+%193 = OpIEqual  %8  %189 %61
+%194 = OpLogicalAnd  %8  %192 %193
+%195 = OpLogicalOr  %8  %191 %194
+%196 = OpSelect  %5  %195 %23 %189
+%197 = OpSRem  %5  %188 %196
+OpReturnValue %197
+OpFunctionEnd
+%198 = OpFunction  %16  None %151
+%199 = OpFunctionParameter  %16
+%200 = OpFunctionParameter  %16
+%201 = OpLabel
+%202 = OpIEqual  %8  %200 %155
+%203 = OpSelect  %16  %202 %157 %200
+%204 = OpUMod  %16  %199 %203
+OpReturnValue %204
+OpFunctionEnd
+%205 = OpFunction  %160  None %162
+%206 = OpFunctionParameter  %160
+%207 = OpFunctionParameter  %160
+%208 = OpLabel
+%209 = OpIEqual  %123  %207 %166
+%210 = OpIEqual  %123  %206 %168
+%211 = OpIEqual  %123  %207 %169
+%212 = OpLogicalAnd  %123  %210 %211
+%213 = OpLogicalOr  %123  %209 %212
+%214 = OpSelect  %160  %213 %174 %207
+%215 = OpSRem  %160  %206 %214
+OpReturnValue %215
+OpFunctionEnd
+%216 = OpFunction  %15  None %178
+%217 = OpFunctionParameter  %15
+%218 = OpFunctionParameter  %15
+%219 = OpLabel
+%220 = OpIEqual  %115  %218 %182
+%221 = OpSelect  %15  %220 %184 %218
+%222 = OpUMod  %15  %217 %221
+OpReturnValue %222
+OpFunctionEnd
+%224 = OpFunction  %223  None %225
+%226 = OpFunctionParameter  %223
+%227 = OpFunctionParameter  %223
+%228 = OpLabel
+%230 = OpIEqual  %123  %227 %229
+%232 = OpSelect  %223  %230 %231 %227
+%233 = OpUDiv  %223  %226 %232
+OpReturnValue %233
+OpFunctionEnd
+%234 = OpFunction  %223  None %225
+%235 = OpFunctionParameter  %223
+%236 = OpFunctionParameter  %223
+%237 = OpLabel
+%238 = OpIEqual  %123  %236 %229
+%239 = OpSelect  %223  %238 %231 %236
+%240 = OpUMod  %223  %235 %239
+OpReturnValue %240
+OpFunctionEnd
+%242 = OpFunction  %2  None %122
+%241 = OpLabel
+OpBranch %253
+%253 = OpLabel
+%254 = OpFNegate  %3  %17
+%255 = OpSNegate  %160  %174
+%256 = OpFNegate  %9  %97
+%257 = OpIAdd  %5  %82 %23
+%258 = OpIAdd  %16  %243 %157
+%259 = OpFAdd  %3  %76 %17
+%260 = OpIAdd  %160  %244 %174
+%261 = OpIAdd  %15  %245 %184
+%262 = OpFAdd  %4  %246 %247
+%263 = OpISub  %5  %82 %23
+%264 = OpISub  %16  %243 %157
+%265 = OpFSub  %3  %76 %17
+%266 = OpISub  %160  %244 %174
+%267 = OpISub  %15  %245 %184
+%268 = OpFSub  %4  %246 %247
+%269 = OpIMul  %5  %82 %23
+%270 = OpIMul  %16  %243 %157
+%271 = OpFMul  %3  %76 %17
+%272 = OpIMul  %160  %244 %174
+%273 = OpIMul  %15  %245 %184
+%274 = OpFMul  %4  %246 %247
+%275 = OpFunctionCall  %5  %138 %82 %23
+%276 = OpFunctionCall  %16  %150 %243 %157
+%277 = OpFDiv  %3  %76 %17
+%278 = OpFunctionCall  %160  %161 %244 %174
+%279 = OpFunctionCall  %15  %177 %245 %184
+%280 = OpFDiv  %4  %246 %247
+%281 = OpFunctionCall  %5  %187 %82 %23
+%282 = OpFunctionCall  %16  %198 %243 %157
+%283 = OpFRem  %3  %76 %17
+%284 = OpFunctionCall  %160  %205 %244 %174
+%285 = OpFunctionCall  %15  %216 %245 %184
+%286 = OpFRem  %4  %246 %247
+OpBranch %287
+%287 = OpLabel
+%289 = OpIAdd  %160  %244 %174
+%290 = OpIAdd  %160  %244 %174
+%291 = OpIAdd  %223  %248 %231
+%292 = OpIAdd  %223  %248 %231
+%293 = OpFAdd  %9  %77 %97
+%294 = OpFAdd  %9  %77 %97
+%295 = OpISub  %160  %244 %174
+%296 = OpISub  %160  %244 %174
+%297 = OpISub  %223  %248 %231
+%298 = OpISub  %223  %248 %231
+%299 = OpFSub  %9  %77 %97
+%300 = OpFSub  %9  %77 %97
+%302 = OpCompositeConstruct  %160  %23 %23
+%301 = OpIMul  %160  %244 %302
+%304 = OpCompositeConstruct  %160  %82 %82
+%303 = OpIMul  %160  %174 %304
+%306 = OpCompositeConstruct  %223  %157 %157
+%305 = OpIMul  %223  %248 %306
+%308 = OpCompositeConstruct  %223  %243 %243
+%307 = OpIMul  %223  %231 %308
+%309 = OpVectorTimesScalar  %9  %77 %17
+%310 = OpVectorTimesScalar  %9  %97 %76
+%311 = OpFunctionCall  %160  %161 %244 %174
+%312 = OpFunctionCall  %160  %161 %244 %174
+%313 = OpFunctionCall  %223  %224 %248 %231
+%314 = OpFunctionCall  %223  %224 %248 %231
+%315 = OpFDiv  %9  %77 %97
+%316 = OpFDiv  %9  %77 %97
+%317 = OpFunctionCall  %160  %205 %244 %174
+%318 = OpFunctionCall  %160  %205 %244 %174
+%319 = OpFunctionCall  %223  %234 %248 %231
+%320 = OpFunctionCall  %223  %234 %248 %231
+%321 = OpFRem  %9  %77 %97
+%322 = OpFRem  %9  %77 %97
+OpBranch %288
+%288 = OpLabel
+%324 = OpCompositeExtract  %10  %249 0
+%325 = OpCompositeExtract  %10  %249 0
+%326 = OpFAdd  %10  %324 %325
+%327 = OpCompositeExtract  %10  %249 1
+%328 = OpCompositeExtract  %10  %249 1
+%329 = OpFAdd  %10  %327 %328
+%330 = OpCompositeExtract  %10  %249 2
+%331 = OpCompositeExtract  %10  %249 2
+%332 = OpFAdd  %10  %330 %331
+%323 = OpCompositeConstruct  %11  %326 %329 %332
+%334 = OpCompositeExtract  %10  %249 0
+%335 = OpCompositeExtract  %10  %249 0
+%336 = OpFSub  %10  %334 %335
+%337 = OpCompositeExtract  %10  %249 1
+%338 = OpCompositeExtract  %10  %249 1
+%339 = OpFSub  %10  %337 %338
+%340 = OpCompositeExtract  %10  %249 2
+%341 = OpCompositeExtract  %10  %249 2
+%342 = OpFSub  %10  %340 %341
+%333 = OpCompositeConstruct  %11  %336 %339 %342
+%343 = OpMatrixTimesScalar  %11  %249 %17
+%344 = OpMatrixTimesScalar  %11  %249 %76
+%345 = OpMatrixTimesVector  %10  %250 %247
+%346 = OpVectorTimesMatrix  %4  %251 %250
+%347 = OpMatrixTimesMatrix  %11  %250 %252
 OpReturn
 OpFunctionEnd
-%234 = OpFunction  %2  None %104
-%233 = OpLabel
-OpBranch %235
-%235 = OpLabel
-%236 = OpNot  %5  %23
-%237 = OpNot  %16  %122
-%238 = OpNot  %124  %125
-%239 = OpNot  %15  %128
-%240 = OpBitwiseOr  %5  %64 %23
-%241 = OpBitwiseOr  %16  %123 %122
-%242 = OpBitwiseOr  %124  %126 %125
-%243 = OpBitwiseOr  %15  %127 %128
-%244 = OpBitwiseAnd  %5  %64 %23
-%245 = OpBitwiseAnd  %16  %123 %122
-%246 = OpBitwiseAnd  %124  %126 %125
-%247 = OpBitwiseAnd  %15  %127 %128
-%248 = OpBitwiseXor  %5  %64 %23
-%249 = OpBitwiseXor  %16  %123 %122
-%250 = OpBitwiseXor  %124  %126 %125
-%251 = OpBitwiseXor  %15  %127 %128
-%252 = OpShiftLeftLogical  %5  %64 %122
-%253 = OpShiftLeftLogical  %16  %123 %122
-%254 = OpShiftLeftLogical  %124  %126 %133
-%255 = OpShiftLeftLogical  %15  %127 %128
-%256 = OpShiftRightArithmetic  %5  %64 %122
-%257 = OpShiftRightLogical  %16  %123 %122
-%258 = OpShiftRightArithmetic  %124  %126 %133
-%259 = OpShiftRightLogical  %15  %127 %128
+%349 = OpFunction  %2  None %122
+%348 = OpLabel
+OpBranch %350
+%350 = OpLabel
+%351 = OpNot  %5  %23
+%352 = OpNot  %16  %157
+%353 = OpNot  %160  %174
+%354 = OpNot  %15  %184
+%355 = OpBitwiseOr  %5  %82 %23
+%356 = OpBitwiseOr  %16  %243 %157
+%357 = OpBitwiseOr  %160  %244 %174
+%358 = OpBitwiseOr  %15  %245 %184
+%359 = OpBitwiseAnd  %5  %82 %23
+%360 = OpBitwiseAnd  %16  %243 %157
+%361 = OpBitwiseAnd  %160  %244 %174
+%362 = OpBitwiseAnd  %15  %245 %184
+%363 = OpBitwiseXor  %5  %82 %23
+%364 = OpBitwiseXor  %16  %243 %157
+%365 = OpBitwiseXor  %160  %244 %174
+%366 = OpBitwiseXor  %15  %245 %184
+%367 = OpShiftLeftLogical  %5  %82 %157
+%368 = OpShiftLeftLogical  %16  %243 %157
+%369 = OpShiftLeftLogical  %160  %244 %231
+%370 = OpShiftLeftLogical  %15  %245 %184
+%371 = OpShiftRightArithmetic  %5  %82 %157
+%372 = OpShiftRightLogical  %16  %243 %157
+%373 = OpShiftRightArithmetic  %160  %244 %231
+%374 = OpShiftRightLogical  %15  %245 %184
 OpReturn
 OpFunctionEnd
-%261 = OpFunction  %2  None %104
-%260 = OpLabel
-OpBranch %262
-%262 = OpLabel
-%263 = OpIEqual  %8  %64 %23
-%264 = OpIEqual  %8  %123 %122
-%265 = OpFOrdEqual  %8  %58 %17
-%266 = OpIEqual  %105  %126 %125
-%267 = OpIEqual  %97  %127 %128
-%268 = OpFOrdEqual  %7  %129 %130
-%269 = OpINotEqual  %8  %64 %23
-%270 = OpINotEqual  %8  %123 %122
-%271 = OpFOrdNotEqual  %8  %58 %17
-%272 = OpINotEqual  %105  %126 %125
-%273 = OpINotEqual  %97  %127 %128
-%274 = OpFOrdNotEqual  %7  %129 %130
-%275 = OpSLessThan  %8  %64 %23
-%276 = OpULessThan  %8  %123 %122
-%277 = OpFOrdLessThan  %8  %58 %17
-%278 = OpSLessThan  %105  %126 %125
-%279 = OpULessThan  %97  %127 %128
-%280 = OpFOrdLessThan  %7  %129 %130
-%281 = OpSLessThanEqual  %8  %64 %23
-%282 = OpULessThanEqual  %8  %123 %122
-%283 = OpFOrdLessThanEqual  %8  %58 %17
-%284 = OpSLessThanEqual  %105  %126 %125
-%285 = OpULessThanEqual  %97  %127 %128
-%286 = OpFOrdLessThanEqual  %7  %129 %130
-%287 = OpSGreaterThan  %8  %64 %23
-%288 = OpUGreaterThan  %8  %123 %122
-%289 = OpFOrdGreaterThan  %8  %58 %17
-%290 = OpSGreaterThan  %105  %126 %125
-%291 = OpUGreaterThan  %97  %127 %128
-%292 = OpFOrdGreaterThan  %7  %129 %130
-%293 = OpSGreaterThanEqual  %8  %64 %23
-%294 = OpUGreaterThanEqual  %8  %123 %122
-%295 = OpFOrdGreaterThanEqual  %8  %58 %17
-%296 = OpSGreaterThanEqual  %105  %126 %125
-%297 = OpUGreaterThanEqual  %97  %127 %128
-%298 = OpFOrdGreaterThanEqual  %7  %129 %130
+%376 = OpFunction  %2  None %122
+%375 = OpLabel
+OpBranch %377
+%377 = OpLabel
+%378 = OpIEqual  %8  %82 %23
+%379 = OpIEqual  %8  %243 %157
+%380 = OpFOrdEqual  %8  %76 %17
+%381 = OpIEqual  %123  %244 %174
+%382 = OpIEqual  %115  %245 %184
+%383 = OpFOrdEqual  %7  %246 %247
+%384 = OpINotEqual  %8  %82 %23
+%385 = OpINotEqual  %8  %243 %157
+%386 = OpFOrdNotEqual  %8  %76 %17
+%387 = OpINotEqual  %123  %244 %174
+%388 = OpINotEqual  %115  %245 %184
+%389 = OpFOrdNotEqual  %7  %246 %247
+%390 = OpSLessThan  %8  %82 %23
+%391 = OpULessThan  %8  %243 %157
+%392 = OpFOrdLessThan  %8  %76 %17
+%393 = OpSLessThan  %123  %244 %174
+%394 = OpULessThan  %115  %245 %184
+%395 = OpFOrdLessThan  %7  %246 %247
+%396 = OpSLessThanEqual  %8  %82 %23
+%397 = OpULessThanEqual  %8  %243 %157
+%398 = OpFOrdLessThanEqual  %8  %76 %17
+%399 = OpSLessThanEqual  %123  %244 %174
+%400 = OpULessThanEqual  %115  %245 %184
+%401 = OpFOrdLessThanEqual  %7  %246 %247
+%402 = OpSGreaterThan  %8  %82 %23
+%403 = OpUGreaterThan  %8  %243 %157
+%404 = OpFOrdGreaterThan  %8  %76 %17
+%405 = OpSGreaterThan  %123  %244 %174
+%406 = OpUGreaterThan  %115  %245 %184
+%407 = OpFOrdGreaterThan  %7  %246 %247
+%408 = OpSGreaterThanEqual  %8  %82 %23
+%409 = OpUGreaterThanEqual  %8  %243 %157
+%410 = OpFOrdGreaterThanEqual  %8  %76 %17
+%411 = OpSGreaterThanEqual  %123  %244 %174
+%412 = OpUGreaterThanEqual  %115  %245 %184
+%413 = OpFOrdGreaterThanEqual  %7  %246 %247
 OpReturn
 OpFunctionEnd
-%300 = OpFunction  %2  None %104
-%299 = OpLabel
-%302 = OpVariable  %303  Function %304
-%305 = OpVariable  %306  Function %301
-OpBranch %307
-%307 = OpLabel
-OpStore %302 %23
-%308 = OpLoad  %5  %302
-%309 = OpIAdd  %5  %308 %23
-OpStore %302 %309
-%310 = OpLoad  %5  %302
-%311 = OpISub  %5  %310 %23
-OpStore %302 %311
-%312 = OpLoad  %5  %302
-%313 = OpLoad  %5  %302
-%314 = OpIMul  %5  %313 %312
-OpStore %302 %314
-%315 = OpLoad  %5  %302
-%316 = OpLoad  %5  %302
-%317 = OpSDiv  %5  %316 %315
-OpStore %302 %317
-%318 = OpLoad  %5  %302
-%319 = OpSRem  %5  %318 %23
-OpStore %302 %319
-%320 = OpLoad  %5  %302
-%321 = OpBitwiseAnd  %5  %320 %29
-OpStore %302 %321
-%322 = OpLoad  %5  %302
-%323 = OpBitwiseOr  %5  %322 %29
-OpStore %302 %323
-%324 = OpLoad  %5  %302
-%325 = OpBitwiseXor  %5  %324 %29
-OpStore %302 %325
-%326 = OpLoad  %5  %302
-%327 = OpShiftLeftLogical  %5  %326 %123
-OpStore %302 %327
-%328 = OpLoad  %5  %302
-%329 = OpShiftRightArithmetic  %5  %328 %122
-OpStore %302 %329
-%330 = OpLoad  %5  %302
-%331 = OpIAdd  %5  %330 %23
-OpStore %302 %331
-%332 = OpLoad  %5  %302
-%333 = OpISub  %5  %332 %23
-OpStore %302 %333
-%335 = OpAccessChain  %334  %305 %122
-%336 = OpLoad  %5  %335
-%337 = OpIAdd  %5  %336 %23
-%338 = OpAccessChain  %334  %305 %122
-OpStore %338 %337
-%339 = OpAccessChain  %334  %305 %122
-%340 = OpLoad  %5  %339
-%341 = OpISub  %5  %340 %23
-%342 = OpAccessChain  %334  %305 %122
-OpStore %342 %341
+%415 = OpFunction  %2  None %122
+%414 = OpLabel
+%417 = OpVariable  %418  Function %419
+%420 = OpVariable  %421  Function %416
+OpBranch %422
+%422 = OpLabel
+OpStore %417 %23
+%423 = OpLoad  %5  %417
+%424 = OpIAdd  %5  %423 %23
+OpStore %417 %424
+%425 = OpLoad  %5  %417
+%426 = OpISub  %5  %425 %23
+OpStore %417 %426
+%427 = OpLoad  %5  %417
+%428 = OpLoad  %5  %417
+%429 = OpIMul  %5  %428 %427
+OpStore %417 %429
+%430 = OpLoad  %5  %417
+%431 = OpLoad  %5  %417
+%432 = OpFunctionCall  %5  %138 %431 %430
+OpStore %417 %432
+%433 = OpLoad  %5  %417
+%434 = OpFunctionCall  %5  %187 %433 %23
+OpStore %417 %434
+%435 = OpLoad  %5  %417
+%436 = OpBitwiseAnd  %5  %435 %29
+OpStore %417 %436
+%437 = OpLoad  %5  %417
+%438 = OpBitwiseOr  %5  %437 %29
+OpStore %417 %438
+%439 = OpLoad  %5  %417
+%440 = OpBitwiseXor  %5  %439 %29
+OpStore %417 %440
+%441 = OpLoad  %5  %417
+%442 = OpShiftLeftLogical  %5  %441 %243
+OpStore %417 %442
+%443 = OpLoad  %5  %417
+%444 = OpShiftRightArithmetic  %5  %443 %157
+OpStore %417 %444
+%445 = OpLoad  %5  %417
+%446 = OpIAdd  %5  %445 %23
+OpStore %417 %446
+%447 = OpLoad  %5  %417
+%448 = OpISub  %5  %447 %23
+OpStore %417 %448
+%450 = OpAccessChain  %449  %420 %157
+%451 = OpLoad  %5  %450
+%452 = OpIAdd  %5  %451 %23
+%453 = OpAccessChain  %449  %420 %157
+OpStore %453 %452
+%454 = OpAccessChain  %449  %420 %157
+%455 = OpLoad  %5  %454
+%456 = OpISub  %5  %455 %23
+%457 = OpAccessChain  %449  %420 %157
+OpStore %457 %456
 OpReturn
 OpFunctionEnd
-%344 = OpFunction  %2  None %104
-%343 = OpLabel
-OpBranch %345
-%345 = OpLabel
-%346 = OpSNegate  %5  %23
-%347 = OpSNegate  %5  %23
-%348 = OpSNegate  %5  %347
-%349 = OpSNegate  %5  %23
-%350 = OpSNegate  %5  %349
-%351 = OpSNegate  %5  %23
-%352 = OpSNegate  %5  %351
-%353 = OpSNegate  %5  %23
-%354 = OpSNegate  %5  %353
-%355 = OpSNegate  %5  %354
-%356 = OpSNegate  %5  %23
-%357 = OpSNegate  %5  %356
-%358 = OpSNegate  %5  %357
-%359 = OpSNegate  %5  %358
-%360 = OpSNegate  %5  %23
-%361 = OpSNegate  %5  %360
-%362 = OpSNegate  %5  %361
-%363 = OpSNegate  %5  %362
-%364 = OpSNegate  %5  %363
-%365 = OpSNegate  %5  %23
-%366 = OpSNegate  %5  %365
-%367 = OpSNegate  %5  %366
-%368 = OpSNegate  %5  %367
-%369 = OpSNegate  %5  %368
+%459 = OpFunction  %2  None %122
+%458 = OpLabel
+OpBranch %460
+%460 = OpLabel
+%461 = OpSNegate  %5  %23
+%462 = OpSNegate  %5  %23
+%463 = OpSNegate  %5  %462
+%464 = OpSNegate  %5  %23
+%465 = OpSNegate  %5  %464
+%466 = OpSNegate  %5  %23
+%467 = OpSNegate  %5  %466
+%468 = OpSNegate  %5  %23
+%469 = OpSNegate  %5  %468
+%470 = OpSNegate  %5  %469
+%471 = OpSNegate  %5  %23
+%472 = OpSNegate  %5  %471
+%473 = OpSNegate  %5  %472
+%474 = OpSNegate  %5  %473
+%475 = OpSNegate  %5  %23
+%476 = OpSNegate  %5  %475
+%477 = OpSNegate  %5  %476
+%478 = OpSNegate  %5  %477
+%479 = OpSNegate  %5  %478
+%480 = OpSNegate  %5  %23
+%481 = OpSNegate  %5  %480
+%482 = OpSNegate  %5  %481
+%483 = OpSNegate  %5  %482
+%484 = OpSNegate  %5  %483
+%485 = OpFNegate  %3  %17
+%486 = OpFNegate  %3  %17
+%487 = OpFNegate  %3  %486
+%488 = OpFNegate  %3  %17
+%489 = OpFNegate  %3  %488
+%490 = OpFNegate  %3  %17
+%491 = OpFNegate  %3  %490
+%492 = OpFNegate  %3  %17
+%493 = OpFNegate  %3  %492
+%494 = OpFNegate  %3  %493
+%495 = OpFNegate  %3  %17
+%496 = OpFNegate  %3  %495
+%497 = OpFNegate  %3  %496
+%498 = OpFNegate  %3  %497
+%499 = OpFNegate  %3  %17
+%500 = OpFNegate  %3  %499
+%501 = OpFNegate  %3  %500
+%502 = OpFNegate  %3  %501
+%503 = OpFNegate  %3  %502
+%504 = OpFNegate  %3  %17
+%505 = OpFNegate  %3  %504
+%506 = OpFNegate  %3  %505
+%507 = OpFNegate  %3  %506
+%508 = OpFNegate  %3  %507
 OpReturn
 OpFunctionEnd
-%374 = OpFunction  %2  None %104
-%370 = OpLabel
-%373 = OpLoad  %15  %371
-OpBranch %376
-%376 = OpLabel
-%377 = OpFunctionCall  %4  %26
-%378 = OpCompositeExtract  %16  %373 0
-%379 = OpConvertUToF  %3  %378
-%380 = OpCompositeExtract  %16  %373 1
-%381 = OpBitcast  %5  %380
-%382 = OpFunctionCall  %4  %56 %379 %381
-%383 = OpFunctionCall  %10  %94 %375
-%384 = OpFunctionCall  %2  %103
-%385 = OpFunctionCall  %2  %121
-%386 = OpFunctionCall  %2  %234
-%387 = OpFunctionCall  %2  %261
-%388 = OpFunctionCall  %2  %300
+%513 = OpFunction  %2  None %122
+%509 = OpLabel
+%512 = OpLoad  %15  %510
+OpBranch %515
+%515 = OpLabel
+%516 = OpFunctionCall  %4  %26
+%517 = OpCompositeExtract  %16  %512 0
+%518 = OpConvertUToF  %3  %517
+%519 = OpCompositeExtract  %16  %512 1
+%520 = OpBitcast  %5  %519
+%521 = OpFunctionCall  %4  %74 %518 %520
+%522 = OpFunctionCall  %10  %112 %514
+%523 = OpFunctionCall  %2  %121
+%524 = OpFunctionCall  %2  %242
+%525 = OpFunctionCall  %2  %349
+%526 = OpFunctionCall  %2  %376
+%527 = OpFunctionCall  %2  %415
 OpReturn
 OpFunctionEnd
\ No newline at end of file
diff --git a/naga/tests/out/spv/skybox.spvasm b/naga/tests/out/spv/skybox.spvasm
index 4d541321a9..7bc7b30f6d 100644
--- a/naga/tests/out/spv/skybox.spvasm
+++ b/naga/tests/out/spv/skybox.spvasm
@@ -1,13 +1,13 @@
 ; SPIR-V
 ; Version: 1.0
 ; Generator: rspirv
-; Bound: 98
+; Bound: 114
 OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %29 "vs_main" %22 %25 %27
-OpEntryPoint Fragment %90 "fs_main" %83 %86 %89
-OpExecutionMode %90 OriginUpperLeft
+OpEntryPoint Vertex %46 "vs_main" %39 %42 %44
+OpEntryPoint Fragment %106 "fs_main" %99 %102 %105
+OpExecutionMode %106 OriginUpperLeft
 OpMemberDecorate %6 0 Offset 0
 OpMemberDecorate %6 1 Offset 16
 OpMemberDecorate %8 0 Offset 0
@@ -24,12 +24,12 @@ OpDecorate %17 DescriptorSet 0
 OpDecorate %17 Binding 1
 OpDecorate %19 DescriptorSet 0
 OpDecorate %19 Binding 2
-OpDecorate %22 BuiltIn VertexIndex
-OpDecorate %25 BuiltIn Position
-OpDecorate %27 Location 0
-OpDecorate %83 BuiltIn FragCoord
-OpDecorate %86 Location 0
-OpDecorate %89 Location 0
+OpDecorate %39 BuiltIn VertexIndex
+OpDecorate %42 BuiltIn Position
+OpDecorate %44 Location 0
+OpDecorate %99 BuiltIn FragCoord
+OpDecorate %102 Location 0
+OpDecorate %105 Location 0
 %2 = OpTypeVoid
 %4 = OpTypeFloat 32
 %3 = OpTypeVector %4 4
@@ -49,91 +49,109 @@ OpDecorate %89 Location 0
 %17 = OpVariable  %18  UniformConstant
 %20 = OpTypePointer UniformConstant %13
 %19 = OpVariable  %20  UniformConstant
-%23 = OpTypePointer Input %9
-%22 = OpVariable  %23  Input
-%26 = OpTypePointer Output %3
-%25 = OpVariable  %26  Output
-%28 = OpTypePointer Output %5
-%27 = OpVariable  %28  Output
-%30 = OpTypeFunction %2
-%31 = OpTypePointer Uniform %8
-%32 = OpConstant  %9  0
-%34 = OpConstant  %10  2
+%22 = OpTypeFunction %10 %10 %10
+%26 = OpTypeBool
+%27 = OpConstant  %10  0
+%29 = OpConstant  %10  -2147483648
+%30 = OpConstant  %10  -1
 %35 = OpConstant  %10  1
-%36 = OpConstant  %4  4.0
-%37 = OpConstant  %4  1.0
-%38 = OpConstant  %4  0.0
-%40 = OpTypePointer Function %10
-%41 = OpConstantNull  %10
-%43 = OpConstantNull  %10
-%58 = OpTypePointer Uniform %7
-%59 = OpTypePointer Uniform %3
-%60 = OpConstant  %9  1
-%67 = OpConstant  %9  2
-%84 = OpTypePointer Input %3
-%83 = OpVariable  %84  Input
-%87 = OpTypePointer Input %5
-%86 = OpVariable  %87  Input
-%89 = OpVariable  %26  Output
-%95 = OpTypeSampledImage %12
-%29 = OpFunction  %2  None %30
-%21 = OpLabel
-%39 = OpVariable  %40  Function %41
-%42 = OpVariable  %40  Function %43
-%24 = OpLoad  %9  %22
-%33 = OpAccessChain  %31  %14 %32
-OpBranch %44
-%44 = OpLabel
-%45 = OpBitcast  %10  %24
-%46 = OpSDiv  %10  %45 %34
-OpStore %39 %46
-%47 = OpBitcast  %10  %24
-%48 = OpBitwiseAnd  %10  %47 %35
-OpStore %42 %48
-%49 = OpLoad  %10  %39
-%50 = OpConvertSToF  %4  %49
-%51 = OpFMul  %4  %50 %36
-%52 = OpFSub  %4  %51 %37
-%53 = OpLoad  %10  %42
-%54 = OpConvertSToF  %4  %53
-%55 = OpFMul  %4  %54 %36
-%56 = OpFSub  %4  %55 %37
-%57 = OpCompositeConstruct  %3  %52 %56 %38 %37
-%61 = OpAccessChain  %59  %33 %60 %32
-%62 = OpLoad  %3  %61
-%63 = OpVectorShuffle  %5  %62 %62 0 1 2
-%64 = OpAccessChain  %59  %33 %60 %60
-%65 = OpLoad  %3  %64
-%66 = OpVectorShuffle  %5  %65 %65 0 1 2
-%68 = OpAccessChain  %59  %33 %60 %67
-%69 = OpLoad  %3  %68
-%70 = OpVectorShuffle  %5  %69 %69 0 1 2
-%71 = OpCompositeConstruct  %11  %63 %66 %70
-%72 = OpTranspose  %11  %71
-%73 = OpAccessChain  %58  %33 %32
-%74 = OpLoad  %7  %73
-%75 = OpMatrixTimesVector  %3  %74 %57
-%76 = OpVectorShuffle  %5  %75 %75 0 1 2
-%77 = OpMatrixTimesVector  %5  %72 %76
-%78 = OpCompositeConstruct  %6  %57 %77
-%79 = OpCompositeExtract  %3  %78 0
-OpStore %25 %79
-%80 = OpCompositeExtract  %5  %78 1
-OpStore %27 %80
+%40 = OpTypePointer Input %9
+%39 = OpVariable  %40  Input
+%43 = OpTypePointer Output %3
+%42 = OpVariable  %43  Output
+%45 = OpTypePointer Output %5
+%44 = OpVariable  %45  Output
+%47 = OpTypeFunction %2
+%48 = OpTypePointer Uniform %8
+%49 = OpConstant  %9  0
+%51 = OpConstant  %10  2
+%52 = OpConstant  %4  4.0
+%53 = OpConstant  %4  1.0
+%54 = OpConstant  %4  0.0
+%56 = OpTypePointer Function %10
+%57 = OpConstantNull  %10
+%59 = OpConstantNull  %10
+%74 = OpTypePointer Uniform %7
+%75 = OpTypePointer Uniform %3
+%76 = OpConstant  %9  1
+%83 = OpConstant  %9  2
+%100 = OpTypePointer Input %3
+%99 = OpVariable  %100  Input
+%103 = OpTypePointer Input %5
+%102 = OpVariable  %103  Input
+%105 = OpVariable  %43  Output
+%111 = OpTypeSampledImage %12
+%21 = OpFunction  %10  None %22
+%23 = OpFunctionParameter  %10
+%24 = OpFunctionParameter  %10
+%25 = OpLabel
+%28 = OpIEqual  %26  %24 %27
+%31 = OpIEqual  %26  %23 %29
+%32 = OpIEqual  %26  %24 %30
+%33 = OpLogicalAnd  %26  %31 %32
+%34 = OpLogicalOr  %26  %28 %33
+%36 = OpSelect  %10  %34 %35 %24
+%37 = OpSDiv  %10  %23 %36
+OpReturnValue %37
+OpFunctionEnd
+%46 = OpFunction  %2  None %47
+%38 = OpLabel
+%55 = OpVariable  %56  Function %57
+%58 = OpVariable  %56  Function %59
+%41 = OpLoad  %9  %39
+%50 = OpAccessChain  %48  %14 %49
+OpBranch %60
+%60 = OpLabel
+%61 = OpBitcast  %10  %41
+%62 = OpFunctionCall  %10  %21 %61 %51
+OpStore %55 %62
+%63 = OpBitcast  %10  %41
+%64 = OpBitwiseAnd  %10  %63 %35
+OpStore %58 %64
+%65 = OpLoad  %10  %55
+%66 = OpConvertSToF  %4  %65
+%67 = OpFMul  %4  %66 %52
+%68 = OpFSub  %4  %67 %53
+%69 = OpLoad  %10  %58
+%70 = OpConvertSToF  %4  %69
+%71 = OpFMul  %4  %70 %52
+%72 = OpFSub  %4  %71 %53
+%73 = OpCompositeConstruct  %3  %68 %72 %54 %53
+%77 = OpAccessChain  %75  %50 %76 %49
+%78 = OpLoad  %3  %77
+%79 = OpVectorShuffle  %5  %78 %78 0 1 2
+%80 = OpAccessChain  %75  %50 %76 %76
+%81 = OpLoad  %3  %80
+%82 = OpVectorShuffle  %5  %81 %81 0 1 2
+%84 = OpAccessChain  %75  %50 %76 %83
+%85 = OpLoad  %3  %84
+%86 = OpVectorShuffle  %5  %85 %85 0 1 2
+%87 = OpCompositeConstruct  %11  %79 %82 %86
+%88 = OpTranspose  %11  %87
+%89 = OpAccessChain  %74  %50 %49
+%90 = OpLoad  %7  %89
+%91 = OpMatrixTimesVector  %3  %90 %73
+%92 = OpVectorShuffle  %5  %91 %91 0 1 2
+%93 = OpMatrixTimesVector  %5  %88 %92
+%94 = OpCompositeConstruct  %6  %73 %93
+%95 = OpCompositeExtract  %3  %94 0
+OpStore %42 %95
+%96 = OpCompositeExtract  %5  %94 1
+OpStore %44 %96
 OpReturn
 OpFunctionEnd
-%90 = OpFunction  %2  None %30
-%81 = OpLabel
-%85 = OpLoad  %3  %83
-%88 = OpLoad  %5  %86
-%82 = OpCompositeConstruct  %6  %85 %88
-%91 = OpLoad  %12  %17
-%92 = OpLoad  %13  %19
-OpBranch %93
-%93 = OpLabel
-%94 = OpCompositeExtract  %5  %82 1
-%96 = OpSampledImage  %95  %91 %92
-%97 = OpImageSampleImplicitLod  %3  %96 %94
-OpStore %89 %97
+%106 = OpFunction  %2  None %47
+%97 = OpLabel
+%101 = OpLoad  %3  %99
+%104 = OpLoad  %5  %102
+%98 = OpCompositeConstruct  %6  %101 %104
+%107 = OpLoad  %12  %17
+%108 = OpLoad  %13  %19
+OpBranch %109
+%109 = OpLabel
+%110 = OpCompositeExtract  %5  %98 1
+%112 = OpSampledImage  %111  %107 %108
+%113 = OpImageSampleImplicitLod  %3  %112 %110
+OpStore %105 %113
 OpReturn
 OpFunctionEnd
\ No newline at end of file
diff --git a/naga/tests/out/wgsl/operators.wgsl b/naga/tests/out/wgsl/operators.wgsl
index 4a6f1c8ab4..0927b831a0 100644
--- a/naga/tests/out/wgsl/operators.wgsl
+++ b/naga/tests/out/wgsl/operators.wgsl
@@ -234,14 +234,22 @@ fn assignment() {
 }
 
 fn negation_avoids_prefix_decrement() {
-    const p0_ = -(1i);
-    const p1_ = -(-(1i));
-    const p2_ = -(-(1i));
-    const p3_ = -(-(1i));
-    const p4_ = -(-(-(1i)));
-    const p5_ = -(-(-(-(1i))));
-    const p6_ = -(-(-(-(-(1i)))));
-    const p7_ = -(-(-(-(-(1i)))));
+    const i0_ = -(1i);
+    const i1_ = -(-(1i));
+    const i2_ = -(-(1i));
+    const i3_ = -(-(1i));
+    const i4_ = -(-(-(1i)));
+    const i5_ = -(-(-(-(1i))));
+    const i6_ = -(-(-(-(-(1i)))));
+    const i7_ = -(-(-(-(-(1i)))));
+    const f0_ = -(1f);
+    const f1_ = -(-(1f));
+    const f2_ = -(-(1f));
+    const f3_ = -(-(1f));
+    const f4_ = -(-(-(1f)));
+    const f5_ = -(-(-(-(1f))));
+    const f6_ = -(-(-(-(-(1f)))));
+    const f7_ = -(-(-(-(-(1f)))));
     return;
 }