From 75ce75c3752e478789381292f96851ecc97ac793 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Mon, 14 Oct 2024 11:37:56 +0800 Subject: [PATCH] [CIR] [CodeGen] Introduce IsFPClassOp --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 33 ++++ .../include/clang/CIR/Dialect/IR/CIRTypes.td | 3 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 5 + clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 148 ++++++++++++++---- clang/lib/CIR/CodeGen/TargetInfo.h | 10 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 29 +++- clang/test/CIR/CodeGen/builtin-isfpclass.c | 129 +++++++++++++++ clang/test/CIR/Lowering/builtin-isfpclass.c | 125 +++++++++++++++ 8 files changed, 447 insertions(+), 35 deletions(-) create mode 100644 clang/test/CIR/CodeGen/builtin-isfpclass.c create mode 100644 clang/test/CIR/Lowering/builtin-isfpclass.c diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index a16b4c9ec3b2..3b21cc4b717a 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4027,6 +4027,39 @@ def FMinOp : BinaryFPToFPBuiltinOp<"fmin", "MinNumOp">; def FModOp : BinaryFPToFPBuiltinOp<"fmod", "FRemOp">; def PowOp : BinaryFPToFPBuiltinOp<"pow", "PowOp">; +def IsFPClassOp : CIR_Op<"is_fp_class"> { + let summary = "Corresponding to the `__builtin_fpclassify` builtin function in clang"; + + let description = [{ + The `cir.is_fp_class` operation takes a floating-point value as its first + argument and a bitfield of flags as its second argument. The operation + returns a boolean value indicating whether the floating-point value + satisfies the given flags. + + The flags must be a compile time constant and the values are: + + | Bit # | floating-point class | + | -------- | ------- | + | 0 | Signaling NaN | + | 1 | Quiet NaN | + | 2 | Negative infinity | + | 3 | Negative normal | + | 4 | Negative subnormal | + | 5 | Negative zero | + | 6 | Positive zero | + | 7 | Positive subnormal | + | 8 | Positive normal | + | 9 | Positive infinity | + }]; + + let arguments = (ins CIR_AnyFloat:$src, + I32Attr:$flags); + let results = (outs CIR_BoolType:$result); + let assemblyFormat = [{ + $src `,` $flags `:` functional-type($src, $result) attr-dict + }]; +} + //===----------------------------------------------------------------------===// // Assume Operations //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index 81b939df6b1f..fe136a58a4a4 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -202,7 +202,8 @@ def CIR_LongDouble : CIR_FloatType<"LongDouble", "long_double"> { // Constraints -def CIR_AnyFloat: AnyTypeOf<[CIR_Single, CIR_Double, CIR_FP80, CIR_FP128, CIR_LongDouble]>; +def CIR_AnyFloat: AnyTypeOf<[CIR_Single, CIR_Double, CIR_FP80, CIR_FP128, CIR_LongDouble, + CIR_FP16, CIR_BFloat16]>; def CIR_AnyIntOrFloat: AnyTypeOf<[CIR_AnyFloat, CIR_IntType]>; //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 4c7f4bf6f7bc..fef41474ec44 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -584,6 +584,11 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { getAttr(t, fpVal)); } + mlir::cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src, + unsigned flags) { + return create(loc, src, flags); + } + /// Create constant nullptr for pointer-to-data-member type ty. mlir::cir::ConstantOp getNullDataMemberPtr(mlir::cir::DataMemberType ty, mlir::Location loc) { diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 5b612b8a844d..91c46bf7422a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -46,6 +46,17 @@ static RValue buildLibraryCall(CIRGenFunction &CGF, const FunctionDecl *FD, return CGF.buildCall(E->getCallee()->getType(), callee, E, ReturnValueSlot()); } +static mlir::Value tryUseTestFPKind(CIRGenFunction &CGF, unsigned BuiltinID, + mlir::Value V) { + if (CGF.getBuilder().getIsFPConstrained() && + CGF.getBuilder().getDefaultConstrainedExcept() != fp::ebIgnore) { + if (mlir::Value Result = CGF.getTargetHooks().testFPKind( + V, BuiltinID, CGF.getBuilder(), CGF.CGM)) + return Result; + } + return nullptr; +} + template static RValue buildUnaryFPBuiltin(CIRGenFunction &CGF, const CallExpr &E) { auto Arg = CGF.buildScalarExpr(E.getArg(0)); @@ -1191,36 +1202,6 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_isunordered: llvm_unreachable("BI__builtin_isgreater and BI__builtin_isless like NYI"); - case Builtin::BI__builtin_isnan: - llvm_unreachable("BI__builtin_isnan NYI"); - - case Builtin::BI__builtin_issignaling: - llvm_unreachable("BI__builtin_issignaling NYI"); - - case Builtin::BI__builtin_isinf: - llvm_unreachable("BI__builtin_isinf NYI"); - - case Builtin::BIfinite: - case Builtin::BI__finite: - case Builtin::BIfinitef: - case Builtin::BI__finitef: - case Builtin::BIfinitel: - case Builtin::BI__finitel: - case Builtin::BI__builtin_isfinite: - llvm_unreachable("Builtin::BIfinite like NYI"); - - case Builtin::BI__builtin_isnormal: - llvm_unreachable("BI__builtin_isnormal NYI"); - - case Builtin::BI__builtin_issubnormal: - llvm_unreachable("BI__builtin_issubnormal NYI"); - - case Builtin::BI__builtin_iszero: - llvm_unreachable("BI__builtin_iszero NYI"); - - case Builtin::BI__builtin_isfpclass: - llvm_unreachable("BI__builtin_isfpclass NYI"); - case Builtin::BI__builtin_nondeterministic_value: llvm_unreachable("BI__builtin_nondeterministic_value NYI"); @@ -1328,9 +1309,6 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_matrix_column_major_store: llvm_unreachable("BI__builtin_matrix_column_major_store NYI"); - case Builtin::BI__builtin_isinf_sign: - llvm_unreachable("BI__builtin_isinf_sign NYI"); - case Builtin::BI__builtin_flt_rounds: llvm_unreachable("BI__builtin_flt_rounds NYI"); @@ -2080,6 +2058,110 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, llvm_unreachable("BI__builtin_ms_va_copy NYI"); case Builtin::BI__builtin_get_device_side_mangled_name: llvm_unreachable("BI__builtin_get_device_side_mangled_name NYI"); + + // From https://clang.llvm.org/docs/LanguageExtensions.html#builtin-isfpclass + // : + // + // The `__builtin_isfpclass()` builtin is a generalization of functions + // isnan, isinf, isfinite and some others defined by the C standard. It tests + // if the floating-point value, specified by the first argument, falls into + // any of data classes, specified by the second argument. + case Builtin::BI__builtin_isnan: { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + if (mlir::Value Result = tryUseTestFPKind(*this, BuiltinID, V)) + return RValue::get(Result); + mlir::Location Loc = getLoc(E->getBeginLoc()); + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcNan), + ConvertType(E->getType()))); + } + + case Builtin::BI__builtin_issignaling: { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + mlir::Location Loc = getLoc(E->getBeginLoc()); + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcSNan), + ConvertType(E->getType()))); + } + + case Builtin::BI__builtin_isinf: { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + if (mlir::Value Result = tryUseTestFPKind(*this, BuiltinID, V)) + return RValue::get(Result); + mlir::Location Loc = getLoc(E->getBeginLoc()); + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcInf), + ConvertType(E->getType()))); + } + + case Builtin::BIfinite: + case Builtin::BI__finite: + case Builtin::BIfinitef: + case Builtin::BI__finitef: + case Builtin::BIfinitel: + case Builtin::BI__finitel: + case Builtin::BI__builtin_isfinite: { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + if (mlir::Value Result = tryUseTestFPKind(*this, BuiltinID, V)) + return RValue::get(Result); + mlir::Location Loc = getLoc(E->getBeginLoc()); + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcFinite), + ConvertType(E->getType()))); + } + + case Builtin::BI__builtin_isnormal: { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + mlir::Location Loc = getLoc(E->getBeginLoc()); + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcNormal), + ConvertType(E->getType()))); + } + + case Builtin::BI__builtin_issubnormal: { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + mlir::Location Loc = getLoc(E->getBeginLoc()); + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcSubnormal), + ConvertType(E->getType()))); + } + + case Builtin::BI__builtin_iszero: { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + mlir::Location Loc = getLoc(E->getBeginLoc()); + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcZero), + ConvertType(E->getType()))); + } + + case Builtin::BI__builtin_isfpclass: { + Expr::EvalResult Result; + if (!E->getArg(1)->EvaluateAsInt(Result, CGM.getASTContext())) + break; + + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E); + mlir::Value V = buildScalarExpr(E->getArg(0)); + uint64_t Test = Result.Val.getInt().getLimitedValue(); + mlir::Location Loc = getLoc(E->getBeginLoc()); + + // FIXME: We should use builder.createZExt once createZExt is available. + return RValue::get(builder.createZExtOrBitCast( + Loc, builder.createIsFPClass(Loc, V, Test), ConvertType(E->getType()))); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h index 994fa357c864..873b2ef0fb00 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.h +++ b/clang/lib/CIR/CodeGen/TargetInfo.h @@ -25,6 +25,7 @@ namespace cir { class CIRGenFunction; class CIRGenModule; +class CIRGenBuilderTy; /// This class organizes various target-specific codegeneration issues, like /// target-specific attributes, builtins and so on. @@ -43,6 +44,15 @@ class TargetCIRGenInfo { return false; } + /// Performs a target specific test of a floating point value for things + /// like IsNaN, Infinity, ... Nullptr is returned if no implementation + /// exists. + virtual mlir::Value testFPKind(mlir::Value V, unsigned BuiltinID, + CIRGenBuilderTy &Builder, + CIRGenModule &CGM) const { + return {}; + } + /// Corrects the MLIR type for a given constraint and "usual" /// type. /// diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index dec1a01f2063..7f40fa6c7d4a 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4119,6 +4119,33 @@ class CIRThrowOpLowering } }; +class CIRIsFPClassOpLowering + : public mlir::OpConversionPattern { +public: + using OpConversionPattern::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(mlir::cir::IsFPClassOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + auto src = adaptor.getSrc(); + auto flags = adaptor.getFlags(); + auto retTy = rewriter.getI1Type(); + + auto loc = op->getLoc(); + + auto intrinsic = + rewriter.create(loc, retTy, src, flags); + // FIMXE: CIR now will convert cir::BoolType to i8 type unconditionally. + // Remove this conversion after we fix + // https://github.com/llvm/clangir/issues/480 + auto converted = rewriter.create( + loc, rewriter.getI8Type(), intrinsic->getResult(0)); + + rewriter.replaceOp(op, converted); + return mlir::success(); + } +}; + void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, mlir::TypeConverter &converter, mlir::DataLayout &dataLayout) { @@ -4151,7 +4178,7 @@ void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, CIREhTypeIdOpLowering, CIRCatchParamOpLowering, CIRResumeOpLowering, CIRAllocExceptionOpLowering, CIRFreeExceptionOpLowering, CIRThrowOpLowering, CIRIntrinsicCallLowering, CIRBaseClassAddrOpLowering, - CIRVTTAddrPointOpLowering + CIRVTTAddrPointOpLowering, CIRIsFPClassOpLowering #define GET_BUILTIN_LOWERING_LIST #include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc" #undef GET_BUILTIN_LOWERING_LIST diff --git a/clang/test/CIR/CodeGen/builtin-isfpclass.c b/clang/test/CIR/CodeGen/builtin-isfpclass.c new file mode 100644 index 000000000000..d1e295203dfd --- /dev/null +++ b/clang/test/CIR/CodeGen/builtin-isfpclass.c @@ -0,0 +1,129 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +int finite(double); + +// CHECK: cir.func {{.*}}@test_is_finite +void test_is_finite(__fp16 *H, float F, double D, long double LD) { + volatile int res; + res = __builtin_isinf(*H); + // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.f16) -> !cir.bool + + res = __builtin_isinf(F); + // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.float) -> !cir.bool + + res = __builtin_isinf(D); + // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.double) -> !cir.bool + + res = __builtin_isinf(LD); + // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.long_double) -> !cir.bool + + res = __builtin_isfinite(*H); + // CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.f16) -> !cir.bool + res = __builtin_isfinite(F); + // CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.float) -> !cir.bool + res = finite(D); + // CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.double) -> !cir.bool + + res = __builtin_isnormal(*H); + // CHECK: cir.is_fp_class %{{.*}}, 264 : (!cir.f16) -> !cir.bool + res = __builtin_isnormal(F); + // CHECK: cir.is_fp_class %{{.*}}, 264 : (!cir.float) -> !cir.bool + + res = __builtin_issubnormal(F); + // CHECK: cir.is_fp_class %{{.*}}, 144 : (!cir.float) -> !cir.bool + res = __builtin_iszero(F); + // CHECK: cir.is_fp_class %{{.*}}, 96 : (!cir.float) -> !cir.bool + res = __builtin_issignaling(F); + // CHECK: cir.is_fp_class %{{.*}}, 1 : (!cir.float) -> !cir.bool +} + +_Bool check_isfpclass_finite(float x) { + return __builtin_isfpclass(x, 504 /*Finite*/); +} + +// CHECK: cir.func {{.*}}@check_isfpclass_finite +// CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.float) + +_Bool check_isfpclass_nan_f32(float x) { + return __builtin_isfpclass(x, 3 /*NaN*/); +} + +// CHECK: cir.func {{.*}}@check_isfpclass_nan_f32 +// CHECK: cir.is_fp_class %{{.*}}, 3 : (!cir.float) + + +_Bool check_isfpclass_snan_f64(double x) { + return __builtin_isfpclass(x, 1 /*SNaN*/); +} + +// CHECK: cir.func {{.*}}@check_isfpclass_snan_f64 +// CHECK: cir.is_fp_class %{{.*}}, 1 : (!cir.double) + + +_Bool check_isfpclass_zero_f16(_Float16 x) { + return __builtin_isfpclass(x, 96 /*Zero*/); +} + +// CHECK: cir.func {{.*}}@check_isfpclass_zero_f16 +// CHECK: cir.is_fp_class %{{.*}}, 96 : (!cir.f16) + +// Update when we support FP pragma in functions and can convert BoolType in prvalue to i1. + +// _Bool check_isfpclass_finite_strict(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 504 /*Finite*/); +// } +// +// _Bool check_isfpclass_nan_f32_strict(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 3 /*NaN*/); +// } +// +// _Bool check_isfpclass_snan_f64_strict(double x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 1 /*NaN*/); +// } +// +// _Bool check_isfpclass_zero_f16_strict(_Float16 x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 96 /*Zero*/); +// } +// +// _Bool check_isnan(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isnan(x); +// } +// +// _Bool check_isinf(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isinf(x); +// } +// +// _Bool check_isfinite(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfinite(x); +// } +// +// _Bool check_isnormal(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isnormal(x); +// } +// +// typedef float __attribute__((ext_vector_type(4))) float4; +// typedef double __attribute__((ext_vector_type(4))) double4; +// typedef int __attribute__((ext_vector_type(4))) int4; +// typedef long __attribute__((ext_vector_type(4))) long4; +// +// int4 check_isfpclass_nan_v4f32(float4 x) { +// return __builtin_isfpclass(x, 3 /*NaN*/); +// } +// +// int4 check_isfpclass_nan_strict_v4f32(float4 x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 3 /*NaN*/); +// } +// +// long4 check_isfpclass_nan_v4f64(double4 x) { +// return __builtin_isfpclass(x, 3 /*NaN*/); +// } diff --git a/clang/test/CIR/Lowering/builtin-isfpclass.c b/clang/test/CIR/Lowering/builtin-isfpclass.c new file mode 100644 index 000000000000..630ded117ab3 --- /dev/null +++ b/clang/test/CIR/Lowering/builtin-isfpclass.c @@ -0,0 +1,125 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s + +int finite(double); + +// CHECK: define {{.*}}@test_is_finite +void test_is_finite(__fp16 *H, float F, double D, long double LD) { + volatile int res; + res = __builtin_isinf(*H); + // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 516) + res = __builtin_isinf(F); + // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 516) + res = __builtin_isinf(D); + // CHECK: call i1 @llvm.is.fpclass.f64(double %{{.*}}, i32 516) + res = __builtin_isinf(LD); + // CHECK: call i1 @llvm.is.fpclass.f80(x86_fp80 %{{.*}}, i32 516) + + res = __builtin_isfinite(*H); + // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 504) + res = __builtin_isfinite(F); + // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 504) + res = finite(D); + // CHECK: call i1 @llvm.is.fpclass.f64(double %{{.*}}, i32 504) + + res = __builtin_isnormal(*H); + // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 264) + res = __builtin_isnormal(F); + // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 264) + + res = __builtin_issubnormal(F); + // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 144) + res = __builtin_iszero(F); + // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 96) + res = __builtin_issignaling(F); + // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 1) +} + +_Bool check_isfpclass_finite(float x) { + return __builtin_isfpclass(x, 504 /*Finite*/); +} + +// CHECK: define {{.*}}@check_isfpclass_finite +// CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 504) + +_Bool check_isfpclass_nan_f32(float x) { + return __builtin_isfpclass(x, 3 /*NaN*/); +} + +// CHECK: define {{.*}}@check_isfpclass_nan_f32 +// CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 3) + +_Bool check_isfpclass_snan_f64(double x) { + return __builtin_isfpclass(x, 1 /*SNaN*/); +} + +// CHECK: define {{.*}}@check_isfpclass_snan_f64 +// CHECK: call i1 @llvm.is.fpclass.f64(double %{{.*}}, i32 1) + + +_Bool check_isfpclass_zero_f16(_Float16 x) { + return __builtin_isfpclass(x, 96 /*Zero*/); +} + +// CHECK: define {{.*}}@check_isfpclass_zero_f16 +// CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 96) + +// Update when we support FP pragma in functions. + +// _Bool check_isfpclass_finite_strict(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 504 /*Finite*/); +// } +// +// _Bool check_isfpclass_nan_f32_strict(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 3 /*NaN*/); +// } +// +// _Bool check_isfpclass_snan_f64_strict(double x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 1 /*NaN*/); +// } +// +// _Bool check_isfpclass_zero_f16_strict(_Float16 x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 96 /*Zero*/); +// } +// +// _Bool check_isnan(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isnan(x); +// } +// +// _Bool check_isinf(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isinf(x); +// } +// +// _Bool check_isfinite(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfinite(x); +// } +// +// _Bool check_isnormal(float x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isnormal(x); +// } +// +// typedef float __attribute__((ext_vector_type(4))) float4; +// typedef double __attribute__((ext_vector_type(4))) double4; +// typedef int __attribute__((ext_vector_type(4))) int4; +// typedef long __attribute__((ext_vector_type(4))) long4; +// +// int4 check_isfpclass_nan_v4f32(float4 x) { +// return __builtin_isfpclass(x, 3 /*NaN*/); +// } +// +// int4 check_isfpclass_nan_strict_v4f32(float4 x) { +// #pragma STDC FENV_ACCESS ON +// return __builtin_isfpclass(x, 3 /*NaN*/); +// } +// +// long4 check_isfpclass_nan_v4f64(double4 x) { +// return __builtin_isfpclass(x, 3 /*NaN*/); +// }