[libc-commits] [clang] [libc] [clang] Make __builtin_exp and __builtin_expf constexpr. (PR #199808)

via libc-commits libc-commits at lists.llvm.org
Fri Jun 19 08:07:42 PDT 2026


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

>From cce2790bc3f4908234ca5becb07a6bc494bc31ec Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Wed, 27 May 2026 01:56:13 +0000
Subject: [PATCH 1/5] [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 053a257ba6d4a..8dffdec359336 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4150,7 +4150,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 b16a34543757b..ef32620f3d18a 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;
@@ -4503,6 +4511,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 6ac16c2b831d2..dfc633ac46617 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20009,6 +20009,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 4f5d91d6d512dbf456ce952f18f3ee7335fd8f84 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Wed, 17 Jun 2026 05:35:55 +0000
Subject: [PATCH 2/5] 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 ef32620f3d18a..cd5cf81874c98 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;
 }
 
@@ -4513,7 +4524,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 dfc633ac46617..e7ca34b2ef992 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20014,7 +20014,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 dd622099bf39d72221a163195166d4e0b9ad7128 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Thu, 18 Jun 2026 20:28:05 +0000
Subject: [PATCH 3/5] 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 431155842a73e4b48003ff03ea72f52b595d8cc5 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 19 Jun 2026 04:49:06 +0000
Subject: [PATCH 4/5] 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 6c092b8b2bdfebb254b945671c83a7cd391006c9 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 19 Jun 2026 15:05:57 +0000
Subject: [PATCH 5/5] 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 cd5cf81874c98..12ada481057bc 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;
 }
 



More information about the libc-commits mailing list