Skip to content

Commit f50ca24

Browse files
authoredOct 21, 2024··
[CIR] [CodeGen] Introduce IsFPClassOp to support builtin_isfpclass (#971)
The llvm's intrinsic `llvm.is.fpclass` is used to support multiple float point builtins: 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. I meant to support this by creating IntrinsicCallOp directly. But I can't make it due to #480 since the return type of the intrinsic will mismatch. So I have to create a new Op for it. But I feel it might not be too bad. At least it is more explicit and more expressive.

File tree

8 files changed

+447
-35
lines changed

8 files changed

+447
-35
lines changed
 

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

+33
Original file line numberDiff line numberDiff line change
@@ -4027,6 +4027,39 @@ def FMinOp : BinaryFPToFPBuiltinOp<"fmin", "MinNumOp">;
40274027
def FModOp : BinaryFPToFPBuiltinOp<"fmod", "FRemOp">;
40284028
def PowOp : BinaryFPToFPBuiltinOp<"pow", "PowOp">;
40294029

4030+
def IsFPClassOp : CIR_Op<"is_fp_class"> {
4031+
let summary = "Corresponding to the `__builtin_fpclassify` builtin function in clang";
4032+
4033+
let description = [{
4034+
The `cir.is_fp_class` operation takes a floating-point value as its first
4035+
argument and a bitfield of flags as its second argument. The operation
4036+
returns a boolean value indicating whether the floating-point value
4037+
satisfies the given flags.
4038+
4039+
The flags must be a compile time constant and the values are:
4040+
4041+
| Bit # | floating-point class |
4042+
| -------- | ------- |
4043+
| 0 | Signaling NaN |
4044+
| 1 | Quiet NaN |
4045+
| 2 | Negative infinity |
4046+
| 3 | Negative normal |
4047+
| 4 | Negative subnormal |
4048+
| 5 | Negative zero |
4049+
| 6 | Positive zero |
4050+
| 7 | Positive subnormal |
4051+
| 8 | Positive normal |
4052+
| 9 | Positive infinity |
4053+
}];
4054+
4055+
let arguments = (ins CIR_AnyFloat:$src,
4056+
I32Attr:$flags);
4057+
let results = (outs CIR_BoolType:$result);
4058+
let assemblyFormat = [{
4059+
$src `,` $flags `:` functional-type($src, $result) attr-dict
4060+
}];
4061+
}
4062+
40304063
//===----------------------------------------------------------------------===//
40314064
// Assume Operations
40324065
//===----------------------------------------------------------------------===//

‎clang/include/clang/CIR/Dialect/IR/CIRTypes.td

+2-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ def CIR_LongDouble : CIR_FloatType<"LongDouble", "long_double"> {
202202

203203
// Constraints
204204

205-
def CIR_AnyFloat: AnyTypeOf<[CIR_Single, CIR_Double, CIR_FP80, CIR_FP128, CIR_LongDouble]>;
205+
def CIR_AnyFloat: AnyTypeOf<[CIR_Single, CIR_Double, CIR_FP80, CIR_FP128, CIR_LongDouble,
206+
CIR_FP16, CIR_BFloat16]>;
206207
def CIR_AnyIntOrFloat: AnyTypeOf<[CIR_AnyFloat, CIR_IntType]>;
207208

208209
//===----------------------------------------------------------------------===//

‎clang/lib/CIR/CodeGen/CIRGenBuilder.h

+5
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,11 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
584584
getAttr<mlir::cir::FPAttr>(t, fpVal));
585585
}
586586

