tint: add precise float mod polyfill and enable it for HLSL

HLSL's % operator results in less precise results than expected.

Bug: tint:1799
Change-Id: I1a9572288a0e536f0fc9c0748a25dcf58551e57b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/119760
Kokoro: Kokoro <noreply+kokoro@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index 5baef8a..e140fb3 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -808,6 +808,58 @@
         return b.Call(fn, lhs, rhs);
     }
 
+    /// Builds the polyfill inline expression for a precise float modulo, as defined in the spec.
+    /// @param bin_op the original BinaryExpression
+    /// @return the polyfill divide or modulo
+    const ast::Expression* PreciseFloatMod(const ast::BinaryExpression* bin_op) {
+        auto* lhs_ty = ctx.src->TypeOf(bin_op->lhs)->UnwrapRef();
+        auto* rhs_ty = ctx.src->TypeOf(bin_op->rhs)->UnwrapRef();
+        BinaryOpSignature sig{bin_op->op, lhs_ty, rhs_ty};
+        auto fn = binary_op_polyfills.GetOrCreate(sig, [&] {
+            uint32_t lhs_width = 1;
+            uint32_t rhs_width = 1;
+            const auto* lhs_el_ty = type::Type::ElementOf(lhs_ty, &lhs_width);
+            const auto* rhs_el_ty = type::Type::ElementOf(rhs_ty, &rhs_width);
+
+            const uint32_t width = std::max(lhs_width, rhs_width);
+
+            const char* lhs = "lhs";
+            const char* rhs = "rhs";
+
+            utils::Vector<const ast::Statement*, 4> body;
+
+            if (lhs_width < width) {
+                // lhs is scalar, rhs is vector. Convert lhs to vector.
+                body.Push(b.Decl(b.Let("l", b.vec(T(lhs_el_ty), width, b.Expr(lhs)))));
+                lhs = "l";
+            }
+            if (rhs_width < width) {
+                // lhs is vector, rhs is scalar. Convert rhs to vector.
+                body.Push(b.Decl(b.Let("r", b.vec(T(rhs_el_ty), width, b.Expr(rhs)))));
+                rhs = "r";
+            }
+
+            auto name = b.Symbols().New("tint_float_mod");
+
+            // lhs - trunc(lhs / rhs) * rhs
+            auto* precise_mod = b.Sub(lhs, b.Mul(b.Call("trunc", b.Div(lhs, rhs)), rhs));
+            body.Push(b.Return(precise_mod));
+
+            b.Func(name,
+                   utils::Vector{
+                       b.Param("lhs", T(lhs_ty)),
+                       b.Param("rhs", T(rhs_ty)),
+                   },
+                   width == 1 ? T(lhs_ty) : b.ty.vec(T(lhs_el_ty), width),  // return type
+                   std::move(body));
+
+            return name;
+        });
+        auto* lhs = ctx.Clone(bin_op->lhs);
+        auto* rhs = ctx.Clone(bin_op->rhs);
+        return b.Call(fn, lhs, rhs);
+    }
+
   private:
     /// The clone context
     CloneContext& ctx;
@@ -1052,7 +1104,16 @@
                         }
                         break;
                     }
-                    case ast::BinaryOp::kDivide:
+                    case ast::BinaryOp::kDivide: {
+                        if (polyfill.int_div_mod) {
+                            auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
+                            if (lhs_ty->is_integer_scalar_or_vector()) {
+                                ctx.Replace(bin_op, [bin_op, &s] { return s.IntDivMod(bin_op); });
+                                made_changes = true;
+                            }
+                        }
+                        break;
+                    }
                     case ast::BinaryOp::kModulo: {
                         if (polyfill.int_div_mod) {
                             auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
@@ -1061,6 +1122,14 @@
                                 made_changes = true;
                             }
                         }
+                        if (polyfill.precise_float_mod) {
+                            auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
+                            if (lhs_ty->is_float_scalar_or_vector()) {
+                                ctx.Replace(bin_op,
+                                            [bin_op, &s] { return s.PreciseFloatMod(bin_op); });
+                                made_changes = true;
+                            }
+                        }
                         break;
                     }
                     default:
diff --git a/src/tint/transform/builtin_polyfill.h b/src/tint/transform/builtin_polyfill.h
index ae5f588..521940c 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -68,6 +68,8 @@
         /// Should integer scalar / vector divides and modulos be polyfilled to avoid DBZ and
         /// integer overflows?
         bool int_div_mod = false;
