Author: Alexander Johnston
Date: 2025-12-11T10:04:35-08:00
New Revision: 3142e3ab15bb0485e1ac731cca3fb086b108eb06

URL: 
https://github.com/llvm/llvm-project/commit/3142e3ab15bb0485e1ac731cca3fb086b108eb06
DIFF: 
https://github.com/llvm/llvm-project/commit/3142e3ab15bb0485e1ac731cca3fb086b108eb06.diff

LOG: [HLSL] Implement ddx and ddy HLSL intrinsics (#168887)

Implements the ddx and ddy HLSL intrinsics. The DXIL intrinsics call the
ddx_coarse and ddy_builtins as they lower to the coarse variants.
The SPIRV intrinsics lower to their own opcodes OpDPdx and OpDPdy. Tests
are added to ensure the SPIRV builtins are not available outside of
shaders stage.

Closes https://github.com/llvm/llvm-project/issues/99096
Closes https://github.com/llvm/llvm-project/issues/99099

Added: 
    clang/test/CodeGenHLSL/builtins/ddx.hlsl
    clang/test/CodeGenHLSL/builtins/ddy.hlsl
    clang/test/CodeGenSPIRV/Builtins/ddx.c
    clang/test/CodeGenSPIRV/Builtins/ddy.c
    clang/test/SemaSPIRV/BuiltIns/ddx-errors.c
    clang/test/SemaSPIRV/BuiltIns/ddy-errors.c
    llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx.ll
    llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy.ll
    llvm/test/CodeGen/SPIRV/opencl/ddx-error.ll
    llvm/test/CodeGen/SPIRV/opencl/ddy-error.ll

Modified: 
    clang/include/clang/Basic/BuiltinsSPIRVVK.td
    clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
    clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
    clang/lib/Headers/hlsl/hlsl_intrinsics.h
    clang/lib/Sema/SemaSPIRV.cpp
    llvm/include/llvm/IR/IntrinsicsSPIRV.td
    llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/BuiltinsSPIRVVK.td 
b/clang/include/clang/Basic/BuiltinsSPIRVVK.td
index a077a763923d6..67daa16390cf2 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRVVK.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRVVK.td
@@ -12,4 +12,6 @@ include "clang/Basic/BuiltinsSPIRVBase.td"
 def reflect : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
 def faceforward : SPIRVBuiltin<"void(...)", [NoThrow, Const, 
CustomTypeChecking]>;
 def refract : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
+def ddx : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
+def ddy : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
 def fwidth : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;

diff  --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp 
b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
index 43b05a128e876..9b0ca3eb0035a 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -151,6 +151,14 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned 
BuiltinID,
         Intrinsic::spv_global_offset,
         ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr,
         "spv.global.offset");
