https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/205941
>From 8336c5ae898c02497d89be0b93189dd1b687cdd0 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Thu, 25 Jun 2026 15:31:06 -0700 Subject: [PATCH 1/2] [CIR] Model cir.is_fp_class flags as a bit-enum `__builtin_isfpclass(x, flags)` takes an arbitrary 10-bit class mask, but `cir.is_fp_class`'s `flags` operand was a closed `I32EnumAttr` that only accepted the enumerated values. The generated `FPClassTestAttr::get` does `cast<FPClassTestAttr>(IntegerAttr)`, whose `classof` rejects any non-enumerated mask, so CIRGen aborted on e.g. `__builtin_isfpclass(x, 5)` (`fcSNan|fcNegInf`). The named fp-classification builtins never hit this because they pass enumerated composite values. Remodel `FPClassTestEnum` as a bit-enum (`I32BitEnumAttr`): the ten classes become single-bit cases (positions 0-9, matching `llvm::FPClassTest`), with `fcNone` and the composite aliases (`fcNan`, `fcInf`, `fcFinite`, ...) as case groups so the existing `cir::FPClassTest::X` C++ members and call sites are unchanged. Any 0-1023 mask is now representable, and the flags print pipe-delimited (`fcSNan|fcNegInf`) with `printBitEnumPrimaryGroups` so exact groups still print as their keyword (`fcInf`). Adds a `CIR_I32BitEnumAttr` wrapper -- CIR's first bit-enum. builtin-isfpclass.c gains non-enumerated-mask coverage; the printed-flag form in the other is_fp_class tests is updated for the bit-enum syntax. --- .../clang/CIR/Dialect/IR/CIREnumAttr.td | 6 ++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 76 ++++++++++++------- .../CIR/CodeGenBuiltins/builtin-fpclassify.c | 40 +++++----- .../CIR/CodeGenBuiltins/builtin-isfpclass.c | 50 ++++++++---- .../CIR/CodeGenBuiltins/builtin-isinf-sign.c | 2 +- 5 files changed, 112 insertions(+), 62 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td b/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td index cc6f256ddfef4..f75598d6ca75c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td +++ b/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td @@ -26,6 +26,12 @@ class CIR_I64EnumAttr<string name, string summary, list<I64EnumAttrCase> cases> let cppNamespace = "::cir"; } +class CIR_I32BitEnumAttr<string name, string summary, + list<BitEnumCaseBase> cases> + : I32BitEnumAttr<name, summary, cases> { + let cppNamespace = "::cir"; +} + class CIR_EnumAttr<EnumAttrInfo info, string name = "", list<Trait> traits = []> : EnumAttr<CIR_Dialect, info, name, traits> { let assemblyFormat = "`<` $value `>`"; diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 355d4cb047a04..fe212fd395059 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -6422,33 +6422,55 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> { // 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"; +// Individual floating-point classes, one per bit (positions 0-9), matching +// LLVM's `FPClassTest` bit layout (`llvm/ADT/FloatingPointMode.h`). Modeled +// as a bit-enum so `__builtin_isfpclass` can pass any combination of bits. +def FPClass_None : I32BitEnumAttrCaseNone<"None", "fcNone">; +def FPClass_SNan : I32BitEnumAttrCaseBit<"SignalingNaN", 0, "fcSNan">; +def FPClass_QNan : I32BitEnumAttrCaseBit<"QuietNaN", 1, "fcQNan">; +def FPClass_NegInf : I32BitEnumAttrCaseBit<"NegativeInfinity", 2, "fcNegInf">; +def FPClass_NegNorm : I32BitEnumAttrCaseBit<"NegativeNormal", 3, "fcNegNormal">; +def FPClass_NegSub : I32BitEnumAttrCaseBit<"NegativeSubnormal", 4, + "fcNegSubnormal">; +def FPClass_NegZero : I32BitEnumAttrCaseBit<"NegativeZero", 5, "fcNegZero">; +def FPClass_PosZero : I32BitEnumAttrCaseBit<"PositiveZero", 6, "fcPosZero">; +def FPClass_PosSub : I32BitEnumAttrCaseBit<"PositiveSubnormal", 7, + "fcPosSubnormal">; +def FPClass_PosNorm : I32BitEnumAttrCaseBit<"PositiveNormal", 8, "fcPosNormal">; +def FPClass_PosInf : I32BitEnumAttrCaseBit<"PositiveInfinity", 9, "fcPosInf">; + +// Composite groups (aliases for combinations of the individual classes). +def FPClass_Nan : I32BitEnumAttrCaseGroup<"Nan", + [FPClass_SNan, FPClass_QNan], "fcNan">; +def FPClass_Inf : I32BitEnumAttrCaseGroup<"Infinity", + [FPClass_NegInf, FPClass_PosInf], "fcInf">; +def FPClass_Norm : I32BitEnumAttrCaseGroup<"Normal", + [FPClass_NegNorm, FPClass_PosNorm], "fcNormal">; +def FPClass_Sub : I32BitEnumAttrCaseGroup<"Subnormal", + [FPClass_NegSub, FPClass_PosSub], "fcSubnormal">; +def FPClass_Zero : I32BitEnumAttrCaseGroup<"Zero", + [FPClass_NegZero, FPClass_PosZero], "fcZero">; +def FPClass_PosFin : I32BitEnumAttrCaseGroup<"PositiveFinite", + [FPClass_PosNorm, FPClass_PosSub, FPClass_PosZero], "fcPosFinite">; +def FPClass_NegFin : I32BitEnumAttrCaseGroup<"NegativeFinite", + [FPClass_NegNorm, FPClass_NegSub, FPClass_NegZero], "fcNegFinite">; +def FPClass_Fin : I32BitEnumAttrCaseGroup<"Finite", + [FPClass_PosFin, FPClass_NegFin], "fcFinite">; +def FPClass_Pos : I32BitEnumAttrCaseGroup<"Positive", + [FPClass_PosFin, FPClass_PosInf], "fcPositive">; +def FPClass_Neg : I32BitEnumAttrCaseGroup<"Negative", + [FPClass_NegFin, FPClass_NegInf], "fcNegative">; +def FPClass_All : I32BitEnumAttrCaseGroup<"All", + [FPClass_Nan, FPClass_Inf, FPClass_Fin], "fcAllFlags">; + +def FPClassTestEnum + : CIR_I32BitEnumAttr<"FPClassTest", "floating-point class test flags", [ + FPClass_None, FPClass_SNan, FPClass_QNan, FPClass_NegInf, FPClass_NegNorm, + FPClass_NegSub, FPClass_NegZero, FPClass_PosZero, FPClass_PosSub, + FPClass_PosNorm, FPClass_PosInf, FPClass_Nan, FPClass_Inf, FPClass_Norm, + FPClass_Sub, FPClass_Zero, FPClass_PosFin, FPClass_NegFin, FPClass_Fin, + FPClass_Pos, FPClass_Neg, FPClass_All]> { + let printBitEnumPrimaryGroups = 1; } def CIR_IsFPClassOp : CIR_Op<"is_fp_class", [Pure]> { diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c b/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c index bad83c4f0ef4c..7ebc3c7b2fdfc 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c +++ b/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c @@ -15,16 +15,16 @@ void test_fpclassify_nan(){ float nanValue = 0.0f / 0.0f; __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, nanValue); -// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, fcZero : (!cir.float) -> !cir.bool +// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, "fcZero" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_ZERO]], true { // CIR: cir.const #cir.int<96> : !s32i -// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, fcNan : (!cir.float) -> !cir.bool +// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, "fcNan" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_NAN]], true { // CIR: cir.const #cir.int<3> : !s32i -// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, fcInf : (!cir.float) -> !cir.bool +// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, "fcInf" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_INF]], true { // CIR: cir.const #cir.int<516> : !s32i -// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, fcNormal : (!cir.float) -> !cir.bool +// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, "fcNormal" : (!cir.float) -> !cir.bool // CIR: %[[NORMAL_VAL:.+]] = cir.const #cir.int<264> : !s32i // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i @@ -84,16 +84,16 @@ void test_fpclassify_inf(){ float infValue = 1.0f / 0.0f; __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, infValue); -// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, fcZero : (!cir.float) -> !cir.bool +// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, "fcZero" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_ZERO]], true { // CIR: cir.const #cir.int<96> : !s32i -// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, fcNan : (!cir.float) -> !cir.bool +// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, "fcNan" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_NAN]], true { // CIR: cir.const #cir.int<3> : !s32i -// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, fcInf : (!cir.float) -> !cir.bool +// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, "fcInf" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_INF]], true { // CIR: cir.const #cir.int<516> : !s32i -// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, fcNormal : (!cir.float) -> !cir.bool +// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, "fcNormal" : (!cir.float) -> !cir.bool // CIR: %[[NORMAL_VAL:.+]] = cir.const #cir.int<264> : !s32i // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i @@ -152,16 +152,16 @@ void test_fpclassify_normal(){ float normalValue = 1.0f; __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, normalValue); -// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, fcZero : (!cir.float) -> !cir.bool +// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, "fcZero" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_ZERO]], true { // CIR: cir.const #cir.int<96> : !s32i -// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, fcNan : (!cir.float) -> !cir.bool +// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, "fcNan" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_NAN]], true { // CIR: cir.const #cir.int<3> : !s32i -// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, fcInf : (!cir.float) -> !cir.bool +// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, "fcInf" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_INF]], true { // CIR: cir.const #cir.int<516> : !s32i -// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, fcNormal : (!cir.float) -> !cir.bool +// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, "fcNormal" : (!cir.float) -> !cir.bool // CIR: %[[NORMAL_VAL:.+]] = cir.const #cir.int<264> : !s32i // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i @@ -221,16 +221,16 @@ void test_fpclassify_subnormal(){ float subnormalValue = 1.0e-40f; __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, subnormalValue); -// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, fcZero : (!cir.float) -> !cir.bool +// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, "fcZero" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_ZERO]], true { // CIR: cir.const #cir.int<96> : !s32i -// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, fcNan : (!cir.float) -> !cir.bool +// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, "fcNan" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_NAN]], true { // CIR: cir.const #cir.int<3> : !s32i -// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, fcInf : (!cir.float) -> !cir.bool +// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, "fcInf" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_INF]], true { // CIR: cir.const #cir.int<516> : !s32i -// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, fcNormal : (!cir.float) -> !cir.bool +// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, "fcNormal" : (!cir.float) -> !cir.bool // CIR: %[[NORMAL_VAL:.+]] = cir.const #cir.int<264> : !s32i // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i @@ -290,16 +290,16 @@ void test_fpclassify_zero(){ float zeroValue = 0.0f; __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, zeroValue); -// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, fcZero : (!cir.float) -> !cir.bool +// CIR: %[[IS_ZERO:.+]] = cir.is_fp_class %{{.+}}, "fcZero" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_ZERO]], true { // CIR: cir.const #cir.int<96> : !s32i -// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, fcNan : (!cir.float) -> !cir.bool +// CIR: %[[IS_NAN:.+]] = cir.is_fp_class %{{.+}}, "fcNan" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_NAN]], true { // CIR: cir.const #cir.int<3> : !s32i -// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, fcInf : (!cir.float) -> !cir.bool +// CIR: %[[IS_INF:.+]] = cir.is_fp_class %{{.+}}, "fcInf" : (!cir.float) -> !cir.bool // CIR: cir.ternary(%[[IS_INF]], true { // CIR: cir.const #cir.int<516> : !s32i -// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, fcNormal : (!cir.float) -> !cir.bool +// CIR: %[[IS_NORMAL:.+]] = cir.is_fp_class %{{.+}}, "fcNormal" : (!cir.float) -> !cir.bool // CIR: %[[NORMAL_VAL:.+]] = cir.const #cir.int<264> : !s32i // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c b/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c index a8dea4dfb7d77..0c71733780cee 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c +++ b/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c @@ -10,55 +10,55 @@ int finite(double); 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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.is_fp_class %{{.*}}, fcFinite : (!cir.double) -> !cir.bool + // CIR: cir.is_fp_class %{{.*}}, "fcFinite" : (!cir.double) -> !cir.bool // LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 504) // 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 + // 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 + // 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 + // 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 + // 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); @@ -72,7 +72,7 @@ _Bool check_isfpclass_finite(float x) { } // CIR: cir.func {{.*}}@check_isfpclass_finite -// CIR: cir.is_fp_class %{{.*}}, fcFinite : (!cir.float) +// 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 @@ -83,7 +83,7 @@ _Bool check_isfpclass_nan_f32(float x) { } // CIR: cir.func {{.*}}@check_isfpclass_nan_f32 -// CIR: cir.is_fp_class %{{.*}}, fcNan : (!cir.float) +// 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 @@ -107,12 +107,34 @@ _Bool check_isfpclass_zero_f16(_Float16 x) { } // CIR: cir.func {{.*}}@check_isfpclass_zero_f16 -// CIR: cir.is_fp_class %{{.*}}, fcZero : (!cir.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) +_Bool check_isfpclass_snan_neginf(double x) { + return __builtin_isfpclass(x, 5 /*fcSNan|fcNegInf*/); +} + +// CIR: cir.func {{.*}}@check_isfpclass_snan_neginf +// CIR: cir.is_fp_class %{{.*}}, "fcSNan|fcNegInf" : (!cir.double) +// LLVM: @check_isfpclass_snan_neginf +// LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 5) +// OGCG: @check_isfpclass_snan_neginf +// OGCG: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 5) + +_Bool check_isfpclass_multi(float x) { + return __builtin_isfpclass(x, 1022 /*all but fcSNan*/); +} + +// CIR: cir.func {{.*}}@check_isfpclass_multi +// CIR: cir.is_fp_class %{{.*}}, "fcNegative|fcPositive|fcQNan" : (!cir.float) +// LLVM: @check_isfpclass_multi +// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1022) +// OGCG: @check_isfpclass_multi +// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1022) + // Update when we support FP pragma in functions and can convert BoolType in prvalue to i1. // _Bool check_isfpclass_finite_strict(float x) { diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-isinf-sign.c b/clang/test/CIR/CodeGenBuiltins/builtin-isinf-sign.c index 91f9b4cc99e1a..428b368685a82 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtin-isinf-sign.c +++ b/clang/test/CIR/CodeGenBuiltins/builtin-isinf-sign.c @@ -8,7 +8,7 @@ int test_float_isinf_sign(float x) { // CIR-LABEL: test_float_isinf_sign // CIR: %[[ARG:.*]] = cir.load align(4) %{{.*}} : !cir.ptr<!cir.float>, !cir.float - // CIR: %[[IS_INF:.*]] = cir.is_fp_class %[[ARG]], fcInf : (!cir.float) -> !cir.bool + // CIR: %[[IS_INF:.*]] = cir.is_fp_class %[[ARG]], "fcInf" : (!cir.float) -> !cir.bool // CIR: %[[IS_NEG:.*]] = cir.signbit %[[ARG]] : !cir.float -> !cir.bool // CIR: %[[C_0:.*]] = cir.const #cir.int<0> : !s32i // CIR: %[[C_1:.*]] = cir.const #cir.int<1> : !s32i >From e848d09cb8b5ced8a88786cffca55fc27424219d Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Fri, 26 Jun 2026 07:19:15 -0700 Subject: [PATCH 2/2] [CIR] Address review on is_fp_class bit-enum Use I32BitEnumAttr directly for FPClassTestEnum instead of the CIR_I32BitEnumAttr wrapper, which only set the ::cir namespace and added nothing else; cppNamespace now lives on the enum def. The wrapper had no other users, so it is removed. Guard createIsFPClass against a mask with bits outside fcAllFlags (0-9). Sema already rejects an out-of-range __builtin_isfpclass mask (BuiltinConstantArgRange against llvm::fcAllFlags), so an extra bit reaching here would be an internal error: assert in debug builds and mask it off so lowering stays well-formed. Clarify the 1022 mask comment in builtin-isfpclass.c -- it excludes fcSNan only; fcQNan is still set. --- clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td | 6 ------ clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 ++- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 7 +++++++ clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td b/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td index f75598d6ca75c..cc6f256ddfef4 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td +++ b/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td @@ -26,12 +26,6 @@ class CIR_I64EnumAttr<string name, string summary, list<I64EnumAttrCase> cases> let cppNamespace = "::cir"; } -class CIR_I32BitEnumAttr<string name, string summary, - list<BitEnumCaseBase> cases> - : I32BitEnumAttr<name, summary, cases> { - let cppNamespace = "::cir"; -} - class CIR_EnumAttr<EnumAttrInfo info, string name = "", list<Trait> traits = []> : EnumAttr<CIR_Dialect, info, name, traits> { let assemblyFormat = "`<` $value `>`"; diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index fe212fd395059..03bd32c29c8c0 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -6464,12 +6464,13 @@ def FPClass_All : I32BitEnumAttrCaseGroup<"All", [FPClass_Nan, FPClass_Inf, FPClass_Fin], "fcAllFlags">; def FPClassTestEnum - : CIR_I32BitEnumAttr<"FPClassTest", "floating-point class test flags", [ + : I32BitEnumAttr<"FPClassTest", "floating-point class test flags", [ FPClass_None, FPClass_SNan, FPClass_QNan, FPClass_NegInf, FPClass_NegNorm, FPClass_NegSub, FPClass_NegZero, FPClass_PosZero, FPClass_PosSub, FPClass_PosNorm, FPClass_PosInf, FPClass_Nan, FPClass_Inf, FPClass_Norm, FPClass_Sub, FPClass_Zero, FPClass_PosFin, FPClass_NegFin, FPClass_Fin, FPClass_Pos, FPClass_Neg, FPClass_All]> { + let cppNamespace = "::cir"; let printBitEnumPrimaryGroups = 1; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index b8db0d9157aa6..8df84e58785a3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -561,6 +561,13 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { //===--------------------------------------------------------------------===// cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src, cir::FPClassTest flags) { + // FPClassTest occupies bits 0-9 (fcAllFlags). Sema rejects an + // out-of-range __builtin_isfpclass mask, so any extra bit here is an + // internal error; assert and mask it off so lowering stays well-formed. + uint32_t raw = static_cast<uint32_t>(flags); + uint32_t all = static_cast<uint32_t>(cir::FPClassTest::All); + assert((raw & ~all) == 0 && "FPClassTest mask has bits outside 0-9"); + flags = static_cast<cir::FPClassTest>(raw & all); return cir::IsFPClassOp::create(*this, loc, src, flags); } diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c b/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c index 0c71733780cee..d45a1c4ac179e 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c +++ b/clang/test/CIR/CodeGenBuiltins/builtin-isfpclass.c @@ -125,7 +125,7 @@ _Bool check_isfpclass_snan_neginf(double x) { // OGCG: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 5) _Bool check_isfpclass_multi(float x) { - return __builtin_isfpclass(x, 1022 /*all but fcSNan*/); + return __builtin_isfpclass(x, 1022 /*all but fcSNan (fcQNan still set)*/); } // CIR: cir.func {{.*}}@check_isfpclass_multi _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
