https://github.com/lntue updated 
https://github.com/llvm/llvm-project/pull/199808

>From dcb57bfcabc21b99b221f4acfec5c31bd6d55370 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Wed, 27 May 2026 01:56:13 +0000
Subject: [PATCH 01/11] [clang] Make __builtin_exp and __builtin_expf
 constexpr.

This is step 3 in 
https://discourse.llvm.org/t/rfc-make-clang-builtin-math-functions-constexpr-with-llvm-libc-to-support-c-23-constexpr-math-functions/86450
---
 clang/include/clang/Basic/Builtins.td     |  9 ++++++++-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp  | 16 ++++++++++++++++
 clang/lib/AST/ExprConstant.cpp            | 13 +++++++++++++
 clang/test/Preprocessor/feature_tests.cpp |  4 +++-
 clang/test/Sema/constant-builtins-exp.cpp | 19 +++++++++++++++++++
 5 files changed, 59 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Sema/constant-builtins-exp.cpp

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 344a712ddc585..2188e9d8f4d79 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4218,7 +4218,14 @@ def Erfc : FPMathTemplate, LibBuiltin<"math.h"> {
   let AddBuiltinPrefixedAlias = 1;
 }
 
-def Exp : FPMathTemplate, LibBuiltin<"math.h"> {
+def Exp : Template<["float", "double"], ["f", ""]>, LibBuiltin<"math.h"> {
+  let Spellings = ["exp"];
+  let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr];
+  let Prototype = "T(T)";
+  let AddBuiltinPrefixedAlias = 1;
+}
+
+def Expl : Template<["long double"], ["l"]>, LibBuiltin<"math.h"> {
   let Spellings = ["exp"];
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T)";
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index a539fb26abc08..499d0e3d6e3d1 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -710,6 +710,14 @@ static bool interp__builtin_fpclassify(InterpState &S, 
CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_exp(InterpState &S, CodePtr OpPC,
+                                const InterpFrame *Frame) {
+  const Floating &Arg = S.Stk.pop<Floating>();
+  APFloat Result = exp(Arg.getAPFloat());
+  S.Stk.push<Floating>(Floating(Result));
+  return true;
+}
+
 static inline Floating abs(InterpState &S, const Floating &In) {
   if (!In.isNegative())
     return In;
@@ -4742,6 +4750,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, 
const CallExpr *Call,
   case Builtin::BI__builtin_copysignf128:
     return interp__builtin_copysign(S, OpPC, Frame);
 
+  case Builtin::BI__builtin_exp:
+  case Builtin::BI__builtin_expf:
+    return interp__builtin_exp(S, OpPC, Frame);
+  case Builtin::BI__builtin_expl:
+  case Builtin::BI__builtin_expf16:
+  case Builtin::BI__builtin_expf128:
+    return false;
+
   case Builtin::BI__builtin_fmin:
   case Builtin::BI__builtin_fminf:
   case Builtin::BI__builtin_fminl:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 574dd8b04e779..60f425589dacb 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20510,6 +20510,19 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     return true;
   }
 
+  case Builtin::BI__builtin_exp:
+  case Builtin::BI__builtin_expf: {
+    APFloat Input(0.);
+    if (!EvaluateFloat(E->getArg(0), Input, Info))
+      return false;
+    Result = exp(Input);
+    return true;
+  }
+  case Builtin::BI__builtin_expl:
+  case Builtin::BI__builtin_expf16:
+  case Builtin::BI__builtin_expf128:
+    return false;
+
   case Builtin::BI__builtin_fmax:
   case Builtin::BI__builtin_fmaxf:
   case Builtin::BI__builtin_fmaxl:
diff --git a/clang/test/Preprocessor/feature_tests.cpp 
b/clang/test/Preprocessor/feature_tests.cpp
index 029f446113af4..b49376a14644f 100644
--- a/clang/test/Preprocessor/feature_tests.cpp
+++ b/clang/test/Preprocessor/feature_tests.cpp
@@ -60,7 +60,9 @@
 #if  !__has_constexpr_builtin(__builtin_fmax) || \
      !__has_constexpr_builtin(__builtin_fmin) || \
      !__has_constexpr_builtin(__builtin_fmaximum_num) || \
-     !__has_constexpr_builtin(__builtin_fminimum_num)
+     !__has_constexpr_builtin(__builtin_fminimum_num) || \
+     !__has_constexpr_builtin(__builtin_exp) || \
+     !__has_constexpr_builtin(__builtin_expf)
 #error Clang should have these constexpr builtins
 #endif
 
diff --git a/clang/test/Sema/constant-builtins-exp.cpp 
b/clang/test/Sema/constant-builtins-exp.cpp
new file mode 100644
index 0000000000000..215d2c2765961
--- /dev/null
+++ b/clang/test/Sema/constant-builtins-exp.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify 
-fexperimental-new-constant-interpreter %s
+// expected-no-diagnostics
+
+constexpr float InfFloat = __builtin_inff();
+constexpr float NegInfFloat = -__builtin_inff();
+
+static_assert(InfFloat == __builtin_expf(InfFloat));
+static_assert(0.0f == __builtin_expf(NegInfFloat));
+static_assert(1.0f == __builtin_expf(0.0f));
+static_assert(0x1.5bf0a8p1f == __builtin_expf(1.0f));
+
+constexpr double InfDouble = __builtin_inf();
+constexpr double NegInfDouble = -__builtin_inf();
+
+static_assert(InfDouble == __builtin_exp(InfDouble));
+static_assert(0.0 == __builtin_exp(NegInfDouble));
+static_assert(1.0 == __builtin_exp(0.0));
+static_assert(0x1.5bf0a8b145769p1 == __builtin_exp(1.0));

>From 7588cb8f64cd00ec9a76487142011a8f604aef94 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Wed, 17 Jun 2026 05:35:55 +0000
Subject: [PATCH 02/11] Use updated version of APFloat::exp to correctly report
 supported and unsupported cases.

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp  | 19 +++++++++++++++----
 clang/lib/AST/ExprConstant.cpp            | 11 ++++++++++-
 clang/test/Sema/constant-builtins-exp.cpp | 11 ++++++++++-
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 499d0e3d6e3d1..8153858785e8c 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -23,6 +23,7 @@
 #include "llvm/Support/AllocToken.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/SipHash.h"
+#include <optional>
 
 namespace clang {
 namespace interp {
@@ -711,10 +712,20 @@ static bool interp__builtin_fpclassify(InterpState &S, 
CodePtr OpPC,
 }
 
 static bool interp__builtin_exp(InterpState &S, CodePtr OpPC,
-                                const InterpFrame *Frame) {
+                                const InterpFrame *Frame,
+                                const CallExpr *Call) {
   const Floating &Arg = S.Stk.pop<Floating>();
-  APFloat Result = exp(Arg.getAPFloat());
-  S.Stk.push<Floating>(Floating(Result));
+  FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts());
+  llvm::RoundingMode RM = getRoundingMode(FPO);
+  APFloat::opStatus Status;
+  std::optional<APFloat> Result = exp(Arg.getAPFloat(), RM, &Status);
+  // Check for unsupported rounding modes.
+  if (!Result.has_value())
+    return false;
+  // Check for raised non-FE_INEXACT exceptions.
+  if (Status & (~APFloat::opStatus::opInexact))
+    return false;
+  S.Stk.push<Floating>(Floating(*Result));
   return true;
 }
 
@@ -4752,7 +4763,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const 
CallExpr *Call,
 
   case Builtin::BI__builtin_exp:
   case Builtin::BI__builtin_expf:
-    return interp__builtin_exp(S, OpPC, Frame);
+    return interp__builtin_exp(S, OpPC, Frame, Call);
   case Builtin::BI__builtin_expl:
   case Builtin::BI__builtin_expf16:
   case Builtin::BI__builtin_expf128:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 60f425589dacb..def6b775b9952 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20515,7 +20515,16 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     APFloat Input(0.);
     if (!EvaluateFloat(E->getArg(0), Input, Info))
       return false;
-    Result = exp(Input);
+    llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
+    APFloat::opStatus Status;
+    std::optional<APFloat> r = exp(Input, RM, &Status);
+    // Check for unsupported rounding modes.
+    if (!r.has_value())
+      return false;
+    // Check for raised non-FE_INEXACT exceptions.
+    if (Status & (~APFloat::opStatus::opInexact))
+      return false;
+    Result = *r;
     return true;
   }
   case Builtin::BI__builtin_expl:
diff --git a/clang/test/Sema/constant-builtins-exp.cpp 
b/clang/test/Sema/constant-builtins-exp.cpp
index 215d2c2765961..d1e220dc94d86 100644
--- a/clang/test/Sema/constant-builtins-exp.cpp
+++ b/clang/test/Sema/constant-builtins-exp.cpp
@@ -1,19 +1,28 @@
 // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify 
-fexperimental-new-constant-interpreter %s
-// expected-no-diagnostics
 
 constexpr float InfFloat = __builtin_inff();
 constexpr float NegInfFloat = -__builtin_inff();
+constexpr float qNaNFloat = __builtin_nanf("");
 
+static_assert(__builtin_expf(qNaNFloat) != __builtin_expf(qNaNFloat));
 static_assert(InfFloat == __builtin_expf(InfFloat));
 static_assert(0.0f == __builtin_expf(NegInfFloat));
 static_assert(1.0f == __builtin_expf(0.0f));
 static_assert(0x1.5bf0a8p1f == __builtin_expf(1.0f));
 
+// No constexpr for overflow.
+static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+
 constexpr double InfDouble = __builtin_inf();
 constexpr double NegInfDouble = -__builtin_inf();
+constexpr double qNaNDouble = __builtin_nan("");
 
+static_assert(__builtin_exp(qNaNDouble) != __builtin_exp(qNaNDouble));
 static_assert(InfDouble == __builtin_exp(InfDouble));
 static_assert(0.0 == __builtin_exp(NegInfDouble));
 static_assert(1.0 == __builtin_exp(0.0));
 static_assert(0x1.5bf0a8b145769p1 == __builtin_exp(1.0));
+
+// No constexpr for overflow.
+static_assert(InfDouble == __builtin_expf(1000.0)); // expected-error {{static 
assertion expression is not an integral constant expression}}