+  case SPIRV::BI__builtin_spirv_ddx:
+    return Builder.CreateIntrinsic(
+        /*ReturnType=*/getTypes().ConvertType(E->getType()), 
Intrinsic::spv_ddx,
+        ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr, "spv.ddx");
+  case SPIRV::BI__builtin_spirv_ddy:
+    return Builder.CreateIntrinsic(
+        /*ReturnType=*/getTypes().ConvertType(E->getType()), 
Intrinsic::spv_ddy,
+        ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr, "spv.ddy");
   case SPIRV::BI__builtin_spirv_fwidth:
     return Builder.CreateIntrinsic(
         /*ReturnType=*/getTypes().ConvertType(E->getType()),

diff  --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
index 3550409b6988d..dfd4659637929 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
@@ -156,6 +156,22 @@ constexpr K firstbithigh_impl(T X) {
   return FBH;
 }
 
+template <typename T> constexpr T ddx_impl(T input) {
+#if (__has_builtin(__builtin_spirv_ddx))
+  return __builtin_spirv_ddx(input);
+#else
+  return __builtin_hlsl_elementwise_ddx_coarse(input);
+#endif
+}
+
+template <typename T> constexpr T ddy_impl(T input) {
+#if (__has_builtin(__builtin_spirv_ddy))
+  return __builtin_spirv_ddy(input);
+#else
+  return __builtin_hlsl_elementwise_ddy_coarse(input);
+#endif
+}
+
 template <typename T> constexpr T fwidth_impl(T input) {
 #if (__has_builtin(__builtin_spirv_fwidth))
   return __builtin_spirv_fwidth(input);

diff  --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index a538be5ebd099..330b3f12635e4 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -670,6 +670,86 @@ inline bool CheckAccessFullyMapped(uint Status) {
   return static_cast<bool>(Status);
 }
 
+//===----------------------------------------------------------------------===//
+// ddx builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddx(T x)
+/// \brief Computes the sum of the absolute values of the partial derivatives
+/// with regard to the x screen space coordinate.
+/// \param x [in] The floating-point scalar or vector to process.
+///
+/// The return value is a floating-point scalar or vector where each element
+/// holds the computation of the matching element in the input.
+
+template <typename T>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
+                                       __detail::is_same<half, T>::value,
+                                   T> ddx(T input) {
+  return __detail::ddx_impl(input);
+}
+
+template <typename T>
+const inline __detail::enable_if_t<
+    __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
+ddx(T input) {
+  return __detail::ddx_impl(input);
+}
+
+template <int N>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline __detail::HLSL_FIXED_VECTOR<half, N> ddx(
+    __detail::HLSL_FIXED_VECTOR<half, N> input) {
+  return __detail::ddx_impl(input);
+}
+
+template <int N>
+const inline __detail::HLSL_FIXED_VECTOR<float, N>
+ddx(__detail::HLSL_FIXED_VECTOR<float, N> input) {
+  return __detail::ddx_impl(input);
+}
+
+//===----------------------------------------------------------------------===//
+// ddy builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddy(T x)
+/// \brief Computes the sum of the absolute values of the partial derivatives
+/// with regard to the y screen space coordinate.
+/// \param x [in] The floating-point scalar or vector to process.
+///
+/// The return value is a floating-point scalar or vector where each element
+/// holds the computation of the matching element in the input.
+
+template <typename T>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
+                                       __detail::is_same<half, T>::value,
+                                   T> ddy(T input) {
+  return __detail::ddy_impl(input);
+}
+
+template <typename T>
+const inline __detail::enable_if_t<
+    __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
+ddy(T input) {
+  return __detail::ddy_impl(input);
+}
+
+template <int N>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline __detail::HLSL_FIXED_VECTOR<half, N> ddy(
+    __detail::HLSL_FIXED_VECTOR<half, N> input) {
+  return __detail::ddy_impl(input);
+}
+
+template <int N>
+const inline __detail::HLSL_FIXED_VECTOR<float, N>
+ddy(__detail::HLSL_FIXED_VECTOR<float, N> input) {
+  return __detail::ddy_impl(input);
+}
+
 
//===----------------------------------------------------------------------===//
 // fwidth builtin
 
//===----------------------------------------------------------------------===//

diff  --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index 0e78cff9c1774..fa30e149c209a 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -360,6 +360,8 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(const 
TargetInfo &TI,
   case SPIRV::BI__builtin_spirv_generic_cast_to_ptr_explicit: {
     return checkGenericCastToPtr(SemaRef, TheCall);
   }
+  case SPIRV::BI__builtin_spirv_ddx:
+  case SPIRV::BI__builtin_spirv_ddy:
   case SPIRV::BI__builtin_spirv_fwidth: {
     if (SemaRef.checkArgCount(TheCall, 1))
       return true;

diff  --git a/clang/test/CodeGenHLSL/builtins/ddx.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddx.hlsl
new file mode 100644
index 0000000000000..1a736e20c47ae
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddx.hlsl
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
spirv-pc-vulkan-pixel  %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK-SPIRV
+
+// CHECK-LABEL: define {{.*}} half @_ZN4hlsl8__detail8ddx_implIDhEET_S2_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} half @llvm.dx.ddx.coarse.f16(half 
%{{.*}})
+// CHECK: ret half %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: half @_ZN4hlsl8__detail8ddx_implIDhEET_S2_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} half @llvm.spv.ddx.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %spv.ddx
+half test_f16_ddx(half val) {
+    return ddx(val);
+}
+
+// CHECK-LABEL: define {{.*}} <2 x half> 
@_ZN4hlsl8__detail8ddx_implIDv2_DhEET_S3_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <2 x half> 
@llvm.dx.ddx.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK: ret <2 x half> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <2 x half> @_ZN4hlsl8__detail8ddx_implIDv2_DhEET_S3_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} <2 x half> @llvm.spv.ddx.v2f16(<2 x 
half> %{{.*}})
+// CHECK-SPIRV: ret <2 x half> %spv.ddx
+half2 test_f16_ddx2(half2 val) {
+    return ddx(val);
+}
+
+// CHECK-LABEL: define {{.*}} <3 x half> 
@_ZN4hlsl8__detail8ddx_implIDv3_DhEET_S3_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <3 x half> 
@llvm.dx.ddx.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK: ret <3 x half> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <3 x half> @_ZN4hlsl8__detail8ddx_implIDv3_DhEET_S3_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} <3 x half> @llvm.spv.ddx.v3f16(<3 x 
half> %{{.*}})
+// CHECK-SPIRV: ret <3 x half> %spv.ddx
+half3 test_f16_ddx3(half3 val) {
+    return ddx(val);
+}
+
+// CHECK-LABEL: define {{.*}} <4 x half> 
@_ZN4hlsl8__detail8ddx_implIDv4_DhEET_S3_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <4 x half> 
@llvm.dx.ddx.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK: ret <4 x half> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <4 x half> @_ZN4hlsl8__detail8ddx_implIDv4_DhEET_S3_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} <4 x half> @llvm.spv.ddx.v4f16(<4 x 
half> %{{.*}})
+// CHECK-SPIRV: ret <4 x half> %spv.ddx
+half4 test_f16_ddx4(half4 val) {
+    return ddx(val);
+}
+
+// CHECK-LABEL: define {{.*}} float @_ZN4hlsl8__detail8ddx_implIfEET_S2_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} float @llvm.dx.ddx.coarse.f32(float 
%{{.*}})
+// CHECK: ret float %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: float @_ZN4hlsl8__detail8ddx_implIfEET_S2_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} float @llvm.spv.ddx.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %spv.ddx
+float test_f32_ddx(float val) {
+    return ddx(val);
+}
+
+// CHECK-LABEL: define {{.*}} <2 x float> 
@_ZN4hlsl8__detail8ddx_implIDv2_fEET_S3_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <2 x float> 
@llvm.dx.ddx.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK: ret <2 x float> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <2 x float> @_ZN4hlsl8__detail8ddx_implIDv2_fEET_S3_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} <2 x float> @llvm.spv.ddx.v2f32(<2 x 
float> %{{.*}})
+// CHECK-SPIRV: ret <2 x float> %spv.ddx
+float2 test_f32_ddx2(float2 val) {
+    return ddx(val);
+}
+
+// CHECK-LABEL: define {{.*}} <3 x float> 
@_ZN4hlsl8__detail8ddx_implIDv3_fEET_S3_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <3 x float> 
@llvm.dx.ddx.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK: ret <3 x float> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <3 x float> @_ZN4hlsl8__detail8ddx_implIDv3_fEET_S3_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} <3 x float> @llvm.spv.ddx.v3f32(<3 x 
float> %{{.*}})
+// CHECK-SPIRV: ret <3 x float> %spv.ddx
+float3 test_f32_ddx3(float3 val) {
+    return ddx(val);
+}
+
+// CHECK-LABEL: define {{.*}} <4 x float> 
@_ZN4hlsl8__detail8ddx_implIDv4_fEET_S3_
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <4 x float> 
@llvm.dx.ddx.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK: ret <4 x float> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <4 x float> @_ZN4hlsl8__detail8ddx_implIDv4_fEET_S3_
+// CHECK-SPIRV: %spv.ddx = call {{.*}} <4 x float> @llvm.spv.ddx.v4f32(<4 x 
float> %{{.*}})
+// CHECK-SPIRV: ret <4 x float> %spv.ddx
+float4 test_f32_ddx4(float4 val) {
+    return ddx(val);
+}

