https://github.com/Alexander-Johnston created https://github.com/llvm/llvm-project/pull/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 >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] [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) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
