[tint] Polyfill fwidthFine for MSL and HLSL

The `fwidth()` builtins in these languages do not guarantee 'fine'
derivatives, so polyfill `fwidthFine` using `dpdxFine` and `dpdyFine`.

Change-Id: I4a59cc7c19376c77db8a84668bed791c0c9d28cf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/184420
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
index ffde096..13b1fd6 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
@@ -258,6 +258,7 @@
         polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kFull;
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
+        polyfills.fwidth_fine = true;
         polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kFull;
         polyfills.int_div_mod = !options.disable_polyfill_integer_div_mod;
         polyfills.precise_float_mod = true;
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
index 6b4d13d..bb583cd 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
@@ -178,6 +178,7 @@
         polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kClampParameters;
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
+        polyfills.fwidth_fine = true;
         polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kClampParameters;
         polyfills.int_div_mod = !options.disable_polyfill_integer_div_mod;
         polyfills.sign_int = true;
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
index b1f36fc..f82fe95 100644
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
+++ b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
@@ -587,6 +587,27 @@
         return name;
     }
 
+    /// Builds the polyfill function for the `fwidthFine` builtin
+    /// @param ty the parameter and return type for the function
+    /// @return the polyfill function name
+    Symbol fwidthFine(const core::type::Type* ty) {
+        auto name = b.Symbols().New("tint_fwidth_fine");
+        // WGSL polyfill function:
+        //      fn tint_fwidth_fine(v : T) -> T {
+        //          return abs(dpdxFine(v)) + abs(dpdyFine(v));
+        //      }
+        auto body = tint::Vector{
+            b.Return(b.Add(b.Call("abs", b.Call("dpdxFine", "v")),
+                           b.Call("abs", b.Call("dpdyFine", "v")))),
+        };
+        b.Func(name,
+               tint::Vector{
+                   b.Param("v", T(ty)),
+               },
+               T(ty), body);
+        return name;
+    }
+
     /// Builds the polyfill function for the `insertBits` builtin
     /// @param ty the parameter and return type for the function
     /// @return the polyfill function name
@@ -1359,6 +1380,13 @@
                         }
                         return Symbol{};
 
