Skip to content

Commit 1278d47

Browse files
authored
[CIR] Upstream isfpclass op (llvm#166037)
Ref commit in incubator: ee17ff6 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 llvm#163892
1 parent 2aa2290 commit 1278d47

File tree

6 files changed

+350
-0
lines changed

6 files changed

+350
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4112,6 +4112,72 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
41124112
let hasFolder = 1;
41134113
}
41144114

4115+
//===----------------------------------------------------------------------===//
4116+
// FPClass Test Flags
4117+
//===----------------------------------------------------------------------===//
4118+
4119+
def FPClassTestEnum : CIR_I32EnumAttr<"FPClassTest", "floating-point class test flags", [
4120+
// Basic flags
4121+
I32EnumAttrCase<"SignalingNaN", 1, "fcSNan">,
4122+
I32EnumAttrCase<"QuietNaN", 2, "fcQNan">,
4123+
I32EnumAttrCase<"NegativeInfinity", 4, "fcNegInf">,
4124+
I32EnumAttrCase<"NegativeNormal", 8, "fcNegNormal">,
4125+
I32EnumAttrCase<"NegativeSubnormal", 16, "fcNegSubnormal">,
4126+
I32EnumAttrCase<"NegativeZero", 32, "fcNegZero">,
4127+
I32EnumAttrCase<"PositiveZero", 64, "fcPosZero">,
4128+
I32EnumAttrCase<"PositiveSubnormal", 128, "fcPosSubnormal">,
4129+
I32EnumAttrCase<"PositiveNormal", 256, "fcPosNormal">,
4130+
I32EnumAttrCase<"PositiveInfinity", 512, "fcPosInf">,
4131+
4132+
// Composite flags
4133+
I32EnumAttrCase<"Nan", 3, "fcNan">, // fcSNan | fcQNan
4134+
I32EnumAttrCase<"Infinity", 516, "fcInf">, // fcPosInf | fcNegInf
4135+
I32EnumAttrCase<"Normal", 264, "fcNormal">, // fcPosNormal | fcNegNormal
4136+
I32EnumAttrCase<"Subnormal", 144, "fcSubnormal">, // fcPosSubnormal | fcNegSubnormal
4137+
I32EnumAttrCase<"Zero", 96, "fcZero">, // fcPosZero | fcNegZero
4138+
I32EnumAttrCase<"PositiveFinite", 448, "fcPosFinite">,// fcPosNormal | fcPosSubnormal | fcPosZero
4139+
I32EnumAttrCase<"NegativeFinite", 56, "fcNegFinite">, // fcNegNormal | fcNegSubnormal | fcNegZero
4140+
I32EnumAttrCase<"Finite", 504, "fcFinite">, // fcPosFinite | fcNegFinite
4141+
I32EnumAttrCase<"Positive", 960, "fcPositive">, // fcPosFinite | fcPosInf
4142+
I32EnumAttrCase<"Negative", 60, "fcNegative">, // fcNegFinite | fcNegInf
4143+
I32EnumAttrCase<"All", 1023, "fcAllFlags">, // fcNan | fcInf | fcFinite
4144+
]> {
4145+
let cppNamespace = "::cir";
4146+
}
4147+
4148+
def CIR_IsFPClassOp : CIR_Op<"is_fp_class"> {
4149+
let summary = "Corresponding to the `__builtin_fpclassify` builtin function in clang";
4150+
4151+
let description = [{
4152+
The `cir.is_fp_class` operation takes a floating-point value as its first
4153+
argument and a bitfield of flags as its second argument. The operation
4154+
returns a boolean value indicating whether the floating-point value
4155+
satisfies the given flags.
4156+
4157+
The flags must be a compile time constant and the values are:
4158+
4159+
| Bit # | floating-point class |
4160+
| ----- | -------------------- |
4161+
| 0 | Signaling NaN |
4162+
| 1 | Quiet NaN |
4163+
| 2 | Negative infinity |
4164+
| 3 | Negative normal |
4165+
| 4 | Negative subnormal |
4166+
| 5 | Negative zero |
4167+
| 6 | Positive zero |
4168+
| 7 | Positive subnormal |
4169+
| 8 | Positive normal |
4170+
| 9 | Positive infinity |
4171+
}];
4172+
4173+
let arguments = (ins CIR_AnyFloatType:$src,
4174+
FPClassTestEnum:$flags);
4175+
let results = (outs CIR_BoolType:$result);
4176+
let assemblyFormat = [{
4177+
$src `,` $flags `:` functional-type($src, $result) attr-dict
4178+
}];
4179+
}
4180+
41154181
//===----------------------------------------------------------------------===//
41164182
// Assume Operations
41174183
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ struct MissingFeatures {
266266
static bool emitTypeCheck() { return false; }
267267
static bool emitTypeMetadataCodeForVCall() { return false; }
268268
static bool fastMathFlags() { return false; }
269+
269270
static bool fpConstraints() { return false; }
270271
static bool generateDebugInfo() { return false; }
271272
static bool globalViewIndices() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
344344
llvm_unreachable("negation for the given type is NYI");
345345
}
346346

