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 <[email protected]> Date: Wed, 27 May 2026 01:56:13 +0000 Subject: [PATCH 1/7] [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 <[email protected]> Date: Wed, 17 Jun 2026 05:35:55 +0000 Subject: [PATCH 2/7] 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 <[email protected]> Date: Thu, 18 Jun 2026 20:28:05 +0000 Subject: [PATCH 3/7] 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 <[email protected]> Date: Fri, 19 Jun 2026 04:49:06 +0000 Subject: [PATCH 4/7] 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 <[email protected]> Date: Fri, 19 Jun 2026 15:05:57 +0000 Subject: [PATCH 5/7] 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 <[email protected]> Date: Thu, 25 Jun 2026 06:05:32 +0000 Subject: [PATCH 6/7] 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 <[email protected]> Date: Thu, 25 Jun 2026 06:07:48 +0000 Subject: [PATCH 7/7] 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; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