>From 900951dfe956867b6d7827d0a4ec07ffcdd37404 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Thu, 18 Jun 2026 20:28:05 +0000
Subject: [PATCH 03/11] Fix test.

---
 clang/test/Sema/constant-builtins-exp.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Sema/constant-builtins-exp.cpp 
b/clang/test/Sema/constant-builtins-exp.cpp
index d1e220dc94d86..4d68f04e381a1 100644
--- a/clang/test/Sema/constant-builtins-exp.cpp
+++ b/clang/test/Sema/constant-builtins-exp.cpp
@@ -25,4 +25,4 @@ static_assert(1.0 == __builtin_exp(0.0));
 static_assert(0x1.5bf0a8b145769p1 == __builtin_exp(1.0));
 
 // No constexpr for overflow.
-static_assert(InfDouble == __builtin_expf(1000.0)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error {{static 
assertion expression is not an integral constant expression}}

>From 16a2844ab0fc896c93bb6d6fe261de3c626db5d3 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Fri, 19 Jun 2026 04:49:06 +0000
Subject: [PATCH 04/11] Try to see whether FE_* exceptions are 0.

---
 libc/src/__support/math/check/exp_exceptions.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libc/src/__support/math/check/exp_exceptions.h 
b/libc/src/__support/math/check/exp_exceptions.h
index 16b92cf8110ff..19d2fba3e5f4b 100644
--- a/libc/src/__support/math/check/exp_exceptions.h
+++ b/libc/src/__support/math/check/exp_exceptions.h
@@ -57,6 +57,12 @@ template <> struct Bounds<double> {
 } // namespace exp_internal
 
 template <typename T> LIBC_INLINE int exp_exceptions(T x, int rounding_mode) {
+  static_assert(FE_OVERFLOW != 0);
+  static_assert(FE_UNDERFLOW != 0);
+  static_assert(FE_INEXACT != 0);
+  static_assert(FE_INVALID != 0);
+  static_assert(FE_DIVBYZERO != 0);
+
   using FPBits = typename fputil::FPBits<T>;
   using StorageType = typename FPBits::StorageType;
 

>From b7936d5166a56e0c02c1f66bd19210ca449366b1 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Fri, 19 Jun 2026 15:05:57 +0000
Subject: [PATCH 05/11] Allocate a new Floating.

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 8153858785e8c..dc313bf37383f 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -725,7 +725,9 @@ static bool interp__builtin_exp(InterpState &S, CodePtr 
OpPC,
   // Check for raised non-FE_INEXACT exceptions.
   if (Status & (~APFloat::opStatus::opInexact))
     return false;
-  S.Stk.push<Floating>(Floating(*Result));
+  Floating Res = S.allocFloat(Arg.getSemantics());
+  Res.copy(*Result);
+  S.Stk.push<Floating>(Res);
   return true;
 }
 

>From e53cfe371c441326775776cd8f6cd7d37521776e Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Thu, 25 Jun 2026 06:05:32 +0000
Subject: [PATCH 06/11] Initialize Status.

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index dc313bf37383f..1fb86dc15d9bc 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -717,7 +717,7 @@ static bool interp__builtin_exp(InterpState &S, CodePtr 
OpPC,
   const Floating &Arg = S.Stk.pop<Floating>();
   FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts());
   llvm::RoundingMode RM = getRoundingMode(FPO);
-  APFloat::opStatus Status;
+  APFloat::opStatus Status = APFloat::opStatus::opOK;
   std::optional<APFloat> Result = exp(Arg.getAPFloat(), RM, &Status);
   // Check for unsupported rounding modes.
   if (!Result.has_value())

>From 4014d8263999e04f1b5164b84305ba3050d881ec Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Thu, 25 Jun 2026 06:07:48 +0000
Subject: [PATCH 07/11] Revert libc changes.

---
 libc/src/__support/math/check/exp_exceptions.h | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/libc/src/__support/math/check/exp_exceptions.h 
b/libc/src/__support/math/check/exp_exceptions.h
index 19d2fba3e5f4b..16b92cf8110ff 100644
--- a/libc/src/__support/math/check/exp_exceptions.h
+++ b/libc/src/__support/math/check/exp_exceptions.h
@@ -57,12 +57,6 @@ template <> struct Bounds<double> {
 } // namespace exp_internal
 
 template <typename T> LIBC_INLINE int exp_exceptions(T x, int rounding_mode) {
-  static_assert(FE_OVERFLOW != 0);
-  static_assert(FE_UNDERFLOW != 0);
-  static_assert(FE_INEXACT != 0);
-  static_assert(FE_INVALID != 0);
-  static_assert(FE_DIVBYZERO != 0);
-
   using FPBits = typename fputil::FPBits<T>;
   using StorageType = typename FPBits::StorageType;
 

>From 2ab459254f809f47cd50c651e5a33a9a02437267 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Thu, 25 Jun 2026 11:47:33 +0000
Subject: [PATCH 08/11] Initialize Status.

---
 clang/lib/AST/ExprConstant.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index def6b775b9952..daf62851a73d1 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20516,7 +20516,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     if (!EvaluateFloat(E->getArg(0), Input, Info))
       return false;
     llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
-    APFloat::opStatus Status;
+    APFloat::opStatus Status = APFloat::opStatus::opOK;
     std::optional<APFloat> r = exp(Input, RM, &Status);
     // Check for unsupported rounding modes.
     if (!r.has_value())

>From 5cc225f5d8cfb97f85d753be304ebbf9ed9335f0 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Fri, 17 Jul 2026 04:20:33 +0000
Subject: [PATCH 09/11] Remove LLVM_READONLY attribute from APFloat::exp.

---
 llvm/include/llvm/ADT/APFloat.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index ce94e0bacbeca..b81e14760fed5 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1804,7 +1804,6 @@ inline APFloat maximumnum(const APFloat &A, const APFloat 
&B) {
 }
 
 /// Implement IEEE 754-2019 exp functions
-LLVM_READONLY
 LLVM_ABI std::optional<APFloat>
 exp(const APFloat &X, RoundingMode RM = APFloat::rmNearestTiesToEven,
     APFloat::opStatus *Status = nullptr);

>From d0bdcbefc0480146eb0594318c155fccf40e8ac2 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Sun, 19 Jul 2026 16:59:35 +0000
Subject: [PATCH 10/11] Let __builtin_exp(f) respect FENV_ACCESS and FPE
 strict, also update checkFloatingPointResults to match expectations.

---
 clang/lib/AST/ByteCode/Interp.cpp         | 69 ++++++++++++++---------
 clang/lib/AST/ByteCode/Interp.h           |  8 +++
 clang/lib/AST/ByteCode/InterpBuiltin.cpp  |  8 ++-
 clang/lib/AST/ExprConstant.cpp            | 44 +++++++++------
 clang/test/CodeGen/pragma-fenv_access.c   | 33 +++++++++++
 clang/test/Sema/constant-builtins-exp.cpp | 25 +++++++-
 6 files changed, 140 insertions(+), 47 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index f59485ec306e4..348d65e7eb2f8 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1188,51 +1188,66 @@ bool CheckThis(InterpState &S, CodePtr OpPC) {
   return false;
 }
 
-bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
-                      APFloat::opStatus Status, FPOptions FPO) {
-  // [expr.pre]p4:
-  //   If during the evaluation of an expression, the result is not
-  //   mathematically defined [...], the behavior is undefined.
-  // FIXME: C++ rules require us to not conform to IEEE 754 here.
-  if (Result.isNan()) {
-    const SourceInfo &E = S.Current->getSource(OpPC);
-    S.CCEDiag(E, diag::note_constexpr_float_arithmetic)
-        << /*NaN=*/true << S.Current->getRange(OpPC);
-    return S.noteUndefinedBehavior();
-  }
-
+bool CheckFloatStatus(InterpState &S, CodePtr OpPC, APFloat::opStatus Status,
+                      FPOptions FPO) {
   // In a constant context, assume that any dynamic rounding mode or FP
   // exception state matches the default floating-point environment.
   if (S.inConstantContext())
     return true;
 
-  if ((Status & APFloat::opInexact) &&
-      FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
-    // Inexact result means that it depends on rounding mode. If the requested
-    // mode is dynamic, the evaluation cannot be made in compile time.
+  // The output result is exact and no exceptions are raised.
+  if (Status == APFloat::opOK)
+    return true;
+
+  // No fenv access and floating point exceptions are ignored, so it is safe to
+  // to perform compile-time evaluation.
+  if (!FPO.getAllowFEnvAccess() &&
+      FPO.getExceptionMode() == LangOptions::FPE_Ignore)
+    return true;
+
+  if (FPO.getAllowFEnvAccess() &&
+      (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic)) {
+    // Some floating point exception is raised, so the output might depend on
+    // rounding mode. If the FENV_ACCESS is "on" and the rounding mode is
+    // dynamic, the evaluation cannot be made in compile time.
     const SourceInfo &E = S.Current->getSource(OpPC);
     S.FFDiag(E, diag::note_constexpr_dynamic_rounding);
     return false;
   }
 
-  if ((Status != APFloat::opOK) &&
-      (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic ||
-       FPO.getExceptionMode() != LangOptions::FPE_Ignore ||
-       FPO.getAllowFEnvAccess())) {
+  if (FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
+    // Some floating point exception is raised and the FP mode is strict or the
+    // exceptions may be trapped.
     const SourceInfo &E = S.Current->getSource(OpPC);
     S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
     return false;
   }
 
-  if ((Status & APFloat::opStatus::opInvalidOp) &&
-      FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
+  // FIXME: if:
+  // - evaluation triggered other FP exception, and
+  // - exception mode is not "ignore", and
+  // - the expression being evaluated is not a part of global variable
+  //   initializer,
+  // the evaluation probably need to be rejected.
+  const SourceInfo &E = S.Current->getSource(OpPC);
+  S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
+  return false;
+}
+
+bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
+                      APFloat::opStatus Status, FPOptions FPO) {
+  // [expr.pre]p4:
+  //   If during the evaluation of an expression, the result is not
+  //   mathematically defined [...], the behavior is undefined.
+  // FIXME: C++ rules require us to not conform to IEEE 754 here.
+  if (Result.isNan()) {
     const SourceInfo &E = S.Current->getSource(OpPC);
-    // There is no usefully definable result.
-    S.FFDiag(E);
-    return false;
+    S.CCEDiag(E, diag::note_constexpr_float_arithmetic)
+        << /*NaN=*/true << S.Current->getRange(OpPC);
+    return S.noteUndefinedBehavior();
   }
 
-  return true;
+  return CheckFloatStatus(S, OpPC, Status, FPO);
 }
 
 bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC) {
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 405f4a29ec982..138a7a811e4b6 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -246,9 +246,17 @@ bool CheckDivRem(InterpState &S, CodePtr OpPC, const T 
&LHS, const T &RHS) {
 
 /// Checks if the result of a floating-point operation is valid
 /// in the current context.
+/// Notes:
+///   - CheckFloatStatus is the same as checkFloatingPointResult in
+///       clang/lib/AST/ExprConstant.cpp.
+///   - CheckFloatResult will also check if the result is NaN, in addition to
+///     CheckFloatStatus's checks.
 bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
                       APFloat::opStatus Status, FPOptions FPO);
 
+bool CheckFloatStatus(InterpState &S, CodePtr OpPC, APFloat::opStatus Status,
+                      FPOptions FPO);
+
 /// Checks why the given DeclRefExpr is invalid.
 bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR);
 bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 1fb86dc15d9bc..9aa1e600fc6b4 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -9,6 +9,7 @@
 #include "Boolean.h"
 #include "Char.h"
 #include "EvalEmitter.h"
+#include "Interp.h"
 #include "InterpBuiltinBitCast.h"
 #include "InterpHelpers.h"
 #include "PrimType.h"
@@ -716,7 +717,9 @@ static bool interp__builtin_exp(InterpState &S, CodePtr 
OpPC,
                                 const CallExpr *Call) {
   const Floating &Arg = S.Stk.pop<Floating>();
   FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts());
-  llvm::RoundingMode RM = getRoundingMode(FPO);
+  llvm::RoundingMode RM = llvm::RoundingMode::NearestTiesToEven;
+  if (S.inConstantContext())
+    RM = getRoundingMode(FPO);
   APFloat::opStatus Status = APFloat::opStatus::opOK;
   std::optional<APFloat> Result = exp(Arg.getAPFloat(), RM, &Status);
   // Check for unsupported rounding modes.
@@ -727,6 +730,9 @@ static bool interp__builtin_exp(InterpState &S, CodePtr 
OpPC,
     return false;
   Floating Res = S.allocFloat(Arg.getSemantics());
   Res.copy(*Result);
+  // Add diagnostic when not in constant evaluation context.
+  if (!CheckFloatStatus(S, OpPC, Status, FPO))
+    return false;
   S.Stk.push<Floating>(Res);
   return true;
 }
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index daf62851a73d1..b209aa273998c 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2707,37 +2707,43 @@ static bool checkFloatingPointResult(EvalInfo &Info, 
const Expr *E,
   if (Info.InConstantContext)
     return true;
 
+  // The output result is exact and no exceptions are raised, so it is safe to
+  // perform compile-time evaluation.
+  if (St == APFloat::opOK)
+    return true;
+
   FPOptions FPO = E->getFPFeaturesInEffect(Info.getLangOpts());
-  if ((St & APFloat::opInexact) &&
-      FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
-    // Inexact result means that it depends on rounding mode. If the requested
-    // mode is dynamic, the evaluation cannot be made in compile time.
+
+  // No fenv access and floating point exceptions are ignored, so it is safe to
+  // to perform compile-time evaluation.
+  if (!FPO.getAllowFEnvAccess() &&
+      FPO.getExceptionMode() == LangOptions::FPE_Ignore)
+    return true;
+
+  if (FPO.getAllowFEnvAccess() &&
+      (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic)) {
+    // Some floating point exception is raised, so the result might depend on
+    // rounding mode. If the FENV_ACCESS is "on" and the rounding mode is
+    // dynamic, the evaluation cannot be made in compile time.
     Info.FFDiag(E, diag::note_constexpr_dynamic_rounding);
     return false;
   }
 
-  if ((St != APFloat::opOK) &&
-      (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic ||
-       FPO.getExceptionMode() != LangOptions::FPE_Ignore ||
-       FPO.getAllowFEnvAccess())) {
+  if (FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
+    // Some floating point exception is raised and the FP mode is strict or the
+    // exceptions may be trapped.
     Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
     return false;
   }
 
-  if ((St & APFloat::opStatus::opInvalidOp) &&
-      FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
-    // There is no usefully definable result.
-    Info.FFDiag(E);
-    return false;
-  }
-
   // FIXME: if:
   // - evaluation triggered other FP exception, and
   // - exception mode is not "ignore", and
   // - the expression being evaluated is not a part of global variable
   //   initializer,
   // the evaluation probably need to be rejected.
-  return true;
+  Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
+  return false;
 }
 
 static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
@@ -20515,7 +20521,9 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     APFloat Input(0.);
     if (!EvaluateFloat(E->getArg(0), Input, Info))
       return false;
-    llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
+    llvm::RoundingMode RM = llvm::RoundingMode::NearestTiesToEven;
+    if (Info.InConstantContext)
+      RM = getActiveRoundingMode(Info, E);
     APFloat::opStatus Status = APFloat::opStatus::opOK;
     std::optional<APFloat> r = exp(Input, RM, &Status);
     // Check for unsupported rounding modes.
@@ -20524,6 +20532,8 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     // Check for raised non-FE_INEXACT exceptions.
     if (Status & (~APFloat::opStatus::opInexact))
       return false;
+    if (!checkFloatingPointResult(Info, E, Status))
+      return false;
     Result = *r;
     return true;
   }
diff --git a/clang/test/CodeGen/pragma-fenv_access.c 
b/clang/test/CodeGen/pragma-fenv_access.c
index 76c38f957d632..9763ac926de82 100644
--- a/clang/test/CodeGen/pragma-fenv_access.c
+++ b/clang/test/CodeGen/pragma-fenv_access.c
@@ -287,3 +287,36 @@ vector3ulong func_23(vector3float x) {
 }
 // CHECK-LABEL: @func_23
 // STRICT: call <3 x i64> @llvm.experimental.constrained.fptoui.v3i64.v3f32(<3 
x float> {{.*}}, metadata !"fpexcept.ignore")
+
+float func_26() {
+  #pragma STDC FENV_ACCESS ON
+  return __builtin_expf(1.0F);
+}
+// CHECK-LABEL: @func_26
+// CHECK: call float @llvm.experimental.constrained.exp.f32(float 
1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict")
+
+
+float func_27() {
+  #pragma STDC FENV_ACCESS ON
+  return __builtin_expf(0.0F);
+}
+// CHECK-LABEL: @func_27
+// CHECK: ret float 1.000000e+00
+
+
+float func_28() {
+  #pragma STDC FENV_ACCESS ON
+  return __builtin_expf(__builtin_inff());
+}
+// CHECK-LABEL: @func_28
+// CHECK: ret float +inf
+
+
+float func_29() {
+  #pragma STDC FENV_ACCESS ON
+  return __builtin_expf(-__builtin_inff());
+}
+// CHECK-LABEL: @func_29
+// CHECK: ret float 0.000000e+00
+
+
diff --git a/clang/test/Sema/constant-builtins-exp.cpp 
b/clang/test/Sema/constant-builtins-exp.cpp
index 4d68f04e381a1..11120696f2615 100644
--- a/clang/test/Sema/constant-builtins-exp.cpp
+++ b/clang/test/Sema/constant-builtins-exp.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify 
-fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-unknown-pragmas %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify 
-fexperimental-new-constant-interpreter -Wno-unknown-pragmas %s
 
 constexpr float InfFloat = __builtin_inff();
 constexpr float NegInfFloat = -__builtin_inff();
@@ -26,3 +26,24 @@ static_assert(0x1.5bf0a8b145769p1 == __builtin_exp(1.0));
 
 // No constexpr for overflow.
 static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+
+// No constexpr for underflow.
+static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+static_assert(0.0 == __builtin_exp(-1000.0)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+
+void test_rounding_modes() {
+  {
+    #pragma STDC FENV_ROUND FE_DOWNWARD
+    static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error 
{{static assertion expression is not an integral constant expression}}
+    static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error 
{{static assertion expression is not an integral constant expression}}
+    static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+    static_assert(0.0 == __builtin_exp(-1000.0)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+  }
+  {
+    #pragma STDC FENV_ROUND FE_TOWARDZERO
+    static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error 
{{static assertion expression is not an integral constant expression}}
+    static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error 
{{static assertion expression is not an integral constant expression}}
+    static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+    static_assert(0.0 == __builtin_exp(-1000.0)); // expected-error {{static 
assertion expression is not an integral constant expression}}
+  }
+}

>From 8a111d574bff431de678a308a76dc6a37c50f449 Mon Sep 17 00:00:00 2001
From: Tue Ly <[email protected]>
Date: Sun, 19 Jul 2026 19:21:57 +0000
Subject: [PATCH 11/11] Correct the condition for -frounding-math without
 FENV_ACCESS.

---
 clang/lib/AST/ByteCode/Interp.cpp | 19 +++++++++----------
 clang/lib/AST/ExprConstant.cpp    | 17 ++++++++---------
 2 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 348d65e7eb2f8..96a4bbe589147 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1199,22 +1199,21 @@ bool CheckFloatStatus(InterpState &S, CodePtr OpPC, 
APFloat::opStatus Status,
   if (Status == APFloat::opOK)
     return true;
 
-  // No fenv access and floating point exceptions are ignored, so it is safe to
-  // to perform compile-time evaluation.
-  if (!FPO.getAllowFEnvAccess() &&
-      FPO.getExceptionMode() == LangOptions::FPE_Ignore)
-    return true;
-
-  if (FPO.getAllowFEnvAccess() &&
-      (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic)) {
+  if (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
     // Some floating point exception is raised, so the output might depend on
-    // rounding mode. If the FENV_ACCESS is "on" and the rounding mode is
-    // dynamic, the evaluation cannot be made in compile time.
+    // rounding mode. If the requested mode is dynamic, the evaluation cannot 
be
+    // made in compile time.
     const SourceInfo &E = S.Current->getSource(OpPC);
     S.FFDiag(E, diag::note_constexpr_dynamic_rounding);
     return false;
   }
 
+  // No fenv access and floating point exceptions are ignored, so it is safe to
+  // to perform compile-time evaluation.
+  if (!FPO.getAllowFEnvAccess() &&
+      FPO.getExceptionMode() == LangOptions::FPE_Ignore)
+    return true;
+
   if (FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
     // Some floating point exception is raised and the FP mode is strict or the
     // exceptions may be trapped.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b209aa273998c..0d6c904c2fd92 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2714,21 +2714,20 @@ static bool checkFloatingPointResult(EvalInfo &Info, 
const Expr *E,
 
   FPOptions FPO = E->getFPFeaturesInEffect(Info.getLangOpts());
 
+  if (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
+    // Some floating point exception is raised, so the result might depend on
+    // rounding mode. If the requested mode is dynamic, the evaluation cannot
+    // be made in compile time.
+    Info.FFDiag(E, diag::note_constexpr_dynamic_rounding);
+    return false;
+  }
+
   // No fenv access and floating point exceptions are ignored, so it is safe to
   // to perform compile-time evaluation.
   if (!FPO.getAllowFEnvAccess() &&
       FPO.getExceptionMode() == LangOptions::FPE_Ignore)
     return true;
 
-  if (FPO.getAllowFEnvAccess() &&
-      (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic)) {
-    // Some floating point exception is raised, so the result might depend on
-    // rounding mode. If the FENV_ACCESS is "on" and the rounding mode is
-    // dynamic, the evaluation cannot be made in compile time.
-    Info.FFDiag(E, diag::note_constexpr_dynamic_rounding);
-    return false;
-  }
-
   if (FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
     // Some floating point exception is raised and the FP mode is strict or the
     // exceptions may be trapped.

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

Reply via email to