https://github.com/badumbatish updated 
https://github.com/llvm/llvm-project/pull/166037

>From 3ab831e2335304bf83c076d07bb233697708d4d6 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <[email protected]>
Date: Sat, 1 Nov 2025 20:32:55 -0700
Subject: [PATCH] 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        |  19 +++
 clang/lib/CIR/CodeGen/TargetInfo.h            |   8 ++
 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 +++++++++++++++++
 14 files changed, 701 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 3288f5b12c77e..a680ac58cc904 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -442,6 +442,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 86d09d72fe6ca..a6ee8c668efec 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3921,6 +3921,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 598e826a473a6..d9bbd64d029a1 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -256,7 +256,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 50d585dca3b8c..f525f24918cb6 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,
@@ -332,6 +386,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 798e9d9fbb99e..4088018738d57 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));
@@ -454,14 +464,117 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
     assert(!cir::MissingFeatures::coroSizeBuiltinCall());
     return getUndefRValue(e->getType());
   }
+  // 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 d3c0d9f109317..8b1856da22130 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -805,6 +805,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,
@@ -975,6 +991,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 e3b9b6a8180d9..7d9a93bde4894 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"
@@ -174,6 +175,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,
@@ -217,6 +233,9 @@ class CIRGenFunction : public CIRGenTypeCache {
   const TargetInfo &getTarget() const { return cgm.getTarget(); }
   mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
 
+  const TargetCIRGenInfo &getTargetHooks() const {
+    return cgm.getTargetCIRGenInfo();
+  }
   // ---------------------
   // Opaque value handling
   // ---------------------
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h 
b/clang/lib/CIR/CodeGen/TargetInfo.h
index dbb0312c76040..ae55dae4da964 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -48,6 +48,14 @@ class TargetCIRGenInfo {
   virtual cir::TargetAddressSpaceAttr getCIRAllocaAddressSpace() const {
     return {};
   }
+  /// Performs a target specific test of a floating point value for things
+  /// like IsNaN, Infinity, ... Nullptr is returned if no implementation
+  /// exists.
+  virtual mlir::Value testFPKind(mlir::Value v, unsigned builtinId,
+                                 CIRGenBuilderTy &builder,
+                                 CIRGenModule &cgm) const {
+    return {};
+  }
 
   /// Determine whether a call to an unprototyped functions under
   /// the given calling convention should use the variadic
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 bb75f2d94066f..f39d72f2fe593 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -632,6 +632,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*/);
+// }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to