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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