347+
cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src,
348+
cir::FPClassTest flags) {
349+
return cir::IsFPClassOp::create(*this, loc, src, flags);
350+
}
351+
347352
// TODO: split this to createFPExt/createFPTrunc when we have dedicated cast
348353
// operations.
349354
mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) {

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,98 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
520520
cir::PrefetchOp::create(builder, loc, address, locality, isWrite);
521521
return RValue::get(nullptr);
522522
}
523+
// From https://clang.llvm.org/docs/LanguageExtensions.html#builtin-isfpclass
524+
// :
525+
//
526+
// The `__builtin_isfpclass()` builtin is a generalization of functions
527+
// isnan, isinf, isfinite and some others defined by the C standard. It tests
528+
// if the floating-point value, specified by the first argument, falls into
529+
// any of data classes, specified by the second argument.
530+
case Builtin::BI__builtin_isnan: {
531+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
532+
mlir::Value v = emitScalarExpr(e->getArg(0));
533+
assert(!cir::MissingFeatures::fpConstraints());
534+
mlir::Location loc = getLoc(e->getBeginLoc());
535+
return RValue::get(builder.createBoolToInt(
536+
builder.createIsFPClass(loc, v, cir::FPClassTest::Nan),
537+
convertType(e->getType())));
538+
}
539+
540+
case Builtin::BI__builtin_issignaling: {
541+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
542+
mlir::Value v = emitScalarExpr(e->getArg(0));
543+
mlir::Location loc = getLoc(e->getBeginLoc());
544+
return RValue::get(builder.createBoolToInt(
545+
builder.createIsFPClass(loc, v, cir::FPClassTest::SignalingNaN),
546+
convertType(e->getType())));
547+
}
548+
549+
case Builtin::BI__builtin_isinf: {
550+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
551+
mlir::Value v = emitScalarExpr(e->getArg(0));
552+
assert(!cir::MissingFeatures::fpConstraints());
553+
mlir::Location loc = getLoc(e->getBeginLoc());
554+
return RValue::get(builder.createBoolToInt(
555+
builder.createIsFPClass(loc, v, cir::FPClassTest::Infinity),
556+
convertType(e->getType())));
557+
}
558+
559+
case Builtin::BIfinite:
560+
case Builtin::BI__finite:
561+
case Builtin::BIfinitef:
562+
case Builtin::BI__finitef:
563+
case Builtin::BIfinitel:
564+
case Builtin::BI__finitel:
565+
case Builtin::BI__builtin_isfinite: {
566+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
567+
mlir::Value v = emitScalarExpr(e->getArg(0));
568+
assert(!cir::MissingFeatures::fpConstraints());
569+
mlir::Location loc = getLoc(e->getBeginLoc());
570+
return RValue::get(builder.createBoolToInt(
571+
builder.createIsFPClass(loc, v, cir::FPClassTest::Finite),
572+
convertType(e->getType())));
573+
}
574+
575+
case Builtin::BI__builtin_isnormal: {
576+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
577+
mlir::Value v = emitScalarExpr(e->getArg(0));
578+
mlir::Location loc = getLoc(e->getBeginLoc());
579+
return RValue::get(builder.createBoolToInt(
580+
builder.createIsFPClass(loc, v, cir::FPClassTest::Normal),
581+
convertType(e->getType())));
582+
}
583+
584+
case Builtin::BI__builtin_issubnormal: {
585+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
586+
mlir::Value v = emitScalarExpr(e->getArg(0));
587+
mlir::Location loc = getLoc(e->getBeginLoc());
588+
return RValue::get(builder.createBoolToInt(
589+
builder.createIsFPClass(loc, v, cir::FPClassTest::Subnormal),
590+
convertType(e->getType())));
591+
}
592+
593+
case Builtin::BI__builtin_iszero: {
594+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
595+
mlir::Value v = emitScalarExpr(e->getArg(0));
596+
mlir::Location loc = getLoc(e->getBeginLoc());
597+
return RValue::get(builder.createBoolToInt(
598+
builder.createIsFPClass(loc, v, cir::FPClassTest::Zero),
599+
convertType(e->getType())));
600+
}
601+
case Builtin::BI__builtin_isfpclass: {
602+
Expr::EvalResult result;
603+
if (!e->getArg(1)->EvaluateAsInt(result, cgm.getASTContext()))
604+
break;
605+
606+
assert(!cir::MissingFeatures::cgFPOptionsRAII());
607+
mlir::Value v = emitScalarExpr(e->getArg(0));
608+
uint64_t test = result.Val.getInt().getLimitedValue();
609+
mlir::Location loc = getLoc(e->getBeginLoc());
610+
//
611+
return RValue::get(builder.createBoolToInt(
612+
builder.createIsFPClass(loc, v, cir::FPClassTest(test)),
613+
convertType(e->getType())));
614+
}
523615
}
524616