+        /// Should float modulos be polyfilled to emit a precise modulo operation as per the spec?
+        bool precise_float_mod = false;
         /// Should `saturate()` be polyfilled?
         bool saturate = false;
         /// Should `sign()` be polyfilled for integer types?
diff --git a/src/tint/transform/builtin_polyfill_test.cc b/src/tint/transform/builtin_polyfill_test.cc
index 65365bb..688d95e5 100644
--- a/src/tint/transform/builtin_polyfill_test.cc
+++ b/src/tint/transform/builtin_polyfill_test.cc
@@ -2005,6 +2005,232 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// precise_float_mod
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillPreciseFloatMod() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.precise_float_mod = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunPreciseFloatMod) {
+    auto* src = R"(
+fn f() {
+  let v = 10f;
+  let x = 20f % v;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillPreciseFloatMod()));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_af_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 10f;
+  let x = 20.0 % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
+fn f() {
+  let v = 10.0f;
+  let x = tint_float_mod(20.0, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_f32_af) {
+    auto* src = R"(
+fn f() {
+  let v = 10.0;
+  let x = 20f % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
+fn f() {
+  let v = 10.0;
+  let x = tint_float_mod(20.0f, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_f32_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 10f;
+  let x = 20f % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
+fn f() {
+  let v = 10.0f;
+  let x = tint_float_mod(20.0f, v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_Overloads) {
+    auto* src = R"(
+fn f() {
+  let v = 10f;
+  let x = 20f % v;
+  let w = 10i;
+  let y = 20i % w;
+  let u = 10u;
+  let z = 20u % u;
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
+fn f() {
+  let v = 10.0f;
+  let x = tint_float_mod(20.0f, v);
+  let w = 10i;
+  let y = (20i % w);
+  let u = 10u;
+  let z = (20u % u);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_af_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 10f;
+  let x = vec3(20.0) % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
+  let r = vec3<f32>(rhs);
+  return (lhs - (trunc((lhs / r)) * r));
+}
+
+fn f() {
+  let v = 10.0f;
+  let x = tint_float_mod(vec3(20.0), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_af) {
+    auto* src = R"(
+fn f() {
+  let v = 10.0;
+  let x = vec3(20f) % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
+  let r = vec3<f32>(rhs);
+  return (lhs - (trunc((lhs / r)) * r));
+}
+
+fn f() {
+  let v = 10.0;
+  let x = tint_float_mod(vec3(20.0f), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 10f;
+  let x = vec3(20f) % v;
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
+  let r = vec3<f32>(rhs);
+  return (lhs - (trunc((lhs / r)) * r));
+}
+
+fn f() {
+  let v = 10.0f;
+  let x = tint_float_mod(vec3(20.0f), v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_vec3_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 10f;
+  let x = vec3<f32>(20f) % vec3<f32>(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_float_mod(lhs : vec3<f32>, rhs : vec3<f32>) -> vec3<f32> {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
+fn f() {
+  let v = 10.0f;
+  let x = tint_float_mod(vec3<f32>(20.0f), vec3<f32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // int_div_mod
 ////////////////////////////////////////////////////////////////////////////////
 DataMap polyfillIntDivMod() {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 0f8fdb1..4e37faa 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -180,6 +180,7 @@
         polyfills.first_trailing_bit = true;
         polyfills.insert_bits = transform::BuiltinPolyfill::Level::kFull;
         polyfills.int_div_mod = true;
+        polyfills.precise_float_mod = true;
         polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
         polyfills.workgroup_uniform_load = true;
         data.Add<transform::BuiltinPolyfill::Config>(polyfills);
diff --git a/test/tint/bug/tint/948.wgsl.expected.dxc.hlsl b/test/tint/bug/tint/948.wgsl.expected.dxc.hlsl
index c926578..306658c 100644
--- a/test/tint/bug/tint/948.wgsl.expected.dxc.hlsl
+++ b/test/tint/bug/tint/948.wgsl.expected.dxc.hlsl
@@ -33,6 +33,10 @@
   return float4x4(float4(x_40.x, x_40.y, x_40.z, x_40.w), float4(x_47.x, x_47.y, x_47.z, x_47.w), float4(x_54.x, x_54.y, x_54.z, x_54.w), (0.0f).xxxx);
 }
 
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 void main_1() {
   float4 color = float4(0.0f, 0.0f, 0.0f, 0.0f);
   float2 tileUV = float2(0.0f, 0.0f);
@@ -100,7 +104,7 @@
     if ((x_174 > 0.0f)) {
       const float x_181 = asfloat(x_20[0].x);
       const float x_184 = animationData.z;
-      mt = ((x_181 * x_184) % 1.0f);
+      mt = tint_float_mod((x_181 * x_184), 1.0f);
       f = 0.0f;
       while (true) {
         const float x_193 = f;
diff --git a/test/tint/bug/tint/948.wgsl.expected.fxc.hlsl b/test/tint/bug/tint/948.wgsl.expected.fxc.hlsl
index c926578..306658c 100644
--- a/test/tint/bug/tint/948.wgsl.expected.fxc.hlsl
+++ b/test/tint/bug/tint/948.wgsl.expected.fxc.hlsl
@@ -33,6 +33,10 @@
   return float4x4(float4(x_40.x, x_40.y, x_40.z, x_40.w), float4(x_47.x, x_47.y, x_47.z, x_47.w), float4(x_54.x, x_54.y, x_54.z, x_54.w), (0.0f).xxxx);
 }
 
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 void main_1() {
   float4 color = float4(0.0f, 0.0f, 0.0f, 0.0f);
   float2 tileUV = float2(0.0f, 0.0f);
@@ -100,7 +104,7 @@
     if ((x_174 > 0.0f)) {
       const float x_181 = asfloat(x_20[0].x);
       const float x_184 = animationData.z;
-      mt = ((x_181 * x_184) % 1.0f);
+      mt = tint_float_mod((x_181 * x_184), 1.0f);
       f = 0.0f;
       while (true) {
         const float x_193 = f;
diff --git a/test/tint/expressions/binary/mod/scalar-scalar/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/scalar-scalar/f16.wgsl.expected.dxc.hlsl
index 9ce6ac8..ad0a421 100644
--- a/test/tint/expressions/binary/mod/scalar-scalar/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/scalar-scalar/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float16_t tint_float_mod(float16_t lhs, float16_t rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float16_t a = float16_t(1.0h);
   const float16_t b = float16_t(2.0h);
-  const float16_t r = (a % b);
+  const float16_t r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.dxc.hlsl
index 7e166b7..0c9eb2e 100644
--- a/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float a = 1.0f;
   const float b = 2.0f;
-  const float r = (a % b);
+  const float r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.fxc.hlsl
index 7e166b7..0c9eb2e 100644
--- a/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod/scalar-scalar/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float a = 1.0f;
   const float b = 2.0f;
-  const float r = (a % b);
+  const float r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/scalar-vec3/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/scalar-vec3/f16.wgsl.expected.dxc.hlsl
index 5b7fdbe..99ffb58 100644
--- a/test/tint/expressions/binary/mod/scalar-vec3/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/scalar-vec3/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,12 @@
+vector<float16_t, 3> tint_float_mod(float16_t lhs, vector<float16_t, 3> rhs) {
+  const vector<float16_t, 3> l = vector<float16_t, 3>((lhs).xxx);
+  return (l - (trunc((l / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float16_t a = float16_t(4.0h);
   const vector<float16_t, 3> b = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
-  const vector<float16_t, 3> r = (a % b);
+  const vector<float16_t, 3> r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.dxc.hlsl
index b2653df..c864275 100644
--- a/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,12 @@
+float3 tint_float_mod(float lhs, float3 rhs) {
+  const float3 l = float3((lhs).xxx);
+  return (l - (trunc((l / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float a = 4.0f;
   const float3 b = float3(1.0f, 2.0f, 3.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.fxc.hlsl
index b2653df..c864275 100644
--- a/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod/scalar-vec3/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,12 @@
+float3 tint_float_mod(float lhs, float3 rhs) {
+  const float3 l = float3((lhs).xxx);
+  return (l - (trunc((l / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float a = 4.0f;
   const float3 b = float3(1.0f, 2.0f, 3.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/vec3-scalar/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/vec3-scalar/f16.wgsl.expected.dxc.hlsl
index 4c54522..a8976e9 100644
--- a/test/tint/expressions/binary/mod/vec3-scalar/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/vec3-scalar/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,12 @@
+vector<float16_t, 3> tint_float_mod(vector<float16_t, 3> lhs, float16_t rhs) {
+  const vector<float16_t, 3> r = vector<float16_t, 3>((rhs).xxx);
+  return (lhs - (trunc((lhs / r)) * r));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const vector<float16_t, 3> a = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
   const float16_t b = float16_t(4.0h);
-  const vector<float16_t, 3> r = (a % b);
+  const vector<float16_t, 3> r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.dxc.hlsl
index 147b45c..c61e266 100644
--- a/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,12 @@
+float3 tint_float_mod(float3 lhs, float rhs) {
+  const float3 r = float3((rhs).xxx);
+  return (lhs - (trunc((lhs / r)) * r));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float3 a = float3(1.0f, 2.0f, 3.0f);
   const float b = 4.0f;
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.fxc.hlsl
index 147b45c..c61e266 100644
--- a/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod/vec3-scalar/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,12 @@
+float3 tint_float_mod(float3 lhs, float rhs) {
+  const float3 r = float3((rhs).xxx);
+  return (lhs - (trunc((lhs / r)) * r));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float3 a = float3(1.0f, 2.0f, 3.0f);
   const float b = 4.0f;
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/vec3-vec3/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/vec3-vec3/f16.wgsl.expected.dxc.hlsl
index 69def79..69ff97a 100644
--- a/test/tint/expressions/binary/mod/vec3-vec3/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/vec3-vec3/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+vector<float16_t, 3> tint_float_mod(vector<float16_t, 3> lhs, vector<float16_t, 3> rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const vector<float16_t, 3> a = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
   const vector<float16_t, 3> b = vector<float16_t, 3>(float16_t(4.0h), float16_t(5.0h), float16_t(6.0h));
-  const vector<float16_t, 3> r = (a % b);
+  const vector<float16_t, 3> r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.dxc.hlsl
index 87e4dd4..b03e6c8 100644
--- a/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float3 a = float3(1.0f, 2.0f, 3.0f);
   const float3 b = float3(4.0f, 5.0f, 6.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.fxc.hlsl
index 87e4dd4..b03e6c8 100644
--- a/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod/vec3-vec3/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float3 a = float3(1.0f, 2.0f, 3.0f);
   const float3 b = float3(4.0f, 5.0f, 6.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f16.wgsl.expected.dxc.hlsl
index 87f2c8f..45c8ee1 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float16_t tint_float_mod(float16_t lhs, float16_t rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float16_t a = float16_t(1.0h);
   const float16_t b = float16_t(0.0h);
-  const float16_t r = (a % b);
+  const float16_t r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.dxc.hlsl
index 80448b1..515bcfb 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float a = 1.0f;
   const float b = 0.0f;
-  const float r = (a % b);
+  const float r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.fxc.hlsl
index 80448b1..515bcfb 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_constant/scalar-scalar/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float a = 1.0f;
   const float b = 0.0f;
-  const float r = (a % b);
+  const float r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f16.wgsl.expected.dxc.hlsl
index 8c6cb4c..8e4d644 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+vector<float16_t, 3> tint_float_mod(vector<float16_t, 3> lhs, vector<float16_t, 3> rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const vector<float16_t, 3> a = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
   const vector<float16_t, 3> b = vector<float16_t, 3>(float16_t(0.0h), float16_t(5.0h), float16_t(0.0h));
-  const vector<float16_t, 3> r = (a % b);
+  const vector<float16_t, 3> r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.dxc.hlsl
index 5ea2fbd..0cd0191 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float3 a = float3(1.0f, 2.0f, 3.0f);
   const float3 b = float3(0.0f, 5.0f, 0.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.fxc.hlsl
index 5ea2fbd..0cd0191 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_constant/vec3-vec3/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   const float3 a = float3(1.0f, 2.0f, 3.0f);
   const float3 b = float3(0.0f, 5.0f, 0.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f16.wgsl.expected.dxc.hlsl
index 9dd48b3..649bbd2 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float16_t tint_float_mod(float16_t lhs, float16_t rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float16_t a = float16_t(1.0h);
   float16_t b = float16_t(0.0h);
-  const float16_t r = (a % (b + b));
+  const float16_t r = tint_float_mod(a, (b + b));
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.dxc.hlsl
index 468c33d..6af8624 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float a = 1.0f;
   float b = 0.0f;
-  const float r = (a % (b + b));
+  const float r = tint_float_mod(a, (b + b));
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.fxc.hlsl
index 468c33d..6af8624 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_expression/scalar-scalar/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float a = 1.0f;
   float b = 0.0f;
-  const float r = (a % (b + b));
+  const float r = tint_float_mod(a, (b + b));
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f16.wgsl.expected.dxc.hlsl
index 2f75bb3..dfe3a63 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+vector<float16_t, 3> tint_float_mod(vector<float16_t, 3> lhs, vector<float16_t, 3> rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   vector<float16_t, 3> a = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
   vector<float16_t, 3> b = vector<float16_t, 3>(float16_t(0.0h), float16_t(5.0h), float16_t(0.0h));
-  const vector<float16_t, 3> r = (a % (b + b));
+  const vector<float16_t, 3> r = tint_float_mod(a, (b + b));
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.dxc.hlsl
index 8d21550..ff6fdd4 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float3 a = float3(1.0f, 2.0f, 3.0f);
   float3 b = float3(0.0f, 5.0f, 0.0f);
-  const float3 r = (a % (b + b));
+  const float3 r = tint_float_mod(a, (b + b));
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.fxc.hlsl
index 8d21550..ff6fdd4 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_expression/vec3-vec3/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float3 a = float3(1.0f, 2.0f, 3.0f);
   float3 b = float3(0.0f, 5.0f, 0.0f);
-  const float3 r = (a % (b + b));
+  const float3 r = tint_float_mod(a, (b + b));
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f16.wgsl.expected.dxc.hlsl
index 18770f0..7ee2e0b 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float16_t tint_float_mod(float16_t lhs, float16_t rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float16_t a = float16_t(1.0h);
   float16_t b = float16_t(0.0h);
-  const float16_t r = (a % b);
+  const float16_t r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.dxc.hlsl
index 3d43aff..0fc0aef 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float a = 1.0f;
   float b = 0.0f;
-  const float r = (a % b);
+  const float r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.fxc.hlsl
index 3d43aff..0fc0aef 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_identifier/scalar-scalar/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float a = 1.0f;
   float b = 0.0f;
-  const float r = (a % b);
+  const float r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f16.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f16.wgsl.expected.dxc.hlsl
index d79d88c..4c47f3b 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f16.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f16.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+vector<float16_t, 3> tint_float_mod(vector<float16_t, 3> lhs, vector<float16_t, 3> rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   vector<float16_t, 3> a = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
   vector<float16_t, 3> b = vector<float16_t, 3>(float16_t(0.0h), float16_t(5.0h), float16_t(0.0h));
-  const vector<float16_t, 3> r = (a % b);
+  const vector<float16_t, 3> r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.dxc.hlsl
index 636719b..f6fdd4c 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float3 a = float3(1.0f, 2.0f, 3.0f);
   float3 b = float3(0.0f, 5.0f, 0.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.fxc.hlsl
index 636719b..f6fdd4c 100644
--- a/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/mod_by_zero/by_identifier/vec3-vec3/f32.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_float_mod(float3 lhs, float3 rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 [numthreads(1, 1, 1)]
 void f() {
   float3 a = float3(1.0f, 2.0f, 3.0f);
   float3 b = float3(0.0f, 5.0f, 0.0f);
-  const float3 r = (a % b);
+  const float3 r = tint_float_mod(a, b);
   return;
 }
diff --git a/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.dxc.hlsl b/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.dxc.hlsl
index ab7448d..314a630 100644
--- a/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.dxc.hlsl
+++ b/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.dxc.hlsl
@@ -19,13 +19,17 @@
   }
 }
 
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 void foo(int maybe_zero) {
   a = tint_div(a, 0);
   a = tint_mod(a, 0);
   a = tint_div(a, maybe_zero);
   a = tint_mod(a, maybe_zero);
   b = (b / 0.0f);
-  b = (b % 0.0f);
+  b = tint_float_mod(b, 0.0f);
   b = (b / float(maybe_zero));
-  b = (b % float(maybe_zero));
+  b = tint_float_mod(b, float(maybe_zero));
 }
diff --git a/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.fxc.hlsl b/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.fxc.hlsl
index ab7448d..314a630 100644
--- a/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.fxc.hlsl
+++ b/test/tint/statements/compound_assign/divide_by_zero.wgsl.expected.fxc.hlsl
@@ -19,13 +19,17 @@
   }
 }
 
+float tint_float_mod(float lhs, float rhs) {
+  return (lhs - (trunc((lhs / rhs)) * rhs));
+}
+
 void foo(int maybe_zero) {
   a = tint_div(a, 0);
   a = tint_mod(a, 0);
   a = tint_div(a, maybe_zero);
   a = tint_mod(a, maybe_zero);
   b = (b / 0.0f);
-  b = (b % 0.0f);
+  b = tint_float_mod(b, 0.0f);
   b = (b / float(maybe_zero));
-  b = (b % float(maybe_zero));
+  b = tint_float_mod(b, float(maybe_zero));
 }