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/15] [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 344a712ddc585c..2188e9d8f4d796 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 a539fb26abc08d..499d0e3d6e3d13 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 574dd8b04e7794..60f425589dacb4 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 029f446113af45..b49376a14644fa 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 00000000000000..215d2c27659613 --- /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/15] 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 499d0e3d6e3d13..8153858785e8c4 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 60f425589dacb4..def6b775b99529 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 215d2c27659613..d1e220dc94d865 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/15] 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 d1e220dc94d865..4d68f04e381a19 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/15] 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 16b92cf8110ff6..19d2fba3e5f4bf 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/15] 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 8153858785e8c4..dc313bf37383f4 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/15] 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 dc313bf37383f4..1fb86dc15d9bcf 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/15] 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 19d2fba3e5f4bf..16b92cf8110ff6 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/15] 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 def6b775b99529..daf62851a73d1b 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/15] 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 ce94e0bacbeca8..b81e14760fed55 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/15] 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 f59485ec306e4c..348d65e7eb2f85 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 405f4a29ec9827..138a7a811e4b62 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 1fb86dc15d9bcf..9aa1e600fc6b42 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 daf62851a73d1b..b209aa273998ce 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 76c38f957d632f..9763ac926de82a 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 4d68f04e381a19..11120696f26157 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/15] 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 348d65e7eb2f85..96a4bbe589147f 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 b209aa273998ce..0d6c904c2fd920 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. >From e8c3af9a1630756c2863bcad2718f4b8a9aed5ad Mon Sep 17 00:00:00 2001 From: Tue Ly <[email protected]> Date: Sun, 13 Sep 2026 03:21:46 +0000 Subject: [PATCH 12/15] Address comments: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dynamic rounding mode and exception handling: - Only suppress constant folding in dynamic rounding mode when the result is inexact (`Status & opInexact`), allowing exact values (e.g. division by zero to infinity) to fold. - Address the FIXME in `checkFloatingPointResultForConstantFolding` and `CheckFloatStatus`: permit static and global variable initializers to fold at translation time under default FP settings (ISO C17 ยง7.6.1p2). - `-fmath-errno` handling: - Move `-fmath-errno` checking out of general floating-point result checking and into math builtin evaluators, ensuring core arithmetic operators are unaffected. - Suppress runtime constant folding for math builtins only when domain, pole, or range errors would set `errno` at runtime. - Add `note_constexpr_math_errno` diagnostic note. - Diagnostics & Rounding: - Refactor math builtin evaluation to use `getActiveRoundingMode`, removing hardcoded `NearestTiesToEven`. - Update diagnostic notes to standard-aligned phrasing: `note_constexpr_float_{overflow,underflow,divide_by_zero,invalid_op}`. - Tests: - Add `clang/test/CodeGen/builtin-exp-folding.c` to test runtime folding and suppression under `-fmath-errno`, `-fno-math-errno`, `-frounding-math`, and `-ffp-exception-behavior=strict`. - In `clang/test/Sema/constant-builtins-exp.cpp`: - Add underflow tests for normal inputs yielding subnormals (`-88.0f`, `-709.0`). - Test quiet NaN with `__builtin_isnan`. - Add tests and FIXMEs for directed rounding modes (`FE_DOWNWARD`, `FE_UPWARD`, `FE_TOWARDZERO`). --- .../include/clang/Basic/DiagnosticASTKinds.td | 16 +- clang/lib/AST/ByteCode/Interp.cpp | 42 +++-- clang/lib/AST/ByteCode/InterpBuiltin.cpp | 52 +++++- clang/lib/AST/ExprConstant.cpp | 117 ++++++++---- clang/test/CodeGen/builtin-exp-folding.c | 173 ++++++++++++++++++ clang/test/Sema/constant-builtins-exp.cpp | 102 ++++++++++- .../SemaCXX/constexpr-x86-avx-builtins.cpp | 2 +- .../constexpr-x86-avx512f-builtins.cpp | 6 +- .../constexpr-x86-avx512vl-builtins.cpp | 2 +- .../SemaCXX/constexpr-x86-sse2-builtins.cpp | 8 +- 10 files changed, 444 insertions(+), 76 deletions(-) create mode 100644 clang/test/CodeGen/builtin-exp-folding.c diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 0aca1f75428f8a..9be5866422ddc5 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -89,10 +89,22 @@ def note_constexpr_array_index : Note<"cannot refer to element %0 of " "in a constant expression">; def note_constexpr_float_arithmetic : Note< "floating point arithmetic produces %select{an infinity|a NaN}0">; +def note_constexpr_float_underflow : Note< + "call to %0 raises a floating-point underflow exception">; +def note_constexpr_float_overflow : Note< + "call to %0 raises a floating-point overflow exception">; +def note_constexpr_float_divide_by_zero : Note< + "call to %0 raises a floating-point divide-by-zero exception">; +def note_constexpr_float_invalid_op : Note< + "call to %0 raises an invalid floating-point operation exception">; +def note_constexpr_unsupported_rounding : Note< + "cannot evaluate call to %0 in '%1' rounding mode">; def note_constexpr_dynamic_rounding : Note< - "cannot evaluate this expression if rounding mode is dynamic">; + "cannot evaluate this expression in dynamic rounding mode">; def note_constexpr_float_arithmetic_strict : Note< - "compile time floating point arithmetic suppressed in strict evaluation modes">; + "compile-time floating-point evaluation suppressed in strict evaluation modes">; +def note_constexpr_math_errno : Note< + "call to %0 sets 'errno' at runtime">; def note_constexpr_pointer_subtraction_not_same_array : Note< "subtracted pointers are not elements of the same array">; def note_constexpr_pointer_subtraction_zero_size : Note< diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 2d0ce641406e1d..d47a7f32acb0d8 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1246,35 +1246,41 @@ bool CheckFloatStatus(InterpState &S, CodePtr OpPC, APFloat::opStatus Status, if (Status == APFloat::opOK) return true; - if (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) { - // Some floating point exception is raised, so the output might depend on - // rounding mode. If the requested mode is dynamic, the evaluation cannot be - // made in compile time. + // If the result is inexact, it depends on the rounding mode. If the requested + // mode is dynamic, compile-time evaluation cannot be performed. + // Floating-point exceptions other than "inexact" (e.g. exact infinities from + // division by zero) do not depend on the rounding mode and are not suppressed + // here. + if ((Status & APFloat::opStatus::opInexact) && + FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) { const SourceInfo &E = S.Current->getSource(OpPC); S.FFDiag(E, diag::note_constexpr_dynamic_rounding); return false; } + // Note: We do not check MathErrno here. This function is called for all + // floating-point operations, including basic arithmetic operators + // (+, -, *, /) and casts, which never set errno. In addition, -fmath-errno + // is enabled by default on POSIX systems, so checking MathErrno here would + // incorrectly prevent constant folding of basic arithmetic expressions. + // Setting errno is specific to C standard library math functions (and varies + // by function: range errors, pole errors, domain errors), so MathErrno + // checks are handled by the math builtin evaluators directly. + // No fenv access and floating point exceptions are ignored, so it is safe to - // to perform compile-time evaluation. + // 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. - const SourceInfo &E = S.Current->getSource(OpPC); - S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); - return false; - } + // Initializers for objects with static or thread storage duration (such as + // global variables) are evaluated at compile time during translation using + // default floating-point settings (C17 7.6.1p2). + if (S.EvaluatingDecl && S.EvaluatingDecl->hasGlobalStorage()) + return true; - // 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. + // Some floating point exception is raised and the FP mode is strict, + // exceptions may be trapped, or FENV access is enabled. const SourceInfo &E = S.Current->getSource(OpPC); S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); return false; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index d23fa81ff2b27e..2f420d99725e29 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -759,22 +759,54 @@ 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 = llvm::RoundingMode::NearestTiesToEven; - if (S.inConstantContext()) - RM = getRoundingMode(FPO); + llvm::RoundingMode RM = getRoundingMode(FPO); APFloat::opStatus Status = APFloat::opStatus::opOK; std::optional<APFloat> Result = exp(Arg.getAPFloat(), RM, &Status); - // Check for unsupported rounding modes. - if (!Result.has_value()) + const SourceInfo &E = S.Current->getSource(OpPC); + + if (!Result.has_value()) { + if (S.inConstantContext()) + S.FFDiag(E, diag::note_constexpr_unsupported_rounding) + << Call->getDirectCallee() << llvm::spell(RM); + return false; + } + + if (S.inConstantContext()) { + // [library.c]p3: A call to a math function is not a core constant + // expression if an exception other than FE_INEXACT is raised. + if (Status & (~APFloat::opStatus::opInexact)) { + const FunctionDecl *FD = Call->getDirectCallee(); + if (Status & APFloat::opStatus::opUnderflow) + S.FFDiag(E, diag::note_constexpr_float_underflow) << FD; + else if (Status & APFloat::opStatus::opOverflow) + S.FFDiag(E, diag::note_constexpr_float_overflow) << FD; + else if (Status & APFloat::opStatus::opDivByZero) + S.FFDiag(E, diag::note_constexpr_float_divide_by_zero) << FD; + else + S.FFDiag(E, diag::note_constexpr_float_invalid_op) << FD; + return false; + } + Floating Res = S.allocFloat(Arg.getSemantics()); + Res.copy(*Result); + S.Stk.push<Floating>(Res); + return true; + } + + // Under -fmath-errno, any error condition that sets errno at runtime + // (domain error EDOM via opInvalidOp, pole error ERANGE via opDivByZero, + // or range error ERANGE via opOverflow/opUnderflow) cannot be folded. + // This check is performed here rather than in CheckFloatStatus because errno + // is specific to math functions (and not set by core language operators). + if (S.getLangOpts().MathErrno && (Status & (~APFloat::opStatus::opInexact))) { + S.FFDiag(E, diag::note_constexpr_math_errno) << Call->getDirectCallee(); return false; - // Check for raised non-FE_INEXACT exceptions. - if (Status & (~APFloat::opStatus::opInexact)) + } + + if (!CheckFloatStatus(S, OpPC, Status, FPO)) 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 446d960eda1b63..45535dd8a14719 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2725,33 +2725,44 @@ static bool checkFloatingPointResultForConstantFolding(EvalInfo &Info, 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. + // If the result is inexact, it depends on the rounding mode. If the requested + // mode is dynamic, compile-time evaluation cannot be performed. + // Floating-point exceptions other than "inexact" (e.g. exact infinities from + // division by zero) do not depend on the rounding mode and are not suppressed + // here. + if ((St & APFloat::opStatus::opInexact) && + FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) { Info.FFDiag(E, diag::note_constexpr_dynamic_rounding); return false; } + // Note: We do not check MathErrno here. This function is called for all + // floating-point operations, including basic arithmetic operators + // (+, -, *, /) and casts, which never set errno. In addition, -fmath-errno + // is enabled by default on POSIX systems, so checking MathErrno here would + // incorrectly prevent constant folding of basic arithmetic expressions. + // Setting errno is specific to C standard library math functions (and varies + // by function: range errors, pole errors, domain errors), so MathErrno + // checks are handled by the math builtin evaluators directly. + // No fenv access and floating point exceptions are ignored, so it is safe to - // to perform compile-time evaluation. + // 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. - Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); - return false; + // Initializers for objects with static or thread storage duration (such as + // global variables) are evaluated at compile time during translation using + // default floating-point settings (C17 7.6.1p2). + if (const auto *VD = Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()) { + if (const auto *Var = dyn_cast<VarDecl>(VD)) { + if (Var->hasGlobalStorage()) + return true; + } } - // 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. + // Some floating point exception is raised and the FP mode is strict, + // exceptions may be trapped, or FENV access is enabled. Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); return false; } @@ -20538,6 +20549,60 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context, return true; } +/// Evaluate a unary math function (such as exp/expf) for compile-time constant +/// evaluation or opportunistic constant folding. +template <typename MathFn> +static bool evaluateUnaryMathBuiltin(EvalInfo &Info, const CallExpr *E, + const APFloat &Input, APFloat &Result, + MathFn &&Func) { + llvm::RoundingMode RM = getActiveRoundingMode(Info, E); + APFloat::opStatus Status = APFloat::opStatus::opOK; + std::optional<APFloat> r = Func(Input, RM, &Status); + if (!r.has_value()) { + if (Info.InConstantContext) + Info.FFDiag(E, diag::note_constexpr_unsupported_rounding) + << E->getDirectCallee() << llvm::spell(RM); + return false; + } + + if (Info.InConstantContext) { + // [library.c]p3: A call to a math function is not a core constant + // expression if an exception other than FE_INEXACT is raised. + if (Status & (~APFloat::opStatus::opInexact)) { + const FunctionDecl *FD = E->getDirectCallee(); + if (Status & APFloat::opStatus::opUnderflow) + Info.FFDiag(E, diag::note_constexpr_float_underflow) << FD; + else if (Status & APFloat::opStatus::opOverflow) + Info.FFDiag(E, diag::note_constexpr_float_overflow) << FD; + else if (Status & APFloat::opStatus::opDivByZero) + Info.FFDiag(E, diag::note_constexpr_float_divide_by_zero) << FD; + else + Info.FFDiag(E, diag::note_constexpr_float_invalid_op) << FD; + return false; + } + Result = *r; + return true; + } + + // Under -fmath-errno, any error condition that sets errno at runtime + // (domain error EDOM via opInvalidOp, pole error ERANGE via opDivByZero, + // or range error ERANGE via opOverflow/opUnderflow) cannot be folded. + // This check is performed here rather than in + // checkFloatingPointResultForConstantFolding because errno is specific to + // math functions (and not set by core language operators). + if (Info.getLangOpts().MathErrno && + (Status & (~APFloat::opStatus::opInexact))) { + Info.FFDiag(E, diag::note_constexpr_math_errno) << E->getDirectCallee(); + return false; + } + + if (!checkFloatingPointResultForConstantFolding(Info, E, Status)) + return false; + + Result = *r; + return true; +} + bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { if (!IsConstantEvaluatedBuiltinCall(E)) return ExprEvaluatorBaseTy::VisitCallExpr(E); @@ -20627,21 +20692,11 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { APFloat Input(0.); if (!EvaluateFloat(E->getArg(0), Input, Info)) return false; - 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. - if (!r.has_value()) - return false; - // 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; + return evaluateUnaryMathBuiltin( + Info, E, Input, Result, + [](const APFloat &X, llvm::RoundingMode RM, APFloat::opStatus *Status) { + return exp(X, RM, Status); + }); } case Builtin::BI__builtin_expl: case Builtin::BI__builtin_expf16: diff --git a/clang/test/CodeGen/builtin-exp-folding.c b/clang/test/CodeGen/builtin-exp-folding.c new file mode 100644 index 00000000000000..419418a8e76631 --- /dev/null +++ b/clang/test/CodeGen/builtin-exp-folding.c @@ -0,0 +1,173 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -fmath-errno %s | FileCheck %s --check-prefix=CHECK-ERRNO +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -fno-math-errno %s | FileCheck %s --check-prefix=CHECK-NO-ERRNO +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -frounding-math %s | FileCheck %s --check-prefix=CHECK-ROUNDING +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -ffp-exception-behavior=strict %s | FileCheck %s --check-prefix=CHECK-STRICT + +// An exact or inexact exp that doesn't set errno folds even with -fmath-errno: +float test_expf_1() { + return __builtin_expf(1.0f); +} +// CHECK-ERRNO-LABEL: @test_expf_1 +// CHECK-ERRNO: ret float 0x3FF5BF0A80000000 +// CHECK-NO-ERRNO-LABEL: @test_expf_1 +// CHECK-NO-ERRNO: ret float 0x3FF5BF0A80000000 +// CHECK-ROUNDING-LABEL: @test_expf_1 +// CHECK-ROUNDING: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") + +// Exact exp(0.0) folds even with -frounding-math and strict exception mode: +float test_expf_zero() { + return __builtin_expf(0.0f); +} +// CHECK-ERRNO-LABEL: @test_expf_zero +// CHECK-ERRNO: ret float 1.000000e+00 +// CHECK-NO-ERRNO-LABEL: @test_expf_zero +// CHECK-NO-ERRNO: ret float 1.000000e+00 +// CHECK-ROUNDING-LABEL: @test_expf_zero +// CHECK-ROUNDING: ret float 1.000000e+00 +// CHECK-STRICT-LABEL: @test_expf_zero +// CHECK-STRICT: ret float 1.000000e+00 + +// Exact infinities do not set errno, so they fold even under -fmath-errno: +float test_expf_pos_inf() { + return __builtin_expf(__builtin_inff()); +} +// CHECK-ERRNO-LABEL: @test_expf_pos_inf +// CHECK-ERRNO: ret float +inf +// CHECK-NO-ERRNO-LABEL: @test_expf_pos_inf +// CHECK-NO-ERRNO: ret float +inf + +float test_expf_neg_inf() { + return __builtin_expf(-__builtin_inff()); +} +// CHECK-ERRNO-LABEL: @test_expf_neg_inf +// CHECK-ERRNO: ret float 0.000000e+00 +// CHECK-NO-ERRNO-LABEL: @test_expf_neg_inf +// CHECK-NO-ERRNO: ret float 0.000000e+00 + +// Quiet NaN does not set errno or raise exceptions, so it folds to NaN: +float test_expf_nan() { + return __builtin_expf(__builtin_nanf("")); +} +// CHECK-ERRNO-LABEL: @test_expf_nan +// CHECK-ERRNO: ret float 0x7FF8000000000000 +// CHECK-NO-ERRNO-LABEL: @test_expf_nan +// CHECK-NO-ERRNO: ret float 0x7FF8000000000000 + + +// Inexact exp raises FE_INEXACT, so it cannot fold under strict exception mode: +float test_expf_strict_inexact() { + return __builtin_expf(1.0f); +} +// CHECK-STRICT-LABEL: @test_expf_strict_inexact +// CHECK-STRICT: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") + +// Overflow sets errno under -fmath-errno, so it cannot fold: +float test_expf_overflow() { + return __builtin_expf(100.0f); +} +// CHECK-ERRNO-LABEL: @test_expf_overflow +// CHECK-ERRNO: call float @expf(float noundef 1.000000e+02) +// CHECK-NO-ERRNO-LABEL: @test_expf_overflow +// CHECK-NO-ERRNO: ret float +inf +// CHECK-STRICT-LABEL: @test_expf_overflow +// CHECK-STRICT: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+02, metadata !"round.tonearest", metadata !"fpexcept.strict") + +// Underflow to zero sets errno under -fmath-errno, so it cannot fold: +float test_expf_underflow() { + return __builtin_expf(-100.0f); +} +// CHECK-ERRNO-LABEL: @test_expf_underflow +// CHECK-ERRNO: call float @expf(float noundef -1.000000e+02) +// CHECK-NO-ERRNO-LABEL: @test_expf_underflow +// CHECK-NO-ERRNO: ret float 0.000000e+00 +// CHECK-STRICT-LABEL: @test_expf_underflow +// CHECK-STRICT: call float @llvm.experimental.constrained.exp.f32(float -1.000000e+02, metadata !"round.tonearest", metadata !"fpexcept.strict") + +// Underflow to denormal sets errno under -fmath-errno, so it cannot fold: +float test_expf_denormal() { + return __builtin_expf(-88.0f); +} +// CHECK-ERRNO-LABEL: @test_expf_denormal +// CHECK-ERRNO: call float @expf(float noundef -8.800000e+01) +// CHECK-NO-ERRNO-LABEL: @test_expf_denormal +// CHECK-NO-ERRNO: ret float 0x37F6DC7000000000 +// CHECK-STRICT-LABEL: @test_expf_denormal +// CHECK-STRICT: call float @llvm.experimental.constrained.exp.f32(float -8.800000e+01, metadata !"round.tonearest", metadata !"fpexcept.strict") + +double test_exp_overflow() { + return __builtin_exp(1000.0); +} +// CHECK-ERRNO-LABEL: @test_exp_overflow +// CHECK-ERRNO: call double @exp(double noundef 1.000000e+03) +// CHECK-NO-ERRNO-LABEL: @test_exp_overflow +// CHECK-NO-ERRNO: ret double +inf +// CHECK-STRICT-LABEL: @test_exp_overflow +// CHECK-STRICT: call double @llvm.experimental.constrained.exp.f64(double 1.000000e+03, metadata !"round.tonearest", metadata !"fpexcept.strict") + +double test_exp_underflow() { + return __builtin_exp(-1000.0); +} +// CHECK-ERRNO-LABEL: @test_exp_underflow +// CHECK-ERRNO: call double @exp(double noundef -1.000000e+03) +// CHECK-NO-ERRNO-LABEL: @test_exp_underflow +// CHECK-NO-ERRNO: ret double 0.000000e+00 +// CHECK-STRICT-LABEL: @test_exp_underflow +// CHECK-STRICT: call double @llvm.experimental.constrained.exp.f64(double -1.000000e+03, metadata !"round.tonearest", metadata !"fpexcept.strict") + +// Constant rounding mode is ignored for runtime calls; with -frounding-math, dynamic rounding mode prevents folding. +// FIXME: Once FE_DOWNWARD is supported in APFloat::exp, this should constant-fold to: +// ret double 0x4005BF0A8B145769 +double test_exp_rounding_math() { + #pragma STDC FENV_ROUND FE_DOWNWARD + return __builtin_exp(1.0); +} +// CHECK-ROUNDING-LABEL: @test_exp_rounding_math +// CHECK-ROUNDING: call double @llvm.experimental.constrained.exp.f64(double 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") + +// Overriding -frounding-math with static tonearest rounding pragma allows folding: +float test_rounding_math_override() { + #pragma STDC FENV_ROUND FE_TONEAREST + return __builtin_expf(1.0f); +} +// CHECK-ROUNDING-LABEL: @test_rounding_math_override +// CHECK-ROUNDING: ret float 0x3FF5BF0A80000000 + +// Local dynamic rounding mode pragma prevents inexact folding without -frounding-math: +float test_pragma_fenv_round_dynamic() { + #pragma STDC FENV_ROUND FE_DYNAMIC + return __builtin_expf(1.0f); +} +// CHECK-ERRNO-LABEL: @test_pragma_fenv_round_dynamic +// CHECK-ERRNO: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") + +// Local dynamic rounding mode pragma still allows exact folding: +float test_pragma_fenv_round_dynamic_exact() { + #pragma STDC FENV_ROUND FE_DYNAMIC + return __builtin_expf(0.0f); +} +// CHECK-ERRNO-LABEL: @test_pragma_fenv_round_dynamic_exact +// CHECK-ERRNO: ret float 1.000000e+00 + +// Pragma STDC FENV_ACCESS ON prevents folding of inexact calls: +float test_fenv_access_inexact() { + #pragma STDC FENV_ACCESS ON + return __builtin_expf(1.0f); +} +// CHECK-ERRNO-LABEL: @test_fenv_access_inexact +// CHECK-ERRNO: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK-NO-ERRNO-LABEL: @test_fenv_access_inexact +// CHECK-NO-ERRNO: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") + +// Pragma STDC FENV_ACCESS ON still permits folding of exact calls: +float test_fenv_access_exact() { + #pragma STDC FENV_ACCESS ON + return __builtin_expf(0.0f); +} +// CHECK-ERRNO-LABEL: @test_fenv_access_exact +// CHECK-ERRNO: ret float 1.000000e+00 +// CHECK-NO-ERRNO-LABEL: @test_fenv_access_exact +// CHECK-NO-ERRNO: ret float 1.000000e+00 + +// Global variable initializers are evaluated at compile time using default FP settings (C17 7.6.1p2): +float global_exp_inexact = __builtin_expf(1.0f); +// CHECK-STRICT: @global_exp_inexact = {{.*}}global float 0x3FF5BF0A80000000 diff --git a/clang/test/Sema/constant-builtins-exp.cpp b/clang/test/Sema/constant-builtins-exp.cpp index 11120696f26157..685bfce8960c25 100644 --- a/clang/test/Sema/constant-builtins-exp.cpp +++ b/clang/test/Sema/constant-builtins-exp.cpp @@ -1,49 +1,139 @@ // 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 +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-unknown-pragmas -fmath-errno %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-unknown-pragmas -fno-math-errno %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-unknown-pragmas -frounding-math %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter -Wno-unknown-pragmas -frounding-math %s constexpr float InfFloat = __builtin_inff(); constexpr float NegInfFloat = -__builtin_inff(); constexpr float qNaNFloat = __builtin_nanf(""); +constexpr float DenormFloat = 0x1.0p-140f; +constexpr float NegDenormFloat = -0x1.0p-140f; -static_assert(__builtin_expf(qNaNFloat) != __builtin_expf(qNaNFloat)); +static_assert(__builtin_isnan(__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}} +static_assert(1.0f == __builtin_expf(DenormFloat)); +static_assert(1.0f == __builtin_expf(NegDenormFloat)); constexpr double InfDouble = __builtin_inf(); constexpr double NegInfDouble = -__builtin_inf(); constexpr double qNaNDouble = __builtin_nan(""); +constexpr double DenormDouble = 0x1.0p-1050; +constexpr double NegDenormDouble = -0x1.0p-1050; -static_assert(__builtin_exp(qNaNDouble) != __builtin_exp(qNaNDouble)); +static_assert(__builtin_isnan(__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)); +static_assert(1.0 == __builtin_exp(DenormDouble)); +static_assert(1.0 == __builtin_exp(NegDenormDouble)); // No constexpr for overflow. +static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_expf' raises a floating-point overflow exception}} static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_exp' raises a floating-point overflow exception}} + +// No constexpr for underflow: normal input yielding denormal output. +static_assert(0.0f == __builtin_expf(-88.0f)); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_expf' raises a floating-point underflow exception}} +static_assert(0.0 == __builtin_exp(-709.0)); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_exp' raises a floating-point underflow exception}} -// No constexpr for underflow. +// No constexpr for underflow: output rounding to zero. static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_expf' raises a floating-point underflow exception}} static_assert(0.0 == __builtin_exp(-1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_exp' raises a floating-point underflow exception}} + +// No constexpr for signaling NaN (invalid operation). +constexpr float sNaNFloat = __builtin_nansf(""); +constexpr double sNaNDouble = __builtin_nans(""); +static_assert(__builtin_isnan(__builtin_expf(sNaNFloat))); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_expf' raises an invalid floating-point operation exception}} +static_assert(__builtin_isnan(__builtin_exp(sNaNDouble))); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{call to '__builtin_exp' raises an invalid floating-point operation exception}} + void test_rounding_modes() { + // Currently, llvm::APFloat::exp only supports NearestTiesToEven rounding mode. + // Other rounding modes fail evaluation with note_constexpr_unsupported_rounding. + // Once directed rounding modes are supported in APFloat::exp: + // - Inexact evaluations without overflow/underflow (e.g., expf(1.0f)) should become + // valid constant expressions with the appropriately rounded value. + // - Overflow and underflow cases should still fail constant expression evaluation, but + // emit note_constexpr_float_overflow or note_constexpr_float_underflow instead + // of note_constexpr_unsupported_rounding. { #pragma STDC FENV_ROUND FE_DOWNWARD + // FIXME: Once FE_DOWNWARD is supported in APFloat::exp, this should fold to: + // static_assert(0x1.5bf0a8p1f == __builtin_expf(1.0f)); + static_assert(1.0f == __builtin_expf(1.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'downward' rounding mode}} + // FIXME: Once supported, these should fail with note_constexpr_float_overflow / underflow: static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'downward' rounding mode}} static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_exp' in 'downward' rounding mode}} static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'downward' rounding mode}} static_assert(0.0 == __builtin_exp(-1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_exp' in 'downward' rounding mode}} } { #pragma STDC FENV_ROUND FE_TOWARDZERO + // FIXME: Once FE_TOWARDZERO is supported in APFloat::exp, this should fold to: + // static_assert(0x1.5bf0a8p1f == __builtin_expf(1.0f)); + static_assert(1.0f == __builtin_expf(1.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'towardzero' rounding mode}} + // FIXME: Once supported, these should fail with note_constexpr_float_overflow / underflow: static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'towardzero' rounding mode}} static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_exp' in 'towardzero' rounding mode}} static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'towardzero' rounding mode}} static_assert(0.0 == __builtin_exp(-1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_exp' in 'towardzero' rounding mode}} } + { + #pragma STDC FENV_ROUND FE_UPWARD + // FIXME: Once FE_UPWARD is supported in APFloat::exp, this should fold to: + // static_assert(0x1.5bf0aap1f == __builtin_expf(1.0f)); + static_assert(1.0f == __builtin_expf(1.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'upward' rounding mode}} + // FIXME: Once supported, these should fail with note_constexpr_float_overflow / underflow: + static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'upward' rounding mode}} + static_assert(InfDouble == __builtin_exp(1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_exp' in 'upward' rounding mode}} + static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_expf' in 'upward' rounding mode}} + static_assert(0.0 == __builtin_exp(-1000.0)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{cannot evaluate call to '__builtin_exp' in 'upward' rounding mode}} + } + { + #pragma STDC FENV_ROUND FE_TONEAREST + static_assert(1.0f == __builtin_expf(0.0f)); + static_assert(0x1.5bf0a8p1f == __builtin_expf(1.0f)); + static_assert(1.0 == __builtin_exp(0.0)); + static_assert(0x1.5bf0a8b145769p1 == __builtin_exp(1.0)); + } +} + +void test_fenv_access() { + #pragma STDC FENV_ACCESS ON + static_assert(1.0f == __builtin_expf(0.0f)); + static_assert(0x1.5bf0a8p1f == __builtin_expf(1.0f)); + static_assert(1.0 == __builtin_exp(0.0)); + static_assert(0x1.5bf0a8b145769p1 == __builtin_exp(1.0)); + static_assert(InfFloat == __builtin_expf(100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{call to '__builtin_expf' raises a floating-point overflow exception}} + static_assert(0.0f == __builtin_expf(-100.0f)); // expected-error {{static assertion expression is not an integral constant expression}} + // expected-note@-1 {{call to '__builtin_expf' raises a floating-point underflow exception}} } diff --git a/clang/test/SemaCXX/constexpr-x86-avx-builtins.cpp b/clang/test/SemaCXX/constexpr-x86-avx-builtins.cpp index 1eedf6072ecdb6..52371da4894b17 100644 --- a/clang/test/SemaCXX/constexpr-x86-avx-builtins.cpp +++ b/clang/test/SemaCXX/constexpr-x86-avx-builtins.cpp @@ -12,7 +12,7 @@ namespace Inexact { constexpr __m256d a = { 1.0000000000000002, 0.0, 0.0, 0.0 }; constexpr __m128 r = _mm256_cvtpd_ps(a); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm256_cvtpd_ps({1.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00})'}} } } diff --git a/clang/test/SemaCXX/constexpr-x86-avx512f-builtins.cpp b/clang/test/SemaCXX/constexpr-x86-avx512f-builtins.cpp index b101f84596d9e3..dbd5e9e9e25804 100644 --- a/clang/test/SemaCXX/constexpr-x86-avx512f-builtins.cpp +++ b/clang/test/SemaCXX/constexpr-x86-avx512f-builtins.cpp @@ -29,7 +29,7 @@ constexpr __m128 a = { 1.0f, 2.0f, 3.0f, 4.0f }; constexpr __m128d b_inexact = { 1.0000000000000002, 0.0 }; constexpr __m128 r = _mm_mask_cvtsd_ss(src, 0x1, a, b_inexact); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm_mask_cvtsd_ss({9.000000e+00, 5.000000e+00, 6.000000e+00, 7.000000e+00}, 1, {1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00}, {1.000000e+00, 0.000000e+00})'}} } namespace MaskOnInf { @@ -56,7 +56,7 @@ constexpr __m128 a = { 1.0f, 2.0f, 3.0f, 4.0f }; constexpr __m128d b_sub = { 1e-310, 0.0 }; constexpr __m128 r = _mm_mask_cvtsd_ss(src, 0x1, a, b_sub); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm_mask_cvtsd_ss({9.000000e+00, 5.000000e+00, 6.000000e+00, 7.000000e+00}, 1, {1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00}, {1.000000e-310, 0.000000e+00})'}} } } @@ -104,7 +104,7 @@ namespace Inexact { constexpr __m512d a = { 1.0000000000000002, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; constexpr __m256 r = _mm512_cvtpd_ps(a); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm512_cvtpd_ps({1.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00})'}} } } diff --git a/clang/test/SemaCXX/constexpr-x86-avx512vl-builtins.cpp b/clang/test/SemaCXX/constexpr-x86-avx512vl-builtins.cpp index bdce60a357f13c..f24c82f6d80241 100644 --- a/clang/test/SemaCXX/constexpr-x86-avx512vl-builtins.cpp +++ b/clang/test/SemaCXX/constexpr-x86-avx512vl-builtins.cpp @@ -24,7 +24,7 @@ constexpr __m128 src = { 9.0f, 9.0f, 9.0f, 9.0f }; constexpr __m128d a_inexact = { -1.0, 1.0000000000000002 }; constexpr __m128 r = _mm_mask_cvtpd_ps(src, 0x2, a_inexact); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm_mask_cvtpd_ps({9.000000e+00, 9.000000e+00, 9.000000e+00, 9.000000e+00}, 2, {-1.000000e+00, 1.000000e+00})'}} } namespace MaskOnInf { diff --git a/clang/test/SemaCXX/constexpr-x86-sse2-builtins.cpp b/clang/test/SemaCXX/constexpr-x86-sse2-builtins.cpp index 251b46be7e7ff0..48a87d705b186e 100644 --- a/clang/test/SemaCXX/constexpr-x86-sse2-builtins.cpp +++ b/clang/test/SemaCXX/constexpr-x86-sse2-builtins.cpp @@ -14,7 +14,7 @@ constexpr __m128 a = { 0.0f, 1.0f, 2.0f, 3.0f }; constexpr __m128d b = { 1.0000000000000002, 0.0 }; constexpr __m128 r = _mm_cvtsd_ss(a, b); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm_cvtsd_ss({0.000000e+00, 1.000000e+00, 2.000000e+00, 3.000000e+00}, {1.000000e+00, 0.000000e+00})'}} } namespace Inf { @@ -38,7 +38,7 @@ constexpr __m128 a = { 0.0f, 1.0f, 2.0f, 3.0f }; constexpr __m128d b = { 1e-310, 0.0 }; constexpr __m128 r = _mm_cvtsd_ss(a, b); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm_cvtsd_ss({0.000000e+00, 1.000000e+00, 2.000000e+00, 3.000000e+00}, {1.000000e-310, 0.000000e+00})'}} } } @@ -52,7 +52,7 @@ namespace Inexact { constexpr __m128d a = { 1.0000000000000002, 0.0 }; constexpr __m128 r = _mm_cvtpd_ps(a); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm_cvtpd_ps({1.000000e+00, 0.000000e+00})'}} } namespace Inf { @@ -73,7 +73,7 @@ namespace Subnormal { constexpr __m128d a = { 1e-310, 0.0 }; constexpr __m128 r = _mm_cvtpd_ps(a); // expected-error@-1 {{must be initialized by a constant expression}} -// [email protected]:* {{compile time floating point arithmetic suppressed in strict evaluation modes}} +// [email protected]:* {{compile-time floating-point evaluation suppressed in strict evaluation modes}} // expected-note@-3 {{in call to '_mm_cvtpd_ps({1.000000e-310, 0.000000e+00})'}} } } >From 34edf5d1d563103a94e135b25a4fb68ef423cb80 Mon Sep 17 00:00:00 2001 From: Tue Ly <[email protected]> Date: Sun, 13 Sep 2026 03:57:19 +0000 Subject: [PATCH 13/15] Fix merge issue and update tests. --- clang/lib/AST/ByteCode/Interp.cpp | 16 ---------------- clang/test/CodeGen/builtin-exp-folding.c | 2 +- clang/test/Sema/constant-builtins-exp.cpp | 2 +- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index d47a7f32acb0d8..7429bf3a8924e2 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1286,22 +1286,6 @@ bool CheckFloatStatus(InterpState &S, CodePtr OpPC, APFloat::opStatus Status, 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(); - } - - return CheckFloatStatus(S, OpPC, Status, FPO); -} - bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result, APFloat::opStatus Status, FPOptions FPO) { // FIXME: The standard quote below is deleted by P3899R3. diff --git a/clang/test/CodeGen/builtin-exp-folding.c b/clang/test/CodeGen/builtin-exp-folding.c index 419418a8e76631..69ea75a537bf05 100644 --- a/clang/test/CodeGen/builtin-exp-folding.c +++ b/clang/test/CodeGen/builtin-exp-folding.c @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -fmath-errno %s | FileCheck %s --check-prefix=CHECK-ERRNO -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -fno-math-errno %s | FileCheck %s --check-prefix=CHECK-NO-ERRNO +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - %s | FileCheck %s --check-prefix=CHECK-NO-ERRNO // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -frounding-math %s | FileCheck %s --check-prefix=CHECK-ROUNDING // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -ffp-exception-behavior=strict %s | FileCheck %s --check-prefix=CHECK-STRICT diff --git a/clang/test/Sema/constant-builtins-exp.cpp b/clang/test/Sema/constant-builtins-exp.cpp index 685bfce8960c25..a43710704e2269 100644 --- a/clang/test/Sema/constant-builtins-exp.cpp +++ b/clang/test/Sema/constant-builtins-exp.cpp @@ -1,7 +1,7 @@ // 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 // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-unknown-pragmas -fmath-errno %s -// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-unknown-pragmas -fno-math-errno %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter -Wno-unknown-pragmas -fmath-errno %s // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-unknown-pragmas -frounding-math %s // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter -Wno-unknown-pragmas -frounding-math %s >From 06110b86ad2e4e60407b3b859fcfde77337d66fb Mon Sep 17 00:00:00 2001 From: Tue Ly <[email protected]> Date: Sun, 13 Sep 2026 04:59:17 +0000 Subject: [PATCH 14/15] Fix tests. --- clang/test/CodeGen/builtin-exp-folding.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clang/test/CodeGen/builtin-exp-folding.c b/clang/test/CodeGen/builtin-exp-folding.c index 69ea75a537bf05..6e604791ce87e7 100644 --- a/clang/test/CodeGen/builtin-exp-folding.c +++ b/clang/test/CodeGen/builtin-exp-folding.c @@ -122,7 +122,7 @@ double test_exp_rounding_math() { return __builtin_exp(1.0); } // CHECK-ROUNDING-LABEL: @test_exp_rounding_math -// CHECK-ROUNDING: call double @llvm.experimental.constrained.exp.f64(double 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK-ROUNDING: call double @llvm.experimental.constrained.exp.f64(double 1.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore") // Overriding -frounding-math with static tonearest rounding pragma allows folding: float test_rounding_math_override() { @@ -138,7 +138,9 @@ float test_pragma_fenv_round_dynamic() { return __builtin_expf(1.0f); } // CHECK-ERRNO-LABEL: @test_pragma_fenv_round_dynamic -// CHECK-ERRNO: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK-ERRNO: call float @expf(float noundef 1.000000e+00) +// CHECK-NO-ERRNO-LABEL: @test_pragma_fenv_round_dynamic +// CHECK-NO-ERRNO: call float @llvm.exp.f32(float 1.000000e+00) // Local dynamic rounding mode pragma still allows exact folding: float test_pragma_fenv_round_dynamic_exact() { @@ -147,6 +149,8 @@ float test_pragma_fenv_round_dynamic_exact() { } // CHECK-ERRNO-LABEL: @test_pragma_fenv_round_dynamic_exact // CHECK-ERRNO: ret float 1.000000e+00 +// CHECK-NO-ERRNO-LABEL: @test_pragma_fenv_round_dynamic_exact +// CHECK-NO-ERRNO: ret float 1.000000e+00 // Pragma STDC FENV_ACCESS ON prevents folding of inexact calls: float test_fenv_access_inexact() { @@ -154,7 +158,7 @@ float test_fenv_access_inexact() { return __builtin_expf(1.0f); } // CHECK-ERRNO-LABEL: @test_fenv_access_inexact -// CHECK-ERRNO: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK-ERRNO: call float @expf(float noundef 1.000000e+00) // CHECK-NO-ERRNO-LABEL: @test_fenv_access_inexact // CHECK-NO-ERRNO: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") >From a222bbd2dfcb4bac3a14eea13e87765eb599d072 Mon Sep 17 00:00:00 2001 From: Tue Ly <[email protected]> Date: Sun, 13 Sep 2026 05:44:50 +0000 Subject: [PATCH 15/15] Update codegen test. --- clang/test/CodeGen/builtin-exp-folding.c | 33 ++++++++++++++---------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/clang/test/CodeGen/builtin-exp-folding.c b/clang/test/CodeGen/builtin-exp-folding.c index 6e604791ce87e7..c462b424cd3194 100644 --- a/clang/test/CodeGen/builtin-exp-folding.c +++ b/clang/test/CodeGen/builtin-exp-folding.c @@ -3,14 +3,18 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -frounding-math %s | FileCheck %s --check-prefix=CHECK-ROUNDING // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - -ffp-exception-behavior=strict %s | FileCheck %s --check-prefix=CHECK-STRICT +// Global variable initializers are evaluated at compile time using default FP settings (C17 7.6.1p2): +float global_exp_inexact = __builtin_expf(1.0f); +// CHECK-STRICT: @global_exp_inexact = {{.*}}global float f0x402DF854 + // An exact or inexact exp that doesn't set errno folds even with -fmath-errno: float test_expf_1() { return __builtin_expf(1.0f); } // CHECK-ERRNO-LABEL: @test_expf_1 -// CHECK-ERRNO: ret float 0x3FF5BF0A80000000 +// CHECK-ERRNO: ret float f0x402DF854 // CHECK-NO-ERRNO-LABEL: @test_expf_1 -// CHECK-NO-ERRNO: ret float 0x3FF5BF0A80000000 +// CHECK-NO-ERRNO: ret float f0x402DF854 // CHECK-ROUNDING-LABEL: @test_expf_1 // CHECK-ROUNDING: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") @@ -49,9 +53,9 @@ float test_expf_nan() { return __builtin_expf(__builtin_nanf("")); } // CHECK-ERRNO-LABEL: @test_expf_nan -// CHECK-ERRNO: ret float 0x7FF8000000000000 +// CHECK-ERRNO: ret float +qnan // CHECK-NO-ERRNO-LABEL: @test_expf_nan -// CHECK-NO-ERRNO: ret float 0x7FF8000000000000 +// CHECK-NO-ERRNO: ret float +qnan // Inexact exp raises FE_INEXACT, so it cannot fold under strict exception mode: @@ -79,7 +83,7 @@ float test_expf_underflow() { // CHECK-ERRNO-LABEL: @test_expf_underflow // CHECK-ERRNO: call float @expf(float noundef -1.000000e+02) // CHECK-NO-ERRNO-LABEL: @test_expf_underflow -// CHECK-NO-ERRNO: ret float 0.000000e+00 +// CHECK-NO-ERRNO: ret float 3.783510e-44 // CHECK-STRICT-LABEL: @test_expf_underflow // CHECK-STRICT: call float @llvm.experimental.constrained.exp.f32(float -1.000000e+02, metadata !"round.tonearest", metadata !"fpexcept.strict") @@ -90,7 +94,7 @@ float test_expf_denormal() { // CHECK-ERRNO-LABEL: @test_expf_denormal // CHECK-ERRNO: call float @expf(float noundef -8.800000e+01) // CHECK-NO-ERRNO-LABEL: @test_expf_denormal -// CHECK-NO-ERRNO: ret float 0x37F6DC7000000000 +// CHECK-NO-ERRNO: ret float f0x0041EDC4 // CHECK-STRICT-LABEL: @test_expf_denormal // CHECK-STRICT: call float @llvm.experimental.constrained.exp.f32(float -8.800000e+01, metadata !"round.tonearest", metadata !"fpexcept.strict") @@ -130,17 +134,20 @@ float test_rounding_math_override() { return __builtin_expf(1.0f); } // CHECK-ROUNDING-LABEL: @test_rounding_math_override -// CHECK-ROUNDING: ret float 0x3FF5BF0A80000000 +// CHECK-ROUNDING: ret float f0x402DF854 -// Local dynamic rounding mode pragma prevents inexact folding without -frounding-math: +// When FENV_ACCESS is off and without -frounding-math, FE_DYNAMIC allows the +// translator to assume default rounding mode (C23 7.6.2p3), so it folds: float test_pragma_fenv_round_dynamic() { #pragma STDC FENV_ROUND FE_DYNAMIC return __builtin_expf(1.0f); } // CHECK-ERRNO-LABEL: @test_pragma_fenv_round_dynamic -// CHECK-ERRNO: call float @expf(float noundef 1.000000e+00) +// CHECK-ERRNO: ret float f0x402DF854 // CHECK-NO-ERRNO-LABEL: @test_pragma_fenv_round_dynamic -// CHECK-NO-ERRNO: call float @llvm.exp.f32(float 1.000000e+00) +// CHECK-NO-ERRNO: ret float f0x402DF854 +// CHECK-ROUNDING-LABEL: @test_pragma_fenv_round_dynamic +// CHECK-ROUNDING: call float @llvm.experimental.constrained.exp.f32(float 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore") // Local dynamic rounding mode pragma still allows exact folding: float test_pragma_fenv_round_dynamic_exact() { @@ -151,6 +158,8 @@ float test_pragma_fenv_round_dynamic_exact() { // CHECK-ERRNO: ret float 1.000000e+00 // CHECK-NO-ERRNO-LABEL: @test_pragma_fenv_round_dynamic_exact // CHECK-NO-ERRNO: ret float 1.000000e+00 +// CHECK-ROUNDING-LABEL: @test_pragma_fenv_round_dynamic_exact +// CHECK-ROUNDING: ret float 1.000000e+00 // Pragma STDC FENV_ACCESS ON prevents folding of inexact calls: float test_fenv_access_inexact() { @@ -171,7 +180,3 @@ float test_fenv_access_exact() { // CHECK-ERRNO: ret float 1.000000e+00 // CHECK-NO-ERRNO-LABEL: @test_fenv_access_exact // CHECK-NO-ERRNO: ret float 1.000000e+00 - -// Global variable initializers are evaluated at compile time using default FP settings (C17 7.6.1p2): -float global_exp_inexact = __builtin_expf(1.0f); -// CHECK-STRICT: @global_exp_inexact = {{.*}}global float 0x3FF5BF0A80000000 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