525617
// If this is an alias for a lib function (e.g. __builtin_sin), emit

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,18 @@ mlir::LogicalResult CIRToLLVMASinOpLowering::matchAndRewrite(
676676
return mlir::success();
677677
}
678678

679+
mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite(
680+
cir::IsFPClassOp op, OpAdaptor adaptor,
681+
mlir::ConversionPatternRewriter &rewriter) const {
682+
mlir::Value src = adaptor.getSrc();
683+
cir::FPClassTest flags = adaptor.getFlags();
684+
mlir::IntegerType retTy = rewriter.getI1Type();
685+
686+
rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>(
687+
op, retTy, src, static_cast<uint32_t>(flags));
688+
return mlir::success();
689+
}
690+
679691
mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite(
680692
cir::AssumeOp op, OpAdaptor adaptor,
681693
mlir::ConversionPatternRewriter &rewriter) const {
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir
4+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.cir
6+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=OGCG
7+
int finite(double);
8+
9+
// CHECK: cir.func {{.*}}@test_is_finite
10+
void test_is_finite(__fp16 *H, float F, double D, long double LD) {
11+
volatile int res;
12+
res = __builtin_isinf(*H);
13+
// CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.f16) -> !cir.bool
14+
// LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 516)
15+
// OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 516)
16+
17+
res = __builtin_isinf(F);
18+
// CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.float) -> !cir.bool
19+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 516)
20+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 516)
21+
22+
res = __builtin_isinf(D);
23+
// CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.double) -> !cir.bool
24+
// LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 516)
25+
// OGCG: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 516)
26+
27+
res = __builtin_isinf(LD);
28+
// CIR: cir.is_fp_class %{{.*}}, fcInf : (!cir.long_double<!cir.f80>) -> !cir.bool
29+
// LLVM: call i1 @llvm.is.fpclass.f80(x86_fp80 {{.*}}, i32 516)
30+
// OGCG: call i1 @llvm.is.fpclass.f80(x86_fp80 {{.*}}, i32 516)
31+
32+
res = __builtin_isfinite(*H);
33+
// CIR: cir.is_fp_class %{{.*}}, fcFinite : (!cir.f16) -> !cir.bool
34+
// LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 504)
35+
// OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 504)
36+
37+
res = __builtin_isfinite(F);
38+
// CIR: cir.is_fp_class %{{.*}}, fcFinite : (!cir.float) -> !cir.bool
39+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504)
40+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504)
41+
42+
res = finite(D);
43+
// CIR: cir.call @finite(%{{.*}}) nothrow side_effect(const) : (!cir.double) -> !s32i
44+
// LLVM: call i32 @finite(double {{.*}})
45+
// OGCG: call i1 @llvm.is.fpclass.f64(double %20, i32 504)
46+
res = __builtin_isnormal(*H);
47+
// CIR: cir.is_fp_class %{{.*}}, fcNormal : (!cir.f16) -> !cir.bool
48+
// LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 264)
49+
// OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 264)
50+
51+
res = __builtin_isnormal(F);
52+
// CIR: cir.is_fp_class %{{.*}}, fcNormal : (!cir.float) -> !cir.bool
53+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 264)
54+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 264)
55+
56+
res = __builtin_issubnormal(F);
57+
// CIR: cir.is_fp_class %{{.*}}, fcSubnormal : (!cir.float) -> !cir.bool
58+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 144)
59+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 144)
60+
res = __builtin_iszero(F);
61+
// CIR: cir.is_fp_class %{{.*}}, fcZero : (!cir.float) -> !cir.bool
62+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 96)
63+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 96)
64+
res = __builtin_issignaling(F);
65+
// CIR: cir.is_fp_class %{{.*}}, fcSNan : (!cir.float) -> !cir.bool
66+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1)
67+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1)
68+
}
69+
70+
_Bool check_isfpclass_finite(float x) {
71+
return __builtin_isfpclass(x, 504 /*Finite*/);
72+
}
73+
74+
// CIR: cir.func {{.*}}@check_isfpclass_finite
75+
// CIR: cir.is_fp_class %{{.*}}, fcFinite : (!cir.float)
76+
// LLVM: @check_isfpclass_finite
77+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504)
78+
// OGCG: @check_isfpclass_finite
79+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504)
80+
81+
_Bool check_isfpclass_nan_f32(float x) {
82+
return __builtin_isfpclass(x, 3 /*NaN*/);
83+
}
84+
85+
// CIR: cir.func {{.*}}@check_isfpclass_nan_f32
86+
// CIR: cir.is_fp_class %{{.*}}, fcNan : (!cir.float)
87+
// LLVM: @check_isfpclass_nan_f32
88+
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 3)
89+
// OGCG: @check_isfpclass_nan_f32
90+
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 3)
91+
92+
93+
_Bool check_isfpclass_snan_f64(double x) {
94+
return __builtin_isfpclass(x, 1 /*SNaN*/);
95+
}
96+
97+
// CIR: cir.func {{.*}}@check_isfpclass_snan_f64
98+
// CIR: cir.is_fp_class %{{.*}}, fcSNan : (!cir.double)
99+
// LLVM: @check_isfpclass_snan_f64
100+
// LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 1)
101+
// OGCG: @check_isfpclass_snan_f64
102+
// OGCG: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 1)
103+
104+
105+
_Bool check_isfpclass_zero_f16(_Float16 x) {
106+
return __builtin_isfpclass(x, 96 /*Zero*/);
107+
}
108+
109+
// CIR: cir.func {{.*}}@check_isfpclass_zero_f16
110+
// CIR: cir.is_fp_class %{{.*}}, fcZero : (!cir.f16)
111+
// LLVM: @check_isfpclass_zero_f16
112+
// LLVM: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 96)
113+
// OGCG: @check_isfpclass_zero_f16
114+
// OGCG: call i1 @llvm.is.fpclass.f16(half {{.*}}, i32 96)
115+
116+
// Update when we support FP pragma in functions and can convert BoolType in prvalue to i1.
117+
118+
// _Bool check_isfpclass_finite_strict(float x) {
119+
// #pragma STDC FENV_ACCESS ON
120+
// return __builtin_isfpclass(x, 504 /*Finite*/);
121+
// }
122+
//
123+
// _Bool check_isfpclass_nan_f32_strict(float x) {
124+
// #pragma STDC FENV_ACCESS ON
125+
// return __builtin_isfpclass(x, 3 /*NaN*/);
126+
// }
127+
//
128+
// _Bool check_isfpclass_snan_f64_strict(double x) {
129+
// #pragma STDC FENV_ACCESS ON
130+
// return __builtin_isfpclass(x, 1 /*NaN*/);
131+
// }
132+
//
133+
// _Bool check_isfpclass_zero_f16_strict(_Float16 x) {
134+
// #pragma STDC FENV_ACCESS ON
135+
// return __builtin_isfpclass(x, 96 /*Zero*/);
136+
// }
137+
//
138+
// _Bool check_isnan(float x) {
139+
// #pragma STDC FENV_ACCESS ON
140+
// return __builtin_isnan(x);
141+
// }
142+
//
143+
// _Bool check_isinf(float x) {
144+
// #pragma STDC FENV_ACCESS ON
145+
// return __builtin_isinf(x);
146+
// }
147+
//
148+
// _Bool check_isfinite(float x) {
149+
// #pragma STDC FENV_ACCESS ON
150+
// return __builtin_isfinite(x);
151+
// }
152+
//
153+
// _Bool check_isnormal(float x) {
154+
// #pragma STDC FENV_ACCESS ON
155+
// return __builtin_isnormal(x);
156+
// }
157+
//
158+
// typedef float __attribute__((ext_vector_type(4))) float4;
159+
// typedef double __attribute__((ext_vector_type(4))) double4;
160+
// typedef int __attribute__((ext_vector_type(4))) int4;
161+
// typedef long __attribute__((ext_vector_type(4))) long4;
162+
//
163+
// int4 check_isfpclass_nan_v4f32(float4 x) {
164+
// return __builtin_isfpclass(x, 3 /*NaN*/);
165+
// }
166+
//
167+
// int4 check_isfpclass_nan_strict_v4f32(float4 x) {
168+
// #pragma STDC FENV_ACCESS ON
169+
// return __builtin_isfpclass(x, 3 /*NaN*/);
170+
// }
171+
//
172+
// long4 check_isfpclass_nan_v4f64(double4 x) {
173+
// return __builtin_isfpclass(x, 3 /*NaN*/);
174+
// }

0 commit comments

Comments
 (0)