[ir][msl] Emit basic constants types

Add emission of `bool`, `f16`, `f32`, `i32`, and `i32` constants to the
MSL IR generator.

Bug: tint:1967
Change-Id: I3b3218ad7aff962063893d608a2b74d7ea9c43b8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/138920
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index e2dd096..83d7421 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -2218,6 +2218,7 @@
 
     if (tint_build_ir) {
       sources += [
+        "writer/msl/ir/generator_impl_ir_constant_test.cc",
         "writer/msl/ir/generator_impl_ir_function_test.cc",
         "writer/msl/ir/generator_impl_ir_type_test.cc",
         "writer/msl/ir/test_helper_ir.h",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 0ccef44..0ddae38 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -1457,6 +1457,7 @@
 
     if(${TINT_BUILD_IR})
       list(APPEND TINT_TEST_SRCS
+        writer/msl/ir/generator_impl_ir_constant_test.cc
         writer/msl/ir/generator_impl_ir_function_test.cc
         writer/msl/ir/generator_impl_ir_type_test.cc
         writer/msl/ir/test_helper_ir.h
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 83e769a..4aabeef 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -85,7 +85,6 @@
 #include "src/tint/utils/scoped_assignment.h"
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/writer/check_supported_extensions.h"
-#include "src/tint/writer/float_to_string.h"
 #include "src/tint/writer/msl/generator_support.h"
 
 namespace tint::writer::msl {
@@ -95,45 +94,6 @@
     return utils::IsAnyOf<ast::BreakStatement>(stmts->Last());
 }
 
-void PrintF32(utils::StringStream& out, float value) {
-    // Note: Currently inf and nan should not be constructable, but this is implemented for the day
-    // we support them.
-    if (std::isinf(value)) {
-        out << (value >= 0 ? "INFINITY" : "-INFINITY");
-    } else if (std::isnan(value)) {
-        out << "NAN";
-    } else {
-        out << FloatToString(value) << "f";
-    }
-}
-
-void PrintF16(utils::StringStream& out, float value) {
-    // Note: Currently inf and nan should not be constructable, but this is implemented for the day
-    // we support them.
-    if (std::isinf(value)) {
-        // HUGE_VALH evaluates to +infinity.
-        out << (value >= 0 ? "HUGE_VALH" : "-HUGE_VALH");
-    } else if (std::isnan(value)) {
-        // There is no NaN expr for half in MSL, "NAN" is of float type.
-        out << "NAN";
-    } else {
-        out << FloatToString(value) << "h";
-    }
-}
-
-void PrintI32(utils::StringStream& out, int32_t 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, on the other hand, parses this as an `i32`.
-    // To avoid issues with `long` to `int` casts, emit `(-2147483647 - 1)` instead, which ensures
-    // the expression type is `int`.
-    if (auto int_min = std::numeric_limits<int32_t>::min(); value == int_min) {
-        out << "(" << int_min + 1 << " - 1)";
-    } else {
-        out << value;
-    }
-}
-
 class ScopedBitCast {
   public:
     ScopedBitCast(GeneratorImpl* generator,
diff --git a/src/tint/writer/msl/generator_support.cc b/src/tint/writer/msl/generator_support.cc
index 3739cdd..4e3118b 100644
--- a/src/tint/writer/msl/generator_support.cc
+++ b/src/tint/writer/msl/generator_support.cc
@@ -14,6 +14,9 @@
 
 #include "src/tint/writer/msl/generator_support.h"
 
+#include <cmath>
+#include <limits>
+
 #include "src/tint/debug.h"
 #include "src/tint/switch.h"
 #include "src/tint/type/array.h"
@@ -25,6 +28,7 @@
 #include "src/tint/type/struct.h"
 #include "src/tint/type/u32.h"
 #include "src/tint/type/vector.h"
+#include "src/tint/writer/float_to_string.h"
 
 namespace tint::writer::msl {
 
@@ -211,4 +215,44 @@
             return SizeAndAlign{};
         });
 }
+
+void PrintF32(utils::StringStream& out, float value) {
+    // Note: Currently inf and nan should not be constructable, but this is implemented for the day
+    // we support them.
+    if (std::isinf(value)) {
+        out << (value >= 0 ? "INFINITY" : "-INFINITY");
+    } else if (std::isnan(value)) {
+        out << "NAN";
+    } else {
+        out << FloatToString(value) << "f";
+    }
+}
+
+void PrintF16(utils::StringStream& out, float value) {
+    // Note: Currently inf and nan should not be constructable, but this is implemented for the day
+    // we support them.
+    if (std::isinf(value)) {
+        // HUGE_VALH evaluates to +infinity.
+        out << (value >= 0 ? "HUGE_VALH" : "-HUGE_VALH");
+    } else if (std::isnan(value)) {
+        // There is no NaN expr for half in MSL, "NAN" is of float type.
+        out << "NAN";
+    } else {
+        out << FloatToString(value) << "h";
+    }
+}
+
+void PrintI32(utils::StringStream& out, int32_t 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, on the other hand, parses this as an `i32`.
+    // To avoid issues with `long` to `int` casts, emit `(-2147483647 - 1)` instead, which ensures
+    // the expression type is `int`.
+    if (auto int_min = std::numeric_limits<int32_t>::min(); value == int_min) {
+        out << "(" << int_min + 1 << " - 1)";
+    } else {
+        out << value;
+    }
+}
+
 }  // namespace tint::writer::msl
diff --git a/src/tint/writer/msl/generator_support.h b/src/tint/writer/msl/generator_support.h
index 4f5877f..c510f56 100644
--- a/src/tint/writer/msl/generator_support.h
+++ b/src/tint/writer/msl/generator_support.h
@@ -50,6 +50,21 @@
 std::string InterpolationToAttribute(builtin::InterpolationType type,
                                      builtin::InterpolationSampling sampling);
 
+/// Prints a float32 to the output stream
+/// @param out the stream to write too
+/// @param value the float32 value
+void PrintF32(utils::StringStream& out, float value);
+
+/// Prints a float16 to the output stream
+/// @param out the stream to write too
+/// @param value the float16 value
+void PrintF16(utils::StringStream& out, float value);
+
+/// Prints an int32 to the output stream
+/// @param out the stream to write too
+/// @param value the int32 value
+void PrintI32(utils::StringStream& out, int32_t value);
+
 }  // namespace tint::writer::msl
 
 #endif  // SRC_TINT_WRITER_MSL_GENERATOR_SUPPORT_H_
diff --git a/src/tint/writer/msl/ir/generator_impl_ir.cc b/src/tint/writer/msl/ir/generator_impl_ir.cc
index d5e9ac9..3209562 100644
--- a/src/tint/writer/msl/ir/generator_impl_ir.cc
+++ b/src/tint/writer/msl/ir/generator_impl_ir.cc
@@ -14,6 +14,7 @@
 
 #include "src/tint/writer/msl/ir/generator_impl_ir.h"
 
+#include "src/tint/ir/constant.h"
 #include "src/tint/ir/validate.h"
 #include "src/tint/switch.h"
 #include "src/tint/transform/manager.h"
@@ -429,4 +430,15 @@
     preamble_buffer_.Append(str_buf);
 }
 
