Metal: Support feature chromium_experimental_dp4a

Bug: tint:1497
Test: dawn_end2end_tests
Change-Id: I2e3caf2b9c9aa391ebc9dab5f2e82849e427ce00
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/149062
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index d86fad4..29caf35 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -532,6 +532,7 @@
         EnableFeature(Feature::SurfaceCapabilities);
         EnableFeature(Feature::MSAARenderToSingleSampled);
         EnableFeature(Feature::DualSourceBlending);
+        EnableFeature(Feature::ChromiumExperimentalDp4a);
 
         // SIMD-scoped permute operations is supported by GPU family Metal3, Apple6, Apple7, Apple8,
         // and Mac2.
diff --git a/src/dawn/tests/end2end/ExperimentalDP4aTests.cpp b/src/dawn/tests/end2end/ExperimentalDP4aTests.cpp
index 80f14a0..3bcd7d36 100644
--- a/src/dawn/tests/end2end/ExperimentalDP4aTests.cpp
+++ b/src/dawn/tests/end2end/ExperimentalDP4aTests.cpp
@@ -133,6 +133,7 @@
                         {
                             D3D12Backend(),
                             D3D12Backend({}, {"use_dxc"}),
+                            MetalBackend(),
                             VulkanBackend(),
                         },
                         {true, false});
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
index 9be715b..3e6588c 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
@@ -255,6 +255,7 @@
                 core::Extension::kChromiumInternalRelaxedUniformLayout,
                 core::Extension::kF16,
                 core::Extension::kChromiumInternalDualSourceBlending,
+                core::Extension::kChromiumExperimentalDp4A,
             })) {
         return false;
     }
@@ -678,6 +679,10 @@
             return EmitDegreesCall(out, expr, builtin);
         case core::Function::kRadians:
             return EmitRadiansCall(out, expr, builtin);
+        case core::Function::kDot4I8Packed:
+            return EmitDot4I8PackedCall(out, expr, builtin);
+        case core::Function::kDot4U8Packed:
+            return EmitDot4U8PackedCall(out, expr, builtin);
 
         case core::Function::kPack2X16Float:
         case core::Function::kUnpack2X16Float: {
@@ -1335,6 +1340,32 @@
     return true;
 }
 
+bool ASTPrinter::EmitDot4I8PackedCall(StringStream& out,
+                                      const ast::CallExpression* expr,
+                                      const sem::Builtin* builtin) {
+    return CallBuiltinHelper(
+        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
+            Line(b) << "packed_char4 vec1 = as_type<packed_char4>(" << params[0] << ");";
+            Line(b) << "packed_char4 vec2 = as_type<packed_char4>(" << params[1] << ");";
+            Line(b) << "return vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2] + vec1[3] "
+                       "* vec2[3];";
+            return true;
+        });
+}
+
+bool ASTPrinter::EmitDot4U8PackedCall(StringStream& out,
+                                      const ast::CallExpression* expr,
+                                      const sem::Builtin* builtin) {
+    return CallBuiltinHelper(
+        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
+            Line(b) << "packed_uchar4 vec1 = as_type<packed_uchar4>(" << params[0] << ");";
+            Line(b) << "packed_uchar4 vec2 = as_type<packed_uchar4>(" << params[1] << ");";
+            Line(b) << "return vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2] + vec1[3] "
+                       "* vec2[3];";
+            return true;
+        });
+}
+
 bool ASTPrinter::EmitModfCall(StringStream& out,
                               const ast::CallExpression* expr,
                               const sem::Builtin* builtin) {
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.h b/src/tint/lang/msl/writer/ast_printer/ast_printer.h
index 31ef50b..47f1ef7 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.h
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer.h
@@ -348,6 +348,22 @@
     /// @param type the type to emit the value for
     /// @returns true if the zero value was successfully emitted.
     bool EmitZeroValue(StringStream& out, const core::type::Type* type);
+    /// Handles generating a call to the `dot4I8Packed()` builtin
+    /// @param out the output of the expression stream
+    /// @param expr the call expression
+    /// @param builtin the semantic information for the builtin
+    /// @returns true if the call expression is emitted
+    bool EmitDot4I8PackedCall(StringStream& out,
+                              const ast::CallExpression* expr,
+                              const sem::Builtin* builtin);
+    /// Handles generating a call to the `dot4U8Packed()` builtin
+    /// @param out the output of the expression stream
+    /// @param expr the call expression
+    /// @param builtin the semantic information for the builtin
+    /// @returns true if the call expression is emitted
+    bool EmitDot4U8PackedCall(StringStream& out,
+                              const ast::CallExpression* expr,
+                              const sem::Builtin* builtin);
 
     /// Handles generating a builtin name
     /// @param builtin the semantic info for the builtin