+                    case wgsl::BuiltinFn::kFwidthFine:
+                        if (cfg.builtins.fwidth_fine) {
+                            return builtin_polyfills.GetOrAdd(
+                                builtin, [&] { return fwidthFine(builtin->ReturnType()); });
+                        }
+                        return Symbol{};
+
                     case wgsl::BuiltinFn::kInsertBits:
                         if (cfg.builtins.insert_bits != Level::kNone) {
                             return builtin_polyfills.GetOrAdd(
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h
index 2634f74..bdbe157 100644
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h
+++ b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h
@@ -78,6 +78,8 @@
         bool first_leading_bit = false;
         /// Should `firstTrailingBit()` be polyfilled?
         bool first_trailing_bit = false;
+        /// Should `fwidthFine()` be polyfilled?
+        bool fwidth_fine = false;
         /// Should `insertBits()` be polyfilled?
         Level insert_bits = Level::kNone;
         /// Should integer scalar / vector divides and modulos be polyfilled to avoid DBZ and
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
index 0c64163..53a9fc4 100644
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
@@ -1971,6 +1971,77 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// fwidthFine
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillFwidthFine() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.fwidth_fine = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunFwidthFine) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5f;
+  _ = fwidthFine(v);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFwidthFine()));
+}
+
+TEST_F(BuiltinPolyfillTest, FwidthFine_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5f;
+  let r : f32 = fwidthFine(v);
+}
+)";
+
+    auto* expect = R"(
+fn tint_fwidth_fine(v : f32) -> f32 {
+  return (abs(dpdxFine(v)) + abs(dpdyFine(v)));
+}
+
+fn f() {
+  let v = 0.5f;
+  let r : f32 = tint_fwidth_fine(v);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillFwidthFine());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, FwidthFine_vec3_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5f;
+  let r : vec3<f32> = fwidthFine(vec3<f32>(v));
+}
+)";
+
+    auto* expect = R"(
+fn tint_fwidth_fine(v : vec3<f32>) -> vec3<f32> {
+  return (abs(dpdxFine(v)) + abs(dpdyFine(v)));
+}
+
+fn f() {
+  let v = 0.5f;
+  let r : vec3<f32> = tint_fwidth_fine(vec3<f32>(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillFwidthFine());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // insertBits
 ////////////////////////////////////////////////////////////////////////////////
 DataMap polyfillInsertBits(Level level) {
diff --git a/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.dxc.hlsl
index d4dc2b7..e0dd759 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_fwidth_fine(float3 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_523fdc() {
-  float3 res = fwidth((1.0f).xxx);
+  float3 res = tint_fwidth_fine((1.0f).xxx);
   prevent_dce.Store3(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.fxc.hlsl
index d4dc2b7..e0dd759 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float3 tint_fwidth_fine(float3 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_523fdc() {
-  float3 res = fwidth((1.0f).xxx);
+  float3 res = tint_fwidth_fine((1.0f).xxx);
   prevent_dce.Store3(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.msl b/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.msl
index 3c1d71c..86c548b 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/fwidthFine/523fdc.wgsl.expected.msl
@@ -1,8 +1,12 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float3 tint_fwidth_fine(float3 v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_523fdc(device packed_float3* const tint_symbol) {
-  float3 res = fwidth(float3(1.0f));
+  float3 res = tint_fwidth_fine(float3(1.0f));
   *(tint_symbol) = packed_float3(res);
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl
index 947222f..76b494e 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float4 tint_fwidth_fine(float4 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_68f4ef() {
-  float4 res = fwidth((1.0f).xxxx);
+  float4 res = tint_fwidth_fine((1.0f).xxxx);
   prevent_dce.Store4(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl
index 947222f..76b494e 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float4 tint_fwidth_fine(float4 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_68f4ef() {
-  float4 res = fwidth((1.0f).xxxx);
+  float4 res = tint_fwidth_fine((1.0f).xxxx);
   prevent_dce.Store4(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.msl b/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.msl
index 3c2bf35..0cb5d2a 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/fwidthFine/68f4ef.wgsl.expected.msl
@@ -1,8 +1,12 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float4 tint_fwidth_fine(float4 v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_68f4ef(device float4* const tint_symbol) {
-  float4 res = fwidth(float4(1.0f));
+  float4 res = tint_fwidth_fine(float4(1.0f));
   *(tint_symbol) = res;
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.dxc.hlsl
index ec70a68..e19d8b4 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float tint_fwidth_fine(float v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_f1742d() {
-  float res = fwidth(1.0f);
+  float res = tint_fwidth_fine(1.0f);
   prevent_dce.Store(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.fxc.hlsl
index ec70a68..e19d8b4 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float tint_fwidth_fine(float v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_f1742d() {
-  float res = fwidth(1.0f);
+  float res = tint_fwidth_fine(1.0f);
   prevent_dce.Store(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.msl b/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.msl
index 4a2e078..38fcc11 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/fwidthFine/f1742d.wgsl.expected.msl
@@ -1,8 +1,12 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float tint_fwidth_fine(float v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_f1742d(device float* const tint_symbol) {
-  float res = fwidth(1.0f);
+  float res = tint_fwidth_fine(1.0f);
   *(tint_symbol) = res;
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl
index b9e2d02..ad46db4 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl
@@ -1,7 +1,11 @@
+float2 tint_fwidth_fine(float2 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_ff6aa0() {
-  float2 res = fwidth((1.0f).xx);
+  float2 res = tint_fwidth_fine((1.0f).xx);
   prevent_dce.Store2(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl
index b9e2d02..ad46db4 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl
@@ -1,7 +1,11 @@
+float2 tint_fwidth_fine(float2 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_ff6aa0() {
-  float2 res = fwidth((1.0f).xx);
+  float2 res = tint_fwidth_fine((1.0f).xx);
   prevent_dce.Store2(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.msl b/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.msl
index c054945..594c97a 100644
--- a/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/fwidthFine/ff6aa0.wgsl.expected.msl
@@ -1,8 +1,12 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float2 tint_fwidth_fine(float2 v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_ff6aa0(device float2* const tint_symbol) {
-  float2 res = fwidth(float2(1.0f));
+  float2 res = tint_fwidth_fine(float2(1.0f));
   *(tint_symbol) = res;
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.dxc.hlsl
index af999bd..18baa95 100644
--- a/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+float3 tint_fwidth_fine(float3 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_523fdc() {
   float3 arg_0 = (1.0f).xxx;
-  float3 res = fwidth(arg_0);
+  float3 res = tint_fwidth_fine(arg_0);
   prevent_dce.Store3(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.fxc.hlsl
index af999bd..18baa95 100644
--- a/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+float3 tint_fwidth_fine(float3 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_523fdc() {
   float3 arg_0 = (1.0f).xxx;
-  float3 res = fwidth(arg_0);
+  float3 res = tint_fwidth_fine(arg_0);
   prevent_dce.Store3(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.msl b/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.msl
index da1fd5b..b44499e 100644
--- a/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/fwidthFine/523fdc.wgsl.expected.msl
@@ -1,9 +1,13 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float3 tint_fwidth_fine(float3 v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_523fdc(device packed_float3* const tint_symbol) {
   float3 arg_0 = float3(1.0f);
-  float3 res = fwidth(arg_0);
+  float3 res = tint_fwidth_fine(arg_0);
   *(tint_symbol) = packed_float3(res);
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl
index d9c92a8..46e7953 100644
--- a/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+float4 tint_fwidth_fine(float4 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_68f4ef() {
   float4 arg_0 = (1.0f).xxxx;
-  float4 res = fwidth(arg_0);
+  float4 res = tint_fwidth_fine(arg_0);
   prevent_dce.Store4(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl
index d9c92a8..46e7953 100644
--- a/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+float4 tint_fwidth_fine(float4 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_68f4ef() {
   float4 arg_0 = (1.0f).xxxx;
-  float4 res = fwidth(arg_0);
+  float4 res = tint_fwidth_fine(arg_0);
   prevent_dce.Store4(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.msl b/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.msl
index 4cd3e43..5c61147 100644
--- a/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/fwidthFine/68f4ef.wgsl.expected.msl
@@ -1,9 +1,13 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float4 tint_fwidth_fine(float4 v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_68f4ef(device float4* const tint_symbol) {
   float4 arg_0 = float4(1.0f);
-  float4 res = fwidth(arg_0);
+  float4 res = tint_fwidth_fine(arg_0);
   *(tint_symbol) = res;
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.dxc.hlsl
index fe437f7..7a891d5 100644
--- a/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+float tint_fwidth_fine(float v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_f1742d() {
   float arg_0 = 1.0f;
-  float res = fwidth(arg_0);
+  float res = tint_fwidth_fine(arg_0);
   prevent_dce.Store(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.fxc.hlsl
index fe437f7..7a891d5 100644
--- a/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+float tint_fwidth_fine(float v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_f1742d() {
   float arg_0 = 1.0f;
-  float res = fwidth(arg_0);
+  float res = tint_fwidth_fine(arg_0);
   prevent_dce.Store(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.msl b/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.msl
index 46d61583..42b8ba7 100644
--- a/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/fwidthFine/f1742d.wgsl.expected.msl
@@ -1,9 +1,13 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float tint_fwidth_fine(float v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_f1742d(device float* const tint_symbol) {
   float arg_0 = 1.0f;
-  float res = fwidth(arg_0);
+  float res = tint_fwidth_fine(arg_0);
   *(tint_symbol) = res;
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl
index 03677c7..584a8ec 100644
--- a/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+float2 tint_fwidth_fine(float2 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_ff6aa0() {
   float2 arg_0 = (1.0f).xx;
-  float2 res = fwidth(arg_0);
+  float2 res = tint_fwidth_fine(arg_0);
   prevent_dce.Store2(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl
index 03677c7..584a8ec 100644
--- a/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+float2 tint_fwidth_fine(float2 v) {
+  return (abs(ddx_fine(v)) + abs(ddy_fine(v)));
+}
+
 RWByteAddressBuffer prevent_dce : register(u0, space2);
 
 void fwidthFine_ff6aa0() {
   float2 arg_0 = (1.0f).xx;
-  float2 res = fwidth(arg_0);
+  float2 res = tint_fwidth_fine(arg_0);
   prevent_dce.Store2(0u, asuint(res));
 }
 
diff --git a/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.msl b/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.msl
index 63958b3..4dc6409 100644
--- a/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/fwidthFine/ff6aa0.wgsl.expected.msl
@@ -1,9 +1,13 @@
 #include <metal_stdlib>
 
 using namespace metal;
+float2 tint_fwidth_fine(float2 v) {
+  return (fabs(dfdx(v)) + fabs(dfdy(v)));
+}
+
 void fwidthFine_ff6aa0(device float2* const tint_symbol) {
   float2 arg_0 = float2(1.0f);
-  float2 res = fwidth(arg_0);
+  float2 res = tint_fwidth_fine(arg_0);
   *(tint_symbol) = res;
 }