diff options
Diffstat (limited to 'llvm/unittests')
57 files changed, 3221 insertions, 330 deletions
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp index a35594d..141282e 100644 --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -1660,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); @@ -1918,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)); @@ -1929,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) { @@ -2692,9 +2787,7 @@ static APFloat makeDoubleAPFloat(T Hi, U Lo) { return APFloat(APFloat::PPCDoubleDouble(), Bits); } -static APFloat makeDoubleAPFloat(DD X) { - return makeDoubleAPFloat(X.Hi, X.Lo); -} +static APFloat makeDoubleAPFloat(DD X) { return makeDoubleAPFloat(X.Hi, X.Lo); } TEST(APFloatTest, PPCDoubleDouble) { APFloat test(APFloat::PPCDoubleDouble(), "1.0"); @@ -5799,6 +5892,363 @@ TEST_P(PPCDoubleDoubleRoundToIntegralValueTest, } } +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); + } +} + TEST(APFloatTest, PPCDoubleDoubleCompare) { using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, APFloat::cmpResult>; @@ -5841,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>; @@ -5994,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) { @@ -6268,6 +7123,249 @@ TEST(APFloatTest, PPCDoubleDoubleNext) { 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()); @@ -8259,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); @@ -8312,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/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp index 35135cd..92ae364 100644 --- a/llvm/unittests/ADT/StringMapTest.cpp +++ b/llvm/unittests/ADT/StringMapTest.cpp @@ -693,4 +693,58 @@ TEST(StringMapCustomTest, StringMapEntrySize) { EXPECT_EQ(LargeValue, Key.size()); } +TEST(StringMapCustomTest, NonConstIterator) { + StringMap<int> Map; + Map["key"] = 1; + + // Check that Map.begin() returns a non-const iterator. + static_assert( + std::is_same_v<decltype(Map.begin()), StringMap<int>::iterator>); + + // Check that the value_type of a non-const iterator is not a const type. + static_assert( + !std::is_const_v<StringMap<int>::iterator::value_type>, + "The value_type of a non-const iterator should not be a const type."); + + // Check that pointer and reference types are not const. + static_assert(std::is_same_v<StringMap<int>::iterator::pointer, + StringMap<int>::iterator::value_type *>); + static_assert(std::is_same_v<StringMap<int>::iterator::reference, + StringMap<int>::iterator::value_type &>); + + // Check that we can construct a const_iterator from an iterator. + static_assert(std::is_constructible_v<StringMap<int>::const_iterator, + StringMap<int>::iterator>); + + // Double check that we can actually construct a const_iterator. + StringMap<int>::const_iterator const_it = Map.begin(); + (void)const_it; +} + +TEST(StringMapCustomTest, ConstIterator) { + StringMap<int> Map; + const StringMap<int> &ConstMap = Map; + + // Check that ConstMap.begin() returns a const_iterator. + static_assert(std::is_same_v<decltype(ConstMap.begin()), + StringMap<int>::const_iterator>); + + // Check that the value_type of a const iterator is not a const type. + static_assert( + !std::is_const_v<StringMap<int>::const_iterator::value_type>, + "The value_type of a const iterator should not be a const type."); + + // Check that pointer and reference types are const. + static_assert( + std::is_same_v<StringMap<int>::const_iterator::pointer, + const StringMap<int>::const_iterator::value_type *>); + static_assert( + std::is_same_v<StringMap<int>::const_iterator::reference, + const StringMap<int>::const_iterator::value_type &>); + + // Check that we cannot construct an iterator from a const_iterator. + static_assert(!std::is_constructible_v<StringMap<int>::iterator, + StringMap<int>::const_iterator>); +} + } // end anonymous namespace diff --git a/llvm/unittests/Analysis/IR2VecTest.cpp b/llvm/unittests/Analysis/IR2VecTest.cpp index f7838cc4..9c10632 100644 --- a/llvm/unittests/Analysis/IR2VecTest.cpp +++ b/llvm/unittests/Analysis/IR2VecTest.cpp @@ -30,7 +30,6 @@ namespace { class TestableEmbedder : public Embedder { public: TestableEmbedder(const Function &F, const Vocabulary &V) : Embedder(F, V) {} - void computeEmbeddings() const override {} void computeEmbeddings(const BasicBlock &BB) const override {} }; @@ -258,6 +257,18 @@ TEST(IR2VecTest, CreateSymbolicEmbedder) { EXPECT_NE(Emb, nullptr); } +TEST(IR2VecTest, CreateFlowAwareEmbedder) { + Vocabulary V = Vocabulary(Vocabulary::createDummyVocabForTest()); + + LLVMContext Ctx; + Module M("M", Ctx); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M); + + auto Emb = Embedder::create(IR2VecKind::FlowAware, *F, V); + EXPECT_NE(Emb, nullptr); +} + TEST(IR2VecTest, CreateInvalidMode) { Vocabulary V = Vocabulary(Vocabulary::createDummyVocabForTest()); @@ -310,7 +321,7 @@ protected: } }; -TEST_F(IR2VecTestFixture, GetInstVecMap) { +TEST_F(IR2VecTestFixture, GetInstVecMap_Symbolic) { auto Emb = Embedder::create(IR2VecKind::Symbolic, *F, V); ASSERT_TRUE(static_cast<bool>(Emb)); @@ -325,11 +336,28 @@ TEST_F(IR2VecTestFixture, GetInstVecMap) { EXPECT_EQ(AddEmb.size(), 2u); EXPECT_EQ(RetEmb.size(), 2u); - EXPECT_TRUE(AddEmb.approximatelyEquals(Embedding(2, 27.9))); - EXPECT_TRUE(RetEmb.approximatelyEquals(Embedding(2, 17.0))); + EXPECT_TRUE(AddEmb.approximatelyEquals(Embedding(2, 25.5))); + EXPECT_TRUE(RetEmb.approximatelyEquals(Embedding(2, 15.5))); +} + +TEST_F(IR2VecTestFixture, GetInstVecMap_FlowAware) { + auto Emb = Embedder::create(IR2VecKind::FlowAware, *F, V); + ASSERT_TRUE(static_cast<bool>(Emb)); + + const auto &InstMap = Emb->getInstVecMap(); + + EXPECT_EQ(InstMap.size(), 2u); + EXPECT_TRUE(InstMap.count(AddInst)); + EXPECT_TRUE(InstMap.count(RetInst)); + + EXPECT_EQ(InstMap.at(AddInst).size(), 2u); + EXPECT_EQ(InstMap.at(RetInst).size(), 2u); + + EXPECT_TRUE(InstMap.at(AddInst).approximatelyEquals(Embedding(2, 25.5))); + EXPECT_TRUE(InstMap.at(RetInst).approximatelyEquals(Embedding(2, 32.6))); } -TEST_F(IR2VecTestFixture, GetBBVecMap) { +TEST_F(IR2VecTestFixture, GetBBVecMap_Symbolic) { auto Emb = Embedder::create(IR2VecKind::Symbolic, *F, V); ASSERT_TRUE(static_cast<bool>(Emb)); @@ -339,22 +367,47 @@ TEST_F(IR2VecTestFixture, GetBBVecMap) { EXPECT_TRUE(BBMap.count(BB)); EXPECT_EQ(BBMap.at(BB).size(), 2u); - // BB vector should be sum of add and ret: {27.9, 27.9} + {17.0, 17.0} = - // {44.9, 44.9} - EXPECT_TRUE(BBMap.at(BB).approximatelyEquals(Embedding(2, 44.9))); + // BB vector should be sum of add and ret: {25.5, 25.5} + {15.5, 15.5} = + // {41.0, 41.0} + EXPECT_TRUE(BBMap.at(BB).approximatelyEquals(Embedding(2, 41.0))); } -TEST_F(IR2VecTestFixture, GetBBVector) { +TEST_F(IR2VecTestFixture, GetBBVecMap_FlowAware) { + auto Emb = Embedder::create(IR2VecKind::FlowAware, *F, V); + ASSERT_TRUE(static_cast<bool>(Emb)); + + const auto &BBMap = Emb->getBBVecMap(); + + EXPECT_EQ(BBMap.size(), 1u); + EXPECT_TRUE(BBMap.count(BB)); + EXPECT_EQ(BBMap.at(BB).size(), 2u); + + // BB vector should be sum of add and ret: {25.5, 25.5} + {32.6, 32.6} = + // {58.1, 58.1} + EXPECT_TRUE(BBMap.at(BB).approximatelyEquals(Embedding(2, 58.1))); +} + +TEST_F(IR2VecTestFixture, GetBBVector_Symbolic) { auto Emb = Embedder::create(IR2VecKind::Symbolic, *F, V); ASSERT_TRUE(static_cast<bool>(Emb)); const auto &BBVec = Emb->getBBVector(*BB); EXPECT_EQ(BBVec.size(), 2u); - EXPECT_TRUE(BBVec.approximatelyEquals(Embedding(2, 44.9))); + EXPECT_TRUE(BBVec.approximatelyEquals(Embedding(2, 41.0))); +} + +TEST_F(IR2VecTestFixture, GetBBVector_FlowAware) { + auto Emb = Embedder::create(IR2VecKind::FlowAware, *F, V); + ASSERT_TRUE(static_cast<bool>(Emb)); + + const auto &BBVec = Emb->getBBVector(*BB); + + EXPECT_EQ(BBVec.size(), 2u); + EXPECT_TRUE(BBVec.approximatelyEquals(Embedding(2, 58.1))); } -TEST_F(IR2VecTestFixture, GetFunctionVector) { +TEST_F(IR2VecTestFixture, GetFunctionVector_Symbolic) { auto Emb = Embedder::create(IR2VecKind::Symbolic, *F, V); ASSERT_TRUE(static_cast<bool>(Emb)); @@ -362,24 +415,51 @@ TEST_F(IR2VecTestFixture, GetFunctionVector) { EXPECT_EQ(FuncVec.size(), 2u); - // Function vector should match BB vector (only one BB): {44.9, 44.9} - EXPECT_TRUE(FuncVec.approximatelyEquals(Embedding(2, 44.9))); + // Function vector should match BB vector (only one BB): {41.0, 41.0} + EXPECT_TRUE(FuncVec.approximatelyEquals(Embedding(2, 41.0))); +} + +TEST_F(IR2VecTestFixture, GetFunctionVector_FlowAware) { + auto Emb = Embedder::create(IR2VecKind::FlowAware, *F, V); + ASSERT_TRUE(static_cast<bool>(Emb)); + + const auto &FuncVec = Emb->getFunctionVector(); + + EXPECT_EQ(FuncVec.size(), 2u); + // Function vector should match BB vector (only one BB): {58.1, 58.1} + EXPECT_TRUE(FuncVec.approximatelyEquals(Embedding(2, 58.1))); } static constexpr unsigned MaxOpcodes = Vocabulary::MaxOpcodes; static constexpr unsigned MaxTypeIDs = Vocabulary::MaxTypeIDs; +static constexpr unsigned MaxCanonicalTypeIDs = Vocabulary::MaxCanonicalTypeIDs; static constexpr unsigned MaxOperands = Vocabulary::MaxOperandKinds; +// Mapping between LLVM Type::TypeID tokens and Vocabulary::CanonicalTypeID +// names and their canonical string keys. +#define IR2VEC_HANDLE_TYPE_BIMAP(X) \ + X(VoidTyID, VoidTy, "VoidTy") \ + X(IntegerTyID, IntegerTy, "IntegerTy") \ + X(FloatTyID, FloatTy, "FloatTy") \ + X(PointerTyID, PointerTy, "PointerTy") \ + X(FunctionTyID, FunctionTy, "FunctionTy") \ + X(StructTyID, StructTy, "StructTy") \ + X(ArrayTyID, ArrayTy, "ArrayTy") \ + X(FixedVectorTyID, VectorTy, "VectorTy") \ + X(LabelTyID, LabelTy, "LabelTy") \ + X(TokenTyID, TokenTy, "TokenTy") \ + X(MetadataTyID, MetadataTy, "MetadataTy") + TEST(IR2VecVocabularyTest, DummyVocabTest) { for (unsigned Dim = 1; Dim <= 10; ++Dim) { auto VocabVec = Vocabulary::createDummyVocabForTest(Dim); - + auto VocabVecSize = VocabVec.size(); // All embeddings should have the same dimension for (const auto &Emb : VocabVec) EXPECT_EQ(Emb.size(), Dim); // Should have the correct total number of embeddings - EXPECT_EQ(VocabVec.size(), MaxOpcodes + MaxTypeIDs + MaxOperands); + EXPECT_EQ(VocabVecSize, MaxOpcodes + MaxCanonicalTypeIDs + MaxOperands); auto ExpectedVocab = VocabVec; @@ -390,7 +470,7 @@ TEST(IR2VecVocabularyTest, DummyVocabTest) { Vocabulary Result = VocabAnalysis.run(TestMod, MAM); EXPECT_TRUE(Result.isValid()); EXPECT_EQ(Result.getDimension(), Dim); - EXPECT_EQ(Result.size(), MaxOpcodes + MaxTypeIDs + MaxOperands); + EXPECT_EQ(Result.getCanonicalSize(), VocabVecSize); unsigned CurPos = 0; for (const auto &Entry : Result) @@ -398,64 +478,68 @@ TEST(IR2VecVocabularyTest, DummyVocabTest) { } } -TEST(IR2VecVocabularyTest, NumericIDMap) { - // Test getNumericID for opcodes - EXPECT_EQ(Vocabulary::getNumericID(1u), 0u); - EXPECT_EQ(Vocabulary::getNumericID(13u), 12u); - EXPECT_EQ(Vocabulary::getNumericID(MaxOpcodes), MaxOpcodes - 1); - - // Test getNumericID for Type IDs - EXPECT_EQ(Vocabulary::getNumericID(Type::VoidTyID), - MaxOpcodes + static_cast<unsigned>(Type::VoidTyID)); - EXPECT_EQ(Vocabulary::getNumericID(Type::HalfTyID), - MaxOpcodes + static_cast<unsigned>(Type::HalfTyID)); - EXPECT_EQ(Vocabulary::getNumericID(Type::FloatTyID), - MaxOpcodes + static_cast<unsigned>(Type::FloatTyID)); - EXPECT_EQ(Vocabulary::getNumericID(Type::IntegerTyID), - MaxOpcodes + static_cast<unsigned>(Type::IntegerTyID)); - EXPECT_EQ(Vocabulary::getNumericID(Type::PointerTyID), - MaxOpcodes + static_cast<unsigned>(Type::PointerTyID)); - - // Test getNumericID for Value operands +TEST(IR2VecVocabularyTest, SlotIdxMapping) { + // Test getSlotIndex for Opcodes +#define EXPECT_OPCODE_SLOT(NUM, OPCODE, CLASS) \ + EXPECT_EQ(Vocabulary::getSlotIndex(NUM), static_cast<unsigned>(NUM - 1)); +#define HANDLE_INST(NUM, OPCODE, CLASS) EXPECT_OPCODE_SLOT(NUM, OPCODE, CLASS) +#include "llvm/IR/Instruction.def" +#undef HANDLE_INST +#undef EXPECT_OPCODE_SLOT + + // Test getSlotIndex for Types +#define EXPECT_TYPE_SLOT(TypeIDTok, CanonEnum, CanonStr) \ + EXPECT_EQ(Vocabulary::getSlotIndex(Type::TypeIDTok), \ + MaxOpcodes + static_cast<unsigned>( \ + Vocabulary::CanonicalTypeID::CanonEnum)); + + IR2VEC_HANDLE_TYPE_BIMAP(EXPECT_TYPE_SLOT) + +#undef EXPECT_TYPE_SLOT + + // Test getSlotIndex for Value operands LLVMContext Ctx; Module M("TestM", Ctx); FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), {Type::getInt32Ty(Ctx)}, false); Function *F = Function::Create(FTy, Function::ExternalLinkage, "testFunc", M); +#define EXPECTED_VOCAB_OPERAND_SLOT(X) \ + MaxOpcodes + MaxCanonicalTypeIDs + static_cast<unsigned>(X) // Test Function operand - EXPECT_EQ(Vocabulary::getNumericID(F), - MaxOpcodes + MaxTypeIDs + 0u); // Function = 0 + EXPECT_EQ(Vocabulary::getSlotIndex(*F), + EXPECTED_VOCAB_OPERAND_SLOT(Vocabulary::OperandKind::FunctionID)); // Test Constant operand Constant *C = ConstantInt::get(Type::getInt32Ty(Ctx), 42); - EXPECT_EQ(Vocabulary::getNumericID(C), - MaxOpcodes + MaxTypeIDs + 2u); // Constant = 2 + EXPECT_EQ(Vocabulary::getSlotIndex(*C), + EXPECTED_VOCAB_OPERAND_SLOT(Vocabulary::OperandKind::ConstantID)); // Test Pointer operand BasicBlock *BB = BasicBlock::Create(Ctx, "entry", F); AllocaInst *PtrVal = new AllocaInst(Type::getInt32Ty(Ctx), 0, "ptr", BB); - EXPECT_EQ(Vocabulary::getNumericID(PtrVal), - MaxOpcodes + MaxTypeIDs + 1u); // Pointer = 1 + EXPECT_EQ(Vocabulary::getSlotIndex(*PtrVal), + EXPECTED_VOCAB_OPERAND_SLOT(Vocabulary::OperandKind::PointerID)); // Test Variable operand (function argument) Argument *Arg = F->getArg(0); - EXPECT_EQ(Vocabulary::getNumericID(Arg), - MaxOpcodes + MaxTypeIDs + 3u); // Variable = 3 + EXPECT_EQ(Vocabulary::getSlotIndex(*Arg), + EXPECTED_VOCAB_OPERAND_SLOT(Vocabulary::OperandKind::VariableID)); +#undef EXPECTED_VOCAB_OPERAND_SLOT } #if GTEST_HAS_DEATH_TEST #ifndef NDEBUG TEST(IR2VecVocabularyTest, NumericIDMapInvalidInputs) { // Test invalid opcode IDs - EXPECT_DEATH(Vocabulary::getNumericID(0u), "Invalid opcode"); - EXPECT_DEATH(Vocabulary::getNumericID(MaxOpcodes + 1), "Invalid opcode"); + EXPECT_DEATH(Vocabulary::getSlotIndex(0u), "Invalid opcode"); + EXPECT_DEATH(Vocabulary::getSlotIndex(MaxOpcodes + 1), "Invalid opcode"); // Test invalid type IDs - EXPECT_DEATH(Vocabulary::getNumericID(static_cast<Type::TypeID>(MaxTypeIDs)), + EXPECT_DEATH(Vocabulary::getSlotIndex(static_cast<Type::TypeID>(MaxTypeIDs)), "Invalid type ID"); EXPECT_DEATH( - Vocabulary::getNumericID(static_cast<Type::TypeID>(MaxTypeIDs + 10)), + Vocabulary::getSlotIndex(static_cast<Type::TypeID>(MaxTypeIDs + 10)), "Invalid type ID"); } #endif // NDEBUG @@ -465,18 +549,46 @@ TEST(IR2VecVocabularyTest, StringKeyGeneration) { EXPECT_EQ(Vocabulary::getStringKey(0), "Ret"); EXPECT_EQ(Vocabulary::getStringKey(12), "Add"); - StringRef HalfTypeKey = Vocabulary::getStringKey(MaxOpcodes + 0); - StringRef FloatTypeKey = Vocabulary::getStringKey(MaxOpcodes + 2); - StringRef VoidTypeKey = Vocabulary::getStringKey(MaxOpcodes + 7); - StringRef IntTypeKey = Vocabulary::getStringKey(MaxOpcodes + 12); - - EXPECT_EQ(HalfTypeKey, "FloatTy"); - EXPECT_EQ(FloatTypeKey, "FloatTy"); - EXPECT_EQ(VoidTypeKey, "VoidTy"); - EXPECT_EQ(IntTypeKey, "IntegerTy"); - - StringRef FuncArgKey = Vocabulary::getStringKey(MaxOpcodes + MaxTypeIDs + 0); - StringRef PtrArgKey = Vocabulary::getStringKey(MaxOpcodes + MaxTypeIDs + 1); +#define EXPECT_OPCODE(NUM, OPCODE, CLASS) \ + EXPECT_EQ(Vocabulary::getStringKey(Vocabulary::getSlotIndex(NUM)), \ + Vocabulary::getVocabKeyForOpcode(NUM)); +#define HANDLE_INST(NUM, OPCODE, CLASS) EXPECT_OPCODE(NUM, OPCODE, CLASS) +#include "llvm/IR/Instruction.def" +#undef HANDLE_INST +#undef EXPECT_OPCODE + + // Verify CanonicalTypeID -> string mapping +#define EXPECT_CANONICAL_TYPE_NAME(TypeIDTok, CanonEnum, CanonStr) \ + EXPECT_EQ(Vocabulary::getStringKey( \ + MaxOpcodes + static_cast<unsigned>( \ + Vocabulary::CanonicalTypeID::CanonEnum)), \ + CanonStr); + + IR2VEC_HANDLE_TYPE_BIMAP(EXPECT_CANONICAL_TYPE_NAME) + +#undef EXPECT_CANONICAL_TYPE_NAME + +#define HANDLE_OPERAND_KINDS(X) \ + X(FunctionID, "Function") \ + X(PointerID, "Pointer") \ + X(ConstantID, "Constant") \ + X(VariableID, "Variable") + +#define EXPECT_OPERAND_KIND(EnumName, Str) \ + EXPECT_EQ(Vocabulary::getStringKey( \ + MaxOpcodes + MaxCanonicalTypeIDs + \ + static_cast<unsigned>(Vocabulary::OperandKind::EnumName)), \ + Str); + + HANDLE_OPERAND_KINDS(EXPECT_OPERAND_KIND) + +#undef EXPECT_OPERAND_KIND +#undef HANDLE_OPERAND_KINDS + + StringRef FuncArgKey = + Vocabulary::getStringKey(MaxOpcodes + MaxCanonicalTypeIDs + 0); + StringRef PtrArgKey = + Vocabulary::getStringKey(MaxOpcodes + MaxCanonicalTypeIDs + 1); EXPECT_EQ(FuncArgKey, "Function"); EXPECT_EQ(PtrArgKey, "Pointer"); } @@ -514,39 +626,14 @@ TEST(IR2VecVocabularyTest, InvalidAccess) { #endif // GTEST_HAS_DEATH_TEST TEST(IR2VecVocabularyTest, TypeIDStringKeyMapping) { - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::VoidTyID)), - "VoidTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::IntegerTyID)), - "IntegerTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::FloatTyID)), - "FloatTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::PointerTyID)), - "PointerTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::FunctionTyID)), - "FunctionTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::StructTyID)), - "StructTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::ArrayTyID)), - "ArrayTy"); - EXPECT_EQ(Vocabulary::getStringKey( - MaxOpcodes + static_cast<unsigned>(Type::FixedVectorTyID)), - "VectorTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::LabelTyID)), - "LabelTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::TokenTyID)), - "TokenTy"); - EXPECT_EQ(Vocabulary::getStringKey(MaxOpcodes + - static_cast<unsigned>(Type::MetadataTyID)), - "MetadataTy"); +#define EXPECT_TYPE_TO_CANONICAL(TypeIDTok, CanonEnum, CanonStr) \ + EXPECT_EQ( \ + Vocabulary::getStringKey(Vocabulary::getSlotIndex(Type::TypeIDTok)), \ + CanonStr); + + IR2VEC_HANDLE_TYPE_BIMAP(EXPECT_TYPE_TO_CANONICAL) + +#undef EXPECT_TYPE_TO_CANONICAL } TEST(IR2VecVocabularyTest, InvalidVocabularyConstruction) { diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp index 6af2006..559a0b7 100644 --- a/llvm/unittests/Analysis/ValueTrackingTest.cpp +++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/ValueTracking.h" +#include "llvm/ADT/FloatingPointMode.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/FloatingPointPredicateUtils.h" #include "llvm/AsmParser/Parser.h" @@ -2208,6 +2209,41 @@ TEST_F(ComputeKnownFPClassTest, Constants) { } } +TEST_F(ComputeKnownFPClassTest, fcmpImpliesClass_fabs_zero) { + parseAssembly("define float @test(float %x) {\n" + " %A = call float @llvm.fabs.f32(float %x)\n" + " ret float %A\n" + "}\n"); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_OEQ, *F, A, fcZero)), + fcZero); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_UEQ, *F, A, fcZero)), + fcZero | fcNan); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_UNE, *F, A, fcZero)), + ~fcZero); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_ONE, *F, A, fcZero)), + ~fcNan & ~fcZero); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_ORD, *F, A, fcZero)), + ~fcNan); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_UNO, *F, A, fcZero)), + fcNan); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_OGT, *F, A, fcZero)), + fcSubnormal | fcNormal | fcInf); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_UGT, *F, A, fcZero)), + fcSubnormal | fcNormal | fcInf | fcNan); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_OGE, *F, A, fcZero)), + ~fcNan); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_UGE, *F, A, fcZero)), + fcAllFlags); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_OLT, *F, A, fcZero)), + fcNone); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_ULT, *F, A, fcZero)), + fcNan); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_OLE, *F, A, fcZero)), + fcZero); + EXPECT_EQ(std::get<1>(fcmpImpliesClass(FCmpInst::FCMP_ULE, *F, A, fcZero)), + fcZero | fcNan); +} + TEST_F(ValueTrackingTest, isNonZeroRecurrence) { parseAssembly(R"( define i1 @test(i8 %n, i8 %r) { diff --git a/llvm/unittests/BinaryFormat/MsgPackDocumentTest.cpp b/llvm/unittests/BinaryFormat/MsgPackDocumentTest.cpp index a8db0f1..6a6ad70 100644 --- a/llvm/unittests/BinaryFormat/MsgPackDocumentTest.cpp +++ b/llvm/unittests/BinaryFormat/MsgPackDocumentTest.cpp @@ -22,12 +22,58 @@ TEST(MsgPackDocument, DocNodeTest) { ASSERT_TRUE(Str1 == Str2); } +TEST(MsgPackDocument, TestReadBoolean) { + Document Doc1; + bool Ok = Doc1.readFromBlob(StringRef("\xC2", 1), /*Multi=*/false); + ASSERT_TRUE(Ok); + ASSERT_EQ(Doc1.getRoot().getKind(), Type::Boolean); + ASSERT_EQ(Doc1.getRoot().getBool(), false); + Document Doc2; + Ok = Doc2.readFromBlob(StringRef("\xC3", 1), /*Multi=*/false); + ASSERT_TRUE(Ok); + ASSERT_EQ(Doc2.getRoot().getKind(), Type::Boolean); + ASSERT_EQ(Doc2.getRoot().getBool(), true); +} + TEST(MsgPackDocument, TestReadInt) { - Document Doc; - bool Ok = Doc.readFromBlob(StringRef("\xd0\x00", 2), /*Multi=*/false); + Document Doc1; + bool Ok = Doc1.readFromBlob(StringRef("\xD0\x00", 2), /*Multi=*/false); + ASSERT_TRUE(Ok); + ASSERT_EQ(Doc1.getRoot().getKind(), Type::Int); + ASSERT_EQ(Doc1.getRoot().getInt(), 0); + Document Doc2; + Ok = Doc2.readFromBlob(StringRef("\xFF", 1), /*Multi=*/false); + ASSERT_TRUE(Ok); + ASSERT_EQ(Doc2.getRoot().getKind(), Type::Int); + ASSERT_EQ(Doc2.getRoot().getInt(), -1); +} + +TEST(MsgPackDocument, TestReadUInt) { + Document Doc1; + bool Ok = Doc1.readFromBlob(StringRef("\xCC\x00", 2), /*Multi=*/false); + ASSERT_TRUE(Ok); + ASSERT_EQ(Doc1.getRoot().getKind(), Type::UInt); + ASSERT_EQ(Doc1.getRoot().getUInt(), 0U); + Document Doc2; + Ok = Doc2.readFromBlob(StringRef("\x01", 1), /*Multi=*/false); ASSERT_TRUE(Ok); - ASSERT_EQ(Doc.getRoot().getKind(), Type::Int); - ASSERT_EQ(Doc.getRoot().getInt(), 0); + ASSERT_EQ(Doc2.getRoot().getKind(), Type::UInt); + ASSERT_EQ(Doc2.getRoot().getUInt(), 1U); +} + +TEST(MsgPackDocument, TestReadFloat) { + Document Doc1; + bool Ok = + Doc1.readFromBlob(StringRef("\xCA\x3F\x80\x00\x00", 5), /*Multi=*/false); + ASSERT_TRUE(Ok); + ASSERT_EQ(Doc1.getRoot().getKind(), Type::Float); + ASSERT_EQ(Doc1.getRoot().getFloat(), 1.0); + Document Doc2; + Ok = Doc2.readFromBlob(StringRef("\xCB\x48\x3D\x63\x29\xF1\xC3\x5C\xA5", 9), + /*Multi=*/false); + ASSERT_TRUE(Ok); + ASSERT_EQ(Doc2.getRoot().getKind(), Type::Float); + ASSERT_EQ(Doc2.getRoot().getFloat(), 1e40); } TEST(MsgPackDocument, TestReadBinary) { @@ -192,12 +238,54 @@ TEST(MsgPackDocument, TestReadMergeMap) { ASSERT_EQ(BayS.getInt(), 8); } +TEST(MsgPackDocument, TestWriteBoolean) { + Document Doc; + Doc.getRoot() = true; + std::string Buffer; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, "\xc3"); + Doc.getRoot() = false; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, "\xc2"); +} + TEST(MsgPackDocument, TestWriteInt) { Document Doc; Doc.getRoot() = 1; std::string Buffer; Doc.writeToBlob(Buffer); ASSERT_EQ(Buffer, "\x01"); + Doc.getRoot() = -1; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, "\xFF"); + Doc.getRoot() = -4096; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, StringRef("\xD1\xF0\x00", 3)); +} + +TEST(MsgPackDocument, TestWriteUInt) { + Document Doc; + Doc.getRoot() = 1U; + std::string Buffer; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, "\x01"); + Doc.getRoot() = 4096U; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, StringRef("\xCD\x10\x00", 3)); +} + +TEST(MsgPackDocument, TestWriteFloat) { + Document Doc; + Doc.getRoot() = 1.0; + std::string Buffer; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, StringRef("\xCA\x3F\x80\x00\x00", 5)); + Doc.getRoot() = 1.0f; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, StringRef("\xCA\x3F\x80\x00\x00", 5)); + Doc.getRoot() = 1e40; + Doc.writeToBlob(Buffer); + ASSERT_EQ(Buffer, "\xCB\x48\x3D\x63\x29\xF1\xC3\x5C\xA5"); } TEST(MsgPackDocument, TestWriteBinary) { diff --git a/llvm/unittests/BinaryFormat/SFrameTest.cpp b/llvm/unittests/BinaryFormat/SFrameTest.cpp index 394e382..ab7b0fe 100644 --- a/llvm/unittests/BinaryFormat/SFrameTest.cpp +++ b/llvm/unittests/BinaryFormat/SFrameTest.cpp @@ -54,28 +54,28 @@ TYPED_TEST_SUITE(SFrameTest, Types, NameGenerator); TYPED_TEST(SFrameTest, FDEFlags) { FuncDescEntry<TestFixture::Endian> FDE = {}; - EXPECT_EQ(FDE.Info, 0u); - EXPECT_EQ(FDE.getPAuthKey(), 0); - EXPECT_EQ(FDE.getFDEType(), FDEType::PCInc); - EXPECT_EQ(FDE.getFREType(), FREType::Addr1); - - FDE.setPAuthKey(1); - EXPECT_EQ(FDE.Info, 0x20u); - EXPECT_EQ(FDE.getPAuthKey(), 1); - EXPECT_EQ(FDE.getFDEType(), FDEType::PCInc); - EXPECT_EQ(FDE.getFREType(), FREType::Addr1); - - FDE.setFDEType(FDEType::PCMask); - EXPECT_EQ(FDE.Info, 0x30u); - EXPECT_EQ(FDE.getPAuthKey(), 1); - EXPECT_EQ(FDE.getFDEType(), FDEType::PCMask); - EXPECT_EQ(FDE.getFREType(), FREType::Addr1); - - FDE.setFREType(FREType::Addr4); - EXPECT_EQ(FDE.Info, 0x32u); - EXPECT_EQ(FDE.getPAuthKey(), 1); - EXPECT_EQ(FDE.getFDEType(), FDEType::PCMask); - EXPECT_EQ(FDE.getFREType(), FREType::Addr4); + EXPECT_EQ(FDE.Info.Info, 0u); + EXPECT_EQ(FDE.Info.getPAuthKey(), 0); + EXPECT_EQ(FDE.Info.getFDEType(), FDEType::PCInc); + EXPECT_EQ(FDE.Info.getFREType(), FREType::Addr1); + + FDE.Info.setPAuthKey(1); + EXPECT_EQ(FDE.Info.Info, 0x20u); + EXPECT_EQ(FDE.Info.getPAuthKey(), 1); + EXPECT_EQ(FDE.Info.getFDEType(), FDEType::PCInc); + EXPECT_EQ(FDE.Info.getFREType(), FREType::Addr1); + + FDE.Info.setFDEType(FDEType::PCMask); + EXPECT_EQ(FDE.Info.Info, 0x30u); + EXPECT_EQ(FDE.Info.getPAuthKey(), 1); + EXPECT_EQ(FDE.Info.getFDEType(), FDEType::PCMask); + EXPECT_EQ(FDE.Info.getFREType(), FREType::Addr1); + + FDE.Info.setFREType(FREType::Addr4); + EXPECT_EQ(FDE.Info.Info, 0x32u); + EXPECT_EQ(FDE.Info.getPAuthKey(), 1); + EXPECT_EQ(FDE.Info.getFDEType(), FDEType::PCMask); + EXPECT_EQ(FDE.Info.getFREType(), FREType::Addr4); } TYPED_TEST(SFrameTest, FREFlags) { diff --git a/llvm/unittests/CAS/ActionCacheTest.cpp b/llvm/unittests/CAS/ActionCacheTest.cpp new file mode 100644 index 0000000..db67e30 --- /dev/null +++ b/llvm/unittests/CAS/ActionCacheTest.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the tests for ActionCaches. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/CAS/ActionCache.h" +#include "CASTestConfig.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::cas; + +TEST_P(CASTest, ActionCacheHit) { + std::shared_ptr<ObjectStore> CAS = createObjectStore(); + std::unique_ptr<ActionCache> Cache = createActionCache(); + + std::optional<ObjectProxy> ID; + ASSERT_THAT_ERROR(CAS->createProxy({}, "1").moveInto(ID), Succeeded()); + std::optional<CASID> ResultID; + ASSERT_THAT_ERROR(Cache->put(*ID, *ID), Succeeded()); + ASSERT_THAT_ERROR(Cache->get(*ID).moveInto(ResultID), Succeeded()); + ASSERT_TRUE(ResultID); + std::optional<ObjectRef> Result = CAS->getReference(*ResultID); + ASSERT_TRUE(Result); + ASSERT_EQ(*ID, *Result); +} + +TEST_P(CASTest, ActionCacheMiss) { + std::shared_ptr<ObjectStore> CAS = createObjectStore(); + std::unique_ptr<ActionCache> Cache = createActionCache(); + + std::optional<ObjectProxy> ID1, ID2; + ASSERT_THAT_ERROR(CAS->createProxy({}, "1").moveInto(ID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->createProxy({}, "2").moveInto(ID2), Succeeded()); + ASSERT_THAT_ERROR(Cache->put(*ID1, *ID2), Succeeded()); + // This is a cache miss for looking up a key doesn't exist. + std::optional<CASID> Result1; + ASSERT_THAT_ERROR(Cache->get(*ID2).moveInto(Result1), Succeeded()); + ASSERT_FALSE(Result1); + + ASSERT_THAT_ERROR(Cache->put(*ID2, *ID1), Succeeded()); + // Cache hit after adding the value. + std::optional<CASID> Result2; + ASSERT_THAT_ERROR(Cache->get(*ID2).moveInto(Result2), Succeeded()); + ASSERT_TRUE(Result2); + std::optional<ObjectRef> Ref = CAS->getReference(*Result2); + ASSERT_TRUE(Ref); + ASSERT_EQ(*ID1, *Ref); +} + +TEST_P(CASTest, ActionCacheRewrite) { + std::shared_ptr<ObjectStore> CAS = createObjectStore(); + std::unique_ptr<ActionCache> Cache = createActionCache(); + + std::optional<ObjectProxy> ID1, ID2; + ASSERT_THAT_ERROR(CAS->createProxy({}, "1").moveInto(ID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->createProxy({}, "2").moveInto(ID2), Succeeded()); + ASSERT_THAT_ERROR(Cache->put(*ID1, *ID1), Succeeded()); + // Writing to the same key with different value is error. + ASSERT_THAT_ERROR(Cache->put(*ID1, *ID2), Failed()); + // Writing the same value multiple times to the same key is fine. + ASSERT_THAT_ERROR(Cache->put(*ID1, *ID1), Succeeded()); +} diff --git a/llvm/unittests/CAS/CASTestConfig.cpp b/llvm/unittests/CAS/CASTestConfig.cpp new file mode 100644 index 0000000..29e2db4 --- /dev/null +++ b/llvm/unittests/CAS/CASTestConfig.cpp @@ -0,0 +1,21 @@ +//===- CASTestConfig.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CASTestConfig.h" +#include "llvm/CAS/ObjectStore.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::cas; + +static CASTestingEnv createInMemory(int I) { + return CASTestingEnv{createInMemoryCAS(), createInMemoryActionCache()}; +} + +INSTANTIATE_TEST_SUITE_P(InMemoryCAS, CASTest, + ::testing::Values(createInMemory)); diff --git a/llvm/unittests/CAS/CASTestConfig.h b/llvm/unittests/CAS/CASTestConfig.h new file mode 100644 index 0000000..8093a0b --- /dev/null +++ b/llvm/unittests/CAS/CASTestConfig.h @@ -0,0 +1,38 @@ +//===- CASTestConfig.h ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/ObjectStore.h" +#include "gtest/gtest.h" + +#ifndef LLVM_UNITTESTS_CASTESTCONFIG_H +#define LLVM_UNITTESTS_CASTESTCONFIG_H + +struct CASTestingEnv { + std::unique_ptr<llvm::cas::ObjectStore> CAS; + std::unique_ptr<llvm::cas::ActionCache> Cache; +}; + +class CASTest + : public testing::TestWithParam<std::function<CASTestingEnv(int)>> { +protected: + std::optional<int> NextCASIndex; + + std::unique_ptr<llvm::cas::ObjectStore> createObjectStore() { + auto TD = GetParam()(++(*NextCASIndex)); + return std::move(TD.CAS); + } + std::unique_ptr<llvm::cas::ActionCache> createActionCache() { + auto TD = GetParam()(++(*NextCASIndex)); + return std::move(TD.Cache); + } + void SetUp() { NextCASIndex = 0; } + void TearDown() { NextCASIndex = std::nullopt; } +}; + +#endif diff --git a/llvm/unittests/CAS/CMakeLists.txt b/llvm/unittests/CAS/CMakeLists.txt new file mode 100644 index 0000000..ff08100 --- /dev/null +++ b/llvm/unittests/CAS/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + Support + CAS + TestingSupport + ) + +add_llvm_unittest(CASTests + ActionCacheTest.cpp + CASTestConfig.cpp + ObjectStoreTest.cpp + ) + +target_link_libraries(CASTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/CAS/ObjectStoreTest.cpp b/llvm/unittests/CAS/ObjectStoreTest.cpp new file mode 100644 index 0000000..54083fd --- /dev/null +++ b/llvm/unittests/CAS/ObjectStoreTest.cpp @@ -0,0 +1,348 @@ +//===- ObjectStoreTest.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +#include "CASTestConfig.h" + +using namespace llvm; +using namespace llvm::cas; + +TEST_P(CASTest, PrintIDs) { + std::unique_ptr<ObjectStore> CAS = createObjectStore(); + + std::optional<CASID> ID1, ID2; + ASSERT_THAT_ERROR(CAS->createProxy({}, "1").moveInto(ID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->createProxy({}, "2").moveInto(ID2), Succeeded()); + EXPECT_NE(ID1, ID2); + std::string PrintedID1 = ID1->toString(); + std::string PrintedID2 = ID2->toString(); + EXPECT_NE(PrintedID1, PrintedID2); + + std::optional<CASID> ParsedID1, ParsedID2; + ASSERT_THAT_ERROR(CAS->parseID(PrintedID1).moveInto(ParsedID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->parseID(PrintedID2).moveInto(ParsedID2), Succeeded()); + EXPECT_EQ(ID1, ParsedID1); + EXPECT_EQ(ID2, ParsedID2); +} + +TEST_P(CASTest, Blobs) { + std::unique_ptr<ObjectStore> CAS1 = createObjectStore(); + StringRef ContentStrings[] = { + "word", + "some longer text std::string's local memory", + R"(multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text)", + }; + + SmallVector<CASID> IDs; + for (StringRef Content : ContentStrings) { + // Use StringRef::str() to create a temporary std::string. This could cause + // problems if the CAS is storing references to the input string instead of + // copying it. + std::optional<ObjectProxy> Blob; + ASSERT_THAT_ERROR(CAS1->createProxy({}, Content).moveInto(Blob), + Succeeded()); + IDs.push_back(Blob->getID()); + + // Check basic printing of IDs. + EXPECT_EQ(IDs.back().toString(), IDs.back().toString()); + if (IDs.size() > 2) + EXPECT_NE(IDs.front().toString(), IDs.back().toString()); + } + + // Check that the blobs give the same IDs later. + for (int I = 0, E = IDs.size(); I != E; ++I) { + std::optional<ObjectProxy> Blob; + ASSERT_THAT_ERROR(CAS1->createProxy({}, ContentStrings[I]).moveInto(Blob), + Succeeded()); + EXPECT_EQ(IDs[I], Blob->getID()); + } + + // Run validation on all CASIDs. + for (int I = 0, E = IDs.size(); I != E; ++I) + ASSERT_THAT_ERROR(CAS1->validate(IDs[I]), Succeeded()); + + // Check that the blobs can be retrieved multiple times. + for (int I = 0, E = IDs.size(); I != E; ++I) { + for (int J = 0, JE = 3; J != JE; ++J) { + std::optional<ObjectProxy> Buffer; + ASSERT_THAT_ERROR(CAS1->getProxy(IDs[I]).moveInto(Buffer), Succeeded()); + EXPECT_EQ(ContentStrings[I], Buffer->getData()); + } + } + + // Confirm these blobs don't exist in a fresh CAS instance. + std::unique_ptr<ObjectStore> CAS2 = createObjectStore(); + for (int I = 0, E = IDs.size(); I != E; ++I) { + std::optional<ObjectProxy> Proxy; + EXPECT_THAT_ERROR(CAS2->getProxy(IDs[I]).moveInto(Proxy), Failed()); + } + + // Insert into the second CAS and confirm the IDs are stable. Getting them + // should work now. + for (int I = IDs.size(), E = 0; I != E; --I) { + auto &ID = IDs[I - 1]; + auto &Content = ContentStrings[I - 1]; + std::optional<ObjectProxy> Blob; + ASSERT_THAT_ERROR(CAS2->createProxy({}, Content).moveInto(Blob), + Succeeded()); + EXPECT_EQ(ID, Blob->getID()); + + std::optional<ObjectProxy> Buffer; + ASSERT_THAT_ERROR(CAS2->getProxy(ID).moveInto(Buffer), Succeeded()); + EXPECT_EQ(Content, Buffer->getData()); + } +} + +TEST_P(CASTest, BlobsBig) { + // A little bit of validation that bigger blobs are okay. Climb up to 1MB. + std::unique_ptr<ObjectStore> CAS = createObjectStore(); + SmallString<256> String1 = StringRef("a few words"); + SmallString<256> String2 = StringRef("others"); + while (String1.size() < 1024U * 1024U) { + std::optional<CASID> ID1; + std::optional<CASID> ID2; + ASSERT_THAT_ERROR(CAS->createProxy({}, String1).moveInto(ID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->createProxy({}, String1).moveInto(ID2), Succeeded()); + ASSERT_THAT_ERROR(CAS->validate(*ID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->validate(*ID2), Succeeded()); + ASSERT_EQ(ID1, ID2); + + String1.append(String2); + ASSERT_THAT_ERROR(CAS->createProxy({}, String2).moveInto(ID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->createProxy({}, String2).moveInto(ID2), Succeeded()); + ASSERT_THAT_ERROR(CAS->validate(*ID1), Succeeded()); + ASSERT_THAT_ERROR(CAS->validate(*ID2), Succeeded()); + ASSERT_EQ(ID1, ID2); + String2.append(String1); + } + + // Specifically check near 1MB for objects large enough they're likely to be + // stored externally in an on-disk CAS and will be near a page boundary. + SmallString<0> Storage; + const size_t InterestingSize = 1024U * 1024ULL; + const size_t SizeE = InterestingSize + 2; + if (Storage.size() < SizeE) + Storage.resize(SizeE, '\01'); + for (size_t Size = InterestingSize - 2; Size != SizeE; ++Size) { + StringRef Data(Storage.data(), Size); + std::optional<ObjectProxy> Blob; + ASSERT_THAT_ERROR(CAS->createProxy({}, Data).moveInto(Blob), Succeeded()); + ASSERT_EQ(Data, Blob->getData()); + ASSERT_EQ(0, Blob->getData().end()[0]); + } +} + +TEST_P(CASTest, LeafNodes) { + std::unique_ptr<ObjectStore> CAS1 = createObjectStore(); + StringRef ContentStrings[] = { + "word", + "some longer text std::string's local memory", + R"(multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text +multiline text multiline text multiline text multiline text multiline text)", + }; + + SmallVector<ObjectRef> Nodes; + SmallVector<CASID> IDs; + for (StringRef Content : ContentStrings) { + // Use StringRef::str() to create a temporary std::string. This could cause + // problems if the CAS is storing references to the input string instead of + // copying it. + std::optional<ObjectRef> Node; + ASSERT_THAT_ERROR( + CAS1->store({}, arrayRefFromStringRef<char>(Content)).moveInto(Node), + Succeeded()); + Nodes.push_back(*Node); + + // Check basic printing of IDs. + IDs.push_back(CAS1->getID(*Node)); + auto ID = CAS1->getID(Nodes.back()); + EXPECT_EQ(ID.toString(), IDs.back().toString()); + EXPECT_EQ(*Node, Nodes.back()); + EXPECT_EQ(ID, IDs.back()); + if (Nodes.size() <= 1) + continue; + EXPECT_NE(Nodes.front(), Nodes.back()); + EXPECT_NE(IDs.front(), IDs.back()); + } + + // Check that the blobs give the same IDs later. + for (int I = 0, E = IDs.size(); I != E; ++I) { + std::optional<ObjectRef> Node; + ASSERT_THAT_ERROR( + CAS1->store({}, arrayRefFromStringRef<char>(ContentStrings[I])) + .moveInto(Node), + Succeeded()); + EXPECT_EQ(IDs[I], CAS1->getID(*Node)); + } + + // Check that the blobs can be retrieved multiple times. + for (int I = 0, E = IDs.size(); I != E; ++I) { + for (int J = 0, JE = 3; J != JE; ++J) { + std::optional<ObjectProxy> Object; + ASSERT_THAT_ERROR(CAS1->getProxy(IDs[I]).moveInto(Object), Succeeded()); + ASSERT_TRUE(Object); + EXPECT_EQ(ContentStrings[I], Object->getData()); + } + } + + // Confirm these blobs don't exist in a fresh CAS instance. + std::unique_ptr<ObjectStore> CAS2 = createObjectStore(); + for (int I = 0, E = IDs.size(); I != E; ++I) { + std::optional<ObjectProxy> Object; + EXPECT_THAT_ERROR(CAS2->getProxy(IDs[I]).moveInto(Object), Failed()); + } + + // Insert into the second CAS and confirm the IDs are stable. Getting them + // should work now. + for (int I = IDs.size(), E = 0; I != E; --I) { + auto &ID = IDs[I - 1]; + auto &Content = ContentStrings[I - 1]; + std::optional<ObjectRef> Node; + ASSERT_THAT_ERROR( + CAS2->store({}, arrayRefFromStringRef<char>(Content)).moveInto(Node), + Succeeded()); + EXPECT_EQ(ID, CAS2->getID(*Node)); + + std::optional<ObjectProxy> Object; + ASSERT_THAT_ERROR(CAS2->getProxy(ID).moveInto(Object), Succeeded()); + ASSERT_TRUE(Object); + EXPECT_EQ(Content, Object->getData()); + } +} + +TEST_P(CASTest, NodesBig) { + std::unique_ptr<ObjectStore> CAS = createObjectStore(); + + // Specifically check near 1MB for objects large enough they're likely to be + // stored externally in an on-disk CAS, and such that one of them will be + // near a page boundary. + SmallString<0> Storage; + constexpr size_t InterestingSize = 1024U * 1024ULL; + constexpr size_t WordSize = sizeof(void *); + + // Start much smaller to account for headers. + constexpr size_t SizeB = InterestingSize - 8 * WordSize; + constexpr size_t SizeE = InterestingSize + 1; + if (Storage.size() < SizeE) + Storage.resize(SizeE, '\01'); + + SmallVector<ObjectRef, 4> CreatedNodes; + // Avoid checking every size because this is an expensive test. Just check + // for data that is 8B-word-aligned, and one less. Also appending the created + // nodes as the references in the next block to check references are created + // correctly. + for (size_t Size = SizeB; Size < SizeE; Size += WordSize) { + for (bool IsAligned : {false, true}) { + StringRef Data(Storage.data(), Size - (IsAligned ? 0 : 1)); + std::optional<ObjectProxy> Node; + ASSERT_THAT_ERROR(CAS->createProxy(CreatedNodes, Data).moveInto(Node), + Succeeded()); + ASSERT_EQ(Data, Node->getData()); + ASSERT_EQ(0, Node->getData().end()[0]); + ASSERT_EQ(Node->getNumReferences(), CreatedNodes.size()); + CreatedNodes.emplace_back(Node->getRef()); + } + } + + for (auto ID : CreatedNodes) + ASSERT_THAT_ERROR(CAS->validate(CAS->getID(ID)), Succeeded()); +} + +#if LLVM_ENABLE_THREADS +/// Common test functionality for creating blobs in parallel. You can vary which +/// cas instances are the same or different, and the size of the created blobs. +static void testBlobsParallel(ObjectStore &Read1, ObjectStore &Read2, + ObjectStore &Write1, ObjectStore &Write2, + uint64_t BlobSize) { + SCOPED_TRACE("testBlobsParallel"); + unsigned BlobCount = 100; + std::vector<std::string> Blobs; + Blobs.reserve(BlobCount); + for (unsigned I = 0; I < BlobCount; ++I) { + std::string Blob; + Blob.resize(BlobSize); + getRandomBytes(Blob.data(), BlobSize); + Blobs.push_back(std::move(Blob)); + } + + std::mutex NodesMtx; + std::vector<std::optional<CASID>> CreatedNodes(BlobCount); + + auto Producer = [&](unsigned I, ObjectStore *CAS) { + std::optional<ObjectProxy> Node; + EXPECT_THAT_ERROR(CAS->createProxy({}, Blobs[I]).moveInto(Node), + Succeeded()); + { + std::lock_guard<std::mutex> L(NodesMtx); + CreatedNodes[I] = Node ? Node->getID() : CASID::getDenseMapTombstoneKey(); + } + }; + + auto Consumer = [&](unsigned I, ObjectStore *CAS) { + std::optional<CASID> ID; + while (!ID) { + // Busy wait. + std::lock_guard<std::mutex> L(NodesMtx); + ID = CreatedNodes[I]; + } + if (ID == CASID::getDenseMapTombstoneKey()) + // Producer failed; already reported. + return; + + std::optional<ObjectProxy> Node; + ASSERT_THAT_ERROR(CAS->getProxy(*ID).moveInto(Node), Succeeded()); + EXPECT_EQ(Node->getData(), Blobs[I]); + }; + + DefaultThreadPool Threads; + for (unsigned I = 0; I < BlobCount; ++I) { + Threads.async(Producer, I, &Write1); + Threads.async(Producer, I, &Write2); + Threads.async(Consumer, I, &Read1); + Threads.async(Consumer, I, &Read2); + } + + Threads.wait(); +} + +static void testBlobsParallel1(ObjectStore &CAS, uint64_t BlobSize) { + SCOPED_TRACE("testBlobsParallel1"); + testBlobsParallel(CAS, CAS, CAS, CAS, BlobSize); +} + +TEST_P(CASTest, BlobsParallel) { + std::shared_ptr<ObjectStore> CAS = createObjectStore(); + uint64_t Size = 1ULL * 1024; + ASSERT_NO_FATAL_FAILURE(testBlobsParallel1(*CAS, Size)); +} + +#ifdef EXPENSIVE_CHECKS +TEST_P(CASTest, BlobsBigParallel) { + std::shared_ptr<ObjectStore> CAS = createObjectStore(); + // 100k is large enough to be standalone files in our on-disk cas. + uint64_t Size = 100ULL * 1024; + ASSERT_NO_FATAL_FAILURE(testBlobsParallel1(*CAS, Size)); +} +#endif // EXPENSIVE_CHECKS +#endif // LLVM_ENABLE_THREADS diff --git a/llvm/unittests/CGData/StableFunctionMapTest.cpp b/llvm/unittests/CGData/StableFunctionMapTest.cpp index d551ac8..5cf62ae 100644 --- a/llvm/unittests/CGData/StableFunctionMapTest.cpp +++ b/llvm/unittests/CGData/StableFunctionMapTest.cpp @@ -117,7 +117,7 @@ TEST(StableFunctionMap, Finalize3) { Map.finalize(); auto &M = Map.getFunctionMap(); EXPECT_THAT(M, SizeIs(1)); - auto &FuncEntries = M.begin()->second; + auto &FuncEntries = M.begin()->second.Entries; for (auto &FuncEntry : FuncEntries) { EXPECT_THAT(*FuncEntry->IndexOperandHashMap, SizeIs(1)); ASSERT_THAT(*FuncEntry->IndexOperandHashMap, diff --git a/llvm/unittests/CMakeLists.txt b/llvm/unittests/CMakeLists.txt index 81abce5..d22613d 100644 --- a/llvm/unittests/CMakeLists.txt +++ b/llvm/unittests/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory(AsmParser) add_subdirectory(BinaryFormat) add_subdirectory(Bitcode) add_subdirectory(Bitstream) +add_subdirectory(CAS) add_subdirectory(CGData) add_subdirectory(CodeGen) add_subdirectory(DebugInfo) diff --git a/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp b/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp index e72b4e4..c0cf4ff 100644 --- a/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp +++ b/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp @@ -158,8 +158,8 @@ frameInfo: hasTailCall: false isCalleeSavedInfoValid: false localFrameSize: 0 - savePoint: '' - restorePoint: '' + savePoint: [] + restorePoint: [] fixedStack: [] stack: [] entry_values: [] @@ -307,8 +307,8 @@ frameInfo: hasTailCall: false isCalleeSavedInfoValid: false localFrameSize: 0 - savePoint: '' - restorePoint: '' + savePoint: [] + restorePoint: [] fixedStack: [] stack: [] entry_values: [] @@ -450,8 +450,8 @@ frameInfo: hasTailCall: false isCalleeSavedInfoValid: false localFrameSize: 0 - savePoint: '' - restorePoint: '' + savePoint: [] + restorePoint: [] fixedStack: [] stack: [] entry_values: [] @@ -593,8 +593,8 @@ frameInfo: hasTailCall: false isCalleeSavedInfoValid: false localFrameSize: 0 - savePoint: '' - restorePoint: '' + savePoint: [] + restorePoint: [] fixedStack: [] stack: [] entry_values: [] @@ -738,8 +738,8 @@ frameInfo: hasTailCall: false isCalleeSavedInfoValid: false localFrameSize: 0 - savePoint: '' - restorePoint: '' + savePoint: [] + restorePoint: [] fixedStack: [] stack: [] entry_values: [] @@ -883,8 +883,8 @@ frameInfo: hasTailCall: false isCalleeSavedInfoValid: false localFrameSize: 0 - savePoint: '' - restorePoint: '' + savePoint: [] + restorePoint: [] fixedStack: [] stack: [] entry_values: [] @@ -1029,8 +1029,8 @@ frameInfo: hasTailCall: false isCalleeSavedInfoValid: false localFrameSize: 0 - savePoint: '' - restorePoint: '' + savePoint: [] + restorePoint: [] fixedStack: [] stack: [] entry_values: [] diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownBitsVectorTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownBitsVectorTest.cpp index 67e6d14..73ddf0c 100644 --- a/llvm/unittests/CodeGen/GlobalISel/KnownBitsVectorTest.cpp +++ b/llvm/unittests/CodeGen/GlobalISel/KnownBitsVectorTest.cpp @@ -1548,48 +1548,3 @@ TEST_F(AArch64GISelMITest, TestNumSignBitsUAddoOverflow) { // Assert sign-extension from vector boolean EXPECT_EQ(32u, Info.computeNumSignBits(CopyOverflow)); } - -TEST_F(AArch64GISelMITest, TestKnwonBitsUnmergeVectorScalar) { - StringRef MIRString = R"( - %copy_x0:_(<2 x s16>) = COPY $w0 - %maskf:_(s16) = G_CONSTANT i16 15 - %x0_x1:_(<2 x s16>) = G_BUILD_VECTOR %maskf, %maskf - %and:_(<2 x s16>) = G_AND %copy_x0, %x0_x1 - %x0_0:_(s16), %x0_1:_(s16) = G_UNMERGE_VALUES %and - %result:_(s16) = COPY %x0_0 -)"; - - setUp(MIRString); - if (!TM) - GTEST_SKIP(); - - Register CopyOverflow = Copies[Copies.size() - 1]; - - GISelValueTracking Info(*MF); - - EXPECT_EQ(0xFFF0u, Info.getKnownBits(CopyOverflow).Zero.getZExtValue()); -} - -TEST_F(AArch64GISelMITest, TestKnwonBitsUnmergeVectorVector) { - StringRef MIRString = R"( - %copy_x0:_(<4 x s8>) = COPY $w0 - %maskff:_(s8) = G_CONSTANT i8 255 - %maskf:_(s8) = G_CONSTANT i8 15 - %x0_x1:_(<4 x s8>) = G_BUILD_VECTOR %maskf, %maskf, %maskff, %maskff - %and:_(<4 x s8>) = G_AND %copy_x0, %x0_x1 - %x0_0:_(<2 x s8>), %x0_1:_(<2 x s8>) = G_UNMERGE_VALUES %and - %result1:_(<2 x s8>) = COPY %x0_0 - %result2:_(<2 x s8>) = COPY %x0_1 -)"; - - setUp(MIRString); - if (!TM) - GTEST_SKIP(); - - GISelValueTracking Info(*MF); - - Register CopyOverflow1 = Copies[Copies.size() - 2]; - EXPECT_EQ(0xF0u, Info.getKnownBits(CopyOverflow1).Zero.getZExtValue()); - Register CopyOverflow2 = Copies[Copies.size() - 1]; - EXPECT_EQ(0x00u, Info.getKnownBits(CopyOverflow2).Zero.getZExtValue()); -} diff --git a/llvm/unittests/CodeGen/TypeTraitsTest.cpp b/llvm/unittests/CodeGen/TypeTraitsTest.cpp index 0ca4fa4..dde8628 100644 --- a/llvm/unittests/CodeGen/TypeTraitsTest.cpp +++ b/llvm/unittests/CodeGen/TypeTraitsTest.cpp @@ -16,7 +16,6 @@ using namespace llvm; -#if __has_feature(is_trivially_copyable) || (defined(__GNUC__) && __GNUC__ >= 5) static_assert(std::is_trivially_copyable_v<PressureChange>, "trivially copyable"); static_assert(std::is_trivially_copyable_v<SDep>, "trivially copyable"); @@ -24,5 +23,3 @@ static_assert(std::is_trivially_copyable_v<SDValue>, "trivially copyable"); static_assert(std::is_trivially_copyable_v<SlotIndex>, "trivially copyable"); static_assert(std::is_trivially_copyable_v<IdentifyingPassPtr>, "trivially copyable"); -#endif - diff --git a/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp b/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp index 544c39a..78dc850 100644 --- a/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp +++ b/llvm/unittests/DebugInfo/LogicalView/DWARFReaderTest.cpp @@ -81,7 +81,7 @@ void checkElementProperties(LVReader *Reader) { EXPECT_EQ(Language, LVSourceLanguage::DW_LANG_C_plus_plus_14); EXPECT_EQ(Language.getName(), "DW_LANG_C_plus_plus_14"); - EXPECT_EQ(CompileUnit->lineCount(), 0u); + EXPECT_EQ(CompileUnit->lineCount(), 1u); EXPECT_EQ(CompileUnit->scopeCount(), 1u); EXPECT_EQ(CompileUnit->symbolCount(), 0u); EXPECT_EQ(CompileUnit->typeCount(), 7u); @@ -129,7 +129,7 @@ void checkElementProperties(LVReader *Reader) { // Lines (debug and assembler) for 'foo'. const LVLines *Lines = Function->getLines(); ASSERT_NE(Lines, nullptr); - ASSERT_EQ(Lines->size(), 0x12u); + ASSERT_EQ(Lines->size(), 19u); // Check size of types in CompileUnit. const LVTypes *Types = CompileUnit->getTypes(); @@ -252,7 +252,7 @@ void checkElementComparison(LVReader *Reference, LVReader *Target) { // Get comparison table. LVPassTable PassTable = Compare.getPassTable(); - ASSERT_EQ(PassTable.size(), 5u); + ASSERT_EQ(PassTable.size(), 4u); LVReader *Reader; LVElement *Element; @@ -278,18 +278,8 @@ void checkElementComparison(LVReader *Reference, LVReader *Target) { EXPECT_EQ(Element->getName(), "INTEGER"); EXPECT_EQ(Pass, LVComparePass::Missing); - // Reference: Missing DebugLine - std::tie(Reader, Element, Pass) = PassTable[2]; - ASSERT_NE(Reader, nullptr); - ASSERT_NE(Element, nullptr); - EXPECT_EQ(Reader, Reference); - EXPECT_EQ(Element->getLevel(), 3u); - EXPECT_EQ(Element->getLineNumber(), 8u); - EXPECT_EQ(Element->getName(), ""); - EXPECT_EQ(Pass, LVComparePass::Missing); - // Target: Added Variable 'CONSTANT' - std::tie(Reader, Element, Pass) = PassTable[3]; + std::tie(Reader, Element, Pass) = PassTable[2]; ASSERT_NE(Reader, nullptr); ASSERT_NE(Element, nullptr); EXPECT_EQ(Reader, Target); @@ -299,7 +289,7 @@ void checkElementComparison(LVReader *Reference, LVReader *Target) { EXPECT_EQ(Pass, LVComparePass::Added); // Target: Added TypeDefinition 'INTEGER' - std::tie(Reader, Element, Pass) = PassTable[4]; + std::tie(Reader, Element, Pass) = PassTable[3]; ASSERT_NE(Reader, nullptr); ASSERT_NE(Element, nullptr); EXPECT_EQ(Reader, Target); diff --git a/llvm/unittests/ExecutionEngine/Orc/ThreadSafeModuleTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ThreadSafeModuleTest.cpp index bbb9e8d..7db561c 100644 --- a/llvm/unittests/ExecutionEngine/Orc/ThreadSafeModuleTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/ThreadSafeModuleTest.cpp @@ -31,15 +31,21 @@ const llvm::StringRef FooSrc = R"( } )"; -static ThreadSafeModule parseModule(llvm::StringRef Source, - llvm::StringRef Name) { - auto Ctx = std::make_unique<LLVMContext>(); +static std::unique_ptr<Module> +parseModuleRaw(llvm::StringRef Source, llvm::StringRef Name, LLVMContext &Ctx) { SMDiagnostic Err; - auto M = parseIR(MemoryBufferRef(Source, Name), Err, *Ctx); + auto M = parseIR(MemoryBufferRef(Source, Name), Err, Ctx); if (!M) { Err.print("Testcase source failed to parse: ", errs()); exit(1); } + return M; +} + +static ThreadSafeModule parseModule(llvm::StringRef Source, + llvm::StringRef Name) { + auto Ctx = std::make_unique<LLVMContext>(); + auto M = parseModuleRaw(Source, Name, *Ctx); return ThreadSafeModule(std::move(M), std::move(Ctx)); } @@ -128,6 +134,20 @@ TEST(ThreadSafeModuleTest, ConsumingModuleDo) { TSM.consumingModuleDo([](std::unique_ptr<Module> M) {}); } +TEST(ThreadSafeModuleTest, CloneExternalModuleToNewContext) { + auto Ctx = std::make_unique<LLVMContext>(); + auto M = parseModuleRaw(FooSrc, "foo.ll", *Ctx); + auto TSCtx = ThreadSafeContext(std::make_unique<LLVMContext>()); + auto TSM = cloneExternalModuleToContext(*M, TSCtx); + TSM.withModuleDo([&](Module &NewM) { + EXPECT_NE(&NewM.getContext(), Ctx.get()); + TSCtx.withContextDo( + [&](LLVMContext *NewCtx) { EXPECT_EQ(&NewM.getContext(), NewCtx); }); + EXPECT_FALSE(NewM.empty()); + EXPECT_FALSE(verifyModule(NewM, &errs())); + }); +} + TEST(ThreadSafeModuleTest, CloneToNewContext) { auto TSM1 = parseModule(FooSrc, "foo.ll"); auto TSM2 = cloneToNewContext(TSM1); diff --git a/llvm/unittests/Frontend/HLSLBindingTest.cpp b/llvm/unittests/Frontend/HLSLBindingTest.cpp index ca2f7b5..87106c2 100644 --- a/llvm/unittests/Frontend/HLSLBindingTest.cpp +++ b/llvm/unittests/Frontend/HLSLBindingTest.cpp @@ -273,3 +273,21 @@ TEST(HLSLBindingTest, TestFindAvailable) { // In an empty space we find the slot at the beginning. EXPECT_THAT(V, HasSpecificValue(0u)); } + +// Test that add duplicate bindings are correctly de-duplicated +TEST(HLSLBindingTest, TestNoOverlapWithDuplicates) { + hlsl::BindingInfoBuilder Builder; + + // We add the same binding three times, and just use `nullptr` for the cookie + // so that they should all be uniqued away. + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/nullptr); + bool HasOverlap; + hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap); + + EXPECT_FALSE(HasOverlap); +} diff --git a/llvm/unittests/Frontend/HLSLRootSignatureDumpTest.cpp b/llvm/unittests/Frontend/HLSLRootSignatureDumpTest.cpp index 98b33fd..1eb03f1 100644 --- a/llvm/unittests/Frontend/HLSLRootSignatureDumpTest.cpp +++ b/llvm/unittests/Frontend/HLSLRootSignatureDumpTest.cpp @@ -10,12 +10,13 @@ #include "gtest/gtest.h" using namespace llvm::hlsl::rootsig; +using llvm::dxil::ResourceClass; namespace { TEST(HLSLRootSignatureTest, DescriptorCBVClauseDump) { DescriptorTableClause Clause; - Clause.Type = ClauseType::CBuffer; + Clause.Type = ResourceClass::CBuffer; Clause.Reg = {RegisterType::BReg, 0}; Clause.setDefaultFlags(llvm::dxbc::RootSignatureVersion::V1_1); @@ -32,7 +33,7 @@ TEST(HLSLRootSignatureTest, DescriptorCBVClauseDump) { TEST(HLSLRootSignatureTest, DescriptorSRVClauseDump) { DescriptorTableClause Clause; - Clause.Type = ClauseType::SRV; + Clause.Type = ResourceClass::SRV; Clause.Reg = {RegisterType::TReg, 0}; Clause.NumDescriptors = NumDescriptorsUnbounded; Clause.Space = 42; @@ -52,7 +53,7 @@ TEST(HLSLRootSignatureTest, DescriptorSRVClauseDump) { TEST(HLSLRootSignatureTest, DescriptorUAVClauseDump) { using llvm::dxbc::DescriptorRangeFlags; DescriptorTableClause Clause; - Clause.Type = ClauseType::UAV; + Clause.Type = ResourceClass::UAV; Clause.Reg = {RegisterType::UReg, 92374}; Clause.NumDescriptors = 3298; Clause.Space = 932847; @@ -82,7 +83,7 @@ TEST(HLSLRootSignatureTest, DescriptorUAVClauseDump) { TEST(HLSLRootSignatureTest, DescriptorSamplerClauseDump) { DescriptorTableClause Clause; - Clause.Type = ClauseType::Sampler; + Clause.Type = ResourceClass::Sampler; Clause.Reg = {RegisterType::SReg, 0}; Clause.NumDescriptors = 2; Clause.Space = 42; @@ -102,7 +103,7 @@ TEST(HLSLRootSignatureTest, DescriptorSamplerClauseDump) { TEST(HLSLRootSignatureTest, DescriptorCBVV10ClauseDump) { DescriptorTableClause Clause; - Clause.Type = ClauseType::CBuffer; + Clause.Type = ResourceClass::CBuffer; Clause.Reg = {RegisterType::BReg, 0}; Clause.setDefaultFlags(llvm::dxbc::RootSignatureVersion::V1_0); @@ -119,7 +120,7 @@ TEST(HLSLRootSignatureTest, DescriptorCBVV10ClauseDump) { TEST(HLSLRootSignatureTest, DescriptorSamplerV10ClauseDump) { DescriptorTableClause Clause; - Clause.Type = ClauseType::Sampler; + Clause.Type = ResourceClass::Sampler; Clause.Reg = {RegisterType::SReg, 0}; Clause.setDefaultFlags(llvm::dxbc::RootSignatureVersion::V1_0); @@ -151,7 +152,7 @@ TEST(HLSLRootSignatureTest, DescriptorTableDump) { TEST(HLSLRootSignatureTest, RootCBVDump) { RootDescriptor Descriptor; - Descriptor.Type = DescriptorType::CBuffer; + Descriptor.Type = ResourceClass::CBuffer; Descriptor.Reg = {RegisterType::BReg, 0}; Descriptor.setDefaultFlags(llvm::dxbc::RootSignatureVersion::V1_1); @@ -168,7 +169,7 @@ TEST(HLSLRootSignatureTest, RootCBVDump) { TEST(HLSLRootSignatureTest, RootSRV10Dump) { RootDescriptor Descriptor; - Descriptor.Type = DescriptorType::SRV; + Descriptor.Type = ResourceClass::SRV; Descriptor.Reg = {RegisterType::TReg, 0}; Descriptor.setDefaultFlags(llvm::dxbc::RootSignatureVersion::V1_0); @@ -185,7 +186,7 @@ TEST(HLSLRootSignatureTest, RootSRV10Dump) { TEST(HLSLRootSignatureTest, RootUAVV10Dump) { RootDescriptor Descriptor; - Descriptor.Type = DescriptorType::UAV; + Descriptor.Type = ResourceClass::UAV; Descriptor.Reg = {RegisterType::UReg, 0}; Descriptor.setDefaultFlags(llvm::dxbc::RootSignatureVersion::V1_0); @@ -202,7 +203,7 @@ TEST(HLSLRootSignatureTest, RootUAVV10Dump) { TEST(HLSLRootSignatureTest, RootSRVDump) { RootDescriptor Descriptor; - Descriptor.Type = DescriptorType::SRV; + Descriptor.Type = ResourceClass::SRV; Descriptor.Reg = {RegisterType::TReg, 0}; Descriptor.Space = 42; Descriptor.Visibility = llvm::dxbc::ShaderVisibility::Geometry; @@ -221,7 +222,7 @@ TEST(HLSLRootSignatureTest, RootSRVDump) { TEST(HLSLRootSignatureTest, RootUAVDump) { using llvm::dxbc::RootDescriptorFlags; RootDescriptor Descriptor; - Descriptor.Type = DescriptorType::UAV; + Descriptor.Type = ResourceClass::UAV; Descriptor.Reg = {RegisterType::UReg, 92374}; Descriptor.Space = 932847; Descriptor.Visibility = llvm::dxbc::ShaderVisibility::Hull; diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp index b7a060b..c13570d 100644 --- a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp +++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp @@ -7852,4 +7852,28 @@ TEST_F(OpenMPIRBuilderTest, splitBB) { EXPECT_TRUE(DL == AllocaBB->getTerminator()->getStableDebugLoc()); } +TEST_F(OpenMPIRBuilderTest, spliceBBWithEmptyBB) { + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.Config.IsTargetDevice = false; + OMPBuilder.initialize(); + F->setName("func"); + IRBuilder<> Builder(BB); + + // Test calling spliceBB with an empty Block (but having trailing debug + // records). + DIBuilder DIB(*M); + DISubprogram *SP = F->getSubprogram(); + DIType *VoidPtrTy = + DIB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr); + DILocalVariable *Var = DIB.createParameterVariable( + SP, "test", /*ArgNo*/ 1, SP->getFile(), /*LineNo=*/0, VoidPtrTy); + DIB.insertDeclare(F->getArg(0), Var, DIB.createExpression(), DL, + Builder.GetInsertPoint()); + BasicBlock *New = BasicBlock::Create(Ctx, "", F); + spliceBB(Builder.saveIP(), New, true, DL); + Instruction *Terminator = BB->getTerminator(); + EXPECT_NE(Terminator, nullptr); + EXPECT_FALSE(Terminator->getDbgRecordRange().empty()); +} + } // namespace diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt index b66eae9..8b7bd39 100644 --- a/llvm/unittests/IR/CMakeLists.txt +++ b/llvm/unittests/IR/CMakeLists.txt @@ -43,6 +43,7 @@ add_llvm_unittest(IRTests PatternMatch.cpp ShuffleVectorInstTest.cpp StructuralHashTest.cpp + RuntimeLibcallsTest.cpp TimePassesTest.cpp TypesTest.cpp UseTest.cpp diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index bcb5d49..53d581c 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -451,6 +451,65 @@ TEST_F(ConstantRangeTest, Trunc) { EXPECT_EQ(SevenOne.truncate(2), ConstantRange(APInt(2, 3), APInt(2, 1))); } +TEST_F(ConstantRangeTest, TruncNuw) { + auto Range = [](unsigned NumBits, unsigned Lower, unsigned Upper) { + return ConstantRange(APInt(NumBits, Lower), APInt(NumBits, Upper)); + }; + // trunc([0, 4), 3->2) = full + EXPECT_TRUE( + Range(3, 0, 4).truncate(2, TruncInst::NoUnsignedWrap).isFullSet()); + // trunc([0, 3), 3->2) = [0, 3) + EXPECT_EQ(Range(3, 0, 3).truncate(2, TruncInst::NoUnsignedWrap), + Range(2, 0, 3)); + // trunc([1, 3), 3->2) = [1, 3) + EXPECT_EQ(Range(3, 1, 3).truncate(2, TruncInst::NoUnsignedWrap), + Range(2, 1, 3)); + // trunc([1, 5), 3->2) = [1, 0) + EXPECT_EQ(Range(3, 1, 5).truncate(2, TruncInst::NoUnsignedWrap), + Range(2, 1, 0)); + // trunc([4, 7), 3->2) = empty + EXPECT_TRUE( + Range(3, 4, 7).truncate(2, TruncInst::NoUnsignedWrap).isEmptySet()); + // trunc([4, 0), 3->2) = empty + EXPECT_TRUE( + Range(3, 4, 0).truncate(2, TruncInst::NoUnsignedWrap).isEmptySet()); + // trunc([4, 1), 3->2) = [0, 1) + EXPECT_EQ(Range(3, 4, 1).truncate(2, TruncInst::NoUnsignedWrap), + Range(2, 0, 1)); + // trunc([3, 1), 3->2) = [3, 1) + EXPECT_EQ(Range(3, 3, 1).truncate(2, TruncInst::NoUnsignedWrap), + Range(2, 3, 1)); + // trunc([3, 0), 3->2) = [3, 0) + EXPECT_EQ(Range(3, 3, 0).truncate(2, TruncInst::NoUnsignedWrap), + Range(2, 3, 0)); + // trunc([1, 0), 2->1) = [1, 0) + EXPECT_EQ(Range(2, 1, 0).truncate(1, TruncInst::NoUnsignedWrap), + Range(1, 1, 0)); + // trunc([2, 1), 2->1) = [0, 1) + EXPECT_EQ(Range(2, 2, 1).truncate(1, TruncInst::NoUnsignedWrap), + Range(1, 0, 1)); +} + +TEST_F(ConstantRangeTest, TruncNuwExhaustive) { + EnumerateConstantRanges(4, [&](const ConstantRange &CR) { + unsigned NumBits = 3; + ConstantRange Trunc = CR.truncate(NumBits, TruncInst::NoUnsignedWrap); + SmallBitVector Elems(1 << NumBits); + ForeachNumInConstantRange(CR, [&](const APInt &N) { + if (N.isIntN(NumBits)) + Elems.set(N.getZExtValue()); + }); + TestRange(Trunc, Elems, PreferSmallest, {CR}); + }); + EnumerateConstantRanges(3, [&](const ConstantRange &CR) { + ConstantRange Trunc = CR.truncate(1, TruncInst::NoUnsignedWrap); + EXPECT_EQ(CR.contains(APInt::getZero(3)), + Trunc.contains(APInt::getZero(1))); + EXPECT_EQ(CR.contains(APInt::getOneBitSet(3, 0)), + Trunc.contains(APInt::getAllOnes(1))); + }); +} + TEST_F(ConstantRangeTest, ZExt) { ConstantRange ZFull = Full.zeroExtend(20); ConstantRange ZEmpty = Empty.zeroExtend(20); diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp index 0065615..03333d5 100644 --- a/llvm/unittests/IR/DebugInfoTest.cpp +++ b/llvm/unittests/IR/DebugInfoTest.cpp @@ -553,17 +553,15 @@ TEST(DIBuilder, FixedPointType) { EXPECT_TRUE(Ty->getTag() == dwarf::DW_TAG_base_type); } -TEST(DbgAssignIntrinsicTest, replaceVariableLocationOp) { +TEST(DbgAssignRecordTest, replaceVariableLocationOp) { LLVMContext C; std::unique_ptr<Module> M = parseIR(C, R"( define dso_local void @fun(i32 %v1, ptr %p1, ptr %p2) !dbg !7 { entry: - call void @llvm.dbg.assign(metadata i32 %v1, metadata !14, metadata !DIExpression(), metadata !17, metadata ptr %p1, metadata !DIExpression()), !dbg !16 + #dbg_assign(i32 %v1, !14, !DIExpression(), !17, ptr %p1, !DIExpression(), !16) ret void } - declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) - !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!3} @@ -629,28 +627,28 @@ TEST(AssignmentTrackingTest, Utils) { std::unique_ptr<Module> M = parseIR(C, R"( define dso_local void @fun1() !dbg !7 { entry: - call void @llvm.dbg.assign(metadata i32 undef, metadata !10, metadata !DIExpression(), metadata !12, metadata i32 undef, metadata !DIExpression()), !dbg !13 + #dbg_assign(i32 undef, !10, !DIExpression(), !12, i32 undef, !DIExpression(), !13) %local = alloca i32, align 4, !DIAssignID !12 - call void @llvm.dbg.assign(metadata i32 undef, metadata !16, metadata !DIExpression(), metadata !12, metadata i32 undef, metadata !DIExpression()), !dbg !15 + #dbg_assign(i32 undef, !16, !DIExpression(), !12, i32 undef, !DIExpression(), !15) + #dbg_assign(i32 undef, !16, !DIExpression(), !25, i32 undef, !DIExpression(), !15) + #dbg_assign(i32 undef, !16, !DIExpression(), !25, i32 undef, !DIExpression(), !15) ret void, !dbg !15 } define dso_local void @fun2() !dbg !17 { entry: %local = alloca i32, align 4, !DIAssignID !20 - call void @llvm.dbg.assign(metadata i32 undef, metadata !18, metadata !DIExpression(), metadata !20, metadata i32 undef, metadata !DIExpression()), !dbg !19 + #dbg_assign(i32 undef, !18, !DIExpression(), !20, i32 undef, !DIExpression(), !19) ret void, !dbg !19 } define dso_local void @fun3() !dbg !21 { entry: %local = alloca i32, align 4, !DIAssignID !24 - call void @llvm.dbg.assign(metadata i32 undef, metadata !22, metadata !DIExpression(), metadata !24, metadata i32* undef, metadata !DIExpression()), !dbg !23 + #dbg_assign(i32 undef, !22, !DIExpression(), !24, i32* undef, !DIExpression(), !23) ret void } - declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) - !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!3, !4, !5} !llvm.ident = !{!6} @@ -680,6 +678,7 @@ TEST(AssignmentTrackingTest, Utils) { !22 = !DILocalVariable(name: "local4", scope: !21, file: !1, line: 2, type: !11) !23 = !DILocation(line: 4, column: 1, scope: !21) !24 = distinct !DIAssignID() + !25 = distinct !DIAssignID() )"); // Check the test IR isn't malformed. diff --git a/llvm/unittests/IR/InstructionsTest.cpp b/llvm/unittests/IR/InstructionsTest.cpp index fd8838e..21d4596 100644 --- a/llvm/unittests/IR/InstructionsTest.cpp +++ b/llvm/unittests/IR/InstructionsTest.cpp @@ -1935,5 +1935,22 @@ TEST(InstructionsTest, CmpPredicate) { EXPECT_EQ(P2, R2); } +TEST(InstructionsTest, StripAndAccumulateConstantOffset) { + LLVMContext C; + DataLayout DL; + std::unique_ptr<Module> M = parseIR(C, R"( + define void @foo(ptr %ptr, i64 %offset) { + %gep = getelementptr inbounds [1 x i8], ptr %ptr, i64 4, i64 %offset + ret void + })"); + ASSERT_TRUE(M); + Value *GEP = &M->getFunction("foo")->getEntryBlock().front(); + APInt Offset(DL.getIndexTypeSizeInBits(GEP->getType()), 0); + Value *Stripped = GEP->stripAndAccumulateConstantOffsets( + DL, Offset, /*AllowNonInBounds=*/true); + EXPECT_EQ(Stripped, GEP); + EXPECT_TRUE(Offset.isZero()); +} + } // end anonymous namespace } // end namespace llvm diff --git a/llvm/unittests/IR/PatternMatch.cpp b/llvm/unittests/IR/PatternMatch.cpp index bb7cc080..972dac8 100644 --- a/llvm/unittests/IR/PatternMatch.cpp +++ b/llvm/unittests/IR/PatternMatch.cpp @@ -2621,4 +2621,40 @@ TEST_F(PatternMatchTest, PtrAdd) { EXPECT_FALSE(match(OtherGEP, m_PtrAdd(m_Value(A), m_Value(B)))); } +TEST_F(PatternMatchTest, ShiftOrSelf) { + Type *I64Ty = Type::getInt64Ty(Ctx); + Constant *LHS = ConstantInt::get(I64Ty, 7); + Constant *ShAmt = ConstantInt::get(I64Ty, 16); + Value *Shl = IRB.CreateShl(LHS, ShAmt); + Value *LShr = IRB.CreateLShr(LHS, ShAmt); + Value *AShr = IRB.CreateAShr(LHS, ShAmt); + Value *Add = IRB.CreateAdd(LHS, LHS); + + uint64_t ShAmtC; + Value *A; + EXPECT_TRUE(match(Shl, m_ShlOrSelf(m_Value(A), ShAmtC))); + EXPECT_EQ(A, LHS); + EXPECT_EQ(ShAmtC, 16U); + + EXPECT_TRUE(match(Add, m_ShlOrSelf(m_Value(A), ShAmtC))); + EXPECT_EQ(A, Add); + EXPECT_EQ(ShAmtC, 0U); + + EXPECT_TRUE(match(LShr, m_LShrOrSelf(m_Value(A), ShAmtC))); + EXPECT_EQ(A, LHS); + EXPECT_EQ(ShAmtC, 16U); + + EXPECT_TRUE(match(Add, m_LShrOrSelf(m_Value(A), ShAmtC))); + EXPECT_EQ(A, Add); + EXPECT_EQ(ShAmtC, 0U); + + EXPECT_TRUE(match(AShr, m_AShrOrSelf(m_Value(A), ShAmtC))); + EXPECT_EQ(A, LHS); + EXPECT_EQ(ShAmtC, 16U); + + EXPECT_TRUE(match(Add, m_AShrOrSelf(m_Value(A), ShAmtC))); + EXPECT_EQ(A, Add); + EXPECT_EQ(ShAmtC, 0U); +} + } // anonymous namespace. diff --git a/llvm/unittests/IR/RuntimeLibcallsTest.cpp b/llvm/unittests/IR/RuntimeLibcallsTest.cpp new file mode 100644 index 0000000..0123168 --- /dev/null +++ b/llvm/unittests/IR/RuntimeLibcallsTest.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/RuntimeLibcalls.h" +#include "llvm/ADT/STLExtras.h" +#include "gtest/gtest.h" +using namespace llvm; + +namespace { + +TEST(RuntimeLibcallsTest, LibcallImplByName) { + EXPECT_TRUE(RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName("").empty()); + EXPECT_TRUE( + RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName("unknown").empty()); + EXPECT_TRUE( + RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName("Unsupported").empty()); + EXPECT_TRUE( + RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName("unsupported").empty()); + + for (RTLIB::LibcallImpl LC : RTLIB::libcall_impls()) { + StringRef Name = RTLIB::RuntimeLibcallsInfo::getLibcallImplName(LC); + EXPECT_TRUE(is_contained( + RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName(Name), LC)); + } + + // Test first libcall name + EXPECT_EQ( + RTLIB::arm64ec__Unwind_Resume, + *RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName("#_Unwind_Resume") + .begin()); + // Test longest libcall names + EXPECT_EQ(RTLIB::__hexagon_memcpy_likely_aligned_min32bytes_mult8bytes, + *RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName( + "__hexagon_memcpy_likely_aligned_min32bytes_mult8bytes") + .begin()); + + { + auto SquirtleSquad = + RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName("sqrtl"); + ASSERT_EQ(size(SquirtleSquad), 3); + auto I = SquirtleSquad.begin(); + EXPECT_EQ(*I++, RTLIB::sqrt_f128); + EXPECT_EQ(*I++, RTLIB::sqrt_f80); + EXPECT_EQ(*I++, RTLIB::sqrt_ppcf128); + } + + // Last libcall + { + auto Truncs = RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName("truncl"); + ASSERT_EQ(size(Truncs), 3); + auto I = Truncs.begin(); + EXPECT_EQ(*I++, RTLIB::trunc_f128); + EXPECT_EQ(*I++, RTLIB::trunc_f80); + EXPECT_EQ(*I++, RTLIB::trunc_ppcf128); + } +} + +} // namespace diff --git a/llvm/unittests/Object/ELFObjectFileTest.cpp b/llvm/unittests/Object/ELFObjectFileTest.cpp index 25b3907..17d9f50 100644 --- a/llvm/unittests/Object/ELFObjectFileTest.cpp +++ b/llvm/unittests/Object/ELFObjectFileTest.cpp @@ -680,7 +680,7 @@ Sections: - AddressOffset: 0x0 Size: 0x1 Metadata: 0x2 - CallsiteOffsets: [ 0x1 ] + CallsiteEndOffsets: [ 0x1 ] )"; { @@ -713,7 +713,7 @@ Sections: AddressOffset: 0x0 Size: 0x1 Metadata: 0x2 - CallsiteOffsets: [ 0x1 , 0x1 ] + CallsiteEndOffsets: [ 0x1 , 0x1 ] - Name: .llvm_bb_addr_map_2 Type: SHT_LLVM_BB_ADDR_MAP Link: 1 diff --git a/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp b/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp index 9e6f7cc..4cf8f61 100644 --- a/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp +++ b/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp @@ -130,7 +130,7 @@ TEST(RootSignature, ParseRootFlags) { NumRootParameters: 0 RootParametersOffset: 24 NumStaticSamplers: 0 - StaticSamplersOffset: 0 + StaticSamplersOffset: 24 Parameters: [] AllowInputAssemblerInputLayout: true )")); @@ -141,7 +141,7 @@ TEST(RootSignature, ParseRootFlags) { 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, }; EXPECT_EQ(Storage.size(), 68u); @@ -168,9 +168,9 @@ TEST(RootSignature, HeaderData) { RootSignature: Version: 2 NumRootParameters: 1 - RootParametersOffset: 255 + RootParametersOffset: 24 NumStaticSamplers: 0 - StaticSamplersOffset: 0 + StaticSamplersOffset: 48 Parameters: - ParameterType: 1 ShaderVisibility: 2 @@ -187,8 +187,8 @@ TEST(RootSignature, HeaderData) { 0x05, 0x39, 0xe1, 0xfe, 0x31, 0x20, 0xf0, 0xc1, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x59, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -220,9 +220,9 @@ TEST(RootSignature, ParseRootConstants) { RootSignature: Version: 2 NumRootParameters: 1 - RootParametersOffset: 36 + RootParametersOffset: 24 NumStaticSamplers: 0 - StaticSamplersOffset: 0 + StaticSamplersOffset: 48 Parameters: - ParameterType: 1 ShaderVisibility: 2 @@ -239,8 +239,8 @@ TEST(RootSignature, ParseRootConstants) { 0x05, 0x39, 0xe1, 0xfe, 0x31, 0x20, 0xf0, 0xc1, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x59, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -274,7 +274,7 @@ TEST(RootSignature, ParseRootDescriptorsV10) { NumRootParameters: 1 RootParametersOffset: 24 NumStaticSamplers: 0 - StaticSamplersOffset: 60 + StaticSamplersOffset: 44 Parameters: - ParameterType: 2 # SRV ShaderVisibility: 3 # Domain @@ -291,7 +291,7 @@ TEST(RootSignature, ParseRootDescriptorsV10) { 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x59, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -325,7 +325,7 @@ TEST(RootSignature, ParseRootDescriptorsV11) { NumRootParameters: 1 RootParametersOffset: 24 NumStaticSamplers: 0 - StaticSamplersOffset: 60 + StaticSamplersOffset: 48 Parameters: - ParameterType: 2 # SRV ShaderVisibility: 3 # Domain @@ -343,7 +343,7 @@ TEST(RootSignature, ParseRootDescriptorsV11) { 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x59, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -377,7 +377,7 @@ TEST(RootSignature, ParseDescriptorTableV10) { NumRootParameters: 1 RootParametersOffset: 24 NumStaticSamplers: 0 - StaticSamplersOffset: 60 + StaticSamplersOffset: 64 Parameters: - ParameterType: 0 # SRV ShaderVisibility: 3 # Domain @@ -399,7 +399,7 @@ TEST(RootSignature, ParseDescriptorTableV10) { 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x59, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, @@ -433,7 +433,7 @@ TEST(RootSignature, ParseDescriptorTableV11) { NumRootParameters: 1 RootParametersOffset: 24 NumStaticSamplers: 0 - StaticSamplersOffset: 60 + StaticSamplersOffset: 68 Parameters: - ParameterType: 0 # Descriptor Table ShaderVisibility: 3 # Domain @@ -456,7 +456,7 @@ TEST(RootSignature, ParseDescriptorTableV11) { 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x59, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, @@ -487,7 +487,7 @@ Parts: RootSignature: Version: 2 NumRootParameters: 0 - RootParametersOffset: 0 + RootParametersOffset: 24 NumStaticSamplers: 1 StaticSamplersOffset: 24 Parameters: [] @@ -516,7 +516,7 @@ Parts: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x54, 0x53, 0x30, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xa4, 0x70, 0x9d, 0x3f, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, diff --git a/llvm/unittests/Support/AlignOfTest.cpp b/llvm/unittests/Support/AlignOfTest.cpp index f84895c..979f2cf 100644 --- a/llvm/unittests/Support/AlignOfTest.cpp +++ b/llvm/unittests/Support/AlignOfTest.cpp @@ -30,7 +30,7 @@ namespace { #ifdef __clang__ #pragma clang diagnostic ignored "-Wunknown-pragmas" #pragma clang diagnostic ignored "-Winaccessible-base" -#elif ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402 +#elif defined(__GNUC__) // Pragma based warning suppression was introduced in GGC 4.2. Additionally // this warning is "enabled by default". The warning still appears if -Wall is // suppressed. Apparently GCC suppresses it when -w is specifed, which is odd. diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 868c40b..89edfb3 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -111,6 +111,7 @@ add_llvm_unittest(SupportTests formatted_raw_ostream_test.cpp raw_fd_stream_test.cpp raw_ostream_test.cpp + raw_ostream_proxy_test.cpp raw_pwrite_stream_test.cpp raw_sha1_ostream_test.cpp raw_socket_stream_test.cpp diff --git a/llvm/unittests/Support/DebugLogTest.cpp b/llvm/unittests/Support/DebugLogTest.cpp index b28c59c..e087705 100644 --- a/llvm/unittests/Support/DebugLogTest.cpp +++ b/llvm/unittests/Support/DebugLogTest.cpp @@ -115,8 +115,18 @@ TEST(DebugLogTest, StreamPrefix) { ldbg_osA << "5"; EXPECT_EQ(os.str(), expected); } - // After destructors, there was a pending newline for stream B. - EXPECT_EQ(os.str(), expected + "PrefixB "); + EXPECT_EQ(os.str(), expected); +} + +TEST(DebugLogTest, DestructorPrefix) { + llvm::DebugFlag = true; + std::string str; + raw_string_ostream os(str); + { + llvm::impl::raw_ldbg_ostream ldbg_osB("PrefixB ", os); + } + // After destructors, nothing should have been printed. + EXPECT_EQ(os.str(), ""); } #else TEST(DebugLogTest, Basic) { diff --git a/llvm/unittests/Support/DebugTest.cpp b/llvm/unittests/Support/DebugTest.cpp index e8b7548..ed8bfc6 100644 --- a/llvm/unittests/Support/DebugTest.cpp +++ b/llvm/unittests/Support/DebugTest.cpp @@ -50,4 +50,46 @@ TEST(DebugTest, CommaInDebugBlock) { }); EXPECT_EQ("ZYX", os1.str()); } + +TEST(DebugTest, DebugWithType) { + llvm::DebugFlag = true; + + // Check if the DEBUG_WITH_TYPE macro is enabled for the given type. + auto CheckDebugWithType = [](const char *Type) { + bool Visited = false; + DEBUG_WITH_TYPE(Type, { Visited = true; }); + return Visited; + }; + + { + static const char *DT[] = {"A", "B"}; + setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0])); + EXPECT_TRUE(CheckDebugWithType("A")); + EXPECT_TRUE(CheckDebugWithType("B")); + EXPECT_FALSE(CheckDebugWithType("C")); + } + { + static const char *DT[] = {"A:"}; + setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0])); + EXPECT_FALSE(CheckDebugWithType("A")); + EXPECT_TRUE(CheckDebugWithType("B")); + EXPECT_TRUE(CheckDebugWithType("C")); + } + { + static const char *DT[] = {"A:", "B"}; + setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0])); + EXPECT_FALSE(CheckDebugWithType("A")); + EXPECT_TRUE(CheckDebugWithType("B")); + EXPECT_FALSE(CheckDebugWithType("C")); + } + { + static const char *DT[] = {"A:3", "B:", "C"}; + setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0])); + EXPECT_TRUE(CheckDebugWithType("A")); + EXPECT_FALSE(isCurrentDebugType("A", 4)); + EXPECT_FALSE(CheckDebugWithType("B")); + EXPECT_TRUE(isCurrentDebugType("C", 10)); + EXPECT_FALSE(CheckDebugWithType("D")); + } +} #endif diff --git a/llvm/unittests/Support/DynamicLibrary/CMakeLists.txt b/llvm/unittests/Support/DynamicLibrary/CMakeLists.txt index 2366209..98b7f2d 100644 --- a/llvm/unittests/Support/DynamicLibrary/CMakeLists.txt +++ b/llvm/unittests/Support/DynamicLibrary/CMakeLists.txt @@ -16,7 +16,7 @@ set_output_directory(DynamicLibraryLib ) # FIXME: Find out why AIX fails with new DynamicLibrary symbols behavior. -if(${CMAKE_SYSTEM_NAME} MATCHES "AIX") +if("${CMAKE_SYSTEM_NAME}" MATCHES "AIX") add_llvm_unittest(DynamicLibraryTests DynamicLibraryTest.cpp ) @@ -28,7 +28,7 @@ else() ) endif() target_link_libraries(DynamicLibraryTests PRIVATE DynamicLibraryLib) -if(${CMAKE_SYSTEM_NAME} MATCHES "AIX") +if("${CMAKE_SYSTEM_NAME}" MATCHES "AIX") export_executable_symbols(DynamicLibraryTests) endif() @@ -64,7 +64,7 @@ endfunction(dynlib_add_module) # Revert -Wl,-z,nodelete on this test since it relies on the file # being unloaded. -if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") +if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") string(REPLACE "-Wl,-z,nodelete" "" CMAKE_MODULE_LINKER_FLAGS ${CMAKE_MODULE_LINKER_FLAGS}) endif() diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp index 355aa6b..888729b 100644 --- a/llvm/unittests/Support/Path.cpp +++ b/llvm/unittests/Support/Path.cpp @@ -1471,6 +1471,43 @@ TEST_F(FileSystemTest, FileMapping) { ASSERT_NO_ERROR(fs::remove(TempPath)); } +TEST_F(FileSystemTest, FileMappingSync) { + // Create a temp file. + SmallString<0> TempPath(TestDirectory); + sys::path::append(TempPath, "test-%%%%"); + auto TempFileOrError = fs::TempFile::create(TempPath); + ASSERT_TRUE((bool)TempFileOrError); + fs::TempFile File = std::move(*TempFileOrError); + StringRef Content("hello there"); + std::string FileName = File.TmpName; + ASSERT_NO_ERROR( + fs::resize_file_before_mapping_readwrite(File.FD, Content.size())); + { + // Map in the file and write some content. + std::error_code EC; + fs::mapped_file_region MFR(fs::convertFDToNativeFile(File.FD), + fs::mapped_file_region::readwrite, + Content.size(), 0, EC); + + // Keep the file so it can be read. + ASSERT_FALSE((bool)File.keep()); + + // Write content through mapped memory. + ASSERT_NO_ERROR(EC); + std::copy(Content.begin(), Content.end(), MFR.data()); + + // Synchronize to file system. + ASSERT_FALSE((bool)MFR.sync()); + + // Check the file content using file IO APIs. + auto Buffer = MemoryBuffer::getFile(FileName); + ASSERT_TRUE((bool)Buffer); + ASSERT_EQ(Content, Buffer->get()->getBuffer()); + } + // Manually remove the test file. + ASSERT_FALSE((bool)fs::remove(FileName)); +} + TEST(Support, NormalizePath) { // Input, Expected Win, Expected Posix using TestTuple = std::tuple<const char *, const char *, const char *>; diff --git a/llvm/unittests/Support/ProgramTest.cpp b/llvm/unittests/Support/ProgramTest.cpp index 693b53b..d30bf45 100644 --- a/llvm/unittests/Support/ProgramTest.cpp +++ b/llvm/unittests/Support/ProgramTest.cpp @@ -10,6 +10,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ExponentialBackoff.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" @@ -573,6 +574,88 @@ TEST_F(ProgramEnvTest, TestLockFile) { sys::fs::remove(LockedFile); } +TEST_F(ProgramEnvTest, TestLockFileExclusive) { + using namespace llvm::sys; + using namespace std::chrono_literals; + + if (const char *LockedFile = getenv("LLVM_PROGRAM_TEST_LOCKED_FILE")) { + // Child process. + int FD2; + ASSERT_NO_ERROR(fs::openFileForReadWrite(LockedFile, FD2, + fs::CD_OpenExisting, fs::OF_None)); + + // File should currently be non-exclusive locked by the main process, thus + // trying to acquire exclusive lock will fail and trying to acquire + // non-exclusive will succeed. + EXPECT_TRUE( + fs::tryLockFile(FD2, std::chrono::seconds(0), fs::LockKind::Exclusive)); + + EXPECT_FALSE( + fs::tryLockFile(FD2, std::chrono::seconds(0), fs::LockKind::Shared)); + + close(FD2); + // Write a file to indicate just finished. + std::string FinishFile = std::string(LockedFile) + "-finished"; + int FD3; + ASSERT_NO_ERROR(fs::openFileForReadWrite(FinishFile, FD3, fs::CD_CreateNew, + fs::OF_None)); + close(FD3); + exit(0); + } + + // Create file that will be locked. + SmallString<64> LockedFile; + int FD1; + ASSERT_NO_ERROR( + fs::createUniqueDirectory("TestLockFileExclusive", LockedFile)); + sys::path::append(LockedFile, "file"); + ASSERT_NO_ERROR( + fs::openFileForReadWrite(LockedFile, FD1, fs::CD_CreateNew, fs::OF_None)); + + std::string Executable = + sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); + StringRef argv[] = {Executable, + "--gtest_filter=ProgramEnvTest.TestLockFileExclusive"}; + + // Add LLVM_PROGRAM_TEST_LOCKED_FILE to the environment of the child. + std::string EnvVar = "LLVM_PROGRAM_TEST_LOCKED_FILE="; + EnvVar += LockedFile.str(); + addEnvVar(EnvVar); + + // Lock the file. + ASSERT_NO_ERROR( + fs::tryLockFile(FD1, std::chrono::seconds(0), fs::LockKind::Exclusive)); + + std::string Error; + bool ExecutionFailed; + ProcessInfo PI2 = ExecuteNoWait(Executable, argv, getEnviron(), {}, 0, &Error, + &ExecutionFailed); + ASSERT_FALSE(ExecutionFailed) << Error; + ASSERT_TRUE(Error.empty()); + ASSERT_NE(PI2.Pid, ProcessInfo::InvalidPid) << "Invalid process id"; + + std::string FinishFile = std::string(LockedFile) + "-finished"; + // Wait till child process writes the file to indicate the job finished. + bool Finished = false; + ExponentialBackoff Backoff(5s); // timeout 5s. + do { + if (fs::exists(FinishFile)) { + Finished = true; + break; + } + } while (Backoff.waitForNextAttempt()); + + ASSERT_TRUE(Finished); + ASSERT_NO_ERROR(fs::unlockFile(FD1)); + ProcessInfo WaitResult = llvm::sys::Wait(PI2, /*SecondsToWait=*/1, &Error); + ASSERT_TRUE(Error.empty()); + ASSERT_EQ(0, WaitResult.ReturnCode); + ASSERT_EQ(WaitResult.Pid, PI2.Pid); + sys::fs::remove(LockedFile); + sys::fs::remove(FinishFile); + sys::fs::remove_directories(sys::path::parent_path(LockedFile)); +} + TEST_F(ProgramEnvTest, TestExecuteWithNoStacktraceHandler) { using namespace llvm::sys; diff --git a/llvm/unittests/Support/ThreadPool.cpp b/llvm/unittests/Support/ThreadPool.cpp index 8dca182..aa7f874 100644 --- a/llvm/unittests/Support/ThreadPool.cpp +++ b/llvm/unittests/Support/ThreadPool.cpp @@ -140,7 +140,7 @@ TYPED_TEST(ThreadPoolTest, AsyncBarrier) { std::atomic_int checked_in{0}; - DefaultThreadPool Pool; + TypeParam Pool; for (size_t i = 0; i < 5; ++i) { Pool.async([this, &checked_in] { this->waitForMainThread(); @@ -453,15 +453,19 @@ TYPED_TEST(ThreadPoolTest, AffinityMask) { [](auto &T) { return T.getData().front() < 16UL; }) && "Threads ran on more CPUs than expected! The affinity mask does not " "seem to work."); - GTEST_SKIP(); + return; } std::string Executable = sys::fs::getMainExecutable(TestMainArgv0, &ThreadPoolTestStringArg1); - StringRef argv[] = {Executable, "--gtest_filter=ThreadPoolTest.AffinityMask"}; + const auto *TestInfo = testing::UnitTest::GetInstance()->current_test_info(); + std::string Arg = (Twine("--gtest_filter=") + TestInfo->test_suite_name() + + "." + TestInfo->name()) + .str(); + StringRef argv[] = {Executable, Arg}; // Add environment variable to the environment of the child process. int Res = setenv("LLVM_THREADPOOL_AFFINITYMASK", "1", false); - ASSERT_EQ(Res, 0); + ASSERT_EQ(0, Res); std::string Error; bool ExecutionFailed; @@ -470,6 +474,8 @@ TYPED_TEST(ThreadPoolTest, AffinityMask) { Affinity.set(0, 4); // Use CPUs 0,1,2,3. int Ret = sys::ExecuteAndWait(Executable, argv, {}, {}, 0, 0, &Error, &ExecutionFailed, nullptr, &Affinity); + Res = setenv("LLVM_THREADPOOL_AFFINITYMASK", "", false); + ASSERT_EQ(0, Res); ASSERT_EQ(0, Ret); } diff --git a/llvm/unittests/Support/raw_ostream_proxy_test.cpp b/llvm/unittests/Support/raw_ostream_proxy_test.cpp new file mode 100644 index 0000000..864dda7 --- /dev/null +++ b/llvm/unittests/Support/raw_ostream_proxy_test.cpp @@ -0,0 +1,225 @@ +//===- raw_ostream_proxy_test.cpp - Tests for raw ostream proxies ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/raw_ostream_proxy.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +/// Naive version of raw_svector_ostream that is buffered (by default) and +/// doesn't support pwrite. +class BufferedNoPwriteSmallVectorStream : public raw_ostream { +public: + // Choose a strange buffer size to ensure it doesn't collide with the default + // on \a raw_ostream. + static constexpr size_t PreferredBufferSize = 63; + + size_t preferred_buffer_size() const override { return PreferredBufferSize; } + uint64_t current_pos() const override { return Vector.size(); } + void write_impl(const char *Ptr, size_t Size) override { + Vector.append(Ptr, Ptr + Size); + } + + bool is_displayed() const override { return IsDisplayed; } + + explicit BufferedNoPwriteSmallVectorStream(SmallVectorImpl<char> &Vector) + : Vector(Vector) {} + ~BufferedNoPwriteSmallVectorStream() override { flush(); } + + SmallVectorImpl<char> &Vector; + bool IsDisplayed = false; +}; + +constexpr size_t BufferedNoPwriteSmallVectorStream::PreferredBufferSize; + +TEST(raw_ostream_proxyTest, write) { + // Besides confirming that "write" works, this test confirms that the proxy + // takes on the buffer from the stream it's proxying, such that writes to the + // proxy are flushed to the underlying stream as if no proxy were present. + SmallString<128> Dest; + { + // Confirm that BufferedNoPwriteSmallVectorStream is buffered by default, + // and that setting up a proxy effectively transfers a buffer of the same + // size to the proxy. + BufferedNoPwriteSmallVectorStream DestOS(Dest); + EXPECT_EQ(BufferedNoPwriteSmallVectorStream::PreferredBufferSize, + DestOS.GetBufferSize()); + raw_ostream_proxy ProxyOS(DestOS); + EXPECT_EQ(0u, DestOS.GetBufferSize()); + EXPECT_EQ(BufferedNoPwriteSmallVectorStream::PreferredBufferSize, + ProxyOS.GetBufferSize()); + + // Flushing should send through to Dest. + ProxyOS << "abcd"; + EXPECT_EQ("", Dest); + ProxyOS.flush(); + EXPECT_EQ("abcd", Dest); + + // Buffer should still work. + ProxyOS << "e"; + EXPECT_EQ("abcd", Dest); + } + + // Destructing ProxyOS should flush (and not crash). + EXPECT_EQ("abcde", Dest); + + { + // Set up another stream, this time unbuffered. + BufferedNoPwriteSmallVectorStream DestOS(Dest); + DestOS.SetUnbuffered(); + EXPECT_EQ(0u, DestOS.GetBufferSize()); + raw_ostream_proxy ProxyOS(DestOS); + EXPECT_EQ(0u, DestOS.GetBufferSize()); + EXPECT_EQ(0u, ProxyOS.GetBufferSize()); + + // Flushing should not be required. + ProxyOS << "f"; + EXPECT_EQ("abcdef", Dest); + } + EXPECT_EQ("abcdef", Dest); +} + +TEST(raw_ostream_proxyTest, pwrite) { + // This test confirms that the proxy takes on the buffer from the stream it's + // proxying, such that writes to the proxy are flushed to the underlying + // stream as if no proxy were present. + SmallString<128> Dest; + raw_svector_ostream DestOS(Dest); + raw_pwrite_stream_proxy ProxyOS(DestOS); + EXPECT_EQ(0u, ProxyOS.GetBufferSize()); + + // Get some initial data. + ProxyOS << "abcd"; + EXPECT_EQ("abcd", Dest); + + // Confirm that pwrite works. + ProxyOS.pwrite("BC", 2, 1); + EXPECT_EQ("aBCd", Dest); +} + +TEST(raw_ostream_proxyTest, pwriteWithBuffer) { + // This test confirms that when a buffer is configured, pwrite still works. + SmallString<128> Dest; + raw_svector_ostream DestOS(Dest); + DestOS.SetBufferSize(256); + EXPECT_EQ(256u, DestOS.GetBufferSize()); + + // Confirm that the proxy steals the buffer. + raw_pwrite_stream_proxy ProxyOS(DestOS); + EXPECT_EQ(0u, DestOS.GetBufferSize()); + EXPECT_EQ(256u, ProxyOS.GetBufferSize()); + + // Check that the buffer is working. + ProxyOS << "abcd"; + EXPECT_EQ("", Dest); + + // Confirm that pwrite flushes. + ProxyOS.pwrite("BC", 2, 1); + EXPECT_EQ("aBCd", Dest); +} + +class ProxyWithReset : public raw_ostream_proxy_adaptor<> { +public: + ProxyWithReset(raw_ostream &OS) : raw_ostream_proxy_adaptor<>(OS) {} + + // Allow this to be called outside the class. + using raw_ostream_proxy_adaptor<>::hasProxiedOS; + using raw_ostream_proxy_adaptor<>::getProxiedOS; + using raw_ostream_proxy_adaptor<>::resetProxiedOS; +}; + +TEST(raw_ostream_proxyTest, resetProxiedOS) { + // Confirm that base classes can drop the proxied OS before destruction and + // get consistent crashes. + SmallString<128> Dest; + BufferedNoPwriteSmallVectorStream DestOS(Dest); + ProxyWithReset ProxyOS(DestOS); + EXPECT_TRUE(ProxyOS.hasProxiedOS()); + EXPECT_EQ(&DestOS, &ProxyOS.getProxiedOS()); + + // Write some data. + ProxyOS << "abcd"; + EXPECT_EQ("", Dest); + + // Reset the underlying stream. + ProxyOS.resetProxiedOS(); + EXPECT_EQ("abcd", Dest); + EXPECT_EQ(0u, ProxyOS.GetBufferSize()); + EXPECT_FALSE(ProxyOS.hasProxiedOS()); + +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + EXPECT_DEATH(ProxyOS << "e", "use after reset"); + EXPECT_DEATH(ProxyOS.getProxiedOS(), "use after reset"); +#endif +} + +TEST(raw_ostream_proxyTest, ColorMode) { + { + SmallString<128> Dest; + BufferedNoPwriteSmallVectorStream DestOS(Dest); + DestOS.IsDisplayed = true; + raw_ostream_proxy ProxyOS(DestOS); + ProxyOS.enable_colors(true); + + WithColor(ProxyOS, raw_ostream::Colors::RED, /*Bold=*/true, /*BG=*/false, + ColorMode::Disable) + << "test"; + EXPECT_EQ("", Dest); + ProxyOS.flush(); + EXPECT_EQ("test", Dest); + } + + { + SmallString<128> Dest; + BufferedNoPwriteSmallVectorStream DestOS(Dest); + raw_ostream_proxy ProxyOS(DestOS); + + WithColor(ProxyOS, raw_ostream::Colors::RED, /*Bold=*/true, /*BG=*/false, + ColorMode::Auto) + << "test"; + EXPECT_EQ("", Dest); + ProxyOS.flush(); + EXPECT_EQ("test", Dest); + } + +#ifdef LLVM_ON_UNIX + { + SmallString<128> Dest; + BufferedNoPwriteSmallVectorStream DestOS(Dest); + raw_ostream_proxy ProxyOS(DestOS); + ProxyOS.enable_colors(true); + + WithColor(ProxyOS, raw_ostream::Colors::RED, /*Bold=*/true, /*BG=*/false, + ColorMode::Enable) + << "test"; + EXPECT_EQ("", Dest); + ProxyOS.flush(); + EXPECT_EQ("\x1B[0;1;31mtest\x1B[0m", Dest); + } + + { + SmallString<128> Dest; + BufferedNoPwriteSmallVectorStream DestOS(Dest); + DestOS.IsDisplayed = true; + raw_ostream_proxy ProxyOS(DestOS); + ProxyOS.enable_colors(true); + + WithColor(ProxyOS, HighlightColor::Error, ColorMode::Auto) << "test"; + EXPECT_EQ("", Dest); + ProxyOS.flush(); + EXPECT_EQ("\x1B[0;1;31mtest\x1B[0m", Dest); + } +#endif +} + +} // end namespace diff --git a/llvm/unittests/Target/AArch64/CMakeLists.txt b/llvm/unittests/Target/AArch64/CMakeLists.txt index 9387ca9..3875163 100644 --- a/llvm/unittests/Target/AArch64/CMakeLists.txt +++ b/llvm/unittests/Target/AArch64/CMakeLists.txt @@ -34,4 +34,5 @@ add_llvm_target_unittest(AArch64Tests AArch64SVESchedPseudoTest.cpp AArch64SelectionDAGTest.cpp Immediates.cpp + MCInstrAnalysisTest.cpp ) diff --git a/llvm/unittests/Target/AArch64/MCInstrAnalysisTest.cpp b/llvm/unittests/Target/AArch64/MCInstrAnalysisTest.cpp new file mode 100644 index 0000000..271d19e --- /dev/null +++ b/llvm/unittests/Target/AArch64/MCInstrAnalysisTest.cpp @@ -0,0 +1,191 @@ +//===- MCInstrAnalysisTest.cpp - AArch64MCInstrAnalysis unit tests --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCInstrAnalysis.h" +#include "MCTargetDesc/AArch64MCTargetDesc.h" +#include "Utils/AArch64BaseInfo.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" + +#include "gtest/gtest.h" + +#include <memory> + +using namespace llvm; + +namespace { + +class InstrAnalysisTest : public testing::TestWithParam<const char *> { +protected: + std::unique_ptr<const MCInstrInfo> Info; + std::unique_ptr<const MCInstrAnalysis> Analysis; + + static void SetUpTestSuite() { + LLVMInitializeAArch64TargetInfo(); + LLVMInitializeAArch64Target(); + LLVMInitializeAArch64TargetMC(); + } + + InstrAnalysisTest() { + std::string Error; + const Target *TheTarget = + TargetRegistry::lookupTarget(Triple::normalize(GetParam()), Error); + Info = std::unique_ptr<const MCInstrInfo>(TheTarget->createMCInstrInfo()); + Analysis = std::unique_ptr<const MCInstrAnalysis>( + TheTarget->createMCInstrAnalysis(Info.get())); + } +}; + +} // namespace + +static MCInst beq() { + return MCInstBuilder(AArch64::Bcc).addImm(AArch64CC::EQ).addReg(AArch64::X0); +} +static MCInst tbz(unsigned Rt = AArch64::X0, unsigned Imm = 0, + unsigned Label = 32) { + return MCInstBuilder(AArch64::TBZX).addReg(Rt).addImm(Imm).addImm(Label); +} +static MCInst cbz(unsigned Rt = AArch64::X0, unsigned Label = 32) { + return MCInstBuilder(AArch64::CBZX).addReg(Rt).addImm(Label); +} +static MCInst b() { return MCInstBuilder(AArch64::B).addImm(32); } +static MCInst bl() { return MCInstBuilder(AArch64::BL).addImm(32); } +static MCInst br(unsigned Rn = AArch64::X0) { + return MCInstBuilder(AArch64::BR).addReg(Rn); +} +static MCInst blr(unsigned Rn = AArch64::X0) { + return MCInstBuilder(AArch64::BLR).addReg(Rn); +} +static MCInst ret(unsigned Rn = AArch64::LR) { + return MCInstBuilder(AArch64::RET).addReg(Rn); +} +static MCInst retaa() { return MCInstBuilder(AArch64::RETAA); } +static MCInst eret() { return MCInstBuilder(AArch64::ERET); } +static MCInst hlt() { return MCInstBuilder(AArch64::HLT); } +static MCInst brk() { return MCInstBuilder(AArch64::BRK); } +static MCInst svc() { return MCInstBuilder(AArch64::SVC); } +static MCInst hvc() { return MCInstBuilder(AArch64::HVC); } +static MCInst smc() { return MCInstBuilder(AArch64::SMC); } + +TEST_P(InstrAnalysisTest, IsTerminator) { + EXPECT_TRUE(Analysis->isTerminator(beq())); + EXPECT_TRUE(Analysis->isTerminator(tbz())); + EXPECT_TRUE(Analysis->isTerminator(cbz())); + EXPECT_TRUE(Analysis->isTerminator(b())); + EXPECT_FALSE(Analysis->isTerminator(bl())); + EXPECT_FALSE(Analysis->isTerminator(blr())); + EXPECT_TRUE(Analysis->isTerminator(br())); + EXPECT_TRUE(Analysis->isTerminator(ret())); + EXPECT_TRUE(Analysis->isTerminator(retaa())); + EXPECT_TRUE(Analysis->isTerminator(eret())); + EXPECT_FALSE(Analysis->isTerminator(hlt())); + EXPECT_FALSE(Analysis->isTerminator(brk())); + EXPECT_FALSE(Analysis->isTerminator(svc())); + EXPECT_FALSE(Analysis->isTerminator(hvc())); + EXPECT_FALSE(Analysis->isTerminator(smc())); +} + +TEST_P(InstrAnalysisTest, IsBarrier) { + EXPECT_FALSE(Analysis->isBarrier(beq())); + EXPECT_FALSE(Analysis->isBarrier(tbz())); + EXPECT_FALSE(Analysis->isBarrier(cbz())); + EXPECT_TRUE(Analysis->isBarrier(b())); + EXPECT_FALSE(Analysis->isBarrier(bl())); + EXPECT_FALSE(Analysis->isBarrier(blr())); + EXPECT_TRUE(Analysis->isBarrier(br())); + EXPECT_TRUE(Analysis->isBarrier(ret())); + EXPECT_TRUE(Analysis->isBarrier(retaa())); + EXPECT_TRUE(Analysis->isBarrier(eret())); + EXPECT_FALSE(Analysis->isBarrier(hlt())); + EXPECT_FALSE(Analysis->isBarrier(brk())); + EXPECT_FALSE(Analysis->isBarrier(svc())); + EXPECT_FALSE(Analysis->isBarrier(hvc())); + EXPECT_FALSE(Analysis->isBarrier(smc())); +} + +TEST_P(InstrAnalysisTest, IsCall) { + EXPECT_FALSE(Analysis->isCall(beq())); + EXPECT_FALSE(Analysis->isCall(tbz())); + EXPECT_FALSE(Analysis->isCall(cbz())); + EXPECT_FALSE(Analysis->isCall(b())); + EXPECT_TRUE(Analysis->isCall(bl())); + EXPECT_TRUE(Analysis->isCall(blr())); + EXPECT_FALSE(Analysis->isCall(br())); + EXPECT_FALSE(Analysis->isCall(ret())); + EXPECT_FALSE(Analysis->isCall(retaa())); + EXPECT_FALSE(Analysis->isCall(eret())); +} + +TEST_P(InstrAnalysisTest, IsReturn) { + EXPECT_FALSE(Analysis->isReturn(beq())); + EXPECT_FALSE(Analysis->isReturn(tbz())); + EXPECT_FALSE(Analysis->isReturn(cbz())); + EXPECT_FALSE(Analysis->isReturn(b())); + EXPECT_FALSE(Analysis->isReturn(bl())); + EXPECT_FALSE(Analysis->isReturn(br())); + EXPECT_FALSE(Analysis->isReturn(blr())); + EXPECT_FALSE(Analysis->isReturn(br(AArch64::LR))); + EXPECT_TRUE(Analysis->isReturn(ret())); + EXPECT_TRUE(Analysis->isReturn(retaa())); + EXPECT_TRUE(Analysis->isReturn(eret())); +} + +TEST_P(InstrAnalysisTest, IsBranch) { + EXPECT_TRUE(Analysis->isBranch(beq())); + EXPECT_TRUE(Analysis->isBranch(tbz())); + EXPECT_TRUE(Analysis->isBranch(cbz())); + EXPECT_TRUE(Analysis->isBranch(b())); + EXPECT_FALSE(Analysis->isBranch(bl())); + EXPECT_FALSE(Analysis->isBranch(blr())); + EXPECT_TRUE(Analysis->isBranch(br())); + EXPECT_FALSE(Analysis->isBranch(ret())); + EXPECT_FALSE(Analysis->isBranch(retaa())); + EXPECT_FALSE(Analysis->isBranch(eret())); +} + +TEST_P(InstrAnalysisTest, IsConditionalBranch) { + EXPECT_TRUE(Analysis->isConditionalBranch(beq())); + EXPECT_TRUE(Analysis->isConditionalBranch(tbz())); + EXPECT_TRUE(Analysis->isConditionalBranch(cbz())); + EXPECT_FALSE(Analysis->isConditionalBranch(b())); + EXPECT_FALSE(Analysis->isConditionalBranch(bl())); + EXPECT_FALSE(Analysis->isConditionalBranch(blr())); + EXPECT_FALSE(Analysis->isConditionalBranch(ret())); + EXPECT_FALSE(Analysis->isConditionalBranch(retaa())); + EXPECT_FALSE(Analysis->isConditionalBranch(eret())); +} + +TEST_P(InstrAnalysisTest, IsUnconditionalBranch) { + EXPECT_FALSE(Analysis->isUnconditionalBranch(beq())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(tbz())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(cbz())); + EXPECT_TRUE(Analysis->isUnconditionalBranch(b())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(bl())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(blr())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(br())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(ret())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(retaa())); + EXPECT_FALSE(Analysis->isUnconditionalBranch(eret())); +} + +TEST_P(InstrAnalysisTest, IsIndirectBranch) { + EXPECT_FALSE(Analysis->isIndirectBranch(beq())); + EXPECT_FALSE(Analysis->isIndirectBranch(tbz())); + EXPECT_FALSE(Analysis->isIndirectBranch(cbz())); + EXPECT_FALSE(Analysis->isIndirectBranch(b())); + EXPECT_FALSE(Analysis->isIndirectBranch(bl())); + EXPECT_FALSE(Analysis->isIndirectBranch(blr())); + EXPECT_TRUE(Analysis->isIndirectBranch(br())); + EXPECT_FALSE(Analysis->isIndirectBranch(ret())); + EXPECT_FALSE(Analysis->isIndirectBranch(retaa())); + EXPECT_FALSE(Analysis->isIndirectBranch(eret())); +} + +INSTANTIATE_TEST_SUITE_P(AArch64, InstrAnalysisTest, + testing::Values("aarch64")); diff --git a/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp b/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp index f13252f..bd0e53c 100644 --- a/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp +++ b/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp @@ -78,7 +78,7 @@ TEST(SMEAttributes, Constructors) { "ret void\n}"); CallBase &Call = cast<CallBase>((CallModule->getFunction("foo")->begin()->front())); - ASSERT_TRUE(SMECallAttrs(Call).callsite().hasUndefZT0()); + ASSERT_TRUE(SMECallAttrs(Call, nullptr).callsite().hasUndefZT0()); // Invalid combinations. EXPECT_DEBUG_DEATH(SA(SA::SM_Enabled | SA::SM_Compatible), @@ -309,7 +309,7 @@ TEST(SMEAttributes, Transitions) { // Shared ZA -> Private ZA Interface ASSERT_FALSE(CA(ZA_Shared, Private_ZA).requiresDisablingZABeforeCall()); - ASSERT_TRUE(CA(ZA_Shared, Private_ZA).requiresEnablingZAAfterCall()); + ASSERT_FALSE(CA(ZA_Shared, Private_ZA).requiresEnablingZAAfterCall()); // Shared ZT0 -> Private ZA Interface ASSERT_TRUE(CA(ZT0_Shared, Private_ZA).requiresDisablingZABeforeCall()); @@ -328,7 +328,7 @@ TEST(SMEAttributes, Transitions) { // Shared ZA & ZT0 -> Private ZA Interface ASSERT_FALSE(CA(ZA_ZT0_Shared, Private_ZA).requiresDisablingZABeforeCall()); ASSERT_TRUE(CA(ZA_ZT0_Shared, Private_ZA).requiresPreservingZT0()); - ASSERT_TRUE(CA(ZA_ZT0_Shared, Private_ZA).requiresEnablingZAAfterCall()); + ASSERT_FALSE(CA(ZA_ZT0_Shared, Private_ZA).requiresEnablingZAAfterCall()); // Shared ZA -> Shared ZA Interface ASSERT_FALSE(CA(ZA_Shared, ZT0_Shared).requiresDisablingZABeforeCall()); diff --git a/llvm/unittests/Target/AMDGPU/AMDGPUUnitTests.cpp b/llvm/unittests/Target/AMDGPU/AMDGPUUnitTests.cpp index ac08501..593991c 100644 --- a/llvm/unittests/Target/AMDGPU/AMDGPUUnitTests.cpp +++ b/llvm/unittests/Target/AMDGPU/AMDGPUUnitTests.cpp @@ -320,3 +320,23 @@ TEST(AMDGPU, TestReverseComposeSubRegIndices) { } } } + +TEST(AMDGPU, TestGetNamedOperandIdx) { + std::unique_ptr<const GCNTargetMachine> TM = + createAMDGPUTargetMachine("amdgcn-amd-", "gfx900", ""); + if (!TM) + return; + const MCInstrInfo *MCII = TM->getMCInstrInfo(); + + for (unsigned Opcode = 0, E = MCII->getNumOpcodes(); Opcode != E; ++Opcode) { + const MCInstrDesc &Desc = MCII->get(Opcode); + for (unsigned Idx = 0; Idx < Desc.getNumOperands(); ++Idx) { + AMDGPU::OpName OpName = AMDGPU::getOperandIdxName(Opcode, Idx); + if (OpName == AMDGPU::OpName::NUM_OPERAND_NAMES) + continue; + int16_t RetrievedIdx = AMDGPU::getNamedOperandIdx(Opcode, OpName); + EXPECT_EQ(Idx, static_cast<unsigned>(RetrievedIdx)) + << "Opcode " << Opcode << " (" << MCII->getName(Opcode) << ')'; + } + } +} diff --git a/llvm/unittests/Target/AMDGPU/CSETest.cpp b/llvm/unittests/Target/AMDGPU/CSETest.cpp index 3de5b88..ff44ff1 100644 --- a/llvm/unittests/Target/AMDGPU/CSETest.cpp +++ b/llvm/unittests/Target/AMDGPU/CSETest.cpp @@ -10,6 +10,7 @@ #include "AMDGPUUnitTests.h" #include "llvm/CodeGen/GlobalISel/CSEInfo.h" #include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "gtest/gtest.h" using namespace llvm; diff --git a/llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp b/llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp index 9d41e94..6ae139e 100644 --- a/llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp +++ b/llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp @@ -8,6 +8,7 @@ #include "DirectXIRPasses/PointerTypeAnalysis.h" #include "llvm/AsmParser/Parser.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" @@ -123,6 +124,33 @@ TEST(PointerTypeAnalysis, DiscoverGEP) { EXPECT_THAT(Map, Contains(Pair(IsA<GetElementPtrInst>(), I64Ptr))); } +TEST(PointerTypeAnalysis, DiscoverConstantExprGEP) { + StringRef Assembly = R"( + @g = internal global [10 x i32] zeroinitializer + define i32 @test() { + %i = load i32, ptr getelementptr ([10 x i32], ptr @g, i64 0, i64 1) + ret i32 %i + } + )"; + + LLVMContext Context; + SMDiagnostic Error; + auto M = parseAssemblyString(Assembly, Error, Context); + ASSERT_TRUE(M) << "Bad assembly?"; + + PointerTypeMap Map = PointerTypeAnalysis::run(*M); + ASSERT_EQ(Map.size(), 3u); + Type *I32Ty = Type::getInt32Ty(Context); + Type *I32Ptr = TypedPointerType::get(I32Ty, 0); + Type *I32ArrPtr = TypedPointerType::get(ArrayType::get(I32Ty, 10), 0); + Type *FnTy = FunctionType::get(I32Ty, {}, false); + + EXPECT_THAT(Map, Contains(Pair(IsA<GlobalVariable>(), I32ArrPtr))); + EXPECT_THAT(Map, + Contains(Pair(IsA<Function>(), TypedPointerType::get(FnTy, 0)))); + EXPECT_THAT(Map, Contains(Pair(IsA<ConstantExpr>(), I32Ptr))); +} + TEST(PointerTypeAnalysis, TraceIndirect) { StringRef Assembly = R"( define i64 @test(ptr %p) { diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp index d4715be..3211c2c 100644 --- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp +++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp @@ -51,7 +51,7 @@ TEST_F(ResourceBindingAnalysisTest, TestTrivialCase) { StringRef Assembly = R"( define void @main() { entry: - %handle = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr null) + %handle = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, ptr null) ret void } )"; @@ -71,8 +71,8 @@ TEST_F(ResourceBindingAnalysisTest, TestOverlap) { StringRef Assembly = R"( define void @main() { entry: - %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false, ptr null) - %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false, ptr null) + %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, ptr null) + %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, ptr null) ret void } )"; @@ -94,8 +94,8 @@ TEST_F(ResourceBindingAnalysisTest, TestExactOverlap) { @B.str = private unnamed_addr constant [2 x i8] c"B\00", align 1 define void @main() { entry: - %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr @A.str) - %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr @B.str) + %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, ptr @A.str) + %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, ptr @B.str) ret void } )"; @@ -115,8 +115,8 @@ TEST_F(ResourceBindingAnalysisTest, TestImplicitFlag) { StringRef Assembly = R"( define void @main() { entry: - %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 100, i32 5, i32 1, i32 0, i1 false, ptr null) - %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 0, i32 0, i32 1, i32 0, i1 false, ptr null) + %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 100, i32 5, i32 1, i32 0, ptr null) + %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 0, i32 0, i32 1, i32 0, ptr null) ret void } )"; diff --git a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp index 160a915..5b54c13 100644 --- a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp +++ b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp @@ -59,7 +59,7 @@ TEST_F(UniqueResourceFromUseTest, TestResourceCounterDecrement) { StringRef Assembly = R"( define void @main() { entry: - %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false, ptr null) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, ptr null) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) @@ -89,7 +89,7 @@ TEST_F(UniqueResourceFromUseTest, TestResourceCounterIncrement) { StringRef Assembly = R"( define void @main() { entry: - %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false, ptr null) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, ptr null) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) @@ -119,7 +119,7 @@ TEST_F(UniqueResourceFromUseTest, TestResourceCounterUnknown) { StringRef Assembly = R"( define void @main() { entry: - %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false, ptr null) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, ptr null) ret void } )"; @@ -146,8 +146,8 @@ TEST_F(UniqueResourceFromUseTest, TestResourceCounterMultiple) { StringRef Assembly = R"( define void @main() { entry: - %handle1 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false, ptr null) - %handle2 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 3, i32 2, i32 1, i1 false, ptr null) + %handle1 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, ptr null) + %handle2 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 3, i32 2, i32 1, ptr null) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle1, i8 -1) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle2, i8 1) ret void @@ -182,7 +182,7 @@ TEST_F(UniqueResourceFromUseTest, TestResourceCounterInvalid) { StringRef Assembly = R"( define void @main() { entry: - %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false, ptr null) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, ptr null) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) ret void diff --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp index 319538e..d10dcb6 100644 --- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp +++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp @@ -1099,6 +1099,7 @@ R"(All available -march extensions for RISC-V smcdeleg 1.0 smcntrpmf 1.0 smcsrind 1.0 + smctr 1.0 smdbltrp 1.0 smepmp 1.0 smmpm 1.0 @@ -1111,6 +1112,7 @@ R"(All available -march extensions for RISC-V sscofpmf 1.0 sscounterenw 1.0 sscsrind 1.0 + ssctr 1.0 ssdbltrp 1.0 ssnpm 1.0 sspm 1.0 @@ -1144,6 +1146,7 @@ R"(All available -march extensions for RISC-V xcvsimd 1.0 xmipscbop 1.0 xmipscmov 1.0 + xmipsexectl 1.0 xmipslsp 1.0 xsfcease 1.0 xsfmm128t 0.6 @@ -1163,6 +1166,7 @@ R"(All available -march extensions for RISC-V xsfvqmaccqoq 1.0 xsifivecdiscarddlone 1.0 xsifivecflushdlone 1.0 + xsmtvdot 1.0 xtheadba 1.0 xtheadbb 1.0 xtheadbs 1.0 @@ -1178,15 +1182,14 @@ R"(All available -march extensions for RISC-V xwchc 2.2 Experimental extensions - p 0.14 + p 0.15 zicfilp 1.0 This is a long dummy description zicfiss 1.0 zalasr 0.1 zvbc32e 0.7 + zvfbfa 0.1 zvkgs 0.7 zvqdotq 0.0 - smctr 1.0 - ssctr 1.0 svukte 0.3 xqccmp 0.3 xqcia 0.7 diff --git a/llvm/unittests/Transforms/IPO/FunctionSpecializationTest.cpp b/llvm/unittests/Transforms/IPO/FunctionSpecializationTest.cpp index 9f76e9f..49373d3 100644 --- a/llvm/unittests/Transforms/IPO/FunctionSpecializationTest.cpp +++ b/llvm/unittests/Transforms/IPO/FunctionSpecializationTest.cpp @@ -27,12 +27,11 @@ namespace llvm { static void removeSSACopy(Function &F) { for (BasicBlock &BB : F) { for (Instruction &Inst : llvm::make_early_inc_range(BB)) { - if (auto *II = dyn_cast<IntrinsicInst>(&Inst)) { - if (II->getIntrinsicID() != Intrinsic::ssa_copy) - continue; - Inst.replaceAllUsesWith(II->getOperand(0)); - Inst.eraseFromParent(); - } + auto *BC = dyn_cast<BitCastInst>(&Inst); + if (!BC || BC->getType() != BC->getOperand(0)->getType()) + continue; + Inst.replaceAllUsesWith(BC->getOperand(0)); + Inst.eraseFromParent(); } } } diff --git a/llvm/unittests/Transforms/Utils/LocalTest.cpp b/llvm/unittests/Transforms/Utils/LocalTest.cpp index 0c70feb..4b53cc3 100644 --- a/llvm/unittests/Transforms/Utils/LocalTest.cpp +++ b/llvm/unittests/Transforms/Utils/LocalTest.cpp @@ -1057,6 +1057,14 @@ TEST(Local, SimplifyCFGWithNullAC) { RequireAndPreserveDomTree ? &DTU : nullptr, Options)); } +TEST(LocalTest, TargetTypeInfoHasNoReplacementProperty) { + LLVMContext Ctx; + SmallVector<unsigned, 3> Ints = {}; + auto *TT = llvm::TargetExtType::get(Ctx, "dx.RawBuffer", {}, Ints); + + EXPECT_TRUE(TT->hasProperty(TargetExtType::Property::IsTokenLike)); +} + TEST(Local, CanReplaceOperandWithVariable) { LLVMContext Ctx; Module M("test_module", Ctx); diff --git a/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/SeedCollectorTest.cpp b/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/SeedCollectorTest.cpp index 7f9a59b..31b4a5e 100644 --- a/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/SeedCollectorTest.cpp +++ b/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/SeedCollectorTest.cpp @@ -259,10 +259,10 @@ bb: // Check begin() end() when empty. EXPECT_EQ(SC.begin(), SC.end()); - SC.insert(S0); - SC.insert(S1); - SC.insert(S2); - SC.insert(S3); + SC.insert(S0, /*AllowDiffTypes=*/false); + SC.insert(S1, /*AllowDiffTypes=*/false); + SC.insert(S2, /*AllowDiffTypes=*/false); + SC.insert(S3, /*AllowDiffTypes=*/false); unsigned Cnt = 0; SmallVector<sandboxir::SeedBundle *> Bndls; for (auto &SeedBndl : SC) { @@ -480,6 +480,45 @@ bb: ExpectThatElementsAre(SB, {St0, St1, St3}); } +TEST_F(SeedBundleTest, DiffTypes) { + parseIR(C, R"IR( +define void @foo(ptr noalias %ptr, i8 %v, i16 %v16) { +bb: + %ptr0 = getelementptr i8, ptr %ptr, i32 0 + %ptr1 = getelementptr i8, ptr %ptr, i32 1 + %ptr3 = getelementptr i8, ptr %ptr, i32 3 + store i8 %v, ptr %ptr0 + store i8 %v, ptr %ptr3 + store i16 %v16, ptr %ptr1 + ret void +} +)IR"); + Function &LLVMF = *M->getFunction("foo"); + DominatorTree DT(LLVMF); + TargetLibraryInfoImpl TLII(M->getTargetTriple()); + TargetLibraryInfo TLI(TLII); + DataLayout DL(M->getDataLayout()); + LoopInfo LI(DT); + AssumptionCache AC(LLVMF); + ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); + + sandboxir::Context Ctx(C); + auto &F = *Ctx.createFunction(&LLVMF); + auto BB = F.begin(); + auto It = std::next(BB->begin(), 3); + auto *St0 = &*It++; + auto *St3 = &*It++; + auto *St1 = &*It++; + + sandboxir::SeedCollector SC(&*BB, SE, /*CollectStores=*/true, + /*CollectLoads=*/false, /*AllowDiffTypes=*/true); + + auto StoreSeedsRange = SC.getStoreSeeds(); + EXPECT_EQ(range_size(StoreSeedsRange), 1u); + auto &SB = *StoreSeedsRange.begin(); + ExpectThatElementsAre(SB, {St0, St1, St3}); +} + TEST_F(SeedBundleTest, VectorLoads) { parseIR(C, R"IR( define void @foo(ptr noalias %ptr, <2 x float> %val0) { diff --git a/llvm/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp index e7439397..a943e7ac 100644 --- a/llvm/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp +++ b/llvm/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp @@ -203,7 +203,7 @@ TEST_F(VPlanHCFGTest, testVPInstructionToVPRecipesInner) { VPInstruction::BranchOnCond, {Plan->getOrAddLiveIn(ConstantInt::getTrue(F->getContext()))})); VPlanTransforms::tryToConvertVPInstructionsToVPRecipes( - Plan, [](PHINode *P) { return nullptr; }, *SE, TLI); + Plan, [](PHINode *P) { return nullptr; }, TLI); VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock(); EXPECT_EQ(0u, Entry->getNumPredecessors()); diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp index 94b74d2..db64c75 100644 --- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp +++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp @@ -1088,7 +1088,7 @@ TEST_F(VPRecipeTest, CastVPInterleaveRecipeToVPUser) { VPValue *Addr = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 1)); VPValue *Mask = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 2)); InterleaveGroup<Instruction> IG(4, false, Align(4)); - VPInterleaveRecipe Recipe(&IG, Addr, {}, Mask, false, DebugLoc()); + VPInterleaveRecipe Recipe(&IG, Addr, {}, Mask, false, {}, DebugLoc()); EXPECT_TRUE(isa<VPUser>(&Recipe)); VPRecipeBase *BaseR = &Recipe; EXPECT_TRUE(isa<VPUser>(BaseR)); diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h b/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h index 7dfd11a..383f79b 100644 --- a/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h +++ b/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h @@ -72,10 +72,12 @@ protected: Loop *L = LI->getLoopFor(LoopHeader); PredicatedScalarEvolution PSE(*SE, *L); - auto Plan = VPlanTransforms::buildPlainCFG(L, *LI); - VFRange R(ElementCount::getFixed(1), ElementCount::getFixed(2)); - VPlanTransforms::prepareForVectorization(*Plan, IntegerType::get(*Ctx, 64), - PSE, true, false, L, {}, false, R); + auto Plan = VPlanTransforms::buildVPlan0(L, *LI, IntegerType::get(*Ctx, 64), + {}, PSE); + + VPlanTransforms::handleEarlyExits(*Plan, false); + VPlanTransforms::addMiddleCheck(*Plan, true, false); + VPlanTransforms::createLoopRegions(*Plan); return Plan; } diff --git a/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp index b698b28..c2f045b 100644 --- a/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp +++ b/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp @@ -170,15 +170,14 @@ TEST_F(VPVerifierTest, VPPhiIncomingValueDoesntDominateIncomingBlock) { EXPECT_FALSE(verifyVPlanIsValid(Plan)); #if GTEST_HAS_STREAM_REDIRECTION #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - EXPECT_STREQ("Incoming def at index 0 does not dominate incoming block!\n" + EXPECT_STREQ("Incoming def does not dominate incoming block!\n" " EMIT vp<%2> = add ir<0>\n" " does not dominate preheader for\n" " EMIT-SCALAR vp<%1> = phi [ vp<%2>, preheader ]", ::testing::internal::GetCapturedStderr().c_str()); #else - EXPECT_STREQ("Incoming def at index 0 does not dominate incoming block!\n", :: - testing::internal::GetCapturedStderr() - .c_str()); + EXPECT_STREQ("Incoming def does not dominate incoming block!\n", + ::testing::internal::GetCapturedStderr().c_str()); #endif #endif } |