https://github.com/Serosh-commits updated https://github.com/llvm/llvm-project/pull/194327
>From 07d3937ff15dff49c2e2cfcd56c2faed9c827faf Mon Sep 17 00:00:00 2001 From: LLVM Contributor <[email protected]> Date: Sat, 23 May 2026 03:35:30 +0530 Subject: [PATCH] address reviewer feedback --- clang/include/clang/Basic/Builtins.td | 60 +- clang/lib/AST/ByteCode/Interp.h | 18 +- clang/lib/AST/ByteCode/InterpBuiltin.cpp | 547 +++++++++++++++++- clang/lib/AST/ByteCode/InterpHelpers.h | 8 +- clang/lib/AST/ExprConstant.cpp | 362 +++++++++++- clang/test/CodeGen/builtins-fenv-access.c | 76 +++ clang/test/CodeGen/builtins-fenv-round.c | 22 + .../test/SemaCXX/constexpr-cmath-builtins.cpp | 311 ++++++++++ clang/test/SemaCXX/constexpr-fenv-access.cpp | 13 + 9 files changed, 1380 insertions(+), 37 deletions(-) create mode 100644 clang/test/CodeGen/builtins-fenv-access.c create mode 100644 clang/test/CodeGen/builtins-fenv-round.c create mode 100644 clang/test/SemaCXX/constexpr-cmath-builtins.cpp create mode 100644 clang/test/SemaCXX/constexpr-fenv-access.cpp diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 40ec94ab75046..cc7bd98542634 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -138,7 +138,7 @@ def CbrtF128 : Builtin { def CeilF16F128 : Builtin, F16F128MathTemplate { let Spellings = ["__builtin_ceil"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr]; let Prototype = "T(T)"; } @@ -192,19 +192,19 @@ def Expm1F128 : Builtin { def FdimF128 : Builtin { let Spellings = ["__builtin_fdimf128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "__float128(__float128, __float128)"; } def FloorF16F128 : Builtin, F16F128MathTemplate { let Spellings = ["__builtin_floor"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr]; let Prototype = "T(T)"; } def FmaF16F128 : Builtin, F16F128MathTemplate { let Spellings = ["__builtin_fma"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "T(T, T, T)"; } @@ -264,13 +264,13 @@ def FabsF128 : Builtin { def FmodF16F128 : F16F128MathTemplate, Builtin { let Spellings = ["__builtin_fmod"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "T(T, T)"; } def FrexpF16F128 : F16F128MathTemplate, Builtin { let Spellings = ["__builtin_frexp"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "T(T, int*)"; } @@ -300,13 +300,13 @@ def InfF16 : Builtin { def LdexpF16F128 : F16F128MathTemplate, Builtin { let Spellings = ["__builtin_ldexp"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "T(T, int)"; } def ModfF128 : Builtin { let Spellings = ["__builtin_modff128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Constexpr]; let Prototype = "__float128(__float128, __float128*)"; } @@ -352,7 +352,7 @@ def HypotF128 : Builtin { def ILogbF128 : Builtin { let Spellings = ["__builtin_ilogbf128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "int(__float128)"; } @@ -370,7 +370,7 @@ def LLrintF128 : Builtin { def LLroundF128 : Builtin { let Spellings = ["__builtin_llroundf128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "long long int(__float128)"; } @@ -412,7 +412,7 @@ def LrintF128 : Builtin { def LroundF128 : Builtin { let Spellings = ["__builtin_lroundf128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "long int(__float128)"; } @@ -424,25 +424,25 @@ def NearbyintF128 : Builtin { def NextafterF128 : Builtin { let Spellings = ["__builtin_nextafterf128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "__float128(__float128, __float128)"; } def NexttowardF128 : Builtin { let Spellings = ["__builtin_nexttowardf128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "__float128(__float128, __float128)"; } def RemainderF128 : Builtin { let Spellings = ["__builtin_remainderf128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "__float128(__float128, __float128)"; } def RemquoF128 : Builtin { let Spellings = ["__builtin_remquof128"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Constexpr]; let Prototype = "__float128(__float128, __float128, int*)"; } @@ -454,27 +454,27 @@ def RintF16F128 : Builtin, F16F128MathTemplate { def RoundF16F128 : Builtin, F16F128MathTemplate { let Spellings = ["__builtin_round"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr]; let Prototype = "T(T)"; } def RoundevenF16F128 : Builtin, F16F128MathTemplate { let Spellings = ["__builtin_roundeven"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr]; let Prototype = "T(T)"; } def ScanlblnF128 : Builtin { let Spellings = ["__builtin_scalblnf128"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, - ConstIgnoringErrnoAndExceptions]; + ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "__float128(__float128, long int)"; } def ScanlbnF128 : Builtin { let Spellings = ["__builtin_scalbnf128"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, - ConstIgnoringErrnoAndExceptions]; + ConstIgnoringErrnoAndExceptions, Constexpr]; let Prototype = "__float128(__float128, int)"; } @@ -522,7 +522,7 @@ def TgammaF128 : Builtin { def TruncF16F128 : Builtin, F16F128MathTemplate { let Spellings = ["__builtin_trunc"]; - let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const]; + let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr]; let Prototype = "T(T)"; } @@ -3986,6 +3986,7 @@ def Fmod : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Frexp : FPMathTemplate, LibBuiltin<"math.h"> { @@ -3993,6 +3994,7 @@ def Frexp : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow]; let Prototype = "T(T, int*)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Sincos : FPMathTemplate, GNULibBuiltin<"math.h"> { @@ -4013,6 +4015,7 @@ def Ldexp : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, int)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Modf : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4020,6 +4023,7 @@ def Modf : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow]; let Prototype = "T(T, T*)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Nan : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4091,6 +4095,7 @@ def Ceil : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, Const]; let Prototype = "T(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Cos : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4155,6 +4160,7 @@ def Fdim : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Floor : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4162,6 +4168,7 @@ def Floor : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, Const]; let Prototype = "T(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Fma : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4169,6 +4176,7 @@ def Fma : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, T, T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Fmax : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4215,6 +4223,7 @@ def Ilogb : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "int(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Lgamma : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4236,6 +4245,7 @@ def Llround : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "long long int(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Log : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4285,6 +4295,7 @@ def Lround : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "long int(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Nearbyint : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4299,6 +4310,7 @@ def Nextafter : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Nexttoward : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4306,6 +4318,7 @@ def Nexttoward : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, long double)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Remainder : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4313,6 +4326,7 @@ def Remainder : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Remquo : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4320,6 +4334,7 @@ def Remquo : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow]; let Prototype = "T(T, T, int*)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Rint : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4334,6 +4349,7 @@ def Round : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, Const]; let Prototype = "T(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def RoundEven : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4341,6 +4357,7 @@ def RoundEven : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, Const]; let Prototype = "T(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Scalbln : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4348,6 +4365,7 @@ def Scalbln : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, long int)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Scalbn : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4355,6 +4373,7 @@ def Scalbn : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions]; let Prototype = "T(T, int)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Sin : FPMathTemplate, LibBuiltin<"math.h"> { @@ -4404,6 +4423,7 @@ def Trunc : FPMathTemplate, LibBuiltin<"math.h"> { let Attributes = [NoThrow, Const]; let Prototype = "T(T)"; let AddBuiltinPrefixedAlias = 1; + let OnlyBuiltinPrefixedAliasIsConstexpr = 1; } def Cabs : FPMathTemplate, LibBuiltin<"complex.h"> { diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 235f1c471f471..3d4930d491ed3 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -88,10 +88,6 @@ bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern, bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B); bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B); -/// Checks if a value can be stored in a block. -bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - bool WillBeActivated = false); - /// Checks if a value can be initialized. bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); @@ -401,7 +397,7 @@ inline bool Addf(InterpState &S, CodePtr OpPC, uint32_t FPOI) { FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI); Floating Result = S.allocFloat(LHS.getSemantics()); - auto Status = Floating::add(LHS, RHS, getRoundingMode(FPO), &Result); + auto Status = Floating::add(LHS, RHS, getRoundingMode(FPO, S.inConstantContext()), &Result); S.Stk.push<Floating>(Result); return CheckFloatResult(S, OpPC, Result, Status, FPO); } @@ -455,7 +451,7 @@ inline bool Subf(InterpState &S, CodePtr OpPC, uint32_t FPOI) { FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI); Floating Result = S.allocFloat(LHS.getSemantics()); - auto Status = Floating::sub(LHS, RHS, getRoundingMode(FPO), &Result); + auto Status = Floating::sub(LHS, RHS, getRoundingMode(FPO, S.inConstantContext()), &Result); S.Stk.push<Floating>(Result); return CheckFloatResult(S, OpPC, Result, Status, FPO); } @@ -481,7 +477,7 @@ inline bool Mulf(InterpState &S, CodePtr OpPC, uint32_t FPOI) { FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI); Floating Result = S.allocFloat(LHS.getSemantics()); - auto Status = Floating::mul(LHS, RHS, getRoundingMode(FPO), &Result); + auto Status = Floating::mul(LHS, RHS, getRoundingMode(FPO, S.inConstantContext()), &Result); S.Stk.push<Floating>(Result); return CheckFloatResult(S, OpPC, Result, Status, FPO); @@ -783,7 +779,7 @@ inline bool Divf(InterpState &S, CodePtr OpPC, uint32_t FPOI) { FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI); Floating Result = S.allocFloat(LHS.getSemantics()); - auto Status = Floating::div(LHS, RHS, getRoundingMode(FPO), &Result); + auto Status = Floating::div(LHS, RHS, getRoundingMode(FPO, S.inConstantContext()), &Result); S.Stk.push<Floating>(Result); return CheckFloatResult(S, OpPC, Result, Status, FPO); @@ -1105,9 +1101,9 @@ bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr, FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI); llvm::APFloat::opStatus Status; if constexpr (Op == IncDecOp::Inc) - Status = Floating::increment(Value, getRoundingMode(FPO), &Result); + Status = Floating::increment(Value, getRoundingMode(FPO, S.inConstantContext()), &Result); else - Status = Floating::decrement(Value, getRoundingMode(FPO), &Result); + Status = Floating::decrement(Value, getRoundingMode(FPO, S.inConstantContext()), &Result); Ptr.deref<Floating>() = Result; @@ -2829,7 +2825,7 @@ bool CastIntegralFloating(InterpState &S, CodePtr OpPC, FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI); Floating Result = S.allocFloat(*Sem); auto Status = - Floating::fromIntegral(FromAP, *Sem, getRoundingMode(FPO), &Result); + Floating::fromIntegral(FromAP, *Sem, getRoundingMode(FPO, S.inConstantContext()), &Result); S.Stk.push<Floating>(Result); return CheckFloatResult(S, OpPC, Result, Status, FPO); diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 3e9ce902427eb..47e6f2227c020 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -199,6 +199,79 @@ static llvm::APSInt convertBoolVectorToInt(const Pointer &Val) { return Result; } +static bool CheckFloatResult(InterpState &S, CodePtr OpPC, const CallExpr *Call, + const APFloat &Result, APFloat::opStatus Status) { + FPOptions FPO = Call->getFPFeaturesInEffect(S.getLangOpts()); + + if (!S.inConstantContext()) { + if ((Status & APFloat::opInexact) && + FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) { + 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())) { + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); + return false; + } + } + + // If any of the following exceptions were raised, the operation is not a + // constant expression. + if (Status & (APFloat::opInvalidOp | APFloat::opOverflow | + APFloat::opUnderflow | APFloat::opDivByZero)) { + if (!S.checkingPotentialConstantExpression()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_float_arithmetic) << Result.isNaN(); + } + return false; + } + + return true; +} + +static bool CheckBuiltinStore(InterpState &S, CodePtr OpPC, + const Pointer &Ptr) { + if (!CheckLive(S, OpPC, Ptr, AK_Assign)) + return false; + if (!Ptr.isBlockPointer()) + return Invalid(S, OpPC); + return CheckStore(S, OpPC, Ptr); +} + +static APFloat::opStatus getScalbnStatus(const APFloat &Arg, + const APFloat &Result, int Exp) { + // APFloat::scalbn returns only the scaled value, so derive the status needed + // by strict floating-point evaluation. + if (!Arg.isFinite() || Arg.isZero()) + return APFloat::opOK; + + if (Result.isInfinity()) + return static_cast<APFloat::opStatus>(APFloat::opOverflow | + APFloat::opInexact); + if (Result.isZero()) + return static_cast<APFloat::opStatus>(APFloat::opUnderflow | + APFloat::opInexact); + if (Exp == std::numeric_limits<int>::min()) + return static_cast<APFloat::opStatus>(APFloat::opUnderflow | + APFloat::opInexact); + + APFloat Inverse = scalbn(Result, -Exp, APFloat::rmNearestTiesToEven); + if (Inverse.compare(Arg) == APFloat::cmpEqual) + return APFloat::opOK; + + if (Result.isDenormal()) + return static_cast<APFloat::opStatus>(APFloat::opUnderflow | + APFloat::opInexact); + return static_cast<APFloat::opStatus>(APFloat::opOverflow | + APFloat::opInexact); +} + // Strict double -> float conversion used for X86 PD2PS/cvtsd2ss intrinsics. // Reject NaN/Inf/Subnormal inputs and any lossy/inexact conversions. static bool convertDoubleToFloatStrict(APFloat Src, Floating &Dst, @@ -717,6 +790,361 @@ static inline Floating abs(InterpState &S, const Floating &In) { return Output; } +static bool interp__builtin_roundToIntegral(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call, + unsigned BuiltinOp) { + const Floating &Val = S.Stk.pop<Floating>(); + Floating Result = S.allocFloat(Val.getSemantics()); + APFloat F = Val.getAPFloat(); + + llvm::RoundingMode RM; + switch (BuiltinOp) { + case Builtin::BI__builtin_ceil: + case Builtin::BI__builtin_ceilf: + case Builtin::BI__builtin_ceill: + case Builtin::BI__builtin_ceilf16: + case Builtin::BI__builtin_ceilf128: + RM = llvm::RoundingMode::TowardPositive; + break; + case Builtin::BI__builtin_floor: + case Builtin::BI__builtin_floorf: + case Builtin::BI__builtin_floorl: + case Builtin::BI__builtin_floorf16: + case Builtin::BI__builtin_floorf128: + RM = llvm::RoundingMode::TowardNegative; + break; + case Builtin::BI__builtin_trunc: + case Builtin::BI__builtin_truncf: + case Builtin::BI__builtin_truncl: + case Builtin::BI__builtin_truncf16: + case Builtin::BI__builtin_truncf128: + RM = llvm::RoundingMode::TowardZero; + break; + case Builtin::BI__builtin_round: + case Builtin::BI__builtin_roundf: + case Builtin::BI__builtin_roundl: + case Builtin::BI__builtin_roundf16: + case Builtin::BI__builtin_roundf128: + RM = llvm::RoundingMode::NearestTiesToAway; + break; + case Builtin::BI__builtin_roundeven: + case Builtin::BI__builtin_roundevenf: + case Builtin::BI__builtin_roundevenl: + case Builtin::BI__builtin_roundevenf16: + case Builtin::BI__builtin_roundevenf128: + RM = llvm::RoundingMode::NearestTiesToEven; + break; + default: + llvm_unreachable("invalid builtin ID"); + } + + // roundToIntegral handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus Status = F.roundToIntegral(RM); + if (!CheckFloatResult(S, OpPC, Call, F, Status)) + return false; + + Result.copy(F); + S.Stk.push<Floating>(Result); + return true; +} + +static bool interp__builtin_fdim(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Floating &RHS = S.Stk.pop<Floating>(); + const Floating &LHS = S.Stk.pop<Floating>(); + APFloat L = LHS.getAPFloat(); + APFloat R = RHS.getAPFloat(); + APFloat Result(L.getSemantics()); + + if (L.isNaN()) { + Result = L; + } else if (R.isNaN()) { + Result = R; + } else if (L.compare(R) == APFloat::cmpGreaterThan) { + llvm::RoundingMode RM = + getRoundingMode(Call->getFPFeaturesInEffect(S.getLangOpts()), + S.inConstantContext()); + APFloat::opStatus Status = L.subtract(R, RM); + if (!CheckFloatResult(S, OpPC, Call, L, Status)) + return false; + Result = L; + } else { + Result = APFloat::getZero(L.getSemantics()); + } + + Floating F = S.allocFloat(Result.getSemantics()); + F.copy(Result); + S.Stk.push<Floating>(F); + return true; +} + +static bool interp__builtin_fma(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Floating &Z = S.Stk.pop<Floating>(); + const Floating &Y = S.Stk.pop<Floating>(); + const Floating &X = S.Stk.pop<Floating>(); + APFloat Result = X.getAPFloat(); + + llvm::RoundingMode RM = + getRoundingMode(Call->getFPFeaturesInEffect(S.getLangOpts()), + S.inConstantContext()); + + // fusedMultiplyAdd handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus Status = + Result.fusedMultiplyAdd(Y.getAPFloat(), Z.getAPFloat(), RM); + if (!CheckFloatResult(S, OpPC, Call, Result, Status)) + return false; + + Floating F = S.allocFloat(Result.getSemantics()); + F.copy(Result); + S.Stk.push<Floating>(F); + return true; +} + +static bool interp__builtin_frexp(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Pointer &Ptr = S.Stk.pop<Pointer>(); + const Floating &Val = S.Stk.pop<Floating>(); + + int Exp = 0; + // frexp handles special values (NaN, INF) per IEEE 754. + APFloat F = frexp(Val.getAPFloat(), Exp, APFloat::rmNearestTiesToEven); + + if (!CheckBuiltinStore(S, OpPC, Ptr)) + return false; + + QualType ExpType = Call->getArg(1)->getType()->getPointeeType(); + PrimType ExpT = *S.getContext().classify(ExpType); + assignIntegral(S, Ptr, ExpT, APSInt::get(Exp)); + Ptr.initialize(); + + Floating Result = S.allocFloat(F.getSemantics()); + Result.copy(F); + S.Stk.push<Floating>(Result); + return true; +} + +static bool interp__builtin_modf(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Pointer &Ptr = S.Stk.pop<Pointer>(); + const Floating &Val = S.Stk.pop<Floating>(); + const APFloat &F = Val.getAPFloat(); + + APFloat Integral = F; + Integral.roundToIntegral(APFloat::rmTowardZero); + + if (!CheckBuiltinStore(S, OpPC, Ptr)) + return false; + + Floating I = S.allocFloat(Integral.getSemantics()); + I.copy(Integral); + Ptr.deref<Floating>() = I; + Ptr.initialize(); + + if (F.isInfinity()) { + Floating Fraction = S.allocFloat(F.getSemantics()); + Fraction.copy(APFloat::getZero(F.getSemantics(), F.isNegative())); + S.Stk.push<Floating>(Fraction); + return true; + } + + APFloat Fraction = F; + if (!Fraction.isZero()) + Fraction.subtract(Integral, APFloat::rmNearestTiesToEven); + + Floating Result = S.allocFloat(Fraction.getSemantics()); + Result.copy(Fraction); + S.Stk.push<Floating>(Result); + return true; +} + +static bool interp__builtin_fmod(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, const CallExpr *Call, + unsigned BuiltinOp) { + const Floating &RHS = S.Stk.pop<Floating>(); + const Floating &LHS = S.Stk.pop<Floating>(); + const APFloat &L = LHS.getAPFloat(); + const APFloat &R = RHS.getAPFloat(); + APFloat ResF = L; + + // mod and remainder handle special values (NaN, INF) per IEEE 754. + APFloat::opStatus Status; + if (BuiltinOp == Builtin::BI__builtin_remainder || + BuiltinOp == Builtin::BI__builtin_remainderf || + BuiltinOp == Builtin::BI__builtin_remainderl || + BuiltinOp == Builtin::BI__builtin_remainderf128) + Status = ResF.remainder(R); + else + Status = ResF.mod(R); + + if (!CheckFloatResult(S, OpPC, Call, ResF, Status)) + return false; + + Floating F = S.allocFloat(ResF.getSemantics()); + F.copy(ResF); + S.Stk.push<Floating>(F); + return true; +} + +static bool interp__builtin_nextafter(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Floating &RHS = S.Stk.pop<Floating>(); + const Floating &LHS = S.Stk.pop<Floating>(); + const APFloat &L = LHS.getAPFloat(); + const APFloat &R = RHS.getAPFloat(); + + if (L.isNaN()) { + S.Stk.push<Floating>(LHS); + return true; + } + + // nexttoward(x, y) takes y as long double, so if y is NaN we must + // convert it to x's semantics before returning. + if (R.isNaN()) { + bool LoseInfo = false; + APFloat NaN = R; + NaN.convert(L.getSemantics(), APFloat::rmNearestTiesToEven, &LoseInfo); + Floating Result = S.allocFloat(NaN.getSemantics()); + Result.copy(NaN); + S.Stk.push<Floating>(Result); + return true; + } + + APFloat LCopy = L; + bool LoseInfo = false; + LCopy.convert(R.getSemantics(), APFloat::rmNearestTiesToEven, &LoseInfo); + APFloat::cmpResult Res = LCopy.compare(R); + + APFloat Next = L; + bool Stepped = false; + if (Res == APFloat::cmpEqual && L.isZero() && R.isZero() && + L.isNegative() != R.isNegative()) + Next = APFloat::getZero(L.getSemantics(), R.isNegative()); + else if (Res != APFloat::cmpEqual) { + Next.next(Res == APFloat::cmpGreaterThan); + Stepped = true; + } + + APFloat::opStatus Status = APFloat::opOK; + if (Stepped && Next.isInfinity()) + Status = static_cast<APFloat::opStatus>(APFloat::opOverflow | + APFloat::opInexact); + else if (Stepped && (Next.isDenormal() || Next.isZero())) + Status = static_cast<APFloat::opStatus>(APFloat::opUnderflow | + APFloat::opInexact); + + if (!CheckFloatResult(S, OpPC, Call, Next, Status)) + return false; + + Floating Result = S.allocFloat(Next.getSemantics()); + Result.copy(Next); + S.Stk.push<Floating>(Result); + return true; +} + +static bool interp__builtin_scalbn(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + PrimType ExpT = *S.getContext().classify(Call->getArg(1)->getType()); + APSInt Exp; + if (!popToAPSInt(S.Stk, ExpT, Exp)) + return false; + const Floating &Val = S.Stk.pop<Floating>(); + + llvm::RoundingMode RM = + getRoundingMode(Call->getFPFeaturesInEffect(S.getLangOpts()), + S.inConstantContext()); + + APFloat ValF = Val.getAPFloat(); + int ExpVal = (int)Exp.getExtValue(); + APFloat Scaled = scalbn(ValF, ExpVal, RM); + APFloat::opStatus Status = getScalbnStatus(ValF, Scaled, ExpVal); + if (!CheckFloatResult(S, OpPC, Call, Scaled, Status)) + return false; + + Floating Result = S.allocFloat(Val.getSemantics()); + Result.copy(Scaled); + S.Stk.push<Floating>(Result); + return true; +} + +static bool interp__builtin_ilogb(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Floating &Val = S.Stk.pop<Floating>(); + pushInteger(S, ilogb(Val.getAPFloat()), Call->getType()); + return true; +} + +static bool interp__builtin_remquo(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Pointer &Ptr = S.Stk.pop<Pointer>(); + const Floating &RHS = S.Stk.pop<Floating>(); + const Floating &LHS = S.Stk.pop<Floating>(); + + APFloat Q = LHS.getAPFloat(); + Q.divide(RHS.getAPFloat(), APFloat::rmNearestTiesToEven); + Q.roundToIntegral(APFloat::rmNearestTiesToEven); + + if (Ptr.isDummy()) + return false; + + QualType QuoType = Call->getArg(2)->getType()->getPointeeType(); + APSInt QuoInt(S.getASTContext().getTypeSize(QuoType), /*IsUnsigned=*/false); + bool IsExact = false; + APFloat::opStatus ConvSt = + Q.convertToInteger(QuoInt, APFloat::rmTowardZero, &IsExact); + if (ConvSt & APFloat::opInvalidOp) + QuoInt = 0; + + PrimType QuoT = *S.getContext().classify(QuoType); + assignIntegral(S, Ptr, QuoT, QuoInt); + Ptr.initialize(); + + APFloat R = LHS.getAPFloat(); + // remainder handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus Status = R.remainder(RHS.getAPFloat()); + if (!CheckFloatResult(S, OpPC, Call, R, Status)) + return false; + + Floating Result = S.allocFloat(R.getSemantics()); + Result.copy(R); + S.Stk.push<Floating>(Result); + return true; +} + +static bool interp__builtin_lround(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const Floating &Val = S.Stk.pop<Floating>(); + APFloat F = Val.getAPFloat(); + + llvm::RoundingMode RM = llvm::RoundingMode::NearestTiesToAway; + + // roundToIntegral handles special values (NaN, INF) per IEEE 754. + F.roundToIntegral(RM); + + APSInt IntVal(S.getASTContext().getTypeSize(Call->getType()), + Call->getType()->isUnsignedIntegerOrEnumerationType()); + bool IsExact = false; + APFloat::opStatus Status = F.convertToInteger(IntVal, RM, &IsExact); + + if (Status & APFloat::opInvalidOp) { + auto Loc = S.Current->getSource(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_float_arithmetic) << F.isNaN(); + } + + pushInteger(S, IntVal, Call->getType()); + return true; +} + // The C standard says "fabs raises no floating-point exceptions, // even if x is a signaling NaN. The returned value is independent of // the current rounding direction mode." Therefore constant folding can @@ -3052,7 +3480,7 @@ static bool interp_builtin_horizontal_fp_binop( const Pointer &LHS = S.Stk.pop<Pointer>(); const Pointer &Dst = S.Stk.peek<Pointer>(); FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts()); - llvm::RoundingMode RM = getRoundingMode(FPO); + llvm::RoundingMode RM = getRoundingMode(FPO, S.inConstantContext()); const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); unsigned NumElts = VT->getNumElements(); @@ -3087,7 +3515,7 @@ static bool interp__builtin_ia32_addsub(InterpState &S, CodePtr OpPC, const Pointer &LHS = S.Stk.pop<Pointer>(); const Pointer &Dst = S.Stk.peek<Pointer>(); FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts()); - llvm::RoundingMode RM = getRoundingMode(FPO); + llvm::RoundingMode RM = getRoundingMode(FPO, S.inConstantContext()); const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); unsigned NumElems = VT->getNumElements(); @@ -3177,7 +3605,7 @@ static bool interp__builtin_elementwise_triop_fp( assert(Call->getNumArgs() == 3); FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts()); - llvm::RoundingMode RM = getRoundingMode(FPO); + llvm::RoundingMode RM = getRoundingMode(FPO, S.inConstantContext()); QualType Arg1Type = Call->getArg(0)->getType(); QualType Arg2Type = Call->getArg(1)->getType(); QualType Arg3Type = Call->getArg(2)->getType(); @@ -4536,6 +4964,119 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case Builtin::BI__builtin_issubnormal: return interp__builtin_issubnormal(S, OpPC, Frame, Call); + case Builtin::BI__builtin_lround: + case Builtin::BI__builtin_lroundf: + case Builtin::BI__builtin_lroundl: + case Builtin::BI__builtin_lroundf128: + case Builtin::BI__builtin_llround: + case Builtin::BI__builtin_llroundf: + case Builtin::BI__builtin_llroundl: + case Builtin::BI__builtin_llroundf128: + return interp__builtin_lround(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_ceil: + case Builtin::BI__builtin_ceilf: + case Builtin::BI__builtin_ceill: + case Builtin::BI__builtin_ceilf16: + case Builtin::BI__builtin_ceilf128: + case Builtin::BI__builtin_floor: + case Builtin::BI__builtin_floorf: + case Builtin::BI__builtin_floorl: + case Builtin::BI__builtin_floorf16: + case Builtin::BI__builtin_floorf128: + case Builtin::BI__builtin_trunc: + case Builtin::BI__builtin_truncf: + case Builtin::BI__builtin_truncl: + case Builtin::BI__builtin_truncf16: + case Builtin::BI__builtin_truncf128: + case Builtin::BI__builtin_roundeven: + case Builtin::BI__builtin_roundevenf: + case Builtin::BI__builtin_roundevenl: + case Builtin::BI__builtin_roundevenf16: + case Builtin::BI__builtin_roundevenf128: + return interp__builtin_roundToIntegral(S, OpPC, Frame, Call, BuiltinID); + + case Builtin::BI__builtin_fdim: + case Builtin::BI__builtin_fdimf: + case Builtin::BI__builtin_fdiml: + case Builtin::BI__builtin_fdimf128: + return interp__builtin_fdim(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_frexp: + case Builtin::BI__builtin_frexpf: + case Builtin::BI__builtin_frexpl: + case Builtin::BI__builtin_frexpf16: + case Builtin::BI__builtin_frexpf128: + return interp__builtin_frexp(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_modf: + case Builtin::BI__builtin_modff: + case Builtin::BI__builtin_modfl: + case Builtin::BI__builtin_modff128: + return interp__builtin_modf(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_fma: + case Builtin::BI__builtin_fmaf: + case Builtin::BI__builtin_fmal: + case Builtin::BI__builtin_fmaf16: + case Builtin::BI__builtin_fmaf128: + return interp__builtin_fma(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_fmod: + case Builtin::BI__builtin_fmodf: + case Builtin::BI__builtin_fmodl: + case Builtin::BI__builtin_fmodf16: + case Builtin::BI__builtin_fmodf128: + case Builtin::BI__builtin_remainder: + case Builtin::BI__builtin_remainderf: + case Builtin::BI__builtin_remainderl: + case Builtin::BI__builtin_remainderf128: + return interp__builtin_fmod(S, OpPC, Frame, Call, BuiltinID); + + case Builtin::BI__builtin_nextafter: + case Builtin::BI__builtin_nextafterf: + case Builtin::BI__builtin_nextafterl: + case Builtin::BI__builtin_nextafterf128: + case Builtin::BI__builtin_nexttoward: + case Builtin::BI__builtin_nexttowardf: + case Builtin::BI__builtin_nexttowardl: + case Builtin::BI__builtin_nexttowardf128: + return interp__builtin_nextafter(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_scalbn: + case Builtin::BI__builtin_scalbnf: + case Builtin::BI__builtin_scalbnl: + case Builtin::BI__builtin_scalbnf128: + case Builtin::BI__builtin_scalbln: + case Builtin::BI__builtin_scalblnf: + case Builtin::BI__builtin_scalblnl: + case Builtin::BI__builtin_scalblnf128: + case Builtin::BI__builtin_ldexp: + case Builtin::BI__builtin_ldexpf: + case Builtin::BI__builtin_ldexpl: + case Builtin::BI__builtin_ldexpf16: + case Builtin::BI__builtin_ldexpf128: + return interp__builtin_scalbn(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_ilogb: + case Builtin::BI__builtin_ilogbf: + case Builtin::BI__builtin_ilogbl: + case Builtin::BI__builtin_ilogbf128: + return interp__builtin_ilogb(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_remquo: + case Builtin::BI__builtin_remquof: + case Builtin::BI__builtin_remquol: + case Builtin::BI__builtin_remquof128: + return interp__builtin_remquo(S, OpPC, Frame, Call); + + case Builtin::BI__builtin_round: + case Builtin::BI__builtin_roundf: + case Builtin::BI__builtin_roundl: + case Builtin::BI__builtin_roundf16: + case Builtin::BI__builtin_roundf128: + return interp__builtin_roundToIntegral(S, OpPC, Frame, Call, BuiltinID); + case Builtin::BI__builtin_iszero: return interp__builtin_iszero(S, OpPC, Frame, Call); diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h b/clang/lib/AST/ByteCode/InterpHelpers.h index 905bf1b43bfab..aaf8c5a26eec5 100644 --- a/clang/lib/AST/ByteCode/InterpHelpers.h +++ b/clang/lib/AST/ByteCode/InterpHelpers.h @@ -57,6 +57,10 @@ bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr); bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK = AK_Read); +/// Checks if a value can be stored in a block. +bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + bool WillBeActivated = false); + /// Diagnose mismatched new[]/delete or new/delete[] pairs. bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, DynamicAllocator::Form AllocForm, @@ -87,7 +91,9 @@ inline bool CheckArraySize(InterpState &S, CodePtr OpPC, uint64_t NumElems) { return true; } -static inline llvm::RoundingMode getRoundingMode(FPOptions FPO) { +static inline llvm::RoundingMode getRoundingMode(FPOptions FPO, bool InConstantContext) { + if (!InConstantContext && !FPO.getAllowFEnvAccess()) + return llvm::RoundingMode::NearestTiesToEven; auto RM = FPO.getRoundingMode(); if (RM == llvm::RoundingMode::Dynamic) return llvm::RoundingMode::NearestTiesToEven; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 38aa5798cfeb9..19156aac6d663 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2660,8 +2660,10 @@ static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E, /// expression. If the result is exact, it does not depend on rounding mode. /// So return "tonearest" mode instead of "dynamic". static llvm::RoundingMode getActiveRoundingMode(EvalInfo &Info, const Expr *E) { - llvm::RoundingMode RM = - E->getFPFeaturesInEffect(Info.getLangOpts()).getRoundingMode(); + FPOptions FPO = E->getFPFeaturesInEffect(Info.getLangOpts()); + if (!Info.InConstantContext && !FPO.getAllowFEnvAccess()) + return llvm::RoundingMode::NearestTiesToEven; + llvm::RoundingMode RM = FPO.getRoundingMode(); if (RM == llvm::RoundingMode::Dynamic) RM = llvm::RoundingMode::NearestTiesToEven; return RM; @@ -2708,6 +2710,34 @@ static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E, return true; } +static APFloat::opStatus getScalbnStatus(const APFloat &Arg, + const APFloat &Result, int Exp) { + // APFloat::scalbn returns only the scaled value, so derive the status needed + // by strict floating-point evaluation. + if (!Arg.isFinite() || Arg.isZero()) + return APFloat::opOK; + + if (Result.isInfinity()) + return static_cast<APFloat::opStatus>(APFloat::opOverflow | + APFloat::opInexact); + if (Result.isZero()) + return static_cast<APFloat::opStatus>(APFloat::opUnderflow | + APFloat::opInexact); + if (Exp == std::numeric_limits<int>::min()) + return static_cast<APFloat::opStatus>(APFloat::opUnderflow | + APFloat::opInexact); + + APFloat Inverse = scalbn(Result, -Exp, APFloat::rmNearestTiesToEven); + if (Inverse.compare(Arg) == APFloat::cmpEqual) + return APFloat::opOK; + + if (Result.isDenormal()) + return static_cast<APFloat::opStatus>(APFloat::opUnderflow | + APFloat::opInexact); + return static_cast<APFloat::opStatus>(APFloat::opOverflow | + APFloat::opInexact); +} + static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E, QualType SrcType, QualType DestType, APFloat &Result) { @@ -16396,6 +16426,34 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, }; switch (BuiltinOp) { + case Builtin::BI__builtin_lround: + case Builtin::BI__builtin_lroundf: + case Builtin::BI__builtin_lroundl: + case Builtin::BI__builtin_lroundf128: + case Builtin::BI__builtin_llround: + case Builtin::BI__builtin_llroundf: + case Builtin::BI__builtin_llroundl: + case Builtin::BI__builtin_llroundf128: { + APFloat FloatVal(0.0); + if (!EvaluateFloat(E->getArg(0), FloatVal, Info)) + return false; + + llvm::RoundingMode RM = llvm::RoundingMode::NearestTiesToAway; + + FloatVal.roundToIntegral(RM); + + APSInt IntVal(Info.Ctx.getTypeSize(E->getType()), + E->getType()->isUnsignedIntegerOrEnumerationType()); + bool IsExact = false; + APFloat::opStatus Status = FloatVal.convertToInteger(IntVal, RM, &IsExact); + + if (Status & APFloat::opInvalidOp) + Info.CCEDiag(E, diag::note_constexpr_float_arithmetic) + << (FloatVal.isNaN() ? 1 : 0); + + return Success(IntVal, E); + } + default: return false; @@ -16865,6 +16923,22 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(Val.popcount() % 2, E); } + case Builtin::BI__builtin_ilogb: + case Builtin::BI__builtin_ilogbf: + case Builtin::BI__builtin_ilogbl: + case Builtin::BI__builtin_ilogbf128: { + APFloat FloatVal(0.0); + if (!EvaluateFloat(E->getArg(0), FloatVal, Info)) + return false; + + if (FloatVal.isZero() || FloatVal.isNaN() || FloatVal.isInfinity()) { + if (!checkFloatingPointResult(Info, E, APFloat::opInvalidOp)) + return false; + } + + return Success(ilogb(FloatVal), E); + } + case Builtin::BI__builtin_abs: case Builtin::BI__builtin_labs: case Builtin::BI__builtin_llabs: { @@ -19946,6 +20020,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { if (!EvaluateFloat(E->getArg(0), Result, Info) || !EvaluateFloat(E->getArg(1), RHS, Info)) return false; + // maxnum handle special values (NaN, INF) per IEEE 754. Result = maxnum(Result, RHS); return true; } @@ -19959,6 +20034,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { if (!EvaluateFloat(E->getArg(0), Result, Info) || !EvaluateFloat(E->getArg(1), RHS, Info)) return false; + // minnum handle special values (NaN, INF) per IEEE 754. Result = minnum(Result, RHS); return true; } @@ -19972,6 +20048,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { if (!EvaluateFloat(E->getArg(0), Result, Info) || !EvaluateFloat(E->getArg(1), RHS, Info)) return false; + // maximumnum handle special values (NaN, INF) per IEEE 754. Result = maximumnum(Result, RHS); return true; } @@ -19985,10 +20062,291 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { if (!EvaluateFloat(E->getArg(0), Result, Info) || !EvaluateFloat(E->getArg(1), RHS, Info)) return false; + // minimumnum handles special values (NaN, INF) per IEEE 754. Result = minimumnum(Result, RHS); return true; } + case Builtin::BI__builtin_round: + case Builtin::BI__builtin_roundf: + case Builtin::BI__builtin_roundl: + case Builtin::BI__builtin_roundf16: + case Builtin::BI__builtin_roundf128: + case Builtin::BI__builtin_ceil: + case Builtin::BI__builtin_ceilf: + case Builtin::BI__builtin_ceill: + case Builtin::BI__builtin_ceilf16: + case Builtin::BI__builtin_ceilf128: + case Builtin::BI__builtin_floor: + case Builtin::BI__builtin_floorf: + case Builtin::BI__builtin_floorl: + case Builtin::BI__builtin_floorf16: + case Builtin::BI__builtin_floorf128: + case Builtin::BI__builtin_trunc: + case Builtin::BI__builtin_truncf: + case Builtin::BI__builtin_truncl: + case Builtin::BI__builtin_truncf16: + case Builtin::BI__builtin_truncf128: + case Builtin::BI__builtin_roundeven: + case Builtin::BI__builtin_roundevenf: + case Builtin::BI__builtin_roundevenl: + case Builtin::BI__builtin_roundevenf16: + case Builtin::BI__builtin_roundevenf128: { + if (!EvaluateFloat(E->getArg(0), Result, Info)) + return false; + llvm::RoundingMode RM; + switch (E->getBuiltinCallee()) { + + case Builtin::BI__builtin_round: + case Builtin::BI__builtin_roundf: + case Builtin::BI__builtin_roundl: + case Builtin::BI__builtin_roundf16: + case Builtin::BI__builtin_roundf128: + RM = llvm::RoundingMode::NearestTiesToAway; + break; + case Builtin::BI__builtin_ceil: + case Builtin::BI__builtin_ceilf: + case Builtin::BI__builtin_ceill: + case Builtin::BI__builtin_ceilf16: + case Builtin::BI__builtin_ceilf128: + RM = llvm::RoundingMode::TowardPositive; + break; + case Builtin::BI__builtin_floor: + case Builtin::BI__builtin_floorf: + case Builtin::BI__builtin_floorl: + case Builtin::BI__builtin_floorf16: + case Builtin::BI__builtin_floorf128: + RM = llvm::RoundingMode::TowardNegative; + break; + case Builtin::BI__builtin_roundeven: + case Builtin::BI__builtin_roundevenf: + case Builtin::BI__builtin_roundevenl: + case Builtin::BI__builtin_roundevenf16: + case Builtin::BI__builtin_roundevenf128: + RM = llvm::RoundingMode::NearestTiesToEven; + break; + default: + RM = llvm::RoundingMode::TowardZero; + break; + } + // roundToIntegral handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus St = Result.roundToIntegral(RM); + return checkFloatingPointResult(Info, E, St); + } + + case Builtin::BI__builtin_fdim: + case Builtin::BI__builtin_fdimf: + case Builtin::BI__builtin_fdiml: + case Builtin::BI__builtin_fdimf128: { + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + if (Result.compare(RHS) == APFloat::cmpGreaterThan) { + llvm::RoundingMode RM = getActiveRoundingMode(getEvalInfo(), E); + APFloat::opStatus St = Result.subtract(RHS, RM); + return checkFloatingPointResult(Info, E, St); + } else if (Result.isNaN()) { + // Result is already the NaN with its payload preserved. + } else if (RHS.isNaN()) { + Result = RHS; + } else { + Result = APFloat::getZero(Result.getSemantics()); + } + return true; + } + + case Builtin::BI__builtin_fma: + case Builtin::BI__builtin_fmaf: + case Builtin::BI__builtin_fmal: + case Builtin::BI__builtin_fmaf16: + case Builtin::BI__builtin_fmaf128: { + APFloat RHS(0.), Third(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info) || + !EvaluateFloat(E->getArg(2), Third, Info)) + return false; + + llvm::RoundingMode RM = getActiveRoundingMode(getEvalInfo(), E); + // fusedMultiplyAdd handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus St = Result.fusedMultiplyAdd(RHS, Third, RM); + return checkFloatingPointResult(Info, E, St); + } + + case Builtin::BI__builtin_fmod: + case Builtin::BI__builtin_fmodf: + case Builtin::BI__builtin_fmodl: + case Builtin::BI__builtin_fmodf16: + case Builtin::BI__builtin_fmodf128: { + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + // mod handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus St = Result.mod(RHS); + return checkFloatingPointResult(Info, E, St); + } + + case Builtin::BI__builtin_remainder: + case Builtin::BI__builtin_remainderf: + case Builtin::BI__builtin_remainderl: + case Builtin::BI__builtin_remainderf128: { + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + // remainder handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus St = Result.remainder(RHS); + return checkFloatingPointResult(Info, E, St); + } + + case Builtin::BI__builtin_nextafter: + case Builtin::BI__builtin_nextafterf: + case Builtin::BI__builtin_nextafterl: + case Builtin::BI__builtin_nextafterf128: + case Builtin::BI__builtin_nexttoward: + case Builtin::BI__builtin_nexttowardf: + case Builtin::BI__builtin_nexttowardl: + case Builtin::BI__builtin_nexttowardf128: { + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + + if (Result.isNaN()) + return true; + + if (RHS.isNaN()) { + bool LoseInfo = false; + Result = RHS; + Result.convert(Info.Ctx.getFloatTypeSemantics(E->getType()), + APFloat::rmNearestTiesToEven, &LoseInfo); + return true; + } + + APFloat ResultCopy = Result; + bool LoseInfo = false; + ResultCopy.convert(RHS.getSemantics(), APFloat::rmNearestTiesToEven, + &LoseInfo); + APFloat::cmpResult Res = ResultCopy.compare(RHS); + + if (Res == APFloat::cmpEqual) { + if (Result.isZero() && RHS.isZero() && + Result.isNegative() != RHS.isNegative()) + Result = APFloat::getZero(Result.getSemantics(), RHS.isNegative()); + return true; + } + + Result.next(Res == APFloat::cmpGreaterThan); + return true; + } + + case Builtin::BI__builtin_scalbn: + case Builtin::BI__builtin_scalbnf: + case Builtin::BI__builtin_scalbnl: + case Builtin::BI__builtin_scalbnf128: + case Builtin::BI__builtin_scalbln: + case Builtin::BI__builtin_scalblnf: + case Builtin::BI__builtin_scalblnl: + case Builtin::BI__builtin_scalblnf128: + case Builtin::BI__builtin_ldexp: + case Builtin::BI__builtin_ldexpf: + case Builtin::BI__builtin_ldexpl: + case Builtin::BI__builtin_ldexpf16: + case Builtin::BI__builtin_ldexpf128: { + APSInt Exp; + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateInteger(E->getArg(1), Exp, Info)) + return false; + + llvm::RoundingMode RM = getActiveRoundingMode(getEvalInfo(), E); + APFloat Arg = Result; + int ExpVal = (int)Exp.getExtValue(); + Result = scalbn(Result, ExpVal, RM); + APFloat::opStatus St = getScalbnStatus(Arg, Result, ExpVal); + return checkFloatingPointResult(Info, E, St); + } + + case Builtin::BI__builtin_frexp: + case Builtin::BI__builtin_frexpf: + case Builtin::BI__builtin_frexpl: + case Builtin::BI__builtin_frexpf16: + case Builtin::BI__builtin_frexpf128: { + LValue ExpLVal; + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluatePointer(E->getArg(1), ExpLVal, Info)) + return false; + + int Exp = 0; + // frexp handles special values (NaN, INF) per IEEE 754. + Result = frexp(Result, Exp, APFloat::rmNearestTiesToEven); + + QualType PointeeType = E->getArg(1)->getType()->getPointeeType(); + APValue APV{APSInt(Info.Ctx.getTypeSize(PointeeType), false)}; + APV.getInt() = Exp; + if (!handleAssignment(Info, E, ExpLVal, PointeeType, APV)) + return false; + return true; + } + + case Builtin::BI__builtin_modf: + case Builtin::BI__builtin_modff: + case Builtin::BI__builtin_modfl: + case Builtin::BI__builtin_modff128: { + LValue IptrLVal; + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluatePointer(E->getArg(1), IptrLVal, Info)) + return false; + + APFloat Integral = Result; + Integral.roundToIntegral(APFloat::rmTowardZero); + + QualType PointeeType = E->getArg(1)->getType()->getPointeeType(); + APValue APV{Integral}; + if (!handleAssignment(Info, E, IptrLVal, PointeeType, APV)) + return false; + + if (Result.isInfinity()) + Result = APFloat::getZero(Result.getSemantics(), Result.isNegative()); + else if (!Result.isZero()) + Result.subtract(Integral, APFloat::rmNearestTiesToEven); + return true; + } + + case Builtin::BI__builtin_remquo: + case Builtin::BI__builtin_remquof: + case Builtin::BI__builtin_remquol: + case Builtin::BI__builtin_remquof128: { + APFloat RHS(0.); + LValue QuoLVal; + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info) || + !EvaluatePointer(E->getArg(2), QuoLVal, Info)) + return false; + + APFloat Q = Result; + Q.divide(RHS, APFloat::rmNearestTiesToEven); + Q.roundToIntegral(APFloat::rmNearestTiesToEven); + + APSInt QuoInt( + Info.Ctx.getTypeSize(E->getArg(2)->getType()->getPointeeType()), false); + bool IsExact = false; + + APFloat::opStatus ConvSt = + Q.convertToInteger(QuoInt, APFloat::rmTowardZero, &IsExact); + if (ConvSt & APFloat::opInvalidOp) + QuoInt = 0; + + APValue APV{QuoInt}; + if (!handleAssignment(Info, E, QuoLVal, + E->getArg(2)->getType()->getPointeeType(), APV)) + return false; + + // remainder handles special values (NaN, INF) per IEEE 754. + APFloat::opStatus St = Result.remainder(RHS); + return checkFloatingPointResult(Info, E, St); + } + case Builtin::BI__builtin_elementwise_fma: { if (!E->getArg(0)->isPRValue() || !E->getArg(1)->isPRValue() || !E->getArg(2)->isPRValue()) { diff --git a/clang/test/CodeGen/builtins-fenv-access.c b/clang/test/CodeGen/builtins-fenv-access.c new file mode 100644 index 0000000000000..2edadeb8c75c4 --- /dev/null +++ b/clang/test/CodeGen/builtins-fenv-access.c @@ -0,0 +1,76 @@ +// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple x86_64-linux-gnu -fexperimental-new-constant-interpreter -emit-llvm %s -o - | FileCheck %s + +#pragma STDC FENV_ACCESS ON + +// CHECK-LABEL: define{{.*}} double @test_ceil_inexact( +// CHECK: call double @llvm.experimental.constrained.ceil.f64( +double test_ceil_inexact(void) { return __builtin_ceil(2.1); } + +// CHECK-LABEL: define{{.*}} double @test_ceil_exact( +// CHECK: ret double 2.000000e+00 +double test_ceil_exact(void) { return __builtin_ceil(2.0); } + +// CHECK-LABEL: define{{.*}} double @test_floor_inexact( +// CHECK: call double @llvm.experimental.constrained.floor.f64( +double test_floor_inexact(void) { return __builtin_floor(2.1); } + +// CHECK-LABEL: define{{.*}} double @test_floor_exact( +// CHECK: ret double 2.000000e+00 +double test_floor_exact(void) { return __builtin_floor(2.0); } + +// CHECK-LABEL: define{{.*}} double @test_trunc_inexact( +// CHECK: call double @llvm.experimental.constrained.trunc.f64( +double test_trunc_inexact(void) { return __builtin_trunc(2.1); } + +// CHECK-LABEL: define{{.*}} double @test_trunc_exact( +// CHECK: ret double 2.000000e+00 +double test_trunc_exact(void) { return __builtin_trunc(2.0); } + +// CHECK-LABEL: define{{.*}} double @test_round_inexact( +// CHECK: call double @llvm.experimental.constrained.round.f64( +double test_round_inexact(void) { return __builtin_round(2.1); } + +// CHECK-LABEL: define{{.*}} double @test_round_exact( +// CHECK: ret double 2.000000e+00 +double test_round_exact(void) { return __builtin_round(2.0); } + +// CHECK-LABEL: define{{.*}} double @test_roundeven_inexact( +// CHECK: call double @llvm.experimental.constrained.roundeven.f64( +double test_roundeven_inexact(void) { return __builtin_roundeven(2.1); } + +// CHECK-LABEL: define{{.*}} double @test_roundeven_exact( +// CHECK: ret double 2.000000e+00 +double test_roundeven_exact(void) { return __builtin_roundeven(2.0); } + +// CHECK-LABEL: define{{.*}} double @test_fdim_inexact( +// CHECK: call double @fdim( +double test_fdim_inexact(void) { return __builtin_fdim(3.0, 0.1); } + +// CHECK-LABEL: define{{.*}} double @test_fdim_exact( +// CHECK: ret double 2.000000e+00 +double test_fdim_exact(void) { return __builtin_fdim(3.0, 1.0); } + +// CHECK-LABEL: define{{.*}} double @test_fma_inexact( +// CHECK: call double @llvm.experimental.constrained.fma.f64( +double test_fma_inexact(void) { return __builtin_fma(3.0, 0.1, 0.01); } + +// CHECK-LABEL: define{{.*}} double @test_fma_exact( +// CHECK: ret double 1.000000e+01 +double test_fma_exact(void) { return __builtin_fma(2.0, 3.0, 4.0); } + +// CHECK-LABEL: define{{.*}} double @test_scalbn_inexact( +// CHECK: call double @scalbn( +double test_scalbn_inexact(void) { return __builtin_scalbn(__DBL_MAX__, 1); } + +// CHECK-LABEL: define{{.*}} double @test_scalbn_exact( +// CHECK: ret double 4.000000e+00 +double test_scalbn_exact(void) { return __builtin_scalbn(2.0, 1); } + +// CHECK-LABEL: define{{.*}} double @test_ldexp_inexact( +// CHECK: call double @llvm.experimental.constrained.ldexp.f64.i32( +double test_ldexp_inexact(void) { return __builtin_ldexp(__DBL_MAX__, 1); } + +// CHECK-LABEL: define{{.*}} double @test_ldexp_exact( +// CHECK: ret double 4.000000e+00 +double test_ldexp_exact(void) { return __builtin_ldexp(2.0, 1); } diff --git a/clang/test/CodeGen/builtins-fenv-round.c b/clang/test/CodeGen/builtins-fenv-round.c new file mode 100644 index 0000000000000..054c6eb7d019d --- /dev/null +++ b/clang/test/CodeGen/builtins-fenv-round.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - -Wno-unknown-pragmas | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-new-constant-interpreter -emit-llvm %s -o - -Wno-unknown-pragmas | FileCheck %s + +#pragma STDC FENV_ROUND FE_TONEARESTFROMZERO + +// CHECK-LABEL: define{{.*}} double @test_fdim( +// CHECK: ret double 1.000000e+00 +double test_fdim(void) { + return __builtin_fdim(__DBL_EPSILON__ / 2., -1.); +} + +// CHECK-LABEL: define{{.*}} double @test_fma( +// CHECK: ret double 1.000000e+00 +double test_fma(void) { + return __builtin_fma(0.5, __DBL_EPSILON__, 1.0); +} + +// CHECK-LABEL: define{{.*}} double @test_scalbn( +// CHECK: ret double 1.000000e+00 +double test_scalbn(void) { + return __builtin_scalbn(1.0 + __DBL_EPSILON__ / 2., 0); +} diff --git a/clang/test/SemaCXX/constexpr-cmath-builtins.cpp b/clang/test/SemaCXX/constexpr-cmath-builtins.cpp new file mode 100644 index 0000000000000..a316d9e63c4d3 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-cmath-builtins.cpp @@ -0,0 +1,311 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,new -std=c++20 %s +// RUN: %clang_cc1 -verify=expected -std=c++20 %s + +constexpr bool isNegativeZero(double x) { + return x == 0.0 && __builtin_copysign(1.0, x) < 0.0; +} +constexpr bool isPositiveZero(double x) { + return x == 0.0 && __builtin_copysign(1.0, x) > 0.0; +} + +// round tests +static_assert(__builtin_round(1.1) == 1.0); +static_assert(__builtin_round(1.5) == 2.0); +static_assert(__builtin_round(1.9) == 2.0); +static_assert(__builtin_round(-1.5) == -2.0); +static_assert(__builtin_roundf16(1.5f16) == 2.0f16); +static_assert(__builtin_roundf128(1.5) == 2.0); +static_assert(__builtin_isnan(__builtin_round(__builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_round(__builtin_inf()))); + +// lround tests +static_assert(__builtin_lround(1.1) == 1); +static_assert(__builtin_lround(1.5) == 2); +static_assert(__builtin_lround(-1.5) == -2); +static_assert(__builtin_lroundf128(1.5) == 2); + +// llround tests +static_assert(__builtin_llround(1.1) == 1LL); +static_assert(__builtin_llround(1.5) == 2LL); +static_assert(__builtin_llround(-1.5) == -2LL); +static_assert(__builtin_llroundf128(1.5) == 2LL); + +// ceil tests +static_assert(__builtin_ceil(1.1) == 2.0); +static_assert(__builtin_ceil(-1.1) == -1.0); +static_assert(__builtin_ceilf(1.1f) == 2.0f); +static_assert(__builtin_ceilf16(1.1f16) == 2.0f16); +static_assert(__builtin_ceilf128(1.1) == 2.0); +static_assert(__builtin_isnan(__builtin_ceil(__builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_ceil(__builtin_inf()))); + +// floor tests +static_assert(__builtin_floor(1.1) == 1.0); +static_assert(__builtin_floor(-1.1) == -2.0); +static_assert(__builtin_floorf(1.1f) == 1.0f); +static_assert(__builtin_floorf16(1.1f16) == 1.0f16); +static_assert(__builtin_floorf128(1.1) == 1.0); +static_assert(__builtin_isnan(__builtin_floor(__builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_floor(__builtin_inf()))); + +// trunc tests +static_assert(__builtin_trunc(1.1) == 1.0); +static_assert(__builtin_trunc(-1.1) == -1.0); +static_assert(__builtin_truncf(1.1f) == 1.0f); +static_assert(__builtin_truncf16(1.1f16) == 1.0f16); +static_assert(__builtin_truncf128(1.1) == 1.0); +static_assert(__builtin_isnan(__builtin_trunc(__builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_trunc(__builtin_inf()))); + +// roundeven tests +static_assert(__builtin_roundeven(1.5) == 2.0); +static_assert(__builtin_roundeven(2.5) == 2.0); +static_assert(__builtin_roundeven(-1.5) == -2.0); +static_assert(__builtin_roundeven(-2.5) == -2.0); +static_assert(__builtin_roundevenf(1.5f) == 2.0f); +static_assert(__builtin_roundevenl(1.5l) == 2.0l); +static_assert(__builtin_roundevenf128(1.5) == 2.0); +static_assert(__builtin_roundeven(0.5) == 0.0); +static_assert(isNegativeZero(__builtin_roundeven(-0.5))); +static_assert(__builtin_isnan(__builtin_roundeven(__builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_roundeven(__builtin_inf()))); + +// fdim tests +static_assert(__builtin_fdim(3.0, 1.0) == 2.0); +static_assert(__builtin_fdim(1.0, 3.0) == 0.0); +static_assert(__builtin_fdimf(3.0f, 1.0f) == 2.0f); +static_assert(__builtin_fdimf128(3.0, 1.0) == 2.0); +static_assert(__builtin_isnan(__builtin_fdim(__builtin_nan(""), 1.0))); +static_assert(__builtin_isnan(__builtin_fdim(1.0, __builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_fdim(__builtin_inf(), 0.0))); +static_assert(__builtin_fdim(__builtin_inf(), __builtin_inf()) == 0.0); + +// fma tests +static_assert(__builtin_fma(2.0, 3.0, 4.0) == 10.0); +static_assert(__builtin_fmaf(2.0f, 3.0f, 4.0f) == 10.0f); +static_assert(__builtin_fmaf16(2.0f16, 3.0f16, 4.0f16) == 10.0f16); +static_assert(__builtin_fmaf128(2.0, 3.0, 4.0) == 10.0); +static_assert(__builtin_isnan(__builtin_fma(__builtin_nan(""), 2.0, 3.0))); +static_assert(__builtin_isnan(__builtin_fma(1.0, __builtin_nan(""), 3.0))); +static_assert(__builtin_isnan(__builtin_fma(1.0, 2.0, __builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_fma(__builtin_inf(), 2.0, 3.0))); + +// fmod tests +static_assert(__builtin_fmod(5.5, 3.0) == 2.5); +static_assert(__builtin_fmodf(5.5f, 3.0f) == 2.5f); +static_assert(__builtin_fmodf16(5.5f16, 3.0f16) == 2.5f16); +static_assert(__builtin_fmodf128(5.5, 3.0) == 2.5); +static_assert(__builtin_isnan(__builtin_fmod(__builtin_nan(""), 2.0))); +static_assert(__builtin_isnan(__builtin_fmod(2.0, __builtin_nan("")))); + +// remainder tests +static_assert(__builtin_remainder(5.5, 3.0) == -0.5); +static_assert(__builtin_remainderf(5.5f, 3.0f) == -0.5f); +static_assert(__builtin_remainderf128(5.5, 3.0) == -0.5); +static_assert(__builtin_isnan(__builtin_remainder(__builtin_nan(""), 2.0))); +static_assert(__builtin_isnan(__builtin_remainder(2.0, __builtin_nan("")))); + +// nextafter tests +static_assert(__builtin_nextafter(1.0, 2.0) > 1.0); +static_assert(__builtin_nextafter(1.0, 0.0) < 1.0); +static_assert(__builtin_nextafter(1.0, 1.0) == 1.0); +static_assert(__builtin_nextafter(0.0, 1.0) > 0.0); // new-error {{static assertion expression is not an integral constant expression}} \ + // new-note {{floating point arithmetic produces an infinity}} +static_assert(__builtin_nextafter(0.0, -1.0) < 0.0); // new-error {{static assertion expression is not an integral constant expression}} \ + // new-note {{floating point arithmetic produces an infinity}} +static_assert(isPositiveZero(__builtin_nextafter(-0.0, 0.0))); +static_assert(__builtin_nextafterf128(1.0, 2.0) > 1.0); +static_assert(__builtin_isnan(__builtin_nextafter(__builtin_nan(""), 2.0))); +static_assert(__builtin_isnan(__builtin_nextafter(2.0, __builtin_nan("")))); +static_assert(__builtin_isinf(__builtin_nextafter(__builtin_inf(), __builtin_inf()))); + +// nexttoward tests +static_assert(__builtin_nexttoward(1.0, 2.0L) > 1.0); +static_assert(__builtin_nexttoward(1.0, 1.0L) == 1.0); +static_assert(__builtin_nexttowardf128(1.0, 2.0L) > 1.0); +static_assert(__builtin_isnan(__builtin_nexttoward(__builtin_nan(""), 2.0L))); +static_assert(__builtin_isnan(__builtin_nexttoward(2.0, __builtin_nan("")))); + +// scalbn tests +static_assert(__builtin_scalbn(1.0, 2) == 4.0); +static_assert(__builtin_scalbnf(1.0f, -1) == 0.5f); +static_assert(__builtin_scalbnf128(1.0, 2) == 4.0); +static_assert(__builtin_scalbn(0.0, 2) == 0.0); +static_assert(__builtin_scalbn(1.0, 0) == 1.0); +static_assert(__builtin_isnan(__builtin_scalbn(__builtin_nan(""), 2))); +static_assert(__builtin_isinf(__builtin_scalbn(__builtin_inf(), 2))); + +// scalbln tests +static_assert(__builtin_scalbln(1.0, 2L) == 4.0); +static_assert(__builtin_scalblnf128(1.0, 2L) == 4.0); +static_assert(__builtin_isnan(__builtin_scalbln(__builtin_nan(""), 2L))); +static_assert(__builtin_isinf(__builtin_scalbln(__builtin_inf(), 2L))); + +// ldexp tests +static_assert(__builtin_ldexp(1.0, 3) == 8.0); +static_assert(__builtin_ldexpf16(1.0f16, 3) == 8.0f16); +static_assert(__builtin_ldexpf128(1.0, 3) == 8.0); +static_assert(__builtin_isnan(__builtin_ldexp(__builtin_nan(""), 2))); +static_assert(__builtin_isinf(__builtin_ldexp(__builtin_inf(), 2))); + +// ilogb tests +static_assert(__builtin_ilogb(1.0) == 0); +static_assert(__builtin_ilogb(2.0) == 1); +static_assert(__builtin_ilogb(0.5) == -1); +static_assert(__builtin_ilogbf(8.0f) == 3); +static_assert(__builtin_ilogbf128(8.0) == 3); +static_assert(__builtin_ilogb(0.) == (-__INT_MAX__)); +static_assert(__builtin_ilogb(__builtin_nan("")) == (-__INT_MAX__ - 1)); +static_assert(__builtin_ilogb(__builtin_inf()) == __INT_MAX__); + +// remquo tests +constexpr double test_remquo(double x, double y) { + int quo = 0; + double rem = __builtin_remquo(x, y, &quo); + return rem; +} +static_assert(test_remquo(10.0, 3.0) == 1.0); + +constexpr int test_remquo_quo(double x, double y) { + int quo = 0; + __builtin_remquo(x, y, &quo); + return quo; +} +static_assert(test_remquo_quo(10.0, 3.0) == 3); +static_assert(test_remquo_quo(10.0, -3.0) == -3); + +// remquo NaN cases (per C standard / cppreference): +// - x or y is NaN +// - x is +/-inf +// - y is +/-0 +static_assert(__builtin_isnan(test_remquo(__builtin_nan(""), 2.0))); +static_assert(__builtin_isnan(test_remquo(2.0, __builtin_nan("")))); +static_assert(__builtin_isnan(test_remquo(__builtin_nan(""), __builtin_nan("")))); + +// frexp tests +constexpr double test_frexp_val(double x) { + int exp; + return __builtin_frexp(x, &exp); +} +static_assert(test_frexp_val(8.0) == 0.5); + +constexpr int test_frexp_exp(double x) { + int exp; + __builtin_frexp(x, &exp); + return exp; +} +static_assert(test_frexp_exp(8.0) == 4); + +// frexp special cases: +/- 0 +static_assert(test_frexp_val(0.0) == 0.0); +static_assert(isNegativeZero(test_frexp_val(-0.0))); +static_assert(test_frexp_exp(0.0) == 0); +static_assert(test_frexp_exp(-0.0) == 0); +// NaN and Inf: LLVM does not specify the exponent value for these cases. +static_assert(__builtin_isnan(test_frexp_val(__builtin_nan("")))); +static_assert(__builtin_isinf(test_frexp_val(__builtin_inf()))); +constexpr int frexp_nan_exp = test_frexp_exp(__builtin_nan("")); +constexpr int frexp_inf_exp = test_frexp_exp(__builtin_inf()); +constexpr int frexp_neg_inf_exp = test_frexp_exp(-__builtin_inf()); +static_assert(test_frexp_val(0.5) == 0.5); +static_assert(test_frexp_exp(0.5) == 0); +static_assert(test_frexp_val(1.0) == 0.5); +static_assert(test_frexp_exp(1.0) == 1); + +// modf tests +constexpr double test_modf_val(double x) { + double iptr; + return __builtin_modf(x, &iptr); +} +static_assert(test_modf_val(1.5) == 0.5); +static_assert(test_modf_val(-1.5) == -0.5); +static_assert(isNegativeZero(test_modf_val(-0.0))); + +constexpr double test_modf_iptr(double x) { + double iptr; + __builtin_modf(x, &iptr); + return iptr; +} +static_assert(test_modf_iptr(1.5) == 1.0); +static_assert(test_modf_iptr(-1.5) == -1.0); + +// modf special values +static_assert(test_modf_val(__builtin_inf()) == 0.0); +static_assert(__builtin_isinf(test_modf_iptr(__builtin_inf()))); + + +namespace LRoundDiagnostic { + constexpr int i = __builtin_lround(1e30); // expected-error {{constexpr variable 'i' must be initialized by a constant expression}} \ + // expected-note {{floating point arithmetic produces an infinity}} +} + +// NaN payload preservation tests. +template <typename T> struct Bytes { + unsigned char bytes[sizeof(T)]; + bool operator==(const Bytes &) const = default; +}; +template <typename T> constexpr bool bytesEqual(T a, T b) { + return __builtin_bit_cast(Bytes<T>, a) == __builtin_bit_cast(Bytes<T>, b); +} + +constexpr double nanWithNonZeroPayload = __builtin_nan("0x1337"); + +// fdim NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_fdim(nanWithNonZeroPayload, 0.))); +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_fdim(0., nanWithNonZeroPayload))); + +// round NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_round(nanWithNonZeroPayload))); + +// ceil NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_ceil(nanWithNonZeroPayload))); + +// floor NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_floor(nanWithNonZeroPayload))); + +// trunc NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_trunc(nanWithNonZeroPayload))); + +// roundeven NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_roundeven(nanWithNonZeroPayload))); + +// fmod NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_fmod(nanWithNonZeroPayload, 2.0))); +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_fmod(2.0, nanWithNonZeroPayload))); + +// remainder NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_remainder(nanWithNonZeroPayload, 2.0))); +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_remainder(2.0, nanWithNonZeroPayload))); + +// scalbn NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_scalbn(nanWithNonZeroPayload, 2))); + +// ldexp NaN payload preservation +static_assert(bytesEqual(nanWithNonZeroPayload, + __builtin_ldexp(nanWithNonZeroPayload, 2))); + +// modf NaN payload preservation +constexpr double test_modf_nan() { + double iptr; + return __builtin_modf(nanWithNonZeroPayload, &iptr); +} +static_assert(bytesEqual(nanWithNonZeroPayload, test_modf_nan())); + +// frexp NaN payload preservation +constexpr double test_frexp_nan() { + int exp; + return __builtin_frexp(nanWithNonZeroPayload, &exp); +} +static_assert(bytesEqual(nanWithNonZeroPayload, test_frexp_nan())); diff --git a/clang/test/SemaCXX/constexpr-fenv-access.cpp b/clang/test/SemaCXX/constexpr-fenv-access.cpp new file mode 100644 index 0000000000000..a41a6eacafbd4 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-fenv-access.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple x86_64-linux -verify -Wno-unknown-pragmas %s +// RUN: %clang_cc1 -triple x86_64-linux -verify -fexperimental-new-constant-interpreter -Wno-unknown-pragmas %s +// expected-no-diagnostics + +#pragma STDC FENV_ROUND FE_TONEARESTFROMZERO +static_assert(__builtin_fdim(__DBL_EPSILON__ / 2., -1.) == 1. + __DBL_EPSILON__, ""); +static_assert(__builtin_fma(0.5, __DBL_EPSILON__, 1.0) == 1. + __DBL_EPSILON__, ""); +static_assert(__builtin_scalbn(1.0 + __DBL_EPSILON__ / 2., 0) == 1. + __DBL_EPSILON__, ""); + +#pragma STDC FENV_ROUND FE_TONEAREST +static_assert(__builtin_fdim(__DBL_EPSILON__ / 2., -1.) == 1., ""); +static_assert(__builtin_fma(0.5, __DBL_EPSILON__, 1.0) == 1., ""); +static_assert(__builtin_scalbn(1.0 + __DBL_EPSILON__ / 2., 0) == 1., ""); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
