Skip to content

Commit dfa4cd4

Browse files
committed
Initial implementation for isfpclass and related builtins
Add lowering test
1 parent f5e2c5d commit dfa4cd4

File tree

14 files changed

+701
-1
lines changed

14 files changed

+701
-1
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
442442
return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy());
443443
}
444444

445+
// TODO(cir): the following function was introduced to keep in sync with LLVM
446+
// codegen. CIR does not have "zext" operations. It should eventually be
447+
// renamed or removed. For now, we just add whatever cast is required here.
448+
mlir::Value createZExtOrBitCast(mlir::Location loc, mlir::Value src,
449+
mlir::Type newTy) {
450+
auto srcTy = src.getType();
451+
452+
if (srcTy == newTy)
453+
return src;
454+
455+
if (mlir::isa<cir::BoolType>(srcTy) && mlir::isa<cir::IntType>(newTy))
456+
return createBoolToInt(src, newTy);
457+
458+
llvm_unreachable("unhandled extension cast");
459+
}
460+
445461
mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) {
446462
return createCast(cir::CastKind::bool_to_int, src, newTy);
447463
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3921,6 +3921,39 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
39213921
let hasFolder = 1;
39223922
}
39233923

3924+
def CIR_IsFPClassOp : CIR_Op<"is_fp_class"> {
3925+
let summary = "Corresponding to the `__builtin_fpclassify` builtin function in clang";
3926+
3927+
let description = [{
3928+
The `cir.is_fp_class` operation takes a floating-point value as its first
3929+
argument and a bitfield of flags as its second argument. The operation
3930+
returns a boolean value indicating whether the floating-point value
3931+
satisfies the given flags.
3932+
3933+
The flags must be a compile time constant and the values are:
3934+
3935+
| Bit # | floating-point class |
3936+
| -------- | ------- |
3937+
| 0 | Signaling NaN |
3938+
| 1 | Quiet NaN |
3939+
| 2 | Negative infinity |
3940+
| 3 | Negative normal |
3941+
| 4 | Negative subnormal |
3942+
| 5 | Negative zero |
3943+
| 6 | Positive zero |
3944+
| 7 | Positive subnormal |
3945+
| 8 | Positive normal |
3946+
| 9 | Positive infinity |
3947+
}];
3948+
3949+
let arguments = (ins CIR_AnyFloatType:$src,
3950+
I32Attr:$flags);
3951+
let results = (outs CIR_BoolType:$result);
3952+
let assemblyFormat = [{
3953+
$src `,` $flags `:` functional-type($src, $result) attr-dict
3954+
}];
3955+
}
3956+
39243957
//===----------------------------------------------------------------------===//
39253958
// Assume Operations
39263959
//===----------------------------------------------------------------------===//
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===- FPEnv.h ---- FP Environment ------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
/// @file
10+
/// This file contains the declarations of entities that describe floating
11+
/// point environment and related functions.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef CLANG_CIR_DIALECT_IR_FPENV_H
16+
#define CLANG_CIR_DIALECT_IR_FPENV_H
17+
18+
#include "llvm/ADT/FloatingPointMode.h"
19+
20+
#include <optional>
21+
22+
namespace cir {
23+
24+
namespace fp {
25+
26+
/// Exception behavior used for floating point operations.
27+
///
28+
/// Each of these values corresponds to some LLVMIR metadata argument value of a
29+
/// constrained floating point intrinsic. See the LLVM Language Reference Manual
30+
/// for details.
31+
enum ExceptionBehavior : uint8_t {
32+
ebIgnore, ///< This corresponds to "fpexcept.ignore".
33+
ebMayTrap, ///< This corresponds to "fpexcept.maytrap".
34+
ebStrict, ///< This corresponds to "fpexcept.strict".
35+
};
36+
37+
} // namespace fp
38+
39+
/// For any RoundingMode enumerator, returns a string valid as input in
40+
/// constrained intrinsic rounding mode metadata.
41+
std::optional<llvm::StringRef> convertRoundingModeToStr(llvm::RoundingMode);
42+
43+
/// For any ExceptionBehavior enumerator, returns a string valid as input in
44+
/// constrained intrinsic exception behavior metadata.
45+
std::optional<llvm::StringRef>
46+
convertExceptionBehaviorToStr(fp::ExceptionBehavior);
47+
48+
} // namespace cir
49+
50+
#endif

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,10 @@ struct MissingFeatures {
262262
static bool emitNullabilityCheck() { return false; }
263263
static bool emitTypeCheck() { return false; }
264264
static bool emitTypeMetadataCodeForVCall() { return false; }
265+
static bool fastMathGuard() { return false; }
265266
static bool fastMathFlags() { return false; }
267+
static bool fastMathFuncAttributes() { return false; }
268+
266269
static bool fpConstraints() { return false; }
267270
static bool generateDebugInfo() { return false; }
268271
static bool globalViewIndices() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "mlir/IR/BuiltinAttributes.h"
1717
#include "mlir/Support/LLVM.h"
1818
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
19+
#include "clang/CIR/Dialect/IR/FPEnv.h"
1920
#include "clang/CIR/MissingFeatures.h"
2021

2122
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
@@ -29,6 +30,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
2930
const CIRGenTypeCache &typeCache;
3031
llvm::StringMap<unsigned> recordNames;
3132
llvm::StringMap<unsigned> globalsVersioning;
33+
bool isFpConstrained = false;
34+
cir::fp::ExceptionBehavior defaultConstrainedExcept = cir::fp::ebStrict;
35+
llvm::RoundingMode defaultConstrainedRounding = llvm::RoundingMode::Dynamic;
3236

3337
public:
3438
CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
@@ -66,6 +70,56 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
6670
cir::ArrayType arrayTy) const {
6771
return cir::ConstArrayAttr::get(arrayTy, attrs);
6872
}
73+
//
74+
// Floating point specific helpers
75+
// -------------------------------
76+
//
77+
78+
/// Enable/Disable use of constrained floating point math. When enabled the
79+
/// CreateF<op>() calls instead create constrained floating point intrinsic
80+
/// calls. Fast math flags are unaffected by this setting.
81+
void setIsFPConstrained(bool isCon) {
82+
if (isCon)
83+
llvm_unreachable("Constrained FP NYI");
84+
isFpConstrained = isCon;
85+
}
86+
87+
/// Query for the use of constrained floating point math
88+
bool getIsFPConstrained() {
89+
if (isFpConstrained)
90+
llvm_unreachable("Constrained FP NYI");
91+
return isFpConstrained;
92+
}
93+
///
94+
/// Set the exception handling to be used with constrained floating point
95+
void setDefaultConstrainedExcept(cir::fp::ExceptionBehavior newExcept) {
96+
#ifndef NDEBUG
97+
std::optional<llvm::StringRef> exceptStr =
98+
cir::convertExceptionBehaviorToStr(newExcept);
99+
assert(exceptStr && "Garbage strict exception behavior!");
100+
#endif
101+
defaultConstrainedExcept = newExcept;
102+
}
103+
104+
/// Set the rounding mode handling to be used with constrained floating point
105+
void setDefaultConstrainedRounding(llvm::RoundingMode newRounding) {
106+
#ifndef NDEBUG
107+
std::optional<llvm::StringRef> roundingStr =
108+
cir::convertRoundingModeToStr(newRounding);
109+
assert(roundingStr && "Garbage strict rounding mode!");
110+
#endif
111+
defaultConstrainedRounding = newRounding;
112+
}
113+
114+
/// Get the exception handling used with constrained floating point
115+
cir::fp::ExceptionBehavior getDefaultConstrainedExcept() {
116+
return defaultConstrainedExcept;
117+
}
118+
119+
/// Get the rounding mode handling used with constrained floating point
120+
llvm::RoundingMode getDefaultConstrainedRounding() {
121+
return defaultConstrainedRounding;
122+
}
69123

