diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4698,6 +4698,7 @@ * ``__builtin_fmax`` * ``__builtin_fmin`` * ``__builtin_fpclassify`` +* ``__builtin_ilogb`` * ``__builtin_inf`` * ``__builtin_isinf`` * ``__builtin_isinf_sign`` diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -273,10 +273,10 @@ BUILTIN(__builtin_hypotf, "fff" , "Fne") BUILTIN(__builtin_hypotl, "LdLdLd", "Fne") BUILTIN(__builtin_hypotf128, "LLdLLdLLd", "Fne") -BUILTIN(__builtin_ilogb , "id", "Fne") -BUILTIN(__builtin_ilogbf, "if", "Fne") -BUILTIN(__builtin_ilogbl, "iLd", "Fne") -BUILTIN(__builtin_ilogbf128, "iLLd", "Fne") +BUILTIN(__builtin_ilogb , "id", "FneE") +BUILTIN(__builtin_ilogbf, "if", "FneE") +BUILTIN(__builtin_ilogbl, "iLd", "FneE") +BUILTIN(__builtin_ilogbf128, "iLLd", "FneE") BUILTIN(__builtin_lgamma , "dd", "Fn") BUILTIN(__builtin_lgammaf, "ff", "Fn") BUILTIN(__builtin_lgammal, "LdLd", "Fn") diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7349,6 +7349,13 @@ return true; } +// A call to a C standard library function is a non-constant library call +// ([defns.nonconst.libcall]) if it raises a floating-point exception other +// than FE_INEXACT. +static bool isConstantOpStatus(APFloat::opStatus St) { + return (St & ~APFloat::opInexact) == 0; +} + template class ExprEvaluatorBase : public ConstStmtVisitor { @@ -12433,6 +12440,23 @@ return false; return Success(DidOverflow, E); } + case Builtin::BI__builtin_ilogb: + case Builtin::BI__builtin_ilogbf: + case Builtin::BI__builtin_ilogbl: + case Builtin::BI__builtin_ilogbf128: { + APFloat F(0.0); + if (!EvaluateFloat(E->getArg(0), F, Info)) + return false; + + int Ilogb; + if (APFloat::opStatus St = ilogb(F, Ilogb); !isConstantOpStatus(St)) + return false; + + APSInt Logb(llvm::APInt(32, Ilogb, /*isSigned=*/true), + /*isUnsigned=*/false); + Result = APValue(std::move(Logb)); + return true; + } } } diff --git a/clang/test/Sema/constant-builtins-ilogb.cpp b/clang/test/Sema/constant-builtins-ilogb.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/constant-builtins-ilogb.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics + +constexpr double NaN = __builtin_nan(""); +constexpr double Inf = __builtin_inf(); +constexpr double NegInf = -__builtin_inf(); + +#define ILOGB_TEST_SPECIAL_NUMBERS(T, FUNC) \ + static_assert(!__builtin_constant_p(FUNC(T(0.0)))); \ + static_assert(!__builtin_constant_p(FUNC(T(-0.0)))); \ + static_assert(!__builtin_constant_p(FUNC(T(NaN)))); \ + static_assert(!__builtin_constant_p(FUNC(T(Inf)))); \ + static_assert(!__builtin_constant_p(FUNC(T(NegInf)))); + +#define ILOGB_TEST_POWERS_OF_TWO(T, FUNC) \ + static_assert(0 == FUNC(T(1.0))); \ + static_assert(0 == FUNC(T(-1.0))); \ + static_assert(1 == FUNC(T(2.0))); \ + static_assert(1 == FUNC(T(-2.0))); \ + static_assert(2 == FUNC(T(4.0))); \ + static_assert(2 == FUNC(T(-4.0))); \ + static_assert(3 == FUNC(T(8.0))); \ + static_assert(3 == FUNC(T(-8.0))); \ + static_assert(4 == FUNC(T(16.0))); \ + static_assert(4 == FUNC(T(-16.0))); \ + static_assert(5 == FUNC(T(32.0))); \ + static_assert(5 == FUNC(T(-32.0))); + +#define ILOGB_TEST_SOME_INTEGERS(T, FUNC) \ + static_assert(1 == FUNC(T(3.0))); \ + static_assert(1 == FUNC(T(-3.0))); \ + static_assert(2 == FUNC(T(7.0))); \ + static_assert(2 == FUNC(T(-7.0))); \ + static_assert(3 == FUNC(T(10.0))); \ + static_assert(3 == FUNC(T(-10.0))); \ + static_assert(4 == FUNC(T(31.0))); \ + static_assert(4 == FUNC(T(-31.0))); \ + static_assert(5 == FUNC(T(55.0))); \ + static_assert(5 == FUNC(T(-55.0))); + +#define LIST_ILOGB_TESTS(T, FUNC) \ + ILOGB_TEST_SPECIAL_NUMBERS(T, FUNC) \ + ILOGB_TEST_POWERS_OF_TWO(T, FUNC) \ + ILOGB_TEST_SOME_INTEGERS(T, FUNC) + +LIST_ILOGB_TESTS(double, __builtin_ilogb) +LIST_ILOGB_TESTS(float, __builtin_ilogbf) +LIST_ILOGB_TESTS((long double), __builtin_ilogbl) +#ifdef __FLOAT128__ +LIST_ILOGB_TESTS(__float128, __builtin_ilogbf128) +#endif + +// assert smallest subnormal and largest finite numbers +static_assert(__builtin_ilogbf(1.40129846E-45f) == -149); +static_assert(__builtin_ilogbf(3.40282347E+38f) == 127); + +static_assert(__builtin_ilogb(4.9406564584124654E-324) == -1074); +static_assert(__builtin_ilogb(1.7976931348623157E+308) == 1023); + +#if !defined(_WIN32) && !defined(_WIN64) // in MSVC sizeof(long double) == sizeof(double) +static_assert(__builtin_ilogbl(3.64519953188247460253E-4951L) == -16445); +static_assert(__builtin_ilogbl(1.18973149535723176502E+4932L) == 16383); +#endif + +#ifdef __FLOAT128__ +static_assert(__builtin_ilogbf128(6.47517511943802511092443895822764655E-4966Q) == -16494); +static_assert(__builtin_ilogbf128(1.18973149535723176508575932662800702E+4932Q) == 16383); +#endif diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h --- a/llvm/include/llvm/ADT/APFloat.h +++ b/llvm/include/llvm/ADT/APFloat.h @@ -443,16 +443,19 @@ /// return true. bool getExactInverse(APFloat *inv) const; - /// Returns the exponent of the internal representation of the APFloat. + /// Calculates the exponent of the internal representation of the APFloat + /// and saves the result in `Result`. /// /// Because the radix of APFloat is 2, this is equivalent to floor(log2(x)). - /// For special APFloat values, this returns special error codes: + /// For special APFloat values, this saves special error codes to `Result`: /// /// NaN -> \c IEK_NaN /// 0 -> \c IEK_Zero /// Inf -> \c IEK_Inf /// - friend int ilogb(const IEEEFloat &Arg); + /// and returns `opInvalidOp`. Returns `opOK` for other values. + /// + friend opStatus ilogb(const IEEEFloat &Arg, int &Result); /// Returns: X * 2^Exp for integral exponents. friend IEEEFloat scalbn(IEEEFloat X, int Exp, roundingMode); @@ -597,6 +600,7 @@ }; hash_code hash_value(const IEEEFloat &Arg); +IEEEFloat::opStatus ilogb(const IEEEFloat &Arg, int &Result); int ilogb(const IEEEFloat &Arg); IEEEFloat scalbn(IEEEFloat X, int Exp, IEEEFloat::roundingMode); IEEEFloat frexp(const IEEEFloat &Val, int &Exp, IEEEFloat::roundingMode RM); @@ -1253,6 +1257,9 @@ } friend hash_code hash_value(const APFloat &Arg); + friend opStatus ilogb(const APFloat &Arg, int &Result) { + return ilogb(Arg.getIEEE(), Result); + } friend int ilogb(const APFloat &Arg) { return ilogb(Arg.getIEEE()); } friend APFloat scalbn(APFloat X, int Exp, roundingMode RM); friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM); diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp --- a/llvm/lib/Support/APFloat.cpp +++ b/llvm/lib/Support/APFloat.cpp @@ -4242,22 +4242,37 @@ APInt::tcSetBit(significandParts(), semantics->precision - 2); } -int ilogb(const IEEEFloat &Arg) { - if (Arg.isNaN()) - return IEEEFloat::IEK_NaN; - if (Arg.isZero()) - return IEEEFloat::IEK_Zero; - if (Arg.isInfinity()) - return IEEEFloat::IEK_Inf; - if (!Arg.isDenormal()) - return Arg.exponent; +IEEEFloat::opStatus ilogb(const IEEEFloat &Arg, int &Result) { + if (Arg.isNaN()) { + Result = IEEEFloat::IEK_NaN; + return IEEEFloat::opInvalidOp; + } + if (Arg.isZero()) { + Result = IEEEFloat::IEK_Zero; + return IEEEFloat::opInvalidOp; + } + if (Arg.isInfinity()) { + Result = IEEEFloat::IEK_Inf; + return IEEEFloat::opInvalidOp; + } + if (!Arg.isDenormal()) { + Result = Arg.exponent; + return IEEEFloat::opOK; + } IEEEFloat Normalized(Arg); int SignificandBits = Arg.getSemantics().precision - 1; Normalized.exponent += SignificandBits; Normalized.normalize(IEEEFloat::rmNearestTiesToEven, lfExactlyZero); - return Normalized.exponent - SignificandBits; + Result = Normalized.exponent - SignificandBits; + return IEEEFloat::opOK; +} + +int ilogb(const IEEEFloat &Arg) { + int Result; + ilogb(Arg, Result); + return Result; } IEEEFloat scalbn(IEEEFloat X, int Exp, IEEEFloat::roundingMode RoundingMode) { diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -3250,46 +3250,99 @@ } TEST(APFloatTest, ilogb) { - EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), false))); - EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), true))); - EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1024"))); - EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1023"))); - EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1023"))); - EXPECT_EQ(-51, ilogb(APFloat(APFloat::IEEEdouble(), "0x1p-51"))); - EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1023"))); - EXPECT_EQ(-2, ilogb(APFloat(APFloat::IEEEdouble(), "0x0.ffffp-1"))); - EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.fffep-1023"))); - EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble(), false))); - EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble(), true))); - - - EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+0"))); - EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle(), "-0x1p+0"))); - EXPECT_EQ(42, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+42"))); - EXPECT_EQ(-42, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p-42"))); - - EXPECT_EQ(APFloat::IEK_Inf, - ilogb(APFloat::getInf(APFloat::IEEEsingle(), false))); - EXPECT_EQ(APFloat::IEK_Inf, - ilogb(APFloat::getInf(APFloat::IEEEsingle(), true))); - EXPECT_EQ(APFloat::IEK_Zero, - ilogb(APFloat::getZero(APFloat::IEEEsingle(), false))); - EXPECT_EQ(APFloat::IEK_Zero, - ilogb(APFloat::getZero(APFloat::IEEEsingle(), true))); - EXPECT_EQ(APFloat::IEK_NaN, - ilogb(APFloat::getNaN(APFloat::IEEEsingle(), false))); - EXPECT_EQ(APFloat::IEK_NaN, - ilogb(APFloat::getSNaN(APFloat::IEEEsingle(), false))); - - EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle(), false))); - EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle(), true))); - - EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle(), false))); - EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle(), true))); - EXPECT_EQ(-126, - ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false))); - EXPECT_EQ(-126, - ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true))); + int Result; + + EXPECT_EQ(ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), false), Result), + APFloat::opOK); + EXPECT_EQ(-1074, Result); + EXPECT_EQ(ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), true), Result), + APFloat::opOK); + EXPECT_EQ(-1074, Result); + EXPECT_EQ( + ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1024"), Result), + APFloat::opOK); + EXPECT_EQ(-1023, Result); + EXPECT_EQ( + ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1023"), Result), + APFloat::opOK); + EXPECT_EQ(-1023, Result); + EXPECT_EQ( + ilogb(APFloat(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1023"), Result), + APFloat::opOK); + EXPECT_EQ(-1023, Result); + EXPECT_EQ(ilogb(APFloat(APFloat::IEEEdouble(), "0x1p-51"), Result), + APFloat::opOK); + EXPECT_EQ(-51, Result); + EXPECT_EQ( + ilogb(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1023"), Result), + APFloat::opOK); + EXPECT_EQ(-1023, Result); + EXPECT_EQ(ilogb(APFloat(APFloat::IEEEdouble(), "0x0.ffffp-1"), Result), + APFloat::opOK); + EXPECT_EQ(-2, Result); + EXPECT_EQ(ilogb(APFloat(APFloat::IEEEdouble(), "0x1.fffep-1023"), Result), + APFloat::opOK); + EXPECT_EQ(-1023, Result); + EXPECT_EQ(ilogb(APFloat::getLargest(APFloat::IEEEdouble(), false), Result), + APFloat::opOK); + EXPECT_EQ(1023, Result); + EXPECT_EQ(ilogb(APFloat::getLargest(APFloat::IEEEdouble(), true), Result), + APFloat::opOK); + EXPECT_EQ(1023, Result); + + EXPECT_EQ(ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+0"), Result), + APFloat::opOK); + EXPECT_EQ(0, Result); + EXPECT_EQ(ilogb(APFloat(APFloat::IEEEsingle(), "-0x1p+0"), Result), + APFloat::opOK); + EXPECT_EQ(0, Result); + EXPECT_EQ(ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+42"), Result), + APFloat::opOK); + EXPECT_EQ(42, Result); + EXPECT_EQ(ilogb(APFloat(APFloat::IEEEsingle(), "0x1p-42"), Result), + APFloat::opOK); + EXPECT_EQ(-42, Result); + + EXPECT_EQ(ilogb(APFloat::getInf(APFloat::IEEEsingle(), false), Result), + APFloat::opInvalidOp); + EXPECT_EQ(APFloat::IEK_Inf, Result); + EXPECT_EQ(ilogb(APFloat::getInf(APFloat::IEEEsingle(), true), Result), + APFloat::opInvalidOp); + EXPECT_EQ(APFloat::IEK_Inf, Result); + EXPECT_EQ(ilogb(APFloat::getZero(APFloat::IEEEsingle(), false), Result), + APFloat::opInvalidOp); + EXPECT_EQ(APFloat::IEK_Zero, Result); + EXPECT_EQ(ilogb(APFloat::getZero(APFloat::IEEEsingle(), true), Result), + APFloat::opInvalidOp); + EXPECT_EQ(APFloat::IEK_Zero, Result); + EXPECT_EQ(ilogb(APFloat::getNaN(APFloat::IEEEsingle(), false), Result), + APFloat::opInvalidOp); + EXPECT_EQ(APFloat::IEK_NaN, Result); + EXPECT_EQ(ilogb(APFloat::getSNaN(APFloat::IEEEsingle(), false), Result), + APFloat::opInvalidOp); + EXPECT_EQ(APFloat::IEK_NaN, Result); + + EXPECT_EQ(ilogb(APFloat::getLargest(APFloat::IEEEsingle(), false), Result), + APFloat::opOK); + EXPECT_EQ(127, Result); + EXPECT_EQ(ilogb(APFloat::getLargest(APFloat::IEEEsingle(), true), Result), + APFloat::opOK); + EXPECT_EQ(127, Result); + + EXPECT_EQ(ilogb(APFloat::getSmallest(APFloat::IEEEsingle(), false), Result), + APFloat::opOK); + EXPECT_EQ(-149, Result); + EXPECT_EQ(ilogb(APFloat::getSmallest(APFloat::IEEEsingle(), true), Result), + APFloat::opOK); + EXPECT_EQ(-149, Result); + EXPECT_EQ(ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false), + Result), + APFloat::opOK); + EXPECT_EQ(-126, Result); + EXPECT_EQ(ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true), + Result), + APFloat::opOK); + EXPECT_EQ(-126, Result); } TEST(APFloatTest, scalbn) {