diff  --git a/clang/test/CodeGenHLSL/builtins/ddy.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddy.hlsl
new file mode 100644
index 0000000000000..635838327dc1f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddy.hlsl
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
spirv-pc-vulkan-pixel  %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK-SPIRV
+
+// CHECK-LABEL: define {{.*}} half @_ZN4hlsl8__detail8ddy_implIDhEET_S2_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} half @llvm.dx.ddy.coarse.f16(half 
%{{.*}})
+// CHECK: ret half %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: half @_ZN4hlsl8__detail8ddy_implIDhEET_S2_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} half @llvm.spv.ddy.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %spv.ddy
+half test_f16_ddy(half val) {
+    return ddy(val);
+}
+
+// CHECK-LABEL: define {{.*}} <2 x half> 
@_ZN4hlsl8__detail8ddy_implIDv2_DhEET_S3_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <2 x half> 
@llvm.dx.ddy.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK: ret <2 x half> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <2 x half> @_ZN4hlsl8__detail8ddy_implIDv2_DhEET_S3_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} <2 x half> @llvm.spv.ddy.v2f16(<2 x 
half> %{{.*}})
+// CHECK-SPIRV: ret <2 x half> %spv.ddy
+half2 test_f16_ddy2(half2 val) {
+    return ddy(val);
+}
+
+// CHECK-LABEL: define {{.*}} <3 x half> 
@_ZN4hlsl8__detail8ddy_implIDv3_DhEET_S3_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <3 x half> 
@llvm.dx.ddy.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK: ret <3 x half> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <3 x half> @_ZN4hlsl8__detail8ddy_implIDv3_DhEET_S3_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} <3 x half> @llvm.spv.ddy.v3f16(<3 x 
half> %{{.*}})
+// CHECK-SPIRV: ret <3 x half> %spv.ddy
+half3 test_f16_ddy3(half3 val) {
+    return ddy(val);
+}
+
+// CHECK-LABEL: define {{.*}} <4 x half> 
@_ZN4hlsl8__detail8ddy_implIDv4_DhEET_S3_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <4 x half> 
@llvm.dx.ddy.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK: ret <4 x half> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <4 x half> @_ZN4hlsl8__detail8ddy_implIDv4_DhEET_S3_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} <4 x half> @llvm.spv.ddy.v4f16(<4 x 
half> %{{.*}})
+// CHECK-SPIRV: ret <4 x half> %spv.ddy
+half4 test_f16_ddy4(half4 val) {
+    return ddy(val);
+}
+
+// CHECK-LABEL: define {{.*}} float @_ZN4hlsl8__detail8ddy_implIfEET_S2_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} float @llvm.dx.ddy.coarse.f32(float 
%{{.*}})
+// CHECK: ret float %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: float @_ZN4hlsl8__detail8ddy_implIfEET_S2_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} float @llvm.spv.ddy.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %spv.ddy
+float test_f32_ddy(float val) {
+    return ddy(val);
+}
+
+// CHECK-LABEL: define {{.*}} <2 x float> 
@_ZN4hlsl8__detail8ddy_implIDv2_fEET_S3_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <2 x float> 
@llvm.dx.ddy.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK: ret <2 x float> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <2 x float> @_ZN4hlsl8__detail8ddy_implIDv2_fEET_S3_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} <2 x float> @llvm.spv.ddy.v2f32(<2 x 
float> %{{.*}})
+// CHECK-SPIRV: ret <2 x float> %spv.ddy
+float2 test_f32_ddy2(float2 val) {
+    return ddy(val);
+}
+
+// CHECK-LABEL: define {{.*}} <3 x float> 
@_ZN4hlsl8__detail8ddy_implIDv3_fEET_S3_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <3 x float> 
@llvm.dx.ddy.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK: ret <3 x float> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <3 x float> @_ZN4hlsl8__detail8ddy_implIDv3_fEET_S3_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} <3 x float> @llvm.spv.ddy.v3f32(<3 x 
float> %{{.*}})
+// CHECK-SPIRV: ret <3 x float> %spv.ddy
+float3 test_f32_ddy3(float3 val) {
+    return ddy(val);
+}
+
+// CHECK-LABEL: define {{.*}} <4 x float> 
@_ZN4hlsl8__detail8ddy_implIDv4_fEET_S3_
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <4 x float> 
@llvm.dx.ddy.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK: ret <4 x float> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <4 x float> @_ZN4hlsl8__detail8ddy_implIDv4_fEET_S3_
+// CHECK-SPIRV: %spv.ddy = call {{.*}} <4 x float> @llvm.spv.ddy.v4f32(<4 x 
float> %{{.*}})
+// CHECK-SPIRV: ret <4 x float> %spv.ddy
+float4 test_f32_ddy4(float4 val) {
+    return ddy(val);
+}

