tint/writers: Polyfill integer clamp()

...to `min(max(e, low), high)` as defined by the WGSL spec.

Fixed: tint:1479
Fixed: tint:1539
Change-Id: I39406d5256a155a781e44bd9d6081ce7a9bf5a68
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107640
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index 8c8985f..ec00672 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -137,6 +137,27 @@
         return name;
     }
 
+    /// Builds the polyfill function for the `clamp` builtin when called with integer arguments
+    /// (scalar or vector)
+    /// @param ty the parameter and return type for the function
+    /// @return the polyfill function name
+    Symbol clampInteger(const sem::Type* ty) {
+        auto name = b.Symbols().New("tint_clamp");
+
+        b.Func(name,
+               utils::Vector{
+                   b.Param("e", T(ty)),
+                   b.Param("low", T(ty)),
+                   b.Param("high", T(ty)),
+               },
+               T(ty),
+               utils::Vector{
+                   // return min(max(e, low), high);
+                   b.Return(b.Call("min", b.Call("max", "e", "low"), "high")),
+               });
+        return name;
+    }
+
     /// Builds the polyfill function for the `countLeadingZeros` builtin
     /// @param ty the parameter and return type for the function
     /// @return the polyfill function name
@@ -588,6 +609,12 @@
                                 return true;
                             }
                             break;
+                        case sem::BuiltinType::kClamp:
+                            if (builtins.clamp_int) {
+                                auto& sig = builtin->Signature();
+                                return sig.parameters[0]->Type()->is_integer_scalar_or_vector();
+                            }
+                            break;
                         case sem::BuiltinType::kCountLeadingZeros:
                             if (builtins.count_leading_zeros) {
                                 return true;
@@ -679,6 +706,16 @@
                                 polyfills, builtin, [&] { return s.atanh(builtin->ReturnType()); });
                         }
                         break;
+                    case sem::BuiltinType::kClamp:
+                        if (builtins.clamp_int) {
+                            auto& sig = builtin->Signature();
+                            if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
+                                polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
+                                    return s.clampInteger(builtin->ReturnType());
+                                });
+                            }
+                        }
+                        break;
                     case sem::BuiltinType::kCountLeadingZeros:
                         if (builtins.count_leading_zeros) {
                             polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
diff --git a/src/tint/transform/builtin_polyfill.h b/src/tint/transform/builtin_polyfill.h
index 14720bf..d083252 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -47,6 +47,8 @@
         bool asinh = false;
         /// What level should `atanh` be polyfilled?
         Level atanh = Level::kNone;
+        /// Should `clamp()` be polyfilled for integer values (scalar or vector)?
+        bool clamp_int = false;
         /// Should `countLeadingZeros()` be polyfilled?
         bool count_leading_zeros = false;
         /// Should `countTrailingZeros()` be polyfilled?
diff --git a/src/tint/transform/builtin_polyfill_test.cc b/src/tint/transform/builtin_polyfill_test.cc
index 2a62910..b938195 100644
--- a/src/tint/transform/builtin_polyfill_test.cc
+++ b/src/tint/transform/builtin_polyfill_test.cc
@@ -399,6 +399,173 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// clampInteger
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillClampInteger() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.clamp_int = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 1i;
+  clamp(v, 2i, 3i);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 1u;
+  clamp(v, 2u, 3u);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f32) {
+    auto* src = R"(
+fn f() {
+  let v = 1f;
+  clamp(v, 2f, 3f);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f16) {
+    auto* src = R"(
+enable f16;
+
+fn f() {
+  let v = 1h;
+  clamp(v, 2h, 3h);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
+}
+
+TEST_F(BuiltinPolyfillTest, ClampInteger_ConstantExpression) {
+    auto* src = R"(
+fn f() {
+  let r : i32 = clamp(1i, 2i, 3i);
+}
+)";
+
+    auto* expect = src;
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, ClampInteger_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 1i;
+  let r : i32 = clamp(v, 2i, 3i);
+}
+)";
+
+    auto* expect = R"(
+fn tint_clamp(e : i32, low : i32, high : i32) -> i32 {
+  return min(max(e, low), high);
+}
+
+fn f() {
+  let v = 1i;
+  let r : i32 = tint_clamp(v, 2i, 3i);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_i32) {
+    auto* src = R"(
+fn f() {
+  let v = 1i;
+  let r : vec3<i32> = clamp(vec3(v), vec3(2i), vec3(3i));
+}
+)";
+
+    auto* expect =
+        R"(
+fn tint_clamp(e : vec3<i32>, low : vec3<i32>, high : vec3<i32>) -> vec3<i32> {
+  return min(max(e, low), high);
+}
+
+fn f() {
+  let v = 1i;
+  let r : vec3<i32> = tint_clamp(vec3(v), vec3(2i), vec3(3i));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, ClampInteger_u32) {
+    auto* src = R"(
+fn f() {
+  let r : u32 = clamp(1u, 2u, 3u);
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let r : u32 = clamp(1u, 2u, 3u);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_u32) {
+    auto* src = R"(
+fn f() {
+  let v = 1u;
+  let r : vec3<u32> = clamp(vec3(v), vec3(2u), vec3(3u));
+}
+)";
+
+    auto* expect =
+        R"(
+fn tint_clamp(e : vec3<u32>, low : vec3<u32>, high : vec3<u32>) -> vec3<u32> {
+  return min(max(e, low), high);
+}
+
+fn f() {
+  let v = 1u;
+  let r : vec3<u32> = tint_clamp(vec3(v), vec3(2u), vec3(3u));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // countLeadingZeros
 ////////////////////////////////////////////////////////////////////////////////
 DataMap polyfillCountLeadingZeros() {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index e42274d..d9c3479 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -162,6 +162,7 @@
         polyfills.acosh = transform::BuiltinPolyfill::Level::kFull;
         polyfills.asinh = true;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kFull;
+        polyfills.clamp_int = true;
         // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
         // and `firstbithigh`.
         polyfills.count_leading_zeros = true;
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index cf67e10..43633b5 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -171,6 +171,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.clamp_int = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index c57a908..f75f1f2 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -52,6 +52,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.clamp_int = true;
         polyfills.count_leading_zeros = true;
         polyfills.count_trailing_zeros = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
diff --git a/test/tint/bug/tint/1739.wgsl.expected.dxc.hlsl b/test/tint/bug/tint/1739.wgsl.expected.dxc.hlsl
index d5d5143..6a6f107 100644
--- a/test/tint/bug/tint/1739.wgsl.expected.dxc.hlsl
+++ b/test/tint/bug/tint/1739.wgsl.expected.dxc.hlsl
@@ -46,14 +46,18 @@
   return float4(color, 1.0f);
 }
 
-float3x4 tint_symbol_2(uint4 buffer[11], uint offset) {
+int2 tint_clamp(int2 e, int2 low, int2 high) {
+  return min(max(e, low), high);
+}
+
+float3x4 tint_symbol_6(uint4 buffer[11], uint offset) {
   const uint scalar_offset = ((offset + 0u)) / 4;
   const uint scalar_offset_1 = ((offset + 16u)) / 4;
   const uint scalar_offset_2 = ((offset + 32u)) / 4;
   return float3x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]), asfloat(buffer[scalar_offset_2 / 4]));
 }
 
