https://github.com/danbrown-amd updated https://github.com/llvm/llvm-project/pull/235154
>From 87f91e1c8eaa854da6df9d3d3a414136a873c1a6 Mon Sep 17 00:00:00 2001 From: danbrown-amd <[email protected]> Date: Fri, 16 Oct 2026 18:15:51 -0600 Subject: [PATCH] [HLSL] Implement frexp() Resolves #99119, #220654 Assisted-by: Claude Opus 5 --- clang/include/clang/Basic/Builtins.td | 6 + clang/lib/CodeGen/CGHLSLBuiltins.cpp | 31 ++++ clang/lib/Headers/hlsl/hlsl_intrinsics.h | 30 ++++ clang/lib/Sema/SemaHLSL.cpp | 17 ++ clang/test/CodeGenHLSL/builtins/frexp.hlsl | 78 +++++++++ .../test/CodeGenHLSL/builtins/frexp_mat.hlsl | 155 ++++++++++++++++++ .../test/SemaHLSL/BuiltIns/frexp-errors.hlsl | 65 ++++++++ .../SemaHLSL/BuiltIns/frexp_mat-errors.hlsl | 53 ++++++ .../Target/DirectX/DXILIntrinsicExpansion.cpp | 47 ++++++ .../Target/SPIRV/SPIRVInstructionSelector.cpp | 21 ++- llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 10 +- llvm/test/CodeGen/DirectX/LongVector/frexp.ll | 17 ++ llvm/test/CodeGen/DirectX/frexp.ll | 52 ++++++ llvm/test/CodeGen/DirectX/frexp_error.ll | 11 ++ .../extensions/SPV_EXT_long_vector/frexp.ll | 40 +++++ .../CodeGen/SPIRV/hlsl-intrinsics/frexp.ll | 61 +++++++ .../CodeGen/SPIRV/llvm-intrinsics/frexp.ll | 12 ++ 17 files changed, 698 insertions(+), 8 deletions(-) create mode 100644 clang/test/CodeGenHLSL/builtins/frexp.hlsl create mode 100644 clang/test/CodeGenHLSL/builtins/frexp_mat.hlsl create mode 100644 clang/test/SemaHLSL/BuiltIns/frexp-errors.hlsl create mode 100644 clang/test/SemaHLSL/BuiltIns/frexp_mat-errors.hlsl create mode 100644 llvm/test/CodeGen/DirectX/LongVector/frexp.ll create mode 100644 llvm/test/CodeGen/DirectX/frexp.ll create mode 100644 llvm/test/CodeGen/DirectX/frexp_error.ll create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/frexp.ll create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frexp.ll diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 60feac55f9968b..744aed31df23e4 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5927,6 +5927,12 @@ def HLSLBufferUpdateCounter : LangBuiltin<"HLSL_LANG"> { let Prototype = "uint32_t(__hlsl_resource_t, int)"; } +def HLSLFrexp : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_elementwise_frexp"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def HLSLSplitDouble: LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_elementwise_splitdouble"]; let Attributes = [NoThrow, Const]; diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index fc422918499ed2..211fd919ab6433 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -119,6 +119,32 @@ static Value *handleHlslClip(const CallExpr *E, CodeGenFunction *CGF) { return LastInstr; } +static Value *handleHlslFrexp(const CallExpr *E, CodeGenFunction *CGF) { + Value *X = CGF->EmitScalarExpr(E->getArg(0)); + + // Calls through the frexp overloads wrap the exponent in an HLSLOutArgExpr; + // direct calls to the builtin pass a plain lvalue. + CallArgList Args; + LValue ExpLValue; + if (const auto *OutArg = dyn_cast<HLSLOutArgExpr>(E->getArg(1))) + ExpLValue = CGF->EmitHLSLOutArgExpr(OutArg, Args, OutArg->getType()); + else + ExpLValue = CGF->EmitLValue(E->getArg(1)); + + // llvm.frexp returns {mantissa, integer exponent}; HLSL wants the exponent + // as a float of the same shape as X. + llvm::Type *ExpIntTy = X->getType()->getWithNewType(CGF->Int32Ty); + Value *Frexp = CGF->Builder.CreateIntrinsic( + Intrinsic::frexp, {X->getType(), ExpIntTy}, {X}, nullptr, "hlsl.frexp"); + Value *Mantissa = CGF->Builder.CreateExtractValue(Frexp, 0); + Value *Exp = CGF->Builder.CreateSIToFP( + CGF->Builder.CreateExtractValue(Frexp, 1), X->getType()); + + CGF->EmitStoreThroughLValue(RValue::get(Exp), ExpLValue); + CGF->EmitWritebacks(Args); + return Mantissa; +} + static Value *handleHlslSplitdouble(const CallExpr *E, CodeGenFunction *CGF) { Value *Op0 = CGF->EmitScalarExpr(E->getArg(0)); const auto *OutArg1 = dyn_cast<HLSLOutArgExpr>(E->getArg(1)); @@ -1780,6 +1806,11 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, CGM.getHLSLRuntime().getBufferUpdateCounterIntrinsic(), ArrayRef<Value *>{ResHandle, OffsetI8}, nullptr); } + case Builtin::BI__builtin_hlsl_elementwise_frexp: + assert(E->getArg(0)->getType()->hasFloatingRepresentation() && + E->getArg(1)->getType()->hasFloatingRepresentation() && + "frexp operands types mismatch"); + return handleHlslFrexp(E, this); case Builtin::BI__builtin_hlsl_elementwise_splitdouble: { assert((E->getArg(0)->getType()->hasFloatingRepresentation() && diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index a86fc7dc0988bb..94cdd5632f2538 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -220,6 +220,36 @@ firstbithigh(vector<T, N> X) { return __detail::firstbithigh_impl<vector<uint, N>, vector<T, N>, 64>(X); } +//===----------------------------------------------------------------------===// +// frexp builtins +//===----------------------------------------------------------------------===// + +/// \fn T frexp(T X, out T Exp) +/// \brief Returns the mantissa and exponent of the specified floating-point +/// value. +/// \param X The specified floating-point value. +/// \param Exp The returned exponent of \a X. +/// +/// The mantissa has the same sign as \a X and an absolute value in [0.5, 1), +/// such that X = mantissa * 2^Exp. If \a X is 0, both the mantissa and the +/// exponent are 0. +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_frexp) +float frexp(float, out float); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_frexp) +float2 frexp(float2, out float2); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_frexp) +float3 frexp(float3, out float3); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_frexp) +float4 frexp(float4, out float4); +template <int N> +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_frexp) +vector<__detail::enable_if_t<(N > 4), float>, N> frexp( + vector<__detail::enable_if_t<(N > 4), float>, N>, + out vector<__detail::enable_if_t<(N > 4), float>, N>); +template <int R, int C> +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_frexp) +matrix<float, R, C> frexp(matrix<float, R, C>, out matrix<float, R, C>); + //===----------------------------------------------------------------------===// // mul builtins //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index e4e66235bc0152..5404ece31a3d7a 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -5377,6 +5377,23 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { TheCall->setType(ArgTyExpr); break; } + case Builtin::BI__builtin_hlsl_elementwise_frexp: { + if (SemaRef.checkArgCount(TheCall, 2)) + return true; + + if (CheckScalarOrVectorOrMatrix(&SemaRef, TheCall, SemaRef.Context.FloatTy, + 0) || + CheckScalarOrVectorOrMatrix(&SemaRef, TheCall, SemaRef.Context.FloatTy, + 1) || + CheckAllArgsHaveSameType(&SemaRef, TheCall)) + return true; + + if (CheckModifiableLValue(&SemaRef, TheCall, 1)) + return true; + + TheCall->setType(TheCall->getArg(0)->getType()); + break; + } case Builtin::BI__builtin_hlsl_elementwise_splitdouble: { if (SemaRef.checkArgCount(TheCall, 3)) return true; diff --git a/clang/test/CodeGenHLSL/builtins/frexp.hlsl b/clang/test/CodeGenHLSL/builtins/frexp.hlsl new file mode 100644 index 00000000000000..fee76c7a827efa --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/frexp.hlsl @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \ +// RUN: -o - | FileCheck %s --check-prefixes=CHECK,DXCHECK +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: spirv-unknown-vulkan-library %s -emit-llvm -disable-llvm-passes \ +// RUN: -o - | FileCheck %s --check-prefixes=CHECK,SPVCHECK + +// DXCHECK: define hidden noundef nofpclass(nan inf) float @_Z12test_frexp_ffRf( +// SPVCHECK: define hidden spir_func noundef nofpclass(nan inf) float @_Z12test_frexp_ffRf( +// CHECK: [[X:%.*]] = load float, ptr %{{.*}} +// CHECK: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]]) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { float, i32 } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { float, i32 } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}i32 [[EXPI]] to float +// CHECK-NEXT: store float [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK-NEXT: [[WB:%.*]] = load float, ptr [[TMP]], align 4 +// CHECK-NEXT: store float [[WB]], ptr %{{.*}}, align 4 +// CHECK: ret float [[MANT]] +float test_frexp_f(float X, out float E) { return frexp(X, E); } + +// DXCHECK: define hidden noundef nofpclass(nan inf) <2 x float> @_Z13test_frexp_f2Dv2_fRS_( +// SPVCHECK: define hidden spir_func noundef nofpclass(nan inf) <2 x float> @_Z13test_frexp_f2Dv2_fRS_( +// CHECK: [[FREXP:%.*]] = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<2 x i32> [[EXPI]] to <2 x float> +// CHECK-NEXT: store <2 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <2 x float> [[MANT]] +float2 test_frexp_f2(float2 X, out float2 E) { return frexp(X, E); } + +// DXCHECK: define hidden noundef nofpclass(nan inf) <3 x float> @_Z13test_frexp_f3Dv3_fRS_( +// SPVCHECK: define hidden spir_func noundef nofpclass(nan inf) <3 x float> @_Z13test_frexp_f3Dv3_fRS_( +// CHECK: [[FREXP:%.*]] = call { <3 x float>, <3 x i32> } @llvm.frexp.v3f32.v3i32(<3 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <3 x float>, <3 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <3 x float>, <3 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<3 x i32> [[EXPI]] to <3 x float> +// CHECK-NEXT: store <3 x float> [[EXPF]], ptr [[TMP:%.*]] +// CHECK: ret <3 x float> [[MANT]] +float3 test_frexp_f3(float3 X, out float3 E) { return frexp(X, E); } + +// DXCHECK: define hidden noundef nofpclass(nan inf) <4 x float> @_Z13test_frexp_f4Dv4_fRS_( +// SPVCHECK: define hidden spir_func noundef nofpclass(nan inf) <4 x float> @_Z13test_frexp_f4Dv4_fRS_( +// CHECK: [[FREXP:%.*]] = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<4 x i32> [[EXPI]] to <4 x float> +// CHECK-NEXT: store <4 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <4 x float> [[MANT]] +float4 test_frexp_f4(float4 X, out float4 E) { return frexp(X, E); } + +// DXCHECK: define hidden noundef nofpclass(nan inf) <5 x float> @_Z13test_frexp_f5Dv5_fRS_( +// SPVCHECK: define hidden spir_func noundef nofpclass(nan inf) <5 x float> @_Z13test_frexp_f5Dv5_fRS_( +// CHECK: [[FREXP:%.*]] = call { <5 x float>, <5 x i32> } @llvm.frexp.v5f32.v5i32(<5 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <5 x float>, <5 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <5 x float>, <5 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<5 x i32> [[EXPI]] to <5 x float> +// CHECK-NEXT: store <5 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <5 x float> [[MANT]] +vector<float, 5> test_frexp_f5(vector<float, 5> X, out vector<float, 5> E) { + return frexp(X, E); +} + +// Direct calls to the builtin store the exponent straight to the lvalue. +// CHECK-LABEL: test_frexp_builtin +// CHECK: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { float, i32 } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { float, i32 } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}i32 [[EXPI]] to float +// CHECK-NEXT: store float [[EXPF]], ptr [[E:%.*]], align 4 +// CHECK-NEXT: store float [[MANT]], ptr [[M:%.*]], align 4 +// CHECK-NEXT: [[MLOAD:%.*]] = load float, ptr [[M]], align 4 +// CHECK-NEXT: [[ELOAD:%.*]] = load float, ptr [[E]], align 4 +// CHECK-NEXT: fadd {{.*}}float [[MLOAD]], [[ELOAD]] +float test_frexp_builtin(float X) { + float E; + float M = __builtin_hlsl_elementwise_frexp(X, E); + return M + E; +} diff --git a/clang/test/CodeGenHLSL/builtins/frexp_mat.hlsl b/clang/test/CodeGenHLSL/builtins/frexp_mat.hlsl new file mode 100644 index 00000000000000..9f989b6dc1e2f2 --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/frexp_mat.hlsl @@ -0,0 +1,155 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \ +// RUN: -o - | FileCheck %s --check-prefixes=CHECK,DXCHECK +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: spirv-unknown-vulkan-library %s -emit-llvm -disable-llvm-passes \ +// RUN: -o - | FileCheck %s --check-prefixes=CHECK,SPVCHECK + +// DXCHECK: define hidden [[FN_TYPE:]]noundef nofpclass(nan inf) <2 x float> @{{.*}}test_frexp_f1x2{{.*}}( +// SPVCHECK: define hidden [[FN_TYPE:spir_func ]]noundef nofpclass(nan inf) <2 x float> @{{.*}}test_frexp_f1x2{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<2 x i32> [[EXPI]] to <2 x float> +// CHECK-NEXT: store <2 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <2 x float> [[MANT]] +float1x2 test_frexp_f1x2(float1x2 X, out float1x2 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <3 x float> @{{.*}}test_frexp_f1x3{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <3 x float>, <3 x i32> } @llvm.frexp.v3f32.v3i32(<3 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <3 x float>, <3 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <3 x float>, <3 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<3 x i32> [[EXPI]] to <3 x float> +// CHECK-NEXT: store <3 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <3 x float> [[MANT]] +float1x3 test_frexp_f1x3(float1x3 X, out float1x3 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <4 x float> @{{.*}}test_frexp_f1x4{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<4 x i32> [[EXPI]] to <4 x float> +// CHECK-NEXT: store <4 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <4 x float> [[MANT]] +float1x4 test_frexp_f1x4(float1x4 X, out float1x4 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <2 x float> @{{.*}}test_frexp_f2x1{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<2 x i32> [[EXPI]] to <2 x float> +// CHECK-NEXT: store <2 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <2 x float> [[MANT]] +float2x1 test_frexp_f2x1(float2x1 X, out float2x1 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <4 x float> @{{.*}}test_frexp_f2x2{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<4 x i32> [[EXPI]] to <4 x float> +// CHECK-NEXT: store <4 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <4 x float> [[MANT]] +float2x2 test_frexp_f2x2(float2x2 X, out float2x2 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <6 x float> @{{.*}}test_frexp_f2x3{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <6 x float>, <6 x i32> } @llvm.frexp.v6f32.v6i32(<6 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <6 x float>, <6 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <6 x float>, <6 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<6 x i32> [[EXPI]] to <6 x float> +// CHECK-NEXT: store <6 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <6 x float> [[MANT]] +float2x3 test_frexp_f2x3(float2x3 X, out float2x3 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <8 x float> @{{.*}}test_frexp_f2x4{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <8 x float>, <8 x i32> } @llvm.frexp.v8f32.v8i32(<8 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <8 x float>, <8 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <8 x float>, <8 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<8 x i32> [[EXPI]] to <8 x float> +// CHECK-NEXT: store <8 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <8 x float> [[MANT]] +float2x4 test_frexp_f2x4(float2x4 X, out float2x4 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <3 x float> @{{.*}}test_frexp_f3x1{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <3 x float>, <3 x i32> } @llvm.frexp.v3f32.v3i32(<3 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <3 x float>, <3 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <3 x float>, <3 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<3 x i32> [[EXPI]] to <3 x float> +// CHECK-NEXT: store <3 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <3 x float> [[MANT]] +float3x1 test_frexp_f3x1(float3x1 X, out float3x1 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <6 x float> @{{.*}}test_frexp_f3x2{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <6 x float>, <6 x i32> } @llvm.frexp.v6f32.v6i32(<6 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <6 x float>, <6 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <6 x float>, <6 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<6 x i32> [[EXPI]] to <6 x float> +// CHECK-NEXT: store <6 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <6 x float> [[MANT]] +float3x2 test_frexp_f3x2(float3x2 X, out float3x2 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <9 x float> @{{.*}}test_frexp_f3x3{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <9 x float>, <9 x i32> } @llvm.frexp.v9f32.v9i32(<9 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <9 x float>, <9 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <9 x float>, <9 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<9 x i32> [[EXPI]] to <9 x float> +// CHECK-NEXT: store <9 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <9 x float> [[MANT]] +float3x3 test_frexp_f3x3(float3x3 X, out float3x3 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <12 x float> @{{.*}}test_frexp_f3x4{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <12 x float>, <12 x i32> } @llvm.frexp.v12f32.v12i32(<12 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <12 x float>, <12 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <12 x float>, <12 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<12 x i32> [[EXPI]] to <12 x float> +// CHECK-NEXT: store <12 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <12 x float> [[MANT]] +float3x4 test_frexp_f3x4(float3x4 X, out float3x4 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <4 x float> @{{.*}}test_frexp_f4x1{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <4 x float>, <4 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<4 x i32> [[EXPI]] to <4 x float> +// CHECK-NEXT: store <4 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <4 x float> [[MANT]] +float4x1 test_frexp_f4x1(float4x1 X, out float4x1 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <8 x float> @{{.*}}test_frexp_f4x2{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <8 x float>, <8 x i32> } @llvm.frexp.v8f32.v8i32(<8 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <8 x float>, <8 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <8 x float>, <8 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<8 x i32> [[EXPI]] to <8 x float> +// CHECK-NEXT: store <8 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <8 x float> [[MANT]] +float4x2 test_frexp_f4x2(float4x2 X, out float4x2 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <12 x float> @{{.*}}test_frexp_f4x3{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <12 x float>, <12 x i32> } @llvm.frexp.v12f32.v12i32(<12 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <12 x float>, <12 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <12 x float>, <12 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<12 x i32> [[EXPI]] to <12 x float> +// CHECK-NEXT: store <12 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <12 x float> [[MANT]] +float4x3 test_frexp_f4x3(float4x3 X, out float4x3 E) { return frexp(X, E); } + +// CHECK: define hidden [[FN_TYPE]]noundef nofpclass(nan inf) <16 x float> @{{.*}}test_frexp_f4x4{{.*}}( +// CHECK: [[FREXP:%.*]] = call { <16 x float>, <16 x i32> } @llvm.frexp.v16f32.v16i32(<16 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <16 x float>, <16 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <16 x float>, <16 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<16 x i32> [[EXPI]] to <16 x float> +// CHECK-NEXT: store <16 x float> [[EXPF]], ptr [[TMP:%.*]], align 4 +// CHECK: ret <16 x float> [[MANT]] +float4x4 test_frexp_f4x4(float4x4 X, out float4x4 E) { return frexp(X, E); } + +// Direct calls to the builtin store the exponent straight to the lvalue. +// CHECK-LABEL: test_frexp_builtin_f3x3 +// CHECK: [[FREXP:%.*]] = call { <9 x float>, <9 x i32> } @llvm.frexp.v9f32.v9i32(<9 x float> %{{.*}}) +// CHECK-NEXT: [[MANT:%.*]] = extractvalue { <9 x float>, <9 x i32> } [[FREXP]], 0 +// CHECK-NEXT: [[EXPI:%.*]] = extractvalue { <9 x float>, <9 x i32> } [[FREXP]], 1 +// CHECK-NEXT: [[EXPF:%.*]] = sitofp {{.*}}<9 x i32> [[EXPI]] to <9 x float> +// CHECK-NEXT: store <9 x float> [[EXPF]], ptr [[E:%.*]], align 4 +// CHECK: ret <9 x float> [[MANT]] +float3x3 test_frexp_builtin_f3x3(float3x3 X) { + float3x3 E; + return __builtin_hlsl_elementwise_frexp(X, E); +} diff --git a/clang/test/SemaHLSL/BuiltIns/frexp-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/frexp-errors.hlsl new file mode 100644 index 00000000000000..be0f75d21643f7 --- /dev/null +++ b/clang/test/SemaHLSL/BuiltIns/frexp-errors.hlsl @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -fnative-int16-type -verify +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -verify + +float test_no_second_arg(float X) { + return __builtin_hlsl_elementwise_frexp(X); + // expected-error@-1 {{too few arguments to function call, expected 2, have 1}} +} + +float test_too_many_arg(float X) { + float E, F; + return __builtin_hlsl_elementwise_frexp(X, E, F); + // expected-error@-1 {{too many arguments to function call, expected 2, have 3}} +} + +float test_first_arg_double(double X) { + float E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'double' where 'float' or a vector or matrix of such type is required}} +} + +float test_first_arg_int(int X) { + float E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'int' where 'float' or a vector or matrix of such type is required}} +} + +float test_first_arg_bool(bool X) { + float E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'bool' where 'float' or a vector or matrix of such type is required}} +} + +float test_second_arg_int(float X) { + int E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'int' where 'float' or a vector or matrix of such type is required}} +} + +float2 test_shape_mismatch(float2 X) { + float3 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{all arguments to '__builtin_hlsl_elementwise_frexp' must have the same type}} +} + +float test_second_arg_rvalue(float X) { + return __builtin_hlsl_elementwise_frexp(X, 1.0f); + // expected-error@-1 {{cannot bind non-lvalue argument '1.F' to out parameter}} +} + +float test_second_arg_const(float X) { + const float E = 0; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{cannot bind non-lvalue argument 'E' to out parameter}} +} + +float test_overload_rvalue(float X) { + return frexp(X, 1.0f); + // expected-error@-1 {{cannot bind non-lvalue argument '1.F' to out parameter}} +} + +half test_builtin_half(half X) { + half E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'half' where 'float' or a vector or matrix of such type is required}} +} diff --git a/clang/test/SemaHLSL/BuiltIns/frexp_mat-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/frexp_mat-errors.hlsl new file mode 100644 index 00000000000000..3b6a013ff7c03b --- /dev/null +++ b/clang/test/SemaHLSL/BuiltIns/frexp_mat-errors.hlsl @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -fnative-int16-type -emit-llvm-only -disable-llvm-passes -verify + +float2x2 test_builtin_int_matrix(int2x2 X) { + float2x2 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'int2x2' (aka 'matrix<int, 2, 2>') where 'float' or a vector or matrix of such type is required}} +} + +float2x2 test_builtin_bool_matrix(bool2x2 X) { + float2x2 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'bool2x2' (aka 'matrix<bool, 2, 2>') where 'float' or a vector or matrix of such type is required}} +} + +float2x2 test_builtin_double_matrix(double2x2 X) { + float2x2 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'double2x2' (aka 'matrix<double, 2, 2>') where 'float' or a vector or matrix of such type is required}} +} + +half2x2 test_builtin_half_matrix(half2x2 X) { + half2x2 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'half2x2' (aka 'matrix<half, 2, 2>') where 'float' or a vector or matrix of such type is required}} +} + +float2x2 test_builtin_exp_int_matrix(float2x2 X) { + int2x2 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{invalid operand of type 'int2x2' (aka 'matrix<int, 2, 2>') where 'float' or a vector or matrix of such type is required}} +} + +float2x2 test_builtin_dim_mismatch(float2x2 X) { + float2x3 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{all arguments to '__builtin_hlsl_elementwise_frexp' must have the same type}} +} + +float2x2 test_builtin_matrix_vector_mismatch(float2x2 X) { + float4 E; + return __builtin_hlsl_elementwise_frexp(X, E); + // expected-error@-1 {{all arguments to '__builtin_hlsl_elementwise_frexp' must have the same type}} +} + +float2x2 test_builtin_exp_not_lvalue(float2x2 X) { + return __builtin_hlsl_elementwise_frexp(X, X * 2); + // expected-error@-1 {{cannot bind non-lvalue argument 'X * 2' to out parameter}} +} + +float2x2 test_overload_exp_not_lvalue(float2x2 X) { + return frexp(X, X * 2); + // expected-error@-1 {{cannot bind non-lvalue argument 'X * 2' to out parameter}} +} diff --git a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp index e7aac17b47cb79..1b2e321b5a3eaa 100644 --- a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp +++ b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp @@ -212,6 +212,7 @@ static bool isIntrinsicExpansion(Function &F) { case Intrinsic::abs: case Intrinsic::atan2: case Intrinsic::copysign: + case Intrinsic::frexp: case Intrinsic::fshl: case Intrinsic::fshr: case Intrinsic::exp: @@ -1104,6 +1105,49 @@ static Value *expandCopySignIntrinsic(CallInst *Orig) { return Builder.CreateBitCast(CombinedInt, Ty); } +// Expand llvm.frexp for 32-bit floats by splitting the IEEE bit pattern. The +// mantissa keeps the sign of X and has its exponent field set to that of 0.5, +// so it lies in [0.5, 1). Zeros, denormals, infinities and NaNs return X as the +// mantissa with an exponent of 0. +static Value *expandFrexpIntrinsic(CallInst *Orig) { + Value *X = Orig->getArgOperand(0); + Type *Ty = X->getType(); + auto *RetTy = cast<StructType>(Orig->getType()); + Type *ExpTy = RetTy->getElementType(1); + + if (!Ty->getScalarType()->isFloatTy()) + reportFatalUsageError("frexp is only supported for 32-bit floats in DXIL"); + + IRBuilder<> Builder(Orig); + Type *IntTy = Ty->getWithNewType(Builder.getInt32Ty()); + + // `ConstantInt::get` broadcasts to a splat when `IntTy` is a vector. + Constant *ExpMask = ConstantInt::get(IntTy, 0x7f800000); + Constant *MantSignMask = ConstantInt::get(IntTy, 0x807fffff); + Constant *HalfExpBits = ConstantInt::get(IntTy, 0x3f000000); + Constant *ExpShift = ConstantInt::get(IntTy, 23); + Constant *ExpBias = ConstantInt::get(IntTy, 126); + Constant *Zero = ConstantInt::get(IntTy, 0); + + Value *Bits = Builder.CreateBitCast(X, IntTy); + Value *ExpBits = Builder.CreateAnd(Bits, ExpMask); + Value *IsSpecial = Builder.CreateOr(Builder.CreateICmpEQ(ExpBits, Zero), + Builder.CreateICmpEQ(ExpBits, ExpMask)); + + Value *Mant = + Builder.CreateOr(Builder.CreateAnd(Bits, MantSignMask), HalfExpBits); + Mant = Builder.CreateSelect(IsSpecial, X, Builder.CreateBitCast(Mant, Ty)); + + Value *Exp = + Builder.CreateSub(Builder.CreateLShr(ExpBits, ExpShift), ExpBias); + Exp = Builder.CreateSelect(IsSpecial, Zero, Exp); + Exp = Builder.CreateSExtOrTrunc(Exp, ExpTy); + + Value *Agg = PoisonValue::get(RetTy); + Agg = Builder.CreateInsertValue(Agg, Mant, 0); + return Builder.CreateInsertValue(Agg, Exp, 1); +} + // Expand llvm.matrix.multiply by extracting row/column vectors and computing // dot products. // Result[r,c] = dot(row_r(LHS), col_c(RHS)) @@ -1310,6 +1354,9 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) { case Intrinsic::exp: Result = expandExpIntrinsic(Orig); break; + case Intrinsic::frexp: + Result = expandFrexpIntrinsic(Orig); + break; case Intrinsic::is_fpclass: Result = expandIsFPClass(Orig); break; diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index f1745d48559266..bcadc6a67febfa 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -1623,15 +1623,25 @@ bool SPIRVInstructionSelector::selectFrexp(Register ResVReg, continue; MachineIRBuilder MIRBuilder(I); - SPIRVTypeInst PointeeTy = GR.getSPIRVTypeForVReg(I.getOperand(1).getReg()); + Register ExpResReg = I.getOperand(1).getReg(); + SPIRVTypeInst PointeeTy = GR.getSPIRVTypeForVReg(ExpResReg); + if (!PointeeTy) { + // An unused exponent result may have no SPIR-V type assigned. Both + // extended instruction sets require a 32-bit integer exponent with the + // same number of components as the input. + PointeeTy = GR.getOrCreateSPIRVIntegerType(32, I, TII); + unsigned NumComponents = + GR.getScalarOrVectorComponentCount(I.getOperand(2).getReg()); + if (NumComponents > 1) + PointeeTy = + GR.getOrCreateSPIRVVectorType(PointeeTy, NumComponents, I, TII); + } const SPIRVTypeInst PointerType = GR.getOrCreateSPIRVPointerType( PointeeTy, MIRBuilder, SPIRV::StorageClass::Function); Register PointerVReg = createVirtualRegister(PointerType, &GR, MRI, MRI->getMF()); auto It = getOpVariableMBBIt(*I.getMF()); - // An untyped pointer result type is only legal on OpUntypedVariableKHR, - // but not on OpVariable. const bool IsUntyped = PointerType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR; auto VarMIB = @@ -1642,7 +1652,7 @@ bool SPIRVInstructionSelector::selectFrexp(Register ResVReg, .addUse(GR.getSPIRVTypeID(PointerType)) .addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function)); if (IsUntyped) - VarMIB.addUse(GR.getSPIRVTypeID(PointeeTy)); // Data Type + VarMIB.addUse(GR.getSPIRVTypeID(PointeeTy)); VarMIB.constrainAllUses(TII, TRI, RBI); SPIRVTypeInst MantissaTy = GR.getSPIRVTypeForVReg(I.getOperand(2).getReg()); @@ -1655,7 +1665,6 @@ bool SPIRVInstructionSelector::selectFrexp(Register ResVReg, .addUse(PointerVReg) .constrainAllUses(TII, TRI, RBI); - Register ExpResReg = I.getOperand(1).getReg(); if (!MRI->use_nodbg_empty(ExpResReg)) BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad)) .addDef(ExpResReg) @@ -1664,7 +1673,7 @@ bool SPIRVInstructionSelector::selectFrexp(Register ResVReg, .constrainAllUses(TII, TRI, RBI); return true; } - return false; + llvm_unreachable("no extended instruction set available for frexp"); } bool SPIRVInstructionSelector::selectLdexp(Register ResVReg, diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp index 99cf8654827696..018d08020591d6 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -572,8 +572,14 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { getActionDefinitionsBuilder(G_BR).alwaysLegal(); getActionDefinitionsBuilder(G_BRCOND).legalFor({s1, s32}); - getActionDefinitionsBuilder(G_FFREXP).legalForCartesianProduct( - allFloatScalarsAndVectors, {s32, v2s32, v3s32, v4s32, v8s32, v16s32}); + getActionDefinitionsBuilder(G_FFREXP) + .legalForCartesianProduct(allFloatScalarsAndVectors, + {s32, v2s32, v3s32, v4s32, v8s32, v16s32}) + .fewerElementsIf(vectorElementCountIsGreaterThan(0, MaxVectorSize), + LegalizeMutations::changeElementCountTo( + 0, ElementCount::getFixed(MaxVectorSize))) + // LegalizerHelper::moreElementsVector does not support G_FFREXP. + .scalarizeIf(numElementsNotPow2(0), 0); // TODO: Review the target OpenCL and GLSL Extended Instruction Set specs to // tighten these requirements. Many of these math functions are only legal on diff --git a/llvm/test/CodeGen/DirectX/LongVector/frexp.ll b/llvm/test/CodeGen/DirectX/LongVector/frexp.ll new file mode 100644 index 00000000000000..7639fe7b4ed731 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/LongVector/frexp.ll @@ -0,0 +1,17 @@ +; RUN: llc -mtriple=dxil-pc-shadermodel6.8-library -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SCALAR +; RUN: llc -mtriple=dxil-pc-shadermodel6.9-library -stop-before=dxil-op-lower -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-VECTOR + +; llvm.frexp is expanded into integer operations on the float bit pattern. + +; CHECK-LABEL: define <17 x float> @test_frexp( +; CHECK-SCALAR-NOT: @llvm.frexp +; CHECK-SCALAR-COUNT-17: and i32 {{.*}}, 2139095040 +; CHECK-VECTOR-NOT: @llvm.frexp +; CHECK-VECTOR: bitcast <17 x float> %a to <17 x i32> +; CHECK-VECTOR: and <17 x i32> {{.*}}, splat (i32 2139095040) +; CHECK-VECTOR: lshr <17 x i32> {{.*}}, splat (i32 23) +define <17 x float> @test_frexp(<17 x float> %a) { + %r = call { <17 x float>, <17 x i32> } @llvm.frexp.v17f32.v17i32(<17 x float> %a) + %m = extractvalue { <17 x float>, <17 x i32> } %r, 0 + ret <17 x float> %m +} diff --git a/llvm/test/CodeGen/DirectX/frexp.ll b/llvm/test/CodeGen/DirectX/frexp.ll new file mode 100644 index 00000000000000..29fba05deff903 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/frexp.ll @@ -0,0 +1,52 @@ +; RUN: opt -S -dxil-intrinsic-expansion -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefix=EXPCHECK +; RUN: opt -S -dxil-intrinsic-expansion -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefix=DOPCHECK + +; Make sure llvm.frexp is expanded into bit operations for DXIL. + +define noundef float @frexp_float(float noundef %a, ptr %e) { +entry: +; EXPCHECK-LABEL: define noundef float @frexp_float( +; EXPCHECK: [[BITS:%.*]] = bitcast float %a to i32 +; EXPCHECK-NEXT: [[EXPBITS:%.*]] = and i32 [[BITS]], 2139095040 +; EXPCHECK-NEXT: [[ISINFNAN:%.*]] = icmp eq i32 [[EXPBITS]], 2139095040 +; EXPCHECK-NEXT: [[ISZERO:%.*]] = icmp eq i32 [[EXPBITS]], 0 +; EXPCHECK-NEXT: [[SPECIAL:%.*]] = or i1 [[ISZERO]], [[ISINFNAN]] +; EXPCHECK-NEXT: [[SIGNMANT:%.*]] = and i32 [[BITS]], -2139095041 +; EXPCHECK-NEXT: [[MANTBITS:%.*]] = or i32 [[SIGNMANT]], 1056964608 +; EXPCHECK-NEXT: [[MANTF:%.*]] = bitcast i32 [[MANTBITS]] to float +; EXPCHECK-NEXT: [[MANT:%.*]] = select i1 [[SPECIAL]], float %a, float [[MANTF]] +; EXPCHECK-NEXT: [[SHR:%.*]] = lshr i32 [[EXPBITS]], 23 +; EXPCHECK-NEXT: [[UNBIASED:%.*]] = sub i32 [[SHR]], 126 +; EXPCHECK-NEXT: [[EXP:%.*]] = select i1 [[SPECIAL]], i32 0, i32 [[UNBIASED]] +; EXPCHECK-NEXT: [[AGG0:%.*]] = insertvalue { float, i32 } poison, float [[MANT]], 0 +; EXPCHECK-NEXT: [[AGG1:%.*]] = insertvalue { float, i32 } [[AGG0]], i32 [[EXP]], 1 +; EXPCHECK-NOT: call {{.*}} @llvm.frexp +; DOPCHECK-LABEL: define noundef float @frexp_float( +; DOPCHECK-NOT: @llvm.frexp + %r = call { float, i32 } @llvm.frexp.f32.i32(float %a) + %m = extractvalue { float, i32 } %r, 0 + %x = extractvalue { float, i32 } %r, 1 + %xf = sitofp i32 %x to float + store float %xf, ptr %e + ret float %m +} + +define noundef <4 x float> @frexp_float4(<4 x float> noundef %a, ptr %e) { +entry: +; EXPCHECK-LABEL: define noundef <4 x float> @frexp_float4( +; EXPCHECK: [[BITS:%.*]] = bitcast <4 x float> %a to <4 x i32> +; EXPCHECK-NEXT: [[EXPBITS:%.*]] = and <4 x i32> [[BITS]], splat (i32 2139095040) +; EXPCHECK: [[MANT:%.*]] = select <4 x i1> [[SPECIAL:%.*]], <4 x float> %a, <4 x float> {{%.*}} +; EXPCHECK: [[EXP:%.*]] = select <4 x i1> [[SPECIAL]], <4 x i32> zeroinitializer, <4 x i32> {{%.*}} +; EXPCHECK-NEXT: [[AGG0:%.*]] = insertvalue { <4 x float>, <4 x i32> } poison, <4 x float> [[MANT]], 0 +; EXPCHECK-NEXT: [[AGG1:%.*]] = insertvalue { <4 x float>, <4 x i32> } [[AGG0]], <4 x i32> [[EXP]], 1 +; EXPCHECK-NOT: call {{.*}} @llvm.frexp +; DOPCHECK-LABEL: define noundef <4 x float> @frexp_float4( +; DOPCHECK-NOT: @llvm.frexp + %r = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %a) + %m = extractvalue { <4 x float>, <4 x i32> } %r, 0 + %x = extractvalue { <4 x float>, <4 x i32> } %r, 1 + %xf = sitofp <4 x i32> %x to <4 x float> + store <4 x float> %xf, ptr %e + ret <4 x float> %m +} diff --git a/llvm/test/CodeGen/DirectX/frexp_error.ll b/llvm/test/CodeGen/DirectX/frexp_error.ll new file mode 100644 index 00000000000000..6d571153f61b7c --- /dev/null +++ b/llvm/test/CodeGen/DirectX/frexp_error.ll @@ -0,0 +1,11 @@ +; RUN: not opt -S -dxil-intrinsic-expansion -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s + +; DXIL only supports frexp on 32-bit floats. +; CHECK: LLVM ERROR: frexp is only supported for 32-bit floats in DXIL + +define noundef half @frexp_half(half noundef %a) { +entry: + %r = call { half, i32 } @llvm.frexp.f16.i32(half %a) + %m = extractvalue { half, i32 } %r, 0 + ret half %m +} diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/frexp.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/frexp.ll new file mode 100644 index 00000000000000..9abe4932ba1425 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/frexp.ll @@ -0,0 +1,40 @@ +; RUN: llc -verify-machineinstrs -O0 --spirv-ext=+SPV_EXT_long_vector -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 --spirv-ext=+SPV_EXT_long_vector -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK-DAG: %[[#F32:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#V8F32:]] = OpTypeVector %[[#F32]] 8 +; CHECK-DAG: %[[#V16F32:]] = OpTypeVector %[[#F32]] 16 + +; Power-of-two vectors up to <16 x float> are legal as is. +; CHECK-LABEL: Begin function frexp_v8f32 +; CHECK: OpExtInst %[[#V8F32]] %[[#]] frexp +; CHECK-NOT: OpExtInst +; CHECK: OpFunctionEnd +define <8 x float> @frexp_v8f32(<8 x float> %a) { + %r = call { <8 x float>, <8 x i32> } @llvm.frexp.v8f32.v8i32(<8 x float> %a) + %m = extractvalue { <8 x float>, <8 x i32> } %r, 0 + ret <8 x float> %m +} + +; Vectors with a non-power-of-two element count are scalarized. +; CHECK-LABEL: Begin function frexp_v5f32 +; CHECK-COUNT-5: OpExtInst %[[#F32]] %[[#]] frexp +; CHECK-NOT: OpExtInst +; CHECK: OpFunctionEnd +define <5 x float> @frexp_v5f32(<5 x float> %a) { + %r = call { <5 x float>, <5 x i32> } @llvm.frexp.v5f32.v5i32(<5 x float> %a) + %m = extractvalue { <5 x float>, <5 x i32> } %r, 0 + ret <5 x float> %m +} + +; Vectors larger than <16 x float> are split. +; CHECK-LABEL: Begin function frexp_v17f32 +; CHECK: OpExtInst %[[#V16F32]] %[[#]] frexp +; CHECK: OpExtInst %[[#F32]] %[[#]] frexp +; CHECK-NOT: OpExtInst +; CHECK: OpFunctionEnd +define <17 x float> @frexp_v17f32(<17 x float> %a) { + %r = call { <17 x float>, <17 x i32> } @llvm.frexp.v17f32.v17i32(<17 x float> %a) + %m = extractvalue { <17 x float>, <17 x i32> } %r, 0 + ret <17 x float> %m +} diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frexp.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frexp.ll new file mode 100644 index 00000000000000..b10c917a8888bd --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frexp.ll @@ -0,0 +1,61 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan-library %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-library %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %} + +; Make sure llvm.frexp is lowered to GLSL.std.450 Frexp for Vulkan. + +; CHECK-DAG: %[[#glsl:]] = OpExtInstImport "GLSL.std.450" +; CHECK-DAG: %[[#float:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#int:]] = OpTypeInt 32 0 +; CHECK-DAG: %[[#vec4_float:]] = OpTypeVector %[[#float]] 4 +; CHECK-DAG: %[[#vec4_int:]] = OpTypeVector %[[#int]] 4 +; CHECK-DAG: %[[#ptr_int:]] = OpTypePointer Function %[[#int]] +; CHECK-DAG: %[[#ptr_vec4_int:]] = OpTypePointer Function %[[#vec4_int]] + +define noundef float @frexp_float(float noundef %a) { +entry: + ; CHECK: %[[#]] = OpFunction %[[#float]] None %[[#]] + ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float]] + ; CHECK: %[[#var:]] = OpVariable %[[#ptr_int]] Function + ; CHECK: %[[#mant:]] = OpExtInst %[[#float]] %[[#glsl]] Frexp %[[#arg0]] %[[#var]] + ; CHECK: %[[#exp:]] = OpLoad %[[#int]] %[[#var]] + ; CHECK: %[[#expf:]] = OpConvertSToF %[[#float]] %[[#exp]] + ; CHECK: %[[#]] = OpFAdd %[[#float]] %[[#mant]] %[[#expf]] + %r = call { float, i32 } @llvm.frexp.f32.i32(float %a) + %m = extractvalue { float, i32 } %r, 0 + %x = extractvalue { float, i32 } %r, 1 + %xf = sitofp i32 %x to float + %s = fadd float %m, %xf + ret float %s +} + +define noundef <4 x float> @frexp_float4(<4 x float> noundef %a) { +entry: + ; CHECK: %[[#]] = OpFunction %[[#vec4_float]] None %[[#]] + ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float]] + ; CHECK: %[[#var:]] = OpVariable %[[#ptr_vec4_int]] Function + ; CHECK: %[[#mant:]] = OpExtInst %[[#vec4_float]] %[[#glsl]] Frexp %[[#arg0]] %[[#var]] + ; CHECK: %[[#exp:]] = OpLoad %[[#vec4_int]] %[[#var]] + ; CHECK: %[[#expf:]] = OpConvertSToF %[[#vec4_float]] %[[#exp]] + ; CHECK: %[[#]] = OpFAdd %[[#vec4_float]] %[[#mant]] %[[#expf]] + %r = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %a) + %m = extractvalue { <4 x float>, <4 x i32> } %r, 0 + %x = extractvalue { <4 x float>, <4 x i32> } %r, 1 + %xf = sitofp <4 x i32> %x to <4 x float> + %s = fadd <4 x float> %m, %xf + ret <4 x float> %s +} + +; The exponent result is never extracted, e.g. when an HLSL out argument is +; dead, so its register has no SPIR-V type. +define noundef <4 x float> @frexp_exp_not_extracted(<4 x float> noundef %a) { +entry: + ; CHECK: %[[#]] = OpFunction %[[#vec4_float]] None %[[#]] + ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float]] + ; CHECK: %[[#var:]] = OpVariable %[[#ptr_vec4_int]] Function + ; CHECK: %[[#mant:]] = OpExtInst %[[#vec4_float]] %[[#glsl]] Frexp %[[#arg0]] %[[#var]] + ; CHECK-NOT: OpLoad %[[#vec4_int]] %[[#var]] + ; CHECK: OpReturnValue %[[#mant]] + %r = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %a) + %m = extractvalue { <4 x float>, <4 x i32> } %r, 0 + ret <4 x float> %m +} diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll index 8b02dbd32bc63c..4c5dc59975f1cd 100644 --- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll +++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll @@ -187,3 +187,15 @@ define void @frexp_f64(ptr %out, double %x) { store i32 %exp, ptr %exp.ptr ret void } + +; The exponent result is never extracted, so its register has no SPIR-V type. +; CHECK: %[[#x_var5:]] = OpFunctionParameter %[[#vec4_float_type]] +; CHECK: %[[#var11:]] = OpVariable %[[#fn_ptr_type_vec4_i32]] Function +; CHECK: %[[#extinst11:]] = OpExtInst %[[#vec4_float_type]] %[[#extinst_id]] frexp %[[#x_var5]] %[[#var11]] +; CHECK-NOT: OpLoad %[[#vec4_int_type]] %[[#var11]] +; CHECK: OpReturnValue %[[#extinst11]] +define <4 x float> @frexp_exp_not_extracted(<4 x float> %x) { + %ret = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> %x) + %mantissa = extractvalue { <4 x float>, <4 x i32> } %ret, 0 + ret <4 x float> %mantissa +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
