diff options
Diffstat (limited to 'llvm/unittests/ADT')
-rw-r--r-- | llvm/unittests/ADT/APFloatTest.cpp | 1715 | ||||
-rw-r--r-- | llvm/unittests/ADT/APIntTest.cpp | 125 | ||||
-rw-r--r-- | llvm/unittests/ADT/DenseMapTest.cpp | 52 | ||||
-rw-r--r-- | llvm/unittests/ADT/SmallPtrSetTest.cpp | 4 | ||||
-rw-r--r-- | llvm/unittests/ADT/StringRefTest.cpp | 13 |
5 files changed, 1813 insertions, 96 deletions
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp index 9609e8e..141282e 100644 --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -16,9 +16,11 @@ #include "llvm/Support/FormatVariadic.h" #include "gtest/gtest.h" #include <cmath> +#include <limits> #include <ostream> #include <string> #include <tuple> +#include <type_traits> using namespace llvm; @@ -1658,6 +1660,90 @@ TEST(APFloatTest, toInteger) { EXPECT_EQ(APSInt::getMaxValue(5, false), result); } +class APFloatConvertFromAPIntParamTest + : public ::testing::TestWithParam<const fltSemantics *> { +protected: + // Helper to run a conversion and compare the integer result directly. + static void testConversionAndCompareInt(const APInt &InputValue, + const bool IsSigned, + APFloat::roundingMode RM, + const APInt &ExpectedIntValue) { + const fltSemantics &Sem = *GetParam(); + APFloat F(Sem); + F.convertFromAPInt(InputValue, /*IsSigned=*/IsSigned, RM); + + APSInt ResultInt(InputValue.getBitWidth(), /*isUnsigned=*/!IsSigned); + bool IsExact; + F.convertToInteger(ResultInt, APFloat::rmTowardZero, &IsExact); + + EXPECT_TRUE(IsExact); + EXPECT_TRUE(ResultInt.eq(ExpectedIntValue)) + << "InputValue: " << InputValue << "\n" + << ResultInt << " vs " << ExpectedIntValue << "\n"; + } +}; + +TEST_P(APFloatConvertFromAPIntParamTest, HalfwayRounding) { + const fltSemantics &Sem = *GetParam(); + const unsigned Precision = APFloat::semanticsPrecision(Sem); + + if (Precision == 0) + GTEST_SKIP() << "Skipping test for semantics with no significand."; + + for (bool IsSigned : {false, true}) { + const unsigned BitWidth = Precision + 1 + (IsSigned ? 1 : 0); + + const APInt RoundedDownVal = APInt::getOneBitSet(BitWidth, Precision); + const APInt HalfwayVal = RoundedDownVal + 1; + const APInt RoundedUpVal = RoundedDownVal + 2; + + testConversionAndCompareInt(HalfwayVal, IsSigned, + APFloat::rmNearestTiesToEven, RoundedDownVal); + testConversionAndCompareInt(HalfwayVal, IsSigned, + APFloat::rmNearestTiesToAway, RoundedUpVal); + testConversionAndCompareInt(HalfwayVal, IsSigned, APFloat::rmTowardPositive, + RoundedUpVal); + testConversionAndCompareInt(HalfwayVal, IsSigned, APFloat::rmTowardNegative, + RoundedDownVal); + testConversionAndCompareInt(HalfwayVal, IsSigned, APFloat::rmTowardZero, + RoundedDownVal); + } +} + +TEST_P(APFloatConvertFromAPIntParamTest, MaxMagnitude) { + const fltSemantics &Sem = *GetParam(); + const unsigned Precision = APFloat::semanticsPrecision(Sem); + + if (Precision == 0) + GTEST_SKIP() << "Skipping test for semantics with no significand."; + + const APFloat Largest = APFloat::getLargest(Sem, /*Negative=*/false); + const int Exp = ilogb(Largest); + for (bool IsSigned : {false, true}) { + const unsigned BitWidth = Exp + 1 + (IsSigned ? 1 : 0); + + bool IsExact; + APSInt LargestAsInt{BitWidth, /*IsUnsigned=*/!IsSigned}; + const APFloat::opStatus ToIntStatus = + Largest.convertToInteger(LargestAsInt, APFloat::rmTowardZero, &IsExact); + EXPECT_EQ(ToIntStatus, APFloat::opOK); + + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + testConversionAndCompareInt(LargestAsInt, IsSigned, RM, LargestAsInt); + } + } +} + +INSTANTIATE_TEST_SUITE_P(IEEESemantics, APFloatConvertFromAPIntParamTest, + ::testing::Values(&APFloat::IEEEhalf(), + &APFloat::BFloat(), + &APFloat::IEEEsingle(), + &APFloat::IEEEdouble(), + &APFloat::IEEEquad())); + static APInt nanbitsFromAPInt(const fltSemantics &Sem, bool SNaN, bool Negative, uint64_t payload) { APInt appayload(64, payload); @@ -1916,6 +2002,15 @@ TEST(APFloatTest, exactInverse) { EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::PPCDoubleDouble(), "0.5"))); EXPECT_TRUE(APFloat(APFloat::x87DoubleExtended(), "2.0").getExactInverse(&inv)); EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::x87DoubleExtended(), "0.5"))); + // 0x1p1022 has a normal inverse for IEEE 754 binary64: 0x1p-1022. + EXPECT_TRUE(APFloat(0x1p1022).getExactInverse(&inv)); + EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(0x1p-1022))); + // With regards to getExactInverse, IEEEdouble and PPCDoubleDouble should + // behave the same. + EXPECT_TRUE( + APFloat(APFloat::PPCDoubleDouble(), "0x1p1022").getExactInverse(&inv)); + EXPECT_TRUE( + inv.bitwiseIsEqual(APFloat(APFloat::PPCDoubleDouble(), "0x1p-1022"))); // FLT_MIN EXPECT_TRUE(APFloat(1.17549435e-38f).getExactInverse(&inv)); @@ -1927,6 +2022,8 @@ TEST(APFloatTest, exactInverse) { EXPECT_FALSE(APFloat(0.0).getExactInverse(nullptr)); // Denormalized float EXPECT_FALSE(APFloat(1.40129846e-45f).getExactInverse(nullptr)); + // Largest subnormal + EXPECT_FALSE(APFloat(0x1p-127f).getExactInverse(nullptr)); } TEST(APFloatTest, roundToIntegral) { @@ -2661,6 +2758,37 @@ TEST(APFloatTest, Float8UZConvert) { } } +struct DD { + double Hi; + double Lo; +}; + +template <typename T, typename U> +static APFloat makeDoubleAPFloat(T Hi, U Lo) { + APFloat HiFloat{APFloat::IEEEdouble(), APFloat::uninitialized}; + if constexpr (std::is_same_v<decltype(Hi), APFloat>) { + HiFloat = Hi; + } else if constexpr (std::is_same_v<decltype(Hi), double>) { + HiFloat = APFloat{Hi}; + } else { + HiFloat = {APFloat::IEEEdouble(), Hi}; + } + + APFloat LoFloat{APFloat::IEEEdouble(), APFloat::uninitialized}; + if constexpr (std::is_same_v<decltype(Lo), APFloat>) { + LoFloat = Lo; + } else if constexpr (std::is_same_v<decltype(Lo), double>) { + LoFloat = APFloat{Lo}; + } else { + LoFloat = {APFloat::IEEEdouble(), Lo}; + } + + APInt Bits = LoFloat.bitcastToAPInt().concat(HiFloat.bitcastToAPInt()); + return APFloat(APFloat::PPCDoubleDouble(), Bits); +} + +static APFloat makeDoubleAPFloat(DD X) { return makeDoubleAPFloat(X.Hi, X.Lo); } + TEST(APFloatTest, PPCDoubleDouble) { APFloat test(APFloat::PPCDoubleDouble(), "1.0"); EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); @@ -5315,18 +5443,809 @@ TEST(APFloatTest, PPCDoubleDoubleFMA) { APFloat(APFloat::PPCDoubleDouble(), "10").compare(A)); } -TEST(APFloatTest, PPCDoubleDoubleRoundToIntegral) { - { - APFloat A(APFloat::PPCDoubleDouble(), "1.5"); - A.roundToIntegral(APFloat::rmNearestTiesToEven); - EXPECT_EQ(APFloat::cmpEqual, - APFloat(APFloat::PPCDoubleDouble(), "2").compare(A)); +struct PPCDoubleDoubleRoundToIntegralTestCase { + DD Input; + DD Rounded[5] = {}; + constexpr PPCDoubleDoubleRoundToIntegralTestCase & + withRounded(DD R, APFloat::roundingMode RM) { + Rounded[static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM)] = R; + return *this; } - { - APFloat A(APFloat::PPCDoubleDouble(), "2.5"); - A.roundToIntegral(APFloat::rmNearestTiesToEven); - EXPECT_EQ(APFloat::cmpEqual, - APFloat(APFloat::PPCDoubleDouble(), "2").compare(A)); +}; + +auto ppcDoubleDoubleRoundToIntegralTests() { + constexpr double Eps = std::numeric_limits<double>::epsilon(); + constexpr double HalfEps = Eps / 2.0; + constexpr double QuarterEps = Eps / 4.0; + constexpr double SmallestNormal = std::numeric_limits<double>::min(); + constexpr double EvenIntegerThreshold{uint64_t{1} + << std::numeric_limits<double>::digits}; + constexpr double Inf = std::numeric_limits<double>::infinity(); + constexpr double QNaN = std::numeric_limits<double>::quiet_NaN(); + using TestCase = PPCDoubleDoubleRoundToIntegralTestCase; + static constexpr auto TestCases = std::array{ + // 1. Zeros and Basic Integers + // Input: Positive Zero (0.0, 0.0) + TestCase({{0.0, 0.0}}) + .withRounded({0.0, 0.0}, APFloat::rmTowardZero) + .withRounded({0.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({0.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Negative Zero (-0.0, 0.0) + TestCase({{-0.0, 0.0}}) + .withRounded({-0.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-0.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-0.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Positive Even (2.0, 0.0) + TestCase({{2.0, 0.0}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Positive Odd (3.0, 0.0) + TestCase({{3.0, 0.0}}) + .withRounded({3.0, 0.0}, APFloat::rmTowardZero) + .withRounded({3.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Negative Even (-2.0, 0.0) + TestCase({{-2.0, 0.0}}) + .withRounded({-2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // 2. General Fractions (Non-Ties) + // Input: 2.3 + TestCase({{2.3, 0.0}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: 2.7 + TestCase({{2.7, 0.0}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: -2.3 + TestCase({{-2.3, 0.0}}) + .withRounded({-2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: -2.7 + TestCase({{-2.7, 0.0}}) + .withRounded({-2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: 2.3 + Tiny + TestCase({{2.3, SmallestNormal}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // 3. Exact Midpoints (Ties at N.5) + // Input: 0.5 + TestCase({{0.5, 0.0}}) + .withRounded({0.0, 0.0}, APFloat::rmTowardZero) + .withRounded({0.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({1.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: 1.5 (Odd base) + TestCase({{1.5, 0.0}}) + .withRounded({1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({1.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: 2.5 (Even base) + TestCase({{2.5, 0.0}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: -0.5 + TestCase({{-0.5, 0.0}}) + .withRounded({-0.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-1.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-0.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-1.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: -1.5 (Odd base) + TestCase({{-1.5, 0.0}}) + .withRounded({-1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-1.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: -2.5 (Even base) + TestCase({{-2.5, 0.0}}) + .withRounded({-2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // 4. Near Midpoints (lo breaks the tie) + // Input: Slightly > 2.5 + TestCase({{2.5, SmallestNormal}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly < 2.5 + TestCase({{2.5, -SmallestNormal}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly > 1.5 + TestCase({{1.5, SmallestNormal}}) + .withRounded({1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({1.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly < 1.5 + TestCase({{1.5, -SmallestNormal}}) + .withRounded({1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({1.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly > -2.5 (closer to 0) + TestCase({{-2.5, SmallestNormal}}) + .withRounded({-2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly < -2.5 (further from 0) + TestCase({{-2.5, -SmallestNormal}}) + .withRounded({-2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToEven), + + // 5. Near Integers (lo crosses the integer boundary) + // Input: Slightly > 2.0 + TestCase({{2.0, SmallestNormal}}) + .withRounded({2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({3.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly < 2.0 (1.99...) + TestCase({{2.0, -SmallestNormal}}) + .withRounded({1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({1.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly > -2.0 (-1.99...) + TestCase({{-2.0, SmallestNormal}}) + .withRounded({-1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-2.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-1.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly < -2.0 + TestCase({{-2.0, -SmallestNormal}}) + .withRounded({-2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly > 0.0 + TestCase({{SmallestNormal, 0.0}}) + .withRounded({0.0, 0.0}, APFloat::rmTowardZero) + .withRounded({0.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({1.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: Slightly < 0.0 + TestCase({{-SmallestNormal, 0.0}}) + .withRounded({-0.0, 0.0}, APFloat::rmTowardZero) + .withRounded({-1.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({-0.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToEven), + + // 6. Boundary of Canonicalization (Maximum lo) + // Input: 1.0 + Max lo (1 + 2^-53) + TestCase({{1.0, HalfEps}}) + .withRounded({1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({1.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({2.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: 1.0 - Max lo (1 - 2^-54) + TestCase({{1.0, -QuarterEps}}) + .withRounded({0.0, 0.0}, APFloat::rmTowardZero) + .withRounded({0.0, 0.0}, APFloat::rmTowardNegative) + .withRounded({1.0, 0.0}, APFloat::rmTowardPositive) + .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToEven), + + // 7. Large Magnitudes (Beyond 2^53). N = EvenIntegerThreshold (Even) + // Input: EvenIntegerThreshold (Exact) + TestCase({{EvenIntegerThreshold, 0.0}}) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToEven), + + // Input: EvenIntegerThreshold+1 (Exact) + TestCase({{EvenIntegerThreshold, 1.0}}) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToEven), + + // Fractions + // Input: EvenIntegerThreshold+0.25 + TestCase({{EvenIntegerThreshold, 0.25}}) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToEven), + + // Input: EvenIntegerThreshold+0.75 + TestCase({{EvenIntegerThreshold, 0.75}}) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToEven), + + // Ties (Midpoints) + // Input: EvenIntegerThreshold-0.5 + TestCase({{EvenIntegerThreshold, -0.5}}) + .withRounded({EvenIntegerThreshold - 1.0, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold - 1.0, 0.0}, + APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToEven), + + // Input: EvenIntegerThreshold+0.5 + TestCase({{EvenIntegerThreshold, 0.5}}) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToEven), + + // Input: EvenIntegerThreshold+1.5 + TestCase({{EvenIntegerThreshold + 2.0, -0.5}}) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold + 2.0, 0.0}, + APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold + 2.0, 0.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold + 2.0, 0.0}, + APFloat::rmNearestTiesToEven), + + // Input: EvenIntegerThreshold+2.5 + TestCase({{EvenIntegerThreshold + 2.0, 0.5}}) + .withRounded({EvenIntegerThreshold + 2.0, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold + 2.0, 0.0}, + APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold + 4.0, -1.0}, + APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold + 4.0, -1.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold + 2.0, 0.0}, + APFloat::rmNearestTiesToEven), + + // Near Ties + // Input: EvenIntegerThreshold+0.5+HalfEps + TestCase({{EvenIntegerThreshold, 0.5 + HalfEps}}) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToEven), + + // Input: EvenIntegerThreshold+0.5-QuarterEps + TestCase({{EvenIntegerThreshold, 0.5 - QuarterEps}}) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 0.0}, + APFloat::rmNearestTiesToEven), + + // Canonical Boundary (Max lo for EvenIntegerThreshold is 1.0) + // Input: EvenIntegerThreshold+1.0 + TestCase({{EvenIntegerThreshold, 1.0}}) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardZero) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardNegative) + .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToAway) + .withRounded({EvenIntegerThreshold, 1.0}, + APFloat::rmNearestTiesToEven), + + // 8. Special Values + // Input: +Inf + TestCase({{Inf, 0.0}}) + .withRounded({Inf, 0.0}, APFloat::rmTowardZero) + .withRounded({Inf, 0.0}, APFloat::rmTowardNegative) + .withRounded({Inf, 0.0}, APFloat::rmTowardPositive) + .withRounded({Inf, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({Inf, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: -Inf + TestCase({{-Inf, 0.0}}) + .withRounded({-Inf, 0.0}, APFloat::rmTowardZero) + .withRounded({-Inf, 0.0}, APFloat::rmTowardNegative) + .withRounded({-Inf, 0.0}, APFloat::rmTowardPositive) + .withRounded({-Inf, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({-Inf, 0.0}, APFloat::rmNearestTiesToEven), + + // Input: NaN input hi. Expected output canonical (NaN, 0.0). + TestCase({{QNaN, 0.0}}) + .withRounded({QNaN, 0.0}, APFloat::rmTowardZero) + .withRounded({QNaN, 0.0}, APFloat::rmTowardNegative) + .withRounded({QNaN, 0.0}, APFloat::rmTowardPositive) + .withRounded({QNaN, 0.0}, APFloat::rmNearestTiesToAway) + .withRounded({QNaN, 0.0}, APFloat::rmNearestTiesToEven), + }; + return TestCases; +} + +class PPCDoubleDoubleRoundToIntegralValueTest + : public testing::Test, + public ::testing::WithParamInterface< + PPCDoubleDoubleRoundToIntegralTestCase> {}; + +INSTANTIATE_TEST_SUITE_P( + PPCDoubleDoubleRoundToIntegralValueParamTests, + PPCDoubleDoubleRoundToIntegralValueTest, + ::testing::ValuesIn(ppcDoubleDoubleRoundToIntegralTests())); + +TEST_P(PPCDoubleDoubleRoundToIntegralValueTest, + PPCDoubleDoubleRoundToIntegral) { + const PPCDoubleDoubleRoundToIntegralTestCase TestCase = GetParam(); + const APFloat Input = makeDoubleAPFloat(TestCase.Input); + EXPECT_FALSE(Input.isDenormal()) + << TestCase.Input.Hi << " + " << TestCase.Input.Lo; + for (size_t I = 0, E = std::size(TestCase.Rounded); I != E; ++I) { + const auto RM = static_cast<APFloat::roundingMode>(I); + const APFloat Expected = makeDoubleAPFloat(TestCase.Rounded[I]); + EXPECT_FALSE(Expected.isDenormal()) + << TestCase.Rounded[I].Hi << " + " << TestCase.Input.Lo; + APFloat Actual = Input; + Actual.roundToIntegral(RM); + if (Actual.isNaN()) + EXPECT_TRUE(Actual.isNaN()); + else + EXPECT_EQ(Actual.compare(Expected), APFloat::cmpEqual) + << "RM: " << RM << " Input.Hi: " << TestCase.Input.Hi + << " Input.Lo: " << TestCase.Input.Lo << " Actual: " << Actual + << " Expected.Hi: " << TestCase.Rounded[I].Hi + << " Expected.Lo: " << TestCase.Rounded[I].Lo + << " Expected: " << Expected; + } +} + +namespace PPCDoubleDoubleConvertToIntegerTestDetails { +// Define the rounding modes for easier readability. +static constexpr auto RNE = APFloat::rmNearestTiesToEven; +static constexpr auto RNA = APFloat::rmNearestTiesToAway; +static constexpr auto RTZ = APFloat::rmTowardZero; +static constexpr auto RUP = APFloat::rmTowardPositive; +static constexpr auto RDN = APFloat::rmTowardNegative; + +struct TestCase { + // Structure to hold the expected result of a conversion + struct ExpectedConversion { + // The expected integer value represented as a string (decimal). + // We use a string to easily represent arbitrary precision values in + // constexpr. The test runner should parse this into an APSInt matching the + // test configuration. + const char *ExpectedIntStr; + APFloat::opStatus Status; + }; + + DD Input; + unsigned IntegerWidth; + bool IsSigned; + // Array indexed by the rounding mode enum value. + std::array<ExpectedConversion, 5> Rounded = {}; + + // Helper to define the expected results for a specific rounding mode. + constexpr TestCase &with(APFloat::roundingMode RM, const char *ExpectedStr, + APFloat::opStatus Status) { + Rounded[static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM)] = { + ExpectedStr, + Status, + }; + return *this; + } + + // Helper to define the same result for all rounding modes. + constexpr TestCase &withAll(const char *ExpectedStr, + APFloat::opStatus Status) { + return with(RNE, ExpectedStr, Status) + .with(RNA, ExpectedStr, Status) + .with(RTZ, ExpectedStr, Status) + .with(RUP, ExpectedStr, Status) + .with(RDN, ExpectedStr, Status); + } +}; + +auto testCases() { + // Define the status codes. + constexpr auto OK = llvm::APFloat::opOK; + constexpr auto Inexact = llvm::APFloat::opInexact; + // The API specifies opInvalidOp for out-of-range (overflow/underflow) and + // NaN. + constexpr auto Invalid = llvm::APFloat::opInvalidOp; + + // Helper constants for constructing specific DD values. + constexpr double Infinity = std::numeric_limits<double>::infinity(); + constexpr double NaN = std::numeric_limits<double>::quiet_NaN(); + constexpr double DMAX = std::numeric_limits<double>::max(); + + // Powers of 2 + constexpr double P53 = 0x1p53; + constexpr double P63 = 0x1p63; + constexpr double P64 = 0x1p64; + // 2^-100 (A very small delta demonstrating extended precision) + constexpr double PM100 = 0x1p-100; + + static constexpr auto ConvertToIntegerTestCases = std::array{ + // 1. Zeros, NaNs, and Infinities (Target: 64-bit Signed) + // INT64_MAX = 9223372036854775807 + // INT64_MIN = -9223372036854775808 + + // Input: Positive Zero (0.0, 0.0) + TestCase{{0.0, 0.0}, 64, true}.withAll("0", OK), + + // Input: Negative Zero (-0.0, 0.0) + TestCase{{-0.0, 0.0}, 64, true}.withAll("0", OK), + + // Input: NaN. Expected behavior: Invalid, deterministic result of 0. + TestCase{{NaN, 0.0}, 64, true}.withAll("0", Invalid), + + // Input: +Infinity. Expected behavior: Invalid, deterministic result of + // INT64_MAX. + TestCase{{Infinity, 0.0}, 64, true}.withAll("9223372036854775807", + Invalid), + + // Input: -Infinity. Expected behavior: Invalid, deterministic result of + // INT64_MIN. + TestCase{{-Infinity, 0.0}, 64, true}.withAll("-9223372036854775808", + Invalid), + + // 2. Basic Rounding and Tie-Breaking (Target: 32-bit Signed) + + // Input: 2.5 (Tie, preceding integer is Even) + TestCase{{2.5, 0.0}, 32, true} + .with(RTZ, "2", Inexact) + .with(RDN, "2", Inexact) + .with(RUP, "3", Inexact) + .with(RNA, "3", Inexact) + .with(RNE, "2", Inexact), + + // Input: 3.5 (Tie, preceding integer is Odd) + TestCase{{3.5, 0.0}, 32, true} + .with(RTZ, "3", Inexact) + .with(RDN, "3", Inexact) + .with(RUP, "4", Inexact) + .with(RNA, "4", Inexact) + .with(RNE, "4", Inexact), + + // Input: -2.5 (Tie, preceding integer is Even) + TestCase{{-2.5, 0.0}, 32, true} + .with(RTZ, "-2", Inexact) + .with(RDN, "-3", Inexact) + .with(RUP, "-2", Inexact) + .with(RNA, "-3", Inexact) + .with(RNE, "-2", Inexact), + + // 3. Double-Double Precision (The role of 'lo') + // Testing how extended precision affects rounding decisions. + + // Input: 2.5 + Epsilon (Slightly above tie) + TestCase{{2.5, PM100}, 32, true} + .with(RTZ, "2", Inexact) + .with(RDN, "2", Inexact) + .with(RUP, "3", Inexact) + .with(RNA, "3", Inexact) + .with(RNE, "3", Inexact), + + // Input: 2.5 - Epsilon (Slightly below tie) + TestCase{{2.5, -PM100}, 32, true} + .with(RTZ, "2", Inexact) + .with(RDN, "2", Inexact) + .with(RUP, "3", Inexact) + .with(RNA, "2", Inexact) + .with(RNE, "2", Inexact), + + // Input: 1.0 + Epsilon (Just above 1.0, e.g., 1.00...1) + TestCase{{1.0, PM100}, 32, true} + .with(RTZ, "1", Inexact) + .with(RDN, "1", Inexact) + .with(RUP, "2", Inexact) + .with(RNA, "1", Inexact) + .with(RNE, "1", Inexact), + + // Input: 1.0 - Epsilon (Just below 1.0, e.g. 0.999...) + TestCase{{1.0, -PM100}, 32, true} + .with(RTZ, "0", Inexact) + .with(RDN, "0", Inexact) + .with(RUP, "1", Inexact) + .with(RNA, "1", Inexact) + .with(RNE, "1", Inexact), + + // Input: Large number tie-breaking (Crucial test for DD implementation) + // Input: 2^53 + 1.5. + // A standard double(2^53 + 1.5) rounds to 2^53 + 2.0. + // The DD representation must precisely hold 2^53 + 1.5. + // The canonical DD representation is {2^53 + 2.0, -0.5}. + // Value is 9007199254740993.5 + TestCase{{P53 + 2.0, -0.5}, 64, true} + .with(RTZ, "9007199254740993", Inexact) + .with(RDN, "9007199254740993", Inexact) + .with(RUP, "9007199254740994", Inexact) + .with(RNA, "9007199254740994", Inexact) + .with(RNE, "9007199254740994", Inexact), + + // 4. Overflow Boundaries (Signed) + + // Input: Exactly INT64_MAX. (2^63 - 1) + // Represented precisely as (2^63, -1.0) + TestCase{{P63, -1.0}, 64, true}.withAll("9223372036854775807", OK), + + // Input: INT64_MAX + 0.3. + // Represented as (2^63, -0.7) + TestCase{{P63, -0.7}, 64, true} + .with(RTZ, "9223372036854775807", Inexact) + .with(RDN, "9223372036854775807", Inexact) + .with(RNA, "9223372036854775807", Inexact) + .with(RNE, "9223372036854775807", Inexact) + .with(RUP, "9223372036854775807", Invalid), + + // Input: INT64_MAX + 0.5 (Tie at the boundary) + // Represented as (2^63, -0.5). Target integers are MAX (odd) and 2^63 + // (even). + TestCase{{P63, -0.5}, 64, true} + .with(RTZ, "9223372036854775807", Inexact) + .with(RDN, "9223372036854775807", Inexact) + .with(RUP, "9223372036854775807", Invalid) + .with(RNA, "9223372036854775807", Invalid) + .with(RNE, "9223372036854775807", Invalid), + + // Input: 2^55 - 2^1 - 2^-52 to signed integer. + // Represented as (2^55 - 2^2, 2^1 - 2^-1). + TestCase{{0x1.fffffffffffffp+54, 0x1.8p0}, 56, true} + .with(RTZ, "36028797018963965", Inexact) + .with(RDN, "36028797018963965", Inexact) + .with(RUP, "36028797018963966", Inexact) + .with(RNA, "36028797018963966", Inexact) + .with(RNE, "36028797018963966", Inexact), + + // Input: 2^55 - 2^1 - 2^-52 to signed integer. + // Represented as (2^55 - 2^2, 2^1 - 2^-52). + TestCase{{0x1.fffffffffffffp+54, 0x1.fffffffffffffp0}, 56, true} + .with(RTZ, "36028797018963965", Inexact) + .with(RDN, "36028797018963965", Inexact) + .with(RUP, "36028797018963966", Inexact) + .with(RNA, "36028797018963966", Inexact) + .with(RNE, "36028797018963966", Inexact), + + // Input: Exactly 2^63 (One past INT64_MAX) + TestCase{{P63, 0.0}, 64, true}.withAll("9223372036854775807", Invalid), + + // Input: Exactly INT64_MIN (-2^63) + TestCase{{-P63, 0.0}, 64, true}.withAll("-9223372036854775808", OK), + + // Input: INT64_MIN - 0.5 (Tie at the lower boundary) + // Target integers are -2^63-1 (odd) and MIN (even). + TestCase{{-P63, -0.5}, 64, true} + .with(RTZ, "-9223372036854775808", Inexact) + .with(RUP, "-9223372036854775808", Inexact) + // RDN rounds down, causing overflow. + .with(RDN, "-9223372036854775808", Invalid) + // RNA rounds away (down), causing overflow. + .with(RNA, "-9223372036854775808", Invalid) + // RNE rounds to even (up to -2^63), which is OK. + .with(RNE, "-9223372036854775808", Inexact), + + // 5. Overflow Boundaries (Unsigned) + // UINT64_MAX = 18446744073709551615 (2^64 - 1) + + // Input: Exactly UINT64_MAX. (2^64 - 1) + // Represented precisely as (2^64, -1.0) + TestCase{{P64, -1.0}, 64, false}.withAll("18446744073709551615", OK), + + // Input: UINT64_MAX + 0.5 (Tie at the boundary) + // Represented as (2^64, -0.5) + TestCase{{P64, -0.5}, 64, false} + .with(RTZ, "18446744073709551615", Inexact) + .with(RDN, "18446744073709551615", Inexact) + // RUP rounds up (2^64), causing overflow. + .with(RUP, "18446744073709551615", Invalid) + // RNA rounds away (up), causing overflow. + .with(RNA, "18446744073709551615", Invalid) + // RNE rounds to even (up to 2^64), causing overflow. + .with(RNE, "18446744073709551615", Invalid), + + // Input: 2^55 - 2^1 - 2^-52 to unsigned integer. + // Represented as (2^55 - 2^2, 2^1 - 2^-1). + TestCase{{0x1.fffffffffffffp+54, 0x1.8p0}, 55, false} + .with(RTZ, "36028797018963965", Inexact) + .with(RDN, "36028797018963965", Inexact) + .with(RUP, "36028797018963966", Inexact) + .with(RNA, "36028797018963966", Inexact) + .with(RNE, "36028797018963966", Inexact), + + // Input: 2^55 - 2^1 - 2^-52 to unsigned integer. + // Represented as (2^55 - 2^2, 2^1 - 2^-52). + TestCase{{0x1.fffffffffffffp+54, 0x1.fffffffffffffp0}, 55, false} + .with(RTZ, "36028797018963965", Inexact) + .with(RDN, "36028797018963965", Inexact) + .with(RUP, "36028797018963966", Inexact) + .with(RNA, "36028797018963966", Inexact) + .with(RNE, "36028797018963966", Inexact), + + // Input: -0.3 (Slightly below zero) + TestCase{{-0.3, 0.0}, 64, false} + .with(RTZ, "0", Inexact) + .with(RUP, "0", Inexact) + .with(RNA, "0", Inexact) + .with(RNE, "0", Inexact) + .with(RDN, "0", Invalid), + + // Input: -0.5 (Tie at zero) + TestCase{{-0.5, 0.0}, 64, false} + .with(RTZ, "0", Inexact) + .with(RUP, "0", Inexact) + // RNE rounds to even (0). + .with(RNE, "0", Inexact) + .with(RDN, "0", Invalid) + // RNA rounds away (-1), causing overflow. + .with(RNA, "0", Invalid), + + // Input: -1.0 (Negative integer) + TestCase{{-1.0, 0.0}, 64, false}.withAll("0", Invalid), + + // 6. High Precision Integers (Target: 128-bit Signed) + // INT128_MAX = 170141183460469231731687303715884105727 + + // Input: 2^100 (Exactly representable in DD) + // 2^100 = 1267650600228229401496703205376.0 + TestCase{{1267650600228229401496703205376.0, 0.0}, 128, true}.withAll( + "1267650600228229401496703205376", OK), + + // Input: DMAX. (Approx 1.8e308). + // This is vastly larger than INT128_MAX (Approx 1.7e38). + TestCase{{DMAX, 0.0}, 128, true}.withAll( + "170141183460469231731687303715884105727", Invalid), + + // Input: Largest semPPCDoubleDoubleLegacy + TestCase{{DMAX, 0x1.ffffffffffffep+969}, 128, true}.withAll( + "170141183460469231731687303715884105727", Invalid), + + // 7. Round to negative -0 + TestCase{{-PM100, 0.0}, 32, true} + .with(RTZ, "0", Inexact) + .with(RUP, "0", Inexact) + .with(RNA, "0", Inexact) + .with(RNE, "0", Inexact) + .with(RDN, "-1", Inexact), + }; + return ConvertToIntegerTestCases; +} +} // namespace PPCDoubleDoubleConvertToIntegerTestDetails + +class PPCDoubleDoubleConvertToIntegerValueTest + : public testing::Test, + public ::testing::WithParamInterface< + PPCDoubleDoubleConvertToIntegerTestDetails::TestCase> {}; + +INSTANTIATE_TEST_SUITE_P( + PPCDoubleDoubleConvertToIntegerValueParamTests, + PPCDoubleDoubleConvertToIntegerValueTest, + ::testing::ValuesIn( + PPCDoubleDoubleConvertToIntegerTestDetails::testCases())); + +TEST_P(PPCDoubleDoubleConvertToIntegerValueTest, + PPCDoubleDoubleConvertToInteger) { + const PPCDoubleDoubleConvertToIntegerTestDetails::TestCase Params = + GetParam(); + const APFloat Input = makeDoubleAPFloat(Params.Input); + EXPECT_FALSE(Input.isDenormal()) + << Params.Input.Hi << " + " << Params.Input.Lo; + + for (size_t I = 0, E = std::size(Params.Rounded); I != E; ++I) { + const auto RM = static_cast<APFloat::roundingMode>(I); + const auto &Expected = Params.Rounded[I]; + APSInt ActualInteger(Params.IntegerWidth, /*isUnsigned=*/!Params.IsSigned); + + APSInt ExpectedInteger{Expected.ExpectedIntStr}; + EXPECT_LE(ExpectedInteger.getBitWidth(), Params.IntegerWidth); + ExpectedInteger = ExpectedInteger.extend(Params.IntegerWidth); + if (ExpectedInteger.isUnsigned() && Params.IsSigned) { + ExpectedInteger.setIsSigned(Params.IsSigned); + EXPECT_FALSE(ExpectedInteger.isNegative()); + } + + const bool NegativeUnderflow = + ExpectedInteger.isZero() && Input.isNegative(); + const bool ExpectedIsExact = + Expected.Status == APFloat::opOK && !NegativeUnderflow; + bool ActualIsExact; + const auto ActualStatus = + Input.convertToInteger(ActualInteger, RM, &ActualIsExact); + EXPECT_EQ(ActualStatus, Expected.Status); + EXPECT_EQ(ActualIsExact, ExpectedIsExact); + EXPECT_EQ(ActualInteger, ExpectedInteger); } } @@ -5372,6 +6291,102 @@ TEST(APFloatTest, PPCDoubleDoubleCompare) { } } +namespace PPCDoubleDoubleCompareAbsoluteValueTestDetails { +struct TestCase { + DD LHS; + DD RHS; + APFloat::cmpResult Result; +}; + +auto testCases() { + static constexpr auto CompareAbsoluteValueTestCases = std::array{ + TestCase{ + {1.0, 0.0}, + {1.0, 0.0}, + APFloat::cmpEqual, + }, + TestCase{ + {1.0, -0.0}, + {1.0, +0.0}, + APFloat::cmpEqual, + }, + TestCase{ + {1.0, 0.0}, + {0x1.0000000000001p+0, 0.0}, + APFloat::cmpLessThan, + }, + TestCase{ + {0x1.0000000000001p+0, 0.0}, + {1.0, 0.0}, + APFloat::cmpGreaterThan, + }, + TestCase{ + {0x1.0000000000001p+0, +0x1p-1074}, + {1.0, -0x1p-1074}, + APFloat::cmpGreaterThan, + }, + TestCase{ + {0x1.0000000000001p+0, -0x1p-1074}, + {1.0, +0x1p-1074}, + APFloat::cmpGreaterThan, + }, + TestCase{ + {1.0, 0.0}, + {1.0, -0x1p-1074}, + APFloat::cmpGreaterThan, + }, + TestCase{ + {1.0, 0.0}, + {1.0, +0x1p-1074}, + APFloat::cmpLessThan, + }, + TestCase{ + {1.0, +0x1p-1073}, + {1.0, -0x1p-1074}, + APFloat::cmpGreaterThan, + }, + TestCase{ + {1.0, +0x1p-1074}, + {1.0, -0x1p-1074}, + APFloat::cmpGreaterThan, + }, + }; + return CompareAbsoluteValueTestCases; +} +} // namespace PPCDoubleDoubleCompareAbsoluteValueTestDetails + +class PPCDoubleDoubleCompareAbsoluteValueValueTest + : public testing::Test, + public ::testing::WithParamInterface< + PPCDoubleDoubleCompareAbsoluteValueTestDetails::TestCase> {}; + +INSTANTIATE_TEST_SUITE_P( + PPCDoubleDoubleCompareAbsoluteValueValueParamTests, + PPCDoubleDoubleCompareAbsoluteValueValueTest, + ::testing::ValuesIn( + PPCDoubleDoubleCompareAbsoluteValueTestDetails::testCases())); + +TEST_P(PPCDoubleDoubleCompareAbsoluteValueValueTest, + PPCDoubleDoubleCompareAbsoluteValue) { + auto Param = GetParam(); + for (bool LHSNegate : {false, true}) { + auto LHS = llvm::detail::DoubleAPFloat{APFloat::PPCDoubleDouble(), + APFloat{Param.LHS.Hi}, + APFloat{Param.LHS.Lo}}; + if (LHSNegate) + LHS.changeSign(); + for (bool RHSNegate : {false, true}) { + auto RHS = llvm::detail::DoubleAPFloat{APFloat::PPCDoubleDouble(), + APFloat{Param.RHS.Hi}, + APFloat{Param.RHS.Lo}}; + if (RHSNegate) + RHS.changeSign(); + + EXPECT_EQ(LHS.compareAbsoluteValue(RHS), Param.Result); + } + } +} + TEST(APFloatTest, PPCDoubleDoubleBitwiseIsEqual) { using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, bool>; @@ -5525,19 +6540,328 @@ TEST(APFloatTest, PPCDoubleDoubleScalbn) { EXPECT_EQ(0x3cc8000000000000ull, Result.bitcastToAPInt().getRawData()[1]); } -TEST(APFloatTest, PPCDoubleDoubleFrexp) { - // 3.0 + 3.0 << 53 - uint64_t Input[] = { - 0x4008000000000000ull, 0x3cb8000000000000ull, +namespace PPCDoubleDoubleFrexpTestDetails { +// Define the rounding modes for easier readability. +static constexpr auto RNE = APFloat::rmNearestTiesToEven; +static constexpr auto RNA = APFloat::rmNearestTiesToAway; +static constexpr auto RTZ = APFloat::rmTowardZero; +static constexpr auto RUP = APFloat::rmTowardPositive; +static constexpr auto RDN = APFloat::rmTowardNegative; + +struct TestCase { + // Structure to hold the expected result of a conversion + struct ExpectedFractionExponent { + DD Fraction; + int Exponent; + friend APFloat::cmpResult compare(const ExpectedFractionExponent &Lhs, + const ExpectedFractionExponent &Rhs) { + const APFloat LhsFraction = makeDoubleAPFloat(Lhs.Fraction); + const APFloat RhsFraction = makeDoubleAPFloat(Rhs.Fraction); + const APFloat::cmpResult FractionRelation = + LhsFraction.compare(RhsFraction); + if (FractionRelation == APFloat::cmpUnordered) + return APFloat::cmpUnordered; + if (LhsFraction.isZero() && RhsFraction.isZero()) + return APFloat::cmpEqual; + if (!LhsFraction.isNegative() && + (RhsFraction.isNegative() || RhsFraction.isZero())) + return APFloat::cmpGreaterThan; + if (!RhsFraction.isNegative() && + (LhsFraction.isNegative() || LhsFraction.isZero())) + return APFloat::cmpLessThan; + if (Lhs.Exponent > Rhs.Exponent) + return LhsFraction.isNegative() ? APFloat::cmpLessThan + : APFloat::cmpGreaterThan; + if (Lhs.Exponent < Rhs.Exponent) + return RhsFraction.isNegative() ? APFloat::cmpGreaterThan + : APFloat::cmpLessThan; + return FractionRelation; + } }; - int Exp; - // 0.75 + 0.75 << 53 - APFloat Result = - frexp(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Input)), Exp, - APFloat::rmNearestTiesToEven); - EXPECT_EQ(2, Exp); - EXPECT_EQ(0x3fe8000000000000ull, Result.bitcastToAPInt().getRawData()[0]); - EXPECT_EQ(0x3c98000000000000ull, Result.bitcastToAPInt().getRawData()[1]); + + DD Input; + // Array indexed by the rounding mode enum value. + std::array<ExpectedFractionExponent, 5> Rounded = {}; + + // Helper to define the expected results for a specific rounding mode. + constexpr TestCase &with(APFloat::roundingMode RM, DD ExpectedDD, + int ExpectedExponent) { + Rounded[static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM)] = { + ExpectedDD, + ExpectedExponent, + }; + return *this; + } + + // Helper to define the same result for all rounding modes. + constexpr TestCase &withAll(DD ExpectedDD, int ExpectedExponent) { + return with(RNE, ExpectedDD, ExpectedExponent) + .with(RNA, ExpectedDD, ExpectedExponent) + .with(RTZ, ExpectedDD, ExpectedExponent) + .with(RUP, ExpectedDD, ExpectedExponent) + .with(RDN, ExpectedDD, ExpectedExponent); + } +}; + +auto testCases() { + static constexpr auto FrexpTestCases = std::array{ + // Input: +infinity + TestCase{{std::numeric_limits<double>::infinity(), 0.0}}.withAll( + {std::numeric_limits<double>::infinity(), 0.0}, INT_MAX), + + // Input: -infinity + TestCase{{-std::numeric_limits<double>::infinity(), 0.0}}.withAll( + {-std::numeric_limits<double>::infinity(), 0.0}, INT_MAX), + + // Input: NaN + TestCase{{std::numeric_limits<double>::quiet_NaN(), 0.0}}.withAll( + {std::numeric_limits<double>::quiet_NaN(), 0.0}, INT_MIN), + + // Input: 2^-1074 + TestCase{{0x1p-1074, 0.0}}.withAll({0x1p-1, 0.0}, -1073), + TestCase{{-0x1p-1074, 0.0}}.withAll({-0x1p-1, 0.0}, -1073), + + // Input: (2^1, -2^-1073 + -2^-1074) + TestCase{{0x1p1, -0x1.8p-1073}} + .withAll({0x1p0, -0x1p-1073}, 1) + .with(RNA, {0x1p0, -0x1p-1074}, 1) + .with(RUP, {0x1p0, -0x1p-1074}, 1), + TestCase{{-0x1p1, 0x1.8p-1073}} + .withAll({-0x1p0, 0x1p-1073}, 1) + .with(RNA, {-0x1p0, 0x1p-1074}, 1) + .with(RDN, {-0x1p0, 0x1p-1074}, 1), + + // Input: (2^1, -2^-1073) + TestCase{{0x1p1, -0x1p-1073}}.withAll({0x1p0, -0x1p-1074}, 1), + + // Input: (2^1, -2^-1074) + TestCase{{0x1p1, -0x1p-1074}} + .withAll({0x1p-1, -0.0}, 2) + .with(RDN, {0x1p0, -0x1p-1074}, 1) + .with(RTZ, {0x1p0, -0x1p-1074}, 1), + + // Input: (2^2, -2^-1072 + -2^-1073 + -2^-1074) + TestCase{{0x1p2, -0x1.cp-1072}} + .withAll({0x1p0, -0x1p-1073}, 2) + .with(RUP, {0x1p0, -0x1p-1074}, 2), + + // Input: (2^2, -2^-1072 + -2^-1073) + TestCase{{0x1p2, -0x1.8p-1072}} + .withAll({0x1p0, -0x1p-1073}, 2) + .with(RNA, {0x1p0, -0x1p-1074}, 2) + .with(RUP, {0x1p0, -0x1p-1074}, 2), + TestCase{{-0x1p2, 0x1.8p-1072}} + .withAll({-0x1p0, 0x1p-1073}, 2) + .with(RNA, {-0x1p0, 0x1p-1074}, 2) + .with(RDN, {-0x1p0, 0x1p-1074}, 2), + + // Input: (2^2, -2^-1072 + -2^-1074) + TestCase{{0x1p2, -0x1.4cp-1072}} + .withAll({0x1p0, -0x1p-1074}, 2) + .with(RDN, {0x1p0, -0x1p-1073}, 2) + .with(RTZ, {0x1p0, -0x1p-1073}, 2), + + // Input: (2^2, -2^-1072) + TestCase{{0x1p2, -0x1p-1072}}.withAll({0x1p0, -0x1p-1074}, 2), + + // Input: (2^2, -2^-1073 + -2^-1074) + TestCase{{0x1p2, -0x1.8p-1073}} + .withAll({0x1p0, -0x1p-1074}, 2) + .with(RUP, {0x1p-1, -0.0}, 3), + + // Input: (2^2, -2^-1073) + TestCase{{0x1p2, -0x1p-1073}} + .withAll({0x1p-1, -0.0}, 3) + .with(RDN, {0x1p0, -0x1p-1074}, 2) + .with(RTZ, {0x1p0, -0x1p-1074}, 2), + + // Input: (2^2, -2^-1074) + TestCase{{0x1p2, -0x1p-1074}} + .withAll({0x1p-1, -0.0}, 3) + .with(RDN, {0x1p0, -0x1p-1074}, 2) + .with(RTZ, {0x1p0, -0x1p-1074}, 2), + + // Input: 3+3*2^-53 canonicalized to (3+2^-51, -2^-53) + // Output: 0.75+0.75*2^-53 canonicalized to (.75+2^-53, -2^-55) + TestCase{{0x1.8000000000001p1, -0x1p-53}}.withAll( + {0x1.8000000000001p-1, -0x1p-55}, 2), + TestCase{{-0x1.8000000000001p1, 0x1p-53}}.withAll( + {-0x1.8000000000001p-1, 0x1p-55}, 2), + + // Input: (2^1021+2^969, 2^968-2^915) + TestCase{{0x1.0000000000001p1021, 0x1.fffffffffffffp967}}.withAll( + {0x1.0000000000001p-1, 0x1.fffffffffffffp-55}, 1022), + TestCase{{-0x1.0000000000001p1021, -0x1.fffffffffffffp967}}.withAll( + {-0x1.0000000000001p-1, -0x1.fffffffffffffp-55}, 1022), + + // Input: (2^1023, -2^-1) + TestCase{{0x1p+1023, -0x1p-1}}.withAll({0x1p0, -0x1p-1024}, 1023), + TestCase{{-0x1p+1023, 0x1p-1}}.withAll({-0x1p0, 0x1p-1024}, 1023), + + // Input: (2^1023, -2^-51) + TestCase{{0x1p+1023, -0x1p-51}}.withAll({0x1p0, -0x1p-1074}, 1023), + TestCase{{-0x1p+1023, 0x1p-51}}.withAll({-0x1p0, 0x1p-1074}, 1023), + + // Input: (2^1023, -2^-52) + TestCase{{0x1p+1023, -0x1p-52}} + .withAll({0x1p-1, -0x0p0}, 1024) + .with(RDN, {0x1p0, -0x1p-1074}, 1023) + .with(RTZ, {0x1p0, -0x1p-1074}, 1023), + TestCase{{-0x1p+1023, 0x1p-52}} + .withAll({-0x1p-1, 0x0p0}, 1024) + .with(RUP, {-0x1p0, 0x1p-1074}, 1023) + .with(RTZ, {-0x1p0, 0x1p-1074}, 1023), + + // Input: (2^1023, 2^-1074) + TestCase{{0x1p+1023, 0x1p-1074}} + .withAll({0x1p-1, 0x0p+0}, 1024) + .with(RUP, {0x1p-1, 0x1p-1074}, 1024), + TestCase{{-0x1p+1023, -0x1p-1074}} + .withAll({-0x1p-1, -0x0p+0}, 1024) + .with(RDN, {-0x1p-1, -0x1p-1074}, 1024), + + // Input: (2^1024-2^971, 2^970-2^918) + TestCase{{0x1.fffffffffffffp+1023, 0x1.ffffffffffffep+969}}.withAll( + {0x1.fffffffffffffp-1, 0x1.ffffffffffffep-55}, 1024), + TestCase{{-0x1.fffffffffffffp+1023, -0x1.ffffffffffffep+969}}.withAll( + {-0x1.fffffffffffffp-1, -0x1.ffffffffffffep-55}, 1024), + }; + return FrexpTestCases; +} +} // namespace PPCDoubleDoubleFrexpTestDetails + +class PPCDoubleDoubleFrexpValueTest + : public testing::Test, + public ::testing::WithParamInterface< + PPCDoubleDoubleFrexpTestDetails::TestCase> {}; + +INSTANTIATE_TEST_SUITE_P( + PPCDoubleDoubleFrexpValueParamTests, PPCDoubleDoubleFrexpValueTest, + ::testing::ValuesIn(PPCDoubleDoubleFrexpTestDetails::testCases())); + +TEST_P(PPCDoubleDoubleFrexpValueTest, PPCDoubleDoubleFrexp) { + const PPCDoubleDoubleFrexpTestDetails::TestCase Params = GetParam(); + const APFloat Input = makeDoubleAPFloat(Params.Input); + auto RmToIdx = [](APFloat::roundingMode RM) { + return static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM); + }; + // First, make sure our expected results are consistent with each other before + // bothering to test the implementation. + if (Input.isFinite()) { + // Make sure the input is canonical. + EXPECT_EQ(APFloat{Params.Input.Hi}, + APFloat{Params.Input.Hi} + APFloat{Params.Input.Lo}) + << Params.Input.Hi << " + " << Params.Input.Lo; + + const auto Dn = Params.Rounded[RmToIdx(APFloat::rmTowardNegative)]; + const auto Up = Params.Rounded[RmToIdx(APFloat::rmTowardPositive)]; + const auto Tz = Params.Rounded[RmToIdx(APFloat::rmTowardZero)]; + const auto Ne = Params.Rounded[RmToIdx(APFloat::rmNearestTiesToEven)]; + const auto Na = Params.Rounded[RmToIdx(APFloat::rmNearestTiesToAway)]; + + // The rdn result must be no larger than the rup result. + const APFloat::cmpResult DnVsUp = compare(Dn, Up); + EXPECT_TRUE(DnVsUp == APFloat::cmpLessThan || DnVsUp == APFloat::cmpEqual); + + for (size_t I = 0, E = std::size(Params.Rounded); I != E; ++I) { + const APFloat RoundedFraction = + makeDoubleAPFloat(Params.Rounded[I].Fraction); + // All possible results should be bracketed by [Dn, Up]. + const APFloat::cmpResult VsDn = compare(Params.Rounded[I], Dn); + EXPECT_TRUE(VsDn == APFloat::cmpGreaterThan || VsDn == APFloat::cmpEqual); + const APFloat::cmpResult VsUp = compare(Params.Rounded[I], Up); + EXPECT_TRUE(VsUp == APFloat::cmpLessThan || VsUp == APFloat::cmpEqual); + // A rounding result is either equal to the rup or rdn result. + EXPECT_TRUE(VsUp == APFloat::cmpEqual || VsDn == APFloat::cmpEqual); + // frexp returns a result whose magnitude is in in [.5, 1) so its exponent + // should be -1. + if (!RoundedFraction.isZero()) + EXPECT_EQ(ilogb(RoundedFraction), -1) + << static_cast<APFloat::roundingMode>(I); + // Decomposition preserves sign. + EXPECT_EQ(RoundedFraction.isNegative(), Input.isNegative()); + // A rounding result must be canonical. + EXPECT_EQ(APFloat{Params.Rounded[I].Fraction.Hi}, + APFloat{Params.Rounded[I].Fraction.Hi} + + APFloat{Params.Rounded[I].Fraction.Lo}) + << Params.Rounded[I].Fraction.Hi << " + " + << Params.Rounded[I].Fraction.Lo; + } + + // The rtz result must be either rup or rdn depending on the sign. + if (Input.isNegative()) { + const APFloat::cmpResult TzVsUp = compare(Tz, Up); + EXPECT_EQ(TzVsUp, APFloat::cmpEqual); + } else { + const APFloat::cmpResult TzVsDn = compare(Tz, Dn); + EXPECT_EQ(TzVsDn, APFloat::cmpEqual); + } + + // The recomposed up should be at least as big as the input. + const APFloat RecomposedUp = + scalbn(makeDoubleAPFloat(Up.Fraction), Up.Exponent, + APFloat::rmNearestTiesToEven); + EXPECT_TRUE(RecomposedUp >= Input); + // The recomposed down can't be larger than the input. + const APFloat RecomposedDn = + scalbn(makeDoubleAPFloat(Dn.Fraction), Dn.Exponent, + APFloat::rmNearestTiesToEven); + EXPECT_TRUE(RecomposedDn <= Input); + // The recomposed tz must have a smaller magnitude. + const APFloat RecomposedTz = + scalbn(makeDoubleAPFloat(Tz.Fraction), Tz.Exponent, + APFloat::rmNearestTiesToEven); + EXPECT_TRUE(abs(RecomposedTz) <= abs(Input)); + // Either both or neither of the recomposed round-to-nearest results are + // equal to the input. + const APFloat RecomposedNe = + scalbn(makeDoubleAPFloat(Ne.Fraction), Ne.Exponent, + APFloat::rmNearestTiesToEven); + const APFloat RecomposedNa = + scalbn(makeDoubleAPFloat(Na.Fraction), Na.Exponent, + APFloat::rmNearestTiesToEven); + EXPECT_EQ(RecomposedNe == Input, RecomposedNa == Input); + // Either the ne result equals the na result or the na result has a bigger + // magnitude. + const APFloat::cmpResult NeVsNa = + abs(RecomposedNe).compare(abs(RecomposedNa)); + EXPECT_TRUE(NeVsNa == APFloat::cmpLessThan || NeVsNa == APFloat::cmpEqual); + // ne and na may only disagree if they broke a tie differently. + if (NeVsNa == APFloat::cmpLessThan) { + // ne's magnitude should be lower than input. + const APFloat::cmpResult NeVsInput = + abs(RecomposedNe).compare(abs(Input)); + EXPECT_EQ(NeVsInput, APFloat::cmpLessThan); + // na's magnitude should be greater than input. + const APFloat::cmpResult NaVsInput = + abs(RecomposedNa).compare(abs(Input)); + EXPECT_EQ(NaVsInput, APFloat::cmpGreaterThan); + } + // If up or down perfectly reconstructs the input, the round-to-nearest + // results should too. + if (RecomposedUp == Input || RecomposedDn == Input) { + EXPECT_EQ(RecomposedNe, Input); + EXPECT_EQ(RecomposedNa, Input); + } + } + + for (size_t I = 0, E = std::size(Params.Rounded); I != E; ++I) { + const auto RM = static_cast<APFloat::roundingMode>(I); + const auto &Expected = Params.Rounded[I]; + const APFloat ExpectedFraction = makeDoubleAPFloat(Expected.Fraction); + + int ActualExponent; + const APFloat ActualFraction = frexp(Input, ActualExponent, RM); + if (ExpectedFraction.isNaN()) + EXPECT_TRUE(ActualFraction.isNaN()); + else + EXPECT_EQ(ActualFraction.compare(ExpectedFraction), APFloat::cmpEqual) + << ActualFraction << " vs " << ExpectedFraction << " for input " + << Params.Input.Hi << " + " << Params.Input.Lo << " RM " << RM; + EXPECT_EQ(ActualExponent, Expected.Exponent) + << "for input " << Params.Input.Hi << " + " << Params.Input.Lo + << " RM " << RM; + } } TEST(APFloatTest, PPCDoubleDoubleNext) { @@ -5551,13 +6875,9 @@ TEST(APFloatTest, PPCDoubleDoubleNext) { return X; }; - auto Zero = [] { - return APFloat::getZero(APFloat::IEEEdouble()); - }; + auto Zero = [] { return APFloat::getZero(APFloat::IEEEdouble()); }; - auto One = [] { - return APFloat::getOne(APFloat::IEEEdouble()); - }; + auto One = [] { return APFloat::getOne(APFloat::IEEEdouble()); }; // 0x1p-1074 auto MinSubnormal = [] { @@ -5574,24 +6894,6 @@ TEST(APFloatTest, PPCDoubleDoubleNext) { // 2^-53 auto EpsNeg = [&] { return scalbn(Eps(), -1, APFloat::rmNearestTiesToEven); }; - auto MakeDoubleAPFloat = [](auto Hi, auto Lo) { - APFloat HiFloat{APFloat::IEEEdouble(), APFloat::uninitialized}; - if constexpr (std::is_same_v<decltype(Hi), APFloat>) { - HiFloat = Hi; - } else { - HiFloat = {APFloat::IEEEdouble(), Hi}; - } - - APFloat LoFloat{APFloat::IEEEdouble(), APFloat::uninitialized}; - if constexpr (std::is_same_v<decltype(Lo), APFloat>) { - LoFloat = Lo; - } else { - LoFloat = {APFloat::IEEEdouble(), Lo}; - } - - APInt Bits = LoFloat.bitcastToAPInt().concat(HiFloat.bitcastToAPInt()); - return APFloat(APFloat::PPCDoubleDouble(), Bits); - }; APFloat Test(APFloat::PPCDoubleDouble(), APFloat::uninitialized); APFloat Expected(APFloat::PPCDoubleDouble(), APFloat::uninitialized); @@ -5719,55 +7021,55 @@ TEST(APFloatTest, PPCDoubleDoubleNext) { // 2b. |hi| >= 2*DBL_MIN_NORMAL (DD precision > D precision) // Test at hi = 1.0, lo = 0. - Test = MakeDoubleAPFloat(One(), Zero()); - Expected = MakeDoubleAPFloat(One(), MinSubnormal()); + Test = makeDoubleAPFloat(One(), Zero()); + Expected = makeDoubleAPFloat(One(), MinSubnormal()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); // Test at hi = -1.0. delta = 2^-1074 (positive, moving towards +Inf). - Test = MakeDoubleAPFloat(-One(), Zero()); - Expected = MakeDoubleAPFloat(-One(), MinSubnormal()); + Test = makeDoubleAPFloat(-One(), Zero()); + Expected = makeDoubleAPFloat(-One(), MinSubnormal()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); // Testing the boundary where calculated delta equals DBL_TRUE_MIN. // Requires ilogb(hi) = E = -968. // delta = 2^(-968 - 106) = 2^-1074 = DBL_TRUE_MIN. - Test = MakeDoubleAPFloat("0x1p-968", Zero()); - Expected = MakeDoubleAPFloat("0x1p-968", MinSubnormal()); + Test = makeDoubleAPFloat("0x1p-968", Zero()); + Expected = makeDoubleAPFloat("0x1p-968", MinSubnormal()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); // Testing below the boundary (E < -968). Delta clamps to DBL_TRUE_MIN. - Test = MakeDoubleAPFloat("0x1p-969", Zero()); - Expected = MakeDoubleAPFloat("0x1p-969", MinSubnormal()); + Test = makeDoubleAPFloat("0x1p-969", Zero()); + Expected = makeDoubleAPFloat("0x1p-969", MinSubnormal()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); // 3. Standard Increment (No rollover) // hi=1.0, lo=2^-1074. - Test = MakeDoubleAPFloat(One(), MinSubnormal()); - Expected = MakeDoubleAPFloat(One(), NextUp(MinSubnormal())); + Test = makeDoubleAPFloat(One(), MinSubnormal()); + Expected = makeDoubleAPFloat(One(), NextUp(MinSubnormal())); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); // Incrementing negative lo. - Test = MakeDoubleAPFloat(One(), -MinSubnormal()); - Expected = MakeDoubleAPFloat(One(), Zero()); + Test = makeDoubleAPFloat(One(), -MinSubnormal()); + Expected = makeDoubleAPFloat(One(), Zero()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_EQ(Test.compare(Expected), APFloat::cmpEqual); // Crossing lo=0. - Test = MakeDoubleAPFloat(One(), -MinSubnormal()); - Expected = MakeDoubleAPFloat(One(), Zero()); + Test = makeDoubleAPFloat(One(), -MinSubnormal()); + Expected = makeDoubleAPFloat(One(), Zero()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_EQ(Test.compare(Expected), APFloat::cmpEqual); // 4. Rollover Cases around 1.0 (Positive hi) // hi=1.0, lo=nextDown(2^-53). - Test = MakeDoubleAPFloat(One(), NextDown(EpsNeg())); + Test = makeDoubleAPFloat(One(), NextDown(EpsNeg())); EXPECT_FALSE(Test.isDenormal()); - Expected = MakeDoubleAPFloat(One(), EpsNeg()); + Expected = makeDoubleAPFloat(One(), EpsNeg()); EXPECT_FALSE(Test.isDenormal()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); @@ -5778,17 +7080,17 @@ TEST(APFloatTest, PPCDoubleDoubleNext) { // Can't naively TwoSum(0x1p+0, nextUp(0x1p-53)): // It gives {nextUp(0x1p+0), nextUp(nextUp(-0x1p-53))} but the next // number should be {nextUp(0x1p+0), nextUp(-0x1p-53)}. - Test = MakeDoubleAPFloat(One(), EpsNeg()); + Test = makeDoubleAPFloat(One(), EpsNeg()); EXPECT_FALSE(Test.isDenormal()); - Expected = MakeDoubleAPFloat(NextUp(One()), NextUp(-EpsNeg())); + Expected = makeDoubleAPFloat(NextUp(One()), NextUp(-EpsNeg())); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); EXPECT_FALSE(Test.isDenormal()); // hi = nextDown(1), lo = nextDown(0x1p-54) - Test = MakeDoubleAPFloat(NextDown(One()), NextDown(APFloat(0x1p-54))); + Test = makeDoubleAPFloat(NextDown(One()), NextDown(APFloat(0x1p-54))); EXPECT_FALSE(Test.isDenormal()); - Expected = MakeDoubleAPFloat(One(), APFloat(-0x1p-54)); + Expected = makeDoubleAPFloat(One(), APFloat(-0x1p-54)); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); EXPECT_FALSE(Test.isDenormal()); @@ -5796,31 +7098,274 @@ TEST(APFloatTest, PPCDoubleDoubleNext) { // 5. Negative Rollover (Moving towards Zero / +Inf) // hi = -1, lo = nextDown(0x1p-54) - Test = MakeDoubleAPFloat(APFloat(-1.0), NextDown(APFloat(0x1p-54))); + Test = makeDoubleAPFloat(APFloat(-1.0), NextDown(APFloat(0x1p-54))); EXPECT_FALSE(Test.isDenormal()); - Expected = MakeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54)); + Expected = makeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54)); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); EXPECT_FALSE(Test.isDenormal()); // hi = -1, lo = 0x1p-54 - Test = MakeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54)); + Test = makeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54)); EXPECT_FALSE(Test.isDenormal()); Expected = - MakeDoubleAPFloat(NextUp(APFloat(-1.0)), NextUp(APFloat(-0x1p-54))); + makeDoubleAPFloat(NextUp(APFloat(-1.0)), NextUp(APFloat(-0x1p-54))); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); EXPECT_FALSE(Test.isDenormal()); // 6. Rollover across Power of 2 boundary (Exponent change) - Test = MakeDoubleAPFloat(NextDown(APFloat(2.0)), NextDown(EpsNeg())); + Test = makeDoubleAPFloat(NextDown(APFloat(2.0)), NextDown(EpsNeg())); EXPECT_FALSE(Test.isDenormal()); - Expected = MakeDoubleAPFloat(APFloat(2.0), -EpsNeg()); + Expected = makeDoubleAPFloat(APFloat(2.0), -EpsNeg()); EXPECT_EQ(Test.next(false), APFloat::opOK); EXPECT_TRUE(Test.bitwiseIsEqual(Expected)); EXPECT_FALSE(Test.isDenormal()); } +TEST(APFloatTest, PPCDoubleDoubleConvertFromAPIntInexact) { + // Create an integer which would not be exactly representable in + // PPCDoubleDoubleLegacy. + for (bool IsSigned : {false, true}) { + const unsigned BitWidth = + APFloat::semanticsPrecision(APFloat::IEEEdouble()) * 3 + + (IsSigned ? 1 : 0); + + for (bool Negative : + IsSigned ? std::vector{false, true} : std::vector{false}) { + APInt Huge = APInt{BitWidth, 0}; + // Set the highest bit without making Huge negative.. + Huge.setBit(BitWidth - (IsSigned ? 2 : 1)); + // Set the low bit. + Huge.setBit(0); + if (Negative) + Huge.negate(); + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(Huge, /*IsSigned=*/IsSigned, RM); + EXPECT_EQ(ConvertFromStatus, APFloat::opOK); + + bool IsExact; + APSInt ResultInt{Huge.getBitWidth(), /*isUnsigned=*/!IsSigned}; + const APFloat::opStatus ConvertToStatus = + F.convertToInteger(ResultInt, APFloat::rmTowardZero, &IsExact); + + EXPECT_TRUE(IsExact) << "RM: " << RM; + EXPECT_TRUE(ResultInt.eq(Huge)) << ResultInt << " vs " << Huge << "\n"; + EXPECT_EQ(ConvertToStatus, APFloat::opOK); + } + } + } +} + +TEST(APFloatTest, PPCDoubleDoubleConvertFromAPIntBoundary) { + const unsigned Binary64Precision = + APFloat::semanticsPrecision(APFloat::IEEEdouble()); + APSInt Boundary = + APSInt::getMaxValue(Binary64Precision + 1, /*Unsigned=*/true); + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + const APFloat Exact = makeDoubleAPFloat(0x1p54, -0x1p0); + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(Boundary, /*IsSigned=*/false, RM); + EXPECT_EQ(ConvertFromStatus, APFloat::opOK); + EXPECT_EQ(F, Exact); + } + + Boundary = APSInt{APInt::getHighBitsSet(/*numBits=*/128, + /*hiBitsSet=*/Binary64Precision + 1), + /*isUnsigned=*/true}; + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + const APFloat Exact = makeDoubleAPFloat(0x1p128, -0x1p74); + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(Boundary, /*IsSigned=*/false, RM); + EXPECT_EQ(ConvertFromStatus, APFloat::opOK); + EXPECT_EQ(F, Exact); + } +} + +TEST(APFloatTest, PPCDoubleDoubleConvertFromAPIntEnormous) { + APFloat Largest = APFloat::getLargest(APFloat::PPCDoubleDouble()); + int Exponent = ilogb(Largest); + unsigned BitWidth = Exponent + 1; + APSInt HugeInt{BitWidth, /*isUnsigned=*/true}; + bool IsExact; + APFloat::opStatus Status = + Largest.convertToInteger(HugeInt, APFloat::rmTowardPositive, &IsExact); + ASSERT_EQ(Status, APFloat::opOK); + ASSERT_TRUE(IsExact); + + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + EXPECT_EQ(ConvertFromStatus, APFloat::opOK); + EXPECT_EQ(F, Largest); + } + + const unsigned MaxExponent = + APFloat::semanticsMaxExponent(APFloat::IEEEdouble()); + const unsigned Binary64Precision = + APFloat::semanticsPrecision(APFloat::IEEEdouble()); + const unsigned UlpOfLargest = MaxExponent - (2 * Binary64Precision); + const unsigned HalfUlpOfLargest = UlpOfLargest - 1; + + // Add just under a half-ulp. This should never overflow for + // round-ties-to-nearest modes. + HugeInt.setLowBits(HalfUlpOfLargest); + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + if (RM == APFloat::rmTowardPositive) { + EXPECT_TRUE(F.isPosInfinity()) << F; + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow); + } else { + EXPECT_EQ(F, Largest); + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact); + } + } + + // Now test adding a half-ulp. This should cause overflow for ties-to-away. + // ties-to-even will not overflow if the max finite value has a clear low bit. + ++HugeInt; + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + const bool Overflow = + RM == APFloat::rmTowardPositive || RM == APFloat::rmNearestTiesToAway || + (RM == APFloat::rmNearestTiesToEven && HugeInt[UlpOfLargest]); + if (Overflow) { + EXPECT_TRUE(F.isPosInfinity()) << F; + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow); + } else { + EXPECT_EQ(F, Largest); + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact); + } + } + + // Now test adding just over a half-ulp. This should break all ties. + ++HugeInt; + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + const bool Overflow = RM == APFloat::rmTowardPositive || + RM == APFloat::rmNearestTiesToAway || + RM == APFloat::rmNearestTiesToEven; + if (Overflow) { + EXPECT_TRUE(F.isPosInfinity()) << F; + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow); + } else { + EXPECT_EQ(F, Largest); + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact); + } + } + + HugeInt.setAllBits(); + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + const bool Overflow = RM == APFloat::rmTowardPositive || + RM == APFloat::rmNearestTiesToAway || + RM == APFloat::rmNearestTiesToEven; + if (Overflow) { + EXPECT_TRUE(F.isPosInfinity()) << F; + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow); + } else { + EXPECT_EQ(F, Largest); + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact); + } + } + + HugeInt.clearAllBits(); + HugeInt.setBit(2 * Binary64Precision + 1); + HugeInt.setLowBits(Binary64Precision + 1); + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + const APFloat RoundUp = makeDoubleAPFloat(0x1p107, 0x1p54); + const APFloat RoundDown = makeDoubleAPFloat(0x1p107, 0x1.fffffffffffffp53); + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact); + + if (RM == APFloat::rmNearestTiesToEven || + RM == APFloat::rmNearestTiesToAway || RM == APFloat::rmTowardPositive) + EXPECT_EQ(F, RoundUp); + else + EXPECT_EQ(F, RoundDown); + } + + ++HugeInt; + // 162259276829213381405976519770112 can be represented exactly. + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + const APFloat Exact = makeDoubleAPFloat(0x1p107, 0x1p54); + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + EXPECT_EQ(ConvertFromStatus, APFloat::opOK); + EXPECT_EQ(F, Exact); + } + + ++HugeInt; + // 162259276829213381405976519770113 rounds to either: + // 162259276829213381405976519770112 + // 162259276829213381405976519770114 + for (const APFloat::roundingMode RM : + {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative, + APFloat::rmTowardPositive, APFloat::rmTowardZero, + APFloat::rmNearestTiesToEven}) { + const APFloat RoundUp = + makeDoubleAPFloat(0x1.0000000000001p107, -0x1.fffffffffffffp53); + const APFloat RoundDown = makeDoubleAPFloat(0x1p107, 0x1p54); + EXPECT_LT(RoundDown, RoundUp); + + APFloat F{APFloat::PPCDoubleDouble()}; + const APFloat::opStatus ConvertFromStatus = + F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM); + EXPECT_EQ(ConvertFromStatus, APFloat::opInexact); + + if (RM == APFloat::rmNearestTiesToAway || RM == APFloat::rmTowardPositive) + EXPECT_EQ(F, RoundUp); + else + EXPECT_EQ(F, RoundDown); + } +} + TEST(APFloatTest, x87Largest) { APFloat MaxX87Val = APFloat::getLargest(APFloat::x87DoubleExtended()); EXPECT_TRUE(MaxX87Val.isLargest()); @@ -7812,13 +9357,8 @@ TEST(APFloatTest, getExactLog2) { continue; APFloat One(Semantics, "1.0"); - - if (I == APFloat::S_PPCDoubleDouble) { - // Not implemented - EXPECT_EQ(INT_MIN, One.getExactLog2()); - EXPECT_EQ(INT_MIN, One.getExactLog2Abs()); - continue; - } + APFloat Smallest = APFloat::getSmallest(Semantics); + APFloat Largest = APFloat::getLargest(Semantics); int MinExp = APFloat::semanticsMinExponent(Semantics); int MaxExp = APFloat::semanticsMaxExponent(Semantics); @@ -7865,16 +9405,15 @@ TEST(APFloatTest, getExactLog2) { EXPECT_EQ(INT_MIN, APFloat::getNaN(Semantics, true).getExactLog2Abs()); } - EXPECT_EQ(INT_MIN, - scalbn(One, MinExp - Precision - 1, APFloat::rmNearestTiesToEven) - .getExactLog2()); - EXPECT_EQ(INT_MIN, - scalbn(One, MinExp - Precision, APFloat::rmNearestTiesToEven) - .getExactLog2()); - EXPECT_EQ( INT_MIN, - scalbn(One, MaxExp + 1, APFloat::rmNearestTiesToEven).getExactLog2()); + scalbn(Smallest, -2, APFloat::rmNearestTiesToEven).getExactLog2()); + EXPECT_EQ( + INT_MIN, + scalbn(Smallest, -1, APFloat::rmNearestTiesToEven).getExactLog2()); + + EXPECT_EQ(INT_MIN, + scalbn(Largest, 1, APFloat::rmNearestTiesToEven).getExactLog2()); for (int i = MinExp - Precision + 1; i <= MaxExp; ++i) { EXPECT_EQ(i, scalbn(One, i, APFloat::rmNearestTiesToEven).getExactLog2()); diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index 4741c7b..116693c 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -3103,6 +3103,53 @@ TEST(APIntOpsTest, Mulh) { EXPECT_EQ(APInt(128, "FFEB498812C66C68D4552DB89B8EBF8F", 16), i128Res); } +TEST(APIntOpsTest, muli) { + APInt u32a(32, 0x0001'E235); + APInt u32b(32, 0xF623'55AD); + EXPECT_EQ(0x0001'CFA1'7CA0'76D1, APIntOps::muluExtended(u32a, u32b)); + + APInt u64a(64, 0x1234'5678'90AB'CDEF); + APInt u64b(64, 0xFEDC'BA09'8765'4321); + EXPECT_EQ(APInt(128, "121FA000A3723A57C24A442FE55618CF", 16), + APIntOps::muluExtended(u64a, u64b)); + + APInt u128a(128, "1234567890ABCDEF1234567890ABCDEF", 16); + APInt u128b(128, "FEDCBA0987654321FEDCBA0987654321", 16); + EXPECT_EQ( + APInt(256, + "121FA000A3723A57E68984312C3A8D7E96B428606E1E6BF5C24A442FE55618CF", + 16), + APIntOps::muluExtended(u128a, u128b)); + + APInt s32a(32, 0x1234'5678); + APInt s32b(32, 0x10AB'CDEF); + APInt s32c(32, 0xFEDC'BA09); + EXPECT_EQ(0x012F'7D02'2A42'D208, APIntOps::mulsExtended(s32a, s32b)); + EXPECT_EQ(0xFFEB'4988'09CA'3A38, APIntOps::mulsExtended(s32a, s32c)); + + APInt s64a(64, 0x1234'5678'90AB'CDEF); + APInt s64b(64, 0x1234'5678'90FE'DCBA); + APInt s64c(64, 0xFEDC'BA09'8765'4321); + EXPECT_EQ(APInt(128, "014B66DC328E10C1FB99704184EF03A6", 16), + APIntOps::mulsExtended(s64a, s64b)); + EXPECT_EQ(APInt(128, "FFEB498812C66C68C24A442FE55618CF", 16), + APIntOps::mulsExtended(s64a, s64c)); + + APInt s128a(128, "1234567890ABCDEF1234567890ABCDEF", 16); + APInt s128b(128, "1234567890FEDCBA1234567890FEDCBA", 16); + APInt s128c(128, "FEDCBA0987654321FEDCBA0987654321", 16); + EXPECT_EQ( + APInt(256, + "014B66DC328E10C1FE303DF9EA0B2529F87E475F3C6C180DFB99704184EF03A6", + 16), + APIntOps::mulsExtended(s128a, s128b)); + EXPECT_EQ( + APInt(256, + "FFEB498812C66C68D4552DB89B8EBF8F96B428606E1E6BF5C24A442FE55618CF", + 16), + APIntOps::mulsExtended(s128a, s128c)); +} + TEST(APIntTest, RoundingUDiv) { for (uint64_t Ai = 1; Ai <= 255; Ai++) { APInt A(8, Ai); @@ -3697,4 +3744,82 @@ TEST(APIntTest, TryExt) { ASSERT_EQ(42, APInt(128, -1).trySExtValue().value_or(42)); } +TEST(APIntTest, Fshl) { + EXPECT_EQ( + APIntOps::fshl(APInt(8, 0), APInt(8, 255), APInt(8, 8)).getZExtValue(), + 0U); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 255), APInt(8, 0), APInt(8, 8)).getZExtValue(), + 255U); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 255), APInt(8, 0), APInt(8, 15)).getZExtValue(), + 128U); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 15), APInt(8, 15), APInt(8, 11)).getZExtValue(), + 120U); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 3)).getZExtValue(), + 16U); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 1)).getZExtValue(), + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 9)).getZExtValue()); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 7)).getZExtValue(), + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 15)).getZExtValue()); + EXPECT_EQ(APIntOps::fshl(APInt(32, 0, /*isSigned*/ true), + APInt(32, 2147483647, /*isSigned*/ true), + APInt(32, 32, /*isSigned*/ true)) + .getSExtValue(), + 0); + EXPECT_EQ(APIntOps::fshl(APInt(64, 1, /*isSigned*/ true), + APInt(64, 2, /*isSigned*/ true), + APInt(64, 3, /*isSigned*/ true)) + .getSExtValue(), + 8); + EXPECT_EQ(APIntOps::fshl(APInt(16, -2, /*isSigned*/ true), + APInt(16, -1, /*isSigned*/ true), + APInt(16, 3, /*isSigned*/ true)) + .getSExtValue(), + -9); +} + +TEST(APIntTest, Fshr) { + EXPECT_EQ( + APIntOps::fshr(APInt(8, 0), APInt(8, 255), APInt(8, 8)).getZExtValue(), + 255U); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 255), APInt(8, 0), APInt(8, 8)).getZExtValue(), + 0U); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 255), APInt(8, 0), APInt(8, 15)).getZExtValue(), + 254U); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 15), APInt(8, 15), APInt(8, 11)).getZExtValue(), + 225U); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 3)).getZExtValue(), + 32U); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 1)).getZExtValue(), + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 9)).getZExtValue()); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 7)).getZExtValue(), + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 15)).getZExtValue()); + EXPECT_EQ(APIntOps::fshr(APInt(64, 0, /*isSigned*/ true), + APInt(64, 9223372036854775807, /*isSigned*/ true), + APInt(64, 64, /*isSigned*/ true)) + .getSExtValue(), + 9223372036854775807); + EXPECT_EQ(APIntOps::fshr(APInt(64, 1, /*isSigned*/ true), + APInt(64, 2, /*isSigned*/ true), + APInt(64, 3, /*isSigned*/ true)) + .getSExtValue(), + 2305843009213693952); + EXPECT_EQ(APIntOps::fshr(APInt(16, -2, /*isSigned*/ true), + APInt(16, -1, /*isSigned*/ true), + APInt(16, 3, /*isSigned*/ true)) + .getSExtValue(), + -8193); +} + } // end anonymous namespace diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp index bdfbc85..785ab16 100644 --- a/llvm/unittests/ADT/DenseMapTest.cpp +++ b/llvm/unittests/ADT/DenseMapTest.cpp @@ -10,6 +10,7 @@ #include "CountCopyAndMove.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/DenseMapInfoVariant.h" +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "gmock/gmock.h" @@ -249,6 +250,25 @@ TYPED_TEST(DenseMapTest, CopyConstructorNotSmallTest) { EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]); } +// Test range constructors. +TYPED_TEST(DenseMapTest, RangeConstructorTest) { + using KeyAndValue = + std::pair<typename TypeParam::key_type, typename TypeParam::mapped_type>; + KeyAndValue PlainArray[] = {{this->getKey(0), this->getValue(0)}, + {this->getKey(1), this->getValue(1)}}; + + TypeParam MapFromRange(llvm::from_range, PlainArray); + EXPECT_EQ(2u, MapFromRange.size()); + EXPECT_EQ(this->getValue(0), MapFromRange[this->getKey(0)]); + EXPECT_EQ(this->getValue(1), MapFromRange[this->getKey(1)]); + + TypeParam MapFromInitList({{this->getKey(0), this->getValue(1)}, + {this->getKey(1), this->getValue(2)}}); + EXPECT_EQ(2u, MapFromInitList.size()); + EXPECT_EQ(this->getValue(1), MapFromInitList[this->getKey(0)]); + EXPECT_EQ(this->getValue(2), MapFromInitList[this->getKey(1)]); +} + // Test copying from a default-constructed map. TYPED_TEST(DenseMapTest, CopyConstructorFromDefaultTest) { TypeParam copyMap(this->Map); @@ -367,9 +387,16 @@ TYPED_TEST(DenseMapTest, ConstIteratorTest) { EXPECT_TRUE(cit == cit2); } +// TYPED_TEST below cycles through different types. We define UniversalSmallSet +// here so that we'll use SmallSet or SmallPtrSet depending on whether the +// element type is a pointer. +template <typename T, unsigned N> +using UniversalSmallSet = + std::conditional_t<std::is_pointer_v<T>, SmallPtrSet<T, N>, SmallSet<T, N>>; + TYPED_TEST(DenseMapTest, KeysValuesIterator) { - SmallSet<typename TypeParam::key_type, 10> Keys; - SmallSet<typename TypeParam::mapped_type, 10> Values; + UniversalSmallSet<typename TypeParam::key_type, 10> Keys; + UniversalSmallSet<typename TypeParam::mapped_type, 10> Values; for (int I = 0; I < 10; ++I) { auto K = this->getKey(I); auto V = this->getValue(I); @@ -378,8 +405,8 @@ TYPED_TEST(DenseMapTest, KeysValuesIterator) { this->Map[K] = V; } - SmallSet<typename TypeParam::key_type, 10> ActualKeys; - SmallSet<typename TypeParam::mapped_type, 10> ActualValues; + UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys; + UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues; for (auto K : this->Map.keys()) ActualKeys.insert(K); for (auto V : this->Map.values()) @@ -390,8 +417,8 @@ TYPED_TEST(DenseMapTest, KeysValuesIterator) { } TYPED_TEST(DenseMapTest, ConstKeysValuesIterator) { - SmallSet<typename TypeParam::key_type, 10> Keys; - SmallSet<typename TypeParam::mapped_type, 10> Values; + UniversalSmallSet<typename TypeParam::key_type, 10> Keys; + UniversalSmallSet<typename TypeParam::mapped_type, 10> Values; for (int I = 0; I < 10; ++I) { auto K = this->getKey(I); auto V = this->getValue(I); @@ -401,8 +428,8 @@ TYPED_TEST(DenseMapTest, ConstKeysValuesIterator) { } const TypeParam &ConstMap = this->Map; - SmallSet<typename TypeParam::key_type, 10> ActualKeys; - SmallSet<typename TypeParam::mapped_type, 10> ActualValues; + UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys; + UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues; for (auto K : ConstMap.keys()) ActualKeys.insert(K); for (auto V : ConstMap.values()) @@ -726,6 +753,15 @@ TEST(DenseMapCustomTest, FindAsTest) { EXPECT_TRUE(map.find_as("d") == map.end()); } +TEST(DenseMapCustomTest, SmallDenseMapFromRange) { + std::pair<int, StringRef> PlainArray[] = {{0, "0"}, {1, "1"}, {2, "2"}}; + SmallDenseMap<int, StringRef> M(llvm::from_range, PlainArray); + EXPECT_EQ(3u, M.size()); + using testing::Pair; + EXPECT_THAT(M, testing::UnorderedElementsAre(Pair(0, "0"), Pair(1, "1"), + Pair(2, "2"))); +} + TEST(DenseMapCustomTest, SmallDenseMapInitializerList) { SmallDenseMap<int, int> M = {{0, 0}, {0, 1}, {1, 2}}; EXPECT_EQ(2u, M.size()); diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp index 4ea7403..a627091 100644 --- a/llvm/unittests/ADT/SmallPtrSetTest.cpp +++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp @@ -475,4 +475,8 @@ TEST(SmallPtrSetTest, Reserve) { EXPECT_EQ(Set.capacity(), 128u); EXPECT_EQ(Set.size(), 6u); EXPECT_THAT(Set, UnorderedElementsAre(&Vals[0], &Vals[1], &Vals[2], &Vals[3], &Vals[4], &Vals[5])); + + // Reserving 192 should result in 256 buckets. + Set.reserve(192); + EXPECT_EQ(Set.capacity(), 256u); } diff --git a/llvm/unittests/ADT/StringRefTest.cpp b/llvm/unittests/ADT/StringRefTest.cpp index ec9cdc1..d5f8dc4 100644 --- a/llvm/unittests/ADT/StringRefTest.cpp +++ b/llvm/unittests/ADT/StringRefTest.cpp @@ -619,6 +619,19 @@ TEST(StringRefTest, Hashing) { hash_value(StringRef("hello world").slice(1, -1))); } +TEST(StringRefTest, getAutoSenseRadix) { + struct RadixPair { + const char *Str; + unsigned Expected; + } RadixNumbers[] = {{"123", 10}, {"1", 10}, {"0b1", 2}, {"01", 8}, {"0o1", 8}, + {"0x1", 16}, {"0", 10}, {"00", 8}, {"", 10}}; + for (size_t i = 0; i < std::size(RadixNumbers); ++i) { + StringRef number = RadixNumbers[i].Str; + unsigned radix = getAutoSenseRadix(number); + EXPECT_EQ(radix, RadixNumbers[i].Expected); + } +} + struct UnsignedPair { const char *Str; uint64_t Expected; |