[libc-commits] [clang] [libc] [clang] Make __builtin_exp and __builtin_expf constexpr. (PR #199808)
via libc-commits
libc-commits at lists.llvm.org
Thu Jun 25 04:48:08 PDT 2026
https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/199808
>From f25ce91c3fa4b5d2b30cea84b2d1ad25c4416294 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/8] [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 63cdb787bea16..e55fcae5846aa 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4162,7 +4162,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 b76f13833da14..9559b16ff1130 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;
@@ -4613,6 +4621,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 5ee27dd4e2ba2..6d24e888b0fb4 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20118,6 +20118,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 5ffdc61e30ad593ac558763593bd04cd333f577e 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/8] 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 9559b16ff1130..8d7c309180175 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;
}
@@ -4623,7 +4634,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 6d24e888b0fb4..9dddcace6ecc9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20123,7 +20123,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 31cb03c42eec8754f3d8eaba4dcdd393ebe95ef1 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/8] 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 c790b32a34963dd3435cbe47409c408059455521 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/8] 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 ca3a29a9a7debc9b3e9e8a428132c6e4fb072886 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/8] 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 8d7c309180175..f24ff2e19b367 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 6e94f9cbdf3c353e2635d3c0e2932b8af3d1b4ca Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Thu, 25 Jun 2026 06:05:32 +0000
Subject: [PATCH 6/8] 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 f24ff2e19b367..1add407a77919 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 47a3f6de071aa57ee061f0187997697bdc56da2e Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Thu, 25 Jun 2026 06:07:48 +0000
Subject: [PATCH 7/8] 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 d6c68412564a9a1ee58d1974eaa712a72520b677 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Thu, 25 Jun 2026 11:47:33 +0000
Subject: [PATCH 8/8] 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 9dddcace6ecc9..0af21cff481e6 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20124,7 +20124,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())
More information about the libc-commits
mailing list