70124
mlir::Attribute getConstRecordOrZeroAttr(mlir::ArrayAttr arrayAttr,
71125
bool packed = false,
@@ -332,6 +386,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
332386
llvm_unreachable("negation for the given type is NYI");
333387
}
334388

389+
cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src,
390+
unsigned flags) {
391+
return cir::IsFPClassOp::create(*this, loc, src, flags);
392+
}
393+
335394
// TODO: split this to createFPExt/createFPTrunc when we have dedicated cast
336395
// operations.
337396
mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) {

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ using namespace clang;
2929
using namespace clang::CIRGen;
3030
using namespace llvm;
3131

32+
static mlir::Value tryUseTestFPKind(CIRGenFunction &cgf, unsigned BuiltinID,
33+
mlir::Value V) {
34+
if (cgf.getBuilder().getIsFPConstrained() &&
35+
cgf.getBuilder().getDefaultConstrainedExcept() != cir::fp::ebIgnore) {
36+
if (mlir::Value Result = cgf.getTargetHooks().testFPKind(
37+
V, BuiltinID, cgf.getBuilder(), cgf.cgm))
38+
return Result;
39+
}
40+
return nullptr;
41+
}
3242
static RValue emitLibraryCall(CIRGenFunction &cgf, const FunctionDecl *fd,
3343
const CallExpr *e, mlir::Operation *calleeValue) {
3444
CIRGenCallee callee = CIRGenCallee::forDirect(calleeValue, GlobalDecl(fd));
@@ -515,14 +525,117 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
515525
cir::PrefetchOp::create(builder, loc, address, locality, isWrite);
516526
return RValue::get(nullptr);
517527
}
528+
// From https://clang.llvm.org/docs/LanguageExtensions.html#builtin-isfpclass
529+
// :
530+
//
531+
// The `__builtin_isfpclass()` builtin is a generalization of functions
532+
// isnan, isinf, isfinite and some others defined by the C standard. It tests
533+
// if the floating-point value, specified by the first argument, falls into
534+
// any of data classes, specified by the second argument.
535+
case Builtin::BI__builtin_isnan: {
536+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
537+
mlir::Value v = emitScalarExpr(e->getArg(0));
538+
if (mlir::Value result = tryUseTestFPKind(*this, builtinID, v))
539+
return RValue::get(result);
540+
mlir::Location loc = getLoc(e->getBeginLoc());
541+
// FIXME: We should use builder.createZExt once createZExt is available.
542+
return RValue::get(builder.createZExtOrBitCast(
543+
loc, builder.createIsFPClass(loc, v, FPClassTest::fcNan),
544+
convertType(e->getType())));
545+
}
546+
547+
case Builtin::BI__builtin_issignaling: {
548+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
549+
mlir::Value v = emitScalarExpr(e->getArg(0));
550+
mlir::Location loc = getLoc(e->getBeginLoc());
551+
// FIXME: We should use builder.createZExt once createZExt is available.
552+
return RValue::get(builder.createZExtOrBitCast(
553+
loc, builder.createIsFPClass(loc, v, FPClassTest::fcSNan),
554+
convertType(e->getType())));
555+
}
556+
557+
case Builtin::BI__builtin_isinf: {
558+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
559+
mlir::Value v = emitScalarExpr(e->getArg(0));
560+
if (mlir::Value result = tryUseTestFPKind(*this, builtinID, v))
561+
return RValue::get(result);
562+
mlir::Location loc = getLoc(e->getBeginLoc());
563+
// FIXME: We should use builder.createZExt once createZExt is available.
564+
return RValue::get(builder.createZExtOrBitCast(
565+
loc, builder.createIsFPClass(loc, v, FPClassTest::fcInf),
566+
convertType(e->getType())));
567+
}
568+
569+
case Builtin::BIfinite:
570+
case Builtin::BI__finite:
571+
case Builtin::BIfinitef:
572+
case Builtin::BI__finitef:
573+
case Builtin::BIfinitel:
574+
case Builtin::BI__finitel:
575+
case Builtin::BI__builtin_isfinite: {
576+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
577+
mlir::Value v = emitScalarExpr(e->getArg(0));
578+
if (mlir::Value result = tryUseTestFPKind(*this, builtinID, v))
579+
return RValue::get(result);
580+
mlir::Location loc = getLoc(e->getBeginLoc());
581+
// FIXME: We should use builder.createZExt once createZExt is available.
582+
return RValue::get(builder.createZExtOrBitCast(
583+
loc, builder.createIsFPClass(loc, v, FPClassTest::fcFinite),
584+
convertType(e->getType())));
585+
}
586+
587+
case Builtin::BI__builtin_isnormal: {
588+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
589+
mlir::Value v = emitScalarExpr(e->getArg(0));
590+
mlir::Location loc = getLoc(e->getBeginLoc());
591+
// FIXME: We should use builder.createZExt once createZExt is available.
592+
return RValue::get(builder.createZExtOrBitCast(
593+
loc, builder.createIsFPClass(loc, v, FPClassTest::fcNormal),
594+
convertType(e->getType())));
595+
}
596+
597+
case Builtin::BI__builtin_issubnormal: {
598+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
599+
mlir::Value v = emitScalarExpr(e->getArg(0));
600+
mlir::Location loc = getLoc(e->getBeginLoc());
601+
// FIXME: We should use builder.createZExt once createZExt is available.
602+
return RValue::get(builder.createZExtOrBitCast(
603+
loc, builder.createIsFPClass(loc, v, FPClassTest::fcSubnormal),
604+
convertType(e->getType())));
605+
}
606+
607+
case Builtin::BI__builtin_iszero: {
608+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
609+
mlir::Value v = emitScalarExpr(e->getArg(0));
610+
mlir::Location loc = getLoc(e->getBeginLoc());
611+
// FIXME: We should use builder.createZExt once createZExt is available.
612+
return RValue::get(builder.createZExtOrBitCast(
613+
loc, builder.createIsFPClass(loc, v, FPClassTest::fcZero),
614+
convertType(e->getType())));
615+
}
616+
case Builtin::BI__builtin_isfpclass: {
617+
Expr::EvalResult result;
618+
if (!e->getArg(1)->EvaluateAsInt(result, cgm.getASTContext()))
619+
break;
620+
621+
CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
622+
mlir::Value v = emitScalarExpr(e->getArg(0));
623+
uint64_t test = result.Val.getInt().getLimitedValue();
624+
mlir::Location loc = getLoc(e->getBeginLoc());
625+
//
626+
// // FIXME: We should use builder.createZExt once createZExt is available.
627+
return RValue::get(builder.createZExtOrBitCast(
628+
loc, builder.createIsFPClass(loc, v, test), convertType(e->getType())));
629+
}
518630
}
519631

520632
// If this is an alias for a lib function (e.g. __builtin_sin), emit
521633
// the call using the normal call path, but using the unmangled
522634
// version of the function name.
523-
if (getContext().BuiltinInfo.isLibFunction(builtinID))
635+
if (getContext().BuiltinInfo.isLibFunction(builtinID)) {
524636
return emitLibraryCall(*this, fd, e,
525637
cgm.getBuiltinLibFunction(fd, builtinID));
638+
}
526639

527640
// Some target-specific builtins can have aggregate return values, e.g.
528641
// __builtin_arm_mve_vld2q_u32. So if the result is an aggregate, force

0 commit comments

Comments
 (0)