diff  --git a/clang/test/CodeGenSPIRV/Builtins/ddx.c 
b/clang/test/CodeGenSPIRV/Builtins/ddx.c
new file mode 100644
index 0000000000000..d3cfd1fa7f471
--- /dev/null
+++ b/clang/test/CodeGenSPIRV/Builtins/ddx.c
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -O1 -triple spirv-pc-vulkan-pixel %s -emit-llvm -o - | 
FileCheck %s
+
+typedef _Float16 half;
+typedef half half2 __attribute__((ext_vector_type(2)));
+typedef half half3 __attribute__((ext_vector_type(3)));
+typedef half half4 __attribute__((ext_vector_type(4)));
+typedef float float2 __attribute__((ext_vector_type(2)));
+typedef float float3 __attribute__((ext_vector_type(3)));
+typedef float float4 __attribute__((ext_vector_type(4)));
+
+// CHECK: [[ddx0:%.*]] = tail call half @llvm.spv.ddx.f16(half {{%.*}})
+// CHECK: ret half [[ddx0]]
+half test_ddx_half(half X) { return __builtin_spirv_ddx(X); }
+
+// CHECK: [[ddx0:%.*]] = tail call <2 x half> @llvm.spv.ddx.v2f16(<2 x half>  
{{%.*}})
+// CHECK: ret <2 x half> [[ddx0]]
+half2 test_ddx_half2(half2 X) { return __builtin_spirv_ddx(X); }
+
+// CHECK: [[ddx0:%.*]] = tail call <3 x half> @llvm.spv.ddx.v3f16(<3 x half> 
{{%.*}})
+// CHECK: ret <3 x half> [[ddx0]]
+half3 test_ddx_half3(half3 X) { return __builtin_spirv_ddx(X); }
+
+// CHECK: [[ddx0:%.*]] = tail call <4 x half> @llvm.spv.ddx.v4f16(<4 x half> 
{{%.*}})
+// CHECK: ret <4 x half> [[ddx0]]
+half4 test_ddx_half4(half4 X) { return __builtin_spirv_ddx(X); }
+
+// CHECK: [[ddx0:%.*]] = tail call float @llvm.spv.ddx.f32(float {{%.*}})
+// CHECK: ret float [[ddx0]]
+float test_ddx_float(float X) { return __builtin_spirv_ddx(X); }
+
+// CHECK: [[ddx1:%.*]] = tail call <2 x float> @llvm.spv.ddx.v2f32(<2 x float> 
{{%.*}})
+// CHECK: ret <2 x float> [[ddx1]]
+float2 test_ddx_float2(float2 X) { return __builtin_spirv_ddx(X); }
+
+// CHECK: [[ddx2:%.*]] = tail call <3 x float> @llvm.spv.ddx.v3f32(<3 x float> 
{{%.*}})
+// CHECK: ret <3 x float> [[ddx2]]
+float3 test_ddx_float3(float3 X) { return __builtin_spirv_ddx(X); }
+
+// CHECK: [[ddx3:%.*]] = tail call <4 x float> @llvm.spv.ddx.v4f32(<4 x float> 
{{%.*}})
+// CHECK: ret <4 x float> [[ddx3]]
+float4 test_ddx_float4(float4 X) { return __builtin_spirv_ddx(X); }