+void GeneratorImplIr::EmitConstant(utils::StringStream& out, ir::Constant* c) {
+    return tint::Switch(
+        c->Type(),  //
+        [&](const type::Bool*) { out << (c->Value()->ValueAs<bool>() ? "true" : "false"); },
+        [&](const type::I32*) { PrintI32(out, c->Value()->ValueAs<i32>()); },
+        [&](const type::U32*) { out << c->Value()->ValueAs<u32>() << "u"; },
+        [&](const type::F32*) { PrintF32(out, c->Value()->ValueAs<f32>()); },
+        [&](const type::F16*) { PrintF16(out, c->Value()->ValueAs<f16>()); },
+        [&](Default) { UNHANDLED_CASE(c); });
+}
+
 }  // namespace tint::writer::msl
diff --git a/src/tint/writer/msl/ir/generator_impl_ir.h b/src/tint/writer/msl/ir/generator_impl_ir.h
index 70e5d15..89ec1a1 100644
--- a/src/tint/writer/msl/ir/generator_impl_ir.h
+++ b/src/tint/writer/msl/ir/generator_impl_ir.h
@@ -55,6 +55,11 @@
     /// @param sc the address space to generate
     void EmitAddressSpace(utils::StringStream& out, builtin::AddressSpace sc);
 
