[clang] [llvm] [HLSL] Implement ddx/ddy_fine intrinsics (PR #168874)
Alexander Johnston via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 20 07:06:31 PST 2025
https://github.com/Alexander-Johnston updated https://github.com/llvm/llvm-project/pull/168874
>From 7d4660218d94def086a798852fc856851261bf02 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Wed, 19 Nov 2025 00:21:25 +0000
Subject: [PATCH] [HLSL] Implement ddx/ddy_fine intrinsics
Implements the HLSL ddx_fine and ddy_fine intrinsics together as the
implementations are nearly identical.
---
clang/include/clang/Basic/Builtins.td | 12 +++
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 18 ++++
clang/lib/CodeGen/CGHLSLRuntime.h | 2 +
.../lib/Headers/hlsl/hlsl_alias_intrinsics.h | 68 +++++++++++++++
clang/lib/Sema/SemaHLSL.cpp | 4 +-
.../builtins/ddx-fine-builtin.hlsl | 26 ++++++
clang/test/CodeGenHLSL/builtins/ddx-fine.hlsl | 86 +++++++++++++++++++
.../builtins/ddy-fine-builtin.hlsl | 26 ++++++
clang/test/CodeGenHLSL/builtins/ddy-fine.hlsl | 86 +++++++++++++++++++
.../SemaHLSL/BuiltIns/ddx-fine-errors.hlsl | 22 +++++
.../SemaHLSL/BuiltIns/ddy-fine-errors.hlsl | 22 +++++
llvm/include/llvm/IR/IntrinsicsDirectX.td | 2 +
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 2 +
llvm/lib/Target/DirectX/DXIL.td | 18 ++++
.../DirectX/DirectXTargetTransformInfo.cpp | 2 +
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 4 +
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 4 +-
llvm/test/CodeGen/DirectX/ddx_fine-errors.ll | 29 +++++++
llvm/test/CodeGen/DirectX/ddx_fine.ll | 40 +++++++++
llvm/test/CodeGen/DirectX/ddy_fine-errors.ll | 29 +++++++
llvm/test/CodeGen/DirectX/ddy_fine.ll | 40 +++++++++
.../CodeGen/SPIRV/hlsl-intrinsics/ddx_fine.ll | 47 ++++++++++
.../CodeGen/SPIRV/hlsl-intrinsics/ddy_fine.ll | 47 ++++++++++
.../CodeGen/SPIRV/opencl/ddx_fine-error.ll | 12 +++
.../CodeGen/SPIRV/opencl/ddy_fine-error.ll | 12 +++
25 files changed, 658 insertions(+), 2 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/builtins/ddx-fine-builtin.hlsl
create mode 100644 clang/test/CodeGenHLSL/builtins/ddx-fine.hlsl
create mode 100644 clang/test/CodeGenHLSL/builtins/ddy-fine-builtin.hlsl
create mode 100644 clang/test/CodeGenHLSL/builtins/ddy-fine.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/ddx-fine-errors.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/ddy-fine-errors.hlsl
create mode 100644 llvm/test/CodeGen/DirectX/ddx_fine-errors.ll
create mode 100644 llvm/test/CodeGen/DirectX/ddx_fine.ll
create mode 100644 llvm/test/CodeGen/DirectX/ddy_fine-errors.ll
create mode 100644 llvm/test/CodeGen/DirectX/ddy_fine.ll
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_fine.ll
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_fine.ll
create mode 100644 llvm/test/CodeGen/SPIRV/opencl/ddx_fine-error.ll
create mode 100644 llvm/test/CodeGen/SPIRV/opencl/ddy_fine-error.ll
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 502382a069856..64de76a01f335 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5265,6 +5265,18 @@ def HLSLDdyCoarse : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLDdxFine : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_elementwise_ddx_fine"];
+ let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def HLSLDdyFine : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_elementwise_ddy_fine"];
+ let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 12d9a98915ce3..bfa9a53b7804e 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -942,6 +942,24 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
ArrayRef<Value *>{Op0}, nullptr,
"hlsl.ddy.coarse");
}
+ case Builtin::BI__builtin_hlsl_elementwise_ddx_fine: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ if (!E->getArg(0)->getType()->hasFloatingRepresentation())
+ llvm_unreachable("ddx_fine operand must have a float representation");
+ Intrinsic::ID ID = CGM.getHLSLRuntime().getDdxFineIntrinsic();
+ return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
+ ArrayRef<Value *>{Op0}, nullptr,
+ "hlsl.ddx.fine");
+ }
+ case Builtin::BI__builtin_hlsl_elementwise_ddy_fine: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ if (!E->getArg(0)->getType()->hasFloatingRepresentation())
+ llvm_unreachable("ddy_fine operand must have a float representation");
+ Intrinsic::ID ID = CGM.getHLSLRuntime().getDdyFineIntrinsic();
+ return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
+ ArrayRef<Value *>{Op0}, nullptr,
+ "hlsl.ddy.fine");
+ }
case Builtin::BI__builtin_get_spirv_spec_constant_bool:
case Builtin::BI__builtin_get_spirv_spec_constant_short:
case Builtin::BI__builtin_get_spirv_spec_constant_ushort:
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c883282a8d9c8..2ad39c91280a0 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -171,6 +171,8 @@ class CGHLSLRuntime {
GENERATE_HLSL_INTRINSIC_FUNCTION(GetDimensionsX, resource_getdimensions_x)
GENERATE_HLSL_INTRINSIC_FUNCTION(DdxCoarse, ddx_coarse)
GENERATE_HLSL_INTRINSIC_FUNCTION(DdyCoarse, ddy_coarse)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(DdxFine, ddx_fine)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(DdyFine, ddy_fine)
//===----------------------------------------------------------------------===//
// End of reserved area for HLSL intrinsic getters.
diff --git a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
index 38b95ee90736a..f58150ed61106 100644
--- a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
@@ -3014,5 +3014,73 @@ float3 ddy_coarse(float3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
float4 ddy_coarse(float4);
+//===----------------------------------------------------------------------===//
+// ddx_fine builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddx_fine(T value)
+/// \brief Computes a high precision partial derivative with respect to the
+/// screen-space x-coordinate.
+/// \param value The input value.
+///
+/// The return value is a floating point scalar or vector containing the high
+/// prevision partial derivative of the input value.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+half ddx_fine(half);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+half2 ddx_fine(half2);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+half3 ddx_fine(half3);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+half4 ddx_fine(half4);
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+float ddx_fine(float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+float2 ddx_fine(float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+float3 ddx_fine(float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_fine)
+float4 ddx_fine(float4);
+
+//===----------------------------------------------------------------------===//
+// ddy_fine builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddy_fine(T value)
+/// \brief Computes a high precision partial derivative with respect to the
+/// screen-space y-coordinate.
+/// \param value The input value.
+///
+/// The return value is a floating point scalar or vector containing the high
+/// prevision partial derivative of the input value.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+half ddy_fine(half);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+half2 ddy_fine(half2);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+half3 ddy_fine(half3);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+half4 ddy_fine(half4);
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+float ddy_fine(float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+float2 ddy_fine(float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+float3 ddy_fine(float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_fine)
+float4 ddy_fine(float4);
+
} // namespace hlsl
#endif //_HLSL_HLSL_ALIAS_INTRINSICS_H_
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index c5666941fd36a..cc2939314b0eb 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3211,7 +3211,9 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
case Builtin::BI__builtin_hlsl_elementwise_rsqrt:
case Builtin::BI__builtin_hlsl_elementwise_frac:
case Builtin::BI__builtin_hlsl_elementwise_ddx_coarse:
- case Builtin::BI__builtin_hlsl_elementwise_ddy_coarse: {
+ case Builtin::BI__builtin_hlsl_elementwise_ddy_coarse:
+ case Builtin::BI__builtin_hlsl_elementwise_ddx_fine:
+ case Builtin::BI__builtin_hlsl_elementwise_ddy_fine: {
if (SemaRef.checkArgCount(TheCall, 1))
return true;
if (CheckAllArgTypesAreCorrect(&SemaRef, TheCall,
diff --git a/clang/test/CodeGenHLSL/builtins/ddx-fine-builtin.hlsl b/clang/test/CodeGenHLSL/builtins/ddx-fine-builtin.hlsl
new file mode 100644
index 0000000000000..69f7ab3c6ce62
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddx-fine-builtin.hlsl
@@ -0,0 +1,26 @@
+// 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-compute %s \
+// RUN: -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK-SPIRV
+
+// CHECK-LABEL: half @_Z17test_f16_ddx_fineDh
+// CHECK: %hlsl.ddx.fine = call {{.*}} half @llvm.dx.ddx.fine.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: half @_Z17test_f16_ddx_fineDh
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} half @llvm.spv.ddx.fine.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddx.fine
+half test_f16_ddx_fine(half val) {
+ return __builtin_hlsl_elementwise_ddx_fine(val);
+}
+
+// CHECK-LABEL: float @_Z17test_f32_ddx_finef
+// CHECK: %hlsl.ddx.fine = call {{.*}} float @llvm.dx.ddx.fine.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: float @_Z17test_f32_ddx_finef
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} float @llvm.spv.ddx.fine.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddx.fine
+float test_f32_ddx_fine(float val) {
+ return __builtin_hlsl_elementwise_ddx_fine(val);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ddx-fine.hlsl b/clang/test/CodeGenHLSL/builtins/ddx-fine.hlsl
new file mode 100644
index 0000000000000..2630260abcb43
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddx-fine.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-compute %s \
+// RUN: -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK-SPIRV
+
+// CHECK-LABEL: half @_Z17test_f16_ddx_fineDh
+// CHECK: %hlsl.ddx.fine = call {{.*}} half @llvm.dx.ddx.fine.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: half @_Z17test_f16_ddx_fineDh
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} half @llvm.spv.ddx.fine.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddx.fine
+half test_f16_ddx_fine(half val) {
+ return ddx_fine(val);
+}
+
+// CHECK-LABEL: <2 x half> @_Z18test_f16_ddx_fine2Dv2_Dh
+// CHECK: %hlsl.ddx.fine = call {{.*}} <2 x half> @llvm.dx.ddx.fine.v2f16(<2 x half> %{{.*}})
+// CHECK: ret <2 x half> %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: <2 x half> @_Z18test_f16_ddx_fine2Dv2_Dh
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} <2 x half> @llvm.spv.ddx.fine.v2f16(<2 x half> %{{.*}})
+// CHECK-SPIRV: ret <2 x half> %hlsl.ddx.fine
+half2 test_f16_ddx_fine2(half2 val) {
+ return ddx_fine(val);
+}
+
+// CHECK-LABEL: <3 x half> @_Z18test_f16_ddx_fine3Dv3_Dh
+// CHECK: %hlsl.ddx.fine = call {{.*}} <3 x half> @llvm.dx.ddx.fine.v3f16(<3 x half> %{{.*}})
+// CHECK: ret <3 x half> %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: <3 x half> @_Z18test_f16_ddx_fine3Dv3_Dh
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} <3 x half> @llvm.spv.ddx.fine.v3f16(<3 x half> %{{.*}})
+// CHECK-SPIRV: ret <3 x half> %hlsl.ddx.fine
+half3 test_f16_ddx_fine3(half3 val) {
+ return ddx_fine(val);
+}
+
+// CHECK-LABEL: <4 x half> @_Z18test_f16_ddx_fine4Dv4_Dh
+// CHECK: %hlsl.ddx.fine = call {{.*}} <4 x half> @llvm.dx.ddx.fine.v4f16(<4 x half> %{{.*}})
+// CHECK: ret <4 x half> %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: <4 x half> @_Z18test_f16_ddx_fine4Dv4_Dh
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} <4 x half> @llvm.spv.ddx.fine.v4f16(<4 x half> %{{.*}})
+// CHECK-SPIRV: ret <4 x half> %hlsl.ddx.fine
+half4 test_f16_ddx_fine4(half4 val) {
+ return ddx_fine(val);
+}
+
+// CHECK-LABEL: float @_Z17test_f32_ddx_finef
+// CHECK: %hlsl.ddx.fine = call {{.*}} float @llvm.dx.ddx.fine.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: float @_Z17test_f32_ddx_finef
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} float @llvm.spv.ddx.fine.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddx.fine
+float test_f32_ddx_fine(float val) {
+ return ddx_fine(val);
+}
+
+// CHECK-LABEL: <2 x float> @_Z18test_f32_ddx_fine2Dv2_f
+// CHECK: %hlsl.ddx.fine = call {{.*}} <2 x float> @llvm.dx.ddx.fine.v2f32(<2 x float> %{{.*}})
+// CHECK: ret <2 x float> %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: <2 x float> @_Z18test_f32_ddx_fine2Dv2_f
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} <2 x float> @llvm.spv.ddx.fine.v2f32(<2 x float> %{{.*}})
+// CHECK-SPIRV: ret <2 x float> %hlsl.ddx.fine
+float2 test_f32_ddx_fine2(float2 val) {
+ return ddx_fine(val);
+}
+
+// CHECK-LABEL: <3 x float> @_Z18test_f32_ddx_fine3Dv3_f
+// CHECK: %hlsl.ddx.fine = call {{.*}} <3 x float> @llvm.dx.ddx.fine.v3f32(<3 x float> %{{.*}})
+// CHECK: ret <3 x float> %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: <3 x float> @_Z18test_f32_ddx_fine3Dv3_f
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} <3 x float> @llvm.spv.ddx.fine.v3f32(<3 x float> %{{.*}})
+// CHECK-SPIRV: ret <3 x float> %hlsl.ddx.fine
+float3 test_f32_ddx_fine3(float3 val) {
+ return ddx_fine(val);
+}
+
+// CHECK-LABEL: <4 x float> @_Z18test_f32_ddx_fine4Dv4_f
+// CHECK: %hlsl.ddx.fine = call {{.*}} <4 x float> @llvm.dx.ddx.fine.v4f32(<4 x float> %{{.*}})
+// CHECK: ret <4 x float> %hlsl.ddx.fine
+// CHECK-LABEL-SPIRV: <4 x float> @_Z18test_f32_ddx_fine4Dv4_f
+// CHECK-SPIRV: %hlsl.ddx.fine = call {{.*}} <4 x float> @llvm.spv.ddx.fine.v4f32(<4 x float> %{{.*}})
+// CHECK-SPIRV: ret <4 x float> %hlsl.ddx.fine
+float4 test_f32_ddx_fine4(float4 val) {
+ return ddx_fine(val);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ddy-fine-builtin.hlsl b/clang/test/CodeGenHLSL/builtins/ddy-fine-builtin.hlsl
new file mode 100644
index 0000000000000..00630721ceb66
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddy-fine-builtin.hlsl
@@ -0,0 +1,26 @@
+// 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-compute %s \
+// RUN: -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK-SPIRV
+
+// CHECK-LABEL: half @_Z17test_f16_ddy_fineDh
+// CHECK: %hlsl.ddy.fine = call {{.*}} half @llvm.dx.ddy.fine.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: half @_Z17test_f16_ddy_fineDh
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} half @llvm.spv.ddy.fine.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddy.fine
+half test_f16_ddy_fine(half val) {
+ return __builtin_hlsl_elementwise_ddy_fine(val);
+}
+
+// CHECK-LABEL: float @_Z17test_f32_ddy_finef
+// CHECK: %hlsl.ddy.fine = call {{.*}} float @llvm.dx.ddy.fine.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: float @_Z17test_f32_ddy_finef
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} float @llvm.spv.ddy.fine.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddy.fine
+float test_f32_ddy_fine(float val) {
+ return __builtin_hlsl_elementwise_ddy_fine(val);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ddy-fine.hlsl b/clang/test/CodeGenHLSL/builtins/ddy-fine.hlsl
new file mode 100644
index 0000000000000..7e32ee29e767d
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddy-fine.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-compute %s \
+// RUN: -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK-SPIRV
+
+// CHECK-LABEL: half @_Z17test_f16_ddy_fineDh
+// CHECK: %hlsl.ddy.fine = call {{.*}} half @llvm.dx.ddy.fine.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: half @_Z17test_f16_ddy_fineDh
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} half @llvm.spv.ddy.fine.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddy.fine
+half test_f16_ddy_fine(half val) {
+ return ddy_fine(val);
+}
+
+// CHECK-LABEL: <2 x half> @_Z18test_f16_ddy_fine2Dv2_Dh
+// CHECK: %hlsl.ddy.fine = call {{.*}} <2 x half> @llvm.dx.ddy.fine.v2f16(<2 x half> %{{.*}})
+// CHECK: ret <2 x half> %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: <2 x half> @_Z18test_f16_ddy_fine2Dv2_Dh
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} <2 x half> @llvm.spv.ddy.fine.v2f16(<2 x half> %{{.*}})
+// CHECK-SPIRV: ret <2 x half> %hlsl.ddy.fine
+half2 test_f16_ddy_fine2(half2 val) {
+ return ddy_fine(val);
+}
+
+// CHECK-LABEL: <3 x half> @_Z18test_f16_ddy_fine3Dv3_Dh
+// CHECK: %hlsl.ddy.fine = call {{.*}} <3 x half> @llvm.dx.ddy.fine.v3f16(<3 x half> %{{.*}})
+// CHECK: ret <3 x half> %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: <3 x half> @_Z18test_f16_ddy_fine3Dv3_Dh
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} <3 x half> @llvm.spv.ddy.fine.v3f16(<3 x half> %{{.*}})
+// CHECK-SPIRV: ret <3 x half> %hlsl.ddy.fine
+half3 test_f16_ddy_fine3(half3 val) {
+ return ddy_fine(val);
+}
+
+// CHECK-LABEL: <4 x half> @_Z18test_f16_ddy_fine4Dv4_Dh
+// CHECK: %hlsl.ddy.fine = call {{.*}} <4 x half> @llvm.dx.ddy.fine.v4f16(<4 x half> %{{.*}})
+// CHECK: ret <4 x half> %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: <4 x half> @_Z18test_f16_ddy_fine4Dv4_Dh
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} <4 x half> @llvm.spv.ddy.fine.v4f16(<4 x half> %{{.*}})
+// CHECK-SPIRV: ret <4 x half> %hlsl.ddy.fine
+half4 test_f16_ddy_fine4(half4 val) {
+ return ddy_fine(val);
+}
+
+// CHECK-LABEL: float @_Z17test_f32_ddy_finef
+// CHECK: %hlsl.ddy.fine = call {{.*}} float @llvm.dx.ddy.fine.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: float @_Z17test_f32_ddy_finef
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} float @llvm.spv.ddy.fine.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddy.fine
+float test_f32_ddy_fine(float val) {
+ return ddy_fine(val);
+}
+
+// CHECK-LABEL: <2 x float> @_Z18test_f32_ddy_fine2Dv2_f
+// CHECK: %hlsl.ddy.fine = call {{.*}} <2 x float> @llvm.dx.ddy.fine.v2f32(<2 x float> %{{.*}})
+// CHECK: ret <2 x float> %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: <2 x float> @_Z18test_f32_ddy_fine2Dv2_f
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} <2 x float> @llvm.spv.ddy.fine.v2f32(<2 x float> %{{.*}})
+// CHECK-SPIRV: ret <2 x float> %hlsl.ddy.fine
+float2 test_f32_ddy_fine2(float2 val) {
+ return ddy_fine(val);
+}
+
+// CHECK-LABEL: <3 x float> @_Z18test_f32_ddy_fine3Dv3_f
+// CHECK: %hlsl.ddy.fine = call {{.*}} <3 x float> @llvm.dx.ddy.fine.v3f32(<3 x float> %{{.*}})
+// CHECK: ret <3 x float> %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: <3 x float> @_Z18test_f32_ddy_fine3Dv3_f
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} <3 x float> @llvm.spv.ddy.fine.v3f32(<3 x float> %{{.*}})
+// CHECK-SPIRV: ret <3 x float> %hlsl.ddy.fine
+float3 test_f32_ddy_fine3(float3 val) {
+ return ddy_fine(val);
+}
+
+// CHECK-LABEL: <4 x float> @_Z18test_f32_ddy_fine4Dv4_f
+// CHECK: %hlsl.ddy.fine = call {{.*}} <4 x float> @llvm.dx.ddy.fine.v4f32(<4 x float> %{{.*}})
+// CHECK: ret <4 x float> %hlsl.ddy.fine
+// CHECK-LABEL-SPIRV: <4 x float> @_Z18test_f32_ddy_fine4Dv4_f
+// CHECK-SPIRV: %hlsl.ddy.fine = call {{.*}} <4 x float> @llvm.spv.ddy.fine.v4f32(<4 x float> %{{.*}})
+// CHECK-SPIRV: ret <4 x float> %hlsl.ddy.fine
+float4 test_f32_ddy_fine4(float4 val) {
+ return ddy_fine(val);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ddx-fine-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/ddx-fine-errors.hlsl
new file mode 100644
index 0000000000000..71196943c322a
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ddx-fine-errors.hlsl
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -verify
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library %s -fnative-half-type -verify
+
+float no_arg() {
+ return __builtin_hlsl_elementwise_ddx_fine();
+ // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+float too_many_args(float val) {
+ return __builtin_hlsl_elementwise_ddx_fine(val, val);
+ // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+float test_integer_scalar_input(int val) {
+ return __builtin_hlsl_elementwise_ddx_fine(val);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'int')}}
+}
+
+double test_double_scalar_input(double val) {
+ return __builtin_hlsl_elementwise_ddx_fine(val);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'double')}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ddy-fine-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/ddy-fine-errors.hlsl
new file mode 100644
index 0000000000000..e17d5f36832b2
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ddy-fine-errors.hlsl
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -verify
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library %s -fnative-half-type -verify
+
+float no_arg() {
+ return __builtin_hlsl_elementwise_ddy_fine();
+ // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+float too_many_args(float val) {
+ return __builtin_hlsl_elementwise_ddy_fine(val, val);
+ // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+float test_integer_scalar_input(int val) {
+ return __builtin_hlsl_elementwise_ddy_fine(val);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'int')}}
+}
+
+double test_double_scalar_input(double val) {
+ return __builtin_hlsl_elementwise_ddy_fine(val);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'double')}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 485a49b16e2d2..8ca93731ffa04 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -172,6 +172,8 @@ def int_dx_radians : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>
def int_dx_discard : DefaultAttrsIntrinsic<[], [llvm_i1_ty], []>;
def int_dx_ddx_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
def int_dx_ddy_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
+def int_dx_ddx_fine : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
+def int_dx_ddy_fine : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
def int_dx_firstbituhigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
def int_dx_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
def int_dx_firstbitlow : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 366f8cf36d75c..552cc0aa59d77 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -136,6 +136,8 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
def int_spv_discard : DefaultAttrsIntrinsic<[], [], []>;
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_ddx_fine : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
+ def int_spv_ddy_fine : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
def int_spv_fwidth : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
def int_spv_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
def int_spv_sclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 8b2866260e9c9..b221fa2d7fe87 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -948,6 +948,24 @@ def DerivCoarseY : DXILOp<84, unary> {
let stages = [Stages<DXIL1_0, [library, pixel]>];
}
+def DerivFineX : DXILOp<85, unary> {
+ let Doc = "computes the rate of change per stamp in x direction";
+ let intrinsics = [IntrinSelect<int_dx_ddx_fine>];
+ let arguments = [OverloadTy];
+ let result = OverloadTy;
+ let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy]>];
+ let stages = [Stages<DXIL1_0, [library, pixel]>];
+}
+
+def DerivFineY : DXILOp<86, unary> {
+ let Doc = "computes the rate of change per stamp in y direction";
+ let intrinsics = [IntrinSelect<int_dx_ddy_fine>];
+ let arguments = [OverloadTy];
+ let result = OverloadTy;
+ let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy]>];
+ let stages = [Stages<DXIL1_0, [library, pixel]>];
+}
+
def ThreadId : DXILOp<93, threadId> {
let Doc = "Reads the thread ID";
let intrinsics = [IntrinSelect<int_dx_thread_id>];
diff --git a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
index a755dd522969d..f54b48b91265e 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
@@ -66,6 +66,8 @@ bool DirectXTTIImpl::isTargetIntrinsicTriviallyScalarizable(
case Intrinsic::dx_umad:
case Intrinsic::dx_ddx_coarse:
case Intrinsic::dx_ddy_coarse:
+ case Intrinsic::dx_ddx_fine:
+ case Intrinsic::dx_ddy_fine:
return true;
default:
return false;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index d3fc08eb56cb3..1599d72254f79 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3586,6 +3586,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdxCoarse);
case Intrinsic::spv_ddy_coarse:
return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdyCoarse);
+ case Intrinsic::spv_ddx_fine:
+ return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdxFine);
+ case Intrinsic::spv_ddy_fine:
+ return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdyFine);
case Intrinsic::spv_fwidth:
return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpFwidth);
default: {
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index bd754d17694b8..d1608f9790beb 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -2150,7 +2150,9 @@ void addInstrRequirements(const MachineInstr &MI,
break;
}
case SPIRV::OpDPdxCoarse:
- case SPIRV::OpDPdyCoarse: {
+ case SPIRV::OpDPdyCoarse:
+ case SPIRV::OpDPdxFine:
+ case SPIRV::OpDPdyFine: {
Reqs.addCapability(SPIRV::Capability::DerivativeControl);
break;
}
diff --git a/llvm/test/CodeGen/DirectX/ddx_fine-errors.ll b/llvm/test/CodeGen/DirectX/ddx_fine-errors.ll
new file mode 100644
index 0000000000000..c7c58599b47b4
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddx_fine-errors.ll
@@ -0,0 +1,29 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck --check-prefixes=CHECK-TYPE %s
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-compute %s 2>&1 | FileCheck --check-prefixes=CHECK-STAGE %s
+
+; DXIL operation ddx.fine does not support double overload type
+; CHECK-TYPE: in function ddx.fine
+; CHECK-TYPE-SAME: Cannot create DerivFineX operation: Invalid overload type
+
+; Function Attrs: noinline nounwind optnone
+define noundef double @ddx.fine_double(double noundef %a) #0 {
+entry:
+ %a.addr = alloca double, align 8
+ store double %a, ptr %a.addr, align 8
+ %0 = load double, ptr %a.addr, align 8
+ %dx.ddx.fine = call double @llvm.dx.ddx.fine.f64(double %0)
+ ret double %dx.ddx.fine
+}
+
+; DXIL operation ddx.fine does not support compute shader stage
+; CHECK-STAGE: in function ddx.fine
+; CHECK-STAGE-SAME: Cannot create DerivFineX operation: Invalid stage
+; Function Attrs: noinline nounwind optnone
+define noundef float @ddx.fine_float(float noundef %a) #0 {
+entry:
+ %a.addr = alloca float, align 8
+ store float %a, ptr %a.addr, align 8
+ %0 = load float, ptr %a.addr, align 8
+ %dx.ddx.fine = call float @llvm.dx.ddx.fine.f32(float %0)
+ ret float %dx.ddx.fine
+}
diff --git a/llvm/test/CodeGen/DirectX/ddx_fine.ll b/llvm/test/CodeGen/DirectX/ddx_fine.ll
new file mode 100644
index 0000000000000..b00df1572e083
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddx_fine.ll
@@ -0,0 +1,40 @@
+; RUN: opt -S -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; Make sure dxil operation function calls for ddx_fine are generated for half/float and matching vectors
+
+define noundef half @deriv_fine_x_half(half noundef %a) {
+; CHECK: call half @dx.op.unary.f16(i32 85, half %{{.*}})
+entry:
+ %dx.ddx.fine = call half @llvm.dx.ddx.fine.f16(half %a)
+ ret half %dx.ddx.fine
+}
+
+define noundef float @deriv_fine_x_float(float noundef %a) {
+; CHECK: call float @dx.op.unary.f32(i32 85, float %{{.*}})
+entry:
+ %dx.ddx.fine = call float @llvm.dx.ddx.fine.f32(float %a)
+ ret float %dx.ddx.fine
+}
+
+define noundef <4 x float> @deriv_fine_x_float4(<4 x float> noundef %a) {
+; CHECK: [[ee0:%.*]] = extractelement <4 x float> %a, i64 0
+; CHECK: [[ie0:%.*]] = call float @dx.op.unary.f32(i32 85, float [[ee0]])
+; CHECK: [[ee1:%.*]] = extractelement <4 x float> %a, i64 1
+; CHECK: [[ie1:%.*]] = call float @dx.op.unary.f32(i32 85, float [[ee1]])
+; CHECK: [[ee2:%.*]] = extractelement <4 x float> %a, i64 2
+; CHECK: [[ie2:%.*]] = call float @dx.op.unary.f32(i32 85, float [[ee2]])
+; CHECK: [[ee3:%.*]] = extractelement <4 x float> %a, i64 3
+; CHECK: [[ie3:%.*]] = call float @dx.op.unary.f32(i32 85, float [[ee3]])
+; CHECK: insertelement <4 x float> poison, float [[ie0]], i64 0
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie1]], i64 1
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie2]], i64 2
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie3]], i64 3
+; CHECK: ret <4 x float> %{{.*}}
+entry:
+ %dx.ddx.fine = call <4 x float> @llvm.dx.ddx.fine.v4f32(<4 x float> %a)
+ ret <4 x float> %dx.ddx.fine
+}
+
+declare half @llvm.dx.ddx.fine.f16(half)
+declare float @llvm.dx.ddx.fine.f32(float)
+declare <4 x float> @llvm.dx.ddx.fine.v4f32(<4 x float>)
diff --git a/llvm/test/CodeGen/DirectX/ddy_fine-errors.ll b/llvm/test/CodeGen/DirectX/ddy_fine-errors.ll
new file mode 100644
index 0000000000000..f92ca2a314d4c
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddy_fine-errors.ll
@@ -0,0 +1,29 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck --check-prefixes=CHECK-TYPE %s
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-compute %s 2>&1 | FileCheck --check-prefixes=CHECK-STAGE %s
+
+; DXIL operation ddy.fine does not support double overload type
+; CHECK-TYPE: in function ddy.fine
+; CHECK-TYPE-SAME: Cannot create DerivFineY operation: Invalid overload type
+
+; Function Attrs: noinline nounwind optnone
+define noundef double @ddy.fine_double(double noundef %a) #0 {
+entry:
+ %a.addr = alloca double, align 8
+ store double %a, ptr %a.addr, align 8
+ %0 = load double, ptr %a.addr, align 8
+ %dx.ddy.fine = call double @llvm.dx.ddy.fine.f64(double %0)
+ ret double %dx.ddy.fine
+}
+
+; DXIL operation ddy.fine does not support compute shader stage
+; CHECK-STAGE: in function ddy.fine
+; CHECK-STAGE-SAME: Cannot create DerivFineY operation: Invalid stage
+; Function Attrs: noinline nounwind optnone
+define noundef float @ddy.fine_float(float noundef %a) #0 {
+entry:
+ %a.addr = alloca float, align 8
+ store float %a, ptr %a.addr, align 8
+ %0 = load float, ptr %a.addr, align 8
+ %dx.ddy.fine = call float @llvm.dx.ddy.fine.f32(float %0)
+ ret float %dx.ddy.fine
+}
diff --git a/llvm/test/CodeGen/DirectX/ddy_fine.ll b/llvm/test/CodeGen/DirectX/ddy_fine.ll
new file mode 100644
index 0000000000000..e6c0da2c2c568
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddy_fine.ll
@@ -0,0 +1,40 @@
+; RUN: opt -S -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; Make sure dxil operation function calls for ddy_fine are generated for half/float and matching vectors
+
+define noundef half @deriv_fine_y_half(half noundef %a) {
+; CHECK: call half @dx.op.unary.f16(i32 86, half %{{.*}})
+entry:
+ %dx.ddy.fine = call half @llvm.dx.ddy.fine.f16(half %a)
+ ret half %dx.ddy.fine
+}
+
+define noundef float @deriv_fine_y_float(float noundef %a) {
+; CHECK: call float @dx.op.unary.f32(i32 86, float %{{.*}})
+entry:
+ %dx.ddy.fine = call float @llvm.dx.ddy.fine.f32(float %a)
+ ret float %dx.ddy.fine
+}
+
+define noundef <4 x float> @deriv_fine_y_float4(<4 x float> noundef %a) {
+; CHECK: [[ee0:%.*]] = extractelement <4 x float> %a, i64 0
+; CHECK: [[ie0:%.*]] = call float @dx.op.unary.f32(i32 86, float [[ee0]])
+; CHECK: [[ee1:%.*]] = extractelement <4 x float> %a, i64 1
+; CHECK: [[ie1:%.*]] = call float @dx.op.unary.f32(i32 86, float [[ee1]])
+; CHECK: [[ee2:%.*]] = extractelement <4 x float> %a, i64 2
+; CHECK: [[ie2:%.*]] = call float @dx.op.unary.f32(i32 86, float [[ee2]])
+; CHECK: [[ee3:%.*]] = extractelement <4 x float> %a, i64 3
+; CHECK: [[ie3:%.*]] = call float @dx.op.unary.f32(i32 86, float [[ee3]])
+; CHECK: insertelement <4 x float> poison, float [[ie0]], i64 0
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie1]], i64 1
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie2]], i64 2
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie3]], i64 3
+; CHECK: ret <4 x float> %{{.*}}
+entry:
+ %dx.ddy.fine = call <4 x float> @llvm.dx.ddy.fine.v4f32(<4 x float> %a)
+ ret <4 x float> %dx.ddy.fine
+}
+
+declare half @llvm.dx.ddy.fine.f16(half)
+declare float @llvm.dx.ddy.fine.f32(float)
+declare <4 x float> @llvm.dx.ddy.fine.v4f32(<4 x float>)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_fine.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_fine.ll
new file mode 100644
index 0000000000000..bba4612a05f52
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_fine.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_fine_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpDPdxFine %[[#float_32]] %[[#float_32_arg]]
+ %elt.ddx.fine = call float @llvm.spv.ddx.fine.f32(float %a)
+ ret float %elt.ddx.fine
+}
+
+define noundef half @ddx_fine_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#float_32:]] %[[#float_16_arg]]
+; CHECK: %[[#fine:]] = OpDPdxFine %[[#float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#float_16]] %[[#fine]]
+ %elt.ddx.fine = call half @llvm.spv.ddx.fine.f16(half %a)
+ ret half %elt.ddx.fine
+}
+
+define noundef <4 x float> @ddx_fine_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpDPdxFine %[[#vec4_float_32]] %[[#vec4_float_32_arg]]
+ %elt.ddx.fine = call <4 x float> @llvm.spv.ddx.fine.v4f32(<4 x float> %a)
+ ret <4 x float> %elt.ddx.fine
+}
+
+define noundef <4 x half> @ddx_fine_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: %[[#fine:]] = OpDPdxFine %[[#vec4_float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#vec4_float_16]] %[[#fine]]
+ %elt.ddx.fine = call <4 x half> @llvm.spv.ddx.fine.v4f16(<4 x half> %a)
+ ret <4 x half> %elt.ddx.fine
+}
+
+declare float @llvm.spv.ddx.fine.f32(float)
+declare half @llvm.spv.ddx.fine.f16(half)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_fine.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_fine.ll
new file mode 100644
index 0000000000000..a5609de03bfd8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_fine.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_fine_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpDPdyFine %[[#float_32]] %[[#float_32_arg]]
+ %elt.ddy.fine = call float @llvm.spv.ddy.fine.f32(float %a)
+ ret float %elt.ddy.fine
+}
+
+define noundef half @ddy_fine_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#float_32:]] %[[#float_16_arg]]
+; CHECK: %[[#fine:]] = OpDPdyFine %[[#float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#float_16]] %[[#fine]]
+ %elt.ddy.fine = call half @llvm.spv.ddy.fine.f16(half %a)
+ ret half %elt.ddy.fine
+}
+
+define noundef <4 x float> @ddy_fine_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpDPdyFine %[[#vec4_float_32]] %[[#vec4_float_32_arg]]
+ %elt.ddy.fine = call <4 x float> @llvm.spv.ddy.fine.v4f32(<4 x float> %a)
+ ret <4 x float> %elt.ddy.fine
+}
+
+define noundef <4 x half> @ddy_fine_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: %[[#fine:]] = OpDPdyFine %[[#vec4_float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#vec4_float_16]] %[[#fine]]
+ %elt.ddy.fine = call <4 x half> @llvm.spv.ddy.fine.v4f16(<4 x half> %a)
+ ret <4 x half> %elt.ddy.fine
+}
+
+declare float @llvm.spv.ddy.fine.f32(float)
+declare half @llvm.spv.ddy.fine.f16(half)
diff --git a/llvm/test/CodeGen/SPIRV/opencl/ddx_fine-error.ll b/llvm/test/CodeGen/SPIRV/opencl/ddx_fine-error.ll
new file mode 100644
index 0000000000000..88ac0f594cb5d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddx_fine-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.fine), %{{.*}} is only supported in shaders.
+
+define noundef float @ddx_fine(float noundef %a) {
+entry:
+ %spv.ddx.fine = call float @llvm.spv.ddx.fine.f32(float %a)
+ ret float %spv.ddx.fine
+}
+
+declare float @llvm.spv.ddx.fine.f32(float)
diff --git a/llvm/test/CodeGen/SPIRV/opencl/ddy_fine-error.ll b/llvm/test/CodeGen/SPIRV/opencl/ddy_fine-error.ll
new file mode 100644
index 0000000000000..f1353d2fc59bc
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddy_fine-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.fine), %{{.*}} is only supported in shaders.
+
+define noundef float @ddy_fine(float noundef %a) {
+entry:
+ %spv.ddy.fine = call float @llvm.spv.ddy.fine.f32(float %a)
+ ret float %spv.ddy.fine
+}
+
+declare float @llvm.spv.ddy.fine.f32(float)
More information about the llvm-commits
mailing list