587+
mlir::cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src,
588+
unsigned flags) {
589+
return create<mlir::cir::IsFPClassOp>(loc, src, flags);
590+
}
591+
587592
/// Create constant nullptr for pointer-to-data-member type ty.
588593
mlir::cir::ConstantOp getNullDataMemberPtr(mlir::cir::DataMemberType ty,
589594
mlir::Location loc) {

‎clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

+115-33
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ static RValue buildLibraryCall(CIRGenFunction &CGF, const FunctionDecl *FD,
4646
return CGF.buildCall(E->getCallee()->getType(), callee, E, ReturnValueSlot());
4747
}
4848

49+
static mlir::Value tryUseTestFPKind(CIRGenFunction &CGF, unsigned BuiltinID,
50+
mlir::Value V) {
51+
if (CGF.getBuilder().getIsFPConstrained() &&
52+
CGF.getBuilder().getDefaultConstrainedExcept() != fp::ebIgnore) {
53+
if (mlir::Value Result = CGF.getTargetHooks().testFPKind(
54+
V, BuiltinID, CGF.getBuilder(), CGF.CGM))
55+
return Result;
56+
}
57+
return nullptr;
58+
}
59+
4960
template <class Operation>
5061
static RValue buildUnaryFPBuiltin(CIRGenFunction &CGF, const CallExpr &E) {
5162
auto Arg = CGF.buildScalarExpr(E.getArg(0));
@@ -1191,36 +1202,6 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
11911202
case Builtin::BI__builtin_isunordered:
11921203
llvm_unreachable("BI__builtin_isgreater and BI__builtin_isless like NYI");
11931204

1194-
case Builtin::BI__builtin_isnan:
1195-
llvm_unreachable("BI__builtin_isnan NYI");
1196-
1197-
case Builtin::BI__builtin_issignaling:
1198-
llvm_unreachable("BI__builtin_issignaling NYI");
1199-
1200-
case Builtin::BI__builtin_isinf:
1201-
llvm_unreachable("BI__builtin_isinf NYI");
1202-
1203-
case Builtin::BIfinite:
1204-
case Builtin::BI__finite:
1205-
case Builtin::BIfinitef:
1206-
case Builtin::BI__finitef:
1207-
case Builtin::BIfinitel:
1208-
case Builtin::BI__finitel:
1209-
case Builtin::BI__builtin_isfinite:
1210-
llvm_unreachable("Builtin::BIfinite like NYI");
1211-
1212-
case Builtin::BI__builtin_isnormal:
1213-
llvm_unreachable("BI__builtin_isnormal NYI");
1214-
1215-
case Builtin::BI__builtin_issubnormal:
1216-
llvm_unreachable("BI__builtin_issubnormal NYI");
1217-
1218-
case Builtin::BI__builtin_iszero:
1219-
llvm_unreachable("BI__builtin_iszero NYI");
1220-
1221-
case Builtin::BI__builtin_isfpclass:
1222-
llvm_unreachable("BI__builtin_isfpclass NYI");
1223-
12241205
case Builtin::BI__builtin_nondeterministic_value:
12251206
llvm_unreachable("BI__builtin_nondeterministic_value NYI");
12261207

@@ -1328,9 +1309,6 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
13281309
case Builtin::BI__builtin_matrix_column_major_store:
13291310
llvm_unreachable("BI__builtin_matrix_column_major_store NYI");
13301311

1331-
case Builtin::BI__builtin_isinf_sign:
1332-
llvm_unreachable("BI__builtin_isinf_sign NYI");
1333-
13341312
case Builtin::BI__builtin_flt_rounds:
13351313
llvm_unreachable("BI__builtin_flt_rounds NYI");
13361314

@@ -2080,6 +2058,110 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
20802058
llvm_unreachable("BI__builtin_ms_va_copy NYI");
20812059
case Builtin::BI__builtin_get_device_side_mangled_name:
20822060
llvm_unreachable("BI__builtin_get_device_side_mangled_name NYI");
2061+
2062+
// From https://clang.llvm.org/docs/LanguageExtensions.html#builtin-isfpclass
2063+
// :
2064+
//
2065+
// The `__builtin_isfpclass()` builtin is a generalization of functions
2066+
// isnan, isinf, isfinite and some others defined by the C standard. It tests
2067+
// if the floating-point value, specified by the first argument, falls into
2068+
// any of data classes, specified by the second argument.
2069+
case Builtin::BI__builtin_isnan: {
2070+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2071+
mlir::Value V = buildScalarExpr(E->getArg(0));
2072+
if (mlir::Value Result = tryUseTestFPKind(*this, BuiltinID, V))
2073+
return RValue::get(Result);
2074+
mlir::Location Loc = getLoc(E->getBeginLoc());
2075+
// FIXME: We should use builder.createZExt once createZExt is available.
2076+
return RValue::get(builder.createZExtOrBitCast(
2077+
Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcNan),
2078+
ConvertType(E->getType())));
2079+
}
2080+
2081+
case Builtin::BI__builtin_issignaling: {
2082+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2083+
mlir::Value V = buildScalarExpr(E->getArg(0));
2084+
mlir::Location Loc = getLoc(E->getBeginLoc());
2085+
// FIXME: We should use builder.createZExt once createZExt is available.
2086+
return RValue::get(builder.createZExtOrBitCast(
2087+
Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcSNan),
2088+
ConvertType(E->getType())));
2089+
}
2090+
2091+
case Builtin::BI__builtin_isinf: {
2092+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2093+
mlir::Value V = buildScalarExpr(E->getArg(0));
2094+
if (mlir::Value Result = tryUseTestFPKind(*this, BuiltinID, V))
2095+
return RValue::get(Result);
2096+
mlir::Location Loc = getLoc(E->getBeginLoc());
2097+
// FIXME: We should use builder.createZExt once createZExt is available.
2098+
return RValue::get(builder.createZExtOrBitCast(
2099+
Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcInf),
2100+
ConvertType(E->getType())));
2101+
}
2102+
2103+
case Builtin::BIfinite:
2104+
case Builtin::BI__finite:
2105+
case Builtin::BIfinitef:
2106+
case Builtin::BI__finitef:
2107+
case Builtin::BIfinitel:
2108+
case Builtin::BI__finitel:
2109+
case Builtin::BI__builtin_isfinite: {
2110+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2111+
mlir::Value V = buildScalarExpr(E->getArg(0));
2112+
if (mlir::Value Result = tryUseTestFPKind(*this, BuiltinID, V))
2113+
return RValue::get(Result);
2114+
mlir::Location Loc = getLoc(E->getBeginLoc());
2115+
// FIXME: We should use builder.createZExt once createZExt is available.
2116+
return RValue::get(builder.createZExtOrBitCast(
2117+
Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcFinite),
2118+
ConvertType(E->getType())));
2119+
}
2120+
2121+
case Builtin::BI__builtin_isnormal: {
2122+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2123+
mlir::Value V = buildScalarExpr(E->getArg(0));
2124+
mlir::Location Loc = getLoc(E->getBeginLoc());
2125+
// FIXME: We should use builder.createZExt once createZExt is available.
2126+
return RValue::get(builder.createZExtOrBitCast(
2127+
Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcNormal),
2128+
ConvertType(E->getType())));
2129+
}
2130+
2131+
case Builtin::BI__builtin_issubnormal: {
2132+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2133+
mlir::Value V = buildScalarExpr(E->getArg(0));
2134+
mlir::Location Loc = getLoc(E->getBeginLoc());
2135+
// FIXME: We should use builder.createZExt once createZExt is available.
2136+
return RValue::get(builder.createZExtOrBitCast(
2137+
Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcSubnormal),
2138+
ConvertType(E->getType())));
2139+
}
2140+
2141+
case Builtin::BI__builtin_iszero: {
2142+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2143+
mlir::Value V = buildScalarExpr(E->getArg(0));
2144+
mlir::Location Loc = getLoc(E->getBeginLoc());
2145+
// FIXME: We should use builder.createZExt once createZExt is available.
2146+
return RValue::get(builder.createZExtOrBitCast(
2147+
Loc, builder.createIsFPClass(Loc, V, FPClassTest::fcZero),
2148+
ConvertType(E->getType())));
2149+
}
2150+
2151+
case Builtin::BI__builtin_isfpclass: {
2152+
Expr::EvalResult Result;
2153+
if (!E->getArg(1)->EvaluateAsInt(Result, CGM.getASTContext()))
2154+
break;
2155+
2156+
CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, E);
2157+
mlir::Value V = buildScalarExpr(E->getArg(0));
2158+
uint64_t Test = Result.Val.getInt().getLimitedValue();
2159+
mlir::Location Loc = getLoc(E->getBeginLoc());
2160+
2161+
// FIXME: We should use builder.createZExt once createZExt is available.
2162+
return RValue::get(builder.createZExtOrBitCast(
2163+
Loc, builder.createIsFPClass(Loc, V, Test), ConvertType(E->getType())));
2164+
}
20832165
}
20842166

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

