https://github.com/Alexander-Johnston updated https://github.com/llvm/llvm-project/pull/161378
>From 5af43f878799ca037a963ba2bd331a7b641d295c Mon Sep 17 00:00:00 2001 From: Alexander Johnston <[email protected]> Date: Mon, 29 Sep 2025 19:54:02 +0100 Subject: [PATCH 1/3] [HLSL] Implement the `fwidth` intrinsic Closes #99120 --- clang/include/clang/Basic/BuiltinsSPIRVVK.td | 1 + clang/lib/CodeGen/TargetBuiltins/SPIR.cpp | 5 + .../lib/Headers/hlsl/hlsl_intrinsic_helpers.h | 12 ++ clang/lib/Headers/hlsl/hlsl_intrinsics.h | 40 ++++++ clang/lib/Sema/SemaSPIRV.cpp | 18 +++ clang/test/CodeGenHLSL/builtins/fwidth.hlsl | 118 ++++++++++++++++++ clang/test/CodeGenSPIRV/Builtins/fwidth.c | 41 ++++++ clang/test/SemaSPIRV/BuiltIns/fwidth-errors.c | 24 ++++ llvm/include/llvm/IR/IntrinsicsSPIRV.td | 1 + .../Target/SPIRV/SPIRVInstructionSelector.cpp | 18 +-- .../CodeGen/SPIRV/hlsl-intrinsics/fwidth.ll | 47 +++++++ .../test/CodeGen/SPIRV/opencl/fwidth-error.ll | 12 ++ 12 files changed, 329 insertions(+), 8 deletions(-) create mode 100644 clang/test/CodeGenHLSL/builtins/fwidth.hlsl create mode 100644 clang/test/CodeGenSPIRV/Builtins/fwidth.c create mode 100644 clang/test/SemaSPIRV/BuiltIns/fwidth-errors.c create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/fwidth.ll create mode 100644 llvm/test/CodeGen/SPIRV/opencl/fwidth-error.ll diff --git a/clang/include/clang/Basic/BuiltinsSPIRVVK.td b/clang/include/clang/Basic/BuiltinsSPIRVVK.td index 5dc3c7588cd2a..a077a763923d6 100644 --- a/clang/include/clang/Basic/BuiltinsSPIRVVK.td +++ b/clang/include/clang/Basic/BuiltinsSPIRVVK.td @@ -12,3 +12,4 @@ 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 fwidth : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>; diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp index 243aad8bf7083..43b05a128e876 100644 --- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp @@ -151,6 +151,11 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID, Intrinsic::spv_global_offset, ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr, "spv.global.offset"); + case SPIRV::BI__builtin_spirv_fwidth: + return Builder.CreateIntrinsic( + /*ReturnType=*/getTypes().ConvertType(E->getType()), + Intrinsic::spv_fwidth, ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, + nullptr, "spv.fwidth"); } return nullptr; } diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h index 3d8fe7ea701a6..d1dc8275431c0 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h @@ -160,6 +160,18 @@ constexpr K firstbithigh_impl(T X) { return FBH; } +template <typename T> constexpr T fwidth_impl(T input) { +#if (__has_builtin(__builtin_spirv_fwidth)) + return __builtin_spirv_fwidth(input); +#else + T derivCoarseX = ddx_coarse(input); + derivCoarseX = abs(derivCoarseX); + T derivCoarseY = ddy_coarse(input); + derivCoarseY = abs(derivCoarseY); + return derivCoarseX + derivCoarseY; +#endif +} + } // namespace __detail } // namespace hlsl diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index 33ed14328ee8a..c26c8bb5261d4 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -666,5 +666,45 @@ smoothstep(__detail::HLSL_FIXED_VECTOR<float, N> Min, return __detail::smoothstep_vec_impl(Min, Max, X); } +//===----------------------------------------------------------------------===// +// fwidth builtin +//===----------------------------------------------------------------------===// + +/// \fn T fwidth(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> fwidth(T input) { + return __detail::fwidth_impl(input); +} + +template <typename T> +const inline __detail::enable_if_t< + __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T> +fwidth(T input) { + return __detail::fwidth_impl(input); +} + +template <int N> +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +const inline __detail::HLSL_FIXED_VECTOR<half, N> fwidth( + __detail::HLSL_FIXED_VECTOR<half, N> input) { + return __detail::fwidth_impl(input); +} + +template <int N> +const inline __detail::HLSL_FIXED_VECTOR<float, N> +fwidth(__detail::HLSL_FIXED_VECTOR<float, N> input) { + return __detail::fwidth_impl(input); +} + } // namespace hlsl #endif //_HLSL_HLSL_INTRINSICS_H_ diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp index c8ea0d09c4081..0e78cff9c1774 100644 --- a/clang/lib/Sema/SemaSPIRV.cpp +++ b/clang/lib/Sema/SemaSPIRV.cpp @@ -360,6 +360,24 @@ 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_fwidth: { + if (SemaRef.checkArgCount(TheCall, 1)) + return true; + + // Check if first argument has floating representation + ExprResult A = TheCall->getArg(0); + QualType ArgTyA = A.get()->getType(); + if (!ArgTyA->hasFloatingRepresentation()) { + SemaRef.Diag(A.get()->getBeginLoc(), diag::err_builtin_invalid_arg_type) + << /* ordinal */ 1 << /* scalar or vector */ 5 << /* no int */ 0 + << /* fp */ 1 << ArgTyA; + return true; + } + + QualType RetTy = ArgTyA; + TheCall->setType(RetTy); + break; + } } return false; } diff --git a/clang/test/CodeGenHLSL/builtins/fwidth.hlsl b/clang/test/CodeGenHLSL/builtins/fwidth.hlsl new file mode 100644 index 0000000000000..2388bbd8d5a1e --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/fwidth.hlsl @@ -0,0 +1,118 @@ +// 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: define {{.*}} half @_ZN4hlsl8__detail11fwidth_implIDhEET_S2_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} half @llvm.dx.ddx.coarse.f16(half %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} half @llvm.fabs.f16(half %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} half @llvm.dx.ddy.coarse.f16(half %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} half @llvm.fabs.f16(half %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret half %{{.*}} +// CHECK-LABEL-SPIRV: half @_Z15test_f16_fwidthDh +// CHECK-SPIRV: %spv.fwidth = call {{.*}} half @llvm.spv.fwidth.f16(half %{{.*}}) +// CHECK-SPIRV: ret half %spv.fwidth +half test_f16_fwidth(half val) { + return fwidth(val); +} + +// CHECK-LABEL: define {{.*}} <2 x half> @_ZN4hlsl8__detail11fwidth_implIDv2_DhEET_S3_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} <2 x half> @llvm.dx.ddx.coarse.v2f16(<2 x half> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <2 x half> @llvm.fabs.v2f16(<2 x half> %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} <2 x half> @llvm.dx.ddy.coarse.v2f16(<2 x half> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <2 x half> @llvm.fabs.v2f16(<2 x half> %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret <2 x half> %{{.*}} +// CHECK-LABEL-SPIRV: <2 x half> @_Z16test_f16_fwidth2Dv2_Dh +// CHECK-SPIRV: %spv.fwidth = call {{.*}} <2 x half> @llvm.spv.fwidth.v2f16(<2 x half> %{{.*}}) +// CHECK-SPIRV: ret <2 x half> %spv.fwidth +half2 test_f16_fwidth2(half2 val) { + return fwidth(val); +} + +// CHECK-LABEL: define {{.*}} <3 x half> @_ZN4hlsl8__detail11fwidth_implIDv3_DhEET_S3_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} <3 x half> @llvm.dx.ddx.coarse.v3f16(<3 x half> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <3 x half> @llvm.fabs.v3f16(<3 x half> %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} <3 x half> @llvm.dx.ddy.coarse.v3f16(<3 x half> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <3 x half> @llvm.fabs.v3f16(<3 x half> %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret <3 x half> %{{.*}} +// CHECK-LABEL-SPIRV: <3 x half> @_Z16test_f16_fwidth3Dv3_Dh +// CHECK-SPIRV: %spv.fwidth = call {{.*}} <3 x half> @llvm.spv.fwidth.v3f16(<3 x half> %{{.*}}) +// CHECK-SPIRV: ret <3 x half> %spv.fwidth +half3 test_f16_fwidth3(half3 val) { + return fwidth(val); +} + +// CHECK-LABEL: define {{.*}} <4 x half> @_ZN4hlsl8__detail11fwidth_implIDv4_DhEET_S3_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} <4 x half> @llvm.dx.ddx.coarse.v4f16(<4 x half> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <4 x half> @llvm.fabs.v4f16(<4 x half> %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} <4 x half> @llvm.dx.ddy.coarse.v4f16(<4 x half> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <4 x half> @llvm.fabs.v4f16(<4 x half> %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret <4 x half> %{{.*}} +// CHECK-LABEL-SPIRV: <4 x half> @_Z16test_f16_fwidth4Dv4_Dh +// CHECK-SPIRV: %spv.fwidth = call {{.*}} <4 x half> @llvm.spv.fwidth.v4f16(<4 x half> %{{.*}}) +// CHECK-SPIRV: ret <4 x half> %spv.fwidth +half4 test_f16_fwidth4(half4 val) { + return fwidth(val); +} + +// CHECK-LABEL: define {{.*}} float @_ZN4hlsl8__detail11fwidth_implIfEET_S2_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} float @llvm.dx.ddx.coarse.f32(float %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} float @llvm.fabs.f32(float %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} float @llvm.dx.ddy.coarse.f32(float %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} float @llvm.fabs.f32(float %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret float %{{.*}} +// CHECK-LABEL-SPIRV: float @_Z15test_f32_fwidthf +// CHECK-SPIRV: %spv.fwidth = call {{.*}} float @llvm.spv.fwidth.f32(float %{{.*}}) +// CHECK-SPIRV: ret float %spv.fwidth +float test_f32_fwidth(float val) { + return fwidth(val); +} + +// CHECK-LABEL: define {{.*}} <2 x float> @_ZN4hlsl8__detail11fwidth_implIDv2_fEET_S3_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} <2 x float> @llvm.dx.ddx.coarse.v2f32(<2 x float> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <2 x float> @llvm.fabs.v2f32(<2 x float> %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} <2 x float> @llvm.dx.ddy.coarse.v2f32(<2 x float> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <2 x float> @llvm.fabs.v2f32(<2 x float> %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret <2 x float> %{{.*}} +// CHECK-LABEL-SPIRV: <2 x float> @_Z16test_f32_fwidth2Dv2_f +// CHECK-SPIRV: %spv.fwidth = call {{.*}} <2 x float> @llvm.spv.fwidth.v2f32(<2 x float> %{{.*}}) +// CHECK-SPIRV: ret <2 x float> %spv.fwidth +float2 test_f32_fwidth2(float2 val) { + return fwidth(val); +} + +// CHECK-LABEL: define {{.*}} <3 x float> @_ZN4hlsl8__detail11fwidth_implIDv3_fEET_S3_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} <3 x float> @llvm.dx.ddx.coarse.v3f32(<3 x float> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <3 x float> @llvm.fabs.v3f32(<3 x float> %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} <3 x float> @llvm.dx.ddy.coarse.v3f32(<3 x float> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <3 x float> @llvm.fabs.v3f32(<3 x float> %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret <3 x float> %{{.*}} +// CHECK-LABEL-SPIRV: <3 x float> @_Z16test_f32_fwidth3Dv3_f +// CHECK-SPIRV: %spv.fwidth = call {{.*}} <3 x float> @llvm.spv.fwidth.v3f32(<3 x float> %{{.*}}) +// CHECK-SPIRV: ret <3 x float> %spv.fwidth +float3 test_f32_fwidth3(float3 val) { + return fwidth(val); +} + +// CHECK-LABEL: define {{.*}} <4 x float> @_ZN4hlsl8__detail11fwidth_implIDv4_fEET_S3_ +// CHECK: %hlsl.ddx.coarse = call {{.*}} <4 x float> @llvm.dx.ddx.coarse.v4f32(<4 x float> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <4 x float> @llvm.fabs.v4f32(<4 x float> %{{.*}}) +// CHECK: %hlsl.ddy.coarse = call {{.*}} <4 x float> @llvm.dx.ddy.coarse.v4f32(<4 x float> %{{.*}}) +// CHECK: %{{.*}} = call {{.*}} <4 x float> @llvm.fabs.v4f32(<4 x float> %{{.*}}) +// CHECK: %{{.*}} = fadd {{.*}} %{{.*}}, %{{.*}} +// CHECK: ret <4 x float> %{{.*}} +// CHECK-LABEL-SPIRV: <4 x float> @_Z16test_f32_fwidth4Dv4_f +// CHECK-SPIRV: %spv.fwidth = call {{.*}} <4 x float> @llvm.spv.fwidth.v4f32(<4 x float> %{{.*}}) +// CHECK-SPIRV: ret <4 x float> %spv.fwidth +float4 test_f32_fwidth4(float4 val) { + return fwidth(val); +} diff --git a/clang/test/CodeGenSPIRV/Builtins/fwidth.c b/clang/test/CodeGenSPIRV/Builtins/fwidth.c new file mode 100644 index 0000000000000..db19c1362c501 --- /dev/null +++ b/clang/test/CodeGenSPIRV/Builtins/fwidth.c @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -O1 -triple spirv-pc-vulkan-compute %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: [[fwidth0:%.*]] = tail call half @llvm.spv.fwidth.f16(half {{%.*}}) +// CHECK: ret half [[fwidth0]] +half test_fwidth_half(half X) { return __builtin_spirv_fwidth(X); } + +// CHECK: [[fwidth0:%.*]] = tail call <2 x half> @llvm.spv.fwidth.v2f16(<2 x half> {{%.*}}) +// CHECK: ret <2 x half> [[fwidth0]] +half2 test_fwidth_half2(half2 X) { return __builtin_spirv_fwidth(X); } + +// CHECK: [[fwidth0:%.*]] = tail call <3 x half> @llvm.spv.fwidth.v3f16(<3 x half> {{%.*}}) +// CHECK: ret <3 x half> [[fwidth0]] +half3 test_fwidth_half3(half3 X) { return __builtin_spirv_fwidth(X); } + +// CHECK: [[fwidth0:%.*]] = tail call <4 x half> @llvm.spv.fwidth.v4f16(<4 x half> {{%.*}}) +// CHECK: ret <4 x half> [[fwidth0]] +half4 test_fwidth_half4(half4 X) { return __builtin_spirv_fwidth(X); } + +// CHECK: [[fwidth0:%.*]] = tail call float @llvm.spv.fwidth.f32(float {{%.*}}) +// CHECK: ret float [[fwidth0]] +float test_fwidth_float(float X) { return __builtin_spirv_fwidth(X); } + +// CHECK: [[fwidth1:%.*]] = tail call <2 x float> @llvm.spv.fwidth.v2f32(<2 x float> {{%.*}}) +// CHECK: ret <2 x float> [[fwidth1]] +float2 test_fwidth_float2(float2 X) { return __builtin_spirv_fwidth(X); } + +// CHECK: [[fwidth2:%.*]] = tail call <3 x float> @llvm.spv.fwidth.v3f32(<3 x float> {{%.*}}) +// CHECK: ret <3 x float> [[fwidth2]] +float3 test_fwidth_float3(float3 X) { return __builtin_spirv_fwidth(X); } + +// CHECK: [[fwidth3:%.*]] = tail call <4 x float> @llvm.spv.fwidth.v4f32(<4 x float> {{%.*}}) +// CHECK: ret <4 x float> [[fwidth3]] +float4 test_fwidth_float4(float4 X) { return __builtin_spirv_fwidth(X); } diff --git a/clang/test/SemaSPIRV/BuiltIns/fwidth-errors.c b/clang/test/SemaSPIRV/BuiltIns/fwidth-errors.c new file mode 100644 index 0000000000000..44cdd819e4332 --- /dev/null +++ b/clang/test/SemaSPIRV/BuiltIns/fwidth-errors.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-compute -verify + +typedef float float2 __attribute__((ext_vector_type(2))); + +void test_too_few_arg() +{ + return __builtin_spirv_fwidth(); + // expected-error@-1 {{too few arguments to function call, expected 1, have 0}} +} + +float test_too_many_arg(float p0) { + return __builtin_spirv_fwidth(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_fwidth(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_fwidth(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 2f7c25550a0cc..366f8cf36d75c 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -136,6 +136,7 @@ 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_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]>; def int_spv_nclamp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>; diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 0653b4eb9dfe2..40b1142850233 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -328,8 +328,8 @@ class SPIRVInstructionSelector : public InstructionSelector { MachineInstr &I) const; bool selectFrexp(Register ResVReg, const SPIRVType *ResType, MachineInstr &I) const; - bool selectDpdCoarse(Register ResVReg, const SPIRVType *ResType, - MachineInstr &I, const unsigned DPdOpCode) const; + bool selectDerivativeInst(Register ResVReg, const SPIRVType *ResType, + MachineInstr &I, const unsigned DPdOpCode) const; // Utilities std::pair<Register, bool> buildI32Constant(uint32_t Val, MachineInstr &I, @@ -3143,10 +3143,9 @@ bool SPIRVInstructionSelector::wrapIntoSpecConstantOp( return Result; } -bool SPIRVInstructionSelector::selectDpdCoarse(Register ResVReg, - const SPIRVType *ResType, - MachineInstr &I, - const unsigned DPdOpCode) const { +bool SPIRVInstructionSelector::selectDerivativeInst( + Register ResVReg, const SPIRVType *ResType, MachineInstr &I, + const unsigned DPdOpCode) const { // TODO: This should check specifically for Fragment Execution Model, but STI // doesn't provide that information yet. See #167562 errorIfInstrOutsideShader(I); @@ -3584,10 +3583,13 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, return selectExtInst(ResVReg, ResType, I, GL::UnpackHalf2x16); } case Intrinsic::spv_ddx_coarse: { - return selectDpdCoarse(ResVReg, ResType, I, SPIRV::OpDPdxCoarse); + return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdxCoarse); } case Intrinsic::spv_ddy_coarse: { - return selectDpdCoarse(ResVReg, ResType, I, SPIRV::OpDPdyCoarse); + return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdyCoarse); + } + case Intrinsic::spv_fwidth: { + return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpFwidth); } default: { std::string DiagMsg; diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/fwidth.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/fwidth.ll new file mode 100644 index 0000000000000..1dea47d6b7a21 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/fwidth.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 %} + +; 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 @fwidth_float(float noundef %a) { +entry: +; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]] +; CHECK: %[[#]] = OpFwidth %[[#float_32]] %[[#float_32_arg]] + %elt.fwidth = call float @llvm.spv.fwidth.f32(float %a) + ret float %elt.fwidth +} + +define noundef half @fwidth_half(half noundef %a) { +entry: +; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]] +; CHECK: %[[#converted:]] = OpFConvert %[[#float_32]] %[[#float_16_arg]] +; CHECK: %[[#fwidth:]] = OpFwidth %[[#float_32]] %[[#converted]] +; CHECK: %[[#]] = OpFConvert %[[#float_16]] %[[#fwidth]] + %elt.fwidth = call half @llvm.spv.fwidth.f16(half %a) + ret half %elt.fwidth +} + +define noundef <4 x float> @fwidth_float_vector(<4 x float> noundef %a) { +entry: +; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]] +; CHECK: %[[#]] = OpFwidth %[[#vec4_float_32]] %[[#vec4_float_32_arg]] + %elt.fwidth = call <4 x float> @llvm.spv.fwidth.v4f32(<4 x float> %a) + ret <4 x float> %elt.fwidth +} + +define noundef <4 x half> @fwidth_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: %[[#fwidth:]] = OpFwidth %[[#vec4_float_32]] %[[#converted]] +; CHECK: %[[#]] = OpFConvert %[[#vec4_float_16]] %[[#fwidth]] + %elt.fwidth = call <4 x half> @llvm.spv.fwidth.v4f16(<4 x half> %a) + ret <4 x half> %elt.fwidth +} + +declare float @llvm.spv.fwidth.f32(float) +declare half @llvm.spv.fwidth.f16(half) diff --git a/llvm/test/CodeGen/SPIRV/opencl/fwidth-error.ll b/llvm/test/CodeGen/SPIRV/opencl/fwidth-error.ll new file mode 100644 index 0000000000000..feb994bfd0c71 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/opencl/fwidth-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.fwidth), %{{.*}} is only supported in shaders. + +define noundef float @fwidth(float noundef %a) { +entry: + %spv.fwidth = call float @llvm.spv.fwidth.f32(float %a) + ret float %spv.fwidth +} + +declare float @llvm.spv.fwidth.f32(float) >From 449b4226ea4a564e7d1e1016b369ca839812bc82 Mon Sep 17 00:00:00 2001 From: Alexander Johnston <[email protected]> Date: Wed, 19 Nov 2025 13:56:21 +0000 Subject: [PATCH 2/3] Fixup nits: Code style and fwidth test triple --- clang/test/CodeGenHLSL/builtins/fwidth.hlsl | 2 +- llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/clang/test/CodeGenHLSL/builtins/fwidth.hlsl b/clang/test/CodeGenHLSL/builtins/fwidth.hlsl index 2388bbd8d5a1e..2935fbbc4f6a2 100644 --- a/clang/test/CodeGenHLSL/builtins/fwidth.hlsl +++ b/clang/test/CodeGenHLSL/builtins/fwidth.hlsl @@ -1,7 +1,7 @@ // 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: %clang_cc1 -finclude-default-header -x hlsl -triple spirv-unknown-vulkan1.3-library %s \ // RUN: -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \ // RUN: FileCheck %s --check-prefixes=CHECK-SPIRV diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 40b1142850233..d3fc08eb56cb3 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -3582,15 +3582,12 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, case Intrinsic::spv_unpackhalf2x16: { return selectExtInst(ResVReg, ResType, I, GL::UnpackHalf2x16); } - case Intrinsic::spv_ddx_coarse: { + case Intrinsic::spv_ddx_coarse: return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdxCoarse); - } - case Intrinsic::spv_ddy_coarse: { + case Intrinsic::spv_ddy_coarse: return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdyCoarse); - } - case Intrinsic::spv_fwidth: { + case Intrinsic::spv_fwidth: return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpFwidth); - } default: { std::string DiagMsg; raw_string_ostream OS(DiagMsg); >From 6a7ef6876c1aff983b95f5a5347516b5c41fe1f4 Mon Sep 17 00:00:00 2001 From: Alexander Johnston <[email protected]> Date: Wed, 19 Nov 2025 13:56:45 +0000 Subject: [PATCH 3/3] Add Invalid stage test to ddx/y_coarse tests --- .../test/CodeGen/DirectX/ddx_coarse-errors.ll | 20 +++++++++++++++--- .../test/CodeGen/DirectX/ddy_coarse-errors.ll | 21 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll b/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll index 0679eec31cec1..567a3ef1d1d3c 100644 --- a/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll +++ b/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll @@ -1,8 +1,9 @@ -; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s +; 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.coarse does not support double overload type -; CHECK: in function ddx.coarse -; CHECK-SAME: Cannot create DerivCoarseX operation: Invalid overload type +; CHECK-TYPE: in function ddx.coarse +; CHECK-TYPE-SAME: Cannot create DerivCoarseX operation: Invalid overload type ; Function Attrs: noinline nounwind optnone define noundef double @ddx.coarse_double(double noundef %a) #0 { @@ -13,3 +14,16 @@ entry: %dx.ddx.coarse = call double @llvm.dx.ddx.coarse.f64(double %0) ret double %dx.ddx.coarse } + +; DXIL operation ddx.coarse does not support compute shader stage +; CHECK-STAGE: in function ddx.coarse +; CHECK-STAGE-SAME: Cannot create DerivCoarseX operation: Invalid stage +; Function Attrs: noinline nounwind optnone +define noundef float @ddx.coarse_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.coarse = call float @llvm.dx.ddx.coarse.f32(float %0) + ret float %dx.ddx.coarse +} diff --git a/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll b/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll index df8e3eb0f7e0b..215ece4017c17 100644 --- a/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll +++ b/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll @@ -1,8 +1,9 @@ -; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s +; 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.coarse does not support double overload type -; CHECK: in function ddy.coarse -; CHECK-SAME: Cannot create DerivCoarseY operation: Invalid overload type +; CHECK-TYPE: in function ddy.coarse +; CHECK-TYPE-SAME: Cannot create DerivCoarseY operation: Invalid overload type ; Function Attrs: noinline nounwind optnone define noundef double @ddy.coarse_double(double noundef %a) #0 { @@ -13,3 +14,17 @@ entry: %dx.ddy.coarse = call double @llvm.dx.ddy.coarse.f64(double %0) ret double %dx.ddy.coarse } + +; DXIL operation ddy.coarse does not support compute shader stage +; CHECK-STAGE: in function ddy.coarse +; CHECK-STAGE-SAME: Cannot create DerivCoarseY operation: Invalid stage + +; Function Attrs: noinline nounwind optnone +define noundef float @ddy.coarse_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.coarse = call float @llvm.dx.ddy.coarse.f32(float %0) + ret float %dx.ddy.coarse +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