diff  --git a/clang/test/CodeGenSPIRV/Builtins/ddy.c 
b/clang/test/CodeGenSPIRV/Builtins/ddy.c
new file mode 100644
index 0000000000000..bb14b76a9fb86
--- /dev/null
+++ b/clang/test/CodeGenSPIRV/Builtins/ddy.c
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -O1 -triple spirv-pc-vulkan-pixel %s -emit-llvm -o - | 
FileCheck %s
+
+typedef _Float16 half;
+typedef half half2 __attribute__((ext_vector_type(2)));
+typedef half half3 __attribute__((ext_vector_type(3)));
+typedef half half4 __attribute__((ext_vector_type(4)));
+typedef float float2 __attribute__((ext_vector_type(2)));
+typedef float float3 __attribute__((ext_vector_type(3)));
+typedef float float4 __attribute__((ext_vector_type(4)));
+
+// CHECK: [[ddy0:%.*]] = tail call half @llvm.spv.ddy.f16(half {{%.*}})
+// CHECK: ret half [[ddy0]]
+half test_ddy_half(half X) { return __builtin_spirv_ddy(X); }
+
+// CHECK: [[ddy0:%.*]] = tail call <2 x half> @llvm.spv.ddy.v2f16(<2 x half>  
{{%.*}})
+// CHECK: ret <2 x half> [[ddy0]]
+half2 test_ddy_half2(half2 X) { return __builtin_spirv_ddy(X); }
+
+// CHECK: [[ddy0:%.*]] = tail call <3 x half> @llvm.spv.ddy.v3f16(<3 x half> 
{{%.*}})
+// CHECK: ret <3 x half> [[ddy0]]
+half3 test_ddy_half3(half3 X) { return __builtin_spirv_ddy(X); }
+
+// CHECK: [[ddy0:%.*]] = tail call <4 x half> @llvm.spv.ddy.v4f16(<4 x half> 
{{%.*}})
+// CHECK: ret <4 x half> [[ddy0]]
+half4 test_ddy_half4(half4 X) { return __builtin_spirv_ddy(X); }
+
+// CHECK: [[ddy0:%.*]] = tail call float @llvm.spv.ddy.f32(float {{%.*}})
+// CHECK: ret float [[ddy0]]
+float test_ddy_float(float X) { return __builtin_spirv_ddy(X); }
+
+// CHECK: [[ddy1:%.*]] = tail call <2 x float> @llvm.spv.ddy.v2f32(<2 x float> 
{{%.*}})
+// CHECK: ret <2 x float> [[ddy1]]
+float2 test_ddy_float2(float2 X) { return __builtin_spirv_ddy(X); }
+
+// CHECK: [[ddy2:%.*]] = tail call <3 x float> @llvm.spv.ddy.v3f32(<3 x float> 
{{%.*}})
+// CHECK: ret <3 x float> [[ddy2]]
+float3 test_ddy_float3(float3 X) { return __builtin_spirv_ddy(X); }
+
+// CHECK: [[ddy3:%.*]] = tail call <4 x float> @llvm.spv.ddy.v4f32(<4 x float> 
{{%.*}})
+// CHECK: ret <4 x float> [[ddy3]]
+float4 test_ddy_float4(float4 X) { return __builtin_spirv_ddy(X); }

