[msl] Add a dialect-specific binary instruction

This will be used to represent the 8-bit integer arithmetic operations
that are not available in the core IR.

Bug: 42251016
Change-Id: Id289509f9fe143a8bb604a11fb410218baed8cf7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/201155
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/fluent_types.h b/src/tint/lang/core/fluent_types.h
index a67135a..2e97287 100644
--- a/src/tint/lang/core/fluent_types.h
+++ b/src/tint/lang/core/fluent_types.h
@@ -39,7 +39,9 @@
 using f16 = tint::core::f16;
 using f32 = tint::core::f32;
 using i32 = tint::core::i32;
+using i8 = tint::core::i8;
 using u32 = tint::core::u32;
+using u8 = tint::core::u8;
 using AFloat = tint::core::AFloat;
 using AInt = tint::core::AInt;
 
diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h
index 792c082..8d080d6 100644
--- a/src/tint/lang/core/ir/builder.h
+++ b/src/tint/lang/core/ir/builder.h
@@ -559,6 +559,22 @@
                                                                         lhs_val, rhs_val));
     }
 
+    /// Creates an op for `lhs kind rhs`
+    /// @param op the binary operator
+    /// @param type the result type of the binary expression
+    /// @param lhs the left-hand-side of the operation
+    /// @param rhs the right-hand-side of the operation
+    /// @returns the operation
+    template <typename KLASS, typename LHS, typename RHS>
+    tint::traits::EnableIf<tint::traits::IsTypeOrDerived<KLASS, ir::Binary>, KLASS*>
+    Binary(BinaryOp op, const core::type::Type* type, LHS&& lhs, RHS&& rhs) {
+        CheckForNonDeterministicEvaluation<LHS, RHS>();
+        auto* lhs_val = Value(std::forward<LHS>(lhs));
+        auto* rhs_val = Value(std::forward<RHS>(rhs));
+        return Append(ir.allocators.instructions.Create<KLASS>(InstructionResult(type), op, lhs_val,
+                                                               rhs_val));
+    }
+
     /// Creates an And operation
     /// @param type the result type of the expression
     /// @param lhs the lhs of the add