-GammaTransferParams tint_symbol_4(uint4 buffer[11], uint offset) {
+GammaTransferParams tint_symbol_8(uint4 buffer[11], uint offset) {
   const uint scalar_offset_3 = ((offset + 0u)) / 4;
   const uint scalar_offset_4 = ((offset + 4u)) / 4;
   const uint scalar_offset_5 = ((offset + 8u)) / 4;
@@ -62,37 +66,41 @@
   const uint scalar_offset_8 = ((offset + 20u)) / 4;
   const uint scalar_offset_9 = ((offset + 24u)) / 4;
   const uint scalar_offset_10 = ((offset + 28u)) / 4;
-  const GammaTransferParams tint_symbol_8 = {asfloat(buffer[scalar_offset_3 / 4][scalar_offset_3 % 4]), asfloat(buffer[scalar_offset_4 / 4][scalar_offset_4 % 4]), asfloat(buffer[scalar_offset_5 / 4][scalar_offset_5 % 4]), asfloat(buffer[scalar_offset_6 / 4][scalar_offset_6 % 4]), asfloat(buffer[scalar_offset_7 / 4][scalar_offset_7 % 4]), asfloat(buffer[scalar_offset_8 / 4][scalar_offset_8 % 4]), asfloat(buffer[scalar_offset_9 / 4][scalar_offset_9 % 4]), buffer[scalar_offset_10 / 4][scalar_offset_10 % 4]};
-  return tint_symbol_8;
+  const GammaTransferParams tint_symbol_12 = {asfloat(buffer[scalar_offset_3 / 4][scalar_offset_3 % 4]), asfloat(buffer[scalar_offset_4 / 4][scalar_offset_4 % 4]), asfloat(buffer[scalar_offset_5 / 4][scalar_offset_5 % 4]), asfloat(buffer[scalar_offset_6 / 4][scalar_offset_6 % 4]), asfloat(buffer[scalar_offset_7 / 4][scalar_offset_7 % 4]), asfloat(buffer[scalar_offset_8 / 4][scalar_offset_8 % 4]), asfloat(buffer[scalar_offset_9 / 4][scalar_offset_9 % 4]), buffer[scalar_offset_10 / 4][scalar_offset_10 % 4]};
+  return tint_symbol_12;
 }
 
-float3x3 tint_symbol_6(uint4 buffer[11], uint offset) {
+float3x3 tint_symbol_10(uint4 buffer[11], uint offset) {
   const uint scalar_offset_11 = ((offset + 0u)) / 4;
   const uint scalar_offset_12 = ((offset + 16u)) / 4;
   const uint scalar_offset_13 = ((offset + 32u)) / 4;
   return float3x3(asfloat(buffer[scalar_offset_11 / 4].xyz), asfloat(buffer[scalar_offset_12 / 4].xyz), asfloat(buffer[scalar_offset_13 / 4].xyz));
 }
 
-ExternalTextureParams tint_symbol(uint4 buffer[11], uint offset) {
+ExternalTextureParams tint_symbol_4(uint4 buffer[11], uint offset) {
   const uint scalar_offset_14 = ((offset + 0u)) / 4;
   const uint scalar_offset_15 = ((offset + 4u)) / 4;
-  const ExternalTextureParams tint_symbol_9 = {buffer[scalar_offset_14 / 4][scalar_offset_14 % 4], buffer[scalar_offset_15 / 4][scalar_offset_15 % 4], tint_symbol_2(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 64u)), tint_symbol_4(buffer, (offset + 96u)), tint_symbol_6(buffer, (offset + 128u))};
-  return tint_symbol_9;
+  const ExternalTextureParams tint_symbol_13 = {buffer[scalar_offset_14 / 4][scalar_offset_14 % 4], buffer[scalar_offset_15 / 4][scalar_offset_15 % 4], tint_symbol_6(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 64u)), tint_symbol_8(buffer, (offset + 96u)), tint_symbol_10(buffer, (offset + 128u))};
+  return tint_symbol_13;
 }
 
 [numthreads(1, 1, 1)]
 void main() {
   int2 tint_tmp;
   t.GetDimensions(tint_tmp.x, tint_tmp.y);
-  float4 red = textureLoadExternal(t, ext_tex_plane_1, clamp((10).xx, (0).xx, int2((uint2(tint_tmp) - (1u).xx))), tint_symbol(ext_tex_params, 0u));
+  const int2 tint_symbol = tint_clamp((10).xx, (0).xx, int2((uint2(tint_tmp) - (1u).xx)));
+  float4 red = textureLoadExternal(t, ext_tex_plane_1, tint_symbol, tint_symbol_4(ext_tex_params, 0u));
   int2 tint_tmp_1;
   outImage.GetDimensions(tint_tmp_1.x, tint_tmp_1.y);
-  outImage[clamp((0).xx, (0).xx, int2((uint2(tint_tmp_1) - (1u).xx)))] = red;
+  const int2 tint_symbol_1 = tint_clamp((0).xx, (0).xx, int2((uint2(tint_tmp_1) - (1u).xx)));
+  outImage[tint_symbol_1] = red;
   int2 tint_tmp_2;
   t.GetDimensions(tint_tmp_2.x, tint_tmp_2.y);
-  float4 green = textureLoadExternal(t, ext_tex_plane_1, clamp(int2(70, 118), (0).xx, int2((uint2(tint_tmp_2) - (1u).xx))), tint_symbol(ext_tex_params, 0u));
+  const int2 tint_symbol_2 = tint_clamp(int2(70, 118), (0).xx, int2((uint2(tint_tmp_2) - (1u).xx)));
+  float4 green = textureLoadExternal(t, ext_tex_plane_1, tint_symbol_2, tint_symbol_4(ext_tex_params, 0u));
   int2 tint_tmp_3;
   outImage.GetDimensions(tint_tmp_3.x, tint_tmp_3.y);
-  outImage[clamp(int2(1, 0), (0).xx, int2((uint2(tint_tmp_3) - (1u).xx)))] = green;
+  const int2 tint_symbol_3 = tint_clamp(int2(1, 0), (0).xx, int2((uint2(tint_tmp_3) - (1u).xx)));
+  outImage[tint_symbol_3] = green;
   return;
 }
diff --git a/test/tint/bug/tint/1739.wgsl.expected.fxc.hlsl b/test/tint/bug/tint/1739.wgsl.expected.fxc.hlsl
index d5d5143..6a6f107 100644
--- a/test/tint/bug/tint/1739.wgsl.expected.fxc.hlsl
+++ b/test/tint/bug/tint/1739.wgsl.expected.fxc.hlsl
@@ -46,14 +46,18 @@
   return float4(color, 1.0f);
 }
 
