[clang] df420ee - Implements isnan() HLSL intrinsic for DXIL and SPIR-V targets. (#157733)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 25 09:34:53 PDT 2025
Author: Dan Brown
Date: 2025-09-25T12:34:47-04:00
New Revision: df420ee2ba3ce58b07f7ae70b32ebd649dcb0a5a
URL: https://github.com/llvm/llvm-project/commit/df420ee2ba3ce58b07f7ae70b32ebd649dcb0a5a
DIFF: https://github.com/llvm/llvm-project/commit/df420ee2ba3ce58b07f7ae70b32ebd649dcb0a5a.diff
LOG: Implements isnan() HLSL intrinsic for DXIL and SPIR-V targets. (#157733)
Addresses #99132.
Added:
clang/test/CodeGenHLSL/builtins/isnan-overloads.hlsl
clang/test/CodeGenHLSL/builtins/isnan.hlsl
clang/test/SemaHLSL/BuiltIns/isnan-errors.hlsl
llvm/test/CodeGen/DirectX/isnan.ll
llvm/test/CodeGen/SPIRV/hlsl-intrinsics/isnan.ll
Modified:
clang/include/clang/Basic/Builtins.td
clang/lib/CodeGen/CGHLSLBuiltins.cpp
clang/lib/CodeGen/CGHLSLRuntime.h
clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
clang/lib/Headers/hlsl/hlsl_compat_overloads.h
clang/lib/Sema/SemaHLSL.cpp
llvm/include/llvm/IR/IntrinsicsDirectX.td
llvm/include/llvm/IR/IntrinsicsSPIRV.td
llvm/lib/Target/DirectX/DXIL.td
llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 9bc70ea5e5858..468121f7d20ab 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5101,6 +5101,12 @@ def HLSLIsinf : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLIsnan : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_elementwise_isnan"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(...)";
+}
+
def HLSLLerp : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_lerp"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 9f87afa5a8a3d..6c0fc8d7f07be 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -547,6 +547,21 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
retType, CGM.getHLSLRuntime().getIsInfIntrinsic(),
ArrayRef<Value *>{Op0}, nullptr, "hlsl.isinf");
}
+ case Builtin::BI__builtin_hlsl_elementwise_isnan: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ llvm::Type *Xty = Op0->getType();
+ llvm::Type *retType = llvm::Type::getInt1Ty(this->getLLVMContext());
+ if (Xty->isVectorTy()) {
+ auto *XVecTy = E->getArg(0)->getType()->castAs<VectorType>();
+ retType = llvm::VectorType::get(
+ retType, ElementCount::getFixed(XVecTy->getNumElements()));
+ }
+ if (!E->getArg(0)->getType()->hasFloatingRepresentation())
+ llvm_unreachable("isnan operand must have a float representation");
+ return Builder.CreateIntrinsic(
+ retType, CGM.getHLSLRuntime().getIsNaNIntrinsic(),
+ ArrayRef<Value *>{Op0}, nullptr, "hlsl.isnan");
+ }
case Builtin::BI__builtin_hlsl_mad: {
Value *M = EmitScalarExpr(E->getArg(0));
Value *A = EmitScalarExpr(E->getArg(1));
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index f4b410664d60c..9c0e6056fd4ee 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -95,6 +95,7 @@ class CGHLSLRuntime {
GENERATE_HLSL_INTRINSIC_FUNCTION(FlattenedThreadIdInGroup,
flattened_thread_id_in_group)
GENERATE_HLSL_INTRINSIC_FUNCTION(IsInf, isinf)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(IsNaN, isnan)
GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
GENERATE_HLSL_INTRINSIC_FUNCTION(Normalize, normalize)
GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
diff --git a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
index cd1ffc8c23298..d973371312701 100644
--- a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
@@ -1292,6 +1292,39 @@ bool3 isinf(float3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isinf)
bool4 isinf(float4);
+//===----------------------------------------------------------------------===//
+// isnan builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn T isnan(T x)
+/// \brief Determines if the specified value \a x is Not a Number.
+/// \param x The specified input value.
+///
+/// Returns a value of the same size as the input, with a value set
+/// to True if the x parameter is NaN or QNaN. Otherwise, False.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool isnan(half);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool2 isnan(half2);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool3 isnan(half3);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool4 isnan(half4);
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool isnan(float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool2 isnan(float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool3 isnan(float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_isnan)
+bool4 isnan(float4);
+
//===----------------------------------------------------------------------===//
// lerp builtins
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
index 72a7bed21f3c9..fe4277ed4a7d2 100644
--- a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
+++ b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
@@ -352,6 +352,15 @@ constexpr bool3 isinf(double3 V) { return isinf((float3)V); }
_DXC_DEPRECATED_64BIT_FN(fn)
constexpr bool4 isinf(double4 V) { return isinf((float4)V); }
+//===----------------------------------------------------------------------===//
+// isnan builtins overloads
+//===----------------------------------------------------------------------===//
+
+constexpr bool isnan(double V) { return isnan((float)V); }
+constexpr bool2 isnan(double2 V) { return isnan((float2)V); }
+constexpr bool3 isnan(double3 V) { return isnan((float3)V); }
+constexpr bool4 isnan(double4 V) { return isnan((float4)V); }
+
//===----------------------------------------------------------------------===//
// lerp builtins overloads
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e17f9b9e8d758..940d510b4cc02 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3090,7 +3090,8 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
- case Builtin::BI__builtin_hlsl_elementwise_isinf: {
+ case Builtin::BI__builtin_hlsl_elementwise_isinf:
+ case Builtin::BI__builtin_hlsl_elementwise_isnan: {
if (SemaRef.checkArgCount(TheCall, 1))
return true;
if (CheckAllArgTypesAreCorrect(&SemaRef, TheCall,
diff --git a/clang/test/CodeGenHLSL/builtins/isnan-overloads.hlsl b/clang/test/CodeGenHLSL/builtins/isnan-overloads.hlsl
new file mode 100644
index 0000000000000..a0c3eee5da636
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/isnan-overloads.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
+// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
+// RUN: -o - | FileCheck %s
+
+// CHECK: define hidden noundef i1 @
+// CHECK: %hlsl.isnan = call i1 @llvm.dx.isnan.f32(
+// CHECK: ret i1 %hlsl.isnan
+bool test_isnan_double(double p0) { return isnan(p0); }
+// CHECK: define hidden noundef <2 x i1> @
+// CHECK: %hlsl.isnan = call <2 x i1> @llvm.dx.isnan.v2f32
+// CHECK: ret <2 x i1> %hlsl.isnan
+bool2 test_isnan_double2(double2 p0) { return isnan(p0); }
+// CHECK: define hidden noundef <3 x i1> @
+// CHECK: %hlsl.isnan = call <3 x i1> @llvm.dx.isnan.v3f32
+// CHECK: ret <3 x i1> %hlsl.isnan
+bool3 test_isnan_double3(double3 p0) { return isnan(p0); }
+// CHECK: define hidden noundef <4 x i1> @
+// CHECK: %hlsl.isnan = call <4 x i1> @llvm.dx.isnan.v4f32
+// CHECK: ret <4 x i1> %hlsl.isnan
+bool4 test_isnan_double4(double4 p0) { return isnan(p0); }
diff --git a/clang/test/CodeGenHLSL/builtins/isnan.hlsl b/clang/test/CodeGenHLSL/builtins/isnan.hlsl
new file mode 100644
index 0000000000000..ce7dbe1aedea4
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/isnan.hlsl
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-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-compute %s -fnative-half-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-compute %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.isnan = call i1 @llvm.[[ICF:dx]].isnan.f32(
+// SPVCHECK: %hlsl.isnan = call i1 @llvm.[[ICF:spv]].isnan.f32(
+// CHECK: ret i1 %hlsl.isnan
+bool test_isnan_float(float p0) { return isnan(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef i1 @
+// NATIVE_HALF: %hlsl.isnan = call i1 @llvm.[[ICF]].isnan.f16(
+// NO_HALF: %hlsl.isnan = call i1 @llvm.[[ICF]].isnan.f32(
+// CHECK: ret i1 %hlsl.isnan
+bool test_isnan_half(half p0) { return isnan(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef <2 x i1> @
+// NATIVE_HALF: %hlsl.isnan = call <2 x i1> @llvm.[[ICF]].isnan.v2f16
+// NO_HALF: %hlsl.isnan = call <2 x i1> @llvm.[[ICF]].isnan.v2f32(
+// CHECK: ret <2 x i1> %hlsl.isnan
+bool2 test_isnan_half2(half2 p0) { return isnan(p0); }
+
+// NATIVE_HALF: define hidden [[FN_TYPE]]noundef <3 x i1> @
+// NATIVE_HALF: %hlsl.isnan = call <3 x i1> @llvm.[[ICF]].isnan.v3f16
+// NO_HALF: %hlsl.isnan = call <3 x i1> @llvm.[[ICF]].isnan.v3f32(
+// CHECK: ret <3 x i1> %hlsl.isnan
+bool3 test_isnan_half3(half3 p0) { return isnan(p0); }
+
+// NATIVE_HALF: define hidden [[FN_TYPE]]noundef <4 x i1> @
+// NATIVE_HALF: %hlsl.isnan = call <4 x i1> @llvm.[[ICF]].isnan.v4f16
+// NO_HALF: %hlsl.isnan = call <4 x i1> @llvm.[[ICF]].isnan.v4f32(
+// CHECK: ret <4 x i1> %hlsl.isnan
+bool4 test_isnan_half4(half4 p0) { return isnan(p0); }
+
+
+// CHECK: define hidden [[FN_TYPE]]noundef <2 x i1> @
+// CHECK: %hlsl.isnan = call <2 x i1> @llvm.[[ICF]].isnan.v2f32
+// CHECK: ret <2 x i1> %hlsl.isnan
+bool2 test_isnan_float2(float2 p0) { return isnan(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef <3 x i1> @
+// CHECK: %hlsl.isnan = call <3 x i1> @llvm.[[ICF]].isnan.v3f32
+// CHECK: ret <3 x i1> %hlsl.isnan
+bool3 test_isnan_float3(float3 p0) { return isnan(p0); }
+
+// CHECK: define hidden [[FN_TYPE]]noundef <4 x i1> @
+// CHECK: %hlsl.isnan = call <4 x i1> @llvm.[[ICF]].isnan.v4f32
+// CHECK: ret <4 x i1> %hlsl.isnan
+bool4 test_isnan_float4(float4 p0) { return isnan(p0); }
diff --git a/clang/test/SemaHLSL/BuiltIns/isnan-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/isnan-errors.hlsl
new file mode 100644
index 0000000000000..a6be28117af4f
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/isnan-errors.hlsl
@@ -0,0 +1,38 @@
+
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm-only -disable-llvm-passes -verify
+
+bool test_too_few_arg() {
+ return __builtin_hlsl_elementwise_isnan();
+ // 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_isnan(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_isnan(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_isnan_int_to_float_promotion(int p1) {
+ return __builtin_hlsl_elementwise_isnan(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_isnan_int2_to_float2_promotion(int2 p1) {
+ return __builtin_hlsl_elementwise_isnan(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>'))}}
+}
+
+// builtins are variadic functions and so are subject to DefaultVariadicArgumentPromotion
+half builtin_isnan_half_scalar (half p0) {
+ return __builtin_hlsl_elementwise_isnan (p0);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'double')}}
+}
+
+float builtin_isnan_float_scalar ( float p0) {
+ return __builtin_hlsl_elementwise_isnan (p0);
+ // expected-error at -1 {{1st argument must be a scalar or vector of 16 or 32 bit floating-point types (was 'double')}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index d27d42841e012..570d6bc35cbd0 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -134,6 +134,8 @@ def int_dx_degrees : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty
def int_dx_isinf : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
[llvm_anyfloat_ty], [IntrNoMem]>;
+def int_dx_isnan : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
+ [llvm_anyfloat_ty], [IntrNoMem]>;
def int_dx_lerp : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
[IntrNoMem]>;
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index b89fa87bf77b9..823c491e1bfee 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -87,6 +87,8 @@ let TargetPrefix = "spv" in {
def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
def int_spv_isinf : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
[llvm_anyfloat_ty], [IntrNoMem]>;
+ def int_spv_isnan : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
+ [llvm_anyfloat_ty], [IntrNoMem]>;
def int_spv_lerp : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
[IntrNoMem] >;
def int_spv_length : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index c65ead45e2c7e..228114c5c24b2 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -424,6 +424,7 @@ def Saturate : DXILOp<7, unary> {
def IsNaN : DXILOp<8, isSpecialFloat> {
let Doc = "Determines if the specified value is NaN.";
+ let intrinsics = [IntrinSelect<int_dx_isnan>];
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 e2469d8df957f..ebb7c2607c0c8 100644
--- a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
+++ b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
@@ -213,6 +213,7 @@ static bool isIntrinsicExpansion(Function &F) {
case Intrinsic::dx_nclamp:
case Intrinsic::dx_degrees:
case Intrinsic::dx_isinf:
+ case Intrinsic::dx_isnan:
case Intrinsic::dx_lerp:
case Intrinsic::dx_normalize:
case Intrinsic::dx_fdot:
@@ -1024,6 +1025,9 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) {
case Intrinsic::dx_isinf:
Result = expand16BitIsInf(Orig);
break;
+ case Intrinsic::dx_isnan:
+ Result = expand16BitIsNaN(Orig);
+ break;
case Intrinsic::dx_lerp:
Result = expandLerpIntrinsic(Orig);
break;
diff --git a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
index 5153d24070dc9..68fd3e0bc74c7 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
@@ -30,6 +30,7 @@ bool DirectXTTIImpl::isTargetIntrinsicWithOverloadTypeAtArg(Intrinsic::ID ID,
switch (ID) {
case Intrinsic::dx_asdouble:
case Intrinsic::dx_isinf:
+ case Intrinsic::dx_isnan:
case Intrinsic::dx_firstbitlow:
case Intrinsic::dx_firstbituhigh:
case Intrinsic::dx_firstbitshigh:
@@ -48,6 +49,7 @@ bool DirectXTTIImpl::isTargetIntrinsicTriviallyScalarizable(
case Intrinsic::dx_firstbituhigh:
case Intrinsic::dx_frac:
case Intrinsic::dx_isinf:
+ case Intrinsic::dx_isnan:
case Intrinsic::dx_rsqrt:
case Intrinsic::dx_saturate:
case Intrinsic::dx_splitdouble:
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 50ccca01c31c7..a7b2179a312e1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -207,6 +207,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
bool selectOpIsInf(Register ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
+ bool selectOpIsNan(Register ResVReg, const SPIRVType *ResType,
+ MachineInstr &I) const;
+
template <bool Signed>
bool selectDot4AddPacked(Register ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
@@ -2112,6 +2115,17 @@ bool SPIRVInstructionSelector::selectOpIsInf(Register ResVReg,
.constrainAllUses(TII, TRI, RBI);
}
+bool SPIRVInstructionSelector::selectOpIsNan(Register ResVReg,
+ const SPIRVType *ResType,
+ MachineInstr &I) const {
+ MachineBasicBlock &BB = *I.getParent();
+ return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpIsNan))
+ .addDef(ResVReg)
+ .addUse(GR.getSPIRVTypeID(ResType))
+ .addUse(I.getOperand(2).getReg())
+ .constrainAllUses(TII, TRI, RBI);
+}
+
template <bool Signed>
bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
const SPIRVType *ResType,
@@ -3255,6 +3269,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
return selectExtInst(ResVReg, ResType, I, CL::fract, GL::Fract);
case Intrinsic::spv_isinf:
return selectOpIsInf(ResVReg, ResType, I);
+ case Intrinsic::spv_isnan:
+ return selectOpIsNan(ResVReg, ResType, I);
case Intrinsic::spv_normalize:
return selectExtInst(ResVReg, ResType, I, CL::normalize, GL::Normalize);
case Intrinsic::spv_refract:
diff --git a/llvm/test/CodeGen/DirectX/isnan.ll b/llvm/test/CodeGen/DirectX/isnan.ll
new file mode 100644
index 0000000000000..2becd75209331
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/isnan.ll
@@ -0,0 +1,53 @@
+; 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 -mtriple=dxil-pc-shadermodel6.8-library %s | FileCheck %s --check-prefixes=CHECK,SMOLDCHECK
+
+; Make sure dxil operation function calls for isnan are generated for float and half.
+
+define noundef i1 @isnan_float(float noundef %a) {
+entry:
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 8, float %{{.*}}) #[[#ATTR:]]
+ ; SMOLDCHECK: call i1 @llvm.dx.isnan.f32(float %{{.*}})
+ %dx.isnan = call i1 @llvm.dx.isnan.f32(float %a)
+ ret i1 %dx.isnan
+}
+
+define noundef i1 @isnan_half(half noundef %a) {
+entry:
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 8, half %{{.*}}) #[[#ATTR]]
+ ; SMOLDCHECK: [[BITCAST:%.*]] = bitcast half %{{.*}} to i16
+ ; SMOLDCHECK: [[ANDHIGH:%.*]] = and i16 [[BITCAST]], 31744
+ ; SMOLDCHECK: [[CMPHIGH:%.*]] = icmp eq i16 [[ANDHIGH]], 31744
+ ; SMOLDCHECK: [[ANDLOW:%.*]] = and i16 [[BITCAST]], 1023
+ ; SMOLDCHECK: [[CMPLOW:%.*]] = icmp ne i16 [[ANDLOW]], 0
+ ; SMOLDCHECK: [[AND:%.*]] = and i1 [[CMPHIGH]], [[CMPLOW]]
+ %dx.isnan = call i1 @llvm.dx.isnan.f16(half %a)
+ ret i1 %dx.isnan
+}
+
+define noundef <4 x i1> @isnan_half4(<4 x half> noundef %p0) {
+entry:
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 8, half
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 8, half
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 8, half
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f16(i32 8, half
+ ; SMOLDCHECK: [[BITCAST:%.*]] = bitcast <4 x half> %{{.*}} to <4 x i16>
+ ; SMOLDCHECK: [[ANDHIGH:%.*]] = and <4 x i16> [[BITCAST]], splat (i16 31744)
+ ; SMOLDCHECK: [[CMPHIGH:%.*]] = icmp eq <4 x i16> [[ANDHIGH]], splat (i16 31744)
+ ; SMOLDCHECK: [[ANDLOW:%.*]] = and <4 x i16> [[BITCAST]], splat (i16 1023)
+ ; SMOLDCHECK: [[CMPLOW:%.*]] = icmp ne <4 x i16> [[ANDLOW]], zeroinitializer
+ ; SMOLDCHECK: [[AND:%.*]] = and <4 x i1> [[CMPHIGH]], [[CMPLOW]]
+ %hlsl.isnan = call <4 x i1> @llvm.dx.isnan.v4f16(<4 x half> %p0)
+ ret <4 x i1> %hlsl.isnan
+}
+
+define noundef <3 x i1> @isnan_float3(<3 x float> noundef %p0) {
+entry:
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 8, float
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 8, float
+ ; SM69CHECK: call i1 @dx.op.isSpecialFloat.f32(i32 8, float
+ ; SMOLDCHECK: = call <3 x i1> @llvm.dx.isnan.v3f32(<3 x float>
+ %hlsl.isnan = call <3 x i1> @llvm.dx.isnan.v3f32(<3 x float> %p0)
+ ret <3 x i1> %hlsl.isnan
+}
+
+; CHECK: attributes #{{[0-9]*}} = {{{.*}} memory(none) {{.*}}}
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/isnan.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/isnan.ll
new file mode 100644
index 0000000000000..67bb0cd8240f3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/isnan.ll
@@ -0,0 +1,45 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - -filetype=obj | spirv-val --target-env spv1.4 %}
+
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#bool:]] = OpTypeBool
+; CHECK-DAG: %[[#vec4_bool:]] = OpTypeVector %[[#bool]] 4
+
+define noundef i1 @isnan_half(half noundef %a) {
+entry:
+ ; CHECK: %[[#]] = OpFunction %[[#bool]] None %[[#]]
+ ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_16]]
+ ; CHECK: %[[#]] = OpIsNan %[[#bool]] %[[#arg0]]
+ %hlsl.isnan = call i1 @llvm.spv.isnan.f16(half %a)
+ ret i1 %hlsl.isnan
+}
+
+define noundef i1 @isnan_float(float noundef %a) {
+entry:
+ ; CHECK: %[[#]] = OpFunction %[[#bool]] None %[[#]]
+ ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_32]]
+ ; CHECK: %[[#]] = OpIsNan %[[#bool]] %[[#arg0]]
+ %hlsl.isnan = call i1 @llvm.spv.isnan.f32(float %a)
+ ret i1 %hlsl.isnan
+}
+
+define noundef <4 x i1> @isnan_half4(<4 x half> noundef %a) {
+entry:
+ ; CHECK: %[[#]] = OpFunction %[[#vec4_bool]] None %[[#]]
+ ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_16]]
+ ; CHECK: %[[#]] = OpIsNan %[[#vec4_bool]] %[[#arg0]]
+ %hlsl.isnan = call <4 x i1> @llvm.spv.isnan.v4f16(<4 x half> %a)
+ ret <4 x i1> %hlsl.isnan
+}
+
+define noundef <4 x i1> @isnan_float4(<4 x float> noundef %a) {
+entry:
+ ; CHECK: %[[#]] = OpFunction %[[#vec4_bool]] None %[[#]]
+ ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
+ ; CHECK: %[[#]] = OpIsNan %[[#vec4_bool]] %[[#arg0]]
+ %hlsl.isnan = call <4 x i1> @llvm.spv.isnan.v4f32(<4 x float> %a)
+ ret <4 x i1> %hlsl.isnan
+}
More information about the cfe-commits
mailing list