diff  --git a/clang/test/SemaSPIRV/BuiltIns/ddx-errors.c 
b/clang/test/SemaSPIRV/BuiltIns/ddx-errors.c
new file mode 100644
index 0000000000000..a29af5dc43e30
--- /dev/null
+++ b/clang/test/SemaSPIRV/BuiltIns/ddx-errors.c
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-pixel -verify
+
+typedef float float2 __attribute__((ext_vector_type(2)));
+
+void test_too_few_arg()
+{
+  return __builtin_spirv_ddx();
+  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
+}
+
+float test_too_many_arg(float p0) {
+  return __builtin_spirv_ddx(p0, p0);
+  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
+}
+
+float test_int_scalar_inputs(int p0) {
+  return __builtin_spirv_ddx(p0);
+  //  expected-error@-1 {{1st argument must be a scalar or vector of 
floating-point types (was 'int')}}
+}
+
+float test_mismatched_return(float2 p0) {
+  return __builtin_spirv_ddx(p0);
+  // expected-error@-1 {{returning 'float2' (vector of 2 'float' values) from 
a function with incompatible result type 'float'}}
+}

diff  --git a/clang/test/SemaSPIRV/BuiltIns/ddy-errors.c 
b/clang/test/SemaSPIRV/BuiltIns/ddy-errors.c
new file mode 100644
index 0000000000000..65c37af1369c5
--- /dev/null
+++ b/clang/test/SemaSPIRV/BuiltIns/ddy-errors.c
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-pixel -verify
+
+typedef float float2 __attribute__((ext_vector_type(2)));
+
+void test_too_few_arg()
+{
+  return __builtin_spirv_ddy();
+  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
+}
+
+float test_too_many_arg(float p0) {
+  return __builtin_spirv_ddy(p0, p0);
+  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
+}
+
+float test_int_scalar_inputs(int p0) {
+  return __builtin_spirv_ddy(p0);
+  //  expected-error@-1 {{1st argument must be a scalar or vector of 
floating-point types (was 'int')}}
+}
+
+float test_mismatched_return(float2 p0) {
+  return __builtin_spirv_ddy(p0);
+  // expected-error@-1 {{returning 'float2' (vector of 2 'float' values) from 
a function with incompatible result type 'float'}}
+}

