[clang] [CIR] Upstream isfpclass op (PR #166037)
Jasmine Tang via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 18 19:14:52 PST 2025
https://github.com/badumbatish updated https://github.com/llvm/llvm-project/pull/166037
>From 6b6f0f523d9c52f912937e206c374ee4001ad583 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Sat, 1 Nov 2025 20:32:55 -0700
Subject: [PATCH 1/7] Initial implementation for isfpclass and related builtins
Add lowering test
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 16 +++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 33 +++++
clang/include/clang/CIR/Dialect/IR/FPEnv.h | 50 +++++++
clang/include/clang/CIR/MissingFeatures.h | 3 +
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 59 ++++++++
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 115 +++++++++++++++-
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 69 ++++++++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 16 +++
clang/lib/CIR/Dialect/IR/CMakeLists.txt | 1 +
clang/lib/CIR/Dialect/IR/FPEnv.cpp | 64 +++++++++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 11 ++
clang/test/CIR/CodeGen/builtin-isfpclass.c | 129 ++++++++++++++++++
clang/test/CIR/Lowering/builtin_isfpclass.c | 125 +++++++++++++++++
13 files changed, 690 insertions(+), 1 deletion(-)
create mode 100644 clang/include/clang/CIR/Dialect/IR/FPEnv.h
create mode 100644 clang/lib/CIR/Dialect/IR/FPEnv.cpp
create mode 100644 clang/test/CIR/CodeGen/builtin-isfpclass.c
create mode 100644 clang/test/CIR/Lowering/builtin_isfpclass.c
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index be9965ae3101f..bf14a96aeec07 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -431,6 +431,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy());
}
+ // TODO(cir): the following function was introduced to keep in sync with LLVM
+ // codegen. CIR does not have "zext" operations. It should eventually be
+ // renamed or removed. For now, we just add whatever cast is required here.
+ mlir::Value createZExtOrBitCast(mlir::Location loc, mlir::Value src,
+ mlir::Type newTy) {
+ auto srcTy = src.getType();
+
+ if (srcTy == newTy)
+ return src;
+
+ if (mlir::isa<cir::BoolType>(srcTy) && mlir::isa<cir::IntType>(newTy))
+ return createBoolToInt(src, newTy);
+
+ llvm_unreachable("unhandled extension cast");
+ }
+
mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) {
return createCast(cir::CastKind::bool_to_int, src, newTy);
}
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e612d6a0ba886..a1231cc3305d8 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4018,6 +4018,39 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
let hasFolder = 1;
}
+def CIR_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_AnyFloatType:$src,
+ I32Attr:$flags);
+ let results = (outs CIR_BoolType:$result);
+ let assemblyFormat = [{
+ $src `,` $flags `:` functional-type($src, $result) attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Assume Operations
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/FPEnv.h b/clang/include/clang/CIR/Dialect/IR/FPEnv.h
new file mode 100644
index 0000000000000..aceba9ee57d05
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/IR/FPEnv.h
@@ -0,0 +1,50 @@
+//===- FPEnv.h ---- FP Environment ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// @file
+/// This file contains the declarations of entities that describe floating
+/// point environment and related functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_DIALECT_IR_FPENV_H
+#define CLANG_CIR_DIALECT_IR_FPENV_H
+
+#include "llvm/ADT/FloatingPointMode.h"
+
+#include <optional>
+
+namespace cir {
+
+namespace fp {
+
+/// Exception behavior used for floating point operations.
+///
+/// Each of these values corresponds to some LLVMIR metadata argument value of a
+/// constrained floating point intrinsic. See the LLVM Language Reference Manual
+/// for details.
+enum ExceptionBehavior : uint8_t {
+ ebIgnore, ///< This corresponds to "fpexcept.ignore".
+ ebMayTrap, ///< This corresponds to "fpexcept.maytrap".
+ ebStrict, ///< This corresponds to "fpexcept.strict".
+};
+
+} // namespace fp
+
+/// For any RoundingMode enumerator, returns a string valid as input in
+/// constrained intrinsic rounding mode metadata.
+std::optional<llvm::StringRef> convertRoundingModeToStr(llvm::RoundingMode);
+
+/// For any ExceptionBehavior enumerator, returns a string valid as input in
+/// constrained intrinsic exception behavior metadata.
+std::optional<llvm::StringRef>
+ convertExceptionBehaviorToStr(fp::ExceptionBehavior);
+
+} // namespace cir
+
+#endif
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 6b5c34d28ce2a..4cad360e1ba31 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -265,7 +265,10 @@ struct MissingFeatures {
static bool emitNullabilityCheck() { return false; }
static bool emitTypeCheck() { return false; }
static bool emitTypeMetadataCodeForVCall() { return false; }
+ static bool fastMathGuard() { return false; }
static bool fastMathFlags() { return false; }
+ static bool fastMathFuncAttributes() { return false; }
+
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }
static bool globalViewIndices() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 5ab1d0e05cf8a..b40b252b2af12 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -16,6 +16,7 @@
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/Support/LLVM.h"
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
+#include "clang/CIR/Dialect/IR/FPEnv.h"
#include "clang/CIR/MissingFeatures.h"
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
@@ -29,6 +30,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
const CIRGenTypeCache &typeCache;
llvm::StringMap<unsigned> recordNames;
llvm::StringMap<unsigned> globalsVersioning;
+ bool isFpConstrained = false;
+ cir::fp::ExceptionBehavior defaultConstrainedExcept = cir::fp::ebStrict;
+ llvm::RoundingMode defaultConstrainedRounding = llvm::RoundingMode::Dynamic;
public:
CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
@@ -66,6 +70,56 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
cir::ArrayType arrayTy) const {
return cir::ConstArrayAttr::get(arrayTy, attrs);
}
+ //
+ // Floating point specific helpers
+ // -------------------------------
+ //
+
+ /// Enable/Disable use of constrained floating point math. When enabled the
+ /// CreateF<op>() calls instead create constrained floating point intrinsic
+ /// calls. Fast math flags are unaffected by this setting.
+ void setIsFPConstrained(bool isCon) {
+ if (isCon)
+ llvm_unreachable("Constrained FP NYI");
+ isFpConstrained = isCon;
+ }
+
+ /// Query for the use of constrained floating point math
+ bool getIsFPConstrained() {
+ if (isFpConstrained)
+ llvm_unreachable("Constrained FP NYI");
+ return isFpConstrained;
+ }
+ ///
+ /// Set the exception handling to be used with constrained floating point
+ void setDefaultConstrainedExcept(cir::fp::ExceptionBehavior newExcept) {
+#ifndef NDEBUG
+ std::optional<llvm::StringRef> exceptStr =
+ cir::convertExceptionBehaviorToStr(newExcept);
+ assert(exceptStr && "Garbage strict exception behavior!");
+#endif
+ defaultConstrainedExcept = newExcept;
+ }
+
+ /// Set the rounding mode handling to be used with constrained floating point
+ void setDefaultConstrainedRounding(llvm::RoundingMode newRounding) {
+#ifndef NDEBUG
+ std::optional<llvm::StringRef> roundingStr =
+ cir::convertRoundingModeToStr(newRounding);
+ assert(roundingStr && "Garbage strict rounding mode!");
+#endif
+ defaultConstrainedRounding = newRounding;
+ }
+
+ /// Get the exception handling used with constrained floating point
+ cir::fp::ExceptionBehavior getDefaultConstrainedExcept() {
+ return defaultConstrainedExcept;
+ }
+
+ /// Get the rounding mode handling used with constrained floating point
+ llvm::RoundingMode getDefaultConstrainedRounding() {
+ return defaultConstrainedRounding;
+ }
mlir::Attribute getConstRecordOrZeroAttr(mlir::ArrayAttr arrayAttr,
bool packed = false,
@@ -334,6 +388,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
llvm_unreachable("negation for the given type is NYI");
}
+ cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src,
+ unsigned flags) {
+ return cir::IsFPClassOp::create(*this, loc, src, flags);
+ }
+
// TODO: split this to createFPExt/createFPTrunc when we have dedicated cast
// operations.
mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 77f19343653db..0547c883c49cf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -29,6 +29,16 @@ using namespace clang;
using namespace clang::CIRGen;
using namespace llvm;
+static mlir::Value tryUseTestFPKind(CIRGenFunction &cgf, unsigned BuiltinID,
+ mlir::Value V) {
+ if (cgf.getBuilder().getIsFPConstrained() &&
+ cgf.getBuilder().getDefaultConstrainedExcept() != cir::fp::ebIgnore) {
+ if (mlir::Value Result = cgf.getTargetHooks().testFPKind(
+ V, BuiltinID, cgf.getBuilder(), cgf.cgm))
+ return Result;
+ }
+ return nullptr;
+}
static RValue emitLibraryCall(CIRGenFunction &cgf, const FunctionDecl *fd,
const CallExpr *e, mlir::Operation *calleeValue) {
CIRGenCallee callee = CIRGenCallee::forDirect(calleeValue, GlobalDecl(fd));
@@ -520,14 +530,117 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
cir::PrefetchOp::create(builder, loc, address, locality, isWrite);
return RValue::get(nullptr);
}
+ // 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 = emitScalarExpr(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 = emitScalarExpr(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 = emitScalarExpr(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 = emitScalarExpr(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 = emitScalarExpr(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 = emitScalarExpr(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 = emitScalarExpr(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 = emitScalarExpr(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
// the call using the normal call path, but using the unmangled
// version of the function name.
- if (getContext().BuiltinInfo.isLibFunction(builtinID))
+ if (getContext().BuiltinInfo.isLibFunction(builtinID)) {
return emitLibraryCall(*this, fd, e,
cgm.getBuiltinLibFunction(fd, builtinID));
+ }
// Some target-specific builtins can have aggregate return values, e.g.
// __builtin_arm_mve_vld2q_u32. So if the result is an aggregate, force
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 2739469d7202e..ac48c25435530 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -834,6 +834,22 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor");
}
+// Map the LangOption for exception behavior into the corresponding enum in
+// the IR.
+static cir::fp::ExceptionBehavior
+toConstrainedExceptMd(LangOptions::FPExceptionModeKind kind) {
+ switch (kind) {
+ case LangOptions::FPE_Ignore:
+ return cir::fp::ebIgnore;
+ case LangOptions::FPE_MayTrap:
+ return cir::fp::ebMayTrap;
+ case LangOptions::FPE_Strict:
+ return cir::fp::ebStrict;
+ default:
+ llvm_unreachable("Unsupported FP Exception Behavior");
+ }
+}
+
/// Given a value of type T* that may not be to a complete object, construct
/// an l-vlaue withi the natural pointee alignment of T.
LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
@@ -1018,6 +1034,59 @@ void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
builder.createStore(loc, zeroValue, destPtr);
}
+CIRGenFunction::CIRGenFPOptionsRAII::CIRGenFPOptionsRAII(CIRGenFunction &cgf,
+ const clang::Expr *e)
+ : cgf(cgf) {
+ constructorHelper(e->getFPFeaturesInEffect(cgf.getLangOpts()));
+}
+
+CIRGenFunction::CIRGenFPOptionsRAII::CIRGenFPOptionsRAII(CIRGenFunction &cgf,
+ FPOptions fpFeatures)
+ : cgf(cgf) {
+ constructorHelper(fpFeatures);
+}
+
+void CIRGenFunction::CIRGenFPOptionsRAII::constructorHelper(
+ FPOptions fpFeatures) {
+ oldFpFeatures = cgf.curFpFeatures;
+ cgf.curFpFeatures = fpFeatures;
+
+ oldExcept = cgf.builder.getDefaultConstrainedExcept();
+ oldRounding = cgf.builder.getDefaultConstrainedRounding();
+
+ if (oldFpFeatures == fpFeatures)
+ return;
+
+ // TODO(cir): create guard to restore fast math configurations.
+ assert(!cir::MissingFeatures::fastMathGuard());
+
+ llvm::RoundingMode newRoundingBehavior = fpFeatures.getRoundingMode();
+ // TODO(cir): override rounding behaviour once FM configs are guarded.
+ auto newExceptionBehavior =
+ toConstrainedExceptMd(static_cast<LangOptions::FPExceptionModeKind>(
+ fpFeatures.getExceptionMode()));
+ // TODO(cir): override exception behaviour once FM configs are guarded.
+
+ // TODO(cir): override FP flags once FM configs are guarded.
+ assert(!cir::MissingFeatures::fastMathFlags());
+
+ assert((cgf.curFuncDecl == nullptr || cgf.builder.getIsFPConstrained() ||
+ isa<CXXConstructorDecl>(cgf.curFuncDecl) ||
+ isa<CXXDestructorDecl>(cgf.curFuncDecl) ||
+ (newExceptionBehavior == cir::fp::ebIgnore &&
+ newRoundingBehavior == llvm::RoundingMode::NearestTiesToEven)) &&
+ "FPConstrained should be enabled on entire function");
+
+ // TODO(cir): mark CIR function with fast math attributes.
+ assert(!cir::MissingFeatures::fastMathFuncAttributes());
+}
+
+CIRGenFunction::CIRGenFPOptionsRAII::~CIRGenFPOptionsRAII() {
+ cgf.curFpFeatures = oldFpFeatures;
+ cgf.builder.setDefaultConstrainedExcept(oldExcept);
+ cgf.builder.setDefaultConstrainedRounding(oldRounding);
+}
+
// TODO(cir): should be shared with LLVM codegen.
bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) {
const Expr *e = ce->getSubExpr();
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index b22bf2d87fc10..5080b3ddad7cf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -31,6 +31,7 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/IR/FPEnv.h"
#include "clang/CIR/MissingFeatures.h"
#include "clang/CIR/TypeEvaluationKind.h"
#include "llvm/ADT/ScopedHashTable.h"
@@ -179,6 +180,21 @@ class CIRGenFunction : public CIRGenTypeCache {
/// Sanitizers enabled for this function.
clang::SanitizerSet sanOpts;
+ class CIRGenFPOptionsRAII {
+ public:
+ CIRGenFPOptionsRAII(CIRGenFunction &cgf, FPOptions fpFeatures);
+ CIRGenFPOptionsRAII(CIRGenFunction &cgf, const clang::Expr *e);
+ ~CIRGenFPOptionsRAII();
+
+ private:
+ void constructorHelper(clang::FPOptions fpFeatures);
+ CIRGenFunction &cgf;
+ clang::FPOptions oldFpFeatures;
+ cir::fp::ExceptionBehavior oldExcept;
+ llvm::RoundingMode oldRounding;
+ };
+ clang::FPOptions curFpFeatures;
+
/// The symbol table maps a variable name to a value in the current scope.
/// Entering a function creates a new scope, and the function arguments are
/// added to the mapping. When the processing of a function is terminated,
diff --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
index 98575941035f2..de3c7d7a1c3b9 100644
--- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
@@ -4,6 +4,7 @@ add_clang_library(MLIRCIR
CIRMemorySlot.cpp
CIRTypes.cpp
CIRDataLayout.cpp
+ FPEnv.cpp
DEPENDS
MLIRCIROpsIncGen
diff --git a/clang/lib/CIR/Dialect/IR/FPEnv.cpp b/clang/lib/CIR/Dialect/IR/FPEnv.cpp
new file mode 100644
index 0000000000000..719ceb32480c9
--- /dev/null
+++ b/clang/lib/CIR/Dialect/IR/FPEnv.cpp
@@ -0,0 +1,64 @@
+//===-- FPEnv.cpp ---- FP Environment -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// @file
+/// This file contains the implementations of entities that describe floating
+/// point environment.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/CIR/Dialect/IR/FPEnv.h"
+
+namespace cir {
+
+std::optional<llvm::StringRef>
+convertRoundingModeToStr(llvm::RoundingMode useRounding) {
+ std::optional<llvm::StringRef> roundingStr;
+ switch (useRounding) {
+ case llvm::RoundingMode::Dynamic:
+ roundingStr = "round.dynamic";
+ break;
+ case llvm::RoundingMode::NearestTiesToEven:
+ roundingStr = "round.tonearest";
+ break;
+ case llvm::RoundingMode::NearestTiesToAway:
+ roundingStr = "round.tonearestaway";
+ break;
+ case llvm::RoundingMode::TowardNegative:
+ roundingStr = "round.downward";
+ break;
+ case llvm::RoundingMode::TowardPositive:
+ roundingStr = "round.upward";
+ break;
+ case llvm::RoundingMode::TowardZero:
+ roundingStr = "round.towardZero";
+ break;
+ default:
+ break;
+ }
+ return roundingStr;
+}
+
+std::optional<llvm::StringRef>
+convertExceptionBehaviorToStr(fp::ExceptionBehavior useExcept) {
+ std::optional<llvm::StringRef> exceptStr;
+ switch (useExcept) {
+ case fp::ebStrict:
+ exceptStr = "fpexcept.strict";
+ break;
+ case fp::ebIgnore:
+ exceptStr = "fpexcept.ignore";
+ break;
+ case fp::ebMayTrap:
+ exceptStr = "fpexcept.maytrap";
+ break;
+ }
+ return exceptStr;
+}
+
+} // namespace cir
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 4912bd197dba4..04cf62ed3bd08 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -670,6 +670,17 @@ mlir::LogicalResult CIRToLLVMASinOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite(
+ cir::IsFPClassOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto src = adaptor.getSrc();
+ auto flags = adaptor.getFlags();
+ auto retTy = rewriter.getI1Type();
+
+ rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>(op, retTy, src, flags);
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite(
cir::AssumeOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/builtin-isfpclass.c b/clang/test/CIR/CodeGen/builtin-isfpclass.c
new file mode 100644
index 0000000000000..b02069fcbb7f5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtin-isfpclass.c
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+int finite(double);
+
+// CHECK: cir.func {{.*}}@test_is_finite
+void test_is_finite(__fp16 *H, float F, double D, long double LD) {
+ volatile int res;
+ res = __builtin_isinf(*H);
+ // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.f16) -> !cir.bool
+
+ res = __builtin_isinf(F);
+ // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.float) -> !cir.bool
+
+ res = __builtin_isinf(D);
+ // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.double) -> !cir.bool
+
+ res = __builtin_isinf(LD);
+ // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.long_double<!cir.f80>) -> !cir.bool
+
+ res = __builtin_isfinite(*H);
+ // CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.f16) -> !cir.bool
+ res = __builtin_isfinite(F);
+ // CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.float) -> !cir.bool
+ res = finite(D);
+ // CHECK: cir.call @finite(%{{.*}}) nothrow side_effect(const) : (!cir.double) -> !s32i
+
+ res = __builtin_isnormal(*H);
+ // CHECK: cir.is_fp_class %{{.*}}, 264 : (!cir.f16) -> !cir.bool
+ res = __builtin_isnormal(F);
+ // CHECK: cir.is_fp_class %{{.*}}, 264 : (!cir.float) -> !cir.bool
+
+ res = __builtin_issubnormal(F);
+ // CHECK: cir.is_fp_class %{{.*}}, 144 : (!cir.float) -> !cir.bool
+ res = __builtin_iszero(F);
+ // CHECK: cir.is_fp_class %{{.*}}, 96 : (!cir.float) -> !cir.bool
+ res = __builtin_issignaling(F);
+ // CHECK: cir.is_fp_class %{{.*}}, 1 : (!cir.float) -> !cir.bool
+}
+
+_Bool check_isfpclass_finite(float x) {
+ return __builtin_isfpclass(x, 504 /*Finite*/);
+}
+
+// CHECK: cir.func {{.*}}@check_isfpclass_finite
+// CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.float)
+
+_Bool check_isfpclass_nan_f32(float x) {
+ return __builtin_isfpclass(x, 3 /*NaN*/);
+}
+
+// CHECK: cir.func {{.*}}@check_isfpclass_nan_f32
+// CHECK: cir.is_fp_class %{{.*}}, 3 : (!cir.float)
+
+
+_Bool check_isfpclass_snan_f64(double x) {
+ return __builtin_isfpclass(x, 1 /*SNaN*/);
+}
+
+// CHECK: cir.func {{.*}}@check_isfpclass_snan_f64
+// CHECK: cir.is_fp_class %{{.*}}, 1 : (!cir.double)
+
+
+_Bool check_isfpclass_zero_f16(_Float16 x) {
+ return __builtin_isfpclass(x, 96 /*Zero*/);
+}
+
+// CHECK: cir.func {{.*}}@check_isfpclass_zero_f16
+// CHECK: cir.is_fp_class %{{.*}}, 96 : (!cir.f16)
+
+// Update when we support FP pragma in functions and can convert BoolType in prvalue to i1.
+
+// _Bool check_isfpclass_finite_strict(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 504 /*Finite*/);
+// }
+//
+// _Bool check_isfpclass_nan_f32_strict(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
+//
+// _Bool check_isfpclass_snan_f64_strict(double x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 1 /*NaN*/);
+// }
+//
+// _Bool check_isfpclass_zero_f16_strict(_Float16 x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 96 /*Zero*/);
+// }
+//
+// _Bool check_isnan(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isnan(x);
+// }
+//
+// _Bool check_isinf(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isinf(x);
+// }
+//
+// _Bool check_isfinite(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfinite(x);
+// }
+//
+// _Bool check_isnormal(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isnormal(x);
+// }
+//
+// typedef float __attribute__((ext_vector_type(4))) float4;
+// typedef double __attribute__((ext_vector_type(4))) double4;
+// typedef int __attribute__((ext_vector_type(4))) int4;
+// typedef long __attribute__((ext_vector_type(4))) long4;
+//
+// int4 check_isfpclass_nan_v4f32(float4 x) {
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
+//
+// int4 check_isfpclass_nan_strict_v4f32(float4 x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
+//
+// long4 check_isfpclass_nan_v4f64(double4 x) {
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
diff --git a/clang/test/CIR/Lowering/builtin_isfpclass.c b/clang/test/CIR/Lowering/builtin_isfpclass.c
new file mode 100644
index 0000000000000..f3b480b492a3f
--- /dev/null
+++ b/clang/test/CIR/Lowering/builtin_isfpclass.c
@@ -0,0 +1,125 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s
+
+int finite(double);
+
+// CHECK: define {{.*}}@test_is_finite
+void test_is_finite(__fp16 *H, float F, double D, long double LD) {
+ volatile int res;
+ res = __builtin_isinf(*H);
+ // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 516)
+ res = __builtin_isinf(F);
+ // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 516)
+ res = __builtin_isinf(D);
+ // CHECK: call i1 @llvm.is.fpclass.f64(double %{{.*}}, i32 516)
+ res = __builtin_isinf(LD);
+ // CHECK: call i1 @llvm.is.fpclass.f80(x86_fp80 %{{.*}}, i32 516)
+
+ res = __builtin_isfinite(*H);
+ // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 504)
+ res = __builtin_isfinite(F);
+ // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 504)
+ res = finite(D);
+ // CHECK: call i32 @finite(double %{{.*}})
+
+ res = __builtin_isnormal(*H);
+ // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 264)
+ res = __builtin_isnormal(F);
+ // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 264)
+
+ res = __builtin_issubnormal(F);
+ // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 144)
+ res = __builtin_iszero(F);
+ // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 96)
+ res = __builtin_issignaling(F);
+ // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 1)
+}
+
+_Bool check_isfpclass_finite(float x) {
+ return __builtin_isfpclass(x, 504 /*Finite*/);
+}
+
+// CHECK: define {{.*}}@check_isfpclass_finite
+// CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 504)
+
+_Bool check_isfpclass_nan_f32(float x) {
+ return __builtin_isfpclass(x, 3 /*NaN*/);
+}
+
+// CHECK: define {{.*}}@check_isfpclass_nan_f32
+// CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 3)
+
+_Bool check_isfpclass_snan_f64(double x) {
+ return __builtin_isfpclass(x, 1 /*SNaN*/);
+}
+
+// CHECK: define {{.*}}@check_isfpclass_snan_f64
+// CHECK: call i1 @llvm.is.fpclass.f64(double %{{.*}}, i32 1)
+
+
+_Bool check_isfpclass_zero_f16(_Float16 x) {
+ return __builtin_isfpclass(x, 96 /*Zero*/);
+}
+
+// CHECK: define {{.*}}@check_isfpclass_zero_f16
+// CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 96)
+
+// Update when we support FP pragma in functions.
+
+// _Bool check_isfpclass_finite_strict(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 504 /*Finite*/);
+// }
+//
+// _Bool check_isfpclass_nan_f32_strict(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
+//
+// _Bool check_isfpclass_snan_f64_strict(double x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 1 /*NaN*/);
+// }
+//
+// _Bool check_isfpclass_zero_f16_strict(_Float16 x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 96 /*Zero*/);
+// }
+//
+// _Bool check_isnan(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isnan(x);
+// }
+//
+// _Bool check_isinf(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isinf(x);
+// }
+//
+// _Bool check_isfinite(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfinite(x);
+// }
+//
+// _Bool check_isnormal(float x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isnormal(x);
+// }
+//
+// typedef float __attribute__((ext_vector_type(4))) float4;
+// typedef double __attribute__((ext_vector_type(4))) double4;
+// typedef int __attribute__((ext_vector_type(4))) int4;
+// typedef long __attribute__((ext_vector_type(4))) long4;
+//
+// int4 check_isfpclass_nan_v4f32(float4 x) {
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
+//
+// int4 check_isfpclass_nan_strict_v4f32(float4 x) {
+// #pragma STDC FENV_ACCESS ON
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
+//
+// long4 check_isfpclass_nan_v4f64(double4 x) {
+// return __builtin_isfpclass(x, 3 /*NaN*/);
+// }
>From b036765e665f384f20d6ecf29bed1c8659fbb29f Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Tue, 11 Nov 2025 15:06:26 -0800
Subject: [PATCH 2/7] Address PR review: remove FP skeleton features, lowering
test and add OGCG and LLVM test
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 16 ---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 22 +--
clang/include/clang/CIR/MissingFeatures.h | 2 -
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 50 -------
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 78 ++++-------
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 69 ----------
clang/lib/CIR/CodeGen/CIRGenFunction.h | 14 --
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 6 +-
clang/test/CIR/CodeGen/builtin-isfpclass.c | 91 +++++++++----
clang/test/CIR/Lowering/builtin_isfpclass.c | 125 ------------------
10 files changed, 110 insertions(+), 363 deletions(-)
delete mode 100644 clang/test/CIR/Lowering/builtin_isfpclass.c
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index bf14a96aeec07..be9965ae3101f 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -431,22 +431,6 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy());
}
- // TODO(cir): the following function was introduced to keep in sync with LLVM
- // codegen. CIR does not have "zext" operations. It should eventually be
- // renamed or removed. For now, we just add whatever cast is required here.
- mlir::Value createZExtOrBitCast(mlir::Location loc, mlir::Value src,
- mlir::Type newTy) {
- auto srcTy = src.getType();
-
- if (srcTy == newTy)
- return src;
-
- if (mlir::isa<cir::BoolType>(srcTy) && mlir::isa<cir::IntType>(newTy))
- return createBoolToInt(src, newTy);
-
- llvm_unreachable("unhandled extension cast");
- }
-
mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) {
return createCast(cir::CastKind::bool_to_int, src, newTy);
}
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a1231cc3305d8..67fe7e8e1f4a1 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4030,17 +4030,17 @@ def CIR_IsFPClassOp : CIR_Op<"is_fp_class"> {
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 |
+ | ----- | -------------------- |
+ | 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_AnyFloatType:$src,
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 4cad360e1ba31..2ecde9aa5d56d 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -265,9 +265,7 @@ struct MissingFeatures {
static bool emitNullabilityCheck() { return false; }
static bool emitTypeCheck() { return false; }
static bool emitTypeMetadataCodeForVCall() { return false; }
- static bool fastMathGuard() { return false; }
static bool fastMathFlags() { return false; }
- static bool fastMathFuncAttributes() { return false; }
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index b40b252b2af12..d611c0dc3d675 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -70,56 +70,6 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
cir::ArrayType arrayTy) const {
return cir::ConstArrayAttr::get(arrayTy, attrs);
}
- //
- // Floating point specific helpers
- // -------------------------------
- //
-
- /// Enable/Disable use of constrained floating point math. When enabled the
- /// CreateF<op>() calls instead create constrained floating point intrinsic
- /// calls. Fast math flags are unaffected by this setting.
- void setIsFPConstrained(bool isCon) {
- if (isCon)
- llvm_unreachable("Constrained FP NYI");
- isFpConstrained = isCon;
- }
-
- /// Query for the use of constrained floating point math
- bool getIsFPConstrained() {
- if (isFpConstrained)
- llvm_unreachable("Constrained FP NYI");
- return isFpConstrained;
- }
- ///
- /// Set the exception handling to be used with constrained floating point
- void setDefaultConstrainedExcept(cir::fp::ExceptionBehavior newExcept) {
-#ifndef NDEBUG
- std::optional<llvm::StringRef> exceptStr =
- cir::convertExceptionBehaviorToStr(newExcept);
- assert(exceptStr && "Garbage strict exception behavior!");
-#endif
- defaultConstrainedExcept = newExcept;
- }
-
- /// Set the rounding mode handling to be used with constrained floating point
- void setDefaultConstrainedRounding(llvm::RoundingMode newRounding) {
-#ifndef NDEBUG
- std::optional<llvm::StringRef> roundingStr =
- cir::convertRoundingModeToStr(newRounding);
- assert(roundingStr && "Garbage strict rounding mode!");
-#endif
- defaultConstrainedRounding = newRounding;
- }
-
- /// Get the exception handling used with constrained floating point
- cir::fp::ExceptionBehavior getDefaultConstrainedExcept() {
- return defaultConstrainedExcept;
- }
-
- /// Get the rounding mode handling used with constrained floating point
- llvm::RoundingMode getDefaultConstrainedRounding() {
- return defaultConstrainedRounding;
- }
mlir::Attribute getConstRecordOrZeroAttr(mlir::ArrayAttr arrayAttr,
bool packed = false,
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 0547c883c49cf..14c3ccde80055 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -29,16 +29,6 @@ using namespace clang;
using namespace clang::CIRGen;
using namespace llvm;
-static mlir::Value tryUseTestFPKind(CIRGenFunction &cgf, unsigned BuiltinID,
- mlir::Value V) {
- if (cgf.getBuilder().getIsFPConstrained() &&
- cgf.getBuilder().getDefaultConstrainedExcept() != cir::fp::ebIgnore) {
- if (mlir::Value Result = cgf.getTargetHooks().testFPKind(
- V, BuiltinID, cgf.getBuilder(), cgf.cgm))
- return Result;
- }
- return nullptr;
-}
static RValue emitLibraryCall(CIRGenFunction &cgf, const FunctionDecl *fd,
const CallExpr *e, mlir::Operation *calleeValue) {
CIRGenCallee callee = CIRGenCallee::forDirect(calleeValue, GlobalDecl(fd));
@@ -538,36 +528,31 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// 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);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(e->getArg(0));
- if (mlir::Value result = tryUseTestFPKind(*this, builtinID, v))
- return RValue::get(result);
+ assert(!cir::MissingFeatures::fpConstraints());
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),
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, FPClassTest::fcNan),
convertType(e->getType())));
}
case Builtin::BI__builtin_issignaling: {
- CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(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),
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, FPClassTest::fcSNan),
convertType(e->getType())));
}
case Builtin::BI__builtin_isinf: {
- CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(e->getArg(0));
- if (mlir::Value result = tryUseTestFPKind(*this, builtinID, v))
- return RValue::get(result);
+ assert(!cir::MissingFeatures::fpConstraints());
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),
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, FPClassTest::fcInf),
convertType(e->getType())));
}
@@ -578,44 +563,39 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BIfinitel:
case Builtin::BI__finitel:
case Builtin::BI__builtin_isfinite: {
- CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(e->getArg(0));
- if (mlir::Value result = tryUseTestFPKind(*this, builtinID, v))
- return RValue::get(result);
+ assert(!cir::MissingFeatures::fpConstraints());
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),
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, FPClassTest::fcFinite),
convertType(e->getType())));
}
case Builtin::BI__builtin_isnormal: {
- CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(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),
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, FPClassTest::fcNormal),
convertType(e->getType())));
}
case Builtin::BI__builtin_issubnormal: {
- CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(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),
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, FPClassTest::fcSubnormal),
convertType(e->getType())));
}
case Builtin::BI__builtin_iszero: {
- CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(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),
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, FPClassTest::fcZero),
convertType(e->getType())));
}
case Builtin::BI__builtin_isfpclass: {
@@ -623,24 +603,22 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
if (!e->getArg(1)->EvaluateAsInt(result, cgm.getASTContext()))
break;
- CIRGenFunction::CIRGenFPOptionsRAII fpOptsRaii(*this, e);
+ assert(!cir::MissingFeatures::cgFPOptionsRAII());
mlir::Value v = emitScalarExpr(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())));
+ return RValue::get(builder.createBoolToInt(
+ builder.createIsFPClass(loc, v, test), convertType(e->getType())));
}
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
// the call using the normal call path, but using the unmangled
// version of the function name.
- if (getContext().BuiltinInfo.isLibFunction(builtinID)) {
+ if (getContext().BuiltinInfo.isLibFunction(builtinID))
return emitLibraryCall(*this, fd, e,
cgm.getBuiltinLibFunction(fd, builtinID));
- }
// Some target-specific builtins can have aggregate return values, e.g.
// __builtin_arm_mve_vld2q_u32. So if the result is an aggregate, force
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index ac48c25435530..2739469d7202e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -834,22 +834,6 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor");
}
-// Map the LangOption for exception behavior into the corresponding enum in
-// the IR.
-static cir::fp::ExceptionBehavior
-toConstrainedExceptMd(LangOptions::FPExceptionModeKind kind) {
- switch (kind) {
- case LangOptions::FPE_Ignore:
- return cir::fp::ebIgnore;
- case LangOptions::FPE_MayTrap:
- return cir::fp::ebMayTrap;
- case LangOptions::FPE_Strict:
- return cir::fp::ebStrict;
- default:
- llvm_unreachable("Unsupported FP Exception Behavior");
- }
-}
-
/// Given a value of type T* that may not be to a complete object, construct
/// an l-vlaue withi the natural pointee alignment of T.
LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
@@ -1034,59 +1018,6 @@ void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
builder.createStore(loc, zeroValue, destPtr);
}
-CIRGenFunction::CIRGenFPOptionsRAII::CIRGenFPOptionsRAII(CIRGenFunction &cgf,
- const clang::Expr *e)
- : cgf(cgf) {
- constructorHelper(e->getFPFeaturesInEffect(cgf.getLangOpts()));
-}
-
-CIRGenFunction::CIRGenFPOptionsRAII::CIRGenFPOptionsRAII(CIRGenFunction &cgf,
- FPOptions fpFeatures)
- : cgf(cgf) {
- constructorHelper(fpFeatures);
-}
-
-void CIRGenFunction::CIRGenFPOptionsRAII::constructorHelper(
- FPOptions fpFeatures) {
- oldFpFeatures = cgf.curFpFeatures;
- cgf.curFpFeatures = fpFeatures;
-
- oldExcept = cgf.builder.getDefaultConstrainedExcept();
- oldRounding = cgf.builder.getDefaultConstrainedRounding();
-
- if (oldFpFeatures == fpFeatures)
- return;
-
- // TODO(cir): create guard to restore fast math configurations.
- assert(!cir::MissingFeatures::fastMathGuard());
-
- llvm::RoundingMode newRoundingBehavior = fpFeatures.getRoundingMode();
- // TODO(cir): override rounding behaviour once FM configs are guarded.
- auto newExceptionBehavior =
- toConstrainedExceptMd(static_cast<LangOptions::FPExceptionModeKind>(
- fpFeatures.getExceptionMode()));
- // TODO(cir): override exception behaviour once FM configs are guarded.
-
- // TODO(cir): override FP flags once FM configs are guarded.
- assert(!cir::MissingFeatures::fastMathFlags());
-
- assert((cgf.curFuncDecl == nullptr || cgf.builder.getIsFPConstrained() ||
- isa<CXXConstructorDecl>(cgf.curFuncDecl) ||
- isa<CXXDestructorDecl>(cgf.curFuncDecl) ||
- (newExceptionBehavior == cir::fp::ebIgnore &&
- newRoundingBehavior == llvm::RoundingMode::NearestTiesToEven)) &&
- "FPConstrained should be enabled on entire function");
-
- // TODO(cir): mark CIR function with fast math attributes.
- assert(!cir::MissingFeatures::fastMathFuncAttributes());
-}
-
-CIRGenFunction::CIRGenFPOptionsRAII::~CIRGenFPOptionsRAII() {
- cgf.curFpFeatures = oldFpFeatures;
- cgf.builder.setDefaultConstrainedExcept(oldExcept);
- cgf.builder.setDefaultConstrainedRounding(oldRounding);
-}
-
// TODO(cir): should be shared with LLVM codegen.
bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) {
const Expr *e = ce->getSubExpr();
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 5080b3ddad7cf..98e185c11a506 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -180,20 +180,6 @@ class CIRGenFunction : public CIRGenTypeCache {
/// Sanitizers enabled for this function.
clang::SanitizerSet sanOpts;
- class CIRGenFPOptionsRAII {
- public:
- CIRGenFPOptionsRAII(CIRGenFunction &cgf, FPOptions fpFeatures);
- CIRGenFPOptionsRAII(CIRGenFunction &cgf, const clang::Expr *e);
- ~CIRGenFPOptionsRAII();
-
- private:
- void constructorHelper(clang::FPOptions fpFeatures);
- CIRGenFunction &cgf;
- clang::FPOptions oldFpFeatures;
- cir::fp::ExceptionBehavior oldExcept;
- llvm::RoundingMode oldRounding;
- };
- clang::FPOptions curFpFeatures;
/// The symbol table maps a variable name to a value in the current scope.
/// Entering a function creates a new scope, and the function arguments are
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 04cf62ed3bd08..5b818b8cd9a8a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -673,9 +673,9 @@ mlir::LogicalResult CIRToLLVMASinOpLowering::matchAndRewrite(
mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite(
cir::IsFPClassOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
- auto src = adaptor.getSrc();
- auto flags = adaptor.getFlags();
- auto retTy = rewriter.getI1Type();
+ mlir::Value src = adaptor.getSrc();
+ uint32_t flags = adaptor.getFlags();
+ mlir::IntegerType retTy = rewriter.getI1Type();
rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>(op, retTy, src, flags);
return mlir::success();
diff --git a/clang/test/CIR/CodeGen/builtin-isfpclass.c b/clang/test/CIR/CodeGen/builtin-isfpclass.c
index b02069fcbb7f5..06d473d259e97 100644
--- a/clang/test/CIR/CodeGen/builtin-isfpclass.c
+++ b/clang/test/CIR/CodeGen/builtin-isfpclass.c
@@ -1,72 +1,117 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
-// RUN: FileCheck --input-file=%t.cir %s
-
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=OGCG
int finite(double);
// CHECK: cir.func {{.*}}@test_is_finite
void test_is_finite(__fp16 *H, float F, double D, long double LD) {
volatile int res;
res = __builtin_isinf(*H);
- // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.f16) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 516 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.float) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 516 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.double) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 516 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 516 : (!cir.long_double<!cir.f80>) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 516 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.f16) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 504 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.float) -> !cir.bool
- res = finite(D);
- // CHECK: cir.call @finite(%{{.*}}) nothrow side_effect(const) : (!cir.double) -> !s32i
+ // CIR: cir.is_fp_class %{{.*}}, 504 : (!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.call @finite(%{{.*}}) nothrow side_effect(const) : (!cir.double) -> !s32i
+ // LLVM: call i32 @finite(double {{.*}})
+ // OGCG: call i1 @llvm.is.fpclass.f64(double %20, i32 504)
res = __builtin_isnormal(*H);
- // CHECK: cir.is_fp_class %{{.*}}, 264 : (!cir.f16) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 264 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 264 : (!cir.float) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 264 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 144 : (!cir.float) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 144 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 96 : (!cir.float) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 96 : (!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);
- // CHECK: cir.is_fp_class %{{.*}}, 1 : (!cir.float) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, 1 : (!cir.float) -> !cir.bool
+ // LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1)
+ // OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1)
}
_Bool check_isfpclass_finite(float x) {
return __builtin_isfpclass(x, 504 /*Finite*/);
}
-// CHECK: cir.func {{.*}}@check_isfpclass_finite
-// CHECK: cir.is_fp_class %{{.*}}, 504 : (!cir.float)
+// CIR: cir.func {{.*}}@check_isfpclass_finite
+// CIR: cir.is_fp_class %{{.*}}, 504 : (!cir.float)
+// LLVM: @check_isfpclass_finite
+// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504)
+// OGCG: @check_isfpclass_finite
+// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 504)
_Bool check_isfpclass_nan_f32(float x) {
return __builtin_isfpclass(x, 3 /*NaN*/);
}
-// CHECK: cir.func {{.*}}@check_isfpclass_nan_f32
-// CHECK: cir.is_fp_class %{{.*}}, 3 : (!cir.float)
+// CIR: cir.func {{.*}}@check_isfpclass_nan_f32
+// CIR: cir.is_fp_class %{{.*}}, 3 : (!cir.float)
+// LLVM: @check_isfpclass_nan_f32
+// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 3)
+// OGCG: @check_isfpclass_nan_f32
+// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 3)
_Bool check_isfpclass_snan_f64(double x) {
return __builtin_isfpclass(x, 1 /*SNaN*/);
}
-// CHECK: cir.func {{.*}}@check_isfpclass_snan_f64
-// CHECK: cir.is_fp_class %{{.*}}, 1 : (!cir.double)
+// CIR: cir.func {{.*}}@check_isfpclass_snan_f64
+// CIR: cir.is_fp_class %{{.*}}, 1 : (!cir.double)
+// LLVM: @check_isfpclass_snan_f64
+// LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 1)
+// OGCG: @check_isfpclass_snan_f64
+// OGCG: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 1)
_Bool check_isfpclass_zero_f16(_Float16 x) {
return __builtin_isfpclass(x, 96 /*Zero*/);
}
-// CHECK: cir.func {{.*}}@check_isfpclass_zero_f16
-// CHECK: cir.is_fp_class %{{.*}}, 96 : (!cir.f16)
+// CIR: cir.func {{.*}}@check_isfpclass_zero_f16
+// CIR: cir.is_fp_class %{{.*}}, 96 : (!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)
// Update when we support FP pragma in functions and can convert BoolType in prvalue to i1.
diff --git a/clang/test/CIR/Lowering/builtin_isfpclass.c b/clang/test/CIR/Lowering/builtin_isfpclass.c
deleted file mode 100644
index f3b480b492a3f..0000000000000
--- a/clang/test/CIR/Lowering/builtin_isfpclass.c
+++ /dev/null
@@ -1,125 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s
-
-int finite(double);
-
-// CHECK: define {{.*}}@test_is_finite
-void test_is_finite(__fp16 *H, float F, double D, long double LD) {
- volatile int res;
- res = __builtin_isinf(*H);
- // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 516)
- res = __builtin_isinf(F);
- // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 516)
- res = __builtin_isinf(D);
- // CHECK: call i1 @llvm.is.fpclass.f64(double %{{.*}}, i32 516)
- res = __builtin_isinf(LD);
- // CHECK: call i1 @llvm.is.fpclass.f80(x86_fp80 %{{.*}}, i32 516)
-
- res = __builtin_isfinite(*H);
- // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 504)
- res = __builtin_isfinite(F);
- // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 504)
- res = finite(D);
- // CHECK: call i32 @finite(double %{{.*}})
-
- res = __builtin_isnormal(*H);
- // CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 264)
- res = __builtin_isnormal(F);
- // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 264)
-
- res = __builtin_issubnormal(F);
- // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 144)
- res = __builtin_iszero(F);
- // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 96)
- res = __builtin_issignaling(F);
- // CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 1)
-}
-
-_Bool check_isfpclass_finite(float x) {
- return __builtin_isfpclass(x, 504 /*Finite*/);
-}
-
-// CHECK: define {{.*}}@check_isfpclass_finite
-// CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 504)
-
-_Bool check_isfpclass_nan_f32(float x) {
- return __builtin_isfpclass(x, 3 /*NaN*/);
-}
-
-// CHECK: define {{.*}}@check_isfpclass_nan_f32
-// CHECK: call i1 @llvm.is.fpclass.f32(float %{{.*}}, i32 3)
-
-_Bool check_isfpclass_snan_f64(double x) {
- return __builtin_isfpclass(x, 1 /*SNaN*/);
-}
-
-// CHECK: define {{.*}}@check_isfpclass_snan_f64
-// CHECK: call i1 @llvm.is.fpclass.f64(double %{{.*}}, i32 1)
-
-
-_Bool check_isfpclass_zero_f16(_Float16 x) {
- return __builtin_isfpclass(x, 96 /*Zero*/);
-}
-
-// CHECK: define {{.*}}@check_isfpclass_zero_f16
-// CHECK: call i1 @llvm.is.fpclass.f16(half %{{.*}}, i32 96)
-
-// Update when we support FP pragma in functions.
-
-// _Bool check_isfpclass_finite_strict(float x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isfpclass(x, 504 /*Finite*/);
-// }
-//
-// _Bool check_isfpclass_nan_f32_strict(float x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isfpclass(x, 3 /*NaN*/);
-// }
-//
-// _Bool check_isfpclass_snan_f64_strict(double x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isfpclass(x, 1 /*NaN*/);
-// }
-//
-// _Bool check_isfpclass_zero_f16_strict(_Float16 x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isfpclass(x, 96 /*Zero*/);
-// }
-//
-// _Bool check_isnan(float x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isnan(x);
-// }
-//
-// _Bool check_isinf(float x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isinf(x);
-// }
-//
-// _Bool check_isfinite(float x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isfinite(x);
-// }
-//
-// _Bool check_isnormal(float x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isnormal(x);
-// }
-//
-// typedef float __attribute__((ext_vector_type(4))) float4;
-// typedef double __attribute__((ext_vector_type(4))) double4;
-// typedef int __attribute__((ext_vector_type(4))) int4;
-// typedef long __attribute__((ext_vector_type(4))) long4;
-//
-// int4 check_isfpclass_nan_v4f32(float4 x) {
-// return __builtin_isfpclass(x, 3 /*NaN*/);
-// }
-//
-// int4 check_isfpclass_nan_strict_v4f32(float4 x) {
-// #pragma STDC FENV_ACCESS ON
-// return __builtin_isfpclass(x, 3 /*NaN*/);
-// }
-//
-// long4 check_isfpclass_nan_v4f64(double4 x) {
-// return __builtin_isfpclass(x, 3 /*NaN*/);
-// }
>From 995d9c0677590ef92380bce7cbadde0b5c925ba6 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Tue, 11 Nov 2025 15:11:26 -0800
Subject: [PATCH 3/7] Fix formatting
---
clang/lib/CIR/CodeGen/CIRGenFunction.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 98e185c11a506..1966424b6993f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -180,7 +180,6 @@ class CIRGenFunction : public CIRGenTypeCache {
/// Sanitizers enabled for this function.
clang::SanitizerSet sanOpts;
-
/// The symbol table maps a variable name to a value in the current scope.
/// Entering a function creates a new scope, and the function arguments are
/// added to the mapping. When the processing of a function is terminated,
>From 9715ff884c6bcaf600e5b6bb237fde78659d2f0b Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Tue, 11 Nov 2025 23:27:09 -0800
Subject: [PATCH 4/7] Remove rest of unused code
---
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index d611c0dc3d675..ef702deb2c9ae 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -30,9 +30,6 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
const CIRGenTypeCache &typeCache;
llvm::StringMap<unsigned> recordNames;
llvm::StringMap<unsigned> globalsVersioning;
- bool isFpConstrained = false;
- cir::fp::ExceptionBehavior defaultConstrainedExcept = cir::fp::ebStrict;
- llvm::RoundingMode defaultConstrainedRounding = llvm::RoundingMode::Dynamic;
public:
CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
>From e96ef8c3b6a5314b551357fd62544ea37776ec9a Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Wed, 12 Nov 2025 10:05:23 -0800
Subject: [PATCH 5/7] Remove CIR fpenv.h
---
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 1 -
clang/lib/CIR/Dialect/IR/FPEnv.cpp | 64 ---------------------------
2 files changed, 65 deletions(-)
delete mode 100644 clang/lib/CIR/Dialect/IR/FPEnv.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index ef702deb2c9ae..71c07679d529e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -16,7 +16,6 @@
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/Support/LLVM.h"
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
-#include "clang/CIR/Dialect/IR/FPEnv.h"
#include "clang/CIR/MissingFeatures.h"
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
diff --git a/clang/lib/CIR/Dialect/IR/FPEnv.cpp b/clang/lib/CIR/Dialect/IR/FPEnv.cpp
deleted file mode 100644
index 719ceb32480c9..0000000000000
--- a/clang/lib/CIR/Dialect/IR/FPEnv.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-//===-- FPEnv.cpp ---- FP Environment -------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-/// @file
-/// This file contains the implementations of entities that describe floating
-/// point environment.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/CIR/Dialect/IR/FPEnv.h"
-
-namespace cir {
-
-std::optional<llvm::StringRef>
-convertRoundingModeToStr(llvm::RoundingMode useRounding) {
- std::optional<llvm::StringRef> roundingStr;
- switch (useRounding) {
- case llvm::RoundingMode::Dynamic:
- roundingStr = "round.dynamic";
- break;
- case llvm::RoundingMode::NearestTiesToEven:
- roundingStr = "round.tonearest";
- break;
- case llvm::RoundingMode::NearestTiesToAway:
- roundingStr = "round.tonearestaway";
- break;
- case llvm::RoundingMode::TowardNegative:
- roundingStr = "round.downward";
- break;
- case llvm::RoundingMode::TowardPositive:
- roundingStr = "round.upward";
- break;
- case llvm::RoundingMode::TowardZero:
- roundingStr = "round.towardZero";
- break;
- default:
- break;
- }
- return roundingStr;
-}
-
-std::optional<llvm::StringRef>
-convertExceptionBehaviorToStr(fp::ExceptionBehavior useExcept) {
- std::optional<llvm::StringRef> exceptStr;
- switch (useExcept) {
- case fp::ebStrict:
- exceptStr = "fpexcept.strict";
- break;
- case fp::ebIgnore:
- exceptStr = "fpexcept.ignore";
- break;
- case fp::ebMayTrap:
- exceptStr = "fpexcept.maytrap";
- break;
- }
- return exceptStr;
-}
-
-} // namespace cir
>From a7d5592a2891a2639c042cb99a2ba3803ae2fc0a Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Wed, 12 Nov 2025 15:22:46 -0800
Subject: [PATCH 6/7] Remove rest of unused code
---
clang/include/clang/CIR/Dialect/IR/FPEnv.h | 50 ----------------------
clang/lib/CIR/CodeGen/CIRGenFunction.h | 1 -
clang/lib/CIR/Dialect/IR/CMakeLists.txt | 1 -
3 files changed, 52 deletions(-)
delete mode 100644 clang/include/clang/CIR/Dialect/IR/FPEnv.h
diff --git a/clang/include/clang/CIR/Dialect/IR/FPEnv.h b/clang/include/clang/CIR/Dialect/IR/FPEnv.h
deleted file mode 100644
index aceba9ee57d05..0000000000000
--- a/clang/include/clang/CIR/Dialect/IR/FPEnv.h
+++ /dev/null
@@ -1,50 +0,0 @@
-//===- FPEnv.h ---- FP Environment ------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-/// @file
-/// This file contains the declarations of entities that describe floating
-/// point environment and related functions.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_CIR_DIALECT_IR_FPENV_H
-#define CLANG_CIR_DIALECT_IR_FPENV_H
-
-#include "llvm/ADT/FloatingPointMode.h"
-
-#include <optional>
-
-namespace cir {
-
-namespace fp {
-
-/// Exception behavior used for floating point operations.
-///
-/// Each of these values corresponds to some LLVMIR metadata argument value of a
-/// constrained floating point intrinsic. See the LLVM Language Reference Manual
-/// for details.
-enum ExceptionBehavior : uint8_t {
- ebIgnore, ///< This corresponds to "fpexcept.ignore".
- ebMayTrap, ///< This corresponds to "fpexcept.maytrap".
- ebStrict, ///< This corresponds to "fpexcept.strict".
-};
-
-} // namespace fp
-
-/// For any RoundingMode enumerator, returns a string valid as input in
-/// constrained intrinsic rounding mode metadata.
-std::optional<llvm::StringRef> convertRoundingModeToStr(llvm::RoundingMode);
-
-/// For any ExceptionBehavior enumerator, returns a string valid as input in
-/// constrained intrinsic exception behavior metadata.
-std::optional<llvm::StringRef>
- convertExceptionBehaviorToStr(fp::ExceptionBehavior);
-
-} // namespace cir
-
-#endif
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 1966424b6993f..b22bf2d87fc10 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -31,7 +31,6 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
-#include "clang/CIR/Dialect/IR/FPEnv.h"
#include "clang/CIR/MissingFeatures.h"
#include "clang/CIR/TypeEvaluationKind.h"
#include "llvm/ADT/ScopedHashTable.h"
diff --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
index de3c7d7a1c3b9..98575941035f2 100644
--- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
@@ -4,7 +4,6 @@ add_clang_library(MLIRCIR
CIRMemorySlot.cpp
CIRTypes.cpp
CIRDataLayout.cpp
- FPEnv.cpp
DEPENDS
MLIRCIROpsIncGen
>From e54de58f9dcee36af8da20f7b8ab2bb4f3a33460 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Tue, 18 Nov 2025 18:55:54 -0800
Subject: [PATCH 7/7] Change to named enum
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 35 ++++++++++++++++++-
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 2 +-
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 16 ++++-----
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 4 +--
clang/test/CIR/CodeGen/builtin-isfpclass.c | 30 ++++++++--------
5 files changed, 60 insertions(+), 27 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 67fe7e8e1f4a1..f27c15f29ca97 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4018,6 +4018,39 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// 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";
+}
+
def CIR_IsFPClassOp : CIR_Op<"is_fp_class"> {
let summary = "Corresponding to the `__builtin_fpclassify` builtin function in clang";
@@ -4044,7 +4077,7 @@ def CIR_IsFPClassOp : CIR_Op<"is_fp_class"> {
}];
let arguments = (ins CIR_AnyFloatType:$src,
- I32Attr:$flags);
+ FPClassTestEnum:$flags);
let results = (outs CIR_BoolType:$result);
let assemblyFormat = [{
$src `,` $flags `:` functional-type($src, $result) attr-dict
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 71c07679d529e..f1e61835f7af9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -335,7 +335,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
}
cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src,
- unsigned flags) {
+ cir::FPClassTest flags) {
return cir::IsFPClassOp::create(*this, loc, src, flags);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 14c3ccde80055..7b21dc34b3b9d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -533,7 +533,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
assert(!cir::MissingFeatures::fpConstraints());
mlir::Location loc = getLoc(e->getBeginLoc());
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, FPClassTest::fcNan),
+ builder.createIsFPClass(loc, v, cir::FPClassTest::Nan),
convertType(e->getType())));
}
@@ -542,7 +542,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Value v = emitScalarExpr(e->getArg(0));
mlir::Location loc = getLoc(e->getBeginLoc());
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, FPClassTest::fcSNan),
+ builder.createIsFPClass(loc, v, cir::FPClassTest::SignalingNaN),
convertType(e->getType())));
}
@@ -552,7 +552,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
assert(!cir::MissingFeatures::fpConstraints());
mlir::Location loc = getLoc(e->getBeginLoc());
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, FPClassTest::fcInf),
+ builder.createIsFPClass(loc, v, cir::FPClassTest::Infinity),
convertType(e->getType())));
}
@@ -568,7 +568,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
assert(!cir::MissingFeatures::fpConstraints());
mlir::Location loc = getLoc(e->getBeginLoc());
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, FPClassTest::fcFinite),
+ builder.createIsFPClass(loc, v, cir::FPClassTest::Finite),
convertType(e->getType())));
}
@@ -577,7 +577,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Value v = emitScalarExpr(e->getArg(0));
mlir::Location loc = getLoc(e->getBeginLoc());
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, FPClassTest::fcNormal),
+ builder.createIsFPClass(loc, v, cir::FPClassTest::Normal),
convertType(e->getType())));
}
@@ -586,7 +586,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Value v = emitScalarExpr(e->getArg(0));
mlir::Location loc = getLoc(e->getBeginLoc());
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, FPClassTest::fcSubnormal),
+ builder.createIsFPClass(loc, v, cir::FPClassTest::Subnormal),
convertType(e->getType())));
}
@@ -595,7 +595,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Value v = emitScalarExpr(e->getArg(0));
mlir::Location loc = getLoc(e->getBeginLoc());
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, FPClassTest::fcZero),
+ builder.createIsFPClass(loc, v, cir::FPClassTest::Zero),
convertType(e->getType())));
}
case Builtin::BI__builtin_isfpclass: {
@@ -609,7 +609,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Location loc = getLoc(e->getBeginLoc());
//
return RValue::get(builder.createBoolToInt(
- builder.createIsFPClass(loc, v, test), convertType(e->getType())));
+ builder.createIsFPClass(loc, v, cir::FPClassTest(test)), convertType(e->getType())));
}
}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5b818b8cd9a8a..94fc4a4244148 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -674,10 +674,10 @@ mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite(
cir::IsFPClassOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Value src = adaptor.getSrc();
- uint32_t flags = adaptor.getFlags();
+ cir::FPClassTest flags = adaptor.getFlags();
mlir::IntegerType retTy = rewriter.getI1Type();
- rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>(op, retTy, src, flags);
+ rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>(op, retTy, src, static_cast<uint32_t>(flags));
return mlir::success();
}
diff --git a/clang/test/CIR/CodeGen/builtin-isfpclass.c b/clang/test/CIR/CodeGen/builtin-isfpclass.c
index 06d473d259e97..16d82c905f445 100644
--- a/clang/test/CIR/CodeGen/builtin-isfpclass.c
+++ b/clang/test/CIR/CodeGen/builtin-isfpclass.c
@@ -10,32 +10,32 @@ 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 %{{.*}}, 516 : (!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 %{{.*}}, 516 : (!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 %{{.*}}, 516 : (!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 %{{.*}}, 516 : (!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 %{{.*}}, 504 : (!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 %{{.*}}, 504 : (!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)
@@ -44,25 +44,25 @@ void test_is_finite(__fp16 *H, float F, double D, long double LD) {
// LLVM: call i32 @finite(double {{.*}})
// OGCG: call i1 @llvm.is.fpclass.f64(double %20, i32 504)
res = __builtin_isnormal(*H);
- // CIR: cir.is_fp_class %{{.*}}, 264 : (!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 %{{.*}}, 264 : (!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 %{{.*}}, 144 : (!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 %{{.*}}, 96 : (!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);
- // CIR: cir.is_fp_class %{{.*}}, 1 : (!cir.float) -> !cir.bool
+ // CIR: cir.is_fp_class %{{.*}}, fcSNan : (!cir.float) -> !cir.bool
// LLVM: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1)
// OGCG: call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1)
}
@@ -72,7 +72,7 @@ _Bool check_isfpclass_finite(float x) {
}
// CIR: cir.func {{.*}}@check_isfpclass_finite
-// CIR: cir.is_fp_class %{{.*}}, 504 : (!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 %{{.*}}, 3 : (!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
@@ -95,7 +95,7 @@ _Bool check_isfpclass_snan_f64(double x) {
}
// CIR: cir.func {{.*}}@check_isfpclass_snan_f64
-// CIR: cir.is_fp_class %{{.*}}, 1 : (!cir.double)
+// CIR: cir.is_fp_class %{{.*}}, fcSNan : (!cir.double)
// LLVM: @check_isfpclass_snan_f64
// LLVM: call i1 @llvm.is.fpclass.f64(double {{.*}}, i32 1)
// OGCG: @check_isfpclass_snan_f64
@@ -107,7 +107,7 @@ _Bool check_isfpclass_zero_f16(_Float16 x) {
}
// CIR: cir.func {{.*}}@check_isfpclass_zero_f16
-// CIR: cir.is_fp_class %{{.*}}, 96 : (!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
More information about the cfe-commits
mailing list