spirv/reader: Implement derivative intrinsics

Fixed: tint:719
Change-Id: Iaa5e44a574b2843f4f6f9264b8baa8d199acd70f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/47770
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Alan Baker <alanbaker@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index d31e56d..6e73457 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -442,6 +442,24 @@
       return semantic::IntrinsicType::kReverseBits;
     case SpvOpDot:
       return semantic::IntrinsicType::kDot;
+    case SpvOpDPdx:
+      return semantic::IntrinsicType::kDpdx;
+    case SpvOpDPdy:
+      return semantic::IntrinsicType::kDpdy;
+    case SpvOpFwidth:
+      return semantic::IntrinsicType::kFwidth;
+    case SpvOpDPdxFine:
+      return semantic::IntrinsicType::kDpdxFine;
+    case SpvOpDPdyFine:
+      return semantic::IntrinsicType::kDpdyFine;
+    case SpvOpFwidthFine:
+      return semantic::IntrinsicType::kFwidthFine;
+    case SpvOpDPdxCoarse:
+      return semantic::IntrinsicType::kDpdxCoarse;
+    case SpvOpDPdyCoarse:
+      return semantic::IntrinsicType::kDpdyCoarse;
+    case SpvOpFwidthCoarse:
+      return semantic::IntrinsicType::kFwidthCoarse;
     default:
       break;
   }
diff --git a/src/reader/spirv/function_arithmetic_test.cc b/src/reader/spirv/function_arithmetic_test.cc
index 5d0fdfc..9ff8fba 100644
--- a/src/reader/spirv/function_arithmetic_test.cc
+++ b/src/reader/spirv/function_arithmetic_test.cc
@@ -1478,6 +1478,79 @@
       << got;
 }
 
+struct IntrinsicData {
+  const std::string spirv;
+  const std::string wgsl;
+};
+inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
+  out << "OpData{" << data.spirv << "," << data.wgsl << "}";
+  return out;
+}
+struct ArgAndTypeData {
+  const std::string spirv_type;
+  const std::string spirv_arg;
+  const std::string ast_type;
+};
+inline std::ostream& operator<<(std::ostream& out, ArgAndTypeData data) {
+  out << "ArgAndTypeData{" << data.spirv_type << "," << data.spirv_arg << ","
+      << data.ast_type << "}";
+  return out;
+}
+
+using SpvBinaryDerivativeTest = SpvParserTestBase<
+    ::testing::TestWithParam<std::tuple<IntrinsicData, ArgAndTypeData>>>;
+
+TEST_P(SpvBinaryDerivativeTest, Derivatives) {
+  auto& intrinsic = std::get<0>(GetParam());
+  auto& arg = std::get<1>(GetParam());
+
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpCopyObject %)" +
+                        arg.spirv_type + " %" + arg.spirv_arg + R"(
+     %2 = )" + intrinsic.spirv +
+                        " %" + arg.spirv_type + R"( %1
+     OpReturn
+     OpFunctionEnd
+)";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
+    x_2
+    none
+    )" + arg.ast_type + R"(
+    {
+      Call[not set]{
+        Identifier[not set]{)" + intrinsic.wgsl + R"(}
+        (
+          Identifier[not set]{x_1}
+        )
+      }
+    }
+  })"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    SpvBinaryDerivativeTest,
+    SpvBinaryDerivativeTest,
+    testing::Combine(
+        ::testing::Values(IntrinsicData{"OpDPdx", "dpdx"},
+                          IntrinsicData{"OpDPdy", "dpdy"},
+                          IntrinsicData{"OpFwidth", "fwidth"},
+                          IntrinsicData{"OpDPdxFine", "dpdxFine"},
+                          IntrinsicData{"OpDPdyFine", "dpdyFine"},
+                          IntrinsicData{"OpFwidthFine", "fwidthFine"},
+                          IntrinsicData{"OpDPdxCoarse", "dpdxCoarse"},
+                          IntrinsicData{"OpDPdyCoarse", "dpdyCoarse"},
+                          IntrinsicData{"OpFwidthCoarse", "fwidthCoarse"}),
+        ::testing::Values(
+            ArgAndTypeData{"float", "float_50", "__f32"},
+            ArgAndTypeData{"v2float", "v2float_50_60", "__vec_2__f32"},
+            ArgAndTypeData{"v3float", "v3float_50_60_70", "__vec_3__f32"})));
+
 // TODO(dneto): OpSRem. Missing from WGSL
 // https://github.com/gpuweb/gpuweb/issues/702