diff  --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td 
b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 366f8cf36d75c..c3b4d8e821318 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -134,6 +134,8 @@ def int_spv_rsqrt : 
DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
   def int_spv_group_memory_barrier_with_group_sync
       : DefaultAttrsIntrinsic<[], [], [IntrConvergent]>;
   def int_spv_discard : DefaultAttrsIntrinsic<[], [], []>;
+  def int_spv_ddx : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
+  def int_spv_ddy : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_ddx_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_ddy_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_fwidth : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index e0f7b19c91fbd..29a5ea1d99d17 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3855,6 +3855,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register 
ResVReg,
   case Intrinsic::spv_unpackhalf2x16: {
     return selectExtInst(ResVReg, ResType, I, GL::UnpackHalf2x16);
   }
+  case Intrinsic::spv_ddx:
+    return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdx);
+  case Intrinsic::spv_ddy:
+    return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdy);
   case Intrinsic::spv_ddx_coarse:
     return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdxCoarse);
   case Intrinsic::spv_ddy_coarse:

diff  --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx.ll
new file mode 100644
index 0000000000000..c4f65a00d1374
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx.ll
@@ -0,0 +1,47 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | 
FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val --target-env spv1.4 %}
+
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+
+define noundef float @ddx_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpDPdx %[[#float_32]] %[[#float_32_arg]]
+  %elt.ddx = call float @llvm.spv.ddx.f32(float %a)
+  ret float %elt.ddx
+}
+
+define noundef half @ddx_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#float_32:]] %[[#float_16_arg]]
+; CHECK: %[[#ddx:]] = OpDPdx %[[#float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#float_16]] %[[#ddx]]
+  %elt.ddx = call half @llvm.spv.ddx.f16(half %a)
+  ret half %elt.ddx
+}
+
+define noundef <4 x float> @ddx_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpDPdx %[[#vec4_float_32]] %[[#vec4_float_32_arg]]
+  %elt.ddx = call <4 x float> @llvm.spv.ddx.v4f32(<4 x float> %a)
+  ret <4 x float> %elt.ddx
+}
+
+define noundef <4 x half> @ddx_half_vector(<4 x half> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#vec4_float_32:]] 
%[[#vec4_float_16_arg]]
+; CHECK: %[[#ddx:]] = OpDPdx %[[#vec4_float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#vec4_float_16]] %[[#ddx]]
+  %elt.ddx = call <4 x half> @llvm.spv.ddx.v4f16(<4 x half> %a)
+  ret <4 x half> %elt.ddx
+}
+
+declare float @llvm.spv.ddx.f32(float)
+declare half @llvm.spv.ddx.f16(half)

diff  --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy.ll
new file mode 100644
index 0000000000000..dd6250b757d9f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy.ll
@@ -0,0 +1,47 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | 
FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val --target-env spv1.4 %}
+
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+
+define noundef float @ddy_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpDPdy %[[#float_32]] %[[#float_32_arg]]
+  %elt.ddy = call float @llvm.spv.ddy.f32(float %a)
+  ret float %elt.ddy
+}
+
+define noundef half @ddy_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#float_32:]] %[[#float_16_arg]]
+; CHECK: %[[#ddy:]] = OpDPdy %[[#float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#float_16]] %[[#ddy]]
+  %elt.ddy = call half @llvm.spv.ddy.f16(half %a)
+  ret half %elt.ddy
+}
+
+define noundef <4 x float> @ddy_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpDPdy %[[#vec4_float_32]] %[[#vec4_float_32_arg]]
+  %elt.ddy = call <4 x float> @llvm.spv.ddy.v4f32(<4 x float> %a)
+  ret <4 x float> %elt.ddy
+}
+
+define noundef <4 x half> @ddy_half_vector(<4 x half> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#vec4_float_32:]] 
%[[#vec4_float_16_arg]]
+; CHECK: %[[#ddy:]] = OpDPdy %[[#vec4_float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#vec4_float_16]] %[[#ddy]]
+  %elt.ddy = call <4 x half> @llvm.spv.ddy.v4f16(<4 x half> %a)
+  ret <4 x half> %elt.ddy
+}
+
+declare float @llvm.spv.ddy.f32(float)
+declare half @llvm.spv.ddy.f16(half)

diff  --git a/llvm/test/CodeGen/SPIRV/opencl/ddx-error.ll 
b/llvm/test/CodeGen/SPIRV/opencl/ddx-error.ll
new file mode 100644
index 0000000000000..5ab1147cee60c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddx-error.ll
@@ -0,0 +1,12 @@
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+
+; CHECK: LLVM ERROR: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.ddx), %{{.*}} 
is only supported in shaders.
+
+define noundef float @ddx(float noundef %a) {
+entry:
+  %spv.ddx = call float @llvm.spv.ddx.f32(float %a)
+  ret float %spv.ddx
+}
+
+declare float @llvm.spv.ddx.f32(float)

diff  --git a/llvm/test/CodeGen/SPIRV/opencl/ddy-error.ll 
b/llvm/test/CodeGen/SPIRV/opencl/ddy-error.ll
new file mode 100644
index 0000000000000..9b281c338f64c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddy-error.ll
@@ -0,0 +1,12 @@
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+
+; CHECK: LLVM ERROR: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.ddy), %{{.*}} 
is only supported in shaders.
+
+define noundef float @ddy(float noundef %a) {
+entry:
+  %spv.ddy = call float @llvm.spv.ddy.f32(float %a)
+  ret float %spv.ddy
+}
+
+declare float @llvm.spv.ddy.f32(float)


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to