MSL: fix i32 INT_MIN literal emitted as `long` instead of `int`

Bug: tint:124
Change-Id: Ie632b78cd67948b65e823f0a3c52fda7ef7343f3
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/60440
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index c5220a0..1307f20 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <cmath>
 #include <iomanip>
+#include <limits>
 #include <utility>
 #include <vector>
 
@@ -1246,7 +1247,17 @@
       out << FloatToString(fl->value()) << "f";
     }
   } else if (auto* sl = lit->As<ast::SintLiteral>()) {
-    out << sl->value();
+    // MSL (and C++) parse `-2147483648` as a `long` because it parses unary
+    // minus and `2147483648` as separate tokens, and the latter doesn't
+    // fit into an (32-bit) `int`. WGSL, OTOH, parses this as an `i32`. To avoid
+    // issues with `long` to `int` casts, emit `(2147483647 - 1)` instead, which
+    // ensures the expression type is `int`.
+    const auto int_min = std::numeric_limits<int32_t>::min();
+    if (sl->value_as_i32() == int_min) {
+      out << "(" << int_min + 1 << " - 1)";
+    } else {
+      out << sl->value();
+    }
   } else if (auto* ul = lit->As<ast::UintLiteral>()) {
     out << ul->value() << "u";
   } else {
diff --git a/src/writer/msl/generator_impl_cast_test.cc b/src/writer/msl/generator_impl_cast_test.cc
index 6f66478..c871382 100644
--- a/src/writer/msl/generator_impl_cast_test.cc
+++ b/src/writer/msl/generator_impl_cast_test.cc
@@ -43,6 +43,17 @@
   EXPECT_EQ(out.str(), "float3(int3(1, 2, 3))");
 }
 
+TEST_F(MslGeneratorImplTest, EmitExpression_Cast_IntMin) {
+  auto* cast = Construct<u32>(std::numeric_limits<int32_t>::min());
+  WrapInFunction(cast);
+
+  GeneratorImpl& gen = Build();
+
+  std::stringstream out;
+  ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
+  EXPECT_EQ(out.str(), "uint((-2147483647 - 1))");
+}
+
 }  // namespace
 }  // namespace msl
 }  // namespace writer
diff --git a/src/writer/msl/generator_impl_unary_op_test.cc b/src/writer/msl/generator_impl_unary_op_test.cc
index 9254494..31eac9c 100644
--- a/src/writer/msl/generator_impl_unary_op_test.cc
+++ b/src/writer/msl/generator_impl_unary_op_test.cc
@@ -88,6 +88,18 @@
   EXPECT_EQ(out.str(), "-(expr)");
 }
 
+TEST_F(MslUnaryOpTest, NegationOfIntMin) {
+  auto* op = create<ast::UnaryOpExpression>(
+      ast::UnaryOp::kNegation, Expr(std::numeric_limits<int32_t>::min()));
+  WrapInFunction(op);
+
+  GeneratorImpl& gen = Build();
+
+  std::stringstream out;
+  ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
+  EXPECT_EQ(out.str(), "-((-2147483647 - 1))");
+}
+
 }  // namespace
 }  // namespace msl
 }  // namespace writer
diff --git a/test/expressions/bitcast/scalar/i32min-u32.wgsl b/test/expressions/bitcast/scalar/i32min-u32.wgsl
new file mode 100644
index 0000000..9812c83
--- /dev/null
+++ b/test/expressions/bitcast/scalar/i32min-u32.wgsl
@@ -0,0 +1,4 @@
+[[stage(compute), workgroup_size(1)]]
+fn f() {
+  let b : u32 = bitcast<u32>(-2147483648);
+}
diff --git a/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.hlsl b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.hlsl
new file mode 100644
index 0000000..6044ed9
--- /dev/null
+++ b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.hlsl
@@ -0,0 +1,5 @@
+[numthreads(1, 1, 1)]
+void f() {
+  const uint b = asuint(-2147483648);
+  return;
+}
diff --git a/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.msl b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.msl
new file mode 100644
index 0000000..5ad270a
--- /dev/null
+++ b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+kernel void f() {
+  uint const b = as_type<uint>((-2147483647 - 1));
+  return;
+}
+
diff --git a/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.spvasm b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.spvasm
new file mode 100644
index 0000000..ffd7f0c
--- /dev/null
+++ b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.spvasm
@@ -0,0 +1,20 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 9
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %f "f"
+               OpExecutionMode %f LocalSize 1 1 1
+               OpName %f "f"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+        %int = OpTypeInt 32 1
+%int_n2147483648 = OpConstant %int -2147483648
+          %f = OpFunction %void None %1
+          %4 = OpLabel
+          %5 = OpBitcast %uint %int_n2147483648
+               OpReturn
+               OpFunctionEnd
diff --git a/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.wgsl b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.wgsl
new file mode 100644
index 0000000..9812c83
--- /dev/null
+++ b/test/expressions/bitcast/scalar/i32min-u32.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+[[stage(compute), workgroup_size(1)]]
+fn f() {
+  let b : u32 = bitcast<u32>(-2147483648);
+}
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.spvasm.expected.msl b/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.spvasm.expected.msl
index bc8e92f..6b6a5dd 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.spvasm.expected.msl
+++ b/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.spvasm.expected.msl
@@ -14,7 +14,7 @@
 void main_1(constant buf0& x_7, thread float4* const tint_symbol_4) {
   int minValue = 0;
   int negMinValue = 0;
-  minValue = -2147483648;
+  minValue = (-2147483647 - 1);
   int const x_25 = minValue;
   negMinValue = -(x_25);
   int const x_27 = negMinValue;
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.wgsl.expected.msl b/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.wgsl.expected.msl
index bc8e92f..6b6a5dd 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.wgsl.expected.msl
+++ b/test/vk-gl-cts/graphicsfuzz/cov-fold-negate-min-int-value/0-opt.wgsl.expected.msl
@@ -14,7 +14,7 @@
 void main_1(constant buf0& x_7, thread float4* const tint_symbol_4) {
   int minValue = 0;
   int negMinValue = 0;
-  minValue = -2147483648;
+  minValue = (-2147483647 - 1);
   int const x_25 = minValue;
   negMinValue = -(x_25);
   int const x_27 = negMinValue;
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.spvasm.expected.msl b/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.spvasm.expected.msl
index a7a8dbb..933626f 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.spvasm.expected.msl
+++ b/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.spvasm.expected.msl
@@ -69,7 +69,7 @@
         }
         case 0: {
           int const x_70 = i;
-          if ((-2147483648 < x_70)) {
+          if (((-2147483647 - 1) < x_70)) {
             {
               int const x_82 = j;
               j = (x_82 + 1);
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.wgsl.expected.msl b/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.wgsl.expected.msl
index a7a8dbb..933626f 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.wgsl.expected.msl
+++ b/test/vk-gl-cts/graphicsfuzz/cov-x86-isel-lowering-negative-left-shift/0-opt.wgsl.expected.msl
@@ -69,7 +69,7 @@
         }
         case 0: {
           int const x_70 = i;
-          if ((-2147483648 < x_70)) {
+          if (((-2147483647 - 1) < x_70)) {
             {
               int const x_82 = j;
               j = (x_82 + 1);