https://github.com/Alexander-Johnston updated https://github.com/llvm/llvm-project/pull/168887
>From 9eeb0f70545165f05235bd117f1867c5fc43132b Mon Sep 17 00:00:00 2001 From: Alexander Johnston <[email protected]> Date: Thu, 20 Nov 2025 14:58:13 +0000 Subject: [PATCH 1/2] [HLSL] Implement ddx and ddy HLSL intrinsics ddx and ddy lower are implemented as ddx_coarse and ddy_coarse in DXIL. They lower to their own opcodes in SPIRV. --- clang/include/clang/Basic/BuiltinsSPIRVVK.td | 2 + clang/lib/CodeGen/TargetBuiltins/SPIR.cpp | 8 ++ .../lib/Headers/hlsl/hlsl_intrinsic_helpers.h | 16 ++++ clang/lib/Headers/hlsl/hlsl_intrinsics.h | 80 +++++++++++++++++ clang/lib/Sema/SemaSPIRV.cpp | 2 + clang/test/CodeGenHLSL/builtins/ddx.hlsl | 86 +++++++++++++++++++ clang/test/CodeGenHLSL/builtins/ddy.hlsl | 86 +++++++++++++++++++ clang/test/CodeGenSPIRV/Builtins/ddx.c | 41 +++++++++ clang/test/CodeGenSPIRV/Builtins/ddy.c | 41 +++++++++ clang/test/SemaSPIRV/BuiltIns/ddx-errors.c | 24 ++++++ clang/test/SemaSPIRV/BuiltIns/ddy-errors.c | 24 ++++++ llvm/include/llvm/IR/IntrinsicsSPIRV.td | 2 + .../Target/SPIRV/SPIRVInstructionSelector.cpp | 4 + .../test/CodeGen/SPIRV/hlsl-intrinsics/ddx.ll | 47 ++++++++++ .../test/CodeGen/SPIRV/hlsl-intrinsics/ddy.ll | 47 ++++++++++ llvm/test/CodeGen/SPIRV/opencl/ddx-error.ll | 12 +++ llvm/test/CodeGen/SPIRV/opencl/ddy-error.ll | 12 +++ 17 files changed, 534 insertions(+) create mode 100644 clang/test/CodeGenHLSL/builtins/ddx.hlsl create mode 100644 clang/test/CodeGenHLSL/builtins/ddy.hlsl create mode 100644 clang/test/CodeGenSPIRV/Builtins/ddx.c create mode 100644 clang/test/CodeGenSPIRV/Builtins/ddy.c create mode 100644 clang/test/SemaSPIRV/BuiltIns/ddx-errors.c create mode 100644 clang/test/SemaSPIRV/BuiltIns/ddy-errors.c create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx.ll create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy.ll create mode 100644 llvm/test/CodeGen/SPIRV/opencl/ddx-error.ll create mode 100644 llvm/test/CodeGen/SPIRV/opencl/ddy-error.ll 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 d1dc8275431c0..d0e1ec8b02797 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h @@ -160,6 +160,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 c26c8bb5261d4..052dcb7f7f455 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -666,6 +666,86 @@ smoothstep(__detail::HLSL_FIXED_VECTOR<float, N> Min, return __detail::smoothstep_vec_impl(Min, Max, X); } +//===----------------------------------------------------------------------===// +// ddx builtin +//===----------------------------------------------------------------------===// + +/// \fn T ddx(T x) +/// \brief Computes the sum of the absolute values of the partial derivatives +/// with regard to the x and y screen space coordinates. +/// \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 x and y screen space coordinates. +/// \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 d3fc08eb56cb3..1ee07fe049611 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -3582,6 +3582,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) >From 314a509717649b75f29d2b74e32fcb7961321f3a Mon Sep 17 00:00:00 2001 From: Alexander Johnston <[email protected]> Date: Thu, 20 Nov 2025 15:28:08 +0000 Subject: [PATCH 2/2] Fix intrinsics comment --- clang/lib/Headers/hlsl/hlsl_intrinsics.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index 052dcb7f7f455..db42972993cbe 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -672,7 +672,7 @@ smoothstep(__detail::HLSL_FIXED_VECTOR<float, N> Min, /// \fn T ddx(T x) /// \brief Computes the sum of the absolute values of the partial derivatives -/// with regard to the x and y screen space coordinates. +/// 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 @@ -712,7 +712,7 @@ ddx(__detail::HLSL_FIXED_VECTOR<float, N> input) { /// \fn T ddy(T x) /// \brief Computes the sum of the absolute values of the partial derivatives -/// with regard to the x and y screen space coordinates. +/// 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