diff --git a/src/tint/lang/msl/ir/BUILD.bazel b/src/tint/lang/msl/ir/BUILD.bazel
index e512f82..07f2a0d 100644
--- a/src/tint/lang/msl/ir/BUILD.bazel
+++ b/src/tint/lang/msl/ir/BUILD.bazel
@@ -39,12 +39,14 @@
 cc_library(
   name = "ir",
   srcs = [
+    "binary.cc",
     "builtin_call.cc",
     "component.cc",
     "member_builtin_call.cc",
     "memory_order.cc",
   ],
   hdrs = [
+    "binary.h",
     "builtin_call.h",
     "component.h",
     "member_builtin_call.h",
@@ -80,6 +82,7 @@
   name = "test",
   alwayslink = True,
   srcs = [
+    "binary_test.cc",
     "builtin_call_test.cc",
     "member_builtin_call_test.cc",
   ],
diff --git a/src/tint/lang/msl/ir/BUILD.cmake b/src/tint/lang/msl/ir/BUILD.cmake
index 97dd3ca..97209c1 100644
--- a/src/tint/lang/msl/ir/BUILD.cmake
+++ b/src/tint/lang/msl/ir/BUILD.cmake
@@ -39,6 +39,8 @@
 # Kind:      lib
 ################################################################################
 tint_add_target(tint_lang_msl_ir lib
+  lang/msl/ir/binary.cc
+  lang/msl/ir/binary.h
   lang/msl/ir/builtin_call.cc
   lang/msl/ir/builtin_call.h
   lang/msl/ir/component.cc
@@ -78,6 +80,7 @@
 # Kind:      test
 ################################################################################
 tint_add_target(tint_lang_msl_ir_test test
+  lang/msl/ir/binary_test.cc
   lang/msl/ir/builtin_call_test.cc
   lang/msl/ir/member_builtin_call_test.cc
 )
diff --git a/src/tint/lang/msl/ir/BUILD.gn b/src/tint/lang/msl/ir/BUILD.gn
index 931211e..aaa9cc7 100644
--- a/src/tint/lang/msl/ir/BUILD.gn
+++ b/src/tint/lang/msl/ir/BUILD.gn
@@ -44,6 +44,8 @@
 
 libtint_source_set("ir") {
   sources = [
+    "binary.cc",
+    "binary.h",
     "builtin_call.cc",
     "builtin_call.h",
     "component.cc",
@@ -80,6 +82,7 @@
 if (tint_build_unittests) {
   tint_unittests_source_set("unittests") {
     sources = [
+      "binary_test.cc",
       "builtin_call_test.cc",
       "member_builtin_call_test.cc",
     ]
diff --git a/src/tint/lang/msl/ir/binary.cc b/src/tint/lang/msl/ir/binary.cc
new file mode 100644
index 0000000..13ada6b
--- /dev/null
+++ b/src/tint/lang/msl/ir/binary.cc
@@ -0,0 +1,55 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/msl/ir/binary.h"
+
+#include <utility>
+
+#include "src/tint/lang/core/ir/clone_context.h"
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/utils/ice/ice.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::msl::ir::Binary);
+
+namespace tint::msl::ir {
+
+Binary::Binary(core::ir::InstructionResult* result,
+               core::BinaryOp op,
+               core::ir::Value* lhs,
+               core::ir::Value* rhs)
+    : Base(result, op, lhs, rhs) {}
+
+Binary::~Binary() = default;
+
+Binary* Binary::Clone(core::ir::CloneContext& ctx) {
+    auto* new_result = ctx.Clone(Result(0));
+    auto* new_rhs = ctx.Remap(RHS());
+    auto* new_lhs = ctx.Remap(LHS());
+    return ctx.ir.allocators.instructions.Create<Binary>(new_result, Op(), new_lhs, new_rhs);
+}
+
+}  // namespace tint::msl::ir
diff --git a/src/tint/lang/msl/ir/binary.h b/src/tint/lang/msl/ir/binary.h
new file mode 100644
index 0000000..3df7799
--- /dev/null
+++ b/src/tint/lang/msl/ir/binary.h
@@ -0,0 +1,63 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_MSL_IR_BINARY_H_
+#define SRC_TINT_LANG_MSL_IR_BINARY_H_
+
+#include "src/tint/lang/core/intrinsic/table_data.h"
+#include "src/tint/lang/core/ir/binary.h"
+#include "src/tint/lang/msl/intrinsic/dialect.h"
+#include "src/tint/utils/rtti/castable.h"
+
+namespace tint::msl::ir {
+
+/// An MSL binary instruction in the IR.
+class Binary final : public Castable<Binary, core::ir::Binary> {
+  public:
+    /// Constructor
+    /// @param result the result value
+    /// @param op the Binary operator
+    /// @param lhs the lhs of the instruction
+    /// @param rhs the rhs of the instruction
+    Binary(core::ir::InstructionResult* result,
+           core::BinaryOp op,
+           core::ir::Value* lhs,
+           core::ir::Value* rhs);
+    ~Binary() override;
+
+    /// @copydoc Instruction::Clone()
+    Binary* Clone(core::ir::CloneContext& ctx) override;
+
+    /// @returns the table data to validate this binary instruction
+    const core::intrinsic::TableData& TableData() const override {
+        return msl::intrinsic::Dialect::kData;
+    }
+};
+
+}  // namespace tint::msl::ir
+
+#endif  // SRC_TINT_LANG_MSL_IR_BINARY_H_
diff --git a/src/tint/lang/msl/ir/binary_test.cc b/src/tint/lang/msl/ir/binary_test.cc
new file mode 100644
index 0000000..c09dbe4
--- /dev/null
+++ b/src/tint/lang/msl/ir/binary_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/msl/ir/binary.h"
+
+#include "gtest/gtest.h"
+#include "src/tint/lang/core/fluent_types.h"
+#include "src/tint/lang/core/ir/ir_helper_test.h"
+#include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/utils/result/result.h"
+
+using namespace tint::core::fluent_types;  // NOLINT
+
+namespace tint::msl::ir {
+namespace {
+
+using namespace tint::core::number_suffixes;  // NOLINT
+
+using IR_MslBinaryTest = core::ir::IRTestHelper;
+
+TEST_F(IR_MslBinaryTest, Clone) {
+    auto* lhs = b.Constant(i8(2));
+    auto* rhs = b.Constant(i8(4));
+    auto* inst = b.Binary<msl::ir::Binary>(core::BinaryOp::kAdd, mod.Types().i8(), lhs, rhs);
+
+    auto* c = clone_ctx.Clone(inst);
+
+    EXPECT_NE(inst, c);
+
+    EXPECT_EQ(mod.Types().i8(), c->Result(0)->Type());
+    EXPECT_EQ(core::BinaryOp::kAdd, c->Op());
+
+    auto new_lhs = c->LHS()->As<core::ir::Constant>()->Value();
+    ASSERT_TRUE(new_lhs->Is<core::constant::Scalar<i8>>());
+    EXPECT_EQ(2_i, new_lhs->As<core::constant::Scalar<i8>>()->ValueAs<i8>());
+
+    auto new_rhs = c->RHS()->As<core::ir::Constant>()->Value();
+    ASSERT_TRUE(new_rhs->Is<core::constant::Scalar<i8>>());
+    EXPECT_EQ(4_i, new_rhs->As<core::constant::Scalar<i8>>()->ValueAs<i8>());
+}
+
+TEST_F(IR_MslBinaryTest, MatchOverloadFromDialect) {
+    auto* v = b.FunctionParam("v", ty.i8());
+    auto* func = b.Function("foo", ty.i8());
+    func->SetParams({v});
+    b.Append(func->Block(), [&] {
+        auto* result = b.Binary<msl::ir::Binary>(core::BinaryOp::kAdd, mod.Types().i8(), v, v);
+        b.Return(func, result);
+    });
+
+    core::ir::Capabilities caps;
+    caps.Add(core::ir::Capability::kAllow8BitIntegers);
+    auto res = core::ir::Validate(mod, caps);
+    EXPECT_EQ(res, Success) << res.Failure().reason.Str();
+}
+
+TEST_F(IR_MslBinaryTest, DoesNotMatchOverloadFromCore) {
+    auto* v = b.FunctionParam("v", ty.i32());
+    auto* func = b.Function("foo", ty.i32());
+    func->SetParams({v});
+    b.Append(func->Block(), [&] {
+        auto* result = b.Binary<msl::ir::Binary>(core::BinaryOp::kAdd, mod.Types().i32(), v, v);
+        b.Return(func, result);
+    });
+
+    auto res = core::ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:3:5 error: binary: no matching overload for 'operator + (i32, i32)'
+
+1 candidate operator:
+ • 'operator + (T  ✗ , T  ✗ ) -> T' where:
+      ✗  'T' is 'i8' or 'u8'
+
+    %3:i32 = add %v, %v
+    ^^^^^^^^^^^^^^^^^^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+note: # Disassembly
+%foo = func(%v:i32):i32 {
+  $B1: {
+    %3:i32 = add %v, %v
+    ret %3
+  }
+}
+)");
+}
+
+}  // namespace
+}  // namespace tint::msl::ir