-float3x4 tint_symbol_2(uint4 buffer[11], uint offset) {
+int2 tint_clamp(int2 e, int2 low, int2 high) {
+  return min(max(e, low), high);
+}
+
+float3x4 tint_symbol_6(uint4 buffer[11], uint offset) {
   const uint scalar_offset = ((offset + 0u)) / 4;
   const uint scalar_offset_1 = ((offset + 16u)) / 4;
   const uint scalar_offset_2 = ((offset + 32u)) / 4;
   return float3x4(asfloat(buffer[scalar_offset / 4]), asfloat(buffer[scalar_offset_1 / 4]), asfloat(buffer[scalar_offset_2 / 4]));
 }
 
-GammaTransferParams tint_symbol_4(uint4 buffer[11], uint offset) {
+GammaTransferParams tint_symbol_8(uint4 buffer[11], uint offset) {
   const uint scalar_offset_3 = ((offset + 0u)) / 4;
   const uint scalar_offset_4 = ((offset + 4u)) / 4;
   const uint scalar_offset_5 = ((offset + 8u)) / 4;
@@ -62,37 +66,41 @@
   const uint scalar_offset_8 = ((offset + 20u)) / 4;
   const uint scalar_offset_9 = ((offset + 24u)) / 4;
   const uint scalar_offset_10 = ((offset + 28u)) / 4;
-  const GammaTransferParams tint_symbol_8 = {asfloat(buffer[scalar_offset_3 / 4][scalar_offset_3 % 4]), asfloat(buffer[scalar_offset_4 / 4][scalar_offset_4 % 4]), asfloat(buffer[scalar_offset_5 / 4][scalar_offset_5 % 4]), asfloat(buffer[scalar_offset_6 / 4][scalar_offset_6 % 4]), asfloat(buffer[scalar_offset_7 / 4][scalar_offset_7 % 4]), asfloat(buffer[scalar_offset_8 / 4][scalar_offset_8 % 4]), asfloat(buffer[scalar_offset_9 / 4][scalar_offset_9 % 4]), buffer[scalar_offset_10 / 4][scalar_offset_10 % 4]};
-  return tint_symbol_8;
+  const GammaTransferParams tint_symbol_12 = {asfloat(buffer[scalar_offset_3 / 4][scalar_offset_3 % 4]), asfloat(buffer[scalar_offset_4 / 4][scalar_offset_4 % 4]), asfloat(buffer[scalar_offset_5 / 4][scalar_offset_5 % 4]), asfloat(buffer[scalar_offset_6 / 4][scalar_offset_6 % 4]), asfloat(buffer[scalar_offset_7 / 4][scalar_offset_7 % 4]), asfloat(buffer[scalar_offset_8 / 4][scalar_offset_8 % 4]), asfloat(buffer[scalar_offset_9 / 4][scalar_offset_9 % 4]), buffer[scalar_offset_10 / 4][scalar_offset_10 % 4]};
+  return tint_symbol_12;
 }
 
-float3x3 tint_symbol_6(uint4 buffer[11], uint offset) {
+float3x3 tint_symbol_10(uint4 buffer[11], uint offset) {
   const uint scalar_offset_11 = ((offset + 0u)) / 4;
   const uint scalar_offset_12 = ((offset + 16u)) / 4;
   const uint scalar_offset_13 = ((offset + 32u)) / 4;
   return float3x3(asfloat(buffer[scalar_offset_11 / 4].xyz), asfloat(buffer[scalar_offset_12 / 4].xyz), asfloat(buffer[scalar_offset_13 / 4].xyz));
 }
 
-ExternalTextureParams tint_symbol(uint4 buffer[11], uint offset) {
+ExternalTextureParams tint_symbol_4(uint4 buffer[11], uint offset) {
   const uint scalar_offset_14 = ((offset + 0u)) / 4;
   const uint scalar_offset_15 = ((offset + 4u)) / 4;
-  const ExternalTextureParams tint_symbol_9 = {buffer[scalar_offset_14 / 4][scalar_offset_14 % 4], buffer[scalar_offset_15 / 4][scalar_offset_15 % 4], tint_symbol_2(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 64u)), tint_symbol_4(buffer, (offset + 96u)), tint_symbol_6(buffer, (offset + 128u))};
-  return tint_symbol_9;
+  const ExternalTextureParams tint_symbol_13 = {buffer[scalar_offset_14 / 4][scalar_offset_14 % 4], buffer[scalar_offset_15 / 4][scalar_offset_15 % 4], tint_symbol_6(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 64u)), tint_symbol_8(buffer, (offset + 96u)), tint_symbol_10(buffer, (offset + 128u))};
+  return tint_symbol_13;
 }
 
 [numthreads(1, 1, 1)]
 void main() {
   int2 tint_tmp;
   t.GetDimensions(tint_tmp.x, tint_tmp.y);
-  float4 red = textureLoadExternal(t, ext_tex_plane_1, clamp((10).xx, (0).xx, int2((uint2(tint_tmp) - (1u).xx))), tint_symbol(ext_tex_params, 0u));
+  const int2 tint_symbol = tint_clamp((10).xx, (0).xx, int2((uint2(tint_tmp) - (1u).xx)));
+  float4 red = textureLoadExternal(t, ext_tex_plane_1, tint_symbol, tint_symbol_4(ext_tex_params, 0u));
   int2 tint_tmp_1;
   outImage.GetDimensions(tint_tmp_1.x, tint_tmp_1.y);
-  outImage[clamp((0).xx, (0).xx, int2((uint2(tint_tmp_1) - (1u).xx)))] = red;
+  const int2 tint_symbol_1 = tint_clamp((0).xx, (0).xx, int2((uint2(tint_tmp_1) - (1u).xx)));
+  outImage[tint_symbol_1] = red;
   int2 tint_tmp_2;
   t.GetDimensions(tint_tmp_2.x, tint_tmp_2.y);
-  float4 green = textureLoadExternal(t, ext_tex_plane_1, clamp(int2(70, 118), (0).xx, int2((uint2(tint_tmp_2) - (1u).xx))), tint_symbol(ext_tex_params, 0u));
+  const int2 tint_symbol_2 = tint_clamp(int2(70, 118), (0).xx, int2((uint2(tint_tmp_2) - (1u).xx)));
+  float4 green = textureLoadExternal(t, ext_tex_plane_1, tint_symbol_2, tint_symbol_4(ext_tex_params, 0u));
   int2 tint_tmp_3;
   outImage.GetDimensions(tint_tmp_3.x, tint_tmp_3.y);
-  outImage[clamp(int2(1, 0), (0).xx, int2((uint2(tint_tmp_3) - (1u).xx)))] = green;
+  const int2 tint_symbol_3 = tint_clamp(int2(1, 0), (0).xx, int2((uint2(tint_tmp_3) - (1u).xx)));
+  outImage[tint_symbol_3] = green;
   return;
 }
diff --git a/test/tint/bug/tint/1739.wgsl.expected.msl b/test/tint/bug/tint/1739.wgsl.expected.msl
index bcd3a46..e1802c5 100644
--- a/test/tint/bug/tint/1739.wgsl.expected.msl
+++ b/test/tint/bug/tint/1739.wgsl.expected.msl
@@ -57,11 +57,19 @@
   return float4(color, 1.0f);
 }
 
-kernel void tint_symbol(texture2d<float, access::sample> tint_symbol_1 [[texture(0)]], texture2d<float, access::sample> tint_symbol_2 [[texture(1)]], const constant ExternalTextureParams* tint_symbol_3 [[buffer(0)]], texture2d<float, access::write> tint_symbol_4 [[texture(2)]]) {
-  float4 red = textureLoadExternal(tint_symbol_1, tint_symbol_2, clamp(int2(10), int2(0), int2((uint2(uint2(tint_symbol_1.get_width(), tint_symbol_1.get_height())) - uint2(1u)))), *(tint_symbol_3));
-  tint_symbol_4.write(red, uint2(clamp(int2(0), int2(0), int2((uint2(uint2(tint_symbol_4.get_width(), tint_symbol_4.get_height())) - uint2(1u))))));
-  float4 green = textureLoadExternal(tint_symbol_1, tint_symbol_2, clamp(int2(70, 118), int2(0), int2((uint2(uint2(tint_symbol_1.get_width(), tint_symbol_1.get_height())) - uint2(1u)))), *(tint_symbol_3));
-  tint_symbol_4.write(green, uint2(clamp(int2(1, 0), int2(0), int2((uint2(uint2(tint_symbol_4.get_width(), tint_symbol_4.get_height())) - uint2(1u))))));
+int2 tint_clamp(int2 e, int2 low, int2 high) {
+  return min(max(e, low), high);
+}
+
+kernel void tint_symbol(texture2d<float, access::sample> tint_symbol_5 [[texture(0)]], texture2d<float, access::sample> tint_symbol_6 [[texture(1)]], const constant ExternalTextureParams* tint_symbol_7 [[buffer(0)]], texture2d<float, access::write> tint_symbol_8 [[texture(2)]]) {
+  int2 const tint_symbol_1 = tint_clamp(int2(10), int2(0), int2((uint2(uint2(tint_symbol_5.get_width(), tint_symbol_5.get_height())) - uint2(1u))));
+  float4 red = textureLoadExternal(tint_symbol_5, tint_symbol_6, tint_symbol_1, *(tint_symbol_7));
+  int2 const tint_symbol_2 = tint_clamp(int2(0), int2(0), int2((uint2(uint2(tint_symbol_8.get_width(), tint_symbol_8.get_height())) - uint2(1u))));
+  tint_symbol_8.write(red, uint2(tint_symbol_2));
+  int2 const tint_symbol_3 = tint_clamp(int2(70, 118), int2(0), int2((uint2(uint2(tint_symbol_5.get_width(), tint_symbol_5.get_height())) - uint2(1u))));
+  float4 green = textureLoadExternal(tint_symbol_5, tint_symbol_6, tint_symbol_3, *(tint_symbol_7));
+  int2 const tint_symbol_4 = tint_clamp(int2(1, 0), int2(0), int2((uint2(uint2(tint_symbol_8.get_width(), tint_symbol_8.get_height())) - uint2(1u))));
+  tint_symbol_8.write(green, uint2(tint_symbol_4));
   return;
 }
 
diff --git a/test/tint/bug/tint/1739.wgsl.expected.spvasm b/test/tint/bug/tint/1739.wgsl.expected.spvasm
index d40805a..2cdbccb 100644
--- a/test/tint/bug/tint/1739.wgsl.expected.spvasm
+++ b/test/tint/bug/tint/1739.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 169
+; Bound: 177
 ; Schema: 0
                OpCapability Shader
                OpCapability ImageQuery
@@ -40,6 +40,10 @@
                OpName %coord "coord"
                OpName %params_0 "params"
                OpName %color "color"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %main "main"
                OpName %red "red"
                OpName %green "green"
@@ -104,23 +108,24 @@
     %v2float = OpTypeVector %float 2
     %float_1 = OpConstant %float 1
          %90 = OpConstantNull %uint
+        %108 = OpTypeFunction %v2int %v2int %v2int %v2int
        %void = OpTypeVoid
-        %108 = OpTypeFunction %void
+        %116 = OpTypeFunction %void
      %int_10 = OpConstant %int 10
-        %117 = OpConstantComposite %v2int %int_10 %int_10
-        %118 = OpConstantNull %v2int
+        %122 = OpConstantComposite %v2int %int_10 %int_10
+        %123 = OpConstantNull %v2int
      %v2uint = OpTypeVector %uint 2
       %int_0 = OpConstant %int 0
-        %125 = OpConstantComposite %v2uint %uint_1 %uint_1
+        %130 = OpConstantComposite %v2uint %uint_1 %uint_1
      %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_ExternalTextureParams = OpTypePointer Uniform %ExternalTextureParams
 %_ptr_Function_v4float = OpTypePointer Function %v4float
-        %133 = OpConstantNull %v4float
+        %141 = OpConstantNull %v4float
      %int_70 = OpConstant %int 70
     %int_118 = OpConstant %int 118
-        %149 = OpConstantComposite %v2int %int_70 %int_118
+        %154 = OpConstantComposite %v2int %int_70 %int_118
       %int_1 = OpConstant %int 1
-        %162 = OpConstantComposite %v2int %int_1 %76
+        %168 = OpConstantComposite %v2int %int_1 %76
 %gammaCorrection = OpFunction %v3float None %19
           %v = OpFunctionParameter %v3float
      %params = OpFunctionParameter %GammaTransferParams
@@ -212,47 +217,56 @@
         %107 = OpCompositeConstruct %v4float %104 %105 %106 %float_1
                OpReturnValue %107
                OpFunctionEnd
-       %main = OpFunction %void None %108
-        %111 = OpLabel
-        %red = OpVariable %_ptr_Function_v4float Function %133
-      %green = OpVariable %_ptr_Function_v4float Function %133
-        %113 = OpLoad %3 %t
-        %114 = OpLoad %3 %ext_tex_plane_1
-        %123 = OpLoad %3 %t
-        %122 = OpImageQuerySizeLod %v2uint %123 %int_0
-        %126 = OpISub %v2uint %122 %125
-        %119 = OpBitcast %v2int %126
-        %115 = OpExtInst %v2int %25 SClamp %117 %118 %119
-        %129 = OpAccessChain %_ptr_Uniform_ExternalTextureParams %ext_tex_params %uint_0
-        %130 = OpLoad %ExternalTextureParams %129
-        %112 = OpFunctionCall %v4float %textureLoadExternal %113 %114 %115 %130
-               OpStore %red %112
-        %135 = OpLoad %18 %outImage
-        %140 = OpLoad %18 %outImage
-        %139 = OpImageQuerySize %v2uint %140
-        %141 = OpISub %v2uint %139 %125
-        %137 = OpBitcast %v2int %141
-        %136 = OpExtInst %v2int %25 SClamp %118 %118 %137
-        %142 = OpLoad %v4float %red
-               OpImageWrite %135 %136 %142
-        %144 = OpLoad %3 %t
-        %145 = OpLoad %3 %ext_tex_plane_1
-        %153 = OpLoad %3 %t
-        %152 = OpImageQuerySizeLod %v2uint %153 %int_0
-        %154 = OpISub %v2uint %152 %125
-        %150 = OpBitcast %v2int %154
-        %146 = OpExtInst %v2int %25 SClamp %149 %118 %150
-        %155 = OpAccessChain %_ptr_Uniform_ExternalTextureParams %ext_tex_params %uint_0
-        %156 = OpLoad %ExternalTextureParams %155
-        %143 = OpFunctionCall %v4float %textureLoadExternal %144 %145 %146 %156
-               OpStore %green %143
-        %159 = OpLoad %18 %outImage
-        %166 = OpLoad %18 %outImage
-        %165 = OpImageQuerySize %v2uint %166
-        %167 = OpISub %v2uint %165 %125
-        %163 = OpBitcast %v2int %167
-        %160 = OpExtInst %v2int %25 SClamp %162 %118 %163
-        %168 = OpLoad %v4float %green
-               OpImageWrite %159 %160 %168
+ %tint_clamp = OpFunction %v2int None %108
+          %e = OpFunctionParameter %v2int
+        %low = OpFunctionParameter %v2int
+       %high = OpFunctionParameter %v2int
+        %113 = OpLabel
+        %115 = OpExtInst %v2int %25 SMax %e %low
+        %114 = OpExtInst %v2int %25 SMin %115 %high
+               OpReturnValue %114
+               OpFunctionEnd
+       %main = OpFunction %void None %116
+        %119 = OpLabel
+        %red = OpVariable %_ptr_Function_v4float Function %141
+      %green = OpVariable %_ptr_Function_v4float Function %141
+        %128 = OpLoad %3 %t
+        %127 = OpImageQuerySizeLod %v2uint %128 %int_0
+        %131 = OpISub %v2uint %127 %130
+        %124 = OpBitcast %v2int %131
+        %120 = OpFunctionCall %v2int %tint_clamp %122 %123 %124
+        %133 = OpLoad %3 %t
+        %134 = OpLoad %3 %ext_tex_plane_1
+        %137 = OpAccessChain %_ptr_Uniform_ExternalTextureParams %ext_tex_params %uint_0
+        %138 = OpLoad %ExternalTextureParams %137
+        %132 = OpFunctionCall %v4float %textureLoadExternal %133 %134 %120 %138
+               OpStore %red %132
+        %146 = OpLoad %18 %outImage
+        %145 = OpImageQuerySize %v2uint %146
+        %147 = OpISub %v2uint %145 %130
+        %143 = OpBitcast %v2int %147
+        %142 = OpFunctionCall %v2int %tint_clamp %123 %123 %143
+        %149 = OpLoad %18 %outImage
+        %150 = OpLoad %v4float %red
+               OpImageWrite %149 %142 %150
+        %158 = OpLoad %3 %t
+        %157 = OpImageQuerySizeLod %v2uint %158 %int_0
+        %159 = OpISub %v2uint %157 %130
+        %155 = OpBitcast %v2int %159
+        %151 = OpFunctionCall %v2int %tint_clamp %154 %123 %155
+        %161 = OpLoad %3 %t
+        %162 = OpLoad %3 %ext_tex_plane_1
+        %163 = OpAccessChain %_ptr_Uniform_ExternalTextureParams %ext_tex_params %uint_0
+        %164 = OpLoad %ExternalTextureParams %163
+        %160 = OpFunctionCall %v4float %textureLoadExternal %161 %162 %151 %164
+               OpStore %green %160
+        %172 = OpLoad %18 %outImage
+        %171 = OpImageQuerySize %v2uint %172
+        %173 = OpISub %v2uint %171 %130
+        %169 = OpBitcast %v2int %173
+        %166 = OpFunctionCall %v2int %tint_clamp %168 %123 %169
+        %175 = OpLoad %18 %outImage
+        %176 = OpLoad %v4float %green
+               OpImageWrite %175 %166 %176
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.dxc.hlsl
index 5df22b1..2a7f794 100644
--- a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+int4 tint_clamp(int4 e, int4 low, int4 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_1a32e3() {
   int4 arg_0 = (1).xxxx;
   int4 arg_1 = (1).xxxx;
   int4 arg_2 = (1).xxxx;
-  int4 res = clamp(arg_0, arg_1, arg_2);
+  int4 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.fxc.hlsl
index 5df22b1..2a7f794 100644
--- a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+int4 tint_clamp(int4 e, int4 low, int4 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_1a32e3() {
   int4 arg_0 = (1).xxxx;
   int4 arg_1 = (1).xxxx;
   int4 arg_2 = (1).xxxx;
-  int4 res = clamp(arg_0, arg_1, arg_2);
+  int4 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.msl
index 16ff83a..eb57d27 100644
--- a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+int4 tint_clamp(int4 e, int4 low, int4 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_1a32e3() {
   int4 arg_0 = int4(1);
   int4 arg_1 = int4(1);
   int4 arg_2 = int4(1);
-  int4 res = clamp(arg_0, arg_1, arg_2);
+  int4 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.spvasm
index 5945c12..ca8ce26 100644
--- a/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/1a32e3.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 42
+; Bound: 50
 ; Schema: 0
                OpCapability Shader
-         %23 = OpExtInstImport "GLSL.std.450"
+         %18 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_1a32e3 "clamp_1a32e3"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,51 +36,61 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
         %int = OpTypeInt 32 1
       %v4int = OpTypeVector %int 4
+          %9 = OpTypeFunction %v4int %v4int %v4int %v4int
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
       %int_1 = OpConstant %int 1
-         %16 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1
+         %25 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1
 %_ptr_Function_v4int = OpTypePointer Function %v4int
-         %19 = OpConstantNull %v4int
-         %28 = OpTypeFunction %v4float
+         %28 = OpConstantNull %v4int
+         %36 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_1a32e3 = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_v4int Function %19
-      %arg_1 = OpVariable %_ptr_Function_v4int Function %19
-      %arg_2 = OpVariable %_ptr_Function_v4int Function %19
-        %res = OpVariable %_ptr_Function_v4int Function %19
-               OpStore %arg_0 %16
-               OpStore %arg_1 %16
-               OpStore %arg_2 %16
-         %24 = OpLoad %v4int %arg_0
-         %25 = OpLoad %v4int %arg_1
-         %26 = OpLoad %v4int %arg_2
-         %22 = OpExtInst %v4int %23 SClamp %24 %25 %26
-               OpStore %res %22
+ %tint_clamp = OpFunction %v4int None %9
+          %e = OpFunctionParameter %v4int
+        %low = OpFunctionParameter %v4int
+       %high = OpFunctionParameter %v4int
+         %16 = OpLabel
+         %19 = OpExtInst %v4int %18 SMax %e %low
+         %17 = OpExtInst %v4int %18 SMin %19 %high
+               OpReturnValue %17
+               OpFunctionEnd
+%clamp_1a32e3 = OpFunction %void None %20
+         %23 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_v4int Function %28
+      %arg_1 = OpVariable %_ptr_Function_v4int Function %28
+      %arg_2 = OpVariable %_ptr_Function_v4int Function %28
+        %res = OpVariable %_ptr_Function_v4int Function %28
+               OpStore %arg_0 %25
+               OpStore %arg_1 %25
+               OpStore %arg_2 %25
+         %32 = OpLoad %v4int %arg_0
+         %33 = OpLoad %v4int %arg_1
+         %34 = OpLoad %v4int %arg_2
+         %31 = OpFunctionCall %v4int %tint_clamp %32 %33 %34
+               OpStore %res %31
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %28
-         %30 = OpLabel
-         %31 = OpFunctionCall %void %clamp_1a32e3
+%vertex_main_inner = OpFunction %v4float None %36
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %clamp_1a32e3
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %33 = OpLabel
-         %34 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %34
+%vertex_main = OpFunction %void None %20
+         %41 = OpLabel
+         %42 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %42
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %37 = OpLabel
-         %38 = OpFunctionCall %void %clamp_1a32e3
+%fragment_main = OpFunction %void None %20
+         %45 = OpLabel
+         %46 = OpFunctionCall %void %clamp_1a32e3
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %40 = OpLabel
-         %41 = OpFunctionCall %void %clamp_1a32e3
+%compute_main = OpFunction %void None %20
+         %48 = OpLabel
+         %49 = OpFunctionCall %void %clamp_1a32e3
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.dxc.hlsl
index 50830b0..8b32676 100644
--- a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+uint3 tint_clamp(uint3 e, uint3 low, uint3 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_548fc7() {
   uint3 arg_0 = (1u).xxx;
   uint3 arg_1 = (1u).xxx;
   uint3 arg_2 = (1u).xxx;
-  uint3 res = clamp(arg_0, arg_1, arg_2);
+  uint3 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.fxc.hlsl
index 50830b0..8b32676 100644
--- a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+uint3 tint_clamp(uint3 e, uint3 low, uint3 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_548fc7() {
   uint3 arg_0 = (1u).xxx;
   uint3 arg_1 = (1u).xxx;
   uint3 arg_2 = (1u).xxx;
-  uint3 res = clamp(arg_0, arg_1, arg_2);
+  uint3 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.msl
index d7b9b9c..4a691dc 100644
--- a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+uint3 tint_clamp(uint3 e, uint3 low, uint3 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_548fc7() {
   uint3 arg_0 = uint3(1u);
   uint3 arg_1 = uint3(1u);
   uint3 arg_2 = uint3(1u);
-  uint3 res = clamp(arg_0, arg_1, arg_2);
+  uint3 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.spvasm
index d1dabc9..5c3020c 100644
--- a/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/548fc7.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 42
+; Bound: 50
 ; Schema: 0
                OpCapability Shader
-         %23 = OpExtInstImport "GLSL.std.450"
+         %18 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_548fc7 "clamp_548fc7"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,51 +36,61 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
        %uint = OpTypeInt 32 0
      %v3uint = OpTypeVector %uint 3
+          %9 = OpTypeFunction %v3uint %v3uint %v3uint %v3uint
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
      %uint_1 = OpConstant %uint 1
-         %16 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+         %25 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
 %_ptr_Function_v3uint = OpTypePointer Function %v3uint
-         %19 = OpConstantNull %v3uint
-         %28 = OpTypeFunction %v4float
+         %28 = OpConstantNull %v3uint
+         %36 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_548fc7 = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_v3uint Function %19
-      %arg_1 = OpVariable %_ptr_Function_v3uint Function %19
-      %arg_2 = OpVariable %_ptr_Function_v3uint Function %19
-        %res = OpVariable %_ptr_Function_v3uint Function %19
-               OpStore %arg_0 %16
-               OpStore %arg_1 %16
-               OpStore %arg_2 %16
-         %24 = OpLoad %v3uint %arg_0
-         %25 = OpLoad %v3uint %arg_1
-         %26 = OpLoad %v3uint %arg_2
-         %22 = OpExtInst %v3uint %23 UClamp %24 %25 %26
-               OpStore %res %22
+ %tint_clamp = OpFunction %v3uint None %9
+          %e = OpFunctionParameter %v3uint
+        %low = OpFunctionParameter %v3uint
+       %high = OpFunctionParameter %v3uint
+         %16 = OpLabel
+         %19 = OpExtInst %v3uint %18 UMax %e %low
+         %17 = OpExtInst %v3uint %18 UMin %19 %high
+               OpReturnValue %17
+               OpFunctionEnd
+%clamp_548fc7 = OpFunction %void None %20
+         %23 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_v3uint Function %28
+      %arg_1 = OpVariable %_ptr_Function_v3uint Function %28
+      %arg_2 = OpVariable %_ptr_Function_v3uint Function %28
+        %res = OpVariable %_ptr_Function_v3uint Function %28
+               OpStore %arg_0 %25
+               OpStore %arg_1 %25
+               OpStore %arg_2 %25
+         %32 = OpLoad %v3uint %arg_0
+         %33 = OpLoad %v3uint %arg_1
+         %34 = OpLoad %v3uint %arg_2
+         %31 = OpFunctionCall %v3uint %tint_clamp %32 %33 %34
+               OpStore %res %31
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %28
-         %30 = OpLabel
-         %31 = OpFunctionCall %void %clamp_548fc7
+%vertex_main_inner = OpFunction %v4float None %36
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %clamp_548fc7
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %33 = OpLabel
-         %34 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %34
+%vertex_main = OpFunction %void None %20
+         %41 = OpLabel
+         %42 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %42
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %37 = OpLabel
-         %38 = OpFunctionCall %void %clamp_548fc7
+%fragment_main = OpFunction %void None %20
+         %45 = OpLabel
+         %46 = OpFunctionCall %void %clamp_548fc7
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %40 = OpLabel
-         %41 = OpFunctionCall %void %clamp_548fc7
+%compute_main = OpFunction %void None %20
+         %48 = OpLabel
+         %49 = OpFunctionCall %void %clamp_548fc7
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.dxc.hlsl
index aa13f0d..8f444fc 100644
--- a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+int3 tint_clamp(int3 e, int3 low, int3 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_5f0819() {
   int3 arg_0 = (1).xxx;
   int3 arg_1 = (1).xxx;
   int3 arg_2 = (1).xxx;
-  int3 res = clamp(arg_0, arg_1, arg_2);
+  int3 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.fxc.hlsl
index aa13f0d..8f444fc 100644
--- a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+int3 tint_clamp(int3 e, int3 low, int3 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_5f0819() {
   int3 arg_0 = (1).xxx;
   int3 arg_1 = (1).xxx;
   int3 arg_2 = (1).xxx;
-  int3 res = clamp(arg_0, arg_1, arg_2);
+  int3 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.msl
index e408621..a0a1dca 100644
--- a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+int3 tint_clamp(int3 e, int3 low, int3 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_5f0819() {
   int3 arg_0 = int3(1);
   int3 arg_1 = int3(1);
   int3 arg_2 = int3(1);
-  int3 res = clamp(arg_0, arg_1, arg_2);
+  int3 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.spvasm
index 44ca5d7..0bc5ea2 100644
--- a/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/5f0819.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 42
+; Bound: 50
 ; Schema: 0
                OpCapability Shader
-         %23 = OpExtInstImport "GLSL.std.450"
+         %18 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_5f0819 "clamp_5f0819"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,51 +36,61 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
         %int = OpTypeInt 32 1
       %v3int = OpTypeVector %int 3
+          %9 = OpTypeFunction %v3int %v3int %v3int %v3int
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
       %int_1 = OpConstant %int 1
-         %16 = OpConstantComposite %v3int %int_1 %int_1 %int_1
+         %25 = OpConstantComposite %v3int %int_1 %int_1 %int_1
 %_ptr_Function_v3int = OpTypePointer Function %v3int
-         %19 = OpConstantNull %v3int
-         %28 = OpTypeFunction %v4float
+         %28 = OpConstantNull %v3int
+         %36 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_5f0819 = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_v3int Function %19
-      %arg_1 = OpVariable %_ptr_Function_v3int Function %19
-      %arg_2 = OpVariable %_ptr_Function_v3int Function %19
-        %res = OpVariable %_ptr_Function_v3int Function %19
-               OpStore %arg_0 %16
-               OpStore %arg_1 %16
-               OpStore %arg_2 %16
-         %24 = OpLoad %v3int %arg_0
-         %25 = OpLoad %v3int %arg_1
-         %26 = OpLoad %v3int %arg_2
-         %22 = OpExtInst %v3int %23 SClamp %24 %25 %26
-               OpStore %res %22
+ %tint_clamp = OpFunction %v3int None %9
+          %e = OpFunctionParameter %v3int
+        %low = OpFunctionParameter %v3int
+       %high = OpFunctionParameter %v3int
+         %16 = OpLabel
+         %19 = OpExtInst %v3int %18 SMax %e %low
+         %17 = OpExtInst %v3int %18 SMin %19 %high
+               OpReturnValue %17
+               OpFunctionEnd
+%clamp_5f0819 = OpFunction %void None %20
+         %23 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_v3int Function %28
+      %arg_1 = OpVariable %_ptr_Function_v3int Function %28
+      %arg_2 = OpVariable %_ptr_Function_v3int Function %28
+        %res = OpVariable %_ptr_Function_v3int Function %28
+               OpStore %arg_0 %25
+               OpStore %arg_1 %25
+               OpStore %arg_2 %25
+         %32 = OpLoad %v3int %arg_0
+         %33 = OpLoad %v3int %arg_1
+         %34 = OpLoad %v3int %arg_2
+         %31 = OpFunctionCall %v3int %tint_clamp %32 %33 %34
+               OpStore %res %31
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %28
-         %30 = OpLabel
-         %31 = OpFunctionCall %void %clamp_5f0819
+%vertex_main_inner = OpFunction %v4float None %36
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %clamp_5f0819
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %33 = OpLabel
-         %34 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %34
+%vertex_main = OpFunction %void None %20
+         %41 = OpLabel
+         %42 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %42
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %37 = OpLabel
-         %38 = OpFunctionCall %void %clamp_5f0819
+%fragment_main = OpFunction %void None %20
+         %45 = OpLabel
+         %46 = OpFunctionCall %void %clamp_5f0819
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %40 = OpLabel
-         %41 = OpFunctionCall %void %clamp_5f0819
+%compute_main = OpFunction %void None %20
+         %48 = OpLabel
+         %49 = OpFunctionCall %void %clamp_5f0819
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.dxc.hlsl
index 77e3d80..13e317b 100644
--- a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+int2 tint_clamp(int2 e, int2 low, int2 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_6c1749() {
   int2 arg_0 = (1).xx;
   int2 arg_1 = (1).xx;
   int2 arg_2 = (1).xx;
-  int2 res = clamp(arg_0, arg_1, arg_2);
+  int2 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.fxc.hlsl
index 77e3d80..13e317b 100644
--- a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+int2 tint_clamp(int2 e, int2 low, int2 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_6c1749() {
   int2 arg_0 = (1).xx;
   int2 arg_1 = (1).xx;
   int2 arg_2 = (1).xx;
-  int2 res = clamp(arg_0, arg_1, arg_2);
+  int2 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.msl
index dd11a3f..2b6d214 100644
--- a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+int2 tint_clamp(int2 e, int2 low, int2 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_6c1749() {
   int2 arg_0 = int2(1);
   int2 arg_1 = int2(1);
   int2 arg_2 = int2(1);
-  int2 res = clamp(arg_0, arg_1, arg_2);
+  int2 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.spvasm
index 490c50a..41d2edb 100644
--- a/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/6c1749.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 42
+; Bound: 50
 ; Schema: 0
                OpCapability Shader
-         %23 = OpExtInstImport "GLSL.std.450"
+         %18 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_6c1749 "clamp_6c1749"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,51 +36,61 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
         %int = OpTypeInt 32 1
       %v2int = OpTypeVector %int 2
+          %9 = OpTypeFunction %v2int %v2int %v2int %v2int
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
       %int_1 = OpConstant %int 1
-         %16 = OpConstantComposite %v2int %int_1 %int_1
+         %25 = OpConstantComposite %v2int %int_1 %int_1
 %_ptr_Function_v2int = OpTypePointer Function %v2int
-         %19 = OpConstantNull %v2int
-         %28 = OpTypeFunction %v4float
+         %28 = OpConstantNull %v2int
+         %36 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_6c1749 = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_v2int Function %19
-      %arg_1 = OpVariable %_ptr_Function_v2int Function %19
-      %arg_2 = OpVariable %_ptr_Function_v2int Function %19
-        %res = OpVariable %_ptr_Function_v2int Function %19
-               OpStore %arg_0 %16
-               OpStore %arg_1 %16
-               OpStore %arg_2 %16
-         %24 = OpLoad %v2int %arg_0
-         %25 = OpLoad %v2int %arg_1
-         %26 = OpLoad %v2int %arg_2
-         %22 = OpExtInst %v2int %23 SClamp %24 %25 %26
-               OpStore %res %22
+ %tint_clamp = OpFunction %v2int None %9
+          %e = OpFunctionParameter %v2int
+        %low = OpFunctionParameter %v2int
+       %high = OpFunctionParameter %v2int
+         %16 = OpLabel
+         %19 = OpExtInst %v2int %18 SMax %e %low
+         %17 = OpExtInst %v2int %18 SMin %19 %high
+               OpReturnValue %17
+               OpFunctionEnd
+%clamp_6c1749 = OpFunction %void None %20
+         %23 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_v2int Function %28
+      %arg_1 = OpVariable %_ptr_Function_v2int Function %28
+      %arg_2 = OpVariable %_ptr_Function_v2int Function %28
+        %res = OpVariable %_ptr_Function_v2int Function %28
+               OpStore %arg_0 %25
+               OpStore %arg_1 %25
+               OpStore %arg_2 %25
+         %32 = OpLoad %v2int %arg_0
+         %33 = OpLoad %v2int %arg_1
+         %34 = OpLoad %v2int %arg_2
+         %31 = OpFunctionCall %v2int %tint_clamp %32 %33 %34
+               OpStore %res %31
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %28
-         %30 = OpLabel
-         %31 = OpFunctionCall %void %clamp_6c1749
+%vertex_main_inner = OpFunction %v4float None %36
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %clamp_6c1749
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %33 = OpLabel
-         %34 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %34
+%vertex_main = OpFunction %void None %20
+         %41 = OpLabel
+         %42 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %42
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %37 = OpLabel
-         %38 = OpFunctionCall %void %clamp_6c1749
+%fragment_main = OpFunction %void None %20
+         %45 = OpLabel
+         %46 = OpFunctionCall %void %clamp_6c1749
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %40 = OpLabel
-         %41 = OpFunctionCall %void %clamp_6c1749
+%compute_main = OpFunction %void None %20
+         %48 = OpLabel
+         %49 = OpFunctionCall %void %clamp_6c1749
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.dxc.hlsl
index bb60ac2..09d5095 100644
--- a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+uint2 tint_clamp(uint2 e, uint2 low, uint2 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_7706d7() {
   uint2 arg_0 = (1u).xx;
   uint2 arg_1 = (1u).xx;
   uint2 arg_2 = (1u).xx;
-  uint2 res = clamp(arg_0, arg_1, arg_2);
+  uint2 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.fxc.hlsl
index bb60ac2..09d5095 100644
--- a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+uint2 tint_clamp(uint2 e, uint2 low, uint2 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_7706d7() {
   uint2 arg_0 = (1u).xx;
   uint2 arg_1 = (1u).xx;
   uint2 arg_2 = (1u).xx;
-  uint2 res = clamp(arg_0, arg_1, arg_2);
+  uint2 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.msl
index e70e2d5..086ac40 100644
--- a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+uint2 tint_clamp(uint2 e, uint2 low, uint2 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_7706d7() {
   uint2 arg_0 = uint2(1u);
   uint2 arg_1 = uint2(1u);
   uint2 arg_2 = uint2(1u);
-  uint2 res = clamp(arg_0, arg_1, arg_2);
+  uint2 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.spvasm
index 5bd6587..643fa83 100644
--- a/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/7706d7.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 42
+; Bound: 50
 ; Schema: 0
                OpCapability Shader
-         %23 = OpExtInstImport "GLSL.std.450"
+         %18 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_7706d7 "clamp_7706d7"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,51 +36,61 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
        %uint = OpTypeInt 32 0
      %v2uint = OpTypeVector %uint 2
+          %9 = OpTypeFunction %v2uint %v2uint %v2uint %v2uint
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
      %uint_1 = OpConstant %uint 1
-         %16 = OpConstantComposite %v2uint %uint_1 %uint_1
+         %25 = OpConstantComposite %v2uint %uint_1 %uint_1
 %_ptr_Function_v2uint = OpTypePointer Function %v2uint
-         %19 = OpConstantNull %v2uint
-         %28 = OpTypeFunction %v4float
+         %28 = OpConstantNull %v2uint
+         %36 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_7706d7 = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_v2uint Function %19
-      %arg_1 = OpVariable %_ptr_Function_v2uint Function %19
-      %arg_2 = OpVariable %_ptr_Function_v2uint Function %19
-        %res = OpVariable %_ptr_Function_v2uint Function %19
-               OpStore %arg_0 %16
-               OpStore %arg_1 %16
-               OpStore %arg_2 %16
-         %24 = OpLoad %v2uint %arg_0
-         %25 = OpLoad %v2uint %arg_1
-         %26 = OpLoad %v2uint %arg_2
-         %22 = OpExtInst %v2uint %23 UClamp %24 %25 %26
-               OpStore %res %22
+ %tint_clamp = OpFunction %v2uint None %9
+          %e = OpFunctionParameter %v2uint
+        %low = OpFunctionParameter %v2uint
+       %high = OpFunctionParameter %v2uint
+         %16 = OpLabel
+         %19 = OpExtInst %v2uint %18 UMax %e %low
+         %17 = OpExtInst %v2uint %18 UMin %19 %high
+               OpReturnValue %17
+               OpFunctionEnd
+%clamp_7706d7 = OpFunction %void None %20
+         %23 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_v2uint Function %28
+      %arg_1 = OpVariable %_ptr_Function_v2uint Function %28
+      %arg_2 = OpVariable %_ptr_Function_v2uint Function %28
+        %res = OpVariable %_ptr_Function_v2uint Function %28
+               OpStore %arg_0 %25
+               OpStore %arg_1 %25
+               OpStore %arg_2 %25
+         %32 = OpLoad %v2uint %arg_0
+         %33 = OpLoad %v2uint %arg_1
+         %34 = OpLoad %v2uint %arg_2
+         %31 = OpFunctionCall %v2uint %tint_clamp %32 %33 %34
+               OpStore %res %31
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %28
-         %30 = OpLabel
-         %31 = OpFunctionCall %void %clamp_7706d7
+%vertex_main_inner = OpFunction %v4float None %36
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %clamp_7706d7
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %33 = OpLabel
-         %34 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %34
+%vertex_main = OpFunction %void None %20
+         %41 = OpLabel
+         %42 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %42
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %37 = OpLabel
-         %38 = OpFunctionCall %void %clamp_7706d7
+%fragment_main = OpFunction %void None %20
+         %45 = OpLabel
+         %46 = OpFunctionCall %void %clamp_7706d7
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %40 = OpLabel
-         %41 = OpFunctionCall %void %clamp_7706d7
+%compute_main = OpFunction %void None %20
+         %48 = OpLabel
+         %49 = OpFunctionCall %void %clamp_7706d7
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.dxc.hlsl
index ec730da..fbba325 100644
--- a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+uint tint_clamp(uint e, uint low, uint high) {
+  return min(max(e, low), high);
+}
+
 void clamp_a2de25() {
   uint arg_0 = 1u;
   uint arg_1 = 1u;
   uint arg_2 = 1u;
-  uint res = clamp(arg_0, arg_1, arg_2);
+  uint res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.fxc.hlsl
index ec730da..fbba325 100644
--- a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+uint tint_clamp(uint e, uint low, uint high) {
+  return min(max(e, low), high);
+}
+
 void clamp_a2de25() {
   uint arg_0 = 1u;
   uint arg_1 = 1u;
   uint arg_2 = 1u;
-  uint res = clamp(arg_0, arg_1, arg_2);
+  uint res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.msl
index e22ed1b..f995afc 100644
--- a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+uint tint_clamp(uint e, uint low, uint high) {
+  return min(max(e, low), high);
+}
+
 void clamp_a2de25() {
   uint arg_0 = 1u;
   uint arg_1 = 1u;
   uint arg_2 = 1u;
-  uint res = clamp(arg_0, arg_1, arg_2);
+  uint res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.spvasm
index 3e53f30..a4a2990 100644
--- a/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/a2de25.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 40
+; Bound: 48
 ; Schema: 0
                OpCapability Shader
-         %21 = OpExtInstImport "GLSL.std.450"
+         %17 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_a2de25 "clamp_a2de25"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,49 +36,59 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
        %uint = OpTypeInt 32 0
+          %9 = OpTypeFunction %uint %uint %uint %uint
+       %void = OpTypeVoid
+         %19 = OpTypeFunction %void
      %uint_1 = OpConstant %uint 1
 %_ptr_Function_uint = OpTypePointer Function %uint
-         %17 = OpConstantNull %uint
-         %26 = OpTypeFunction %v4float
+         %26 = OpConstantNull %uint
+         %34 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_a2de25 = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_uint Function %17
-      %arg_1 = OpVariable %_ptr_Function_uint Function %17
-      %arg_2 = OpVariable %_ptr_Function_uint Function %17
-        %res = OpVariable %_ptr_Function_uint Function %17
+ %tint_clamp = OpFunction %uint None %9
+          %e = OpFunctionParameter %uint
+        %low = OpFunctionParameter %uint
+       %high = OpFunctionParameter %uint
+         %15 = OpLabel
+         %18 = OpExtInst %uint %17 UMax %e %low
+         %16 = OpExtInst %uint %17 UMin %18 %high
+               OpReturnValue %16
+               OpFunctionEnd
+%clamp_a2de25 = OpFunction %void None %19
+         %22 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_uint Function %26
+      %arg_1 = OpVariable %_ptr_Function_uint Function %26
+      %arg_2 = OpVariable %_ptr_Function_uint Function %26
+        %res = OpVariable %_ptr_Function_uint Function %26
                OpStore %arg_0 %uint_1
                OpStore %arg_1 %uint_1
                OpStore %arg_2 %uint_1
-         %22 = OpLoad %uint %arg_0
-         %23 = OpLoad %uint %arg_1
-         %24 = OpLoad %uint %arg_2
-         %20 = OpExtInst %uint %21 UClamp %22 %23 %24
-               OpStore %res %20
+         %30 = OpLoad %uint %arg_0
+         %31 = OpLoad %uint %arg_1
+         %32 = OpLoad %uint %arg_2
+         %29 = OpFunctionCall %uint %tint_clamp %30 %31 %32
+               OpStore %res %29
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %26
-         %28 = OpLabel
-         %29 = OpFunctionCall %void %clamp_a2de25
+%vertex_main_inner = OpFunction %v4float None %34
+         %36 = OpLabel
+         %37 = OpFunctionCall %void %clamp_a2de25
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %31 = OpLabel
-         %32 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %32
+%vertex_main = OpFunction %void None %19
+         %39 = OpLabel
+         %40 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %40
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %35 = OpLabel
-         %36 = OpFunctionCall %void %clamp_a2de25
+%fragment_main = OpFunction %void None %19
+         %43 = OpLabel
+         %44 = OpFunctionCall %void %clamp_a2de25
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %38 = OpLabel
-         %39 = OpFunctionCall %void %clamp_a2de25
+%compute_main = OpFunction %void None %19
+         %46 = OpLabel
+         %47 = OpFunctionCall %void %clamp_a2de25
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.dxc.hlsl
index 42208eb..88d36c6 100644
--- a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+int tint_clamp(int e, int low, int high) {
+  return min(max(e, low), high);
+}
+
 void clamp_b07c65() {
   int arg_0 = 1;
   int arg_1 = 1;
   int arg_2 = 1;
-  int res = clamp(arg_0, arg_1, arg_2);
+  int res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.fxc.hlsl
index 42208eb..88d36c6 100644
--- a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+int tint_clamp(int e, int low, int high) {
+  return min(max(e, low), high);
+}
+
 void clamp_b07c65() {
   int arg_0 = 1;
   int arg_1 = 1;
   int arg_2 = 1;
-  int res = clamp(arg_0, arg_1, arg_2);
+  int res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.msl
index 90b191c..ee00853 100644
--- a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+int tint_clamp(int e, int low, int high) {
+  return min(max(e, low), high);
+}
+
 void clamp_b07c65() {
   int arg_0 = 1;
   int arg_1 = 1;
   int arg_2 = 1;
-  int res = clamp(arg_0, arg_1, arg_2);
+  int res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.spvasm
index e593502..007433e 100644
--- a/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/b07c65.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 40
+; Bound: 48
 ; Schema: 0
                OpCapability Shader
-         %21 = OpExtInstImport "GLSL.std.450"
+         %17 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_b07c65 "clamp_b07c65"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,49 +36,59 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
         %int = OpTypeInt 32 1
+          %9 = OpTypeFunction %int %int %int %int
+       %void = OpTypeVoid
+         %19 = OpTypeFunction %void
       %int_1 = OpConstant %int 1
 %_ptr_Function_int = OpTypePointer Function %int
-         %17 = OpConstantNull %int
-         %26 = OpTypeFunction %v4float
+         %26 = OpConstantNull %int
+         %34 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_b07c65 = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_int Function %17
-      %arg_1 = OpVariable %_ptr_Function_int Function %17
-      %arg_2 = OpVariable %_ptr_Function_int Function %17
-        %res = OpVariable %_ptr_Function_int Function %17
+ %tint_clamp = OpFunction %int None %9
+          %e = OpFunctionParameter %int
+        %low = OpFunctionParameter %int
+       %high = OpFunctionParameter %int
+         %15 = OpLabel
+         %18 = OpExtInst %int %17 SMax %e %low
+         %16 = OpExtInst %int %17 SMin %18 %high
+               OpReturnValue %16
+               OpFunctionEnd
+%clamp_b07c65 = OpFunction %void None %19
+         %22 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_int Function %26
+      %arg_1 = OpVariable %_ptr_Function_int Function %26
+      %arg_2 = OpVariable %_ptr_Function_int Function %26
+        %res = OpVariable %_ptr_Function_int Function %26
                OpStore %arg_0 %int_1
                OpStore %arg_1 %int_1
                OpStore %arg_2 %int_1
-         %22 = OpLoad %int %arg_0
-         %23 = OpLoad %int %arg_1
-         %24 = OpLoad %int %arg_2
-         %20 = OpExtInst %int %21 SClamp %22 %23 %24
-               OpStore %res %20
+         %30 = OpLoad %int %arg_0
+         %31 = OpLoad %int %arg_1
+         %32 = OpLoad %int %arg_2
+         %29 = OpFunctionCall %int %tint_clamp %30 %31 %32
+               OpStore %res %29
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %26
-         %28 = OpLabel
-         %29 = OpFunctionCall %void %clamp_b07c65
+%vertex_main_inner = OpFunction %v4float None %34
+         %36 = OpLabel
+         %37 = OpFunctionCall %void %clamp_b07c65
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %31 = OpLabel
-         %32 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %32
+%vertex_main = OpFunction %void None %19
+         %39 = OpLabel
+         %40 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %40
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %35 = OpLabel
-         %36 = OpFunctionCall %void %clamp_b07c65
+%fragment_main = OpFunction %void None %19
+         %43 = OpLabel
+         %44 = OpFunctionCall %void %clamp_b07c65
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %38 = OpLabel
-         %39 = OpFunctionCall %void %clamp_b07c65
+%compute_main = OpFunction %void None %19
+         %46 = OpLabel
+         %47 = OpFunctionCall %void %clamp_b07c65
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.dxc.hlsl b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.dxc.hlsl
index 871fa94..6a37b5e 100644
--- a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.dxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.dxc.hlsl
@@ -1,8 +1,12 @@
+uint4 tint_clamp(uint4 e, uint4 low, uint4 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_bd43ce() {
   uint4 arg_0 = (1u).xxxx;
   uint4 arg_1 = (1u).xxxx;
   uint4 arg_2 = (1u).xxxx;
-  uint4 res = clamp(arg_0, arg_1, arg_2);
+  uint4 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.fxc.hlsl b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.fxc.hlsl
index 871fa94..6a37b5e 100644
--- a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.fxc.hlsl
+++ b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.fxc.hlsl
@@ -1,8 +1,12 @@
+uint4 tint_clamp(uint4 e, uint4 low, uint4 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_bd43ce() {
   uint4 arg_0 = (1u).xxxx;
   uint4 arg_1 = (1u).xxxx;
   uint4 arg_2 = (1u).xxxx;
-  uint4 res = clamp(arg_0, arg_1, arg_2);
+  uint4 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.msl b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.msl
index b4af656..b4e465d 100644
--- a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.msl
@@ -1,11 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+uint4 tint_clamp(uint4 e, uint4 low, uint4 high) {
+  return min(max(e, low), high);
+}
+
 void clamp_bd43ce() {
   uint4 arg_0 = uint4(1u);
   uint4 arg_1 = uint4(1u);
   uint4 arg_2 = uint4(1u);
-  uint4 res = clamp(arg_0, arg_1, arg_2);
+  uint4 res = tint_clamp(arg_0, arg_1, arg_2);
 }
 
 struct tint_symbol {
diff --git a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.spvasm b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.spvasm
index 465e316..6751acc 100644
--- a/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/var/clamp/bd43ce.wgsl.expected.spvasm
@@ -1,10 +1,10 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 42
+; Bound: 50
 ; Schema: 0
                OpCapability Shader
-         %23 = OpExtInstImport "GLSL.std.450"
+         %18 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %vertex_main "vertex_main" %value %vertex_point_size
                OpEntryPoint Fragment %fragment_main "fragment_main"
@@ -13,6 +13,10 @@
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpName %value "value"
                OpName %vertex_point_size "vertex_point_size"
+               OpName %tint_clamp "tint_clamp"
+               OpName %e "e"
+               OpName %low "low"
+               OpName %high "high"
                OpName %clamp_bd43ce "clamp_bd43ce"
                OpName %arg_0 "arg_0"
                OpName %arg_1 "arg_1"
@@ -32,51 +36,61 @@
 %_ptr_Output_float = OpTypePointer Output %float
           %8 = OpConstantNull %float
 %vertex_point_size = OpVariable %_ptr_Output_float Output %8
-       %void = OpTypeVoid
-          %9 = OpTypeFunction %void
        %uint = OpTypeInt 32 0
      %v4uint = OpTypeVector %uint 4
+          %9 = OpTypeFunction %v4uint %v4uint %v4uint %v4uint
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
      %uint_1 = OpConstant %uint 1
-         %16 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1
+         %25 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1
 %_ptr_Function_v4uint = OpTypePointer Function %v4uint
-         %19 = OpConstantNull %v4uint
-         %28 = OpTypeFunction %v4float
+         %28 = OpConstantNull %v4uint
+         %36 = OpTypeFunction %v4float
     %float_1 = OpConstant %float 1
-%clamp_bd43ce = OpFunction %void None %9
-         %12 = OpLabel
-      %arg_0 = OpVariable %_ptr_Function_v4uint Function %19
-      %arg_1 = OpVariable %_ptr_Function_v4uint Function %19
-      %arg_2 = OpVariable %_ptr_Function_v4uint Function %19
-        %res = OpVariable %_ptr_Function_v4uint Function %19
-               OpStore %arg_0 %16
-               OpStore %arg_1 %16
-               OpStore %arg_2 %16
-         %24 = OpLoad %v4uint %arg_0
-         %25 = OpLoad %v4uint %arg_1
-         %26 = OpLoad %v4uint %arg_2
-         %22 = OpExtInst %v4uint %23 UClamp %24 %25 %26
-               OpStore %res %22
+ %tint_clamp = OpFunction %v4uint None %9
+          %e = OpFunctionParameter %v4uint
+        %low = OpFunctionParameter %v4uint
+       %high = OpFunctionParameter %v4uint
+         %16 = OpLabel
+         %19 = OpExtInst %v4uint %18 UMax %e %low
+         %17 = OpExtInst %v4uint %18 UMin %19 %high
+               OpReturnValue %17
+               OpFunctionEnd
+%clamp_bd43ce = OpFunction %void None %20
+         %23 = OpLabel
+      %arg_0 = OpVariable %_ptr_Function_v4uint Function %28
+      %arg_1 = OpVariable %_ptr_Function_v4uint Function %28
+      %arg_2 = OpVariable %_ptr_Function_v4uint Function %28
+        %res = OpVariable %_ptr_Function_v4uint Function %28
+               OpStore %arg_0 %25
+               OpStore %arg_1 %25
+               OpStore %arg_2 %25
+         %32 = OpLoad %v4uint %arg_0
+         %33 = OpLoad %v4uint %arg_1
+         %34 = OpLoad %v4uint %arg_2
+         %31 = OpFunctionCall %v4uint %tint_clamp %32 %33 %34
+               OpStore %res %31
                OpReturn
                OpFunctionEnd
-%vertex_main_inner = OpFunction %v4float None %28
-         %30 = OpLabel
-         %31 = OpFunctionCall %void %clamp_bd43ce
+%vertex_main_inner = OpFunction %v4float None %36
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %clamp_bd43ce
                OpReturnValue %5
                OpFunctionEnd
-%vertex_main = OpFunction %void None %9
-         %33 = OpLabel
-         %34 = OpFunctionCall %v4float %vertex_main_inner
-               OpStore %value %34
+%vertex_main = OpFunction %void None %20
+         %41 = OpLabel
+         %42 = OpFunctionCall %v4float %vertex_main_inner
+               OpStore %value %42
                OpStore %vertex_point_size %float_1
                OpReturn
                OpFunctionEnd
-%fragment_main = OpFunction %void None %9
-         %37 = OpLabel
-         %38 = OpFunctionCall %void %clamp_bd43ce
+%fragment_main = OpFunction %void None %20
+         %45 = OpLabel
+         %46 = OpFunctionCall %void %clamp_bd43ce
                OpReturn
                OpFunctionEnd
-%compute_main = OpFunction %void None %9
-         %40 = OpLabel
-         %41 = OpFunctionCall %void %clamp_bd43ce
+%compute_main = OpFunction %void None %20
+         %48 = OpLabel
+         %49 = OpFunctionCall %void %clamp_bd43ce
                OpReturn
                OpFunctionEnd