‎clang/lib/CIR/CodeGen/TargetInfo.h

+10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace cir {
2525

2626
class CIRGenFunction;
2727
class CIRGenModule;
28+
class CIRGenBuilderTy;
2829

2930
/// This class organizes various target-specific codegeneration issues, like
3031
/// target-specific attributes, builtins and so on.
@@ -43,6 +44,15 @@ class TargetCIRGenInfo {
4344
return false;
4445
}
4546

47+
/// Performs a target specific test of a floating point value for things
48+
/// like IsNaN, Infinity, ... Nullptr is returned if no implementation
49+
/// exists.
50+
virtual mlir::Value testFPKind(mlir::Value V, unsigned BuiltinID,
51+
CIRGenBuilderTy &Builder,
52+
CIRGenModule &CGM) const {
53+
return {};
54+
}
55+
4656
/// Corrects the MLIR type for a given constraint and "usual"
4757
/// type.
4858
///

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

+28-1
Original file line numberDiff line numberDiff line change
@@ -4119,6 +4119,33 @@ class CIRThrowOpLowering
41194119
}
41204120
};
41214121

4122+
class CIRIsFPClassOpLowering
4123+
: public mlir::OpConversionPattern<mlir::cir::IsFPClassOp> {
4124+
public:
4125+
using OpConversionPattern<mlir::cir::IsFPClassOp>::OpConversionPattern;
4126+
4127+
mlir::LogicalResult
4128+
matchAndRewrite(mlir::cir::IsFPClassOp op, OpAdaptor adaptor,
4129+
mlir::ConversionPatternRewriter &rewriter) const override {
4130+
auto src = adaptor.getSrc();
4131+
auto flags = adaptor.getFlags();
4132+
auto retTy = rewriter.getI1Type();
4133+
4134+
auto loc = op->getLoc();
4135+
4136+
auto intrinsic =
4137+
rewriter.create<mlir::LLVM::IsFPClass>(loc, retTy, src, flags);
4138+
// FIMXE: CIR now will convert cir::BoolType to i8 type unconditionally.
4139+
// Remove this conversion after we fix
4140+
// https://github.com/llvm/clangir/issues/480
4141+
auto converted = rewriter.create<mlir::LLVM::ZExtOp>(
4142+
loc, rewriter.getI8Type(), intrinsic->getResult(0));
4143+
4144+
rewriter.replaceOp(op, converted);
4145+
return mlir::success();
4146+
}
4147+
};
4148+
41224149
void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns,
41234150
mlir::TypeConverter &converter,
41244151
mlir::DataLayout &dataLayout) {
@@ -4151,7 +4178,7 @@ void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns,
41514178
CIREhTypeIdOpLowering, CIRCatchParamOpLowering, CIRResumeOpLowering,
41524179
CIRAllocExceptionOpLowering, CIRFreeExceptionOpLowering,
41534180
CIRThrowOpLowering, CIRIntrinsicCallLowering, CIRBaseClassAddrOpLowering,
4154-
CIRVTTAddrPointOpLowering
4181+
CIRVTTAddrPointOpLowering, CIRIsFPClassOpLowering
41554182
#define GET_BUILTIN_LOWERING_LIST
41564183
#include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc"
41574184
#undef GET_BUILTIN_LOWERING_LIST

0 commit comments

Comments
 (0)
Please sign in to comment.