+    /// Handles ir::Constant values
+    /// @param out the stream to write the constant too
+    /// @param c the constant to emit
+    void EmitConstant(utils::StringStream& out, ir::Constant* c);
+
     /// @returns the name of the templated `tint_array` helper type, generating it if needed
     const std::string& ArrayTemplateName();
 
diff --git a/src/tint/writer/msl/ir/generator_impl_ir_constant_test.cc b/src/tint/writer/msl/ir/generator_impl_ir_constant_test.cc
new file mode 100644
index 0000000..41a6139
--- /dev/null
+++ b/src/tint/writer/msl/ir/generator_impl_ir_constant_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/utils/string.h"
+#include "src/tint/writer/msl/ir/test_helper_ir.h"
+
+namespace tint::writer::msl {
+namespace {
+
+using namespace tint::number_suffixes;  // NOLINT
+
+TEST_F(MslGeneratorImplIrTest, Constant_Bool_True) {
+    auto* c = b.Constant(true);
+    generator_.EmitConstant(generator_.Line(), c);
+    ASSERT_TRUE(generator_.Diagnostics().empty()) << generator_.Diagnostics().str();
+    EXPECT_EQ(utils::TrimSpace(generator_.Result()), std::string("true"));
+}
+
+TEST_F(MslGeneratorImplIrTest, Constant_Bool_False) {
+    auto* c = b.Constant(false);
+    generator_.EmitConstant(generator_.Line(), c);
+    ASSERT_TRUE(generator_.Diagnostics().empty()) << generator_.Diagnostics().str();
+    EXPECT_EQ(utils::TrimSpace(generator_.Result()), std::string("false"));
+}
+
+TEST_F(MslGeneratorImplIrTest, Constant_i32) {
+    auto* c = b.Constant(-12345_i);
+    generator_.EmitConstant(generator_.Line(), c);
+    ASSERT_TRUE(generator_.Diagnostics().empty()) << generator_.Diagnostics().str();
+    EXPECT_EQ(utils::TrimSpace(generator_.Result()), std::string("-12345"));
+}
+
+TEST_F(MslGeneratorImplIrTest, Constant_u32) {
+    auto* c = b.Constant(12345_u);
+    generator_.EmitConstant(generator_.Line(), c);
+    ASSERT_TRUE(generator_.Diagnostics().empty()) << generator_.Diagnostics().str();
+    EXPECT_EQ(utils::TrimSpace(generator_.Result()), std::string("12345u"));
+}
+
+TEST_F(MslGeneratorImplIrTest, Constant_F32) {
+    auto* c = b.Constant(f32((1 << 30) - 4));
+    generator_.EmitConstant(generator_.Line(), c);
+    ASSERT_TRUE(generator_.Diagnostics().empty()) << generator_.Diagnostics().str();
+    EXPECT_EQ(utils::TrimSpace(generator_.Result()), std::string("1073741824.0f"));
+}
+
+TEST_F(MslGeneratorImplIrTest, Constant_F16) {
+    auto* c = b.Constant(f16((1 << 15) - 8));
+    generator_.EmitConstant(generator_.Line(), c);
+    ASSERT_TRUE(generator_.Diagnostics().empty()) << generator_.Diagnostics().str();
+    EXPECT_EQ(utils::TrimSpace(generator_.Result()), std::string("32752.0h"));
+}
+
+}  // namespace
+}  // namespace tint::writer::msl