[clang] [llvm] [HLSL] Implement isfinite() intrinsic (PR #225947)
Dan Brown via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 25 10:55:45 PDT 2026
https://github.com/danbrown-amd updated https://github.com/llvm/llvm-project/pull/225947
>From daaa5fc7c10c50e891f013abd998af8173a92861 Mon Sep 17 00:00:00 2001
From: Dan Brown <danbrown at amd.com>
Date: Thu, 21 May 2026 16:04:07 -0600
Subject: [PATCH] Implements isfinite() HLSL intrinsic.
Resolves #99131
Assisted-by: Claude Sonnet 4
---
clang/include/clang/Basic/Builtins.td | 6 ++
clang/include/clang/Basic/HLSLIntrinsics.td | 15 +++++
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 19 ++++++
clang/lib/CodeGen/CGHLSLRuntime.h | 1 +
.../lib/Headers/hlsl/hlsl_compat_overloads.h | 13 ++++
clang/lib/Sema/SemaHLSL.cpp | 1 +
.../builtins/isfinite-overloads.hlsl | 37 +++++++++++
clang/test/CodeGenHLSL/builtins/isfinite.hlsl | 62 +++++++++++++++++++
.../SemaHLSL/BuiltIns/isfinite-errors.hlsl | 27 ++++++++
llvm/include/llvm/IR/IntrinsicsDirectX.td | 2 +
llvm/lib/Target/DirectX/DXIL.td | 1 +
.../Target/DirectX/DXILIntrinsicExpansion.cpp | 4 ++
.../DirectX/DirectXTargetTransformInfo.cpp | 1 +
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 33 +++++++++-
.../CodeGen/DirectX/LongVector/isfinite.ll | 11 ++++
llvm/test/CodeGen/DirectX/isfinite.ll | 62 +++++++++++++++++++
llvm/test/CodeGen/DirectX/isfinite_error.ll | 14 +++++
.../SPIRV/hlsl-intrinsics/OpIsFinite.ll | 20 ++++--
18 files changed, 322 insertions(+), 7 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/builtins/isfinite-overloads.hlsl
create mode 100644 clang/test/CodeGenHLSL/builtins/isfinite.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/isfinite-errors.hlsl
create mode 100644 llvm/test/CodeGen/DirectX/LongVector/isfinite.ll
create mode 100644 llvm/test/CodeGen/DirectX/isfinite.ll
create mode 100644 llvm/test/CodeGen/DirectX/isfinite_error.ll
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 28178c0370b27..4d6bf60813e0e 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5756,6 +5756,12 @@ def HLSLFrac : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLIsfinite : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_elementwise_isfinite"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(...)";
+}
+
def HLSLIsinf : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_elementwise_isinf"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
diff --git a/clang/include/clang/Basic/HLSLIntrinsics.td b/clang/include/clang/Basic/HLSLIntrinsics.td
index 10adf4fb676ee..6369def55f2c7 100644
--- a/clang/include/clang/Basic/HLSLIntrinsics.td
+++ b/clang/include/clang/Basic/HLSLIntrinsics.td
@@ -1085,6 +1085,21 @@ call.
let IsConvergent = 1;
}
+// Determines if the specified value x is finite.
+def hlsl_isfinite : HLSLOneArgBuiltin<"isfinite", "__builtin_hlsl_elementwise_isfinite"> {
+ let Doc = [{
+\fn T isinf(T x)
+\brief Determines if the specified value \a x is finite.
+\param x The specified input value.
+
+Returns a value of the same size as the input, with a value set
+to False if the x parameter is +INF, -INF, NaN, or QNaN. Otherwise, True.
+}];
+ let ReturnType = VaryingShape<BoolTy>;
+ let VaryingTypes = [HalfTy, FloatTy];
+ let VaryingLongVector = 1;
+}
+
// Determines if the specified value x is infinite.
def hlsl_isinf : HLSLOneArgBuiltin<"isinf", "__builtin_hlsl_elementwise_isinf"> {
let Doc = [{
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 26220e3a732bf..57742a75fac5d 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1225,6 +1225,25 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
/*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getFracIntrinsic(),
ArrayRef<Value *>{Op0}, nullptr, "hlsl.frac");
}
+ case Builtin::BI__builtin_hlsl_elementwise_isfinite: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ llvm::Type *Xty = Op0->getType();
+ llvm::Type *retType = llvm::Type::getInt1Ty(this->getLLVMContext());
+ if (Xty->isVectorTy()) {
+ unsigned NumElts;
+ if (auto *MatTy = E->getArg(0)->getType()->getAs<ConstantMatrixType>())
+ NumElts = MatTy->getNumRows() * MatTy->getNumColumns();
+ else
+ NumElts =
+ E->getArg(0)->getType()->castAs<VectorType>()->getNumElements();
+ retType = llvm::VectorType::get(retType, ElementCount::getFixed(NumElts));
+ }
+ if (!E->getArg(0)->getType()->hasFloatingRepresentation())
+ llvm_unreachable("isfinite operand must have a float representation");
+ return Builder.CreateIntrinsic(
+ retType, CGM.getHLSLRuntime().getIsFiniteIntrinsic(),
+ ArrayRef<Value *>{Op0}, nullptr, "hlsl.isfinite");
+ }
case Builtin::BI__builtin_hlsl_elementwise_isinf: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
if (!E->getArg(0)->getType()->hasFloatingRepresentation())
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c44fb102598b0..d876d76d877d2 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -127,6 +127,7 @@ class CGHLSLRuntime {
GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac)
GENERATE_HLSL_INTRINSIC_FUNCTION(FlattenedThreadIdInGroup,
flattened_thread_id_in_group)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(IsFinite, isfinite)
GENERATE_HLSL_INTRINSIC_FUNCTION(IsInf, isinf)
GENERATE_HLSL_INTRINSIC_FUNCTION(IsNaN, isnan)
GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
diff --git a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
index 2c0c7677be36f..fc0f3c2d069f8 100644
--- a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
+++ b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
@@ -378,6 +378,19 @@ _DXC_COMPAT_UNARY_INTEGER_OVERLOADS(floor)
_DXC_COMPAT_UNARY_DOUBLE_OVERLOADS(frac)
_DXC_COMPAT_UNARY_INTEGER_OVERLOADS(frac)
+//===----------------------------------------------------------------------===//
+// isfinite builtins overloads
+//===----------------------------------------------------------------------===//
+
+_DXC_DEPRECATED_64BIT_FN(isfinite)
+constexpr bool isfinite(double V) { return isfinite((float)V); }
+_DXC_DEPRECATED_64BIT_FN(isfinite)
+constexpr bool2 isfinite(double2 V) { return isfinite((float2)V); }
+_DXC_DEPRECATED_64BIT_FN(isfinite)
+constexpr bool3 isfinite(double3 V) { return isfinite((float3)V); }
+_DXC_DEPRECATED_64BIT_FN(isfinite)
+constexpr bool4 isfinite(double4 V) { return isfinite((float4)V); }
+
//===----------------------------------------------------------------------===//
// isinf builtins overloads
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 124f5bd23a623..006f71a7533b6 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4460,6 +4460,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
+ case Builtin::BI__builtin_hlsl_elementwise_isfinite:
case Builtin::BI__builtin_hlsl_elementwise_isinf:
case Builtin::BI__builtin_hlsl_elementwise_isnan: {
if (SemaRef.checkArgCount(TheCall, 1))
diff --git a/clang/test/CodeGenHLSL/builtins/isfinite-overloads.hlsl b/clang/test/CodeGenHLSL/builtins/isfinite-overloads.hlsl
new file mode 100644
index 0000000000000..d84c94d8c739c
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/isfinite-overloads.hlsl
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
+// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm \
+// RUN: -Wdeprecated-declarations -o - | FileCheck %s --check-prefixes=CHECK \
+// RUN: -DFNATTRS="hidden noundef" -DTARGET=dx
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
+// RUN: spirv-unknown-vulkan-library %s -emit-llvm \
+// RUN: -Wdeprecated-declarations -o - | FileCheck %s --check-prefixes=CHECK \
+// RUN: -DFNATTRS="hidden spir_func noundef" -DTARGET=spv
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s \
+// RUN: -verify -verify-ignore-unexpected=note
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple spirv-unknown-vulkan-library %s \
+// RUN: -verify -verify-ignore-unexpected=note
+
+// CHECK: define [[FNATTRS]] i1 @_Z20test_isfinite_doubled(
+// CHECK: [[CONVI:%.*]] = fptrunc {{.*}} double %{{.*}} to float
+// CHECK: [[HLSLISFINITEI:%.*]] = call noundef i1 @llvm.[[TARGET]].isfinite.f32(float [[CONVI]])
+// CHECK: ret i1 [[HLSLISFINITEI]]
+// expected-warning at +1 {{'isfinite' is deprecated: In 202x 64 bit API lowering for isfinite is deprecated. Explicitly cast parameters to 32 or 16 bit types.}}
+bool test_isfinite_double(double p0) { return isfinite(p0); }
+// CHECK: define [[FNATTRS]] <2 x i1> @_Z21test_isfinite_double2Dv2_d(
+// CHECK: [[CONVI:%.*]] = fptrunc {{.*}} <2 x double> %{{.*}} to <2 x float>
+// CHECK: [[HLSLISFINITEI:%.*]] = call noundef <2 x i1> @llvm.[[TARGET]].isfinite.v2f32(<2 x float> [[CONVI]])
+// CHECK: ret <2 x i1> [[HLSLISFINITEI]]
+// expected-warning at +1 {{'isfinite' is deprecated: In 202x 64 bit API lowering for isfinite is deprecated. Explicitly cast parameters to 32 or 16 bit types.}}
+bool2 test_isfinite_double2(double2 p0) { return isfinite(p0); }
+// CHECK: define [[FNATTRS]] <3 x i1> @_Z21test_isfinite_double3Dv3_d(
+// CHECK: [[CONVI:%.*]] = fptrunc {{.*}} <3 x double> %{{.*}} to <3 x float>
+// CHECK: [[HLSLISFINITEI:%.*]] = call noundef <3 x i1> @llvm.[[TARGET]].isfinite.v3f32(<3 x float> [[CONVI]])
+// CHECK: ret <3 x i1> [[HLSLISFINITEI]]
+// expected-warning at +1 {{'isfinite' is deprecated: In 202x 64 bit API lowering for isfinite is deprecated. Explicitly cast parameters to 32 or 16 bit types.}}
+bool3 test_isfinite_double3(double3 p0) { return isfinite(p0); }
+// CHECK: define [[FNATTRS]] <4 x i1> @_Z21test_isfinite_double4Dv4_d(
+// CHECK: [[CONVI:%.*]] = fptrunc {{.*}} <4 x double> %{{.*}} to <4 x float>
+// CHECK: [[HLSLISFINITEI:%.*]] = call noundef <4 x i1> @llvm.[[TARGET]].isfinite.v4f32(<4 x float> [[CONVI]])
+// CHECK: ret <4 x i1> [[HLSLISFINITEI]]
+// expected-warning at +1 {{'isfinite' is deprecated: In 202x 64 bit API lowering for isfinite is deprecated. Explicitly cast parameters to 32 or 16 bit types.}}
+bool4 test_isfinite_double4(double4 p0) { return isfinite(p0); }
diff --git a/clang/test/CodeGenHLSL/builtins/isfinite.hlsl b/clang/test/CodeGenHLSL/builtins/isfinite.hlsl
new file mode 100644
index 0000000000000..d05534a5df7bb
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/isfinite.hlsl
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type -fnative-int16-type \
+// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN: --check-prefixes=CHECK,DXCHECK,NATIVE_HALF
+// 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,NO_HALF
+
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN: spirv-unknown-vulkan-library %s -fnative-half-type -fnative-int16-type \
+// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN: --check-prefixes=CHECK,SPVCHECK,NATIVE_HALF
+// 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,NO_HALF
+
+// DXCHECK: define hidden [[FN_TYPE:]]noundef i1 @
+// SPVCHECK: define hidden [[FN_TYPE:spir_func ]]noundef i1 @
+// DXCHECK: %hlsl.isfinite = call i1 @llvm.[[ICF:dx]].isfinite.f32(
+// SPVCHECK: %hlsl.isfinite = call i1 @llvm.[[ICF:spv]].isfinite.f32(
+// CHECK: ret i1 %hlsl.isfinite
+bool test_isfinite_float(float p0) { return isfinite(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef i1 @
+// NATIVE_HALF: %hlsl.isfinite = call i1 @llvm.[[ICF]].isfinite.f16(
+// NO_HALF: %hlsl.isfinite = call i1 @llvm.[[ICF]].isfinite.f32(
+// CHECK: ret i1 %hlsl.isfinite
+bool test_isfinite_half(half p0) { return isfinite(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef <2 x i1> @
+// NATIVE_HALF: %hlsl.isfinite = call <2 x i1> @llvm.[[ICF]].isfinite.v2f16
+// NO_HALF: %hlsl.isfinite = call <2 x i1> @llvm.[[ICF]].isfinite.v2f32(
+// CHECK: ret <2 x i1> %hlsl.isfinite
+bool2 test_isfinite_half2(half2 p0) { return isfinite(p0); }
+
+// NATIVE_HALF: define hidden [[FN_TYPE]]noundef <3 x i1> @
+// NATIVE_HALF: %hlsl.isfinite = call <3 x i1> @llvm.[[ICF]].isfinite.v3f16
+// NO_HALF: %hlsl.isfinite = call <3 x i1> @llvm.[[ICF]].isfinite.v3f32(
+// CHECK: ret <3 x i1> %hlsl.isfinite
+bool3 test_isfinite_half3(half3 p0) { return isfinite(p0); }
+
+// NATIVE_HALF: define hidden [[FN_TYPE]]noundef <4 x i1> @
+// NATIVE_HALF: %hlsl.isfinite = call <4 x i1> @llvm.[[ICF]].isfinite.v4f16
+// NO_HALF: %hlsl.isfinite = call <4 x i1> @llvm.[[ICF]].isfinite.v4f32(
+// CHECK: ret <4 x i1> %hlsl.isfinite
+bool4 test_isfinite_half4(half4 p0) { return isfinite(p0); }
+
+
+// CHECK: define hidden [[FN_TYPE]]noundef <2 x i1> @
+// CHECK: %hlsl.isfinite = call <2 x i1> @llvm.[[ICF]].isfinite.v2f32
+// CHECK: ret <2 x i1> %hlsl.isfinite
+bool2 test_isfinite_float2(float2 p0) { return isfinite(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef <3 x i1> @
+// CHECK: %hlsl.isfinite = call <3 x i1> @llvm.[[ICF]].isfinite.v3f32
+// CHECK: ret <3 x i1> %hlsl.isfinite
+bool3 test_isfinite_float3(float3 p0) { return isfinite(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef <4 x i1> @
+// CHECK: %hlsl.isfinite = call <4 x i1> @llvm.[[ICF]].isfinite.v4f32
+// CHECK: ret <4 x i1> %hlsl.isfinite
+bool4 test_isfinite_float4(float4 p0) { return isfinite(p0); }
diff --git a/clang/test/SemaHLSL/BuiltIns/isfinite-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/isfinite-errors.hlsl
new file mode 100644
index 0000000000000..03892c50fa65d
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/isfinite-errors.hlsl
@@ -0,0 +1,27 @@
+
+// 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
+
+bool test_too_few_arg() {
+ return __builtin_hlsl_elementwise_isfinite();
+ // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+bool2 test_too_many_arg(float2 p0) {
+ return __builtin_hlsl_elementwise_isfinite(p0, p0);
+ // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+bool builtin_bool_to_float_type_promotion(bool p1) {
+ return __builtin_hlsl_elementwise_isfinite(p1);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'bool')}}
+}
+
+bool builtin_isfinite_int_to_float_promotion(int p1) {
+ return __builtin_hlsl_elementwise_isfinite(p1);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'int')}}
+}
+
+bool2 builtin_isfinite_int2_to_float2_promotion(int2 p1) {
+ return __builtin_hlsl_elementwise_isfinite(p1);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'int2' (aka 'vector<int, 2>'))}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 2d29165e27368..a8d58408e5cce 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -268,6 +268,8 @@ def int_dx_dot4add_u8packed : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty,
def int_dx_frac : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem, IntrTriviallyScalarizable]>;
+def int_dx_isfinite : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
+ [llvm_anyfloat_ty], [IntrNoMem, IntrTriviallyScalarizable]>;
def int_dx_isinf : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
[llvm_anyfloat_ty], [IntrNoMem, IntrTriviallyScalarizable]>;
def int_dx_isnan : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index cde3a2ca8db31..e237967ea93a4 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -476,6 +476,7 @@ def IsInf : DXILOp<9, isSpecialFloat> {
def IsFinite : DXILOp<10, isSpecialFloat> {
let Doc = "Determines if the specified value is finite.";
+ let intrinsics = [IntrinSelect<int_dx_isfinite>];
let arguments = [OverloadTy];
let result = Int1Ty;
let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy]>];
diff --git a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
index dce8a1ec9f92a..e7aac17b47cb7 100644
--- a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
+++ b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
@@ -225,6 +225,7 @@ static bool isIntrinsicExpansion(Function &F) {
case Intrinsic::dx_uclamp:
case Intrinsic::dx_sclamp:
case Intrinsic::dx_nclamp:
+ case Intrinsic::dx_isfinite:
case Intrinsic::dx_isinf:
case Intrinsic::dx_isnan:
case Intrinsic::dx_sdot:
@@ -1331,6 +1332,9 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) {
case Intrinsic::dx_nclamp:
Result = expandClampIntrinsic(Orig, IntrinsicId);
break;
+ case Intrinsic::dx_isfinite:
+ Result = expand16BitIsFinite(Orig);
+ break;
case Intrinsic::dx_isinf:
Result = expand16BitIsInf(Orig);
break;
diff --git a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
index af1d7bc452126..6c65fb7b9e0fc 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
@@ -32,6 +32,7 @@ bool DirectXTTIImpl::isTargetIntrinsicWithOverloadTypeAtArg(Intrinsic::ID ID,
case Intrinsic::dx_firstbitlow:
case Intrinsic::dx_firstbitshigh:
case Intrinsic::dx_firstbituhigh:
+ case Intrinsic::dx_isfinite:
case Intrinsic::dx_isinf:
case Intrinsic::dx_isnan:
case Intrinsic::dx_legacyf16tof32:
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 562344ac52b58..2750c7145cddb 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3433,11 +3433,38 @@ bool SPIRVInstructionSelector::selectOpIsNan(Register ResVReg,
bool SPIRVInstructionSelector::selectOpIsFinite(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I) const {
+ // OpIsFinite requires Kernel capability; emulate with OpIsInf & OpIsNan
MachineBasicBlock &BB = *I.getParent();
- BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpIsFinite))
+ Register Src = I.getOperand(2).getReg();
+ Register TypeID = GR.getSPIRVTypeID(ResType);
+ const DebugLoc &DL = I.getDebugLoc();
+
+ Register IsInfReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+ BuildMI(BB, I, DL, TII.get(SPIRV::OpIsInf))
+ .addDef(IsInfReg)
+ .addUse(TypeID)
+ .addUse(Src)
+ .constrainAllUses(TII, TRI, RBI);
+
+ Register IsNanReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+ BuildMI(BB, I, DL, TII.get(SPIRV::OpIsNan))
+ .addDef(IsNanReg)
+ .addUse(TypeID)
+ .addUse(Src)
+ .constrainAllUses(TII, TRI, RBI);
+
+ Register OrReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+ BuildMI(BB, I, DL, TII.get(SPIRV::OpLogicalOr))
+ .addDef(OrReg)
+ .addUse(TypeID)
+ .addUse(IsInfReg)
+ .addUse(IsNanReg)
+ .constrainAllUses(TII, TRI, RBI);
+
+ BuildMI(BB, I, DL, TII.get(SPIRV::OpLogicalNot))
.addDef(ResVReg)
- .addUse(GR.getSPIRVTypeID(ResType))
- .addUse(I.getOperand(2).getReg())
+ .addUse(TypeID)
+ .addUse(OrReg)
.constrainAllUses(TII, TRI, RBI);
return true;
}
diff --git a/llvm/test/CodeGen/DirectX/LongVector/isfinite.ll b/llvm/test/CodeGen/DirectX/LongVector/isfinite.ll
new file mode 100644
index 0000000000000..1b1fa71a0e8c3
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/LongVector/isfinite.ll
@@ -0,0 +1,11 @@
+; 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
+
+; CHECK-LABEL: define <17 x i1> @test_isfinite(
+; CHECK-SCALAR-COUNT-17: call i1 @dx.op.isSpecialFloat.f32(i32 10, float {{.*}})
+; CHECK-VECTOR: call <17 x i1> @llvm.dx.isfinite.v17f32
+define <17 x i1> @test_isfinite(<17 x float> %a) {
+ %result = call <17 x i1> @llvm.dx.isfinite.v17f32(<17 x float> %a)
+ ret <17 x i1> %result
+}
+declare <17 x i1> @llvm.dx.isfinite.v17f32(<17 x float>)
diff --git a/llvm/test/CodeGen/DirectX/isfinite.ll b/llvm/test/CodeGen/DirectX/isfinite.ll
new file mode 100644
index 0000000000000..46c04d22f9f80
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/isfinite.ll
@@ -0,0 +1,62 @@
+; RUN: opt -S -dxil-intrinsic-expansion -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.9-library %s | FileCheck %s --check-prefixes=CHECK,SM69CHECK
+; RUN: opt -S -dxil-intrinsic-expansion -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.8-library %s | FileCheck %s --check-prefixes=CHECK,SMOLDCHECK
+
+; Make sure dxil operation function calls for isfinite are generated for float and half.
+
+define noundef i1 @isfinite_float(float noundef %a) {
+entry:
+ ; CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 10, float %{{.*}})
+ %dx.isfinite = call i1 @llvm.dx.isfinite.f32(float %a)
+ ret i1 %dx.isfinite
+}
+
+define noundef i1 @isfinite_half(half noundef %a) {
+entry:
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 10, half %{{.*}})
+ ; SMOLDCHECK: [[BITCAST:%.*]] = bitcast half %a to i16
+ ; SMOLDCHECK: [[AND:%.*]] = and i16 [[BITCAST]], 31744
+ ; SMOLDCHECK: [[CMP:%.*]] = icmp ne i16 [[AND]], 31744
+ %dx.isfinite = call i1 @llvm.dx.isfinite.f16(half %a)
+ ret i1 %dx.isfinite
+}
+
+define noundef <4 x i1> @isfinite_half4(<4 x half> noundef %p0) {
+entry:
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 10, half
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 10, half
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 10, half
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 10, half
+
+ ; SMOLDCHECK: [[ee0:%.*]] = extractelement <4 x half> %p0, i64 0
+ ; SMOLDCHECK: [[BITCAST0:%.*]] = bitcast half [[ee0]] to i16
+ ; SMOLDCHECK: [[ee1:%.*]] = extractelement <4 x half> %p0, i64 1
+ ; SMOLDCHECK: [[BITCAST1:%.*]] = bitcast half [[ee1]] to i16
+ ; SMOLDCHECK: [[ee2:%.*]] = extractelement <4 x half> %p0, i64 2
+ ; SMOLDCHECK: [[BITCAST2:%.*]] = bitcast half [[ee2]] to i16
+ ; SMOLDCHECK: [[ee3:%.*]] = extractelement <4 x half> %p0, i64 3
+ ; SMOLDCHECK: [[BITCAST3:%.*]] = bitcast half [[ee3]] to i16
+ ; SMOLDCHECK: [[AND0:%.*]] = and i16 [[BITCAST0]], 31744
+ ; SMOLDCHECK: [[AND1:%.*]] = and i16 [[BITCAST1]], 31744
+ ; SMOLDCHECK: [[AND2:%.*]] = and i16 [[BITCAST2]], 31744
+ ; SMOLDCHECK: [[AND3:%.*]] = and i16 [[BITCAST3]], 31744
+ ; SMOLDCHECK: [[CMP0:%.*]] = icmp ne i16 [[AND0]], 31744
+ ; SMOLDCHECK: [[CMP1:%.*]] = icmp ne i16 [[AND1]], 31744
+ ; SMOLDCHECK: [[CMP2:%.*]] = icmp ne i16 [[AND2]], 31744
+ ; SMOLDCHECK: [[CMP3:%.*]] = icmp ne i16 [[AND3]], 31744
+
+ %hlsl.isfinite = call <4 x i1> @llvm.dx.isfinite.v4f16(<4 x half> %p0)
+ ret <4 x i1> %hlsl.isfinite
+}
+
+define noundef <3 x i1> @isfinite_float3(<3 x float> noundef %p0) {
+entry:
+ ; CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 10, float
+ ; CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 10, float
+ ; CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 10, float
+ %hlsl.isfinite = call <3 x i1> @llvm.dx.isfinite.v3f32(<3 x float> %p0)
+ ret <3 x i1> %hlsl.isfinite
+}
+
+; CHECK-DAG: declare i1 @dx.op.isSpecialFloat.f32(i32, float) #[[#ATTR0:]]
+; SM69CHECK-DAG: declare i1 @dx.op.isSpecialFloat.f16(i32, half) #[[#ATTR0]]
+; CHECK: attributes #[[#ATTR0]] = { nounwind memory(none) }
diff --git a/llvm/test/CodeGen/DirectX/isfinite_error.ll b/llvm/test/CodeGen/DirectX/isfinite_error.ll
new file mode 100644
index 0000000000000..03dd513f99ac0
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/isfinite_error.ll
@@ -0,0 +1,14 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; DXIL operation isfinite does not support double overload type
+; CHECK: in function isfinite_double
+; CHECK-SAME: Cannot create IsFinite operation: Invalid overload type
+
+define noundef i1 @isfinite_double(double noundef %a) #0 {
+entry:
+ %a.addr = alloca double, align 8
+ store double %a, ptr %a.addr, align 8
+ %0 = load double, ptr %a.addr, align 8
+ %dx.isfinite = call i1 @llvm.dx.isfinite.f64(double %0)
+ ret i1 %dx.isfinite
+}
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/OpIsFinite.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/OpIsFinite.ll
index 0455e715a349d..4829558aa3e3b 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/OpIsFinite.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/OpIsFinite.ll
@@ -12,7 +12,10 @@ define noundef i1 @isfinite_half(half noundef %a) {
entry:
; CHECK: %[[#]] = OpFunction %[[#bool]] None %[[#]]
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_16]]
- ; CHECK: %[[#]] = OpIsFinite %[[#bool]] %[[#arg0]]
+ ; CHECK: %[[#isinf:]] = OpIsInf %[[#bool]] %[[#arg0]]
+ ; CHECK: %[[#isnan:]] = OpIsNan %[[#bool]] %[[#arg0]]
+ ; CHECK: %[[#or:]] = OpLogicalOr %[[#bool]] %[[#isinf]] %[[#isnan]]
+ ; CHECK: %[[#]] = OpLogicalNot %[[#bool]] %[[#or]]
%hlsl.isfinite = call i1 @llvm.spv.isfinite.f16(half %a)
ret i1 %hlsl.isfinite
}
@@ -21,7 +24,10 @@ define noundef i1 @isfinite_float(float noundef %a) {
entry:
; CHECK: %[[#]] = OpFunction %[[#bool]] None %[[#]]
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_32]]
- ; CHECK: %[[#]] = OpIsFinite %[[#bool]] %[[#arg0]]
+ ; CHECK: %[[#isinf:]] = OpIsInf %[[#bool]] %[[#arg0]]
+ ; CHECK: %[[#isnan:]] = OpIsNan %[[#bool]] %[[#arg0]]
+ ; CHECK: %[[#or:]] = OpLogicalOr %[[#bool]] %[[#isinf]] %[[#isnan]]
+ ; CHECK: %[[#]] = OpLogicalNot %[[#bool]] %[[#or]]
%hlsl.isfinite = call i1 @llvm.spv.isfinite.f32(float %a)
ret i1 %hlsl.isfinite
}
@@ -30,7 +36,10 @@ define noundef <4 x i1> @isfinite_half4(<4 x half> noundef %a) {
entry:
; CHECK: %[[#]] = OpFunction %[[#vec4_bool]] None %[[#]]
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_16]]
- ; CHECK: %[[#]] = OpIsFinite %[[#vec4_bool]] %[[#arg0]]
+ ; CHECK: %[[#isinf:]] = OpIsInf %[[#vec4_bool]] %[[#arg0]]
+ ; CHECK: %[[#isnan:]] = OpIsNan %[[#vec4_bool]] %[[#arg0]]
+ ; CHECK: %[[#or:]] = OpLogicalOr %[[#vec4_bool]] %[[#isinf]] %[[#isnan]]
+ ; CHECK: %[[#]] = OpLogicalNot %[[#vec4_bool]] %[[#or]]
%hlsl.isfinite = call <4 x i1> @llvm.spv.isfinite.v4f16(<4 x half> %a)
ret <4 x i1> %hlsl.isfinite
}
@@ -39,7 +48,10 @@ define noundef <4 x i1> @isfinite_float4(<4 x float> noundef %a) {
entry:
; CHECK: %[[#]] = OpFunction %[[#vec4_bool]] None %[[#]]
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
- ; CHECK: %[[#]] = OpIsFinite %[[#vec4_bool]] %[[#arg0]]
+ ; CHECK: %[[#isinf:]] = OpIsInf %[[#vec4_bool]] %[[#arg0]]
+ ; CHECK: %[[#isnan:]] = OpIsNan %[[#vec4_bool]] %[[#arg0]]
+ ; CHECK: %[[#or:]] = OpLogicalOr %[[#vec4_bool]] %[[#isinf]] %[[#isnan]]
+ ; CHECK: %[[#]] = OpLogicalNot %[[#vec4_bool]] %[[#or]]
%hlsl.isfinite = call <4 x i1> @llvm.spv.isfinite.v4f32(<4 x float> %a)
ret <4 x i1> %hlsl.isfinite
}
More information about the llvm-commits
mailing list