Author: Jasmine Tang Date: 2025-11-19T15:52:51-08:00 New Revision: 1278d47e9f9773972ff17deaf4d69db48dccdad8
URL: https://github.com/llvm/llvm-project/commit/1278d47e9f9773972ff17deaf4d69db48dccdad8 DIFF: https://github.com/llvm/llvm-project/commit/1278d47e9f9773972ff17deaf4d69db48dccdad8.diff LOG: [CIR] Upstream isfpclass op (#166037) Ref commit in incubator: ee17ff67f3e567585db991cdad1159520c516bb4 There is a minor change in the assumption for emitting a direct callee. In incubator, `bool hasAttributeNoBuiltin = false` (`llvm-project/clang/lib/CIR/CodeGen/CIRGenExpr.cpp:1671`), while in upstream, it's true, therefore, the call to finite(...) is not converted to a builtin anymore. Fixes #163892 Added: clang/test/CIR/CodeGen/builtin-isfpclass.c Modified: clang/include/clang/CIR/Dialect/IR/CIROps.td clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/CIRGenBuilder.h clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 92300ef5b152c..79a1b292df462 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4112,6 +4112,72 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> { let hasFolder = 1; } +//===----------------------------------------------------------------------===// +// FPClass Test Flags +//===----------------------------------------------------------------------===// + +def FPClassTestEnum : CIR_I32EnumAttr<"FPClassTest", "floating-point class test flags", [ + // Basic flags + I32EnumAttrCase<"SignalingNaN", 1, "fcSNan">, + I32EnumAttrCase<"QuietNaN", 2, "fcQNan">, + I32EnumAttrCase<"NegativeInfinity", 4, "fcNegInf">, + I32EnumAttrCase<"NegativeNormal", 8, "fcNegNormal">, + I32EnumAttrCase<"NegativeSubnormal", 16, "fcNegSubnormal">, + I32EnumAttrCase<"NegativeZero", 32, "fcNegZero">, + I32EnumAttrCase<"PositiveZero", 64, "fcPosZero">, + I32EnumAttrCase<"PositiveSubnormal", 128, "fcPosSubnormal">, + I32EnumAttrCase<"PositiveNormal", 256, "fcPosNormal">, + I32EnumAttrCase<"PositiveInfinity", 512, "fcPosInf">, + + // Composite flags + I32EnumAttrCase<"Nan", 3, "fcNan">, // fcSNan | fcQNan + I32EnumAttrCase<"Infinity", 516, "fcInf">, // fcPosInf | fcNegInf + I32EnumAttrCase<"Normal", 264, "fcNormal">, // fcPosNormal | fcNegNormal + I32EnumAttrCase<"Subnormal", 144, "fcSubnormal">, // fcPosSubnormal | fcNegSubnormal + I32EnumAttrCase<"Zero", 96, "fcZero">, // fcPosZero | fcNegZero + I32EnumAttrCase<"PositiveFinite", 448, "fcPosFinite">,// fcPosNormal | fcPosSubnormal | fcPosZero + I32EnumAttrCase<"NegativeFinite", 56, "fcNegFinite">, // fcNegNormal | fcNegSubnormal | fcNegZero + I32EnumAttrCase<"Finite", 504, "fcFinite">, // fcPosFinite | fcNegFinite + I32EnumAttrCase<"Positive", 960, "fcPositive">, // fcPosFinite | fcPosInf + I32EnumAttrCase<"Negative", 60, "fcNegative">, // fcNegFinite | fcNegInf + I32EnumAttrCase<"All", 1023, "fcAllFlags">, // fcNan | fcInf | fcFinite +]> { + let cppNamespace = "::cir"; +} + +def CIR_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_AnyFloatType:$src, + FPClassTestEnum:$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/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 6b5c34d28ce2a..2ecde9aa5d56d 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -266,6 +266,7 @@ struct MissingFeatures { static bool emitTypeCheck() { return false; } static bool emitTypeMetadataCodeForVCall() { return false; } static bool fastMathFlags() { return false; } + static bool fpConstraints() { return false; } static bool generateDebugInfo() { return false; } static bool globalViewIndices() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 653ce00b29d36..85b38120169fd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -344,6 +344,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { llvm_unreachable("negation for the given type is NYI"); } + cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src, + cir::FPClassTest flags) { + return cir::IsFPClassOp::create(*this, loc, src, flags); + } + // TODO: split this to createFPExt/createFPTrunc when we have dedicated cast // operations. mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) { diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 77f19343653db..dee5704c66011 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -520,6 +520,98 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, cir::PrefetchOp::create(builder, loc, address, locality, isWrite); return RValue::get(nullptr); } + // 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: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + assert(!cir::MissingFeatures::fpConstraints()); + mlir::Location loc = getLoc(e->getBeginLoc()); + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest::Nan), + convertType(e->getType()))); + } + + case Builtin::BI__builtin_issignaling: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + mlir::Location loc = getLoc(e->getBeginLoc()); + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest::SignalingNaN), + convertType(e->getType()))); + } + + case Builtin::BI__builtin_isinf: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + assert(!cir::MissingFeatures::fpConstraints()); + mlir::Location loc = getLoc(e->getBeginLoc()); + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest::Infinity), + 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: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + assert(!cir::MissingFeatures::fpConstraints()); + mlir::Location loc = getLoc(e->getBeginLoc()); + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest::Finite), + convertType(e->getType()))); + } + + case Builtin::BI__builtin_isnormal: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + mlir::Location loc = getLoc(e->getBeginLoc()); + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest::Normal), + convertType(e->getType()))); + } + + case Builtin::BI__builtin_issubnormal: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + mlir::Location loc = getLoc(e->getBeginLoc()); + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest::Subnormal), + convertType(e->getType()))); + } + + case Builtin::BI__builtin_iszero: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + mlir::Location loc = getLoc(e->getBeginLoc()); + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest::Zero), + convertType(e->getType()))); + } + case Builtin::BI__builtin_isfpclass: { + Expr::EvalResult result; + if (!e->getArg(1)->EvaluateAsInt(result, cgm.getASTContext())) + break; + + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + mlir::Value v = emitScalarExpr(e->getArg(0)); + uint64_t test = result.Val.getInt().getLimitedValue(); + mlir::Location loc = getLoc(e->getBeginLoc()); + // + return RValue::get(builder.createBoolToInt( + builder.createIsFPClass(loc, v, cir::FPClassTest(test)), + convertType(e->getType()))); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index d45a2975c4afc..833464824f0e5 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -676,6 +676,18 @@ mlir::LogicalResult CIRToLLVMASinOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite( + cir::IsFPClassOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Value src = adaptor.getSrc(); + cir::FPClassTest flags = adaptor.getFlags(); + mlir::IntegerType retTy = rewriter.getI1Type(); + + rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>( + op, retTy, src, static_cast<uint32_t>(flags)); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite( cir::AssumeOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/CodeGen/builtin-isfpclass.c b/clang/test/CIR/CodeGen/builtin-isfpclass.c new file mode 100644 index 0000000000000..16d82c905f445 --- /dev/null +++ b/clang/test/CIR/CodeGen/builtin-isfpclass.c @@ -0,0 +1,174 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=OGCG +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); + // CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.f16) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 516) + // OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 516) + + res = __builtin_isinf(F); + // CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.float) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 516) + // OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 516) + + res = __builtin_isinf(D); + // CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.double) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 516) + // OGCG: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 516) + + res = __builtin_isinf(LD); + // CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.long_double<!cir.f80>) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f80(x86_fp80 {{.*}}, i32 516) + // OGCG: call i1 @llvm.is.fpclass.f80(x86_fp80 {{.*}}, i32 516) + + res = __builtin_isfinite(*H); + // CIR: cir.is_fp_class %{{.*}}, fcFinite : (!cir.f16) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 504) + // OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 504) + + res = __builtin_isfinite(F); + // CIR: cir.is_fp_class %{{.*}}, fcFinite : (!cir.float) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504) + // OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504) + + res = finite(D); + // CIR: cir.call @finite(%{{.*}}) nothrow side_effect(const) : (!cir.double) -> !s32i + // LLVM: call i32 @finite(double {{.*}}) + // OGCG: call i1 @llvm.is.fpclass.f64(double %20, i32 504) + res = __builtin_isnormal(*H); + // CIR: cir.is_fp_class %{{.*}}, fcNormal : (!cir.f16) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 264) + // OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 264) + + res = __builtin_isnormal(F); + // CIR: cir.is_fp_class %{{.*}}, fcNormal : (!cir.float) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 264) + // OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 264) + + res = __builtin_issubnormal(F); + // CIR: cir.is_fp_class %{{.*}}, fcSubnormal : (!cir.float) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 144) + // OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 144) + res = __builtin_iszero(F); + // CIR: cir.is_fp_class %{{.*}}, fcZero : (!cir.float) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 96) + // OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 96) + res = __builtin_issignaling(F); + // CIR: cir.is_fp_class %{{.*}}, fcSNan : (!cir.float) -> !cir.bool + // LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1) + // OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1) +} + +_Bool check_isfpclass_finite(float x) { + return __builtin_isfpclass(x, 504 /*Finite*/); +} + +// CIR: cir.func {{.*}}@check_isfpclass_finite +// CIR: cir.is_fp_class %{{.*}}, fcFinite : (!cir.float) +// LLVM: @check_isfpclass_finite +// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504) +// OGCG: @check_isfpclass_finite +// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504) + +_Bool check_isfpclass_nan_f32(float x) { + return __builtin_isfpclass(x, 3 /*NaN*/); +} + +// CIR: cir.func {{.*}}@check_isfpclass_nan_f32 +// CIR: cir.is_fp_class %{{.*}}, fcNan : (!cir.float) +// LLVM: @check_isfpclass_nan_f32 +// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 3) +// OGCG: @check_isfpclass_nan_f32 +// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 3) + + +_Bool check_isfpclass_snan_f64(double x) { + return __builtin_isfpclass(x, 1 /*SNaN*/); +} + +// CIR: cir.func {{.*}}@check_isfpclass_snan_f64 +// CIR: cir.is_fp_class %{{.*}}, fcSNan : (!cir.double) +// LLVM: @check_isfpclass_snan_f64 +// LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 1) +// OGCG: @check_isfpclass_snan_f64 +// OGCG: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 1) + + +_Bool check_isfpclass_zero_f16(_Float16 x) { + return __builtin_isfpclass(x, 96 /*Zero*/); +} + +// CIR: cir.func {{.*}}@check_isfpclass_zero_f16 +// CIR: cir.is_fp_class %{{.*}}, fcZero : (!cir.f16) +// LLVM: @check_isfpclass_zero_f16 +// LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 96) +// OGCG: @check_isfpclass_zero_f16 +// OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 96) + +// 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*/); +// } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
