Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR] [CodeGen] Introduce IsFPClassOp to support builtin_isfpclass #971

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;

//===----------------------------------------------------------------------===//
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,11 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
getAttr<mlir::cir::FPAttr>(t, fpVal));
}

mlir::cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src,
unsigned flags) {
return create<mlir::cir::IsFPClassOp>(loc, src, flags);
}

/// Create constant nullptr for pointer-to-data-member type ty.
mlir::cir::ConstantOp getNullDataMemberPtr(mlir::cir::DataMemberType ty,
mlir::Location loc) {
Expand Down
148 changes: 115 additions & 33 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class Operation>
static RValue buildUnaryFPBuiltin(CIRGenFunction &CGF, const CallExpr &E) {
auto Arg = CGF.buildScalarExpr(E.getArg(0));
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CIR/CodeGen/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
///
Expand Down
29 changes: 28 additions & 1 deletion clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4119,6 +4119,33 @@ class CIRThrowOpLowering
}
};

class CIRIsFPClassOpLowering
: public mlir::OpConversionPattern<mlir::cir::IsFPClassOp> {
public:
using OpConversionPattern<mlir::cir::IsFPClassOp>::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<mlir::LLVM::IsFPClass>(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<mlir::LLVM::ZExtOp>(
loc, rewriter.getI8Type(), intrinsic->getResult(0));

rewriter.replaceOp(op, converted);
return mlir::success();
}
};

void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns,
mlir::TypeConverter &converter,
mlir::DataLayout &dataLayout) {
Expand Down Expand Up @@ -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
Expand Down
Loading