diff options
Diffstat (limited to 'unittests')
88 files changed, 8149 insertions, 1320 deletions
diff --git a/unittests/ADT/APFloatTest.cpp b/unittests/ADT/APFloatTest.cpp index 83fb213109aa2..378c48d7e0a6b 100644 --- a/unittests/ADT/APFloatTest.cpp +++ b/unittests/ADT/APFloatTest.cpp @@ -9,6 +9,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" @@ -2829,6 +2830,28 @@ TEST(APFloatTest, abs) { EXPECT_TRUE(PSmallestNormalized.bitwiseIsEqual(abs(MSmallestNormalized))); } +TEST(APFloatTest, neg) { + APFloat One = APFloat(APFloat::IEEEsingle(), "1.0"); + APFloat NegOne = APFloat(APFloat::IEEEsingle(), "-1.0"); + APFloat Zero = APFloat::getZero(APFloat::IEEEsingle(), false); + APFloat NegZero = APFloat::getZero(APFloat::IEEEsingle(), true); + APFloat Inf = APFloat::getInf(APFloat::IEEEsingle(), false); + APFloat NegInf = APFloat::getInf(APFloat::IEEEsingle(), true); + APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false); + APFloat NegQNaN = APFloat::getNaN(APFloat::IEEEsingle(), true); + + EXPECT_TRUE(NegOne.bitwiseIsEqual(neg(One))); + EXPECT_TRUE(One.bitwiseIsEqual(neg(NegOne))); + EXPECT_TRUE(NegZero.bitwiseIsEqual(neg(Zero))); + EXPECT_TRUE(Zero.bitwiseIsEqual(neg(NegZero))); + EXPECT_TRUE(NegInf.bitwiseIsEqual(neg(Inf))); + EXPECT_TRUE(Inf.bitwiseIsEqual(neg(NegInf))); + EXPECT_TRUE(NegInf.bitwiseIsEqual(neg(Inf))); + EXPECT_TRUE(Inf.bitwiseIsEqual(neg(NegInf))); + EXPECT_TRUE(NegQNaN.bitwiseIsEqual(neg(QNaN))); + EXPECT_TRUE(QNaN.bitwiseIsEqual(neg(NegQNaN))); +} + TEST(APFloatTest, ilogb) { EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), false))); EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), true))); @@ -3169,6 +3192,70 @@ TEST(APFloatTest, frexp) { EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac)); } +TEST(APFloatTest, mod) { + { + APFloat f1(APFloat::IEEEdouble(), "1.5"); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + APFloat expected(APFloat::IEEEdouble(), "0.5"); + EXPECT_EQ(f1.mod(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0.5"); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + APFloat expected(APFloat::IEEEdouble(), "0.5"); + EXPECT_EQ(f1.mod(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0x1.3333333333333p-2"); // 0.3 + APFloat f2(APFloat::IEEEdouble(), "0x1.47ae147ae147bp-7"); // 0.01 + APFloat expected(APFloat::IEEEdouble(), + "0x1.47ae147ae1471p-7"); // 0.009999999999999983 + EXPECT_EQ(f1.mod(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0x1p64"); // 1.8446744073709552e19 + APFloat f2(APFloat::IEEEdouble(), "1.5"); + APFloat expected(APFloat::IEEEdouble(), "1.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0x1p1000"); + APFloat f2(APFloat::IEEEdouble(), "0x1p-1000"); + APFloat expected(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0.0"); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + APFloat expected(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "1.0"); + APFloat f2(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } + { + APFloat f1(APFloat::IEEEdouble(), "0.0"); + APFloat f2(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } + { + APFloat f1 = APFloat::getInf(APFloat::IEEEdouble(), false); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } +} + TEST(APFloatTest, PPCDoubleDoubleAddSpecial) { using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, APFloat::fltCategory, APFloat::roundingMode>; @@ -3181,7 +3268,7 @@ TEST(APFloatTest, PPCDoubleDoubleAddSpecial) { 0x7948000000000000ull, 0ull, APFloat::fcInfinity, APFloat::rmNearestTiesToEven), // TODO: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when - // PPCDoubleDoubleImpl is gone. + // semPPCDoubleDoubleLegacy is gone. // LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 - // 160))) = fcNormal std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull, @@ -3202,14 +3289,26 @@ TEST(APFloatTest, PPCDoubleDoubleAddSpecial) { APFloat::roundingMode RM; std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected, RM) = Tp; - APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); - APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); - A1.add(A2, RM); + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A1.add(A2, RM); - EXPECT_EQ(Expected, A1.getCategory()) - << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], - Op2[1]) - .str(); + EXPECT_EQ(Expected, A1.getCategory()) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], + Op2[0], Op2[1]) + .str(); + } + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A2.add(A1, RM); + + EXPECT_EQ(Expected, A2.getCategory()) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op2[0], Op2[1], + Op1[0], Op1[1]) + .str(); + } } } @@ -3234,14 +3333,14 @@ TEST(APFloatTest, PPCDoubleDoubleAdd) { 0x3ff0000000000000ull, 0x0000000000000001ull, APFloat::rmNearestTiesToEven), // TODO: change 0xf950000000000000 to 0xf940000000000000, when - // PPCDoubleDoubleImpl is gone. + // semPPCDoubleDoubleLegacy is gone. // (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX + // 1.11111... << (1023 - 52) std::make_tuple(0x7fefffffffffffffull, 0xf950000000000000ull, 0x7c90000000000000ull, 0, 0x7fefffffffffffffull, 0x7c8ffffffffffffeull, APFloat::rmNearestTiesToEven), // TODO: change 0xf950000000000000 to 0xf940000000000000, when - // PPCDoubleDoubleImpl is gone. + // semPPCDoubleDoubleLegacy is gone. // (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX + // 1.11111... << (1023 - 52) std::make_tuple(0x7c90000000000000ull, 0, 0x7fefffffffffffffull, @@ -3254,18 +3353,34 @@ TEST(APFloatTest, PPCDoubleDoubleAdd) { APFloat::roundingMode RM; std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp; - APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); - APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); - A1.add(A2, RM); - - EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0]) - << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], - Op2[1]) - .str(); - EXPECT_EQ(Expected[1], A1.getSecondFloat().bitcastToAPInt().getRawData()[0]) - << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], - Op2[1]) - .str(); + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A1.add(A2, RM); + + EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], + Op2[0], Op2[1]) + .str(); + EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1]) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], + Op2[0], Op2[1]) + .str(); + } + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A2.add(A1, RM); + + EXPECT_EQ(Expected[0], A2.bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op2[0], Op2[1], + Op1[0], Op1[1]) + .str(); + EXPECT_EQ(Expected[1], A2.bitcastToAPInt().getRawData()[1]) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op2[0], Op2[1], + Op1[0], Op1[1]) + .str(); + } } } @@ -3296,23 +3411,118 @@ TEST(APFloatTest, PPCDoubleDoubleSubtract) { << formatv("({0:x} + {1:x}) - ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], Op2[1]) .str(); - EXPECT_EQ(Expected[1], A1.getSecondFloat().bitcastToAPInt().getRawData()[0]) + EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1]) << formatv("({0:x} + {1:x}) - ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], Op2[1]) .str(); } } +TEST(APFloatTest, PPCDoubleDoubleMultiplySpecial) { + using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, + APFloat::fltCategory, APFloat::roundingMode>; + DataType Data[] = { + // fcNaN * fcNaN = fcNaN + std::make_tuple(0x7ff8000000000000ull, 0, 0x7ff8000000000000ull, 0, + APFloat::fcNaN, APFloat::rmNearestTiesToEven), + // fcNaN * fcZero = fcNaN + std::make_tuple(0x7ff8000000000000ull, 0, 0, 0, APFloat::fcNaN, + APFloat::rmNearestTiesToEven), + // fcNaN * fcInfinity = fcNaN + std::make_tuple(0x7ff8000000000000ull, 0, 0x7ff0000000000000ull, 0, + APFloat::fcNaN, APFloat::rmNearestTiesToEven), + // fcNaN * fcNormal = fcNaN + std::make_tuple(0x7ff8000000000000ull, 0, 0x3ff0000000000000ull, 0, + APFloat::fcNaN, APFloat::rmNearestTiesToEven), + // fcInfinity * fcInfinity = fcInfinity + std::make_tuple(0x7ff0000000000000ull, 0, 0x7ff0000000000000ull, 0, + APFloat::fcInfinity, APFloat::rmNearestTiesToEven), + // fcInfinity * fcZero = fcNaN + std::make_tuple(0x7ff0000000000000ull, 0, 0, 0, APFloat::fcNaN, + APFloat::rmNearestTiesToEven), + // fcInfinity * fcNormal = fcInfinity + std::make_tuple(0x7ff0000000000000ull, 0, 0x3ff0000000000000ull, 0, + APFloat::fcInfinity, APFloat::rmNearestTiesToEven), + // fcZero * fcZero = fcZero + std::make_tuple(0, 0, 0, 0, APFloat::fcZero, + APFloat::rmNearestTiesToEven), + // fcZero * fcNormal = fcZero + std::make_tuple(0, 0, 0x3ff0000000000000ull, 0, APFloat::fcZero, + APFloat::rmNearestTiesToEven), + }; + + for (auto Tp : Data) { + uint64_t Op1[2], Op2[2]; + APFloat::fltCategory Expected; + APFloat::roundingMode RM; + std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected, RM) = Tp; + + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A1.multiply(A2, RM); + + EXPECT_EQ(Expected, A1.getCategory()) + << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1], + Op2[0], Op2[1]) + .str(); + } + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A2.multiply(A1, RM); + + EXPECT_EQ(Expected, A2.getCategory()) + << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op2[0], Op2[1], + Op1[0], Op1[1]) + .str(); + } + } +} + TEST(APFloatTest, PPCDoubleDoubleMultiply) { using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, APFloat::roundingMode>; - // TODO: Only a sanity check for now. Add more edge cases when the - // double-double algorithm is implemented. DataType Data[] = { // 1/3 * 3 = 1.0 std::make_tuple(0x3fd5555555555555ull, 0x3c75555555555556ull, 0x4008000000000000ull, 0, 0x3ff0000000000000ull, 0, APFloat::rmNearestTiesToEven), + // (1 + epsilon) * (1 + 0) = fcZero + std::make_tuple(0x3ff0000000000000ull, 0x0000000000000001ull, + 0x3ff0000000000000ull, 0, 0x3ff0000000000000ull, + 0x0000000000000001ull, APFloat::rmNearestTiesToEven), + // (1 + epsilon) * (1 + epsilon) = 1 + 2 * epsilon + std::make_tuple(0x3ff0000000000000ull, 0x0000000000000001ull, + 0x3ff0000000000000ull, 0x0000000000000001ull, + 0x3ff0000000000000ull, 0x0000000000000002ull, + APFloat::rmNearestTiesToEven), + // -(1 + epsilon) * (1 + epsilon) = -1 + std::make_tuple(0xbff0000000000000ull, 0x0000000000000001ull, + 0x3ff0000000000000ull, 0x0000000000000001ull, + 0xbff0000000000000ull, 0, APFloat::rmNearestTiesToEven), + // (0.5 + 0) * (1 + 2 * epsilon) = 0.5 + epsilon + std::make_tuple(0x3fe0000000000000ull, 0, 0x3ff0000000000000ull, + 0x0000000000000002ull, 0x3fe0000000000000ull, + 0x0000000000000001ull, APFloat::rmNearestTiesToEven), + // (0.5 + 0) * (1 + epsilon) = 0.5 + std::make_tuple(0x3fe0000000000000ull, 0, 0x3ff0000000000000ull, + 0x0000000000000001ull, 0x3fe0000000000000ull, 0, + APFloat::rmNearestTiesToEven), + // __LDBL_MAX__ * (1 + 1 << 106) = inf + std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + 0x3ff0000000000000ull, 0x3950000000000000ull, + 0x7ff0000000000000ull, 0, APFloat::rmNearestTiesToEven), + // __LDBL_MAX__ * (1 + 1 << 107) > __LDBL_MAX__, but not inf, yes =_=||| + std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + 0x3ff0000000000000ull, 0x3940000000000000ull, + 0x7fefffffffffffffull, 0x7c8fffffffffffffull, + APFloat::rmNearestTiesToEven), + // __LDBL_MAX__ * (1 + 1 << 108) = __LDBL_MAX__ + std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + 0x3ff0000000000000ull, 0x3930000000000000ull, + 0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + APFloat::rmNearestTiesToEven), }; for (auto Tp : Data) { @@ -3320,18 +3530,34 @@ TEST(APFloatTest, PPCDoubleDoubleMultiply) { APFloat::roundingMode RM; std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp; - APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); - APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); - A1.multiply(A2, RM); - - EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0]) - << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], - Op2[1]) - .str(); - EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1]) - << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], - Op2[1]) - .str(); + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A1.multiply(A2, RM); + + EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1], + Op2[0], Op2[1]) + .str(); + EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1]) + << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1], + Op2[0], Op2[1]) + .str(); + } + { + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A2.multiply(A1, RM); + + EXPECT_EQ(Expected[0], A2.bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op2[0], Op2[1], + Op1[0], Op1[1]) + .str(); + EXPECT_EQ(Expected[1], A2.bitcastToAPInt().getRawData()[1]) + << formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op2[0], Op2[1], + Op1[0], Op1[1]) + .str(); + } } } @@ -3496,12 +3722,53 @@ TEST(APFloatTest, PPCDoubleDoubleCompare) { APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); EXPECT_EQ(Expected, A1.compare(A2)) - << formatv("({0:x} + {1:x}) - ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], + << formatv("compare(({0:x} + {1:x}), ({2:x} + {3:x}))", Op1[0], Op1[1], + Op2[0], Op2[1]) + .str(); + } +} + +TEST(APFloatTest, PPCDoubleDoubleBitwiseIsEqual) { + using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, bool>; + + DataType Data[] = { + // (1 + 0) = (1 + 0) + std::make_tuple(0x3ff0000000000000ull, 0, 0x3ff0000000000000ull, 0, true), + // (1 + 0) != (1.00...1 + 0) + std::make_tuple(0x3ff0000000000000ull, 0, 0x3ff0000000000001ull, 0, + false), + // NaN = NaN + std::make_tuple(0x7ff8000000000000ull, 0, 0x7ff8000000000000ull, 0, true), + // NaN != NaN with a different bit pattern + std::make_tuple(0x7ff8000000000000ull, 0, 0x7ff8000000000000ull, + 0x3ff0000000000000ull, false), + // Inf = Inf + std::make_tuple(0x7ff0000000000000ull, 0, 0x7ff0000000000000ull, 0, true), + }; + + for (auto Tp : Data) { + uint64_t Op1[2], Op2[2]; + bool Expected; + std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected) = Tp; + + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + EXPECT_EQ(Expected, A1.bitwiseIsEqual(A2)) + << formatv("({0:x} + {1:x}) = ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], Op2[1]) .str(); } } +TEST(APFloatTest, PPCDoubleDoubleHashValue) { + uint64_t Data1[] = {0x3ff0000000000001ull, 0x0000000000000001ull}; + uint64_t Data2[] = {0x3ff0000000000001ull, 0}; + // The hash values are *hopefully* different. + EXPECT_NE( + hash_value(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Data1))), + hash_value(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Data2)))); +} + TEST(APFloatTest, PPCDoubleDoubleChangeSign) { uint64_t Data[] = { 0x400f000000000000ull, 0xbcb0000000000000ull, @@ -3531,6 +3798,13 @@ TEST(APFloatTest, PPCDoubleDoubleFactories) { } { uint64_t Data[] = { + 0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + }; + EXPECT_EQ(APInt(128, 2, Data), + APFloat::getLargest(APFloat::PPCDoubleDouble()).bitcastToAPInt()); + } + { + uint64_t Data[] = { 0x0000000000000001ull, 0, }; EXPECT_EQ( @@ -3553,24 +3827,72 @@ TEST(APFloatTest, PPCDoubleDoubleFactories) { } { uint64_t Data[] = { + 0xffefffffffffffffull, 0xfc8ffffffffffffeull, + }; + EXPECT_EQ( + APInt(128, 2, Data), + APFloat::getLargest(APFloat::PPCDoubleDouble(), true).bitcastToAPInt()); + } + { + uint64_t Data[] = { 0x8000000000000001ull, 0x0000000000000000ull, }; EXPECT_EQ(APInt(128, 2, Data), APFloat::getSmallest(APFloat::PPCDoubleDouble(), true) .bitcastToAPInt()); } - - EXPECT_EQ(0x8360000000000000ull, - APFloat::getSmallestNormalized(APFloat::PPCDoubleDouble(), true) - .bitcastToAPInt() - .getRawData()[0]); - EXPECT_EQ(0x0000000000000000ull, - APFloat::getSmallestNormalized(APFloat::PPCDoubleDouble(), true) - .getSecondFloat() - .bitcastToAPInt() - .getRawData()[0]); - + { + uint64_t Data[] = { + 0x8360000000000000ull, 0x0000000000000000ull, + }; + EXPECT_EQ(APInt(128, 2, Data), + APFloat::getSmallestNormalized(APFloat::PPCDoubleDouble(), true) + .bitcastToAPInt()); + } EXPECT_TRUE(APFloat::getSmallest(APFloat::PPCDoubleDouble()).isSmallest()); EXPECT_TRUE(APFloat::getLargest(APFloat::PPCDoubleDouble()).isLargest()); } + +TEST(APFloatTest, PPCDoubleDoubleIsDenormal) { + EXPECT_TRUE(APFloat::getSmallest(APFloat::PPCDoubleDouble()).isDenormal()); + EXPECT_FALSE(APFloat::getLargest(APFloat::PPCDoubleDouble()).isDenormal()); + EXPECT_FALSE( + APFloat::getSmallestNormalized(APFloat::PPCDoubleDouble()).isDenormal()); + { + // (4 + 3) is not normalized + uint64_t Data[] = { + 0x4010000000000000ull, 0x4008000000000000ull, + }; + EXPECT_TRUE( + APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Data)).isDenormal()); + } +} + +TEST(APFloatTest, PPCDoubleDoubleScalbn) { + // 3.0 + 3.0 << 53 + uint64_t Input[] = { + 0x4008000000000000ull, 0x3cb8000000000000ull, + }; + APFloat Result = + scalbn(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Input)), 1, + APFloat::rmNearestTiesToEven); + // 6.0 + 6.0 << 53 + EXPECT_EQ(0x4018000000000000ull, Result.bitcastToAPInt().getRawData()[0]); + EXPECT_EQ(0x3cc8000000000000ull, Result.bitcastToAPInt().getRawData()[1]); +} + +TEST(APFloatTest, PPCDoubleDoubleFrexp) { + // 3.0 + 3.0 << 53 + uint64_t Input[] = { + 0x4008000000000000ull, 0x3cb8000000000000ull, + }; + 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]); +} } diff --git a/unittests/ADT/APIntTest.cpp b/unittests/ADT/APIntTest.cpp index cbffdc096fbed..65481f5b2f220 100644 --- a/unittests/ADT/APIntTest.cpp +++ b/unittests/ADT/APIntTest.cpp @@ -37,6 +37,11 @@ TEST(APIntTest, i64_ArithmeticRightShiftNegative) { EXPECT_EQ(neg_one, neg_one.ashr(7)); } +TEST(APIntTest, i64_LogicalRightShiftNegative) { + const APInt neg_one(128, static_cast<uint64_t>(-1), true); + EXPECT_EQ(0, neg_one.lshr(257)); +} + TEST(APIntTest, i128_NegativeCount) { APInt Minus3(128, static_cast<uint64_t>(-3), true); EXPECT_EQ(126u, Minus3.countLeadingOnes()); @@ -63,6 +68,26 @@ TEST(APIntTest, i33_Count) { EXPECT_EQ(((uint64_t)-2)&((1ull<<33) -1), i33minus2.getZExtValue()); } +TEST(APIntTest, i61_Count) { + APInt i61(61, 1 << 15); + EXPECT_EQ(45u, i61.countLeadingZeros()); + EXPECT_EQ(0u, i61.countLeadingOnes()); + EXPECT_EQ(16u, i61.getActiveBits()); + EXPECT_EQ(15u, i61.countTrailingZeros()); + EXPECT_EQ(1u, i61.countPopulation()); + EXPECT_EQ(static_cast<int64_t>(1 << 15), i61.getSExtValue()); + EXPECT_EQ(static_cast<uint64_t>(1 << 15), i61.getZExtValue()); + + i61.setBits(8, 19); + EXPECT_EQ(42u, i61.countLeadingZeros()); + EXPECT_EQ(0u, i61.countLeadingOnes()); + EXPECT_EQ(19u, i61.getActiveBits()); + EXPECT_EQ(8u, i61.countTrailingZeros()); + EXPECT_EQ(11u, i61.countPopulation()); + EXPECT_EQ(static_cast<int64_t>((1 << 19) - (1 << 8)), i61.getSExtValue()); + EXPECT_EQ(static_cast<uint64_t>((1 << 19) - (1 << 8)), i61.getZExtValue()); +} + TEST(APIntTest, i65_Count) { APInt i65(65, 0, true); EXPECT_EQ(65u, i65.countLeadingZeros()); @@ -118,6 +143,80 @@ TEST(APIntTest, i128_PositiveCount) { EXPECT_EQ(1u, one.countPopulation()); EXPECT_EQ(1, one.getSExtValue()); EXPECT_EQ(1u, one.getZExtValue()); + + APInt s128(128, 2, true); + EXPECT_EQ(126u, s128.countLeadingZeros()); + EXPECT_EQ(0u, s128.countLeadingOnes()); + EXPECT_EQ(2u, s128.getActiveBits()); + EXPECT_EQ(1u, s128.countTrailingZeros()); + EXPECT_EQ(0u, s128.countTrailingOnes()); + EXPECT_EQ(1u, s128.countPopulation()); + EXPECT_EQ(2, s128.getSExtValue()); + EXPECT_EQ(2u, s128.getZExtValue()); + + // NOP Test + s128.setBits(42, 42); + EXPECT_EQ(126u, s128.countLeadingZeros()); + EXPECT_EQ(0u, s128.countLeadingOnes()); + EXPECT_EQ(2u, s128.getActiveBits()); + EXPECT_EQ(1u, s128.countTrailingZeros()); + EXPECT_EQ(0u, s128.countTrailingOnes()); + EXPECT_EQ(1u, s128.countPopulation()); + EXPECT_EQ(2, s128.getSExtValue()); + EXPECT_EQ(2u, s128.getZExtValue()); + + s128.setBits(3, 32); + EXPECT_EQ(96u, s128.countLeadingZeros()); + EXPECT_EQ(0u, s128.countLeadingOnes()); + EXPECT_EQ(32u, s128.getActiveBits()); + EXPECT_EQ(33u, s128.getMinSignedBits()); + EXPECT_EQ(1u, s128.countTrailingZeros()); + EXPECT_EQ(0u, s128.countTrailingOnes()); + EXPECT_EQ(30u, s128.countPopulation()); + EXPECT_EQ(static_cast<uint32_t>((~0u << 3) | 2), s128.getZExtValue()); + + s128.setBits(62, 128); + EXPECT_EQ(0u, s128.countLeadingZeros()); + EXPECT_EQ(66u, s128.countLeadingOnes()); + EXPECT_EQ(128u, s128.getActiveBits()); + EXPECT_EQ(63u, s128.getMinSignedBits()); + EXPECT_EQ(1u, s128.countTrailingZeros()); + EXPECT_EQ(0u, s128.countTrailingOnes()); + EXPECT_EQ(96u, s128.countPopulation()); + EXPECT_EQ(static_cast<int64_t>((3ull << 62) | + static_cast<uint32_t>((~0u << 3) | 2)), + s128.getSExtValue()); +} + +TEST(APIntTest, i256) { + APInt s256(256, 15, true); + EXPECT_EQ(252u, s256.countLeadingZeros()); + EXPECT_EQ(0u, s256.countLeadingOnes()); + EXPECT_EQ(4u, s256.getActiveBits()); + EXPECT_EQ(0u, s256.countTrailingZeros()); + EXPECT_EQ(4u, s256.countTrailingOnes()); + EXPECT_EQ(4u, s256.countPopulation()); + EXPECT_EQ(15, s256.getSExtValue()); + EXPECT_EQ(15u, s256.getZExtValue()); + + s256.setBits(62, 66); + EXPECT_EQ(190u, s256.countLeadingZeros()); + EXPECT_EQ(0u, s256.countLeadingOnes()); + EXPECT_EQ(66u, s256.getActiveBits()); + EXPECT_EQ(67u, s256.getMinSignedBits()); + EXPECT_EQ(0u, s256.countTrailingZeros()); + EXPECT_EQ(4u, s256.countTrailingOnes()); + EXPECT_EQ(8u, s256.countPopulation()); + + s256.setBits(60, 256); + EXPECT_EQ(0u, s256.countLeadingZeros()); + EXPECT_EQ(196u, s256.countLeadingOnes()); + EXPECT_EQ(256u, s256.getActiveBits()); + EXPECT_EQ(61u, s256.getMinSignedBits()); + EXPECT_EQ(0u, s256.countTrailingZeros()); + EXPECT_EQ(4u, s256.countTrailingOnes()); + EXPECT_EQ(200u, s256.countPopulation()); + EXPECT_EQ(static_cast<int64_t>((~0ull << 60) | 15), s256.getSExtValue()); } TEST(APIntTest, i1) { @@ -158,6 +257,36 @@ TEST(APIntTest, i1) { EXPECT_EQ(two, one - neg_one); EXPECT_EQ(zero, one - one); + // And + EXPECT_EQ(zero, zero & zero); + EXPECT_EQ(zero, one & zero); + EXPECT_EQ(zero, zero & one); + EXPECT_EQ(one, one & one); + EXPECT_EQ(zero, zero & zero); + EXPECT_EQ(zero, neg_one & zero); + EXPECT_EQ(zero, zero & neg_one); + EXPECT_EQ(neg_one, neg_one & neg_one); + + // Or + EXPECT_EQ(zero, zero | zero); + EXPECT_EQ(one, one | zero); + EXPECT_EQ(one, zero | one); + EXPECT_EQ(one, one | one); + EXPECT_EQ(zero, zero | zero); + EXPECT_EQ(neg_one, neg_one | zero); + EXPECT_EQ(neg_one, zero | neg_one); + EXPECT_EQ(neg_one, neg_one | neg_one); + + // Xor + EXPECT_EQ(zero, zero ^ zero); + EXPECT_EQ(one, one ^ zero); + EXPECT_EQ(one, zero ^ one); + EXPECT_EQ(zero, one ^ one); + EXPECT_EQ(zero, zero ^ zero); + EXPECT_EQ(neg_one, neg_one ^ zero); + EXPECT_EQ(neg_one, zero ^ neg_one); + EXPECT_EQ(zero, neg_one ^ neg_one); + // Shifts. EXPECT_EQ(zero, one << one); EXPECT_EQ(one, one << zero); @@ -416,6 +545,58 @@ TEST(APIntTest, compareLargeIntegers) { EXPECT_TRUE(!MinusTwo.slt(MinusTwo)); } +TEST(APIntTest, binaryOpsWithRawIntegers) { + // Single word check. + uint64_t E1 = 0x2CA7F46BF6569915ULL; + APInt A1(64, E1); + + EXPECT_EQ(A1 & E1, E1); + EXPECT_EQ(A1 & 0, 0); + EXPECT_EQ(A1 & 1, 1); + EXPECT_EQ(A1 & 5, 5); + EXPECT_EQ(A1 & UINT64_MAX, E1); + + EXPECT_EQ(A1 | E1, E1); + EXPECT_EQ(A1 | 0, E1); + EXPECT_EQ(A1 | 1, E1); + EXPECT_EQ(A1 | 2, E1 | 2); + EXPECT_EQ(A1 | UINT64_MAX, UINT64_MAX); + + EXPECT_EQ(A1 ^ E1, 0); + EXPECT_EQ(A1 ^ 0, E1); + EXPECT_EQ(A1 ^ 1, E1 ^ 1); + EXPECT_EQ(A1 ^ 7, E1 ^ 7); + EXPECT_EQ(A1 ^ UINT64_MAX, ~E1); + + // Multiword check. + uint64_t N = 0xEB6EB136591CBA21ULL; + APInt::WordType E2[4] = { + N, + 0x7B9358BD6A33F10AULL, + 0x7E7FFA5EADD8846ULL, + 0x305F341CA00B613DULL + }; + APInt A2(APInt::APINT_BITS_PER_WORD*4, E2); + + EXPECT_EQ(A2 & N, N); + EXPECT_EQ(A2 & 0, 0); + EXPECT_EQ(A2 & 1, 1); + EXPECT_EQ(A2 & 5, 1); + EXPECT_EQ(A2 & UINT64_MAX, N); + + EXPECT_EQ(A2 | N, A2); + EXPECT_EQ(A2 | 0, A2); + EXPECT_EQ(A2 | 1, A2); + EXPECT_EQ(A2 | 2, A2 + 2); + EXPECT_EQ(A2 | UINT64_MAX, A2 - N + UINT64_MAX); + + EXPECT_EQ(A2 ^ N, A2 - N); + EXPECT_EQ(A2 ^ 0, A2); + EXPECT_EQ(A2 ^ 1, A2 - 1); + EXPECT_EQ(A2 ^ 7, A2 + 5); + EXPECT_EQ(A2 ^ UINT64_MAX, A2 - N + ~N); +} + TEST(APIntTest, rvalue_arithmetic) { // Test all combinations of lvalue/rvalue lhs/rhs of add/sub @@ -585,6 +766,150 @@ TEST(APIntTest, rvalue_arithmetic) { } } +TEST(APIntTest, rvalue_bitwise) { + // Test all combinations of lvalue/rvalue lhs/rhs of and/or/xor + + // Lamdba to return an APInt by value, but also provide the raw value of the + // allocated data. + auto getRValue = [](const char *HexString, uint64_t const *&RawData) { + APInt V(129, HexString, 16); + RawData = V.getRawData(); + return V; + }; + + APInt Ten(129, "A", 16); + APInt Twelve(129, "C", 16); + + const uint64_t *RawDataL = nullptr; + const uint64_t *RawDataR = nullptr; + + { + // 12 & 10 = 8 + APInt AndLL = Ten & Twelve; + EXPECT_EQ(AndLL, 0x8); + + APInt AndLR = Ten & getRValue("C", RawDataR); + EXPECT_EQ(AndLR, 0x8); + EXPECT_EQ(AndLR.getRawData(), RawDataR); + + APInt AndRL = getRValue("A", RawDataL) & Twelve; + EXPECT_EQ(AndRL, 0x8); + EXPECT_EQ(AndRL.getRawData(), RawDataL); + + APInt AndRR = getRValue("A", RawDataL) & getRValue("C", RawDataR); + EXPECT_EQ(AndRR, 0x8); + EXPECT_EQ(AndRR.getRawData(), RawDataR); + + // LValue's and constants + APInt AndLK = Ten & 0xc; + EXPECT_EQ(AndLK, 0x8); + + APInt AndKL = 0xa & Twelve; + EXPECT_EQ(AndKL, 0x8); + + // RValue's and constants + APInt AndRK = getRValue("A", RawDataL) & 0xc; + EXPECT_EQ(AndRK, 0x8); + EXPECT_EQ(AndRK.getRawData(), RawDataL); + + APInt AndKR = 0xa & getRValue("C", RawDataR); + EXPECT_EQ(AndKR, 0x8); + EXPECT_EQ(AndKR.getRawData(), RawDataR); + } + + { + // 12 | 10 = 14 + APInt OrLL = Ten | Twelve; + EXPECT_EQ(OrLL, 0xe); + + APInt OrLR = Ten | getRValue("C", RawDataR); + EXPECT_EQ(OrLR, 0xe); + EXPECT_EQ(OrLR.getRawData(), RawDataR); + + APInt OrRL = getRValue("A", RawDataL) | Twelve; + EXPECT_EQ(OrRL, 0xe); + EXPECT_EQ(OrRL.getRawData(), RawDataL); + + APInt OrRR = getRValue("A", RawDataL) | getRValue("C", RawDataR); + EXPECT_EQ(OrRR, 0xe); + EXPECT_EQ(OrRR.getRawData(), RawDataR); + + // LValue's and constants + APInt OrLK = Ten | 0xc; + EXPECT_EQ(OrLK, 0xe); + + APInt OrKL = 0xa | Twelve; + EXPECT_EQ(OrKL, 0xe); + + // RValue's and constants + APInt OrRK = getRValue("A", RawDataL) | 0xc; + EXPECT_EQ(OrRK, 0xe); + EXPECT_EQ(OrRK.getRawData(), RawDataL); + + APInt OrKR = 0xa | getRValue("C", RawDataR); + EXPECT_EQ(OrKR, 0xe); + EXPECT_EQ(OrKR.getRawData(), RawDataR); + } + + { + // 12 ^ 10 = 6 + APInt XorLL = Ten ^ Twelve; + EXPECT_EQ(XorLL, 0x6); + + APInt XorLR = Ten ^ getRValue("C", RawDataR); + EXPECT_EQ(XorLR, 0x6); + EXPECT_EQ(XorLR.getRawData(), RawDataR); + + APInt XorRL = getRValue("A", RawDataL) ^ Twelve; + EXPECT_EQ(XorRL, 0x6); + EXPECT_EQ(XorRL.getRawData(), RawDataL); + + APInt XorRR = getRValue("A", RawDataL) ^ getRValue("C", RawDataR); + EXPECT_EQ(XorRR, 0x6); + EXPECT_EQ(XorRR.getRawData(), RawDataR); + + // LValue's and constants + APInt XorLK = Ten ^ 0xc; + EXPECT_EQ(XorLK, 0x6); + + APInt XorKL = 0xa ^ Twelve; + EXPECT_EQ(XorKL, 0x6); + + // RValue's and constants + APInt XorRK = getRValue("A", RawDataL) ^ 0xc; + EXPECT_EQ(XorRK, 0x6); + EXPECT_EQ(XorRK.getRawData(), RawDataL); + + APInt XorKR = 0xa ^ getRValue("C", RawDataR); + EXPECT_EQ(XorKR, 0x6); + EXPECT_EQ(XorKR.getRawData(), RawDataR); + } +} + +TEST(APIntTest, rvalue_invert) { + // Lamdba to return an APInt by value, but also provide the raw value of the + // allocated data. + auto getRValue = [](const char *HexString, uint64_t const *&RawData) { + APInt V(129, HexString, 16); + RawData = V.getRawData(); + return V; + }; + + APInt One(129, 1); + APInt NegativeTwo(129, -2ULL, true); + + const uint64_t *RawData = nullptr; + + { + // ~1 = -2 + APInt NegL = ~One; + EXPECT_EQ(NegL, NegativeTwo); + + APInt NegR = ~getRValue("1", RawData); + EXPECT_EQ(NegR, NegativeTwo); + EXPECT_EQ(NegR.getRawData(), RawData); + } +} // Tests different div/rem varaints using scheme (a * b + c) / a void testDiv(APInt a, APInt b, APInt c) { @@ -701,7 +1026,6 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, uint64_t(-3LL)), APInt(32, "-11", 2)); EXPECT_EQ(APInt(32, uint64_t(-4LL)), APInt(32, "-100", 2)); - EXPECT_EQ(APInt(32, 0), APInt(32, "0", 8)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 8)); EXPECT_EQ(APInt(32, 7), APInt(32, "7", 8)); @@ -723,7 +1047,6 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, uint64_t(-15LL)), APInt(32, "-17", 8)); EXPECT_EQ(APInt(32, uint64_t(-16LL)), APInt(32, "-20", 8)); - EXPECT_EQ(APInt(32, 0), APInt(32, "0", 10)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 10)); EXPECT_EQ(APInt(32, 9), APInt(32, "9", 10)); @@ -738,7 +1061,6 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, uint64_t(-19LL)), APInt(32, "-19", 10)); EXPECT_EQ(APInt(32, uint64_t(-20LL)), APInt(32, "-20", 10)); - EXPECT_EQ(APInt(32, 0), APInt(32, "0", 16)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 16)); EXPECT_EQ(APInt(32, 15), APInt(32, "F", 16)); @@ -759,7 +1081,7 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 36), APInt(32, "10", 36)); EXPECT_EQ(APInt(32, 71), APInt(32, "1Z", 36)); EXPECT_EQ(APInt(32, 72), APInt(32, "20", 36)); - + EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 36)); EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 36)); EXPECT_EQ(APInt(32, uint64_t(-35LL)), APInt(32, "-Z", 36)); @@ -978,6 +1300,29 @@ TEST(APIntTest, Rotate) { EXPECT_EQ(APInt(8, 1), APInt(8, 16).rotl(4)); EXPECT_EQ(APInt(8, 16), APInt(8, 16).rotl(8)); + EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(33)); + EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(32, 33))); + + EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(33)); + EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(32, 33))); + EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(33, 33))); + EXPECT_EQ(APInt(32, (1 << 8)), APInt(32, 1).rotl(APInt(32, 40))); + EXPECT_EQ(APInt(32, (1 << 30)), APInt(32, 1).rotl(APInt(31, 30))); + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotl(APInt(31, 31))); + + EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotl(APInt(1, 0))); + EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(1, 1))); + + EXPECT_EQ(APInt(32, 16), APInt(32, 1).rotl(APInt(3, 4))); + + EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotl(APInt(64, 64))); + EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(64, 65))); + + EXPECT_EQ(APInt(7, 24), APInt(7, 3).rotl(APInt(7, 3))); + EXPECT_EQ(APInt(7, 24), APInt(7, 3).rotl(APInt(7, 10))); + EXPECT_EQ(APInt(7, 24), APInt(7, 3).rotl(APInt(5, 10))); + EXPECT_EQ(APInt(7, 6), APInt(7, 3).rotl(APInt(12, 120))); + EXPECT_EQ(APInt(8, 16), APInt(8, 16).rotr(0)); EXPECT_EQ(APInt(8, 8), APInt(8, 16).rotr(1)); EXPECT_EQ(APInt(8, 4), APInt(8, 16).rotr(2)); @@ -990,9 +1335,36 @@ TEST(APIntTest, Rotate) { EXPECT_EQ(APInt(8, 16), APInt(8, 1).rotr(4)); EXPECT_EQ(APInt(8, 1), APInt(8, 1).rotr(8)); - APInt Big(256, "00004000800000000000000000003fff8000000000000000", 16); - APInt Rot(256, "3fff80000000000000000000000000000000000040008000", 16); + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33)); + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33))); + + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33)); + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33))); + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(33, 33))); + EXPECT_EQ(APInt(32, (1 << 24)), APInt(32, 1).rotr(APInt(32, 40))); + + EXPECT_EQ(APInt(32, (1 << 2)), APInt(32, 1).rotr(APInt(31, 30))); + EXPECT_EQ(APInt(32, (1 << 1)), APInt(32, 1).rotr(APInt(31, 31))); + + EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(1, 0))); + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(1, 1))); + + EXPECT_EQ(APInt(32, (1 << 28)), APInt(32, 1).rotr(APInt(3, 4))); + + EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(64, 64))); + EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(64, 65))); + + EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 3))); + EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 10))); + EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(5, 10))); + EXPECT_EQ(APInt(7, 65), APInt(7, 3).rotr(APInt(12, 120))); + + APInt Big(256, "00004000800000000000000000003fff8000000000000003", 16); + APInt Rot(256, "3fff80000000000000030000000000000000000040008000", 16); EXPECT_EQ(Rot, Big.rotr(144)); + + EXPECT_EQ(APInt(32, 8), APInt(32, 1).rotl(Big)); + EXPECT_EQ(APInt(32, (1 << 29)), APInt(32, 1).rotr(Big)); } TEST(APIntTest, Splat) { @@ -1010,63 +1382,63 @@ TEST(APIntTest, tcDecrement) { // No out borrow. { - integerPart singleWord = ~integerPart(0) << (integerPartWidth - 1); - integerPart carry = APInt::tcDecrement(&singleWord, 1); - EXPECT_EQ(carry, integerPart(0)); - EXPECT_EQ(singleWord, ~integerPart(0) >> 1); + APInt::WordType singleWord = ~APInt::WordType(0) << (APInt::APINT_BITS_PER_WORD - 1); + APInt::WordType carry = APInt::tcDecrement(&singleWord, 1); + EXPECT_EQ(carry, APInt::WordType(0)); + EXPECT_EQ(singleWord, ~APInt::WordType(0) >> 1); } // With out borrow. { - integerPart singleWord = 0; - integerPart carry = APInt::tcDecrement(&singleWord, 1); - EXPECT_EQ(carry, integerPart(1)); - EXPECT_EQ(singleWord, ~integerPart(0)); + APInt::WordType singleWord = 0; + APInt::WordType carry = APInt::tcDecrement(&singleWord, 1); + EXPECT_EQ(carry, APInt::WordType(1)); + EXPECT_EQ(singleWord, ~APInt::WordType(0)); } // Test multiword decrement. // No across word borrow, no out borrow. { - integerPart test[4] = {0x1, 0x1, 0x1, 0x1}; - integerPart expected[4] = {0x0, 0x1, 0x1, 0x1}; + APInt::WordType test[4] = {0x1, 0x1, 0x1, 0x1}; + APInt::WordType expected[4] = {0x0, 0x1, 0x1, 0x1}; APInt::tcDecrement(test, 4); EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); } // 1 across word borrow, no out borrow. { - integerPart test[4] = {0x0, 0xF, 0x1, 0x1}; - integerPart expected[4] = {~integerPart(0), 0xE, 0x1, 0x1}; - integerPart carry = APInt::tcDecrement(test, 4); - EXPECT_EQ(carry, integerPart(0)); + APInt::WordType test[4] = {0x0, 0xF, 0x1, 0x1}; + APInt::WordType expected[4] = {~APInt::WordType(0), 0xE, 0x1, 0x1}; + APInt::WordType carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, APInt::WordType(0)); EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); } // 2 across word borrow, no out borrow. { - integerPart test[4] = {0x0, 0x0, 0xC, 0x1}; - integerPart expected[4] = {~integerPart(0), ~integerPart(0), 0xB, 0x1}; - integerPart carry = APInt::tcDecrement(test, 4); - EXPECT_EQ(carry, integerPart(0)); + APInt::WordType test[4] = {0x0, 0x0, 0xC, 0x1}; + APInt::WordType expected[4] = {~APInt::WordType(0), ~APInt::WordType(0), 0xB, 0x1}; + APInt::WordType carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, APInt::WordType(0)); EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); } // 3 across word borrow, no out borrow. { - integerPart test[4] = {0x0, 0x0, 0x0, 0x1}; - integerPart expected[4] = {~integerPart(0), ~integerPart(0), ~integerPart(0), 0x0}; - integerPart carry = APInt::tcDecrement(test, 4); - EXPECT_EQ(carry, integerPart(0)); + APInt::WordType test[4] = {0x0, 0x0, 0x0, 0x1}; + APInt::WordType expected[4] = {~APInt::WordType(0), ~APInt::WordType(0), ~APInt::WordType(0), 0x0}; + APInt::WordType carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, APInt::WordType(0)); EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); } // 3 across word borrow, with out borrow. { - integerPart test[4] = {0x0, 0x0, 0x0, 0x0}; - integerPart expected[4] = {~integerPart(0), ~integerPart(0), ~integerPart(0), ~integerPart(0)}; - integerPart carry = APInt::tcDecrement(test, 4); - EXPECT_EQ(carry, integerPart(1)); + APInt::WordType test[4] = {0x0, 0x0, 0x0, 0x0}; + APInt::WordType expected[4] = {~APInt::WordType(0), ~APInt::WordType(0), ~APInt::WordType(0), ~APInt::WordType(0)}; + APInt::WordType carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, APInt::WordType(1)); EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); } } @@ -1081,17 +1453,17 @@ TEST(APIntTest, arrayAccess) { } // Multiword check. - integerPart E2[4] = { + APInt::WordType E2[4] = { 0xEB6EB136591CBA21ULL, 0x7B9358BD6A33F10AULL, 0x7E7FFA5EADD8846ULL, 0x305F341CA00B613DULL }; - APInt A2(integerPartWidth*4, E2); + APInt A2(APInt::APINT_BITS_PER_WORD*4, E2); for (unsigned i = 0; i < 4; ++i) { - for (unsigned j = 0; j < integerPartWidth; ++j) { + for (unsigned j = 0; j < APInt::APINT_BITS_PER_WORD; ++j) { EXPECT_EQ(bool(E2[i] & (1ULL << j)), - A2[i*integerPartWidth + j]); + A2[i*APInt::APINT_BITS_PER_WORD + j]); } } } @@ -1125,18 +1497,18 @@ TEST(APIntTest, nearestLogBase2) { // Multiple word check. // Test round up. - integerPart I4[4] = {0x0, 0xF, 0x18, 0x0}; - APInt A4(integerPartWidth*4, I4); + APInt::WordType I4[4] = {0x0, 0xF, 0x18, 0x0}; + APInt A4(APInt::APINT_BITS_PER_WORD*4, I4); EXPECT_EQ(A4.nearestLogBase2(), A4.ceilLogBase2()); // Test round down. - integerPart I5[4] = {0x0, 0xF, 0x10, 0x0}; - APInt A5(integerPartWidth*4, I5); + APInt::WordType I5[4] = {0x0, 0xF, 0x10, 0x0}; + APInt A5(APInt::APINT_BITS_PER_WORD*4, I5); EXPECT_EQ(A5.nearestLogBase2(), A5.logBase2()); // Test ties round up. uint64_t I6[4] = {0x0, 0x0, 0x0, 0x18}; - APInt A6(integerPartWidth*4, I6); + APInt A6(APInt::APINT_BITS_PER_WORD*4, I6); EXPECT_EQ(A6.nearestLogBase2(), A6.ceilLogBase2()); // Test BitWidth == 1 special cases. @@ -1192,18 +1564,44 @@ TEST(APIntTest, IsSplat) { } TEST(APIntTest, isMask) { - EXPECT_FALSE(APIntOps::isMask(APInt(32, 0x01010101))); - EXPECT_FALSE(APIntOps::isMask(APInt(32, 0xf0000000))); - EXPECT_FALSE(APIntOps::isMask(APInt(32, 0xffff0000))); - EXPECT_FALSE(APIntOps::isMask(APInt(32, 0xff << 1))); + EXPECT_FALSE(APInt(32, 0x01010101).isMask()); + EXPECT_FALSE(APInt(32, 0xf0000000).isMask()); + EXPECT_FALSE(APInt(32, 0xffff0000).isMask()); + EXPECT_FALSE(APInt(32, 0xff << 1).isMask()); for (int N : { 1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256 }) { - EXPECT_FALSE(APIntOps::isMask(APInt(N, 0))); + EXPECT_FALSE(APInt(N, 0).isMask()); APInt One(N, 1); for (int I = 1; I <= N; ++I) { APInt MaskVal = One.shl(I) - 1; - EXPECT_TRUE(APIntOps::isMask(MaskVal)); + EXPECT_TRUE(MaskVal.isMask()); + EXPECT_TRUE(MaskVal.isMask(I)); + } + } +} + +TEST(APIntTest, isShiftedMask) { + EXPECT_FALSE(APInt(32, 0x01010101).isShiftedMask()); + EXPECT_TRUE(APInt(32, 0xf0000000).isShiftedMask()); + EXPECT_TRUE(APInt(32, 0xffff0000).isShiftedMask()); + EXPECT_TRUE(APInt(32, 0xff << 1).isShiftedMask()); + + for (int N : { 1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256 }) { + EXPECT_FALSE(APInt(N, 0).isShiftedMask()); + + APInt One(N, 1); + for (int I = 1; I < N; ++I) { + APInt MaskVal = One.shl(I) - 1; + EXPECT_TRUE(MaskVal.isShiftedMask()); + } + for (int I = 1; I < N - 1; ++I) { + APInt MaskVal = One.shl(I); + EXPECT_TRUE(MaskVal.isShiftedMask()); + } + for (int I = 1; I < N; ++I) { + APInt MaskVal = APInt::getHighBitsSet(N, I); + EXPECT_TRUE(MaskVal.isShiftedMask()); } } } @@ -1279,3 +1677,351 @@ TEST(APIntTest, reverseBits) { } } } + +TEST(APIntTest, insertBits) { + APInt iSrc(31, 0x00123456); + + // Direct copy. + APInt i31(31, 0x76543210ull); + i31.insertBits(iSrc, 0); + EXPECT_EQ(static_cast<int64_t>(0x00123456ull), i31.getSExtValue()); + + // Single word src/dst insertion. + APInt i63(63, 0x01234567FFFFFFFFull); + i63.insertBits(iSrc, 4); + EXPECT_EQ(static_cast<int64_t>(0x012345600123456Full), i63.getSExtValue()); + + // Insert single word src into one word of dst. + APInt i120(120, UINT64_MAX, true); + i120.insertBits(iSrc, 8); + EXPECT_EQ(static_cast<int64_t>(0xFFFFFF80123456FFull), i120.getSExtValue()); + + // Insert single word src into two words of dst. + APInt i127(127, UINT64_MAX, true); + i127.insertBits(iSrc, 48); + EXPECT_EQ(i127.extractBits(64, 0).getZExtValue(), 0x3456FFFFFFFFFFFFull); + EXPECT_EQ(i127.extractBits(63, 64).getZExtValue(), 0x7FFFFFFFFFFF8012ull); + + // Insert on word boundaries. + APInt i128(128, 0); + i128.insertBits(APInt(64, UINT64_MAX, true), 0); + i128.insertBits(APInt(64, UINT64_MAX, true), 64); + EXPECT_EQ(-1, i128.getSExtValue()); + + APInt i256(256, UINT64_MAX, true); + i256.insertBits(APInt(65, 0), 0); + i256.insertBits(APInt(69, 0), 64); + i256.insertBits(APInt(128, 0), 128); + EXPECT_EQ(0u, i256.getSExtValue()); + + APInt i257(257, 0); + i257.insertBits(APInt(96, UINT64_MAX, true), 64); + EXPECT_EQ(i257.extractBits(64, 0).getZExtValue(), 0x0000000000000000ull); + EXPECT_EQ(i257.extractBits(64, 64).getZExtValue(), 0xFFFFFFFFFFFFFFFFull); + EXPECT_EQ(i257.extractBits(64, 128).getZExtValue(), 0x00000000FFFFFFFFull); + EXPECT_EQ(i257.extractBits(65, 192).getZExtValue(), 0x0000000000000000ull); + + // General insertion. + APInt i260(260, UINT64_MAX, true); + i260.insertBits(APInt(129, 1ull << 48), 15); + EXPECT_EQ(i260.extractBits(64, 0).getZExtValue(), 0x8000000000007FFFull); + EXPECT_EQ(i260.extractBits(64, 64).getZExtValue(), 0x0000000000000000ull); + EXPECT_EQ(i260.extractBits(64, 128).getZExtValue(), 0xFFFFFFFFFFFF0000ull); + EXPECT_EQ(i260.extractBits(64, 192).getZExtValue(), 0xFFFFFFFFFFFFFFFFull); + EXPECT_EQ(i260.extractBits(4, 256).getZExtValue(), 0x000000000000000Full); +} + +TEST(APIntTest, extractBits) { + APInt i32(32, 0x1234567); + EXPECT_EQ(0x3456, i32.extractBits(16, 4)); + + APInt i257(257, 0xFFFFFFFFFF0000FFull, true); + EXPECT_EQ(0xFFu, i257.extractBits(16, 0)); + EXPECT_EQ((0xFFu >> 1), i257.extractBits(16, 1)); + EXPECT_EQ(-1, i257.extractBits(32, 64).getSExtValue()); + EXPECT_EQ(-1, i257.extractBits(128, 128).getSExtValue()); + EXPECT_EQ(-1, i257.extractBits(66, 191).getSExtValue()); + EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFFF80007Full), + i257.extractBits(128, 1).getSExtValue()); + EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFFF80007Full), + i257.extractBits(129, 1).getSExtValue()); +} + +TEST(APIntTest, getLowBitsSet) { + APInt i128lo64 = APInt::getLowBitsSet(128, 64); + EXPECT_EQ(0u, i128lo64.countLeadingOnes()); + EXPECT_EQ(64u, i128lo64.countLeadingZeros()); + EXPECT_EQ(64u, i128lo64.getActiveBits()); + EXPECT_EQ(0u, i128lo64.countTrailingZeros()); + EXPECT_EQ(64u, i128lo64.countTrailingOnes()); + EXPECT_EQ(64u, i128lo64.countPopulation()); +} + +TEST(APIntTest, getBitsSet) { + APInt i64hi1lo1 = APInt::getBitsSet(64, 63, 1); + EXPECT_EQ(1u, i64hi1lo1.countLeadingOnes()); + EXPECT_EQ(0u, i64hi1lo1.countLeadingZeros()); + EXPECT_EQ(64u, i64hi1lo1.getActiveBits()); + EXPECT_EQ(0u, i64hi1lo1.countTrailingZeros()); + EXPECT_EQ(1u, i64hi1lo1.countTrailingOnes()); + EXPECT_EQ(2u, i64hi1lo1.countPopulation()); + + APInt i127hi1lo1 = APInt::getBitsSet(127, 126, 1); + EXPECT_EQ(1u, i127hi1lo1.countLeadingOnes()); + EXPECT_EQ(0u, i127hi1lo1.countLeadingZeros()); + EXPECT_EQ(127u, i127hi1lo1.getActiveBits()); + EXPECT_EQ(0u, i127hi1lo1.countTrailingZeros()); + EXPECT_EQ(1u, i127hi1lo1.countTrailingOnes()); + EXPECT_EQ(2u, i127hi1lo1.countPopulation()); +} + +TEST(APIntTest, getHighBitsSet) { + APInt i64hi32 = APInt::getHighBitsSet(64, 32); + EXPECT_EQ(32u, i64hi32.countLeadingOnes()); + EXPECT_EQ(0u, i64hi32.countLeadingZeros()); + EXPECT_EQ(64u, i64hi32.getActiveBits()); + EXPECT_EQ(32u, i64hi32.countTrailingZeros()); + EXPECT_EQ(0u, i64hi32.countTrailingOnes()); + EXPECT_EQ(32u, i64hi32.countPopulation()); +} + +TEST(APIntTest, getBitsSetFrom) { + APInt i64hi31 = APInt::getBitsSetFrom(64, 33); + EXPECT_EQ(31u, i64hi31.countLeadingOnes()); + EXPECT_EQ(0u, i64hi31.countLeadingZeros()); + EXPECT_EQ(64u, i64hi31.getActiveBits()); + EXPECT_EQ(33u, i64hi31.countTrailingZeros()); + EXPECT_EQ(0u, i64hi31.countTrailingOnes()); + EXPECT_EQ(31u, i64hi31.countPopulation()); +} + +TEST(APIntTest, setLowBits) { + APInt i64lo32(64, 0); + i64lo32.setLowBits(32); + EXPECT_EQ(0u, i64lo32.countLeadingOnes()); + EXPECT_EQ(32u, i64lo32.countLeadingZeros()); + EXPECT_EQ(32u, i64lo32.getActiveBits()); + EXPECT_EQ(0u, i64lo32.countTrailingZeros()); + EXPECT_EQ(32u, i64lo32.countTrailingOnes()); + EXPECT_EQ(32u, i64lo32.countPopulation()); + + APInt i128lo64(128, 0); + i128lo64.setLowBits(64); + EXPECT_EQ(0u, i128lo64.countLeadingOnes()); + EXPECT_EQ(64u, i128lo64.countLeadingZeros()); + EXPECT_EQ(64u, i128lo64.getActiveBits()); + EXPECT_EQ(0u, i128lo64.countTrailingZeros()); + EXPECT_EQ(64u, i128lo64.countTrailingOnes()); + EXPECT_EQ(64u, i128lo64.countPopulation()); + + APInt i128lo24(128, 0); + i128lo24.setLowBits(24); + EXPECT_EQ(0u, i128lo24.countLeadingOnes()); + EXPECT_EQ(104u, i128lo24.countLeadingZeros()); + EXPECT_EQ(24u, i128lo24.getActiveBits()); + EXPECT_EQ(0u, i128lo24.countTrailingZeros()); + EXPECT_EQ(24u, i128lo24.countTrailingOnes()); + EXPECT_EQ(24u, i128lo24.countPopulation()); + + APInt i128lo104(128, 0); + i128lo104.setLowBits(104); + EXPECT_EQ(0u, i128lo104.countLeadingOnes()); + EXPECT_EQ(24u, i128lo104.countLeadingZeros()); + EXPECT_EQ(104u, i128lo104.getActiveBits()); + EXPECT_EQ(0u, i128lo104.countTrailingZeros()); + EXPECT_EQ(104u, i128lo104.countTrailingOnes()); + EXPECT_EQ(104u, i128lo104.countPopulation()); + + APInt i128lo0(128, 0); + i128lo0.setLowBits(0); + EXPECT_EQ(0u, i128lo0.countLeadingOnes()); + EXPECT_EQ(128u, i128lo0.countLeadingZeros()); + EXPECT_EQ(0u, i128lo0.getActiveBits()); + EXPECT_EQ(128u, i128lo0.countTrailingZeros()); + EXPECT_EQ(0u, i128lo0.countTrailingOnes()); + EXPECT_EQ(0u, i128lo0.countPopulation()); + + APInt i80lo79(80, 0); + i80lo79.setLowBits(79); + EXPECT_EQ(0u, i80lo79.countLeadingOnes()); + EXPECT_EQ(1u, i80lo79.countLeadingZeros()); + EXPECT_EQ(79u, i80lo79.getActiveBits()); + EXPECT_EQ(0u, i80lo79.countTrailingZeros()); + EXPECT_EQ(79u, i80lo79.countTrailingOnes()); + EXPECT_EQ(79u, i80lo79.countPopulation()); +} + +TEST(APIntTest, setHighBits) { + APInt i64hi32(64, 0); + i64hi32.setHighBits(32); + EXPECT_EQ(32u, i64hi32.countLeadingOnes()); + EXPECT_EQ(0u, i64hi32.countLeadingZeros()); + EXPECT_EQ(64u, i64hi32.getActiveBits()); + EXPECT_EQ(32u, i64hi32.countTrailingZeros()); + EXPECT_EQ(0u, i64hi32.countTrailingOnes()); + EXPECT_EQ(32u, i64hi32.countPopulation()); + + APInt i128hi64(128, 0); + i128hi64.setHighBits(64); + EXPECT_EQ(64u, i128hi64.countLeadingOnes()); + EXPECT_EQ(0u, i128hi64.countLeadingZeros()); + EXPECT_EQ(128u, i128hi64.getActiveBits()); + EXPECT_EQ(64u, i128hi64.countTrailingZeros()); + EXPECT_EQ(0u, i128hi64.countTrailingOnes()); + EXPECT_EQ(64u, i128hi64.countPopulation()); + + APInt i128hi24(128, 0); + i128hi24.setHighBits(24); + EXPECT_EQ(24u, i128hi24.countLeadingOnes()); + EXPECT_EQ(0u, i128hi24.countLeadingZeros()); + EXPECT_EQ(128u, i128hi24.getActiveBits()); + EXPECT_EQ(104u, i128hi24.countTrailingZeros()); + EXPECT_EQ(0u, i128hi24.countTrailingOnes()); + EXPECT_EQ(24u, i128hi24.countPopulation()); + + APInt i128hi104(128, 0); + i128hi104.setHighBits(104); + EXPECT_EQ(104u, i128hi104.countLeadingOnes()); + EXPECT_EQ(0u, i128hi104.countLeadingZeros()); + EXPECT_EQ(128u, i128hi104.getActiveBits()); + EXPECT_EQ(24u, i128hi104.countTrailingZeros()); + EXPECT_EQ(0u, i128hi104.countTrailingOnes()); + EXPECT_EQ(104u, i128hi104.countPopulation()); + + APInt i128hi0(128, 0); + i128hi0.setHighBits(0); + EXPECT_EQ(0u, i128hi0.countLeadingOnes()); + EXPECT_EQ(128u, i128hi0.countLeadingZeros()); + EXPECT_EQ(0u, i128hi0.getActiveBits()); + EXPECT_EQ(128u, i128hi0.countTrailingZeros()); + EXPECT_EQ(0u, i128hi0.countTrailingOnes()); + EXPECT_EQ(0u, i128hi0.countPopulation()); + + APInt i80hi1(80, 0); + i80hi1.setHighBits(1); + EXPECT_EQ(1u, i80hi1.countLeadingOnes()); + EXPECT_EQ(0u, i80hi1.countLeadingZeros()); + EXPECT_EQ(80u, i80hi1.getActiveBits()); + EXPECT_EQ(79u, i80hi1.countTrailingZeros()); + EXPECT_EQ(0u, i80hi1.countTrailingOnes()); + EXPECT_EQ(1u, i80hi1.countPopulation()); + + APInt i32hi16(32, 0); + i32hi16.setHighBits(16); + EXPECT_EQ(16u, i32hi16.countLeadingOnes()); + EXPECT_EQ(0u, i32hi16.countLeadingZeros()); + EXPECT_EQ(32u, i32hi16.getActiveBits()); + EXPECT_EQ(16u, i32hi16.countTrailingZeros()); + EXPECT_EQ(0u, i32hi16.countTrailingOnes()); + EXPECT_EQ(16u, i32hi16.countPopulation()); +} + +TEST(APIntTest, setBitsFrom) { + APInt i64from63(64, 0); + i64from63.setBitsFrom(63); + EXPECT_EQ(1u, i64from63.countLeadingOnes()); + EXPECT_EQ(0u, i64from63.countLeadingZeros()); + EXPECT_EQ(64u, i64from63.getActiveBits()); + EXPECT_EQ(63u, i64from63.countTrailingZeros()); + EXPECT_EQ(0u, i64from63.countTrailingOnes()); + EXPECT_EQ(1u, i64from63.countPopulation()); +} + +TEST(APIntTest, setAllBits) { + APInt i32(32, 0); + i32.setAllBits(); + EXPECT_EQ(32u, i32.countLeadingOnes()); + EXPECT_EQ(0u, i32.countLeadingZeros()); + EXPECT_EQ(32u, i32.getActiveBits()); + EXPECT_EQ(0u, i32.countTrailingZeros()); + EXPECT_EQ(32u, i32.countTrailingOnes()); + EXPECT_EQ(32u, i32.countPopulation()); + + APInt i64(64, 0); + i64.setAllBits(); + EXPECT_EQ(64u, i64.countLeadingOnes()); + EXPECT_EQ(0u, i64.countLeadingZeros()); + EXPECT_EQ(64u, i64.getActiveBits()); + EXPECT_EQ(0u, i64.countTrailingZeros()); + EXPECT_EQ(64u, i64.countTrailingOnes()); + EXPECT_EQ(64u, i64.countPopulation()); + + APInt i96(96, 0); + i96.setAllBits(); + EXPECT_EQ(96u, i96.countLeadingOnes()); + EXPECT_EQ(0u, i96.countLeadingZeros()); + EXPECT_EQ(96u, i96.getActiveBits()); + EXPECT_EQ(0u, i96.countTrailingZeros()); + EXPECT_EQ(96u, i96.countTrailingOnes()); + EXPECT_EQ(96u, i96.countPopulation()); + + APInt i128(128, 0); + i128.setAllBits(); + EXPECT_EQ(128u, i128.countLeadingOnes()); + EXPECT_EQ(0u, i128.countLeadingZeros()); + EXPECT_EQ(128u, i128.getActiveBits()); + EXPECT_EQ(0u, i128.countTrailingZeros()); + EXPECT_EQ(128u, i128.countTrailingOnes()); + EXPECT_EQ(128u, i128.countPopulation()); +} + +TEST(APIntTest, getLoBits) { + APInt i32(32, 0xfa); + i32.setHighBits(1); + EXPECT_EQ(0xa, i32.getLoBits(4)); + APInt i128(128, 0xfa); + i128.setHighBits(1); + EXPECT_EQ(0xa, i128.getLoBits(4)); +} + +TEST(APIntTest, getHiBits) { + APInt i32(32, 0xfa); + i32.setHighBits(2); + EXPECT_EQ(0xc, i32.getHiBits(4)); + APInt i128(128, 0xfa); + i128.setHighBits(2); + EXPECT_EQ(0xc, i128.getHiBits(4)); +} + +TEST(APIntTest, GCD) { + using APIntOps::GreatestCommonDivisor; + + for (unsigned Bits : {1, 2, 32, 63, 64, 65}) { + // Test some corner cases near zero. + APInt Zero(Bits, 0), One(Bits, 1); + EXPECT_EQ(GreatestCommonDivisor(Zero, Zero), Zero); + EXPECT_EQ(GreatestCommonDivisor(Zero, One), One); + EXPECT_EQ(GreatestCommonDivisor(One, Zero), One); + EXPECT_EQ(GreatestCommonDivisor(One, One), One); + + if (Bits > 1) { + APInt Two(Bits, 2); + EXPECT_EQ(GreatestCommonDivisor(Zero, Two), Two); + EXPECT_EQ(GreatestCommonDivisor(One, Two), One); + EXPECT_EQ(GreatestCommonDivisor(Two, Two), Two); + + // Test some corner cases near the highest representable value. + APInt Max(Bits, 0); + Max.setAllBits(); + EXPECT_EQ(GreatestCommonDivisor(Zero, Max), Max); + EXPECT_EQ(GreatestCommonDivisor(One, Max), One); + EXPECT_EQ(GreatestCommonDivisor(Two, Max), One); + EXPECT_EQ(GreatestCommonDivisor(Max, Max), Max); + + APInt MaxOver2 = Max.udiv(Two); + EXPECT_EQ(GreatestCommonDivisor(MaxOver2, Max), One); + // Max - 1 == Max / 2 * 2, because Max is odd. + EXPECT_EQ(GreatestCommonDivisor(MaxOver2, Max - 1), MaxOver2); + } + } + + // Compute the 20th Mersenne prime. + const unsigned BitWidth = 4450; + APInt HugePrime = APInt::getLowBitsSet(BitWidth, 4423); + + // 9931 and 123456 are coprime. + APInt A = HugePrime * APInt(BitWidth, 9931); + APInt B = HugePrime * APInt(BitWidth, 123456); + APInt C = GreatestCommonDivisor(A, B); + EXPECT_EQ(C, HugePrime); +} diff --git a/unittests/ADT/BitVectorTest.cpp b/unittests/ADT/BitVectorTest.cpp index 78fd5ce65677c..98ef66735ad23 100644 --- a/unittests/ADT/BitVectorTest.cpp +++ b/unittests/ADT/BitVectorTest.cpp @@ -182,6 +182,45 @@ TYPED_TEST(BitVectorTest, TrivialOperation) { EXPECT_TRUE(Vec.empty()); } +TYPED_TEST(BitVectorTest, FindOperations) { + // Test finding in an empty BitVector. + TypeParam A; + EXPECT_EQ(-1, A.find_first()); + EXPECT_EQ(-1, A.find_first_unset()); + EXPECT_EQ(-1, A.find_next(0)); + EXPECT_EQ(-1, A.find_next_unset(0)); + + // Test finding next set and unset bits in a BitVector with multiple words + A.resize(100); + A.set(12); + A.set(13); + A.set(75); + + EXPECT_EQ(12, A.find_first()); + EXPECT_EQ(13, A.find_next(12)); + EXPECT_EQ(75, A.find_next(13)); + EXPECT_EQ(-1, A.find_next(75)); + + EXPECT_EQ(0, A.find_first_unset()); + EXPECT_EQ(14, A.find_next_unset(11)); + EXPECT_EQ(14, A.find_next_unset(12)); + EXPECT_EQ(14, A.find_next_unset(13)); + EXPECT_EQ(16, A.find_next_unset(15)); + EXPECT_EQ(76, A.find_next_unset(74)); + EXPECT_EQ(76, A.find_next_unset(75)); + EXPECT_EQ(-1, A.find_next_unset(99)); + + A.set(0, 100); + EXPECT_EQ(100U, A.count()); + EXPECT_EQ(0, A.find_first()); + EXPECT_EQ(-1, A.find_first_unset()); + + A.reset(0, 100); + EXPECT_EQ(0U, A.count()); + EXPECT_EQ(-1, A.find_first()); + EXPECT_EQ(0, A.find_first_unset()); +} + TYPED_TEST(BitVectorTest, CompoundAssignment) { TypeParam A; A.resize(10); @@ -425,5 +464,42 @@ TYPED_TEST(BitVectorTest, MoveAssignment) { EXPECT_EQ(C, B); } +template<class TypeParam> +static void testEmpty(const TypeParam &A) { + EXPECT_TRUE(A.empty()); + EXPECT_EQ((size_t)0, A.size()); + EXPECT_EQ((size_t)0, A.count()); + EXPECT_FALSE(A.any()); + EXPECT_TRUE(A.all()); + EXPECT_TRUE(A.none()); + EXPECT_EQ(-1, A.find_first()); + EXPECT_EQ(A, TypeParam()); +} + +/// Tests whether BitVector behaves well with Bits==nullptr, Capacity==0 +TYPED_TEST(BitVectorTest, EmptyVector) { + TypeParam A; + testEmpty(A); + + TypeParam B; + B.reset(); + testEmpty(B); + + TypeParam C; + C.clear(); + testEmpty(C); + + TypeParam D(A); + testEmpty(D); + + TypeParam E; + E = A; + testEmpty(E); + + TypeParam F; + E.reset(A); + testEmpty(E); +} + } #endif diff --git a/unittests/ADT/BreadthFirstIteratorTest.cpp b/unittests/ADT/BreadthFirstIteratorTest.cpp new file mode 100644 index 0000000000000..42a07bbe930ba --- /dev/null +++ b/unittests/ADT/BreadthFirstIteratorTest.cpp @@ -0,0 +1,74 @@ +//=== llvm/unittest/ADT/BreadthFirstIteratorTest.cpp - BFS iterator tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/BreadthFirstIterator.h" +#include "TestGraph.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace llvm { + +TEST(BreadthFristIteratorTest, Basic) { + typedef bf_iterator<Graph<4>> BFIter; + + Graph<4> G; + G.AddEdge(0, 1); + G.AddEdge(0, 2); + G.AddEdge(1, 3); + + auto It = BFIter::begin(G); + auto End = BFIter::end(G); + EXPECT_EQ(It.getLevel(), 0U); + EXPECT_EQ(*It, G.AccessNode(0)); + ++It; + EXPECT_EQ(It.getLevel(), 1U); + EXPECT_EQ(*It, G.AccessNode(1)); + ++It; + EXPECT_EQ(It.getLevel(), 1U); + EXPECT_EQ(*It, G.AccessNode(2)); + ++It; + EXPECT_EQ(It.getLevel(), 2U); + EXPECT_EQ(*It, G.AccessNode(3)); + ++It; + EXPECT_EQ(It, End); +} + +TEST(BreadthFristIteratorTest, Cycle) { + typedef bf_iterator<Graph<4>> BFIter; + + Graph<4> G; + G.AddEdge(0, 1); + G.AddEdge(1, 0); + G.AddEdge(1, 2); + G.AddEdge(2, 1); + G.AddEdge(2, 1); + G.AddEdge(2, 3); + G.AddEdge(3, 2); + G.AddEdge(3, 1); + G.AddEdge(3, 0); + + auto It = BFIter::begin(G); + auto End = BFIter::end(G); + EXPECT_EQ(It.getLevel(), 0U); + EXPECT_EQ(*It, G.AccessNode(0)); + ++It; + EXPECT_EQ(It.getLevel(), 1U); + EXPECT_EQ(*It, G.AccessNode(1)); + ++It; + EXPECT_EQ(It.getLevel(), 2U); + EXPECT_EQ(*It, G.AccessNode(2)); + ++It; + EXPECT_EQ(It.getLevel(), 3U); + EXPECT_EQ(*It, G.AccessNode(3)); + ++It; + EXPECT_EQ(It, End); +} + +} // end namespace llvm diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index 738f6efe92d63..fa977ac5d3f5f 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -9,6 +9,7 @@ set(ADTSources ArrayRefTest.cpp BitmaskEnumTest.cpp BitVectorTest.cpp + BreadthFirstIteratorTest.cpp BumpPtrListTest.cpp DAGDeltaAlgorithmTest.cpp DeltaAlgorithmTest.cpp diff --git a/unittests/ADT/DenseMapTest.cpp b/unittests/ADT/DenseMapTest.cpp index 80f0462bc8fb6..273f4da021c4a 100644 --- a/unittests/ADT/DenseMapTest.cpp +++ b/unittests/ADT/DenseMapTest.cpp @@ -580,4 +580,18 @@ TEST(DenseMapCustomTest, TryEmplaceTest) { EXPECT_EQ(Try1.first, Try2.first); EXPECT_NE(nullptr, P); } + +TEST(DenseMapCustomTest, ConstTest) { + // Test that const pointers work okay for count and find, even when the + // underlying map is a non-const pointer. + DenseMap<int *, int> Map; + int A; + int *B = &A; + const int *C = &A; + Map.insert({B, 0}); + EXPECT_EQ(Map.count(B), 1u); + EXPECT_EQ(Map.count(C), 1u); + EXPECT_NE(Map.find(B), Map.end()); + EXPECT_NE(Map.find(C), Map.end()); +} } diff --git a/unittests/ADT/DenseSetTest.cpp b/unittests/ADT/DenseSetTest.cpp index 4d5a82902f0ea..a09537a3e9904 100644 --- a/unittests/ADT/DenseSetTest.cpp +++ b/unittests/ADT/DenseSetTest.cpp @@ -73,6 +73,22 @@ TYPED_TEST(DenseSetTest, InitializerList) { EXPECT_EQ(0u, set.count(3)); } +TYPED_TEST(DenseSetTest, ConstIteratorComparison) { + TypeParam set({1}); + const TypeParam &cset = set; + EXPECT_EQ(set.begin(), cset.begin()); + EXPECT_EQ(set.end(), cset.end()); + EXPECT_NE(set.end(), cset.begin()); + EXPECT_NE(set.begin(), cset.end()); +} + +TYPED_TEST(DenseSetTest, DefaultConstruction) { + typename TypeParam::iterator I, J; + typename TypeParam::const_iterator CI, CJ; + EXPECT_EQ(I, J); + EXPECT_EQ(CI, CJ); +} + TYPED_TEST(DenseSetTest, EmptyInitializerList) { TypeParam set({}); EXPECT_EQ(0u, set.size()); @@ -169,4 +185,17 @@ TEST(DenseSetCustomTest, ReserveTest) { EXPECT_EQ(0, CountCopyAndMove::Copy); } } +TEST(DenseSetCustomTest, ConstTest) { + // Test that const pointers work okay for count and find, even when the + // underlying map is a non-const pointer. + DenseSet<int *> Map; + int A; + int *B = &A; + const int *C = &A; + Map.insert(B); + EXPECT_EQ(Map.count(B), 1u); + EXPECT_EQ(Map.count(C), 1u); + EXPECT_NE(Map.find(B), Map.end()); + EXPECT_NE(Map.find(C), Map.end()); +} } diff --git a/unittests/ADT/IteratorTest.cpp b/unittests/ADT/IteratorTest.cpp index a8d5b33a0b497..7f261824b499c 100644 --- a/unittests/ADT/IteratorTest.cpp +++ b/unittests/ADT/IteratorTest.cpp @@ -7,9 +7,9 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/iterator.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/iterator.h" #include "gtest/gtest.h" using namespace llvm; @@ -35,14 +35,15 @@ static_assert(std::is_same<typename AdaptedIter::reference, Shadow<3>>::value, ""); TEST(PointeeIteratorTest, Basic) { - int arr[4] = { 1, 2, 3, 4 }; + int arr[4] = {1, 2, 3, 4}; SmallVector<int *, 4> V; V.push_back(&arr[0]); V.push_back(&arr[1]); V.push_back(&arr[2]); V.push_back(&arr[3]); - typedef pointee_iterator<SmallVectorImpl<int *>::const_iterator> test_iterator; + typedef pointee_iterator<SmallVectorImpl<int *>::const_iterator> + test_iterator; test_iterator Begin, End; Begin = V.begin(); @@ -83,7 +84,8 @@ TEST(PointeeIteratorTest, SmartPointer) { V.push_back(make_unique<int>(4)); typedef pointee_iterator< - SmallVectorImpl<std::unique_ptr<int>>::const_iterator> test_iterator; + SmallVectorImpl<std::unique_ptr<int>>::const_iterator> + test_iterator; test_iterator Begin, End; Begin = V.begin(); @@ -116,6 +118,15 @@ TEST(PointeeIteratorTest, SmartPointer) { EXPECT_EQ(End, I); } +TEST(PointeeIteratorTest, Range) { + int A[] = {1, 2, 3, 4}; + SmallVector<int *, 4> V{&A[0], &A[1], &A[2], &A[3]}; + + int I = 0; + for (int II : make_pointee_range(V)) + EXPECT_EQ(A[I++], II); +} + TEST(FilterIteratorTest, Lambda) { auto IsOdd = [](int N) { return N % 2 == 1; }; int A[] = {0, 1, 2, 3, 4, 5, 6}; @@ -209,6 +220,13 @@ TEST(PointerIterator, Const) { EXPECT_EQ(A + 4, std::next(*Begin, 4)); } +TEST(PointerIterator, Range) { + int A[] = {1, 2, 3, 4}; + int I = 0; + for (int *P : make_pointer_range(A)) + EXPECT_EQ(A + I++, P); +} + TEST(ZipIteratorTest, Basic) { using namespace std; const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9}; @@ -272,4 +290,51 @@ TEST(ZipIteratorTest, ZipFirstMutability) { } } +TEST(ZipIteratorTest, Filter) { + using namespace std; + vector<unsigned> pi{3, 1, 4, 1, 5, 9}; + + unsigned iters = 0; + // pi is length 6, but the zip RHS is length 7. + auto zipped = zip_first(pi, vector<bool>{1, 1, 0, 1, 1, 1, 0}); + for (auto tup : make_filter_range( + zipped, [](decltype(zipped)::value_type t) { return get<1>(t); })) { + EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); + get<0>(tup) += 1; + iters += 1; + } + + // Should have skipped pi[2]. + EXPECT_EQ(iters, 5u); + + // Ensure that in-place mutation works. + EXPECT_TRUE(all_of(pi, [](unsigned n) { return (n & 0x01) == 0; })); +} + +TEST(ZipIteratorTest, Reverse) { + using namespace std; + vector<unsigned> ascending{0, 1, 2, 3, 4, 5}; + + auto zipped = zip_first(ascending, vector<bool>{0, 1, 0, 1, 0, 1}); + unsigned last = 6; + for (auto tup : reverse(zipped)) { + // Check that this is in reverse. + EXPECT_LT(get<0>(tup), last); + last = get<0>(tup); + EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); + } + + auto odds = [](decltype(zipped)::value_type tup) { return get<1>(tup); }; + last = 6; + for (auto tup : make_filter_range(reverse(zipped), odds)) { + EXPECT_LT(get<0>(tup), last); + last = get<0>(tup); + EXPECT_TRUE(get<0>(tup) & 0x01); + get<0>(tup) += 1; + } + + // Ensure that in-place mutation works. + EXPECT_TRUE(all_of(ascending, [](unsigned n) { return (n & 0x01) == 0; })); +} + } // anonymous namespace diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index f17d24f36b238..2e6eb6f413f6c 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -48,7 +48,7 @@ TEST(STLExtrasTest, EnumerateLValue) { std::vector<CharPairType> CharResults; for (auto X : llvm::enumerate(foo)) { - CharResults.emplace_back(X.Index, X.Value); + CharResults.emplace_back(X.index(), X.value()); } ASSERT_EQ(3u, CharResults.size()); EXPECT_EQ(CharPairType(0u, 'a'), CharResults[0]); @@ -60,7 +60,7 @@ TEST(STLExtrasTest, EnumerateLValue) { std::vector<IntPairType> IntResults; const std::vector<int> bar = {1, 2, 3}; for (auto X : llvm::enumerate(bar)) { - IntResults.emplace_back(X.Index, X.Value); + IntResults.emplace_back(X.index(), X.value()); } ASSERT_EQ(3u, IntResults.size()); EXPECT_EQ(IntPairType(0u, 1), IntResults[0]); @@ -69,9 +69,9 @@ TEST(STLExtrasTest, EnumerateLValue) { // Test an empty range. IntResults.clear(); - const std::vector<int> baz; + const std::vector<int> baz{}; for (auto X : llvm::enumerate(baz)) { - IntResults.emplace_back(X.Index, X.Value); + IntResults.emplace_back(X.index(), X.value()); } EXPECT_TRUE(IntResults.empty()); } @@ -82,7 +82,7 @@ TEST(STLExtrasTest, EnumerateModifyLValue) { std::vector<char> foo = {'a', 'b', 'c'}; for (auto X : llvm::enumerate(foo)) { - ++X.Value; + ++X.value(); } EXPECT_EQ('b', foo[0]); EXPECT_EQ('c', foo[1]); @@ -97,7 +97,7 @@ TEST(STLExtrasTest, EnumerateRValueRef) { auto Enumerator = llvm::enumerate(std::vector<int>{1, 2, 3}); for (auto X : llvm::enumerate(std::vector<int>{1, 2, 3})) { - Results.emplace_back(X.Index, X.Value); + Results.emplace_back(X.index(), X.value()); } ASSERT_EQ(3u, Results.size()); @@ -114,8 +114,8 @@ TEST(STLExtrasTest, EnumerateModifyRValue) { std::vector<PairType> Results; for (auto X : llvm::enumerate(std::vector<char>{'1', '2', '3'})) { - ++X.Value; - Results.emplace_back(X.Index, X.Value); + ++X.value(); + Results.emplace_back(X.index(), X.value()); } ASSERT_EQ(3u, Results.size()); @@ -255,6 +255,16 @@ TEST(STLExtrasTest, CountAdaptor) { EXPECT_EQ(1, count(v, 4)); } +TEST(STLExtrasTest, ToVector) { + std::vector<char> v = {'a', 'b', 'c'}; + auto Enumerated = to_vector<4>(enumerate(v)); + ASSERT_EQ(3u, Enumerated.size()); + for (size_t I = 0; I < v.size(); ++I) { + EXPECT_EQ(I, Enumerated[I].index()); + EXPECT_EQ(v[I], Enumerated[I].value()); + } +} + TEST(STLExtrasTest, ConcatRange) { std::vector<int> Expected = {1, 2, 3, 4, 5, 6, 7, 8}; std::vector<int> Test; diff --git a/unittests/ADT/SmallPtrSetTest.cpp b/unittests/ADT/SmallPtrSetTest.cpp index d4d963fdc5bdc..fc14c684d67f3 100644 --- a/unittests/ADT/SmallPtrSetTest.cpp +++ b/unittests/ADT/SmallPtrSetTest.cpp @@ -12,7 +12,9 @@ //===----------------------------------------------------------------------===// #include "gtest/gtest.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/PointerLikeTypeTraits.h" using namespace llvm; @@ -279,3 +281,52 @@ TEST(SmallPtrSetTest, EraseTest) { SmallPtrSet<int *, 2> A; checkEraseAndIterators(A); } + +// Verify that dereferencing and iteration work. +TEST(SmallPtrSetTest, dereferenceAndIterate) { + int Ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + SmallPtrSet<const int *, 4> S; + for (int &I : Ints) { + EXPECT_EQ(&I, *S.insert(&I).first); + EXPECT_EQ(&I, *S.find(&I)); + } + + // Iterate from each and count how many times each element is found. + int Found[sizeof(Ints)/sizeof(int)] = {0}; + for (int &I : Ints) + for (auto F = S.find(&I), E = S.end(); F != E; ++F) + ++Found[*F - Ints]; + + // Sort. We should hit the first element just once and the final element N + // times. + std::sort(std::begin(Found), std::end(Found)); + for (auto F = std::begin(Found), E = std::end(Found); F != E; ++F) + EXPECT_EQ(F - Found + 1, *F); +} + +// Verify that const pointers work for count and find even when the underlying +// SmallPtrSet is not for a const pointer type. +TEST(SmallPtrSetTest, ConstTest) { + SmallPtrSet<int *, 8> IntSet; + int A; + int *B = &A; + const int *C = &A; + IntSet.insert(B); + EXPECT_EQ(IntSet.count(B), 1u); + EXPECT_EQ(IntSet.count(C), 1u); + EXPECT_NE(IntSet.find(B), IntSet.end()); + EXPECT_NE(IntSet.find(C), IntSet.end()); +} + +// Verify that we automatically get the const version of PointerLikeTypeTraits +// filled in for us, even for a non-pointer type +using TestPair = PointerIntPair<int *, 1>; + +TEST(SmallPtrSetTest, ConstNonPtrTest) { + SmallPtrSet<TestPair, 8> IntSet; + int A[1]; + TestPair Pair(&A[0], 1); + IntSet.insert(Pair); + EXPECT_EQ(IntSet.count(Pair), 1u); + EXPECT_NE(IntSet.find(Pair), IntSet.end()); +} diff --git a/unittests/ADT/SparseBitVectorTest.cpp b/unittests/ADT/SparseBitVectorTest.cpp index 9127225860baa..6cd4de35bca7d 100644 --- a/unittests/ADT/SparseBitVectorTest.cpp +++ b/unittests/ADT/SparseBitVectorTest.cpp @@ -127,4 +127,43 @@ TEST(SparseBitVectorTest, SelfAssignment) { EXPECT_TRUE(Vec.empty()); } +TEST(SparseBitVectorTest, Find) { + SparseBitVector<> Vec; + Vec.set(1); + EXPECT_EQ(1, Vec.find_first()); + EXPECT_EQ(1, Vec.find_last()); + + Vec.set(2); + EXPECT_EQ(1, Vec.find_first()); + EXPECT_EQ(2, Vec.find_last()); + + Vec.set(0); + Vec.set(3); + EXPECT_EQ(0, Vec.find_first()); + EXPECT_EQ(3, Vec.find_last()); + + Vec.reset(1); + Vec.reset(0); + Vec.reset(3); + EXPECT_EQ(2, Vec.find_first()); + EXPECT_EQ(2, Vec.find_last()); + + // Set some large bits to ensure we are pulling bits from more than just a + // single bitword. + Vec.set(500); + Vec.set(2000); + Vec.set(3000); + Vec.set(4000); + Vec.reset(2); + EXPECT_EQ(500, Vec.find_first()); + EXPECT_EQ(4000, Vec.find_last()); + + Vec.reset(500); + Vec.reset(3000); + Vec.reset(4000); + EXPECT_EQ(2000, Vec.find_first()); + EXPECT_EQ(2000, Vec.find_last()); + + Vec.clear(); +} } diff --git a/unittests/ADT/StringMapTest.cpp b/unittests/ADT/StringMapTest.cpp index 911c72d749619..b5c63695ff35c 100644 --- a/unittests/ADT/StringMapTest.cpp +++ b/unittests/ADT/StringMapTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/DataTypes.h" #include "gtest/gtest.h" @@ -269,6 +270,34 @@ TEST_F(StringMapTest, InsertRehashingPairTest) { EXPECT_EQ(42u, It->second); } +TEST_F(StringMapTest, IterMapKeys) { + StringMap<int> Map; + Map["A"] = 1; + Map["B"] = 2; + Map["C"] = 3; + Map["D"] = 3; + + auto Keys = to_vector<4>(Map.keys()); + std::sort(Keys.begin(), Keys.end()); + + SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"}; + EXPECT_EQ(Expected, Keys); +} + +TEST_F(StringMapTest, IterSetKeys) { + StringSet<> Set; + Set.insert("A"); + Set.insert("B"); + Set.insert("C"); + Set.insert("D"); + + auto Keys = to_vector<4>(Set.keys()); + std::sort(Keys.begin(), Keys.end()); + + SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"}; + EXPECT_EQ(Expected, Keys); +} + // Create a non-default constructable value struct StringMapTestStruct { StringMapTestStruct(int i) : i(i) {} @@ -425,7 +454,7 @@ TEST(StringMapCustomTest, InitialSizeTest) { Map.insert(std::pair<std::string, CountCtorCopyAndMove>( std::piecewise_construct, std::forward_as_tuple(Twine(i).str()), std::forward_as_tuple(i))); - // After the inital move, the map will move the Elts in the Entry. + // After the initial move, the map will move the Elts in the Entry. EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move); // We copy once the pair from the Elts vector EXPECT_EQ(0u, CountCtorCopyAndMove::Copy); diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 5b6822ed757df..e308f2d7c64b0 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -504,8 +504,22 @@ TEST(StringRefTest, Count) { } TEST(StringRefTest, EditDistance) { - StringRef Str("hello"); - EXPECT_EQ(2U, Str.edit_distance("hill")); + StringRef Hello("hello"); + EXPECT_EQ(2U, Hello.edit_distance("hill")); + + StringRef Industry("industry"); + EXPECT_EQ(6U, Industry.edit_distance("interest")); + + StringRef Soylent("soylent green is people"); + EXPECT_EQ(19U, Soylent.edit_distance("people soiled our green")); + EXPECT_EQ(26U, Soylent.edit_distance("people soiled our green", + /* allow replacements = */ false)); + EXPECT_EQ(9U, Soylent.edit_distance("people soiled our green", + /* allow replacements = */ true, + /* max edit distance = */ 8)); + EXPECT_EQ(53U, Soylent.edit_distance("people soiled our green " + "people soiled our green " + "people soiled our green ")); } TEST(StringRefTest, Misc) { @@ -852,6 +866,27 @@ TEST(StringRefTest, consumeIntegerSigned) { } } +struct GetDoubleStrings { + const char *Str; + bool AllowInexact; + bool ShouldFail; + double D; +} DoubleStrings[] = {{"0", false, false, 0.0}, + {"0.0", false, false, 0.0}, + {"-0.0", false, false, -0.0}, + {"123.45", false, true, 123.45}, + {"123.45", true, false, 123.45}}; + +TEST(StringRefTest, getAsDouble) { + for (const auto &Entry : DoubleStrings) { + double Result; + StringRef S(Entry.Str); + EXPECT_EQ(Entry.ShouldFail, S.getAsDouble(Result, Entry.AllowInexact)); + if (!Entry.ShouldFail) + EXPECT_EQ(Result, Entry.D); + } +} + static const char *join_input[] = { "a", "b", "c" }; static const char join_result1[] = "a"; static const char join_result2[] = "a:b:c"; @@ -878,6 +913,8 @@ TEST(StringRefTest, joinStrings) { EXPECT_TRUE(v2_join2); bool v2_join3 = join(v2.begin(), v2.end(), "::") == join_result3; EXPECT_TRUE(v2_join3); + v2_join3 = join(v2, "::") == join_result3; + EXPECT_TRUE(v2_join3); } diff --git a/unittests/ADT/TinyPtrVectorTest.cpp b/unittests/ADT/TinyPtrVectorTest.cpp index 26189b76394fc..8d5fa4060913b 100644 --- a/unittests/ADT/TinyPtrVectorTest.cpp +++ b/unittests/ADT/TinyPtrVectorTest.cpp @@ -17,19 +17,13 @@ #include "llvm/Support/type_traits.h" #include "gtest/gtest.h" #include <algorithm> +#include <random> #include <vector> using namespace llvm; namespace { -// The world's worst RNG, but it is deterministic and makes it easy to get -// *some* shuffling of elements. -static ptrdiff_t test_shuffle_rng(ptrdiff_t i) { - return (i + i * 33) % i; -} -static ptrdiff_t (*test_shuffle_rng_p)(ptrdiff_t) = &test_shuffle_rng; - template <typename VectorT> class TinyPtrVectorTest : public testing::Test { protected: @@ -46,7 +40,7 @@ protected: for (size_t i = 0, e = array_lengthof(TestValues); i != e; ++i) TestPtrs.push_back(&TestValues[i]); - std::random_shuffle(TestPtrs.begin(), TestPtrs.end(), test_shuffle_rng_p); + std::shuffle(TestPtrs.begin(), TestPtrs.end(), std::mt19937{}); } ArrayRef<PtrT> testArray(size_t N) { diff --git a/unittests/ADT/TripleTest.cpp b/unittests/ADT/TripleTest.cpp index c80477f6ddc98..78616d36e4f8a 100644 --- a/unittests/ADT/TripleTest.cpp +++ b/unittests/ADT/TripleTest.cpp @@ -685,6 +685,54 @@ TEST(TripleTest, BitWidthArchVariants) { T.setArch(Triple::riscv64); EXPECT_EQ(Triple::riscv32, T.get32BitArchVariant().getArch()); EXPECT_EQ(Triple::riscv64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::thumbeb); + EXPECT_EQ(Triple::thumbeb, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::aarch64_be, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::thumb); + EXPECT_EQ(Triple::thumb, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::aarch64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::aarch64); + EXPECT_EQ(Triple::arm, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::aarch64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::aarch64_be); + EXPECT_EQ(Triple::armeb, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::aarch64_be, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::renderscript32); + EXPECT_EQ(Triple::renderscript32, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::renderscript64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::renderscript64); + EXPECT_EQ(Triple::renderscript32, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::renderscript64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::le32); + EXPECT_EQ(Triple::le32, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::le64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::le64); + EXPECT_EQ(Triple::le32, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::le64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::armeb); + EXPECT_EQ(Triple::armeb, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::aarch64_be, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::arm); + EXPECT_EQ(Triple::arm, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::aarch64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::systemz); + EXPECT_EQ(Triple::UnknownArch, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::systemz, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::xcore); + EXPECT_EQ(Triple::xcore, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch()); } TEST(TripleTest, EndianArchVariants) { @@ -775,6 +823,22 @@ TEST(TripleTest, EndianArchVariants) { T.setArch(Triple::lanai); EXPECT_EQ(Triple::lanai, T.getBigEndianArchVariant().getArch()); EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch()); + + T.setArch(Triple::tcele); + EXPECT_EQ(Triple::tce, T.getBigEndianArchVariant().getArch()); + EXPECT_EQ(Triple::tcele, T.getLittleEndianArchVariant().getArch()); + + T.setArch(Triple::tce); + EXPECT_EQ(Triple::tce, T.getBigEndianArchVariant().getArch()); + EXPECT_EQ(Triple::tcele, T.getLittleEndianArchVariant().getArch()); + + T.setArch(Triple::le32); + EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch()); + EXPECT_EQ(Triple::le32, T.getLittleEndianArchVariant().getArch()); + + T.setArch(Triple::le64); + EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch()); + EXPECT_EQ(Triple::le64, T.getLittleEndianArchVariant().getArch()); } TEST(TripleTest, getOSVersion) { @@ -888,6 +952,9 @@ TEST(TripleTest, FileFormat) { EXPECT_EQ(Triple::ELF, Triple("i686-pc-windows-msvc-elf").getObjectFormat()); EXPECT_EQ(Triple::ELF, Triple("i686-pc-cygwin-elf").getObjectFormat()); + EXPECT_EQ(Triple::Wasm, Triple("wasm32-unknown-unknown-wasm").getObjectFormat()); + EXPECT_EQ(Triple::Wasm, Triple("wasm64-unknown-unknown-wasm").getObjectFormat()); + Triple MSVCNormalized(Triple::normalize("i686-pc-windows-msvc-elf")); EXPECT_EQ(Triple::ELF, MSVCNormalized.getObjectFormat()); @@ -903,6 +970,9 @@ TEST(TripleTest, FileFormat) { Triple T = Triple(""); T.setObjectFormat(Triple::ELF); EXPECT_EQ(Triple::ELF, T.getObjectFormat()); + + T.setObjectFormat(Triple::MachO); + EXPECT_EQ(Triple::MachO, T.getObjectFormat()); } TEST(TripleTest, NormalizeWindows) { @@ -948,6 +1018,10 @@ TEST(TripleTest, getARMCPUForArch) { EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch()); } { + llvm::Triple Triple("arm--openbsd"); + EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch()); + } + { llvm::Triple Triple("armv6-unknown-freebsd"); EXPECT_EQ("arm1176jzf-s", Triple.getARMCPUForArch()); } diff --git a/unittests/Analysis/BlockFrequencyInfoTest.cpp b/unittests/Analysis/BlockFrequencyInfoTest.cpp index b3b0fcfb049bf..c5c9d4dea055b 100644 --- a/unittests/Analysis/BlockFrequencyInfoTest.cpp +++ b/unittests/Analysis/BlockFrequencyInfoTest.cpp @@ -80,6 +80,14 @@ TEST_F(BlockFrequencyInfoTest, Basic) { EXPECT_EQ(BFI.getBlockProfileCount(BB3).getValue(), UINT64_C(100)); EXPECT_EQ(BFI.getBlockProfileCount(BB1).getValue(), 100 * BB1Freq / BB0Freq); EXPECT_EQ(BFI.getBlockProfileCount(BB2).getValue(), 100 * BB2Freq / BB0Freq); + + // Scale the frequencies of BB0, BB1 and BB2 by a factor of two. + SmallPtrSet<BasicBlock *, 4> BlocksToScale({BB1, BB2}); + BFI.setBlockFreqAndScale(&BB0, BB0Freq * 2, BlocksToScale); + EXPECT_EQ(BFI.getBlockFreq(&BB0).getFrequency(), 2 * BB0Freq); + EXPECT_EQ(BFI.getBlockFreq(BB1).getFrequency(), 2 * BB1Freq); + EXPECT_EQ(BFI.getBlockFreq(BB2).getFrequency(), 2 * BB2Freq); + EXPECT_EQ(BFI.getBlockFreq(BB3).getFrequency(), BB3Freq); } } // end anonymous namespace diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt index ff4c17ee3b6b3..40d5ea5f5ad78 100644 --- a/unittests/Analysis/CMakeLists.txt +++ b/unittests/Analysis/CMakeLists.txt @@ -9,13 +9,17 @@ add_llvm_unittest(AnalysisTests AliasAnalysisTest.cpp BlockFrequencyInfoTest.cpp BranchProbabilityInfoTest.cpp - CallGraphTest.cpp CFGTest.cpp CGSCCPassManagerTest.cpp + CallGraphTest.cpp LazyCallGraphTest.cpp + LoopInfoTest.cpp MemoryBuiltinsTest.cpp + MemorySSA.cpp + ProfileSummaryInfoTest.cpp ScalarEvolutionTest.cpp TBAATest.cpp - ValueTrackingTest.cpp + TargetLibraryInfoTest.cpp UnrollAnalyzer.cpp + ValueTrackingTest.cpp ) diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp index 5bb9dec3449fe..6955beb37109d 100644 --- a/unittests/Analysis/LazyCallGraphTest.cpp +++ b/unittests/Analysis/LazyCallGraphTest.cpp @@ -225,29 +225,29 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { // the IR, and everything in our module is an entry node, so just directly // build variables for each node. auto I = CG.begin(); - LazyCallGraph::Node &A1 = (I++)->getNode(CG); + LazyCallGraph::Node &A1 = (I++)->getNode(); EXPECT_EQ("a1", A1.getFunction().getName()); - LazyCallGraph::Node &A2 = (I++)->getNode(CG); + LazyCallGraph::Node &A2 = (I++)->getNode(); EXPECT_EQ("a2", A2.getFunction().getName()); - LazyCallGraph::Node &A3 = (I++)->getNode(CG); + LazyCallGraph::Node &A3 = (I++)->getNode(); EXPECT_EQ("a3", A3.getFunction().getName()); - LazyCallGraph::Node &B1 = (I++)->getNode(CG); + LazyCallGraph::Node &B1 = (I++)->getNode(); EXPECT_EQ("b1", B1.getFunction().getName()); - LazyCallGraph::Node &B2 = (I++)->getNode(CG); + LazyCallGraph::Node &B2 = (I++)->getNode(); EXPECT_EQ("b2", B2.getFunction().getName()); - LazyCallGraph::Node &B3 = (I++)->getNode(CG); + LazyCallGraph::Node &B3 = (I++)->getNode(); EXPECT_EQ("b3", B3.getFunction().getName()); - LazyCallGraph::Node &C1 = (I++)->getNode(CG); + LazyCallGraph::Node &C1 = (I++)->getNode(); EXPECT_EQ("c1", C1.getFunction().getName()); - LazyCallGraph::Node &C2 = (I++)->getNode(CG); + LazyCallGraph::Node &C2 = (I++)->getNode(); EXPECT_EQ("c2", C2.getFunction().getName()); - LazyCallGraph::Node &C3 = (I++)->getNode(CG); + LazyCallGraph::Node &C3 = (I++)->getNode(); EXPECT_EQ("c3", C3.getFunction().getName()); - LazyCallGraph::Node &D1 = (I++)->getNode(CG); + LazyCallGraph::Node &D1 = (I++)->getNode(); EXPECT_EQ("d1", D1.getFunction().getName()); - LazyCallGraph::Node &D2 = (I++)->getNode(CG); + LazyCallGraph::Node &D2 = (I++)->getNode(); EXPECT_EQ("d2", D2.getFunction().getName()); - LazyCallGraph::Node &D3 = (I++)->getNode(CG); + LazyCallGraph::Node &D3 = (I++)->getNode(); EXPECT_EQ("d3", D3.getFunction().getName()); EXPECT_EQ(CG.end(), I); @@ -255,7 +255,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { // independent of order. std::vector<std::string> Nodes; - for (LazyCallGraph::Edge &E : A1) + for (LazyCallGraph::Edge &E : A1.populate()) Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("a2", Nodes[0]); @@ -263,43 +263,53 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_EQ("c3", Nodes[2]); Nodes.clear(); - EXPECT_EQ(A2.end(), std::next(A2.begin())); - EXPECT_EQ("a3", A2.begin()->getFunction().getName()); - EXPECT_EQ(A3.end(), std::next(A3.begin())); - EXPECT_EQ("a1", A3.begin()->getFunction().getName()); + A2.populate(); + EXPECT_EQ(A2->end(), std::next(A2->begin())); + EXPECT_EQ("a3", A2->begin()->getFunction().getName()); + A3.populate(); + EXPECT_EQ(A3->end(), std::next(A3->begin())); + EXPECT_EQ("a1", A3->begin()->getFunction().getName()); - for (LazyCallGraph::Edge &E : B1) + for (LazyCallGraph::Edge &E : B1.populate()) Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("b2", Nodes[0]); EXPECT_EQ("d3", Nodes[1]); Nodes.clear(); - EXPECT_EQ(B2.end(), std::next(B2.begin())); - EXPECT_EQ("b3", B2.begin()->getFunction().getName()); - EXPECT_EQ(B3.end(), std::next(B3.begin())); - EXPECT_EQ("b1", B3.begin()->getFunction().getName()); + B2.populate(); + EXPECT_EQ(B2->end(), std::next(B2->begin())); + EXPECT_EQ("b3", B2->begin()->getFunction().getName()); + B3.populate(); + EXPECT_EQ(B3->end(), std::next(B3->begin())); + EXPECT_EQ("b1", B3->begin()->getFunction().getName()); - for (LazyCallGraph::Edge &E : C1) + for (LazyCallGraph::Edge &E : C1.populate()) Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("c2", Nodes[0]); EXPECT_EQ("d2", Nodes[1]); Nodes.clear(); - EXPECT_EQ(C2.end(), std::next(C2.begin())); - EXPECT_EQ("c3", C2.begin()->getFunction().getName()); - EXPECT_EQ(C3.end(), std::next(C3.begin())); - EXPECT_EQ("c1", C3.begin()->getFunction().getName()); - - EXPECT_EQ(D1.end(), std::next(D1.begin())); - EXPECT_EQ("d2", D1.begin()->getFunction().getName()); - EXPECT_EQ(D2.end(), std::next(D2.begin())); - EXPECT_EQ("d3", D2.begin()->getFunction().getName()); - EXPECT_EQ(D3.end(), std::next(D3.begin())); - EXPECT_EQ("d1", D3.begin()->getFunction().getName()); + C2.populate(); + EXPECT_EQ(C2->end(), std::next(C2->begin())); + EXPECT_EQ("c3", C2->begin()->getFunction().getName()); + C3.populate(); + EXPECT_EQ(C3->end(), std::next(C3->begin())); + EXPECT_EQ("c1", C3->begin()->getFunction().getName()); + + D1.populate(); + EXPECT_EQ(D1->end(), std::next(D1->begin())); + EXPECT_EQ("d2", D1->begin()->getFunction().getName()); + D2.populate(); + EXPECT_EQ(D2->end(), std::next(D2->begin())); + EXPECT_EQ("d3", D2->begin()->getFunction().getName()); + D3.populate(); + EXPECT_EQ(D3->end(), std::next(D3->begin())); + EXPECT_EQ("d1", D3->begin()->getFunction().getName()); // Now lets look at the RefSCCs and SCCs. + CG.buildRefSCCs(); auto J = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &D = *J++; @@ -401,32 +411,35 @@ TEST(LazyCallGraphTest, BasicGraphMutation) { LazyCallGraph::Node &A = CG.get(lookupFunction(*M, "a")); LazyCallGraph::Node &B = CG.get(lookupFunction(*M, "b")); - EXPECT_EQ(2, std::distance(A.begin(), A.end())); - EXPECT_EQ(0, std::distance(B.begin(), B.end())); - - CG.insertEdge(B, lookupFunction(*M, "c"), LazyCallGraph::Edge::Call); - EXPECT_EQ(1, std::distance(B.begin(), B.end())); - LazyCallGraph::Node &C = B.begin()->getNode(CG); - EXPECT_EQ(0, std::distance(C.begin(), C.end())); - - CG.insertEdge(C, B.getFunction(), LazyCallGraph::Edge::Call); - EXPECT_EQ(1, std::distance(C.begin(), C.end())); - EXPECT_EQ(&B, C.begin()->getNode()); - - CG.insertEdge(C, C.getFunction(), LazyCallGraph::Edge::Call); - EXPECT_EQ(2, std::distance(C.begin(), C.end())); - EXPECT_EQ(&B, C.begin()->getNode()); - EXPECT_EQ(&C, std::next(C.begin())->getNode()); - - CG.removeEdge(C, B.getFunction()); - EXPECT_EQ(1, std::distance(C.begin(), C.end())); - EXPECT_EQ(&C, C.begin()->getNode()); - - CG.removeEdge(C, C.getFunction()); - EXPECT_EQ(0, std::distance(C.begin(), C.end())); - - CG.removeEdge(B, C.getFunction()); - EXPECT_EQ(0, std::distance(B.begin(), B.end())); + A.populate(); + EXPECT_EQ(2, std::distance(A->begin(), A->end())); + B.populate(); + EXPECT_EQ(0, std::distance(B->begin(), B->end())); + + LazyCallGraph::Node &C = CG.get(lookupFunction(*M, "c")); + C.populate(); + CG.insertEdge(B, C, LazyCallGraph::Edge::Call); + EXPECT_EQ(1, std::distance(B->begin(), B->end())); + EXPECT_EQ(0, std::distance(C->begin(), C->end())); + + CG.insertEdge(C, B, LazyCallGraph::Edge::Call); + EXPECT_EQ(1, std::distance(C->begin(), C->end())); + EXPECT_EQ(&B, &C->begin()->getNode()); + + CG.insertEdge(C, C, LazyCallGraph::Edge::Call); + EXPECT_EQ(2, std::distance(C->begin(), C->end())); + EXPECT_EQ(&B, &C->begin()->getNode()); + EXPECT_EQ(&C, &std::next(C->begin())->getNode()); + + CG.removeEdge(C, B); + EXPECT_EQ(1, std::distance(C->begin(), C->end())); + EXPECT_EQ(&C, &C->begin()->getNode()); + + CG.removeEdge(C, C); + EXPECT_EQ(0, std::distance(C->begin(), C->end())); + + CG.removeEdge(B, C); + EXPECT_EQ(0, std::distance(B->begin(), B->end())); } TEST(LazyCallGraphTest, InnerSCCFormation) { @@ -436,14 +449,18 @@ TEST(LazyCallGraphTest, InnerSCCFormation) { // Now mutate the graph to connect every node into a single RefSCC to ensure // that our inner SCC formation handles the rest. - CG.insertEdge(lookupFunction(*M, "d1"), lookupFunction(*M, "a1"), - LazyCallGraph::Edge::Ref); + LazyCallGraph::Node &D1 = CG.get(lookupFunction(*M, "d1")); + LazyCallGraph::Node &A1 = CG.get(lookupFunction(*M, "a1")); + A1.populate(); + D1.populate(); + CG.insertEdge(D1, A1, LazyCallGraph::Edge::Ref); // Build vectors and sort them for the rest of the assertions to make them // independent of order. std::vector<std::string> Nodes; // We should build a single RefSCC for the entire graph. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &RC = *I++; EXPECT_EQ(CG.postorder_ref_scc_end(), I); @@ -528,6 +545,7 @@ TEST(LazyCallGraphTest, MultiArmSCC) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &RC = *I++; EXPECT_EQ(CG.postorder_ref_scc_end(), I); @@ -578,6 +596,7 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) dbgs() << "Formed RefSCC: " << RC << "\n"; @@ -610,13 +629,13 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { EXPECT_TRUE(DRC.isChildOf(CRC)); EXPECT_TRUE(DC.isChildOf(CC)); - EXPECT_EQ(2, std::distance(A.begin(), A.end())); + EXPECT_EQ(2, std::distance(A->begin(), A->end())); ARC.insertOutgoingEdge(A, D, LazyCallGraph::Edge::Call); - EXPECT_EQ(3, std::distance(A.begin(), A.end())); - const LazyCallGraph::Edge &NewE = A[D]; + EXPECT_EQ(3, std::distance(A->begin(), A->end())); + const LazyCallGraph::Edge &NewE = (*A)[D]; EXPECT_TRUE(NewE); EXPECT_TRUE(NewE.isCall()); - EXPECT_EQ(&D, NewE.getNode()); + EXPECT_EQ(&D, &NewE.getNode()); // Only the parent and child tests sholud have changed. The rest of the graph // remains the same. @@ -680,7 +699,7 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { EXPECT_EQ(&DRC, CG.lookupRefSCC(D)); ARC.removeOutgoingEdge(A, D); - EXPECT_EQ(2, std::distance(A.begin(), A.end())); + EXPECT_EQ(2, std::distance(A->begin(), A->end())); // Now the parent and child tests fail again but the rest remains the same. EXPECT_FALSE(ARC.isParentOf(DRC)); @@ -723,6 +742,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertion) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) dbgs() << "Formed RefSCC: " << RC << "\n"; @@ -750,7 +770,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertion) { ASSERT_EQ(&CRC, CG.lookupRefSCC(C3)); ASSERT_EQ(&DRC, CG.lookupRefSCC(D2)); ASSERT_EQ(&DRC, CG.lookupRefSCC(D3)); - ASSERT_EQ(1, std::distance(D2.begin(), D2.end())); + ASSERT_EQ(1, std::distance(D2->begin(), D2->end())); // Add an edge to make the graph: // @@ -767,10 +787,10 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertion) { // a3--a2 | auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2); // Make sure we connected the nodes. - for (LazyCallGraph::Edge E : D2) { - if (E.getNode() == &D3) + for (LazyCallGraph::Edge E : *D2) { + if (&E.getNode() == &D3) continue; - EXPECT_EQ(&C2, E.getNode()); + EXPECT_EQ(&C2, &E.getNode()); } // And marked the D ref-SCC as no longer valid. EXPECT_EQ(1u, MergedRCs.size()); @@ -805,102 +825,6 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertion) { EXPECT_EQ(++I, E); } -TEST(LazyCallGraphTest, IncomingEdgeInsertionMidTraversal) { - LLVMContext Context; - // This is the same fundamental test as the previous, but we perform it - // having only partially walked the RefSCCs of the graph. - std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); - LazyCallGraph CG(*M); - - // Walk the RefSCCs until we find the one containing 'c1'. - auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); - ASSERT_NE(I, E); - LazyCallGraph::RefSCC &DRC = *I; - ASSERT_NE(&DRC, nullptr); - ++I; - ASSERT_NE(I, E); - LazyCallGraph::RefSCC &CRC = *I; - ASSERT_NE(&CRC, nullptr); - - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a1"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a2"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a3"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b1"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b2"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b3"))); - LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1")); - LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2")); - LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3")); - LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1")); - LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2")); - LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3")); - ASSERT_EQ(&CRC, CG.lookupRefSCC(C1)); - ASSERT_EQ(&CRC, CG.lookupRefSCC(C2)); - ASSERT_EQ(&CRC, CG.lookupRefSCC(C3)); - ASSERT_EQ(&DRC, CG.lookupRefSCC(D1)); - ASSERT_EQ(&DRC, CG.lookupRefSCC(D2)); - ASSERT_EQ(&DRC, CG.lookupRefSCC(D3)); - ASSERT_EQ(1, std::distance(D2.begin(), D2.end())); - - auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2); - // Make sure we connected the nodes. - for (LazyCallGraph::Edge E : D2) { - if (E.getNode() == &D3) - continue; - EXPECT_EQ(&C2, E.getNode()); - } - // And marked the D ref-SCC as no longer valid. - EXPECT_EQ(1u, MergedRCs.size()); - EXPECT_EQ(&DRC, MergedRCs[0]); - - // Make sure we have the correct nodes in the RefSCCs. - EXPECT_EQ(&CRC, CG.lookupRefSCC(C1)); - EXPECT_EQ(&CRC, CG.lookupRefSCC(C2)); - EXPECT_EQ(&CRC, CG.lookupRefSCC(C3)); - EXPECT_EQ(&CRC, CG.lookupRefSCC(D1)); - EXPECT_EQ(&CRC, CG.lookupRefSCC(D2)); - EXPECT_EQ(&CRC, CG.lookupRefSCC(D3)); - - // Verify that the post-order walk reflects the updated but still incomplete - // structure. - auto J = CG.postorder_ref_scc_begin(); - EXPECT_NE(J, E); - EXPECT_EQ(&CRC, &*J) << "Actual RefSCC: " << *J; - EXPECT_EQ(I, J); - - // Check that we can form the last two RefSCCs now, and even that we can do - // it with alternating iterators. - ++J; - EXPECT_NE(J, E); - LazyCallGraph::RefSCC &BRC = *J; - EXPECT_NE(&BRC, nullptr); - EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b1")))); - EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b2")))); - EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b3")))); - EXPECT_TRUE(BRC.isParentOf(CRC)); - ++I; - EXPECT_EQ(J, I); - EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I; - - // Increment I this time to form the new RefSCC, flopping back to the first - // iterator. - ++I; - EXPECT_NE(I, E); - LazyCallGraph::RefSCC &ARC = *I; - EXPECT_NE(&ARC, nullptr); - EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a1")))); - EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a2")))); - EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a3")))); - EXPECT_TRUE(ARC.isParentOf(CRC)); - ++J; - EXPECT_EQ(I, J); - EXPECT_EQ(&ARC, &*J) << "Actual RefSCC: " << *J; - ++I; - EXPECT_EQ(E, I); - ++J; - EXPECT_EQ(E, J); -} - TEST(LazyCallGraphTest, IncomingEdgeInsertionRefGraph) { LLVMContext Context; // Another variation of the above test but with all the edges switched to @@ -910,6 +834,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionRefGraph) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) dbgs() << "Formed RefSCC: " << RC << "\n"; @@ -937,7 +862,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionRefGraph) { ASSERT_EQ(&CRC, CG.lookupRefSCC(C3)); ASSERT_EQ(&DRC, CG.lookupRefSCC(D2)); ASSERT_EQ(&DRC, CG.lookupRefSCC(D3)); - ASSERT_EQ(1, std::distance(D2.begin(), D2.end())); + ASSERT_EQ(1, std::distance(D2->begin(), D2->end())); // Add an edge to make the graph: // @@ -954,10 +879,10 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionRefGraph) { // a3--a2 | auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2); // Make sure we connected the nodes. - for (LazyCallGraph::Edge E : D2) { - if (E.getNode() == &D3) + for (LazyCallGraph::Edge E : *D2) { + if (&E.getNode() == &D3) continue; - EXPECT_EQ(&C2, E.getNode()); + EXPECT_EQ(&C2, &E.getNode()); } // And marked the D ref-SCC as no longer valid. EXPECT_EQ(1u, MergedRCs.size()); @@ -1016,6 +941,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeCallCycle) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) dbgs() << "Formed RefSCC: " << RC << "\n"; @@ -1035,8 +961,8 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeCallCycle) { // Connect the top to the bottom forming a large RefSCC made up mostly of calls. auto MergedRCs = ARC.insertIncomingRefEdge(D, A); // Make sure we connected the nodes. - EXPECT_NE(D.begin(), D.end()); - EXPECT_EQ(&A, D.begin()->getNode()); + EXPECT_NE(D->begin(), D->end()); + EXPECT_EQ(&A, &D->begin()->getNode()); // Check that we have the dead RCs, but ignore the order. EXPECT_EQ(3u, MergedRCs.size()); @@ -1092,6 +1018,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeRefCycle) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) dbgs() << "Formed RefSCC: " << RC << "\n"; @@ -1108,8 +1035,8 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeRefCycle) { // references. auto MergedRCs = ARC.insertIncomingRefEdge(D, A); // Make sure we connected the nodes. - EXPECT_NE(D.begin(), D.end()); - EXPECT_EQ(&A, D.begin()->getNode()); + EXPECT_NE(D->begin(), D->end()); + EXPECT_EQ(&A, &D->begin()->getNode()); // Check that we have the dead RCs, but ignore the order. EXPECT_EQ(3u, MergedRCs.size()); @@ -1153,6 +1080,7 @@ TEST(LazyCallGraphTest, InlineAndDeleteFunction) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) dbgs() << "Formed RefSCC: " << RC << "\n"; @@ -1180,7 +1108,7 @@ TEST(LazyCallGraphTest, InlineAndDeleteFunction) { ASSERT_EQ(&CRC, CG.lookupRefSCC(C3)); ASSERT_EQ(&DRC, CG.lookupRefSCC(D2)); ASSERT_EQ(&DRC, CG.lookupRefSCC(D3)); - ASSERT_EQ(1, std::distance(D2.begin(), D2.end())); + ASSERT_EQ(1, std::distance(D2->begin(), D2->end())); // Delete d2 from the graph, as if it had been inlined. // @@ -1276,177 +1204,6 @@ TEST(LazyCallGraphTest, InlineAndDeleteFunction) { EXPECT_EQ(++I, E); } -TEST(LazyCallGraphTest, InlineAndDeleteFunctionMidTraversal) { - LLVMContext Context; - // This is the same fundamental test as the previous, but we perform it - // having only partially walked the RefSCCs of the graph. - // - // The ascii diagram is repeated here for easy reference. - // - // d1 | - // / \ | - // d3--d2 | - // / \ | - // b1 c1 | - // / \ / \ | - // b3--b2 c3--c2 | - // \ / | - // a1 | - // / \ | - // a3--a2 | - // - std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); - LazyCallGraph CG(*M); - - // Walk the RefSCCs until we find the one containing 'c1'. - auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); - ASSERT_NE(I, E); - LazyCallGraph::RefSCC &DRC = *I; - ASSERT_NE(&DRC, nullptr); - ++I; - ASSERT_NE(I, E); - LazyCallGraph::RefSCC &CRC = *I; - ASSERT_NE(&CRC, nullptr); - - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a1"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a2"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a3"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b1"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b2"))); - ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b3"))); - LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1")); - LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2")); - LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3")); - LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1")); - LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2")); - LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3")); - ASSERT_EQ(&CRC, CG.lookupRefSCC(C1)); - ASSERT_EQ(&CRC, CG.lookupRefSCC(C2)); - ASSERT_EQ(&CRC, CG.lookupRefSCC(C3)); - ASSERT_EQ(&DRC, CG.lookupRefSCC(D1)); - ASSERT_EQ(&DRC, CG.lookupRefSCC(D2)); - ASSERT_EQ(&DRC, CG.lookupRefSCC(D3)); - ASSERT_EQ(1, std::distance(D2.begin(), D2.end())); - - // Delete d2 from the graph, as if it had been inlined. - // - // d1 | - // / / | - // d3--. | - // / \ | - // b1 c1 | - // / \ / \ | - // b3--b2 c3--c2 | - // \ / | - // a1 | - // / \ | - // a3--a2 | - - Function &D2F = D2.getFunction(); - CallInst *C1Call = nullptr, *D1Call = nullptr; - for (User *U : D2F.users()) { - CallInst *CI = dyn_cast<CallInst>(U); - ASSERT_TRUE(CI) << "Expected a call: " << *U; - if (CI->getParent()->getParent() == &C1.getFunction()) { - ASSERT_EQ(nullptr, C1Call) << "Found too many C1 calls: " << *CI; - C1Call = CI; - } else if (CI->getParent()->getParent() == &D1.getFunction()) { - ASSERT_EQ(nullptr, D1Call) << "Found too many D1 calls: " << *CI; - D1Call = CI; - } else { - FAIL() << "Found an unexpected call instruction: " << *CI; - } - } - ASSERT_NE(C1Call, nullptr); - ASSERT_NE(D1Call, nullptr); - ASSERT_EQ(&D2F, C1Call->getCalledFunction()); - ASSERT_EQ(&D2F, D1Call->getCalledFunction()); - C1Call->setCalledFunction(&D3.getFunction()); - D1Call->setCalledFunction(&D3.getFunction()); - ASSERT_EQ(0u, D2F.getNumUses()); - - // Insert new edges first. - CRC.insertTrivialCallEdge(C1, D3); - DRC.insertTrivialCallEdge(D1, D3); - - // Then remove the old ones. - LazyCallGraph::SCC &DC = *CG.lookupSCC(D2); - auto NewCs = DRC.switchInternalEdgeToRef(D1, D2); - EXPECT_EQ(&DC, CG.lookupSCC(D2)); - EXPECT_EQ(NewCs.end(), std::next(NewCs.begin())); - LazyCallGraph::SCC &NewDC = *NewCs.begin(); - EXPECT_EQ(&NewDC, CG.lookupSCC(D1)); - EXPECT_EQ(&NewDC, CG.lookupSCC(D3)); - auto NewRCs = DRC.removeInternalRefEdge(D1, D2); - EXPECT_EQ(&DRC, CG.lookupRefSCC(D2)); - EXPECT_EQ(NewRCs.end(), std::next(NewRCs.begin())); - LazyCallGraph::RefSCC &NewDRC = **NewRCs.begin(); - EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D1)); - EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3)); - EXPECT_FALSE(NewDRC.isParentOf(DRC)); - EXPECT_TRUE(CRC.isParentOf(DRC)); - EXPECT_TRUE(CRC.isParentOf(NewDRC)); - EXPECT_TRUE(DRC.isParentOf(NewDRC)); - CRC.removeOutgoingEdge(C1, D2); - EXPECT_FALSE(CRC.isParentOf(DRC)); - EXPECT_TRUE(CRC.isParentOf(NewDRC)); - EXPECT_TRUE(DRC.isParentOf(NewDRC)); - - // Now that we've updated the call graph, D2 is dead, so remove it. - CG.removeDeadFunction(D2F); - - // Check that the graph still looks the same. - EXPECT_EQ(&CRC, CG.lookupRefSCC(C1)); - EXPECT_EQ(&CRC, CG.lookupRefSCC(C2)); - EXPECT_EQ(&CRC, CG.lookupRefSCC(C3)); - EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D1)); - EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3)); - EXPECT_TRUE(CRC.isParentOf(NewDRC)); - - // Verify that the post-order walk reflects the updated but still incomplete - // structure. - auto J = CG.postorder_ref_scc_begin(); - EXPECT_NE(J, E); - EXPECT_EQ(&NewDRC, &*J) << "Actual RefSCC: " << *J; - ++J; - EXPECT_NE(J, E); - EXPECT_EQ(&CRC, &*J) << "Actual RefSCC: " << *J; - EXPECT_EQ(I, J); - - // Check that we can form the last two RefSCCs now, and even that we can do - // it with alternating iterators. - ++J; - EXPECT_NE(J, E); - LazyCallGraph::RefSCC &BRC = *J; - EXPECT_NE(&BRC, nullptr); - EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b1")))); - EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b2")))); - EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b3")))); - EXPECT_TRUE(BRC.isParentOf(NewDRC)); - ++I; - EXPECT_EQ(J, I); - EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I; - - // Increment I this time to form the new RefSCC, flopping back to the first - // iterator. - ++I; - EXPECT_NE(I, E); - LazyCallGraph::RefSCC &ARC = *I; - EXPECT_NE(&ARC, nullptr); - EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a1")))); - EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a2")))); - EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a3")))); - EXPECT_TRUE(ARC.isParentOf(BRC)); - EXPECT_TRUE(ARC.isParentOf(CRC)); - ++J; - EXPECT_EQ(I, J); - EXPECT_EQ(&ARC, &*J) << "Actual RefSCC: " << *J; - ++I; - EXPECT_EQ(E, I); - ++J; - EXPECT_EQ(E, J); -} - TEST(LazyCallGraphTest, InternalEdgeMutation) { LLVMContext Context; std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n" @@ -1467,6 +1224,7 @@ TEST(LazyCallGraphTest, InternalEdgeMutation) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &RC = *I++; EXPECT_EQ(CG.postorder_ref_scc_end(), I); @@ -1484,7 +1242,7 @@ TEST(LazyCallGraphTest, InternalEdgeMutation) { // Insert an edge from 'a' to 'c'. Nothing changes about the graph. RC.insertInternalRefEdge(A, C); - EXPECT_EQ(2, std::distance(A.begin(), A.end())); + EXPECT_EQ(2, std::distance(A->begin(), A->end())); EXPECT_EQ(&RC, CG.lookupRefSCC(A)); EXPECT_EQ(&RC, CG.lookupRefSCC(B)); EXPECT_EQ(&RC, CG.lookupRefSCC(C)); @@ -1559,6 +1317,7 @@ TEST(LazyCallGraphTest, InternalEdgeRemoval) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); LazyCallGraph::RefSCC &RC = *I; EXPECT_EQ(E, std::next(I)); @@ -1633,6 +1392,7 @@ TEST(LazyCallGraphTest, InternalNoOpEdgeRemoval) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); LazyCallGraph::RefSCC &RC = *I; EXPECT_EQ(E, std::next(I)); @@ -1709,6 +1469,7 @@ TEST(LazyCallGraphTest, InternalCallEdgeToRef) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &RC = *I++; EXPECT_EQ(CG.postorder_ref_scc_end(), I); @@ -1801,6 +1562,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCall) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &RC = *I++; EXPECT_EQ(CG.postorder_ref_scc_end(), I); @@ -1913,6 +1675,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCallNoCycleInterleaved) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &RC = *I++; EXPECT_EQ(CG.postorder_ref_scc_end(), I); @@ -2043,6 +1806,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCallBothPartitionAndMerge) { LazyCallGraph CG(*M); // Force the graph to be fully expanded. + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &RC = *I++; EXPECT_EQ(CG.postorder_ref_scc_end(), I); @@ -2122,6 +1886,7 @@ TEST(LazyCallGraphTest, HandleBlockAddress) { "}\n"); LazyCallGraph CG(*M); + CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); LazyCallGraph::RefSCC &FRC = *I++; LazyCallGraph::RefSCC &GRC = *I++; @@ -2134,4 +1899,165 @@ TEST(LazyCallGraphTest, HandleBlockAddress) { EXPECT_TRUE(GRC.isParentOf(FRC)); } +TEST(LazyCallGraphTest, ReplaceNodeFunction) { + LLVMContext Context; + // A graph with several different kinds of edges pointing at a particular + // function. + std::unique_ptr<Module> M = + parseAssembly(Context, + "define void @a(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @b(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " call void @d(i8** %ptr)" + " ret void\n" + "}\n" + "define void @c(i8** %ptr) {\n" + "entry:\n" + " call void @d(i8** %ptr)" + " call void @d(i8** %ptr)" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @d(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" + " call void @c(i8** %ptr)" + " call void @d(i8** %ptr)" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC1 = *I++; + LazyCallGraph::RefSCC &RC2 = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + ASSERT_EQ(2, RC1.size()); + LazyCallGraph::SCC &C1 = RC1[0]; + LazyCallGraph::SCC &C2 = RC1[1]; + + LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c")); + LazyCallGraph::Node &DN = *CG.lookup(lookupFunction(*M, "d")); + EXPECT_EQ(&C1, CG.lookupSCC(DN)); + EXPECT_EQ(&C1, CG.lookupSCC(CN)); + EXPECT_EQ(&C2, CG.lookupSCC(BN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(DN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(CN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(BN)); + EXPECT_EQ(&RC2, CG.lookupRefSCC(AN)); + + // Now we need to build a new function 'e' with the same signature as 'd'. + Function &D = DN.getFunction(); + Function &E = *Function::Create(D.getFunctionType(), D.getLinkage(), "e"); + D.getParent()->getFunctionList().insert(D.getIterator(), &E); + + // Change each use of 'd' to use 'e'. This is particularly easy as they have + // the same type. + D.replaceAllUsesWith(&E); + + // Splice the body of the old function into the new one. + E.getBasicBlockList().splice(E.begin(), D.getBasicBlockList()); + // And fix up the one argument. + D.arg_begin()->replaceAllUsesWith(&*E.arg_begin()); + E.arg_begin()->takeName(&*D.arg_begin()); + + // Now replace the function in the graph. + RC1.replaceNodeFunction(DN, E); + + EXPECT_EQ(&E, &DN.getFunction()); + EXPECT_EQ(&DN, &(*CN)[DN].getNode()); + EXPECT_EQ(&DN, &(*BN)[DN].getNode()); +} + +TEST(LazyCallGraphTest, RemoveFunctionWithSpurriousRef) { + LLVMContext Context; + // A graph with a couple of RefSCCs. + std::unique_ptr<Module> M = + parseAssembly(Context, + "define void @a(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @b(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @c(i8** %ptr) {\n" + "entry:\n" + " call void @d(i8** %ptr)" + " ret void\n" + "}\n" + "define void @d(i8** %ptr) {\n" + "entry:\n" + " call void @c(i8** %ptr)" + " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @dead() {\n" + "entry:\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + // Insert spurious ref edges. + LazyCallGraph::Node &AN = CG.get(lookupFunction(*M, "a")); + LazyCallGraph::Node &BN = CG.get(lookupFunction(*M, "b")); + LazyCallGraph::Node &CN = CG.get(lookupFunction(*M, "c")); + LazyCallGraph::Node &DN = CG.get(lookupFunction(*M, "d")); + LazyCallGraph::Node &DeadN = CG.get(lookupFunction(*M, "dead")); + AN.populate(); + BN.populate(); + CN.populate(); + DN.populate(); + DeadN.populate(); + CG.insertEdge(AN, DeadN, LazyCallGraph::Edge::Ref); + CG.insertEdge(BN, DeadN, LazyCallGraph::Edge::Ref); + CG.insertEdge(CN, DeadN, LazyCallGraph::Edge::Ref); + CG.insertEdge(DN, DeadN, LazyCallGraph::Edge::Ref); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &DeadRC = *I++; + LazyCallGraph::RefSCC &RC1 = *I++; + LazyCallGraph::RefSCC &RC2 = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + ASSERT_EQ(2, RC1.size()); + LazyCallGraph::SCC &C1 = RC1[0]; + LazyCallGraph::SCC &C2 = RC1[1]; + + EXPECT_EQ(&DeadRC, CG.lookupRefSCC(DeadN)); + EXPECT_EQ(&C1, CG.lookupSCC(DN)); + EXPECT_EQ(&C1, CG.lookupSCC(CN)); + EXPECT_EQ(&C2, CG.lookupSCC(BN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(DN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(CN)); + EXPECT_EQ(&RC1, CG.lookupRefSCC(BN)); + EXPECT_EQ(&RC2, CG.lookupRefSCC(AN)); + + // Now delete 'dead'. There are no uses of this function but there are + // spurious references. + CG.removeDeadFunction(DeadN.getFunction()); + + // The only observable change should be that the RefSCC is gone from the + // postorder sequence. + I = CG.postorder_ref_scc_begin(); + EXPECT_EQ(&RC1, &*I++); + EXPECT_EQ(&RC2, &*I++); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); +} } diff --git a/unittests/Analysis/LoopInfoTest.cpp b/unittests/Analysis/LoopInfoTest.cpp new file mode 100644 index 0000000000000..647ce8a3c1ba0 --- /dev/null +++ b/unittests/Analysis/LoopInfoTest.cpp @@ -0,0 +1,158 @@ +//===- LoopInfoTest.cpp - LoopInfo unit tests -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/Dominators.h" +#include "llvm/Support/SourceMgr.h" +#include "gtest/gtest.h" + +using namespace llvm; + +/// Build the loop info for the function and run the Test. +static void +runWithLoopInfo(Module &M, StringRef FuncName, + function_ref<void(Function &F, LoopInfo &LI)> Test) { + auto *F = M.getFunction(FuncName); + ASSERT_NE(F, nullptr) << "Could not find " << FuncName; + // Compute the dominator tree and the loop info for the function. + DominatorTree DT(*F); + LoopInfo LI(DT); + Test(*F, LI); +} + +static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context, + const char *ModuleStr) { + SMDiagnostic Err; + return parseAssemblyString(ModuleStr, Err, Context); +} + +// This tests that for a loop with a single latch, we get the loop id from +// its only latch, even in case the loop may not be in a simplified form. +TEST(LoopInfoTest, LoopWithSingleLatch) { + const char *ModuleStr = + "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n" + "define void @foo(i32 %n) {\n" + "entry:\n" + " br i1 undef, label %for.cond, label %for.end\n" + "for.cond:\n" + " %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]\n" + " %cmp = icmp slt i32 %i.0, %n\n" + " br i1 %cmp, label %for.inc, label %for.end\n" + "for.inc:\n" + " %inc = add nsw i32 %i.0, 1\n" + " br label %for.cond, !llvm.loop !0\n" + "for.end:\n" + " ret void\n" + "}\n" + "!0 = distinct !{!0, !1}\n" + "!1 = !{!\"llvm.loop.distribute.enable\", i1 true}\n"; + + // Parse the module. + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr); + + runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) { + Function::iterator FI = F.begin(); + // First basic block is entry - skip it. + BasicBlock *Header = &*(++FI); + assert(Header->getName() == "for.cond"); + Loop *L = LI.getLoopFor(Header); + + // This loop is not in simplified form. + EXPECT_FALSE(L->isLoopSimplifyForm()); + + // Analyze the loop metadata id. + bool loopIDFoundAndSet = false; + // Try to get and set the metadata id for the loop. + if (MDNode *D = L->getLoopID()) { + L->setLoopID(D); + loopIDFoundAndSet = true; + } + + // We must have successfully found and set the loop id in the + // only latch the loop has. + EXPECT_TRUE(loopIDFoundAndSet); + }); +} + +TEST(LoopInfoTest, PreorderTraversals) { + const char *ModuleStr = "define void @f() {\n" + "entry:\n" + " br label %loop.0\n" + "loop.0:\n" + " br i1 undef, label %loop.0.0, label %loop.1\n" + "loop.0.0:\n" + " br i1 undef, label %loop.0.0, label %loop.0.1\n" + "loop.0.1:\n" + " br i1 undef, label %loop.0.1, label %loop.0.2\n" + "loop.0.2:\n" + " br i1 undef, label %loop.0.2, label %loop.0\n" + "loop.1:\n" + " br i1 undef, label %loop.1.0, label %end\n" + "loop.1.0:\n" + " br i1 undef, label %loop.1.0, label %loop.1.1\n" + "loop.1.1:\n" + " br i1 undef, label %loop.1.1, label %loop.1.2\n" + "loop.1.2:\n" + " br i1 undef, label %loop.1.2, label %loop.1\n" + "end:\n" + " ret void\n" + "}\n"; + // Parse the module. + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr); + Function &F = *M->begin(); + + DominatorTree DT(F); + LoopInfo LI; + LI.analyze(DT); + + Function::iterator I = F.begin(); + ASSERT_EQ("entry", I->getName()); + ++I; + Loop &L_0 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.0", L_0.getHeader()->getName()); + Loop &L_0_0 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.0.0", L_0_0.getHeader()->getName()); + Loop &L_0_1 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.0.1", L_0_1.getHeader()->getName()); + Loop &L_0_2 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.0.2", L_0_2.getHeader()->getName()); + Loop &L_1 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.1", L_1.getHeader()->getName()); + Loop &L_1_0 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.1.0", L_1_0.getHeader()->getName()); + Loop &L_1_1 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.1.1", L_1_1.getHeader()->getName()); + Loop &L_1_2 = *LI.getLoopFor(&*I++); + ASSERT_EQ("loop.1.2", L_1_2.getHeader()->getName()); + + auto Preorder = LI.getLoopsInPreorder(); + ASSERT_EQ(8u, Preorder.size()); + EXPECT_EQ(&L_0, Preorder[0]); + EXPECT_EQ(&L_0_0, Preorder[1]); + EXPECT_EQ(&L_0_1, Preorder[2]); + EXPECT_EQ(&L_0_2, Preorder[3]); + EXPECT_EQ(&L_1, Preorder[4]); + EXPECT_EQ(&L_1_0, Preorder[5]); + EXPECT_EQ(&L_1_1, Preorder[6]); + EXPECT_EQ(&L_1_2, Preorder[7]); + + auto ReverseSiblingPreorder = LI.getLoopsInReverseSiblingPreorder(); + ASSERT_EQ(8u, ReverseSiblingPreorder.size()); + EXPECT_EQ(&L_1, ReverseSiblingPreorder[0]); + EXPECT_EQ(&L_1_2, ReverseSiblingPreorder[1]); + EXPECT_EQ(&L_1_1, ReverseSiblingPreorder[2]); + EXPECT_EQ(&L_1_0, ReverseSiblingPreorder[3]); + EXPECT_EQ(&L_0, ReverseSiblingPreorder[4]); + EXPECT_EQ(&L_0_2, ReverseSiblingPreorder[5]); + EXPECT_EQ(&L_0_1, ReverseSiblingPreorder[6]); + EXPECT_EQ(&L_0_0, ReverseSiblingPreorder[7]); +} diff --git a/unittests/Transforms/Utils/MemorySSA.cpp b/unittests/Analysis/MemorySSA.cpp index 945fe32c316c2..08b0e830a9b2e 100644 --- a/unittests/Transforms/Utils/MemorySSA.cpp +++ b/unittests/Analysis/MemorySSA.cpp @@ -6,9 +6,10 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -#include "llvm/Transforms/Utils/MemorySSA.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/BasicAliasAnalysis.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" @@ -89,6 +90,7 @@ TEST_F(MemorySSATest, CreateALoad) { setupAnalyses(); MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); // Add the load B.SetInsertPoint(Merge); LoadInst *LoadInst = B.CreateLoad(PointerArg); @@ -98,8 +100,145 @@ TEST_F(MemorySSATest, CreateALoad) { EXPECT_NE(MP, nullptr); // Create the load memory acccess - MemoryUse *LoadAccess = cast<MemoryUse>( - MSSA.createMemoryAccessInBB(LoadInst, MP, Merge, MemorySSA::Beginning)); + MemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB( + LoadInst, MP, Merge, MemorySSA::Beginning)); + MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); + EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); + MSSA.verifyMemorySSA(); +} +TEST_F(MemorySSATest, CreateLoadsAndStoreUpdater) { + // We create a diamond, then build memoryssa with no memory accesses, and + // incrementally update it by inserting a store in the, entry, a load in the + // merge point, then a store in the branch, another load in the merge point, + // and then a store in the entry. + F = Function::Create( + FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + BasicBlock *Entry(BasicBlock::Create(C, "", F)); + BasicBlock *Left(BasicBlock::Create(C, "", F)); + BasicBlock *Right(BasicBlock::Create(C, "", F)); + BasicBlock *Merge(BasicBlock::Create(C, "", F)); + B.SetInsertPoint(Entry); + B.CreateCondBr(B.getTrue(), Left, Right); + B.SetInsertPoint(Left, Left->begin()); + Argument *PointerArg = &*F->arg_begin(); + B.SetInsertPoint(Left); + B.CreateBr(Merge); + B.SetInsertPoint(Right); + B.CreateBr(Merge); + + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); + // Add the store + B.SetInsertPoint(Entry, Entry->begin()); + StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg); + MemoryAccess *EntryStoreAccess = Updater.createMemoryAccessInBB( + EntryStore, nullptr, Entry, MemorySSA::Beginning); + Updater.insertDef(cast<MemoryDef>(EntryStoreAccess)); + + // Add the load + B.SetInsertPoint(Merge, Merge->begin()); + LoadInst *FirstLoad = B.CreateLoad(PointerArg); + + // MemoryPHI should not already exist. + MemoryPhi *MP = MSSA.getMemoryAccess(Merge); + EXPECT_EQ(MP, nullptr); + + // Create the load memory access + MemoryUse *FirstLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB( + FirstLoad, nullptr, Merge, MemorySSA::Beginning)); + Updater.insertUse(FirstLoadAccess); + // Should just have a load using the entry access, because it should discover + // the phi is trivial + EXPECT_EQ(FirstLoadAccess->getDefiningAccess(), EntryStoreAccess); + + // Create a store on the left + // Add the store + B.SetInsertPoint(Left, Left->begin()); + StoreInst *LeftStore = B.CreateStore(B.getInt8(16), PointerArg); + MemoryAccess *LeftStoreAccess = Updater.createMemoryAccessInBB( + LeftStore, nullptr, Left, MemorySSA::Beginning); + Updater.insertDef(cast<MemoryDef>(LeftStoreAccess), false); + // We don't touch existing loads, so we need to create a new one to get a phi + // Add the second load + B.SetInsertPoint(Merge, Merge->begin()); + LoadInst *SecondLoad = B.CreateLoad(PointerArg); + + // MemoryPHI should not already exist. + MP = MSSA.getMemoryAccess(Merge); + EXPECT_EQ(MP, nullptr); + + // Create the load memory access + MemoryUse *SecondLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB( + SecondLoad, nullptr, Merge, MemorySSA::Beginning)); + Updater.insertUse(SecondLoadAccess); + // Now the load should be a phi of the entry store and the left store + MemoryPhi *MergePhi = + dyn_cast<MemoryPhi>(SecondLoadAccess->getDefiningAccess()); + EXPECT_NE(MergePhi, nullptr); + EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(1), LeftStoreAccess); + // Now create a store below the existing one in the entry + B.SetInsertPoint(Entry, --Entry->end()); + StoreInst *SecondEntryStore = B.CreateStore(B.getInt8(16), PointerArg); + MemoryAccess *SecondEntryStoreAccess = Updater.createMemoryAccessInBB( + SecondEntryStore, nullptr, Entry, MemorySSA::End); + // Insert it twice just to test renaming + Updater.insertDef(cast<MemoryDef>(SecondEntryStoreAccess), false); + EXPECT_NE(FirstLoadAccess->getDefiningAccess(), MergePhi); + Updater.insertDef(cast<MemoryDef>(SecondEntryStoreAccess), true); + EXPECT_EQ(FirstLoadAccess->getDefiningAccess(), MergePhi); + // and make sure the phi below it got updated, despite being blocks away + MergePhi = dyn_cast<MemoryPhi>(SecondLoadAccess->getDefiningAccess()); + EXPECT_NE(MergePhi, nullptr); + EXPECT_EQ(MergePhi->getIncomingValue(0), SecondEntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(1), LeftStoreAccess); + MSSA.verifyMemorySSA(); +} + +TEST_F(MemorySSATest, CreateALoadUpdater) { + // We create a diamond, then build memoryssa with no memory accesses, and + // incrementally update it by inserting a store in one of the branches, and a + // load in the merge point + F = Function::Create( + FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + BasicBlock *Entry(BasicBlock::Create(C, "", F)); + BasicBlock *Left(BasicBlock::Create(C, "", F)); + BasicBlock *Right(BasicBlock::Create(C, "", F)); + BasicBlock *Merge(BasicBlock::Create(C, "", F)); + B.SetInsertPoint(Entry); + B.CreateCondBr(B.getTrue(), Left, Right); + B.SetInsertPoint(Left, Left->begin()); + Argument *PointerArg = &*F->arg_begin(); + B.SetInsertPoint(Left); + B.CreateBr(Merge); + B.SetInsertPoint(Right); + B.CreateBr(Merge); + + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); + B.SetInsertPoint(Left, Left->begin()); + // Add the store + StoreInst *SI = B.CreateStore(B.getInt8(16), PointerArg); + MemoryAccess *StoreAccess = + Updater.createMemoryAccessInBB(SI, nullptr, Left, MemorySSA::Beginning); + Updater.insertDef(cast<MemoryDef>(StoreAccess)); + + // Add the load + B.SetInsertPoint(Merge, Merge->begin()); + LoadInst *LoadInst = B.CreateLoad(PointerArg); + + // MemoryPHI should not already exist. + MemoryPhi *MP = MSSA.getMemoryAccess(Merge); + EXPECT_EQ(MP, nullptr); + + // Create the load memory acccess + MemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB( + LoadInst, nullptr, Merge, MemorySSA::Beginning)); + Updater.insertUse(LoadAccess); MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); MSSA.verifyMemorySSA(); @@ -108,7 +247,8 @@ TEST_F(MemorySSATest, CreateALoad) { TEST_F(MemorySSATest, MoveAStore) { // We create a diamond where there is a in the entry, a store on one side, and // a load at the end. After building MemorySSA, we test updating by moving - // the store from the side block to the entry block. + // the store from the side block to the entry block. This destroys the old + // access. F = Function::Create( FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), GlobalValue::ExternalLinkage, "F", &M); @@ -128,15 +268,161 @@ TEST_F(MemorySSATest, MoveAStore) { B.CreateLoad(PointerArg); setupAnalyses(); MemorySSA &MSSA = *Analyses->MSSA; - + MemorySSAUpdater Updater(&MSSA); // Move the store SideStore->moveBefore(Entry->getTerminator()); MemoryAccess *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore); MemoryAccess *SideStoreAccess = MSSA.getMemoryAccess(SideStore); - MemoryAccess *NewStoreAccess = MSSA.createMemoryAccessAfter( + MemoryAccess *NewStoreAccess = Updater.createMemoryAccessAfter( SideStore, EntryStoreAccess, EntryStoreAccess); EntryStoreAccess->replaceAllUsesWith(NewStoreAccess); - MSSA.removeMemoryAccess(SideStoreAccess); + Updater.removeMemoryAccess(SideStoreAccess); + MSSA.verifyMemorySSA(); +} + +TEST_F(MemorySSATest, MoveAStoreUpdater) { + // We create a diamond where there is a in the entry, a store on one side, and + // a load at the end. After building MemorySSA, we test updating by moving + // the store from the side block to the entry block. This destroys the old + // access. + F = Function::Create( + FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + BasicBlock *Entry(BasicBlock::Create(C, "", F)); + BasicBlock *Left(BasicBlock::Create(C, "", F)); + BasicBlock *Right(BasicBlock::Create(C, "", F)); + BasicBlock *Merge(BasicBlock::Create(C, "", F)); + B.SetInsertPoint(Entry); + Argument *PointerArg = &*F->arg_begin(); + StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg); + B.CreateCondBr(B.getTrue(), Left, Right); + B.SetInsertPoint(Left); + auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg); + BranchInst::Create(Merge, Left); + BranchInst::Create(Merge, Right); + B.SetInsertPoint(Merge); + auto *MergeLoad = B.CreateLoad(PointerArg); + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); + + // Move the store + SideStore->moveBefore(Entry->getTerminator()); + auto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore); + auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore); + auto *NewStoreAccess = Updater.createMemoryAccessAfter( + SideStore, EntryStoreAccess, EntryStoreAccess); + // Before, the load will point to a phi of the EntryStore and SideStore. + auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad)); + EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess())); + MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess()); + EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess); + Updater.removeMemoryAccess(SideStoreAccess); + Updater.insertDef(cast<MemoryDef>(NewStoreAccess)); + // After it's a phi of the new side store access. + EXPECT_EQ(MergePhi->getIncomingValue(0), NewStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(1), NewStoreAccess); + MSSA.verifyMemorySSA(); +} + +TEST_F(MemorySSATest, MoveAStoreUpdaterMove) { + // We create a diamond where there is a in the entry, a store on one side, and + // a load at the end. After building MemorySSA, we test updating by moving + // the store from the side block to the entry block. This does not destroy + // the old access. + F = Function::Create( + FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + BasicBlock *Entry(BasicBlock::Create(C, "", F)); + BasicBlock *Left(BasicBlock::Create(C, "", F)); + BasicBlock *Right(BasicBlock::Create(C, "", F)); + BasicBlock *Merge(BasicBlock::Create(C, "", F)); + B.SetInsertPoint(Entry); + Argument *PointerArg = &*F->arg_begin(); + StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg); + B.CreateCondBr(B.getTrue(), Left, Right); + B.SetInsertPoint(Left); + auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg); + BranchInst::Create(Merge, Left); + BranchInst::Create(Merge, Right); + B.SetInsertPoint(Merge); + auto *MergeLoad = B.CreateLoad(PointerArg); + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); + + // Move the store + auto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore); + auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore); + // Before, the load will point to a phi of the EntryStore and SideStore. + auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad)); + EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess())); + MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess()); + EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess); + SideStore->moveBefore(*EntryStore->getParent(), ++EntryStore->getIterator()); + Updater.moveAfter(SideStoreAccess, EntryStoreAccess); + // After, it's a phi of the side store. + EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(1), SideStoreAccess); + + MSSA.verifyMemorySSA(); +} + +TEST_F(MemorySSATest, MoveAStoreAllAround) { + // We create a diamond where there is a in the entry, a store on one side, and + // a load at the end. After building MemorySSA, we test updating by moving + // the store from the side block to the entry block, then to the other side + // block, then to before the load. This does not destroy the old access. + F = Function::Create( + FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + BasicBlock *Entry(BasicBlock::Create(C, "", F)); + BasicBlock *Left(BasicBlock::Create(C, "", F)); + BasicBlock *Right(BasicBlock::Create(C, "", F)); + BasicBlock *Merge(BasicBlock::Create(C, "", F)); + B.SetInsertPoint(Entry); + Argument *PointerArg = &*F->arg_begin(); + StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg); + B.CreateCondBr(B.getTrue(), Left, Right); + B.SetInsertPoint(Left); + auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg); + BranchInst::Create(Merge, Left); + BranchInst::Create(Merge, Right); + B.SetInsertPoint(Merge); + auto *MergeLoad = B.CreateLoad(PointerArg); + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); + + // Move the store + auto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore); + auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore); + // Before, the load will point to a phi of the EntryStore and SideStore. + auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad)); + EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess())); + MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess()); + EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess); + // Move the store before the entry store + SideStore->moveBefore(*EntryStore->getParent(), EntryStore->getIterator()); + Updater.moveBefore(SideStoreAccess, EntryStoreAccess); + // After, it's a phi of the entry store. + EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess); + MSSA.verifyMemorySSA(); + // Now move the store to the right branch + SideStore->moveBefore(*Right, Right->begin()); + Updater.moveToPlace(SideStoreAccess, Right, MemorySSA::Beginning); + MSSA.verifyMemorySSA(); + EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(1), SideStoreAccess); + // Now move it before the load + SideStore->moveBefore(MergeLoad); + Updater.moveBefore(SideStoreAccess, LoadAccess); + EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess); + EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess); MSSA.verifyMemorySSA(); } @@ -163,13 +449,15 @@ TEST_F(MemorySSATest, RemoveAPhi) { setupAnalyses(); MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); + // Before, the load will be a use of a phi<store, liveonentry>. MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst)); MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst)); MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); // Kill the store - MSSA.removeMemoryAccess(StoreAccess); + Updater.removeMemoryAccess(StoreAccess); MemoryPhi *MP = cast<MemoryPhi>(DefiningAccess); // Verify the phi ended up as liveonentry, liveonentry for (auto &Op : MP->incoming_values()) @@ -179,7 +467,7 @@ TEST_F(MemorySSATest, RemoveAPhi) { // Verify the load is now defined by liveOnEntryDef EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess())); // Remove the PHI - MSSA.removeMemoryAccess(MP); + Updater.removeMemoryAccess(MP); MSSA.verifyMemorySSA(); } @@ -207,6 +495,7 @@ TEST_F(MemorySSATest, RemoveMemoryAccess) { setupAnalyses(); MemorySSA &MSSA = *Analyses->MSSA; MemorySSAWalker *Walker = Analyses->Walker; + MemorySSAUpdater Updater(&MSSA); // Before, the load will be a use of a phi<store, liveonentry>. It should be // the same after. @@ -217,7 +506,7 @@ TEST_F(MemorySSATest, RemoveMemoryAccess) { // The load is currently clobbered by one of the phi arguments, so the walker // should determine the clobbering access as the phi. EXPECT_EQ(DefiningAccess, Walker->getClobberingMemoryAccess(LoadInst)); - MSSA.removeMemoryAccess(StoreAccess); + Updater.removeMemoryAccess(StoreAccess); MSSA.verifyMemorySSA(); // After the removeaccess, let's see if we got the right accesses // The load should still point to the phi ... @@ -241,7 +530,7 @@ TEST_F(MemorySSATest, RemoveMemoryAccess) { } // Now we try to remove the single valued phi - MSSA.removeMemoryAccess(DefiningAccess); + Updater.removeMemoryAccess(DefiningAccess); MSSA.verifyMemorySSA(); // Now the load should be a load of live on entry. EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess())); @@ -395,10 +684,11 @@ TEST_F(MemorySSATest, PartialWalkerCacheWithPhis) { setupAnalyses(); MemorySSA &MSSA = *Analyses->MSSA; MemorySSAWalker *Walker = Analyses->Walker; + MemorySSAUpdater Updater(&MSSA); // Kill `KillStore`; it exists solely so that the load after it won't be // optimized to FirstStore. - MSSA.removeMemoryAccess(MSSA.getMemoryAccess(KillStore)); + Updater.removeMemoryAccess(MSSA.getMemoryAccess(KillStore)); KillStore->eraseFromParent(); auto *ALoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(ALoad)); EXPECT_EQ(ALoadMA->getDefiningAccess(), MSSA.getMemoryAccess(BStore)); @@ -470,23 +760,24 @@ TEST_F(MemorySSATest, WalkerReopt) { setupAnalyses(); MemorySSA &MSSA = *Analyses->MSSA; MemorySSAWalker *Walker = Analyses->Walker; + MemorySSAUpdater Updater(&MSSA); MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LIA); MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LIA)); EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SIA)); EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SIA))); - MSSA.removeMemoryAccess(LoadAccess); + Updater.removeMemoryAccess(LoadAccess); // Create the load memory access pointing to an unoptimized place. - MemoryUse *NewLoadAccess = cast<MemoryUse>(MSSA.createMemoryAccessInBB( + MemoryUse *NewLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB( LIA, MSSA.getMemoryAccess(SIB), LIA->getParent(), MemorySSA::End)); // This should it cause it to be optimized EXPECT_EQ(Walker->getClobberingMemoryAccess(NewLoadAccess), LoadClobber); EXPECT_EQ(NewLoadAccess->getDefiningAccess(), LoadClobber); } -// Test out MemorySSA::spliceMemoryAccessAbove. -TEST_F(MemorySSATest, SpliceAboveMemoryDef) { +// Test out MemorySSAUpdater::moveBefore +TEST_F(MemorySSATest, MoveAboveMemoryDef) { F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), GlobalValue::ExternalLinkage, "F", &M); B.SetInsertPoint(BasicBlock::Create(C, "", F)); @@ -500,7 +791,6 @@ TEST_F(MemorySSATest, SpliceAboveMemoryDef) { StoreInst *StoreB = B.CreateStore(ConstantInt::get(Int8, 0), B_); LoadInst *LoadB = B.CreateLoad(B_); StoreInst *StoreA1 = B.CreateStore(ConstantInt::get(Int8, 4), A); - // splice this above StoreB StoreInst *StoreC = B.CreateStore(ConstantInt::get(Int8, 4), C); StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 4), A); LoadInst *LoadC = B.CreateLoad(C); @@ -509,9 +799,10 @@ TEST_F(MemorySSATest, SpliceAboveMemoryDef) { MemorySSA &MSSA = *Analyses->MSSA; MemorySSAWalker &Walker = *Analyses->Walker; + MemorySSAUpdater Updater(&MSSA); StoreC->moveBefore(StoreB); - MSSA.spliceMemoryAccessAbove(cast<MemoryDef>(MSSA.getMemoryAccess(StoreB)), - MSSA.getMemoryAccess(StoreC)); + Updater.moveBefore(cast<MemoryDef>(MSSA.getMemoryAccess(StoreC)), + cast<MemoryDef>(MSSA.getMemoryAccess(StoreB))); MSSA.verifyMemorySSA(); @@ -532,3 +823,43 @@ TEST_F(MemorySSATest, SpliceAboveMemoryDef) { EXPECT_TRUE(MSSA.locallyDominates(MSSA.getMemoryAccess(StoreA1), MSSA.getMemoryAccess(StoreA2))); } + +TEST_F(MemorySSATest, Irreducible) { + // Create the equivalent of + // x = something + // if (...) + // goto second_loop_entry + // while (...) { + // second_loop_entry: + // } + // use(x) + + SmallVector<PHINode *, 8> Inserted; + IRBuilder<> B(C); + F = Function::Create( + FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + + // Make blocks + BasicBlock *IfBB = BasicBlock::Create(C, "if", F); + BasicBlock *LoopStartBB = BasicBlock::Create(C, "loopstart", F); + BasicBlock *LoopMainBB = BasicBlock::Create(C, "loopmain", F); + BasicBlock *AfterLoopBB = BasicBlock::Create(C, "afterloop", F); + B.SetInsertPoint(IfBB); + B.CreateCondBr(B.getTrue(), LoopMainBB, LoopStartBB); + B.SetInsertPoint(LoopStartBB); + B.CreateBr(LoopMainBB); + B.SetInsertPoint(LoopMainBB); + B.CreateCondBr(B.getTrue(), LoopStartBB, AfterLoopBB); + B.SetInsertPoint(AfterLoopBB); + Argument *FirstArg = &*F->arg_begin(); + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAUpdater Updater(&MSSA); + // Create the load memory acccess + LoadInst *LoadInst = B.CreateLoad(FirstArg); + MemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB( + LoadInst, nullptr, AfterLoopBB, MemorySSA::Beginning)); + Updater.insertUse(LoadAccess); + MSSA.verifyMemorySSA(); +} diff --git a/unittests/Analysis/ProfileSummaryInfoTest.cpp b/unittests/Analysis/ProfileSummaryInfoTest.cpp new file mode 100644 index 0000000000000..0b4b1de28053b --- /dev/null +++ b/unittests/Analysis/ProfileSummaryInfoTest.cpp @@ -0,0 +1,198 @@ +//===- ProfileSummaryInfoTest.cpp - ProfileSummaryInfo unit tests ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/BlockFrequencyInfo.h" +#include "llvm/Analysis/BlockFrequencyInfoImpl.h" +#include "llvm/Analysis/BranchProbabilityInfo.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ProfileSummaryInfo.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +namespace llvm { +namespace { + +class ProfileSummaryInfoTest : public testing::Test { +protected: + LLVMContext C; + std::unique_ptr<BranchProbabilityInfo> BPI; + std::unique_ptr<DominatorTree> DT; + std::unique_ptr<LoopInfo> LI; + + ProfileSummaryInfo buildPSI(Module *M) { + return ProfileSummaryInfo(*M); + } + BlockFrequencyInfo buildBFI(Function &F) { + DT.reset(new DominatorTree(F)); + LI.reset(new LoopInfo(*DT)); + BPI.reset(new BranchProbabilityInfo(F, *LI)); + return BlockFrequencyInfo(F, *BPI, *LI); + } + std::unique_ptr<Module> makeLLVMModule(const char *ProfKind = nullptr) { + const char *ModuleString = + "define i32 @g(i32 %x) !prof !21 {{\n" + " ret i32 0\n" + "}\n" + "define i32 @h(i32 %x) !prof !22 {{\n" + " ret i32 0\n" + "}\n" + "define i32 @f(i32 %x) !prof !20 {{\n" + "bb0:\n" + " %y1 = icmp eq i32 %x, 0 \n" + " br i1 %y1, label %bb1, label %bb2, !prof !23 \n" + "bb1:\n" + " %z1 = call i32 @g(i32 %x)\n" + " br label %bb3\n" + "bb2:\n" + " %z2 = call i32 @h(i32 %x)\n" + " br label %bb3\n" + "bb3:\n" + " %y2 = phi i32 [0, %bb1], [1, %bb2] \n" + " ret i32 %y2\n" + "}\n" + "!20 = !{{!\"function_entry_count\", i64 400}\n" + "!21 = !{{!\"function_entry_count\", i64 1}\n" + "!22 = !{{!\"function_entry_count\", i64 100}\n" + "!23 = !{{!\"branch_weights\", i32 64, i32 4}\n" + "{0}"; + const char *SummaryString = "!llvm.module.flags = !{{!1}" + "!1 = !{{i32 1, !\"ProfileSummary\", !2}" + "!2 = !{{!3, !4, !5, !6, !7, !8, !9, !10}" + "!3 = !{{!\"ProfileFormat\", !\"{0}\"}" + "!4 = !{{!\"TotalCount\", i64 10000}" + "!5 = !{{!\"MaxCount\", i64 10}" + "!6 = !{{!\"MaxInternalCount\", i64 1}" + "!7 = !{{!\"MaxFunctionCount\", i64 1000}" + "!8 = !{{!\"NumCounts\", i64 3}" + "!9 = !{{!\"NumFunctions\", i64 3}" + "!10 = !{{!\"DetailedSummary\", !11}" + "!11 = !{{!12, !13, !14}" + "!12 = !{{i32 10000, i64 1000, i32 1}" + "!13 = !{{i32 999000, i64 300, i32 3}" + "!14 = !{{i32 999999, i64 5, i32 10}"; + SMDiagnostic Err; + if (ProfKind) + return parseAssemblyString( + formatv(ModuleString, formatv(SummaryString, ProfKind).str()).str(), + Err, C); + else + return parseAssemblyString(formatv(ModuleString, "").str(), Err, C); + } +}; + +TEST_F(ProfileSummaryInfoTest, TestNoProfile) { + auto M = makeLLVMModule(/*ProfKind=*/nullptr); + Function *F = M->getFunction("f"); + + ProfileSummaryInfo PSI = buildPSI(M.get()); + // In the absence of profiles, is{Hot|Cold}X methods should always return + // false. + EXPECT_FALSE(PSI.isHotCount(1000)); + EXPECT_FALSE(PSI.isHotCount(0)); + EXPECT_FALSE(PSI.isColdCount(1000)); + EXPECT_FALSE(PSI.isColdCount(0)); + + EXPECT_FALSE(PSI.isFunctionEntryHot(F)); + EXPECT_FALSE(PSI.isFunctionEntryCold(F)); + + BasicBlock &BB0 = F->getEntryBlock(); + BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0); + + BlockFrequencyInfo BFI = buildBFI(*F); + EXPECT_FALSE(PSI.isHotBB(&BB0, &BFI)); + EXPECT_FALSE(PSI.isColdBB(&BB0, &BFI)); + + CallSite CS1(BB1->getFirstNonPHI()); + EXPECT_FALSE(PSI.isHotCallSite(CS1, &BFI)); + EXPECT_FALSE(PSI.isColdCallSite(CS1, &BFI)); +} +TEST_F(ProfileSummaryInfoTest, TestCommon) { + auto M = makeLLVMModule("InstrProf"); + Function *F = M->getFunction("f"); + Function *G = M->getFunction("g"); + Function *H = M->getFunction("h"); + + ProfileSummaryInfo PSI = buildPSI(M.get()); + EXPECT_TRUE(PSI.isHotCount(400)); + EXPECT_TRUE(PSI.isColdCount(2)); + EXPECT_FALSE(PSI.isColdCount(100)); + EXPECT_FALSE(PSI.isHotCount(100)); + + EXPECT_TRUE(PSI.isFunctionEntryHot(F)); + EXPECT_FALSE(PSI.isFunctionEntryHot(G)); + EXPECT_FALSE(PSI.isFunctionEntryHot(H)); +} + +TEST_F(ProfileSummaryInfoTest, InstrProf) { + auto M = makeLLVMModule("InstrProf"); + Function *F = M->getFunction("f"); + ProfileSummaryInfo PSI = buildPSI(M.get()); + + BasicBlock &BB0 = F->getEntryBlock(); + BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0); + BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1); + BasicBlock *BB3 = BB1->getSingleSuccessor(); + + BlockFrequencyInfo BFI = buildBFI(*F); + EXPECT_TRUE(PSI.isHotBB(&BB0, &BFI)); + EXPECT_TRUE(PSI.isHotBB(BB1, &BFI)); + EXPECT_FALSE(PSI.isHotBB(BB2, &BFI)); + EXPECT_TRUE(PSI.isHotBB(BB3, &BFI)); + + CallSite CS1(BB1->getFirstNonPHI()); + auto *CI2 = BB2->getFirstNonPHI(); + CallSite CS2(CI2); + + EXPECT_TRUE(PSI.isHotCallSite(CS1, &BFI)); + EXPECT_FALSE(PSI.isHotCallSite(CS2, &BFI)); +} + +TEST_F(ProfileSummaryInfoTest, SampleProf) { + auto M = makeLLVMModule("SampleProfile"); + Function *F = M->getFunction("f"); + ProfileSummaryInfo PSI = buildPSI(M.get()); + + BasicBlock &BB0 = F->getEntryBlock(); + BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0); + BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1); + BasicBlock *BB3 = BB1->getSingleSuccessor(); + + BlockFrequencyInfo BFI = buildBFI(*F); + EXPECT_TRUE(PSI.isHotBB(&BB0, &BFI)); + EXPECT_TRUE(PSI.isHotBB(BB1, &BFI)); + EXPECT_FALSE(PSI.isHotBB(BB2, &BFI)); + EXPECT_TRUE(PSI.isHotBB(BB3, &BFI)); + + CallSite CS1(BB1->getFirstNonPHI()); + auto *CI2 = BB2->getFirstNonPHI(); + CallSite CS2(CI2); + + EXPECT_TRUE(PSI.isHotCallSite(CS1, &BFI)); + EXPECT_FALSE(PSI.isHotCallSite(CS2, &BFI)); + + // Test that CS2 is considered hot when it gets an MD_prof metadata with + // weights that exceed the hot count threshold. + MDBuilder MDB(M->getContext()); + CI2->setMetadata(llvm::LLVMContext::MD_prof, MDB.createBranchWeights({400})); + EXPECT_TRUE(PSI.isHotCallSite(CS2, &BFI)); +} + +} // end anonymous namespace +} // end namespace llvm diff --git a/unittests/Analysis/ScalarEvolutionTest.cpp b/unittests/Analysis/ScalarEvolutionTest.cpp index f4370842edb5e..df9fd4b5ec330 100644 --- a/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/unittests/Analysis/ScalarEvolutionTest.cpp @@ -51,13 +51,13 @@ protected: return ScalarEvolution(F, TLI, *AC, *DT, *LI); } - void runWithFunctionAndSE( + void runWithSE( Module &M, StringRef FuncName, - function_ref<void(Function &F, ScalarEvolution &SE)> Test) { + function_ref<void(Function &F, LoopInfo &LI, ScalarEvolution &SE)> Test) { auto *F = M.getFunction(FuncName); ASSERT_NE(F, nullptr) << "Could not find " << FuncName; ScalarEvolution SE = buildSE(*F); - Test(*F, SE); + Test(*F, *LI, SE); } }; @@ -306,9 +306,11 @@ TEST_F(ScalarEvolutionsTest, ExpandPtrTypeSCEV) { // %bitcast2 = bitcast i8* %select to i32* // br i1 undef, label %loop, label %exit + const DataLayout &DL = F->getParent()->getDataLayout(); BranchInst *Br = BranchInst::Create( LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB); - AllocaInst *Alloca = new AllocaInst(I32Ty, "alloca", Br); + AllocaInst *Alloca = new AllocaInst(I32Ty, DL.getAllocaAddrSpace(), + "alloca", Br); ConstantInt *Ci32 = ConstantInt::get(Context, APInt(32, 1)); GetElementPtrInst *Gep0 = GetElementPtrInst::Create(I32Ty, Alloca, Ci32, "gep0", Br); @@ -417,7 +419,7 @@ TEST_F(ScalarEvolutionsTest, CommutativeExprOperandOrder) { assert(M && "Could not parse module?"); assert(!verifyModule(*M) && "Must have been well formed!"); - runWithFunctionAndSE(*M, "f_1", [&](Function &F, ScalarEvolution &SE) { + runWithSE(*M, "f_1", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { auto *IV0 = getInstructionByName(F, "iv0"); auto *IV0Inc = getInstructionByName(F, "iv0.inc"); @@ -458,11 +460,12 @@ TEST_F(ScalarEvolutionsTest, CommutativeExprOperandOrder) { }; for (StringRef FuncName : {"f_2", "f_3", "f_4"}) - runWithFunctionAndSE(*M, FuncName, [&](Function &F, ScalarEvolution &SE) { - CheckCommutativeMulExprs(SE, SE.getSCEV(getInstructionByName(F, "x")), - SE.getSCEV(getInstructionByName(F, "y")), - SE.getSCEV(getInstructionByName(F, "z"))); - }); + runWithSE( + *M, FuncName, [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + CheckCommutativeMulExprs(SE, SE.getSCEV(getInstructionByName(F, "x")), + SE.getSCEV(getInstructionByName(F, "y")), + SE.getSCEV(getInstructionByName(F, "z"))); + }); } TEST_F(ScalarEvolutionsTest, CompareSCEVComplexity) { @@ -568,5 +571,100 @@ TEST_F(ScalarEvolutionsTest, CompareValueComplexity) { EXPECT_NE(A, B); } +TEST_F(ScalarEvolutionsTest, SCEVAddExpr) { + Type *Ty32 = Type::getInt32Ty(Context); + Type *ArgTys[] = {Type::getInt64Ty(Context), Ty32}; + + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), ArgTys, false); + Function *F = cast<Function>(M.getOrInsertFunction("f", FTy)); + + Argument *A1 = &*F->arg_begin(); + Argument *A2 = &*(std::next(F->arg_begin())); + BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F); + + Instruction *Trunc = CastInst::CreateTruncOrBitCast(A1, Ty32, "", EntryBB); + Instruction *Mul1 = BinaryOperator::CreateMul(Trunc, A2, "", EntryBB); + Instruction *Add1 = BinaryOperator::CreateAdd(Mul1, Trunc, "", EntryBB); + Mul1 = BinaryOperator::CreateMul(Add1, Trunc, "", EntryBB); + Instruction *Add2 = BinaryOperator::CreateAdd(Mul1, Add1, "", EntryBB); + // FIXME: The size of this is arbitrary and doesn't seem to change the + // result, but SCEV will do quadratic work for these so a large number here + // will be extremely slow. We should revisit what and how this is testing + // SCEV. + for (int i = 0; i < 10; i++) { + Mul1 = BinaryOperator::CreateMul(Add2, Add1, "", EntryBB); + Add1 = Add2; + Add2 = BinaryOperator::CreateAdd(Mul1, Add1, "", EntryBB); + } + + ReturnInst::Create(Context, nullptr, EntryBB); + ScalarEvolution SE = buildSE(*F); + EXPECT_NE(nullptr, SE.getSCEV(Mul1)); +} + +static Instruction &GetInstByName(Function &F, StringRef Name) { + for (auto &I : instructions(F)) + if (I.getName() == Name) + return I; + llvm_unreachable("Could not find instructions!"); +} + +TEST_F(ScalarEvolutionsTest, SCEVNormalization) { + LLVMContext C; + SMDiagnostic Err; + std::unique_ptr<Module> M = parseAssemblyString( + "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" " + " " + "@var_0 = external global i32, align 4" + "@var_1 = external global i32, align 4" + "@var_2 = external global i32, align 4" + " " + "declare i32 @unknown(i32, i32, i32)" + " " + "define void @f_1(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) " + " local_unnamed_addr { " + "entry: " + " br label %loop.ph " + " " + "loop.ph: " + " br label %loop " + " " + "loop: " + " %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] " + " %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] " + " %iv0.inc = add i32 %iv0, 1 " + " %iv1.inc = add i32 %iv1, 3 " + " br i1 undef, label %for.end.loopexit, label %loop " + " " + "for.end.loopexit: " + " ret void " + "} " + , + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "f_1", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + auto &I0 = GetInstByName(F, "iv0"); + auto &I1 = *I0.getNextNode(); + + auto *S0 = cast<SCEVAddRecExpr>(SE.getSCEV(&I0)); + PostIncLoopSet Loops; + Loops.insert(S0->getLoop()); + auto *N0 = normalizeForPostIncUse(S0, Loops, SE); + auto *D0 = denormalizeForPostIncUse(N0, Loops, SE); + EXPECT_EQ(S0, D0) << *S0 << " " << *D0; + + auto *S1 = cast<SCEVAddRecExpr>(SE.getSCEV(&I1)); + Loops.clear(); + Loops.insert(S1->getLoop()); + auto *N1 = normalizeForPostIncUse(S1, Loops, SE); + auto *D1 = denormalizeForPostIncUse(N1, Loops, SE); + EXPECT_EQ(S1, D1) << *S1 << " " << *D1; + }); +} + } // end anonymous namespace } // end namespace llvm diff --git a/unittests/Analysis/TargetLibraryInfoTest.cpp b/unittests/Analysis/TargetLibraryInfoTest.cpp new file mode 100644 index 0000000000000..598429c968aa9 --- /dev/null +++ b/unittests/Analysis/TargetLibraryInfoTest.cpp @@ -0,0 +1,481 @@ +//===--- TargetLibraryInfoTest.cpp - TLI/LibFunc unit tests ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/SourceMgr.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class TargetLibraryInfoTest : public testing::Test { +protected: + LLVMContext Context; + TargetLibraryInfoImpl TLII; + TargetLibraryInfo TLI; + + std::unique_ptr<Module> M; + + TargetLibraryInfoTest() : TLI(TLII) {} + + void parseAssembly(const char *Assembly) { + SMDiagnostic Error; + M = parseAssemblyString(Assembly, Error, Context); + + std::string errMsg; + raw_string_ostream os(errMsg); + Error.print("", os); + + if (!M) + report_fatal_error(os.str()); + } + + ::testing::AssertionResult isLibFunc(const Function *FDecl, + LibFunc ExpectedLF) { + StringRef ExpectedLFName = TLI.getName(ExpectedLF); + + if (!FDecl) + return ::testing::AssertionFailure() << ExpectedLFName << " not found"; + + LibFunc F; + if (!TLI.getLibFunc(*FDecl, F)) + return ::testing::AssertionFailure() << ExpectedLFName << " invalid"; + + return ::testing::AssertionSuccess() << ExpectedLFName << " is LibFunc"; + } +}; + +} // end anonymous namespace + +// Check that we don't accept egregiously incorrect prototypes. +TEST_F(TargetLibraryInfoTest, InvalidProto) { + parseAssembly("%foo = type { %foo }\n"); + + auto *StructTy = M->getTypeByName("foo"); + auto *InvalidFTy = FunctionType::get(StructTy, /*isVarArg=*/false); + + for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) { + LibFunc LF = (LibFunc)FI; + auto *F = cast<Function>( + M->getOrInsertFunction(TLI.getName(LF), InvalidFTy)); + EXPECT_FALSE(isLibFunc(F, LF)); + } +} + +// Check that we do accept know-correct prototypes. +TEST_F(TargetLibraryInfoTest, ValidProto) { + parseAssembly( + // These functions use a 64-bit size_t; use the appropriate datalayout. + "target datalayout = \"p:64:64:64\"\n" + + // Struct pointers are replaced with an opaque pointer. + "%struct = type opaque\n" + + // These functions were extracted as-is from the OS X headers. + "declare double @__cospi(double)\n" + "declare float @__cospif(float)\n" + "declare { double, double } @__sincospi_stret(double)\n" + "declare <2 x float> @__sincospif_stret(float)\n" + "declare double @__sinpi(double)\n" + "declare float @__sinpif(float)\n" + "declare i32 @abs(i32)\n" + "declare i32 @access(i8*, i32)\n" + "declare double @acos(double)\n" + "declare float @acosf(float)\n" + "declare double @acosh(double)\n" + "declare float @acoshf(float)\n" + "declare x86_fp80 @acoshl(x86_fp80)\n" + "declare x86_fp80 @acosl(x86_fp80)\n" + "declare double @asin(double)\n" + "declare float @asinf(float)\n" + "declare double @asinh(double)\n" + "declare float @asinhf(float)\n" + "declare x86_fp80 @asinhl(x86_fp80)\n" + "declare x86_fp80 @asinl(x86_fp80)\n" + "declare double @atan(double)\n" + "declare double @atan2(double, double)\n" + "declare float @atan2f(float, float)\n" + "declare x86_fp80 @atan2l(x86_fp80, x86_fp80)\n" + "declare float @atanf(float)\n" + "declare double @atanh(double)\n" + "declare float @atanhf(float)\n" + "declare x86_fp80 @atanhl(x86_fp80)\n" + "declare x86_fp80 @atanl(x86_fp80)\n" + "declare double @atof(i8*)\n" + "declare i32 @atoi(i8*)\n" + "declare i64 @atol(i8*)\n" + "declare i64 @atoll(i8*)\n" + "declare i32 @bcmp(i8*, i8*, i64)\n" + "declare void @bcopy(i8*, i8*, i64)\n" + "declare void @bzero(i8*, i64)\n" + "declare i8* @calloc(i64, i64)\n" + "declare double @cbrt(double)\n" + "declare float @cbrtf(float)\n" + "declare x86_fp80 @cbrtl(x86_fp80)\n" + "declare double @ceil(double)\n" + "declare float @ceilf(float)\n" + "declare x86_fp80 @ceill(x86_fp80)\n" + "declare i32 @chown(i8*, i32, i32)\n" + "declare void @clearerr(%struct*)\n" + "declare double @copysign(double, double)\n" + "declare float @copysignf(float, float)\n" + "declare x86_fp80 @copysignl(x86_fp80, x86_fp80)\n" + "declare double @cos(double)\n" + "declare float @cosf(float)\n" + "declare double @cosh(double)\n" + "declare float @coshf(float)\n" + "declare x86_fp80 @coshl(x86_fp80)\n" + "declare x86_fp80 @cosl(x86_fp80)\n" + "declare i8* @ctermid(i8*)\n" + "declare double @exp(double)\n" + "declare double @exp2(double)\n" + "declare float @exp2f(float)\n" + "declare x86_fp80 @exp2l(x86_fp80)\n" + "declare float @expf(float)\n" + "declare x86_fp80 @expl(x86_fp80)\n" + "declare double @expm1(double)\n" + "declare float @expm1f(float)\n" + "declare x86_fp80 @expm1l(x86_fp80)\n" + "declare double @fabs(double)\n" + "declare float @fabsf(float)\n" + "declare x86_fp80 @fabsl(x86_fp80)\n" + "declare i32 @fclose(%struct*)\n" + "declare i32 @feof(%struct*)\n" + "declare i32 @ferror(%struct*)\n" + "declare i32 @fflush(%struct*)\n" + "declare i32 @ffs(i32)\n" + "declare i32 @ffsl(i64)\n" + "declare i32 @ffsll(i64)\n" + "declare i32 @fgetc(%struct*)\n" + "declare i32 @fgetpos(%struct*, i64*)\n" + "declare i8* @fgets(i8*, i32, %struct*)\n" + "declare i32 @fileno(%struct*)\n" + "declare void @flockfile(%struct*)\n" + "declare double @floor(double)\n" + "declare float @floorf(float)\n" + "declare x86_fp80 @floorl(x86_fp80)\n" + "declare i32 @fls(i32)\n" + "declare i32 @flsl(i64)\n" + "declare i32 @flsll(i64)\n" + "declare double @fmax(double, double)\n" + "declare float @fmaxf(float, float)\n" + "declare x86_fp80 @fmaxl(x86_fp80, x86_fp80)\n" + "declare double @fmin(double, double)\n" + "declare float @fminf(float, float)\n" + "declare x86_fp80 @fminl(x86_fp80, x86_fp80)\n" + "declare double @fmod(double, double)\n" + "declare float @fmodf(float, float)\n" + "declare x86_fp80 @fmodl(x86_fp80, x86_fp80)\n" + "declare i32 @fprintf(%struct*, i8*, ...)\n" + "declare i32 @fputc(i32, %struct*)\n" + "declare i64 @fread(i8*, i64, i64, %struct*)\n" + "declare void @free(i8*)\n" + "declare double @frexp(double, i32*)\n" + "declare float @frexpf(float, i32*)\n" + "declare x86_fp80 @frexpl(x86_fp80, i32*)\n" + "declare i32 @fscanf(%struct*, i8*, ...)\n" + "declare i32 @fseek(%struct*, i64, i32)\n" + "declare i32 @fseeko(%struct*, i64, i32)\n" + "declare i32 @fsetpos(%struct*, i64*)\n" + "declare i32 @fstatvfs(i32, %struct*)\n" + "declare i64 @ftell(%struct*)\n" + "declare i64 @ftello(%struct*)\n" + "declare i32 @ftrylockfile(%struct*)\n" + "declare void @funlockfile(%struct*)\n" + "declare i32 @getc(%struct*)\n" + "declare i32 @getc_unlocked(%struct*)\n" + "declare i32 @getchar()\n" + "declare i8* @getenv(i8*)\n" + "declare i32 @getitimer(i32, %struct*)\n" + "declare i32 @getlogin_r(i8*, i64)\n" + "declare %struct* @getpwnam(i8*)\n" + "declare i8* @gets(i8*)\n" + "declare i32 @gettimeofday(%struct*, i8*)\n" + "declare i32 @_Z7isasciii(i32)\n" + "declare i32 @_Z7isdigiti(i32)\n" + "declare i64 @labs(i64)\n" + "declare double @ldexp(double, i32)\n" + "declare float @ldexpf(float, i32)\n" + "declare x86_fp80 @ldexpl(x86_fp80, i32)\n" + "declare i64 @llabs(i64)\n" + "declare double @log(double)\n" + "declare double @log10(double)\n" + "declare float @log10f(float)\n" + "declare x86_fp80 @log10l(x86_fp80)\n" + "declare double @log1p(double)\n" + "declare float @log1pf(float)\n" + "declare x86_fp80 @log1pl(x86_fp80)\n" + "declare double @log2(double)\n" + "declare float @log2f(float)\n" + "declare x86_fp80 @log2l(x86_fp80)\n" + "declare double @logb(double)\n" + "declare float @logbf(float)\n" + "declare x86_fp80 @logbl(x86_fp80)\n" + "declare float @logf(float)\n" + "declare x86_fp80 @logl(x86_fp80)\n" + "declare i8* @malloc(i64)\n" + "declare i8* @memccpy(i8*, i8*, i32, i64)\n" + "declare i8* @memchr(i8*, i32, i64)\n" + "declare i32 @memcmp(i8*, i8*, i64)\n" + "declare i8* @memcpy(i8*, i8*, i64)\n" + "declare i8* @memmove(i8*, i8*, i64)\n" + "declare i8* @memset(i8*, i32, i64)\n" + "declare void @memset_pattern16(i8*, i8*, i64)\n" + "declare i32 @mkdir(i8*, i16)\n" + "declare double @modf(double, double*)\n" + "declare float @modff(float, float*)\n" + "declare x86_fp80 @modfl(x86_fp80, x86_fp80*)\n" + "declare double @nearbyint(double)\n" + "declare float @nearbyintf(float)\n" + "declare x86_fp80 @nearbyintl(x86_fp80)\n" + "declare i32 @pclose(%struct*)\n" + "declare void @perror(i8*)\n" + "declare i32 @posix_memalign(i8**, i64, i64)\n" + "declare double @pow(double, double)\n" + "declare float @powf(float, float)\n" + "declare x86_fp80 @powl(x86_fp80, x86_fp80)\n" + "declare i32 @printf(i8*, ...)\n" + "declare i32 @putc(i32, %struct*)\n" + "declare i32 @putchar(i32)\n" + "declare i32 @puts(i8*)\n" + "declare void @qsort(i8*, i64, i64, i32 (i8*, i8*)*)\n" + "declare i64 @readlink(i8*, i8*, i64)\n" + "declare i8* @realloc(i8*, i64)\n" + "declare i8* @reallocf(i8*, i64)\n" + "declare i32 @remove(i8*)\n" + "declare i32 @rename(i8*, i8*)\n" + "declare void @rewind(%struct*)\n" + "declare double @rint(double)\n" + "declare float @rintf(float)\n" + "declare x86_fp80 @rintl(x86_fp80)\n" + "declare i32 @rmdir(i8*)\n" + "declare double @round(double)\n" + "declare float @roundf(float)\n" + "declare x86_fp80 @roundl(x86_fp80)\n" + "declare i32 @scanf(i8*, ...)\n" + "declare void @setbuf(%struct*, i8*)\n" + "declare i32 @setitimer(i32, %struct*, %struct*)\n" + "declare i32 @setvbuf(%struct*, i8*, i32, i64)\n" + "declare double @sin(double)\n" + "declare float @sinf(float)\n" + "declare double @sinh(double)\n" + "declare float @sinhf(float)\n" + "declare x86_fp80 @sinhl(x86_fp80)\n" + "declare x86_fp80 @sinl(x86_fp80)\n" + "declare i32 @snprintf(i8*, i64, i8*, ...)\n" + "declare i32 @sprintf(i8*, i8*, ...)\n" + "declare double @sqrt(double)\n" + "declare float @sqrtf(float)\n" + "declare x86_fp80 @sqrtl(x86_fp80)\n" + "declare i32 @sscanf(i8*, i8*, ...)\n" + "declare i32 @statvfs(i8*, %struct*)\n" + "declare i8* @stpcpy(i8*, i8*)\n" + "declare i8* @stpncpy(i8*, i8*, i64)\n" + "declare i32 @strcasecmp(i8*, i8*)\n" + "declare i8* @strcat(i8*, i8*)\n" + "declare i8* @strchr(i8*, i32)\n" + "declare i32 @strcmp(i8*, i8*)\n" + "declare i32 @strcoll(i8*, i8*)\n" + "declare i8* @strcpy(i8*, i8*)\n" + "declare i64 @strcspn(i8*, i8*)\n" + "declare i8* @strdup(i8*)\n" + "declare i64 @strlen(i8*)\n" + "declare i32 @strncasecmp(i8*, i8*, i64)\n" + "declare i8* @strncat(i8*, i8*, i64)\n" + "declare i32 @strncmp(i8*, i8*, i64)\n" + "declare i8* @strncpy(i8*, i8*, i64)\n" + "declare i8* @strndup(i8*, i64)\n" + "declare i64 @strnlen(i8*, i64)\n" + "declare i8* @strpbrk(i8*, i8*)\n" + "declare i8* @strrchr(i8*, i32)\n" + "declare i64 @strspn(i8*, i8*)\n" + "declare i8* @strstr(i8*, i8*)\n" + "declare i8* @strtok(i8*, i8*)\n" + "declare i8* @strtok_r(i8*, i8*, i8**)\n" + "declare i64 @strtol(i8*, i8**, i32)\n" + "declare x86_fp80 @strtold(i8*, i8**)\n" + "declare i64 @strtoll(i8*, i8**, i32)\n" + "declare i64 @strtoul(i8*, i8**, i32)\n" + "declare i64 @strtoull(i8*, i8**, i32)\n" + "declare i64 @strxfrm(i8*, i8*, i64)\n" + "declare double @tan(double)\n" + "declare float @tanf(float)\n" + "declare double @tanh(double)\n" + "declare float @tanhf(float)\n" + "declare x86_fp80 @tanhl(x86_fp80)\n" + "declare x86_fp80 @tanl(x86_fp80)\n" + "declare i64 @times(%struct*)\n" + "declare %struct* @tmpfile()\n" + "declare i32 @_Z7toasciii(i32)\n" + "declare double @trunc(double)\n" + "declare float @truncf(float)\n" + "declare x86_fp80 @truncl(x86_fp80)\n" + "declare i32 @uname(%struct*)\n" + "declare i32 @ungetc(i32, %struct*)\n" + "declare i32 @unlink(i8*)\n" + "declare i32 @utime(i8*, %struct*)\n" + "declare i32 @utimes(i8*, %struct*)\n" + "declare i8* @valloc(i64)\n" + "declare i32 @vfprintf(%struct*, i8*, %struct*)\n" + "declare i32 @vfscanf(%struct*, i8*, %struct*)\n" + "declare i32 @vprintf(i8*, %struct*)\n" + "declare i32 @vscanf(i8*, %struct*)\n" + "declare i32 @vsnprintf(i8*, i64, i8*, %struct*)\n" + "declare i32 @vsprintf(i8*, i8*, %struct*)\n" + "declare i32 @vsscanf(i8*, i8*, %struct*)\n" + + // These functions were also extracted from the OS X headers, but they are + // available with a special name on darwin. + // This test uses the default TLI name instead. + "declare i32 @chmod(i8*, i16)\n" + "declare i32 @closedir(%struct*)\n" + "declare %struct* @fdopen(i32, i8*)\n" + "declare %struct* @fopen(i8*, i8*)\n" + "declare i32 @fputs(i8*, %struct*)\n" + "declare i32 @fstat(i32, %struct*)\n" + "declare i64 @fwrite(i8*, i64, i64, %struct*)\n" + "declare i32 @lchown(i8*, i32, i32)\n" + "declare i32 @lstat(i8*, %struct*)\n" + "declare i64 @mktime(%struct*)\n" + "declare i32 @open(i8*, i32, ...)\n" + "declare %struct* @opendir(i8*)\n" + "declare %struct* @popen(i8*, i8*)\n" + "declare i64 @pread(i32, i8*, i64, i64)\n" + "declare i64 @pwrite(i32, i8*, i64, i64)\n" + "declare i64 @read(i32, i8*, i64)\n" + "declare i8* @realpath(i8*, i8*)\n" + "declare i32 @stat(i8*, %struct*)\n" + "declare double @strtod(i8*, i8**)\n" + "declare float @strtof(i8*, i8**)\n" + "declare i32 @system(i8*)\n" + "declare i32 @unsetenv(i8*)\n" + "declare i64 @write(i32, i8*, i64)\n" + + // These functions are available on Linux but not Darwin; they only differ + // from their non-64 counterparts in the struct type. + // Use the same prototype as the non-64 variant. + "declare %struct* @fopen64(i8*, i8*)\n" + "declare i32 @fstat64(i32, %struct*)\n" + "declare i32 @fstatvfs64(i32, %struct*)\n" + "declare i32 @lstat64(i8*, %struct*)\n" + "declare i32 @open64(i8*, i32, ...)\n" + "declare i32 @stat64(i8*, %struct*)\n" + "declare i32 @statvfs64(i8*, %struct*)\n" + "declare %struct* @tmpfile64()\n" + + // These functions are also -64 variants, but do differ in the type of the + // off_t (vs off64_t) parameter. The non-64 variants declared above used + // a 64-bit off_t, so, in practice, they are also equivalent. + "declare i32 @fseeko64(%struct*, i64, i32)\n" + "declare i64 @ftello64(%struct*)\n" + + "declare void @_ZdaPv(i8*)\n" + "declare void @_ZdaPvRKSt9nothrow_t(i8*, %struct*)\n" + "declare void @_ZdaPvj(i8*, i32)\n" + "declare void @_ZdaPvm(i8*, i64)\n" + "declare void @_ZdlPv(i8*)\n" + "declare void @_ZdlPvRKSt9nothrow_t(i8*, %struct*)\n" + "declare void @_ZdlPvj(i8*, i32)\n" + "declare void @_ZdlPvm(i8*, i64)\n" + "declare i8* @_Znaj(i32)\n" + "declare i8* @_ZnajRKSt9nothrow_t(i32, %struct*)\n" + "declare i8* @_Znam(i64)\n" + "declare i8* @_ZnamRKSt9nothrow_t(i64, %struct*)\n" + "declare i8* @_Znwj(i32)\n" + "declare i8* @_ZnwjRKSt9nothrow_t(i32, %struct*)\n" + "declare i8* @_Znwm(i64)\n" + "declare i8* @_ZnwmRKSt9nothrow_t(i64, %struct*)\n" + + "declare void @\"??3@YAXPEAX@Z\"(i8*)\n" + "declare void @\"??3@YAXPEAXAEBUnothrow_t@std@@@Z\"(i8*, %struct*)\n" + "declare void @\"??3@YAXPEAX_K@Z\"(i8*, i64)\n" + "declare void @\"??_V@YAXPEAX@Z\"(i8*)\n" + "declare void @\"??_V@YAXPEAXAEBUnothrow_t@std@@@Z\"(i8*, %struct*)\n" + "declare void @\"??_V@YAXPEAX_K@Z\"(i8*, i64)\n" + "declare i8* @\"??2@YAPAXI@Z\"(i32)\n" + "declare i8* @\"??2@YAPAXIABUnothrow_t@std@@@Z\"(i32, %struct*)\n" + "declare i8* @\"??2@YAPEAX_K@Z\"(i64)\n" + "declare i8* @\"??2@YAPEAX_KAEBUnothrow_t@std@@@Z\"(i64, %struct*)\n" + "declare i8* @\"??_U@YAPAXI@Z\"(i32)\n" + "declare i8* @\"??_U@YAPAXIABUnothrow_t@std@@@Z\"(i32, %struct*)\n" + "declare i8* @\"??_U@YAPEAX_K@Z\"(i64)\n" + "declare i8* @\"??_U@YAPEAX_KAEBUnothrow_t@std@@@Z\"(i64, %struct*)\n" + + "declare void @\"??3@YAXPAX@Z\"(i8*)\n" + "declare void @\"??3@YAXPAXABUnothrow_t@std@@@Z\"(i8*, %struct*)\n" + "declare void @\"??3@YAXPAXI@Z\"(i8*, i32)\n" + "declare void @\"??_V@YAXPAX@Z\"(i8*)\n" + "declare void @\"??_V@YAXPAXABUnothrow_t@std@@@Z\"(i8*, %struct*)\n" + "declare void @\"??_V@YAXPAXI@Z\"(i8*, i32)\n" + + // These other functions were derived from the .def C declaration. + "declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*)\n" + "declare void @__cxa_guard_abort(%struct*)\n" + "declare i32 @__cxa_guard_acquire(%struct*)\n" + "declare void @__cxa_guard_release(%struct*)\n" + + "declare i32 @__nvvm_reflect(i8*)\n" + + "declare i8* @__memcpy_chk(i8*, i8*, i64, i64)\n" + "declare i8* @__memmove_chk(i8*, i8*, i64, i64)\n" + "declare i8* @__memset_chk(i8*, i32, i64, i64)\n" + "declare i8* @__stpcpy_chk(i8*, i8*, i64)\n" + "declare i8* @__stpncpy_chk(i8*, i8*, i64, i64)\n" + "declare i8* @__strcpy_chk(i8*, i8*, i64)\n" + "declare i8* @__strncpy_chk(i8*, i8*, i64, i64)\n" + + "declare i8* @memalign(i64, i64)\n" + "declare i8* @mempcpy(i8*, i8*, i64)\n" + "declare i8* @memrchr(i8*, i32, i64)\n" + + // These are similar to the FILE* fgetc/fputc. + "declare i32 @_IO_getc(%struct*)\n" + "declare i32 @_IO_putc(i32, %struct*)\n" + + "declare i32 @__isoc99_scanf(i8*, ...)\n" + "declare i32 @__isoc99_sscanf(i8*, i8*, ...)\n" + "declare i8* @__strdup(i8*)\n" + "declare i8* @__strndup(i8*, i64)\n" + "declare i8* @__strtok_r(i8*, i8*, i8**)\n" + + "declare double @__sqrt_finite(double)\n" + "declare float @__sqrtf_finite(float)\n" + "declare x86_fp80 @__sqrtl_finite(x86_fp80)\n" + "declare double @exp10(double)\n" + "declare float @exp10f(float)\n" + "declare x86_fp80 @exp10l(x86_fp80)\n" + + // These printf variants have the same prototype as the non-'i' versions. + "declare i32 @fiprintf(%struct*, i8*, ...)\n" + "declare i32 @iprintf(i8*, ...)\n" + "declare i32 @siprintf(i8*, i8*, ...)\n" + + "declare i32 @htonl(i32)\n" + "declare i16 @htons(i16)\n" + "declare i32 @ntohl(i32)\n" + "declare i16 @ntohs(i16)\n" + + "declare i32 @isascii(i32)\n" + "declare i32 @isdigit(i32)\n" + "declare i32 @toascii(i32)\n" + ); + + for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) { + LibFunc LF = (LibFunc)FI; + // Make sure everything is available; we're not testing target defaults. + TLII.setAvailable(LF); + Function *F = M->getFunction(TLI.getName(LF)); + EXPECT_TRUE(isLibFunc(F, LF)); + } +} diff --git a/unittests/Analysis/UnrollAnalyzer.cpp b/unittests/Analysis/UnrollAnalyzer.cpp index 6d11ab1f2f1ba..d6a7bd360b935 100644 --- a/unittests/Analysis/UnrollAnalyzer.cpp +++ b/unittests/Analysis/UnrollAnalyzer.cpp @@ -61,7 +61,6 @@ struct UnrollAnalyzerTest : public FunctionPass { char UnrollAnalyzerTest::ID = 0; std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context, - UnrollAnalyzerTest *P, const char *ModuleStr) { SMDiagnostic Err; return parseAssemblyString(ModuleStr, Err, Context); @@ -87,7 +86,7 @@ TEST(UnrollAnalyzerTest, BasicSimplifications) { "}\n"; UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); LLVMContext Context; - std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr); legacy::PassManager Passes; Passes.add(P); Passes.run(*M); @@ -150,7 +149,7 @@ TEST(UnrollAnalyzerTest, OuterLoopSimplification) { UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); LLVMContext Context; - std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr); legacy::PassManager Passes; Passes.add(P); Passes.run(*M); @@ -195,7 +194,7 @@ TEST(UnrollAnalyzerTest, CmpSimplifications) { "}\n"; UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); LLVMContext Context; - std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr); legacy::PassManager Passes; Passes.add(P); Passes.run(*M); @@ -242,7 +241,7 @@ TEST(UnrollAnalyzerTest, PtrCmpSimplifications) { "}\n"; UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); LLVMContext Context; - std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr); legacy::PassManager Passes; Passes.add(P); Passes.run(*M); @@ -288,7 +287,7 @@ TEST(UnrollAnalyzerTest, CastSimplifications) { UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); LLVMContext Context; - std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr); legacy::PassManager Passes; Passes.add(P); Passes.run(*M); diff --git a/unittests/Analysis/ValueTrackingTest.cpp b/unittests/Analysis/ValueTrackingTest.cpp index ba0d30d59b662..3c8ecfbe1ee23 100644 --- a/unittests/Analysis/ValueTrackingTest.cpp +++ b/unittests/Analysis/ValueTrackingTest.cpp @@ -219,7 +219,7 @@ TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) { assert(F && "Bad assembly?"); auto &BB = F->getEntryBlock(); - ArrayRef<bool> ExpectedAnswers = { + bool ExpectedAnswers[] = { true, // call void @nounwind_readonly(i32* %p) true, // call void @nounwind_argmemonly(i32* %p) false, // call void @throws_but_readonly(i32* %p) @@ -239,3 +239,22 @@ TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) { Index++; } } + +TEST(ValueTracking, ComputeNumSignBits_PR32045) { + StringRef Assembly = "define i32 @f(i32 %a) { " + " %val = ashr i32 %a, -1 " + " ret i32 %val " + "} "; + + LLVMContext Context; + SMDiagnostic Error; + auto M = parseAssemblyString(Assembly, Error, Context); + assert(M && "Bad assembly?"); + + auto *F = M->getFunction("f"); + assert(F && "Bad assembly?"); + + auto *RVal = + cast<ReturnInst>(F->getEntryBlock().getTerminator())->getOperand(0); + EXPECT_EQ(ComputeNumSignBits(RVal, M->getDataLayout()), 1u); +} diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 8dbca211d0268..8e40f141463ba 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -24,3 +24,4 @@ add_subdirectory(ProfileData) add_subdirectory(Support) add_subdirectory(Target) add_subdirectory(Transforms) +add_subdirectory(XRay) diff --git a/unittests/CodeGen/LowLevelTypeTest.cpp b/unittests/CodeGen/LowLevelTypeTest.cpp index 4ea181c1c9d3c..67113005a46a1 100644 --- a/unittests/CodeGen/LowLevelTypeTest.cpp +++ b/unittests/CodeGen/LowLevelTypeTest.cpp @@ -68,7 +68,7 @@ TEST(LowLevelTypeTest, Scalar) { // Test Type->LLT conversion. Type *IRTy = IntegerType::get(C, S); - EXPECT_EQ(Ty, LLT(*IRTy, DL)); + EXPECT_EQ(Ty, getLLTForType(*IRTy, DL)); } } @@ -160,7 +160,7 @@ TEST(LowLevelTypeTest, Vector) { // Test Type->LLT conversion. Type *IRSTy = IntegerType::get(C, S); Type *IRTy = VectorType::get(IRSTy, Elts); - EXPECT_EQ(VTy, LLT(*IRTy, DL)); + EXPECT_EQ(VTy, getLLTForType(*IRTy, DL)); } } } @@ -188,7 +188,7 @@ TEST(LowLevelTypeTest, Pointer) { // Test Type->LLT conversion. Type *IRTy = PointerType::get(IntegerType::get(C, 8), AS); - EXPECT_EQ(Ty, LLT(*IRTy, DL)); + EXPECT_EQ(Ty, getLLTForType(*IRTy, DL)); } } diff --git a/unittests/DebugInfo/DWARF/CMakeLists.txt b/unittests/DebugInfo/DWARF/CMakeLists.txt index eafca4a2fc06e..ed512a92ef183 100644 --- a/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS DebugInfoDWARF MC Object + ObjectYAML Support ) diff --git a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index a4109a34097f4..a6c5b3a34ccb7 100644 --- a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -8,16 +8,27 @@ //===----------------------------------------------------------------------===// #include "DwarfGenerator.h" -#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" -#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/ObjectYAML/DWARFYAML.h" +#include "llvm/ObjectYAML/DWARFEmitter.h" #include "llvm/Support/Dwarf.h" -#include "llvm/Support/Host.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/TargetSelect.h" #include "gtest/gtest.h" #include <climits> +#include <cstdint> +#include <cstring> +#include <string> using namespace llvm; using namespace dwarf; @@ -52,7 +63,7 @@ Triple getHostTripleForAddrSize(uint8_t AddrSize) { template <typename T> static bool HandleExpectedError(T &Expected) { std::string ErrorMsg; - handleAllErrors(Expected.takeError(), [&](const llvm::ErrorInfoBase &EI) { + handleAllErrors(Expected.takeError(), [&](const ErrorInfoBase &EI) { ErrorMsg = EI.message(); }); if (!ErrorMsg.empty()) { @@ -228,8 +239,7 @@ void TestAllForms() { //---------------------------------------------------------------------- // Test address forms //---------------------------------------------------------------------- - EXPECT_EQ(DieDG.getAttributeValueAsAddress(Attr_DW_FORM_addr).getValueOr(0), - AddrValue); + EXPECT_EQ(AddrValue, toAddress(DieDG.find(Attr_DW_FORM_addr), 0)); //---------------------------------------------------------------------- // Test block forms @@ -238,7 +248,7 @@ void TestAllForms() { ArrayRef<uint8_t> ExtractedBlockData; Optional<ArrayRef<uint8_t>> BlockDataOpt; - FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block); + FormValue = DieDG.find(Attr_DW_FORM_block); EXPECT_TRUE((bool)FormValue); BlockDataOpt = FormValue->getAsBlock(); EXPECT_TRUE(BlockDataOpt.hasValue()); @@ -246,7 +256,7 @@ void TestAllForms() { EXPECT_EQ(ExtractedBlockData.size(), BlockSize); EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); - FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block1); + FormValue = DieDG.find(Attr_DW_FORM_block1); EXPECT_TRUE((bool)FormValue); BlockDataOpt = FormValue->getAsBlock(); EXPECT_TRUE(BlockDataOpt.hasValue()); @@ -254,7 +264,7 @@ void TestAllForms() { EXPECT_EQ(ExtractedBlockData.size(), BlockSize); EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); - FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block2); + FormValue = DieDG.find(Attr_DW_FORM_block2); EXPECT_TRUE((bool)FormValue); BlockDataOpt = FormValue->getAsBlock(); EXPECT_TRUE(BlockDataOpt.hasValue()); @@ -262,7 +272,7 @@ void TestAllForms() { EXPECT_EQ(ExtractedBlockData.size(), BlockSize); EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); - FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block4); + FormValue = DieDG.find(Attr_DW_FORM_block4); EXPECT_TRUE((bool)FormValue); BlockDataOpt = FormValue->getAsBlock(); EXPECT_TRUE(BlockDataOpt.hasValue()); @@ -273,100 +283,64 @@ void TestAllForms() { //---------------------------------------------------------------------- // Test data forms //---------------------------------------------------------------------- - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data1) - .getValueOr(0), - Data1); - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data2) - .getValueOr(0), - Data2); - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data4) - .getValueOr(0), - Data4); - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data8) - .getValueOr(0), - Data8); + EXPECT_EQ(Data1, toUnsigned(DieDG.find(Attr_DW_FORM_data1), 0)); + EXPECT_EQ(Data2, toUnsigned(DieDG.find(Attr_DW_FORM_data2), 0)); + EXPECT_EQ(Data4, toUnsigned(DieDG.find(Attr_DW_FORM_data4), 0)); + EXPECT_EQ(Data8, toUnsigned(DieDG.find(Attr_DW_FORM_data8), 0)); //---------------------------------------------------------------------- // Test string forms //---------------------------------------------------------------------- - const char *ExtractedStringValue = - DieDG.getAttributeValueAsString(Attr_DW_FORM_string, nullptr); - EXPECT_TRUE(ExtractedStringValue != nullptr); - EXPECT_TRUE(strcmp(StringValue, ExtractedStringValue) == 0); + auto ExtractedStringValue = toString(DieDG.find(Attr_DW_FORM_string)); + EXPECT_TRUE((bool)ExtractedStringValue); + EXPECT_TRUE(strcmp(StringValue, *ExtractedStringValue) == 0); - const char *ExtractedStrpValue = - DieDG.getAttributeValueAsString(Attr_DW_FORM_strp, nullptr); - EXPECT_TRUE(ExtractedStrpValue != nullptr); - EXPECT_TRUE(strcmp(StrpValue, ExtractedStrpValue) == 0); + auto ExtractedStrpValue = toString(DieDG.find(Attr_DW_FORM_strp)); + EXPECT_TRUE((bool)ExtractedStrpValue); + EXPECT_TRUE(strcmp(StrpValue, *ExtractedStrpValue) == 0); //---------------------------------------------------------------------- // Test reference forms //---------------------------------------------------------------------- - EXPECT_EQ( - DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref_addr).getValueOr(0), - RefAddr); - EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref1).getValueOr(0), - Data1); - EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref2).getValueOr(0), - Data2); - EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref4).getValueOr(0), - Data4); - EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref8).getValueOr(0), - Data8); - EXPECT_EQ( - DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref_sig8).getValueOr(0), - Data8_2); - EXPECT_EQ( - DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref_udata).getValueOr(0), - UData[0]); + EXPECT_EQ(RefAddr, toReference(DieDG.find(Attr_DW_FORM_ref_addr), 0)); + EXPECT_EQ(Data1, toReference(DieDG.find(Attr_DW_FORM_ref1), 0)); + EXPECT_EQ(Data2, toReference(DieDG.find(Attr_DW_FORM_ref2), 0)); + EXPECT_EQ(Data4, toReference(DieDG.find(Attr_DW_FORM_ref4), 0)); + EXPECT_EQ(Data8, toReference(DieDG.find(Attr_DW_FORM_ref8), 0)); + EXPECT_EQ(Data8_2, toReference(DieDG.find(Attr_DW_FORM_ref_sig8), 0)); + EXPECT_EQ(UData[0], toReference(DieDG.find(Attr_DW_FORM_ref_udata), 0)); //---------------------------------------------------------------------- // Test flag forms //---------------------------------------------------------------------- - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_flag_true) - .getValueOr(0), - 1ULL); - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_flag_false) - .getValueOr(1), - 0ULL); - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_flag_present) - .getValueOr(0ULL), - 1ULL); + EXPECT_EQ(1ULL, toUnsigned(DieDG.find(Attr_DW_FORM_flag_true), 0)); + EXPECT_EQ(0ULL, toUnsigned(DieDG.find(Attr_DW_FORM_flag_false), 1)); + EXPECT_EQ(1ULL, toUnsigned(DieDG.find(Attr_DW_FORM_flag_present), 0)); //---------------------------------------------------------------------- // Test SLEB128 based forms //---------------------------------------------------------------------- - EXPECT_EQ( - DieDG.getAttributeValueAsSignedConstant(Attr_DW_FORM_sdata).getValueOr(0), - SData); + EXPECT_EQ(SData, toSigned(DieDG.find(Attr_DW_FORM_sdata), 0)); if (Version >= 5) - EXPECT_EQ( - DieDG.getAttributeValueAsSignedConstant(Attr_DW_FORM_implicit_const) - .getValueOr(0), - ICSData); + EXPECT_EQ(ICSData, toSigned(DieDG.find(Attr_DW_FORM_implicit_const), 0)); //---------------------------------------------------------------------- // Test ULEB128 based forms //---------------------------------------------------------------------- - EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_udata) - .getValueOr(0), - UData[0]); + EXPECT_EQ(UData[0], toUnsigned(DieDG.find(Attr_DW_FORM_udata), 0)); //---------------------------------------------------------------------- // Test DWARF32/DWARF64 forms //---------------------------------------------------------------------- - EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_GNU_ref_alt) - .getValueOr(0), - Dwarf32Values[0]); - EXPECT_EQ(DieDG.getAttributeValueAsSectionOffset(Attr_DW_FORM_sec_offset) - .getValueOr(0), - Dwarf32Values[1]); + EXPECT_EQ(Dwarf32Values[0], + toReference(DieDG.find(Attr_DW_FORM_GNU_ref_alt), 0)); + EXPECT_EQ(Dwarf32Values[1], + toSectionOffset(DieDG.find(Attr_DW_FORM_sec_offset), 0)); //---------------------------------------------------------------------- // Add an address at the end to make sure we can decode this value //---------------------------------------------------------------------- - EXPECT_EQ(DieDG.getAttributeValueAsAddress(Attr_Last).getValueOr(0), - AddrValue); + EXPECT_EQ(AddrValue, toAddress(DieDG.find(Attr_Last), 0)); } TEST(DWARFDebugInfo, TestDWARF32Version2Addr4AllForms) { @@ -488,7 +462,6 @@ template <uint16_t Version, class AddrType> void TestChildren() { // Get the compile unit DIE is valid. auto DieDG = U->getUnitDIE(false); EXPECT_TRUE(DieDG.isValid()); - // DieDG.dump(llvm::outs(), U, UINT32_MAX); // Verify the first child of the compile unit DIE is our subprogram. auto SubprogramDieDG = DieDG.getFirstChild(); @@ -662,133 +635,115 @@ template <uint16_t Version, class AddrType> void TestReferences() { // Get the compile unit DIE is valid. auto Unit1DieDG = U1->getUnitDIE(false); EXPECT_TRUE(Unit1DieDG.isValid()); - // Unit1DieDG.dump(llvm::outs(), UINT32_MAX); auto Unit2DieDG = U2->getUnitDIE(false); EXPECT_TRUE(Unit2DieDG.isValid()); - // Unit2DieDG.dump(llvm::outs(), UINT32_MAX); // Verify the first child of the compile unit 1 DIE is our int base type. auto CU1TypeDieDG = Unit1DieDG.getFirstChild(); EXPECT_TRUE(CU1TypeDieDG.isValid()); EXPECT_EQ(CU1TypeDieDG.getTag(), DW_TAG_base_type); - EXPECT_EQ(CU1TypeDieDG.getAttributeValueAsUnsignedConstant(DW_AT_encoding) - .getValueOr(0), - DW_ATE_signed); + EXPECT_EQ(DW_ATE_signed, toUnsigned(CU1TypeDieDG.find(DW_AT_encoding), 0)); // Verify the first child of the compile unit 2 DIE is our float base type. auto CU2TypeDieDG = Unit2DieDG.getFirstChild(); EXPECT_TRUE(CU2TypeDieDG.isValid()); EXPECT_EQ(CU2TypeDieDG.getTag(), DW_TAG_base_type); - EXPECT_EQ(CU2TypeDieDG.getAttributeValueAsUnsignedConstant(DW_AT_encoding) - .getValueOr(0), - DW_ATE_float); + EXPECT_EQ(DW_ATE_float, toUnsigned(CU2TypeDieDG.find(DW_AT_encoding), 0)); // Verify the sibling of the base type DIE is our Ref1 DIE and that its // DW_AT_type points to our base type DIE. auto CU1Ref1DieDG = CU1TypeDieDG.getSibling(); EXPECT_TRUE(CU1Ref1DieDG.isValid()); EXPECT_EQ(CU1Ref1DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU1Ref1DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU1TypeDieDG.getOffset()); + EXPECT_EQ(CU1TypeDieDG.getOffset(), + toReference(CU1Ref1DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our Ref2 DIE and that its DW_AT_type points to our // base type DIE in CU1. auto CU1Ref2DieDG = CU1Ref1DieDG.getSibling(); EXPECT_TRUE(CU1Ref2DieDG.isValid()); EXPECT_EQ(CU1Ref2DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU1Ref2DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU1TypeDieDG.getOffset()); + EXPECT_EQ(CU1TypeDieDG.getOffset(), + toReference(CU1Ref2DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our Ref4 DIE and that its DW_AT_type points to our // base type DIE in CU1. auto CU1Ref4DieDG = CU1Ref2DieDG.getSibling(); EXPECT_TRUE(CU1Ref4DieDG.isValid()); EXPECT_EQ(CU1Ref4DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU1Ref4DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU1TypeDieDG.getOffset()); + EXPECT_EQ(CU1TypeDieDG.getOffset(), + toReference(CU1Ref4DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our Ref8 DIE and that its DW_AT_type points to our // base type DIE in CU1. auto CU1Ref8DieDG = CU1Ref4DieDG.getSibling(); EXPECT_TRUE(CU1Ref8DieDG.isValid()); EXPECT_EQ(CU1Ref8DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU1Ref8DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU1TypeDieDG.getOffset()); + EXPECT_EQ(CU1TypeDieDG.getOffset(), + toReference(CU1Ref8DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our RefAddr DIE and that its DW_AT_type points to our // base type DIE in CU1. auto CU1RefAddrDieDG = CU1Ref8DieDG.getSibling(); EXPECT_TRUE(CU1RefAddrDieDG.isValid()); EXPECT_EQ(CU1RefAddrDieDG.getTag(), DW_TAG_variable); - EXPECT_EQ(CU1RefAddrDieDG.getAttributeValueAsReference(DW_AT_type) - .getValueOr(-1ULL), - CU1TypeDieDG.getOffset()); + EXPECT_EQ(CU1TypeDieDG.getOffset(), + toReference(CU1RefAddrDieDG.find(DW_AT_type), -1ULL)); // Verify the sibling of the Ref4 DIE is our RefAddr DIE and that its // DW_AT_type points to our base type DIE. auto CU1ToCU2RefAddrDieDG = CU1RefAddrDieDG.getSibling(); EXPECT_TRUE(CU1ToCU2RefAddrDieDG.isValid()); EXPECT_EQ(CU1ToCU2RefAddrDieDG.getTag(), DW_TAG_variable); - EXPECT_EQ(CU1ToCU2RefAddrDieDG.getAttributeValueAsReference(DW_AT_type) - .getValueOr(-1ULL), - CU2TypeDieDG.getOffset()); + EXPECT_EQ(CU2TypeDieDG.getOffset(), + toReference(CU1ToCU2RefAddrDieDG.find(DW_AT_type), -1ULL)); // Verify the sibling of the base type DIE is our Ref1 DIE and that its // DW_AT_type points to our base type DIE. auto CU2Ref1DieDG = CU2TypeDieDG.getSibling(); EXPECT_TRUE(CU2Ref1DieDG.isValid()); EXPECT_EQ(CU2Ref1DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU2Ref1DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU2TypeDieDG.getOffset()); + EXPECT_EQ(CU2TypeDieDG.getOffset(), + toReference(CU2Ref1DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our Ref2 DIE and that its DW_AT_type points to our // base type DIE in CU2. auto CU2Ref2DieDG = CU2Ref1DieDG.getSibling(); EXPECT_TRUE(CU2Ref2DieDG.isValid()); EXPECT_EQ(CU2Ref2DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU2Ref2DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU2TypeDieDG.getOffset()); + EXPECT_EQ(CU2TypeDieDG.getOffset(), + toReference(CU2Ref2DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our Ref4 DIE and that its DW_AT_type points to our // base type DIE in CU2. auto CU2Ref4DieDG = CU2Ref2DieDG.getSibling(); EXPECT_TRUE(CU2Ref4DieDG.isValid()); EXPECT_EQ(CU2Ref4DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU2Ref4DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU2TypeDieDG.getOffset()); + EXPECT_EQ(CU2TypeDieDG.getOffset(), + toReference(CU2Ref4DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our Ref8 DIE and that its DW_AT_type points to our // base type DIE in CU2. auto CU2Ref8DieDG = CU2Ref4DieDG.getSibling(); EXPECT_TRUE(CU2Ref8DieDG.isValid()); EXPECT_EQ(CU2Ref8DieDG.getTag(), DW_TAG_variable); - EXPECT_EQ( - CU2Ref8DieDG.getAttributeValueAsReference(DW_AT_type).getValueOr(-1ULL), - CU2TypeDieDG.getOffset()); + EXPECT_EQ(CU2TypeDieDG.getOffset(), + toReference(CU2Ref8DieDG.find(DW_AT_type), -1ULL)); // Verify the sibling is our RefAddr DIE and that its DW_AT_type points to our // base type DIE in CU2. auto CU2RefAddrDieDG = CU2Ref8DieDG.getSibling(); EXPECT_TRUE(CU2RefAddrDieDG.isValid()); EXPECT_EQ(CU2RefAddrDieDG.getTag(), DW_TAG_variable); - EXPECT_EQ(CU2RefAddrDieDG.getAttributeValueAsReference(DW_AT_type) - .getValueOr(-1ULL), - CU2TypeDieDG.getOffset()); + EXPECT_EQ(CU2TypeDieDG.getOffset(), + toReference(CU2RefAddrDieDG.find(DW_AT_type), -1ULL)); // Verify the sibling of the Ref4 DIE is our RefAddr DIE and that its // DW_AT_type points to our base type DIE. auto CU2ToCU1RefAddrDieDG = CU2RefAddrDieDG.getSibling(); EXPECT_TRUE(CU2ToCU1RefAddrDieDG.isValid()); EXPECT_EQ(CU2ToCU1RefAddrDieDG.getTag(), DW_TAG_variable); - EXPECT_EQ(CU2ToCU1RefAddrDieDG.getAttributeValueAsReference(DW_AT_type) - .getValueOr(-1ULL), - CU1TypeDieDG.getOffset()); + EXPECT_EQ(CU1TypeDieDG.getOffset(), + toReference(CU2ToCU1RefAddrDieDG.find(DW_AT_type), -1ULL)); } TEST(DWARFDebugInfo, TestDWARF32Version2Addr4References) { @@ -887,7 +842,6 @@ template <uint16_t Version, class AddrType> void TestAddresses() { // Get the compile unit DIE is valid. auto DieDG = U->getUnitDIE(false); EXPECT_TRUE(DieDG.isValid()); - // DieDG.dump(llvm::outs(), U, UINT32_MAX); uint64_t LowPC, HighPC; Optional<uint64_t> OptU64; @@ -896,50 +850,48 @@ template <uint16_t Version, class AddrType> void TestAddresses() { auto SubprogramDieNoPC = DieDG.getFirstChild(); EXPECT_TRUE(SubprogramDieNoPC.isValid()); EXPECT_EQ(SubprogramDieNoPC.getTag(), DW_TAG_subprogram); - OptU64 = SubprogramDieNoPC.getAttributeValueAsAddress(DW_AT_low_pc); + OptU64 = toAddress(SubprogramDieNoPC.find(DW_AT_low_pc)); EXPECT_FALSE((bool)OptU64); - OptU64 = SubprogramDieNoPC.getAttributeValueAsAddress(DW_AT_high_pc); + OptU64 = toAddress(SubprogramDieNoPC.find(DW_AT_high_pc)); EXPECT_FALSE((bool)OptU64); EXPECT_FALSE(SubprogramDieNoPC.getLowAndHighPC(LowPC, HighPC)); - OptU64 = SubprogramDieNoPC.getAttributeValueAsAddress(DW_AT_high_pc); + OptU64 = toAddress(SubprogramDieNoPC.find(DW_AT_high_pc)); EXPECT_FALSE((bool)OptU64); - OptU64 = SubprogramDieNoPC.getAttributeValueAsUnsignedConstant(DW_AT_high_pc); + OptU64 = toUnsigned(SubprogramDieNoPC.find(DW_AT_high_pc)); EXPECT_FALSE((bool)OptU64); OptU64 = SubprogramDieNoPC.getHighPC(ActualLowPC); EXPECT_FALSE((bool)OptU64); EXPECT_FALSE(SubprogramDieNoPC.getLowAndHighPC(LowPC, HighPC)); - - + // Verify the that our subprogram with only a low PC value succeeds when // we ask for the Low PC, but fails appropriately when asked for the high PC // or both low and high PC values. auto SubprogramDieLowPC = SubprogramDieNoPC.getSibling(); EXPECT_TRUE(SubprogramDieLowPC.isValid()); EXPECT_EQ(SubprogramDieLowPC.getTag(), DW_TAG_subprogram); - OptU64 = SubprogramDieLowPC.getAttributeValueAsAddress(DW_AT_low_pc); + OptU64 = toAddress(SubprogramDieLowPC.find(DW_AT_low_pc)); EXPECT_TRUE((bool)OptU64); EXPECT_EQ(OptU64.getValue(), ActualLowPC); - OptU64 = SubprogramDieLowPC.getAttributeValueAsAddress(DW_AT_high_pc); + OptU64 = toAddress(SubprogramDieLowPC.find(DW_AT_high_pc)); EXPECT_FALSE((bool)OptU64); - OptU64 = SubprogramDieLowPC.getAttributeValueAsUnsignedConstant(DW_AT_high_pc); + OptU64 = toUnsigned(SubprogramDieLowPC.find(DW_AT_high_pc)); EXPECT_FALSE((bool)OptU64); OptU64 = SubprogramDieLowPC.getHighPC(ActualLowPC); EXPECT_FALSE((bool)OptU64); EXPECT_FALSE(SubprogramDieLowPC.getLowAndHighPC(LowPC, HighPC)); - // Verify the that our subprogram with only a low PC value succeeds when // we ask for the Low PC, but fails appropriately when asked for the high PC // or both low and high PC values. auto SubprogramDieLowHighPC = SubprogramDieLowPC.getSibling(); EXPECT_TRUE(SubprogramDieLowHighPC.isValid()); EXPECT_EQ(SubprogramDieLowHighPC.getTag(), DW_TAG_subprogram); - OptU64 = SubprogramDieLowHighPC.getAttributeValueAsAddress(DW_AT_low_pc); + OptU64 = toAddress(SubprogramDieLowHighPC.find(DW_AT_low_pc)); EXPECT_TRUE((bool)OptU64); EXPECT_EQ(OptU64.getValue(), ActualLowPC); // Get the high PC as an address. This should succeed if the high PC was // encoded as an address and fail if the high PC was encoded as an offset. - OptU64 = SubprogramDieLowHighPC.getAttributeValueAsAddress(DW_AT_high_pc); + OptU64 = toAddress(SubprogramDieLowHighPC.find(DW_AT_high_pc)); if (SupportsHighPCAsOffset) { EXPECT_FALSE((bool)OptU64); } else { @@ -948,8 +900,7 @@ template <uint16_t Version, class AddrType> void TestAddresses() { } // Get the high PC as an unsigned constant. This should succeed if the high PC // was encoded as an offset and fail if the high PC was encoded as an address. - OptU64 = SubprogramDieLowHighPC.getAttributeValueAsUnsignedConstant( - DW_AT_high_pc); + OptU64 = toUnsigned(SubprogramDieLowHighPC.find(DW_AT_high_pc)); if (SupportsHighPCAsOffset) { EXPECT_TRUE((bool)OptU64); EXPECT_EQ(OptU64.getValue(), ActualHighPCOffset); @@ -1067,7 +1018,6 @@ TEST(DWARFDebugInfo, TestRelations) { // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); EXPECT_TRUE(CUDie.isValid()); - // CUDie.dump(llvm::outs(), UINT32_MAX); // The compile unit doesn't have a parent or a sibling. auto ParentDie = CUDie.getParent(); @@ -1132,7 +1082,6 @@ TEST(DWARFDebugInfo, TestRelations) { } TEST(DWARFDebugInfo, TestDWARFDie) { - // Make sure a default constructed DWARFDie doesn't have any parent, sibling // or child; DWARFDie DefaultDie; @@ -1185,7 +1134,6 @@ TEST(DWARFDebugInfo, TestChildIterators) { // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); EXPECT_TRUE(CUDie.isValid()); - // CUDie.dump(llvm::outs(), UINT32_MAX); uint32_t Index; DWARFDie A; DWARFDie B; @@ -1217,11 +1165,49 @@ TEST(DWARFDebugInfo, TestChildIteratorsOnInvalidDie) { EXPECT_EQ(begin, end); } - TEST(DWARFDebugInfo, TestEmptyChildren) { - // Test a DIE that says it has children in the abbreviation, but actually - // doesn't have any attributes, will not return anything during iteration. - // We do this by making sure the begin and end iterators are equal. + const char *yamldata = "debug_abbrev:\n" + " - Code: 0x00000001\n" + " Tag: DW_TAG_compile_unit\n" + " Children: DW_CHILDREN_yes\n" + " Attributes:\n" + "debug_info:\n" + " - Length:\n" + " TotalLength: 9\n" + " Version: 4\n" + " AbbrOffset: 0\n" + " AddrSize: 8\n" + " Entries:\n" + " - AbbrCode: 0x00000001\n" + " Values:\n" + " - AbbrCode: 0x00000000\n" + " Values:\n"; + + auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); + ASSERT_TRUE((bool)ErrOrSections); + + auto &DebugSections = *ErrOrSections; + + DWARFContextInMemory DwarfContext(DebugSections, 8); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + + // Get the compile unit DIE is valid. + auto CUDie = U->getUnitDIE(false); + EXPECT_TRUE(CUDie.isValid()); + + // Verify that the CU Die that says it has children, but doesn't, actually + // has begin and end iterators that are equal. We want to make sure we don't + // see the Null DIEs during iteration. + EXPECT_EQ(CUDie.begin(), CUDie.end()); +} + +TEST(DWARFDebugInfo, TestAttributeIterators) { + // Test the DWARF APIs related to iterating across all attribute values in a + // a DWARFDie. uint16_t Version = 4; const uint8_t AddrSize = sizeof(void *); @@ -1232,14 +1218,19 @@ TEST(DWARFDebugInfo, TestEmptyChildren) { return; dwarfgen::Generator *DG = ExpectedDG.get().get(); dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + const uint64_t CULowPC = 0x1000; + StringRef CUPath("/tmp/main.c"); // Scope to allow us to re-use the same DIE names { - // Create a compile unit DIE that has an abbreviation that says it has - // children, but doesn't have any actual attributes. This helps us test - // a DIE that has only one child: a NULL DIE. auto CUDie = CU.getUnitDIE(); - CUDie.setForceChildren(); + // Encode an attribute value before an attribute with no data. + CUDie.addAttribute(DW_AT_name, DW_FORM_strp, CUPath.data()); + // Encode an attribute value with no data in .debug_info/types to ensure + // the iteration works correctly. + CUDie.addAttribute(DW_AT_declaration, DW_FORM_flag_present); + // Encode an attribute value after an attribute with no data. + CUDie.addAttribute(DW_AT_low_pc, DW_FORM_addr, CULowPC); } MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); @@ -1255,12 +1246,419 @@ TEST(DWARFDebugInfo, TestEmptyChildren) { // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); EXPECT_TRUE(CUDie.isValid()); - CUDie.dump(llvm::outs(), UINT32_MAX); - // Verify that the CU Die that says it has children, but doesn't, actually - // has begin and end iterators that are equal. We want to make sure we don't - // see the Null DIEs during iteration. - EXPECT_EQ(CUDie.begin(), CUDie.end()); + auto R = CUDie.attributes(); + auto I = R.begin(); + auto E = R.end(); + + ASSERT_NE(E, I); + EXPECT_EQ(I->Attr, DW_AT_name); + auto ActualCUPath = I->Value.getAsCString(); + EXPECT_EQ(CUPath, *ActualCUPath); + + ASSERT_NE(E, ++I); + EXPECT_EQ(I->Attr, DW_AT_declaration); + EXPECT_EQ(1ull, *I->Value.getAsUnsignedConstant()); + + ASSERT_NE(E, ++I); + EXPECT_EQ(I->Attr, DW_AT_low_pc); + EXPECT_EQ(CULowPC, *I->Value.getAsAddress()); + + EXPECT_EQ(E, ++I); +} + +TEST(DWARFDebugInfo, TestFindRecurse) { + uint16_t Version = 4; + + const uint8_t AddrSize = sizeof(void *); + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + + StringRef SpecDieName = "spec"; + StringRef SpecLinkageName = "spec_linkage"; + StringRef AbsDieName = "abs"; + // Scope to allow us to re-use the same DIE names + { + auto CUDie = CU.getUnitDIE(); + auto FuncSpecDie = CUDie.addChild(DW_TAG_subprogram); + auto FuncAbsDie = CUDie.addChild(DW_TAG_subprogram); + auto FuncDie = CUDie.addChild(DW_TAG_subprogram); + auto VarAbsDie = CUDie.addChild(DW_TAG_variable); + auto VarDie = CUDie.addChild(DW_TAG_variable); + FuncSpecDie.addAttribute(DW_AT_name, DW_FORM_strp, SpecDieName); + FuncAbsDie.addAttribute(DW_AT_linkage_name, DW_FORM_strp, SpecLinkageName); + FuncAbsDie.addAttribute(DW_AT_specification, DW_FORM_ref4, FuncSpecDie); + FuncDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, FuncAbsDie); + VarAbsDie.addAttribute(DW_AT_name, DW_FORM_strp, AbsDieName); + VarDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, VarAbsDie); + } + + MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + + // Get the compile unit DIE is valid. + auto CUDie = U->getUnitDIE(false); + EXPECT_TRUE(CUDie.isValid()); + + auto FuncSpecDie = CUDie.getFirstChild(); + auto FuncAbsDie = FuncSpecDie.getSibling(); + auto FuncDie = FuncAbsDie.getSibling(); + auto VarAbsDie = FuncDie.getSibling(); + auto VarDie = VarAbsDie.getSibling(); + + // Make sure we can't extract the name from the specification die when using + // DWARFDie::find() since it won't check the DW_AT_specification DIE. + EXPECT_FALSE(FuncDie.find(DW_AT_name)); + + // Make sure we can extract the name from the specification die when using + // DWARFDie::findRecursively() since it should recurse through the + // DW_AT_specification DIE. + auto NameOpt = FuncDie.findRecursively(DW_AT_name); + EXPECT_TRUE(NameOpt); + // Test the dwarf::toString() helper function. + auto StringOpt = toString(NameOpt); + EXPECT_TRUE(StringOpt); + EXPECT_EQ(SpecDieName, StringOpt.getValueOr(nullptr)); + // Test the dwarf::toString() helper function with a default value specified. + EXPECT_EQ(SpecDieName, toString(NameOpt, nullptr)); + + auto LinkageNameOpt = FuncDie.findRecursively(DW_AT_linkage_name); + EXPECT_EQ(SpecLinkageName, toString(LinkageNameOpt).getValueOr(nullptr)); + + // Make sure we can't extract the name from the abstract origin die when using + // DWARFDie::find() since it won't check the DW_AT_abstract_origin DIE. + EXPECT_FALSE(VarDie.find(DW_AT_name)); + + // Make sure we can extract the name from the abstract origin die when using + // DWARFDie::findRecursively() since it should recurse through the + // DW_AT_abstract_origin DIE. + NameOpt = VarDie.findRecursively(DW_AT_name); + EXPECT_TRUE(NameOpt); + // Test the dwarf::toString() helper function. + StringOpt = toString(NameOpt); + EXPECT_TRUE(StringOpt); + EXPECT_EQ(AbsDieName, StringOpt.getValueOr(nullptr)); +} + +TEST(DWARFDebugInfo, TestDwarfToFunctions) { + // Test all of the dwarf::toXXX functions that take a + // Optional<DWARFFormValue> and extract the values from it. + DWARFFormValue FormVal; + uint64_t InvalidU64 = 0xBADBADBADBADBADB; + int64_t InvalidS64 = 0xBADBADBADBADBADB; + // First test that we don't get valid values back when using an optional with + // no value. + Optional<DWARFFormValue> FormValOpt; + EXPECT_FALSE(toString(FormValOpt).hasValue()); + EXPECT_FALSE(toUnsigned(FormValOpt).hasValue()); + EXPECT_FALSE(toReference(FormValOpt).hasValue()); + EXPECT_FALSE(toSigned(FormValOpt).hasValue()); + EXPECT_FALSE(toAddress(FormValOpt).hasValue()); + EXPECT_FALSE(toSectionOffset(FormValOpt).hasValue()); + EXPECT_FALSE(toBlock(FormValOpt).hasValue()); + EXPECT_EQ(nullptr, toString(FormValOpt, nullptr)); + EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toReference(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toAddress(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidS64, toSigned(FormValOpt, InvalidS64)); + + // Test successful and unsuccessful address decoding. + uint64_t Address = 0x100000000ULL; + FormVal.setForm(DW_FORM_addr); + FormVal.setUValue(Address); + FormValOpt = FormVal; + + EXPECT_FALSE(toString(FormValOpt).hasValue()); + EXPECT_FALSE(toUnsigned(FormValOpt).hasValue()); + EXPECT_FALSE(toReference(FormValOpt).hasValue()); + EXPECT_FALSE(toSigned(FormValOpt).hasValue()); + EXPECT_TRUE(toAddress(FormValOpt).hasValue()); + EXPECT_FALSE(toSectionOffset(FormValOpt).hasValue()); + EXPECT_FALSE(toBlock(FormValOpt).hasValue()); + EXPECT_EQ(nullptr, toString(FormValOpt, nullptr)); + EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toReference(FormValOpt, InvalidU64)); + EXPECT_EQ(Address, toAddress(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidS64, toSigned(FormValOpt, InvalidU64)); + + // Test successful and unsuccessful unsigned constant decoding. + uint64_t UData8 = 0x1020304050607080ULL; + FormVal.setForm(DW_FORM_udata); + FormVal.setUValue(UData8); + FormValOpt = FormVal; + + EXPECT_FALSE(toString(FormValOpt).hasValue()); + EXPECT_TRUE(toUnsigned(FormValOpt).hasValue()); + EXPECT_FALSE(toReference(FormValOpt).hasValue()); + EXPECT_TRUE(toSigned(FormValOpt).hasValue()); + EXPECT_FALSE(toAddress(FormValOpt).hasValue()); + EXPECT_FALSE(toSectionOffset(FormValOpt).hasValue()); + EXPECT_FALSE(toBlock(FormValOpt).hasValue()); + EXPECT_EQ(nullptr, toString(FormValOpt, nullptr)); + EXPECT_EQ(UData8, toUnsigned(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toReference(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toAddress(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt, InvalidU64)); + EXPECT_EQ((int64_t)UData8, toSigned(FormValOpt, InvalidU64)); + + // Test successful and unsuccessful reference decoding. + uint32_t RefData = 0x11223344U; + FormVal.setForm(DW_FORM_ref_addr); + FormVal.setUValue(RefData); + FormValOpt = FormVal; + + EXPECT_FALSE(toString(FormValOpt).hasValue()); + EXPECT_FALSE(toUnsigned(FormValOpt).hasValue()); + EXPECT_TRUE(toReference(FormValOpt).hasValue()); + EXPECT_FALSE(toSigned(FormValOpt).hasValue()); + EXPECT_FALSE(toAddress(FormValOpt).hasValue()); + EXPECT_FALSE(toSectionOffset(FormValOpt).hasValue()); + EXPECT_FALSE(toBlock(FormValOpt).hasValue()); + EXPECT_EQ(nullptr, toString(FormValOpt, nullptr)); + EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt, InvalidU64)); + EXPECT_EQ(RefData, toReference(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toAddress(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidS64, toSigned(FormValOpt, InvalidU64)); + + // Test successful and unsuccessful signed constant decoding. + int64_t SData8 = 0x1020304050607080ULL; + FormVal.setForm(DW_FORM_udata); + FormVal.setSValue(SData8); + FormValOpt = FormVal; + + EXPECT_FALSE(toString(FormValOpt).hasValue()); + EXPECT_TRUE(toUnsigned(FormValOpt).hasValue()); + EXPECT_FALSE(toReference(FormValOpt).hasValue()); + EXPECT_TRUE(toSigned(FormValOpt).hasValue()); + EXPECT_FALSE(toAddress(FormValOpt).hasValue()); + EXPECT_FALSE(toSectionOffset(FormValOpt).hasValue()); + EXPECT_FALSE(toBlock(FormValOpt).hasValue()); + EXPECT_EQ(nullptr, toString(FormValOpt, nullptr)); + EXPECT_EQ((uint64_t)SData8, toUnsigned(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toReference(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toAddress(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt, InvalidU64)); + EXPECT_EQ(SData8, toSigned(FormValOpt, InvalidU64)); + + // Test successful and unsuccessful block decoding. + uint8_t Data[] = { 2, 3, 4 }; + ArrayRef<uint8_t> Array(Data); + FormVal.setForm(DW_FORM_block1); + FormVal.setBlockValue(Array); + FormValOpt = FormVal; + + EXPECT_FALSE(toString(FormValOpt).hasValue()); + EXPECT_FALSE(toUnsigned(FormValOpt).hasValue()); + EXPECT_FALSE(toReference(FormValOpt).hasValue()); + EXPECT_FALSE(toSigned(FormValOpt).hasValue()); + EXPECT_FALSE(toAddress(FormValOpt).hasValue()); + EXPECT_FALSE(toSectionOffset(FormValOpt).hasValue()); + auto BlockOpt = toBlock(FormValOpt); + EXPECT_TRUE(BlockOpt.hasValue()); + EXPECT_EQ(*BlockOpt, Array); + EXPECT_EQ(nullptr, toString(FormValOpt, nullptr)); + EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toReference(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toAddress(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt, InvalidU64)); + EXPECT_EQ(InvalidS64, toSigned(FormValOpt, InvalidU64)); + + // Test +} + +TEST(DWARFDebugInfo, TestFindAttrs) { + // Test the DWARFDie::find() and DWARFDie::findRecursively() that take an + // ArrayRef<dwarf::Attribute> value to make sure they work correctly. + uint16_t Version = 4; + + const uint8_t AddrSize = sizeof(void *); + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + + StringRef DieMangled("_Z3fooi"); + // Scope to allow us to re-use the same DIE names + { + auto CUDie = CU.getUnitDIE(); + auto FuncSpecDie = CUDie.addChild(DW_TAG_subprogram); + auto FuncDie = CUDie.addChild(DW_TAG_subprogram); + FuncSpecDie.addAttribute(DW_AT_MIPS_linkage_name, DW_FORM_strp, DieMangled); + FuncDie.addAttribute(DW_AT_specification, DW_FORM_ref4, FuncSpecDie); + } + + MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + + // Get the compile unit DIE is valid. + auto CUDie = U->getUnitDIE(false); + EXPECT_TRUE(CUDie.isValid()); + + auto FuncSpecDie = CUDie.getFirstChild(); + auto FuncDie = FuncSpecDie.getSibling(); + + // Make sure that passing in an empty attribute list behave correctly. + EXPECT_FALSE(FuncDie.find(ArrayRef<dwarf::Attribute>()).hasValue()); + + // Make sure that passing in a list of attribute that are not contained + // in the DIE returns nothing. + EXPECT_FALSE(FuncDie.find({DW_AT_low_pc, DW_AT_entry_pc}).hasValue()); + + const dwarf::Attribute Attrs[] = {DW_AT_linkage_name, + DW_AT_MIPS_linkage_name}; + + // Make sure we can't extract the linkage name attributes when using + // DWARFDie::find() since it won't check the DW_AT_specification DIE. + EXPECT_FALSE(FuncDie.find(Attrs).hasValue()); + + // Make sure we can extract the name from the specification die when using + // DWARFDie::findRecursively() since it should recurse through the + // DW_AT_specification DIE. + auto NameOpt = FuncDie.findRecursively(Attrs); + EXPECT_TRUE(NameOpt.hasValue()); + EXPECT_EQ(DieMangled, toString(NameOpt, "")); +} + +TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { + uint16_t Version = 5; + + const uint8_t AddrSize = sizeof(void *); + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + dwarfgen::DIE CUDie = CU.getUnitDIE(); + const dwarf::Attribute Attr = DW_AT_lo_user; + const int64_t Val1 = 42; + const int64_t Val2 = 43; + + auto FirstVal1DIE = CUDie.addChild(DW_TAG_class_type); + FirstVal1DIE.addAttribute(Attr, DW_FORM_implicit_const, Val1); + + auto SecondVal1DIE = CUDie.addChild(DW_TAG_class_type); + SecondVal1DIE.addAttribute(Attr, DW_FORM_implicit_const, Val1); + + auto Val2DIE = CUDie.addChild(DW_TAG_class_type); + Val2DIE.addAttribute(Attr, DW_FORM_implicit_const, Val2); + + MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + EXPECT_TRUE((bool)U); + + const auto *Abbrevs = U->getAbbreviations(); + EXPECT_TRUE((bool)Abbrevs); + + // Let's find implicit_const abbrevs and verify, + // that there are exactly two of them and both of them + // can be dumped correctly. + typedef decltype(Abbrevs->begin()) AbbrevIt; + AbbrevIt Val1Abbrev = Abbrevs->end(); + AbbrevIt Val2Abbrev = Abbrevs->end(); + for(auto it = Abbrevs->begin(); it != Abbrevs->end(); ++it) { + if (it->getNumAttributes() == 0) + continue; // root abbrev for DW_TAG_compile_unit + + auto A = it->getAttrByIndex(0); + EXPECT_EQ(A, Attr); + + auto FormValue = it->getAttributeValue(/* offset */ 0, A, *U); + EXPECT_TRUE((bool)FormValue); + EXPECT_EQ(FormValue->getForm(), dwarf::DW_FORM_implicit_const); + + const auto V = FormValue->getAsSignedConstant(); + EXPECT_TRUE((bool)V); + + auto VerifyAbbrevDump = [&V](AbbrevIt it) { + std::string S; + llvm::raw_string_ostream OS(S); + it->dump(OS); + auto FormPos = OS.str().find("DW_FORM_implicit_const"); + EXPECT_NE(FormPos, std::string::npos); + auto ValPos = S.find_first_of("-0123456789", FormPos); + EXPECT_NE(ValPos, std::string::npos); + int64_t Val = std::atoll(S.substr(ValPos).c_str()); + EXPECT_EQ(Val, *V); + }; + + switch(*V) { + case Val1: + EXPECT_EQ(Val1Abbrev, Abbrevs->end()); + Val1Abbrev = it; + VerifyAbbrevDump(it); + break; + case Val2: + EXPECT_EQ(Val2Abbrev, Abbrevs->end()); + Val2Abbrev = it; + VerifyAbbrevDump(it); + break; + default: + FAIL() << "Unexpected attribute value: " << *V; + } + } + + // Now let's make sure that two Val1-DIEs refer to the same abbrev, + // and Val2-DIE refers to another one. + auto DieDG = U->getUnitDIE(false); + auto it = DieDG.begin(); + std::multimap<int64_t, decltype(it->getAbbreviationDeclarationPtr())> DIEs; + const DWARFAbbreviationDeclaration *AbbrevPtrVal1 = nullptr; + const DWARFAbbreviationDeclaration *AbbrevPtrVal2 = nullptr; + for (; it != DieDG.end(); ++it) { + const auto *AbbrevPtr = it->getAbbreviationDeclarationPtr(); + EXPECT_TRUE((bool)AbbrevPtr); + auto FormValue = it->find(Attr); + EXPECT_TRUE((bool)FormValue); + const auto V = FormValue->getAsSignedConstant(); + EXPECT_TRUE((bool)V); + switch(*V) { + case Val1: + AbbrevPtrVal1 = AbbrevPtr; + break; + case Val2: + AbbrevPtrVal2 = AbbrevPtr; + break; + default: + FAIL() << "Unexpected attribute value: " << *V; + } + DIEs.insert(std::make_pair(*V, AbbrevPtr)); + } + EXPECT_EQ(DIEs.count(Val1), 2u); + EXPECT_EQ(DIEs.count(Val2), 1u); + auto Val1Range = DIEs.equal_range(Val1); + for (auto it = Val1Range.first; it != Val1Range.second; ++it) + EXPECT_EQ(it->second, AbbrevPtrVal1); + EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2); } } // end anonymous namespace diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp index 9ec43cab4dc09..ac63bbaf0a11b 100644 --- a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp @@ -108,10 +108,6 @@ dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() { return dwarfgen::DIE(this, &DU.getUnitDie()); } -void dwarfgen::DIE::setForceChildren() { - Die->setForceChildren(true); -} - //===----------------------------------------------------------------------===// /// dwarfgen::Generator implementation. //===----------------------------------------------------------------------===// @@ -240,8 +236,14 @@ StringRef dwarfgen::Generator::generate() { assert(Length != -1U); Asm->EmitInt32(Length); Asm->EmitInt16(Version); - Asm->EmitInt32(0); - Asm->EmitInt8(CU->getAddressSize()); + if (Version <= 4) { + Asm->EmitInt32(0); + Asm->EmitInt8(CU->getAddressSize()); + } else { + Asm->EmitInt8(dwarf::DW_UT_compile); + Asm->EmitInt8(CU->getAddressSize()); + Asm->EmitInt32(0); + } Asm->emitDwarfDIE(*CU->getUnitDIE().Die); } diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.h b/unittests/DebugInfo/DWARF/DwarfGenerator.h index 2978d1ca0021e..966725b4fa4e7 100644 --- a/unittests/DebugInfo/DWARF/DwarfGenerator.h +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.h @@ -129,9 +129,6 @@ public: /// \returns the newly created DIE object that is now a child owned by this /// object. dwarfgen::DIE addChild(dwarf::Tag Tag); - - /// Force a DIE to say it has children even when it doesn't. - void setForceChildren(); }; /// A DWARF compile unit used to generate DWARF compile/type units. diff --git a/unittests/DebugInfo/PDB/CMakeLists.txt b/unittests/DebugInfo/PDB/CMakeLists.txt index 406b487e7a189..cbbbd81774837 100644 --- a/unittests/DebugInfo/PDB/CMakeLists.txt +++ b/unittests/DebugInfo/PDB/CMakeLists.txt @@ -5,9 +5,12 @@ set(LLVM_LINK_COMPONENTS ) set(DebugInfoPDBSources + HashTableTest.cpp MappedBlockStreamTest.cpp + StringTableBuilderTest.cpp MSFBuilderTest.cpp PDBApiTest.cpp + TypeServerHandlerTest.cpp ) add_llvm_unittest(DebugInfoPDBTests diff --git a/unittests/DebugInfo/PDB/HashTableTest.cpp b/unittests/DebugInfo/PDB/HashTableTest.cpp new file mode 100644 index 0000000000000..94c9ee86c4a63 --- /dev/null +++ b/unittests/DebugInfo/PDB/HashTableTest.cpp @@ -0,0 +1,168 @@ +//===- llvm/unittest/DebugInfo/PDB/HashTableTest.cpp ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ErrorChecking.h" +#include "gtest/gtest.h" + +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" + +#include <vector> + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::support; + +namespace { +class HashTableInternals : public HashTable { +public: + using HashTable::Buckets; + using HashTable::Present; + using HashTable::Deleted; +}; +} + +TEST(HashTableTest, TestSimple) { + HashTable Table; + EXPECT_EQ(0u, Table.size()); + EXPECT_GT(Table.capacity(), 0u); + + Table.set(3, 7); + EXPECT_EQ(1u, Table.size()); + ASSERT_NE(Table.end(), Table.find(3)); + EXPECT_EQ(7u, Table.get(3)); +} + +TEST(HashTableTest, TestCollision) { + HashTable Table; + EXPECT_EQ(0u, Table.size()); + EXPECT_GT(Table.capacity(), 0u); + + // We use knowledge of the hash table's implementation details to make sure + // to add another value that is the equivalent to the first value modulo the + // hash table's capacity. + uint32_t N1 = Table.capacity() + 1; + uint32_t N2 = 2 * N1; + + Table.set(N1, 7); + Table.set(N2, 12); + EXPECT_EQ(2u, Table.size()); + ASSERT_NE(Table.end(), Table.find(N1)); + ASSERT_NE(Table.end(), Table.find(N2)); + + EXPECT_EQ(7u, Table.get(N1)); + EXPECT_EQ(12u, Table.get(N2)); +} + +TEST(HashTableTest, TestRemove) { + HashTable Table; + EXPECT_EQ(0u, Table.size()); + EXPECT_GT(Table.capacity(), 0u); + + Table.set(1, 2); + Table.set(3, 4); + EXPECT_EQ(2u, Table.size()); + ASSERT_NE(Table.end(), Table.find(1)); + ASSERT_NE(Table.end(), Table.find(3)); + + EXPECT_EQ(2u, Table.get(1)); + EXPECT_EQ(4u, Table.get(3)); + + Table.remove(1u); + EXPECT_EQ(1u, Table.size()); + EXPECT_EQ(Table.end(), Table.find(1)); + ASSERT_NE(Table.end(), Table.find(3)); + EXPECT_EQ(4u, Table.get(3)); +} + +TEST(HashTableTest, TestCollisionAfterMultipleProbes) { + HashTable Table; + EXPECT_EQ(0u, Table.size()); + EXPECT_GT(Table.capacity(), 0u); + + // Probing looks for the first available slot. A slot may already be filled + // as a result of an item with a *different* hash value already being there. + // Test that when this happens, the probe still finds the value. + uint32_t N1 = Table.capacity() + 1; + uint32_t N2 = N1 + 1; + uint32_t N3 = 2 * N1; + + Table.set(N1, 7); + Table.set(N2, 11); + Table.set(N3, 13); + EXPECT_EQ(3u, Table.size()); + ASSERT_NE(Table.end(), Table.find(N1)); + ASSERT_NE(Table.end(), Table.find(N2)); + ASSERT_NE(Table.end(), Table.find(N3)); + + EXPECT_EQ(7u, Table.get(N1)); + EXPECT_EQ(11u, Table.get(N2)); + EXPECT_EQ(13u, Table.get(N3)); + + // Remove the one that had been filled in the middle, then insert another one + // with a collision. It should fill the newly emptied slot. + Table.remove(N2); + uint32_t N4 = N1 * 3; + Table.set(N4, 17); + EXPECT_EQ(3u, Table.size()); + ASSERT_NE(Table.end(), Table.find(N1)); + ASSERT_NE(Table.end(), Table.find(N3)); + ASSERT_NE(Table.end(), Table.find(N4)); + + EXPECT_EQ(7u, Table.get(N1)); + EXPECT_EQ(13u, Table.get(N3)); + EXPECT_EQ(17u, Table.get(N4)); +} + +TEST(HashTableTest, Grow) { + // So that we are independent of the load factor, `capacity` items, which is + // guaranteed to trigger a grow. Then verify that the size is the same, the + // capacity is larger, and all the original items are still in the table. + + HashTable Table; + uint32_t OldCapacity = Table.capacity(); + for (uint32_t I = 0; I < OldCapacity; ++I) { + Table.set(OldCapacity + I * 2 + 1, I * 2 + 3); + } + EXPECT_EQ(OldCapacity, Table.size()); + EXPECT_GT(Table.capacity(), OldCapacity); + for (uint32_t I = 0; I < OldCapacity; ++I) { + ASSERT_NE(Table.end(), Table.find(OldCapacity + I * 2 + 1)); + EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1)); + } +} + +TEST(HashTableTest, Serialization) { + HashTableInternals Table; + uint32_t Cap = Table.capacity(); + for (uint32_t I = 0; I < Cap; ++I) { + Table.set(Cap + I * 2 + 1, I * 2 + 3); + } + + std::vector<uint8_t> Buffer(Table.calculateSerializedLength()); + MutableBinaryByteStream Stream(Buffer, little); + BinaryStreamWriter Writer(Stream); + EXPECT_NO_ERROR(Table.commit(Writer)); + // We should have written precisely the number of bytes we calculated earlier. + EXPECT_EQ(Buffer.size(), Writer.getOffset()); + + HashTableInternals Table2; + BinaryStreamReader Reader(Stream); + EXPECT_NO_ERROR(Table2.load(Reader)); + // We should have read precisely the number of bytes we calculated earlier. + EXPECT_EQ(Buffer.size(), Reader.getOffset()); + + EXPECT_EQ(Table.size(), Table2.size()); + EXPECT_EQ(Table.capacity(), Table2.capacity()); + EXPECT_EQ(Table.Buckets, Table2.Buckets); + EXPECT_EQ(Table.Present, Table2.Present); + EXPECT_EQ(Table.Deleted, Table2.Deleted); +} diff --git a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp index 07591ca69d308..9f8940b77f28d 100644 --- a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp +++ b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp @@ -9,26 +9,28 @@ #include "ErrorChecking.h" -#include "llvm/DebugInfo/MSF/ByteStream.h" #include "llvm/DebugInfo/MSF/IMSFFile.h" -#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/MSFError.h" #include "llvm/DebugInfo/MSF/MSFStreamLayout.h" -#include "llvm/DebugInfo/MSF/StreamReader.h" -#include "llvm/DebugInfo/MSF/StreamRef.h" -#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/BinaryStreamWriter.h" #include "gtest/gtest.h" #include <unordered_map> using namespace llvm; using namespace llvm::msf; +using namespace llvm::support; namespace { static const uint32_t BlocksAry[] = {0, 1, 2, 5, 4, 3, 6, 7, 8, 9}; static uint8_t DataAry[] = {'A', 'B', 'C', 'F', 'E', 'D', 'G', 'H', 'I', 'J'}; -class DiscontiguousStream : public WritableStream { +class DiscontiguousStream : public WritableBinaryStream { public: DiscontiguousStream(ArrayRef<uint32_t> Blocks, MutableArrayRef<uint8_t> Data) : Blocks(Blocks.begin(), Blocks.end()), Data(Data.begin(), Data.end()) {} @@ -36,31 +38,33 @@ public: uint32_t block_size() const { return 1; } uint32_t block_count() const { return Blocks.size(); } + endianness getEndian() const override { return little; } + Error readBytes(uint32_t Offset, uint32_t Size, - ArrayRef<uint8_t> &Buffer) const override { - if (Offset + Size > Data.size()) - return make_error<MSFError>(msf_error_code::insufficient_buffer); + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffset(Offset, Size)) + return EC; Buffer = Data.slice(Offset, Size); return Error::success(); } Error readLongestContiguousChunk(uint32_t Offset, - ArrayRef<uint8_t> &Buffer) const override { - if (Offset >= Data.size()) - return make_error<MSFError>(msf_error_code::insufficient_buffer); + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffset(Offset, 1)) + return EC; Buffer = Data.drop_front(Offset); return Error::success(); } - uint32_t getLength() const override { return Data.size(); } + uint32_t getLength() override { return Data.size(); } - Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> SrcData) const override { - if (Offset + SrcData.size() > Data.size()) - return make_error<MSFError>(msf_error_code::insufficient_buffer); + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> SrcData) override { + if (auto EC = checkOffset(Offset, SrcData.size())) + return EC; ::memcpy(&Data[Offset], SrcData.data(), SrcData.size()); return Error::success(); } - Error commit() const override { return Error::success(); } + Error commit() override { return Error::success(); } MSFStreamLayout layout() const { return MSFStreamLayout{static_cast<uint32_t>(Data.size()), Blocks}; @@ -78,8 +82,8 @@ TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) { auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); - ReadableStreamRef SR; + BinaryStreamReader R(*S); + BinaryStreamRef SR; EXPECT_NO_ERROR(R.readStreamRef(SR, 0U)); ArrayRef<uint8_t> Buffer; EXPECT_ERROR(SR.readBytes(0U, 1U, Buffer)); @@ -94,7 +98,7 @@ TEST(MappedBlockStreamTest, ReadOntoNonEmptyBuffer) { auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA"; EXPECT_NO_ERROR(R.readFixedString(Str, 1)); EXPECT_EQ(Str, StringRef("A")); @@ -108,7 +112,7 @@ TEST(MappedBlockStreamTest, ZeroCopyReadContiguousBreak) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 2)); EXPECT_EQ(Str, StringRef("AB")); @@ -127,7 +131,7 @@ TEST(MappedBlockStreamTest, CopyReadNonContiguousBreak) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 10)); EXPECT_EQ(Str, StringRef("ABCDEFGHIJ")); @@ -140,7 +144,7 @@ TEST(MappedBlockStreamTest, InvalidReadSizeNoBreak) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str; R.setOffset(10); @@ -154,7 +158,7 @@ TEST(MappedBlockStreamTest, InvalidReadSizeContiguousBreak) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str; R.setOffset(6); @@ -168,7 +172,7 @@ TEST(MappedBlockStreamTest, InvalidReadSizeNonContiguousBreak) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str; EXPECT_ERROR(R.readFixedString(Str, 11)); @@ -181,7 +185,7 @@ TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 1)); EXPECT_EQ(Str, StringRef("A")); @@ -195,7 +199,7 @@ TEST(MappedBlockStreamTest, UnalignedOverlappingRead) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str1; StringRef Str2; EXPECT_NO_ERROR(R.readFixedString(Str1, 7)); @@ -216,7 +220,7 @@ TEST(MappedBlockStreamTest, UnalignedOverlappingReadFail) { DiscontiguousStream F(BlocksAry, DataAry); auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), F.layout(), F); - StreamReader R(*S); + BinaryStreamReader R(*S); StringRef Str1; StringRef Str2; EXPECT_NO_ERROR(R.readFixedString(Str1, 6)); @@ -323,8 +327,8 @@ TEST(MappedBlockStreamTest, TestWriteThenRead) { uint32_t intArr1[] = {890723408, 29082234}; ArrayRef<uint32_t> intArray[] = {intArr0, intArr1}; - StreamReader Reader(*S); - StreamWriter Writer(*S); + BinaryStreamReader Reader(*S); + BinaryStreamWriter Writer(*S); EXPECT_NO_ERROR(Writer.writeInteger(u16[0])); EXPECT_NO_ERROR(Reader.readInteger(u16[1])); EXPECT_EQ(u16[0], u16[1]); @@ -352,8 +356,8 @@ TEST(MappedBlockStreamTest, TestWriteThenRead) { Reader.setOffset(0); Writer.setOffset(0); ::memset(DataBytes.data(), 0, 10); - EXPECT_NO_ERROR(Writer.writeZeroString(ZStr[0])); - EXPECT_NO_ERROR(Reader.readZeroString(ZStr[1])); + EXPECT_NO_ERROR(Writer.writeCString(ZStr[0])); + EXPECT_NO_ERROR(Reader.readCString(ZStr[1])); EXPECT_EQ(ZStr[0], ZStr[1]); EXPECT_EQ( std::vector<uint8_t>({'r', 'e', 'Z', ' ', 'S', 't', 'o', 'r', 0, 0}), @@ -399,22 +403,22 @@ TEST(MappedBlockStreamTest, TestWriteContiguousStreamRef) { F.block_size(), F.block_count(), F.layout(), F); // First write "Test Str" into the source stream. - MutableByteStream SourceStream(SrcData); - StreamWriter SourceWriter(SourceStream); - EXPECT_NO_ERROR(SourceWriter.writeZeroString("Test Str")); + MutableBinaryByteStream SourceStream(SrcData, little); + BinaryStreamWriter SourceWriter(SourceStream); + EXPECT_NO_ERROR(SourceWriter.writeCString("Test Str")); EXPECT_EQ(SrcDataBytes, std::vector<uint8_t>( {'T', 'e', 's', 't', ' ', 'S', 't', 'r', 0, 0})); // Then write the source stream into the dest stream. - StreamWriter DestWriter(*DestStream); + BinaryStreamWriter DestWriter(*DestStream); EXPECT_NO_ERROR(DestWriter.writeStreamRef(SourceStream)); EXPECT_EQ(DestDataBytes, std::vector<uint8_t>( {'s', 'e', 'T', ' ', 'S', 't', 't', 'r', 0, 0})); // Then read the string back out of the dest stream. StringRef Result; - StreamReader DestReader(*DestStream); - EXPECT_NO_ERROR(DestReader.readZeroString(Result)); + BinaryStreamReader DestReader(*DestStream); + EXPECT_NO_ERROR(DestReader.readCString(Result)); EXPECT_EQ(Result, "Test Str"); } @@ -436,21 +440,21 @@ TEST(MappedBlockStreamTest, TestWriteDiscontiguousStreamRef) { SrcF.block_size(), SrcF.block_count(), SrcF.layout(), SrcF); // First write "Test Str" into the source stream. - StreamWriter SourceWriter(*Src); - EXPECT_NO_ERROR(SourceWriter.writeZeroString("Test Str")); + BinaryStreamWriter SourceWriter(*Src); + EXPECT_NO_ERROR(SourceWriter.writeCString("Test Str")); EXPECT_EQ(SrcDataBytes, std::vector<uint8_t>( {'e', 'T', 't', 't', ' ', 'S', 's', 'r', 0, 0})); // Then write the source stream into the dest stream. - StreamWriter DestWriter(*Dest); + BinaryStreamWriter DestWriter(*Dest); EXPECT_NO_ERROR(DestWriter.writeStreamRef(*Src)); EXPECT_EQ(DestDataBytes, std::vector<uint8_t>( {'s', 'e', 'T', ' ', 'S', 't', 't', 'r', 0, 0})); // Then read the string back out of the dest stream. StringRef Result; - StreamReader DestReader(*Dest); - EXPECT_NO_ERROR(DestReader.readZeroString(Result)); + BinaryStreamReader DestReader(*Dest); + EXPECT_NO_ERROR(DestReader.readCString(Result)); EXPECT_EQ(Result, "Test Str"); } diff --git a/unittests/DebugInfo/PDB/PDBApiTest.cpp b/unittests/DebugInfo/PDB/PDBApiTest.cpp index cd0f928a08ab5..6afe83cd90dd5 100644 --- a/unittests/DebugInfo/PDB/PDBApiTest.cpp +++ b/unittests/DebugInfo/PDB/PDBApiTest.cpp @@ -226,6 +226,7 @@ public: MOCK_SYMBOL_ACCESSOR(getMachineType) MOCK_SYMBOL_ACCESSOR(getThunkOrdinal) MOCK_SYMBOL_ACCESSOR(getLength) + MOCK_SYMBOL_ACCESSOR(getVirtualBaseTableType) MOCK_SYMBOL_ACCESSOR(getLiveRangeLength) MOCK_SYMBOL_ACCESSOR(getVirtualAddress) MOCK_SYMBOL_ACCESSOR(getUdtKind) diff --git a/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp b/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp new file mode 100644 index 0000000000000..7c4838778e43b --- /dev/null +++ b/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp @@ -0,0 +1,55 @@ +//===- StringTableBuilderTest.cpp -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ErrorChecking.h" + +#include "llvm/DebugInfo/PDB/Native/StringTable.h" +#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::support; + +namespace { +class StringTableBuilderTest : public ::testing::Test {}; +} + +TEST_F(StringTableBuilderTest, Simple) { + // Create /names table contents. + StringTableBuilder Builder; + EXPECT_EQ(1U, Builder.insert("foo")); + EXPECT_EQ(5U, Builder.insert("bar")); + EXPECT_EQ(1U, Builder.insert("foo")); + EXPECT_EQ(9U, Builder.insert("baz")); + + std::vector<uint8_t> Buffer(Builder.finalize()); + MutableBinaryByteStream OutStream(Buffer, little); + BinaryStreamWriter Writer(OutStream); + EXPECT_NO_ERROR(Builder.commit(Writer)); + + // Reads the contents back. + BinaryByteStream InStream(Buffer, little); + BinaryStreamReader Reader(InStream); + StringTable Table; + EXPECT_NO_ERROR(Table.load(Reader)); + + EXPECT_EQ(3U, Table.getNameCount()); + EXPECT_EQ(1U, Table.getHashVersion()); + EXPECT_EQ("foo", Table.getStringForID(1)); + EXPECT_EQ("bar", Table.getStringForID(5)); + EXPECT_EQ("baz", Table.getStringForID(9)); + EXPECT_EQ(1U, Table.getIDForString("foo")); + EXPECT_EQ(5U, Table.getIDForString("bar")); + EXPECT_EQ(9U, Table.getIDForString("baz")); +} diff --git a/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp b/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp new file mode 100644 index 0000000000000..6995e8f9dded2 --- /dev/null +++ b/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp @@ -0,0 +1,175 @@ +//===- llvm/unittest/DebugInfo/PDB/TypeServerHandlerTest.cpp --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ErrorChecking.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeSerializer.h" +#include "llvm/DebugInfo/CodeView/TypeServerHandler.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { + +constexpr uint8_t Guid[] = {0x2a, 0x2c, 0x1c, 0x2a, 0xcb, 0x9e, 0x48, 0x18, + 0x82, 0x82, 0x7a, 0x87, 0xc3, 0xfe, 0x16, 0xe8}; +StringRef GuidStr(reinterpret_cast<const char *>(Guid), + llvm::array_lengthof(Guid)); + +constexpr const char *Name = "Test Name"; +constexpr int Age = 1; + +class MockTypeServerHandler : public TypeServerHandler { +public: + explicit MockTypeServerHandler(bool HandleAlways) + : HandleAlways(HandleAlways) {} + + Expected<bool> handle(TypeServer2Record &TS, + TypeVisitorCallbacks &Callbacks) override { + if (TS.Age != Age || TS.Guid != GuidStr || TS.Name != Name) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Invalid TypeServer record!"); + + if (Handled && !HandleAlways) + return false; + + Handled = true; + return true; + } + + bool Handled = false; + bool HandleAlways; +}; + +class MockTypeVisitorCallbacks : public TypeVisitorCallbacks { +public: + enum class State { + Ready, + VisitTypeBegin, + VisitKnownRecord, + VisitTypeEnd, + }; + Error visitTypeBegin(CVType &CVT) override { + if (S != State::Ready) + return make_error<CodeViewError>(cv_error_code::unspecified, + "Invalid visitor state!"); + + S = State::VisitTypeBegin; + return Error::success(); + } + + Error visitKnownRecord(CVType &CVT, TypeServer2Record &TS) override { + if (S != State::VisitTypeBegin) + return make_error<CodeViewError>(cv_error_code::unspecified, + "Invalid visitor state!"); + + S = State::VisitKnownRecord; + return Error::success(); + } + + Error visitTypeEnd(CVType &CVT) override { + if (S != State::VisitKnownRecord) + return make_error<CodeViewError>(cv_error_code::unspecified, + "Invalid visitor state!"); + + S = State::VisitTypeEnd; + return Error::success(); + } + + State S = State::Ready; +}; + +class TypeServerHandlerTest : public testing::Test { +public: + void SetUp() override { + TypeServer2Record R(TypeRecordKind::TypeServer2); + R.Age = Age; + R.Guid = GuidStr; + R.Name = Name; + + TypeTableBuilder Builder(Allocator); + Builder.writeKnownType(R); + TypeServerRecord.RecordData = Builder.records().front(); + TypeServerRecord.Type = TypeLeafKind::LF_TYPESERVER2; + } + +protected: + BumpPtrAllocator Allocator; + CVType TypeServerRecord; +}; + +// Test that when no type server handler is registered, it gets handled by the +// normal +// visitor callbacks. +TEST_F(TypeServerHandlerTest, VisitRecordNoTypeServer) { + MockTypeVisitorCallbacks C2; + MockTypeVisitorCallbacks C1; + TypeVisitorCallbackPipeline Pipeline; + + Pipeline.addCallbackToPipeline(C1); + Pipeline.addCallbackToPipeline(C2); + CVTypeVisitor Visitor(Pipeline); + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + + EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C1.S); + EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C2.S); +} + +// Test that when a TypeServerHandler is registered, it gets consumed by the +// handler if and only if the handler returns true. +TEST_F(TypeServerHandlerTest, VisitRecordWithTypeServerOnce) { + MockTypeServerHandler Handler(false); + + MockTypeVisitorCallbacks C1; + CVTypeVisitor Visitor(C1); + Visitor.addTypeServerHandler(Handler); + + // Our mock server returns true the first time. + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S); + + // And false the second time. + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C1.S); +} + +// Test that when a type server handler is registered, if the handler keeps +// returning true, it will keep getting consumed by the handler and not go +// to the default processor. +TEST_F(TypeServerHandlerTest, VisitRecordWithTypeServerAlways) { + MockTypeServerHandler Handler(true); + + MockTypeVisitorCallbacks C1; + CVTypeVisitor Visitor(C1); + Visitor.addTypeServerHandler(Handler); + + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S); + + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S); +} + +} // end anonymous namespace diff --git a/unittests/ExecutionEngine/Orc/CMakeLists.txt b/unittests/ExecutionEngine/Orc/CMakeLists.txt index 68f6d0c28d7ca..db40c4213bd70 100644 --- a/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -14,11 +14,12 @@ add_llvm_unittest(OrcJITTests IndirectionUtilsTest.cpp GlobalMappingLayerTest.cpp LazyEmittingLayerTest.cpp - ObjectLinkingLayerTest.cpp ObjectTransformLayerTest.cpp OrcCAPITest.cpp OrcTestCommon.cpp + QueueChannel.cpp RPCUtilsTest.cpp + RTDyldObjectLinkingLayerTest.cpp ) -target_link_libraries(OrcJITTests ${PTHREAD_LIB}) +target_link_libraries(OrcJITTests ${LLVM_PTHREAD_LIB}) diff --git a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp index ac847039d9fb2..4af3aa707a90e 100644 --- a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp @@ -20,17 +20,17 @@ TEST(IndirectionUtilsTest, MakeStub) { LLVMContext Context; ModuleBuilder MB(Context, "x86_64-apple-macosx10.10", ""); Function *F = MB.createFunctionDecl<void(DummyStruct, DummyStruct)>(""); - SmallVector<AttributeSet, 4> Attrs; + SmallVector<AttributeList, 4> Attrs; Attrs.push_back( - AttributeSet::get(MB.getModule()->getContext(), 1U, - AttrBuilder().addAttribute(Attribute::StructRet))); + AttributeList::get(MB.getModule()->getContext(), 1U, + AttrBuilder().addAttribute(Attribute::StructRet))); Attrs.push_back( - AttributeSet::get(MB.getModule()->getContext(), 2U, - AttrBuilder().addAttribute(Attribute::ByVal))); + AttributeList::get(MB.getModule()->getContext(), 2U, + AttrBuilder().addAttribute(Attribute::ByVal))); Attrs.push_back( - AttributeSet::get(MB.getModule()->getContext(), ~0U, - AttrBuilder().addAttribute(Attribute::NoUnwind))); - F->setAttributes(AttributeSet::get(MB.getModule()->getContext(), Attrs)); + AttributeList::get(MB.getModule()->getContext(), ~0U, + AttrBuilder().addAttribute(Attribute::NoUnwind))); + F->setAttributes(AttributeList::get(MB.getModule()->getContext(), Attrs)); auto ImplPtr = orc::createImplPointer(*F->getType(), *MB.getModule(), "", nullptr); orc::makeStub(*F, *ImplPtr); @@ -42,7 +42,7 @@ TEST(IndirectionUtilsTest, MakeStub) { EXPECT_TRUE(Call->isTailCall()) << "Indirect call from stub should be tail call."; EXPECT_TRUE(Call->hasStructRetAttr()) << "makeStub should propagate sret attr on 1st argument."; - EXPECT_TRUE(Call->paramHasAttr(2U, Attribute::ByVal)) + EXPECT_TRUE(Call->paramHasAttr(1U, Attribute::ByVal)) << "makeStub should propagate byval attr on 2nd argument."; } diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp index 63b85dc82ca84..96214a368dce0 100644 --- a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp @@ -12,7 +12,7 @@ #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" #include "llvm/ExecutionEngine/Orc/NullResolver.h" -#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" #include "llvm/Object/ObjectFile.h" #include "gtest/gtest.h" @@ -309,7 +309,7 @@ TEST(ObjectTransformLayerTest, Main) { }; // Construct the jit layers. - ObjectLinkingLayer<> BaseLayer; + RTDyldObjectLinkingLayer<> BaseLayer; auto IdentityTransform = []( std::unique_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>> Obj) { return Obj; }; diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp b/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp index 17d1e9c9276e7..ccd2fc0fb1892 100644 --- a/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp +++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp @@ -15,7 +15,7 @@ using namespace llvm; -bool OrcExecutionTest::NativeTargetInitialized = false; +bool OrcNativeTarget::NativeTargetInitialized = false; ModuleBuilder::ModuleBuilder(LLVMContext &Context, StringRef Triple, StringRef Name) diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/unittests/ExecutionEngine/Orc/OrcTestCommon.h index f3972a3084e51..7fb26634c7a7a 100644 --- a/unittests/ExecutionEngine/Orc/OrcTestCommon.h +++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.h @@ -28,17 +28,29 @@ namespace llvm { -// Base class for Orc tests that will execute code. -class OrcExecutionTest { +class OrcNativeTarget { public: - - OrcExecutionTest() { + static void initialize() { if (!NativeTargetInitialized) { InitializeNativeTarget(); InitializeNativeTargetAsmParser(); InitializeNativeTargetAsmPrinter(); NativeTargetInitialized = true; } + } + +private: + static bool NativeTargetInitialized; +}; + +// Base class for Orc tests that will execute code. +class OrcExecutionTest { +public: + + OrcExecutionTest() { + + // Initialize the native target if it hasn't been done already. + OrcNativeTarget::initialize(); // Try to select a TargetMachine for the host. TM.reset(EngineBuilder().selectTarget()); @@ -56,8 +68,6 @@ public: protected: LLVMContext Context; std::unique_ptr<TargetMachine> TM; -private: - static bool NativeTargetInitialized; }; class ModuleBuilder { diff --git a/unittests/ExecutionEngine/Orc/QueueChannel.cpp b/unittests/ExecutionEngine/Orc/QueueChannel.cpp new file mode 100644 index 0000000000000..e309a7e428c04 --- /dev/null +++ b/unittests/ExecutionEngine/Orc/QueueChannel.cpp @@ -0,0 +1,14 @@ +//===-------- QueueChannel.cpp - Unit tests the remote executors ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "QueueChannel.h" + +char llvm::QueueChannelError::ID; +char llvm::QueueChannelClosedError::ID; + diff --git a/unittests/ExecutionEngine/Orc/QueueChannel.h b/unittests/ExecutionEngine/Orc/QueueChannel.h new file mode 100644 index 0000000000000..3d1058a83ebc1 --- /dev/null +++ b/unittests/ExecutionEngine/Orc/QueueChannel.h @@ -0,0 +1,146 @@ +//===----------------------- Queue.h - RPC Queue ------------------*-c++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_UNITTESTS_EXECUTIONENGINE_ORC_QUEUECHANNEL_H +#define LLVM_UNITTESTS_EXECUTIONENGINE_ORC_QUEUECHANNEL_H + +#include "llvm/ExecutionEngine/Orc/RawByteChannel.h" +#include "llvm/Support/Error.h" + +#include <queue> +#include <condition_variable> + +namespace llvm { + +class QueueChannelError : public ErrorInfo<QueueChannelError> { +public: + static char ID; +}; + +class QueueChannelClosedError + : public ErrorInfo<QueueChannelClosedError, QueueChannelError> { +public: + static char ID; + std::error_code convertToErrorCode() const override { + return inconvertibleErrorCode(); + } + + void log(raw_ostream &OS) const override { + OS << "Queue closed"; + } +}; + +class Queue : public std::queue<char> { +public: + using ErrorInjector = std::function<Error()>; + + Queue() + : ReadError([]() { return Error::success(); }), + WriteError([]() { return Error::success(); }) {} + + Queue(const Queue&) = delete; + Queue& operator=(const Queue&) = delete; + Queue(Queue&&) = delete; + Queue& operator=(Queue&&) = delete; + + std::mutex &getMutex() { return M; } + std::condition_variable &getCondVar() { return CV; } + Error checkReadError() { return ReadError(); } + Error checkWriteError() { return WriteError(); } + void setReadError(ErrorInjector NewReadError) { + { + std::lock_guard<std::mutex> Lock(M); + ReadError = std::move(NewReadError); + } + CV.notify_one(); + } + void setWriteError(ErrorInjector NewWriteError) { + std::lock_guard<std::mutex> Lock(M); + WriteError = std::move(NewWriteError); + } +private: + std::mutex M; + std::condition_variable CV; + std::function<Error()> ReadError, WriteError; +}; + +class QueueChannel : public orc::rpc::RawByteChannel { +public: + QueueChannel(std::shared_ptr<Queue> InQueue, + std::shared_ptr<Queue> OutQueue) + : InQueue(InQueue), OutQueue(OutQueue) {} + + QueueChannel(const QueueChannel&) = delete; + QueueChannel& operator=(const QueueChannel&) = delete; + QueueChannel(QueueChannel&&) = delete; + QueueChannel& operator=(QueueChannel&&) = delete; + + Error readBytes(char *Dst, unsigned Size) override { + std::unique_lock<std::mutex> Lock(InQueue->getMutex()); + while (Size) { + { + Error Err = InQueue->checkReadError(); + while (!Err && InQueue->empty()) { + InQueue->getCondVar().wait(Lock); + Err = InQueue->checkReadError(); + } + if (Err) + return Err; + } + *Dst++ = InQueue->front(); + --Size; + ++NumRead; + InQueue->pop(); + } + return Error::success(); + } + + Error appendBytes(const char *Src, unsigned Size) override { + std::unique_lock<std::mutex> Lock(OutQueue->getMutex()); + while (Size--) { + if (Error Err = OutQueue->checkWriteError()) + return Err; + OutQueue->push(*Src++); + ++NumWritten; + } + OutQueue->getCondVar().notify_one(); + return Error::success(); + } + + Error send() override { return Error::success(); } + + void close() { + auto ChannelClosed = []() { return make_error<QueueChannelClosedError>(); }; + InQueue->setReadError(ChannelClosed); + InQueue->setWriteError(ChannelClosed); + OutQueue->setReadError(ChannelClosed); + OutQueue->setWriteError(ChannelClosed); + } + + uint64_t NumWritten = 0; + uint64_t NumRead = 0; + +private: + + std::shared_ptr<Queue> InQueue; + std::shared_ptr<Queue> OutQueue; +}; + +inline std::pair<std::unique_ptr<QueueChannel>, std::unique_ptr<QueueChannel>> +createPairedQueueChannels() { + auto Q1 = std::make_shared<Queue>(); + auto Q2 = std::make_shared<Queue>(); + auto C1 = llvm::make_unique<QueueChannel>(Q1, Q2); + auto C2 = llvm::make_unique<QueueChannel>(Q2, Q1); + return std::make_pair(std::move(C1), std::move(C2)); +} + +} + +#endif diff --git a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp index 186c3d4084866..1c9764b555fd6 100644 --- a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ExecutionEngine/Orc/RawByteChannel.h" #include "llvm/ExecutionEngine/Orc/RPCUtils.h" +#include "QueueChannel.h" #include "gtest/gtest.h" #include <queue> @@ -17,47 +17,6 @@ using namespace llvm; using namespace llvm::orc; using namespace llvm::orc::rpc; -class Queue : public std::queue<char> { -public: - std::mutex &getMutex() { return M; } - std::condition_variable &getCondVar() { return CV; } -private: - std::mutex M; - std::condition_variable CV; -}; - -class QueueChannel : public RawByteChannel { -public: - QueueChannel(Queue &InQueue, Queue &OutQueue) - : InQueue(InQueue), OutQueue(OutQueue) {} - - Error readBytes(char *Dst, unsigned Size) override { - std::unique_lock<std::mutex> Lock(InQueue.getMutex()); - while (Size) { - while (InQueue.empty()) - InQueue.getCondVar().wait(Lock); - *Dst++ = InQueue.front(); - --Size; - InQueue.pop(); - } - return Error::success(); - } - - Error appendBytes(const char *Src, unsigned Size) override { - std::unique_lock<std::mutex> Lock(OutQueue.getMutex()); - while (Size--) - OutQueue.push(*Src++); - OutQueue.getCondVar().notify_one(); - return Error::success(); - } - - Error send() override { return Error::success(); } - -private: - Queue &InQueue; - Queue &OutQueue; -}; - class RPCFoo {}; namespace llvm { @@ -88,6 +47,54 @@ namespace rpc { class RPCBar {}; +class DummyError : public ErrorInfo<DummyError> { +public: + + static char ID; + + DummyError(uint32_t Val) : Val(Val) {} + + std::error_code convertToErrorCode() const override { + // Use a nonsense error code - we want to verify that errors + // transmitted over the network are replaced with + // OrcErrorCode::UnknownErrorCodeFromRemote. + return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist); + } + + void log(raw_ostream &OS) const override { + OS << "Dummy error " << Val; + } + + uint32_t getValue() const { return Val; } + +public: + uint32_t Val; +}; + +char DummyError::ID = 0; + +template <typename ChannelT> +void registerDummyErrorSerialization() { + static bool AlreadyRegistered = false; + if (!AlreadyRegistered) { + SerializationTraits<ChannelT, Error>:: + template registerErrorType<DummyError>( + "DummyError", + [](ChannelT &C, const DummyError &DE) { + return serializeSeq(C, DE.getValue()); + }, + [](ChannelT &C, Error &Err) -> Error { + ErrorAsOutParameter EAO(&Err); + uint32_t Val; + if (auto Err = deserializeSeq(C, Val)) + return Err; + Err = make_error<DummyError>(Val); + return Error::success(); + }); + AlreadyRegistered = true; + } +} + namespace llvm { namespace orc { namespace rpc { @@ -120,6 +127,11 @@ namespace DummyRPCAPI { static const char* getName() { return "IntInt"; } }; + class VoidString : public Function<VoidString, void(std::string)> { + public: + static const char* getName() { return "VoidString"; } + }; + class AllTheTypes : public Function<AllTheTypes, void(int8_t, uint8_t, int16_t, uint16_t, int32_t, @@ -134,21 +146,38 @@ namespace DummyRPCAPI { static const char* getName() { return "CustomType"; } }; + class ErrorFunc : public Function<ErrorFunc, Error()> { + public: + static const char* getName() { return "ErrorFunc"; } + }; + + class ExpectedFunc : public Function<ExpectedFunc, Expected<uint32_t>()> { + public: + static const char* getName() { return "ExpectedFunc"; } + }; + } class DummyRPCEndpoint : public SingleThreadedRPCEndpoint<QueueChannel> { public: - DummyRPCEndpoint(Queue &Q1, Queue &Q2) - : SingleThreadedRPCEndpoint(C, true), C(Q1, Q2) {} -private: - QueueChannel C; + DummyRPCEndpoint(QueueChannel &C) + : SingleThreadedRPCEndpoint(C, true) {} }; -TEST(DummyRPC, TestAsyncVoidBool) { - Queue Q1, Q2; - DummyRPCEndpoint Client(Q1, Q2); - DummyRPCEndpoint Server(Q2, Q1); +void freeVoidBool(bool B) { +} + +TEST(DummyRPC, TestFreeFunctionHandler) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Server(*Channels.first); + Server.addHandler<DummyRPCAPI::VoidBool>(freeVoidBool); +} + +TEST(DummyRPC, TestCallAsyncVoidBool) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); std::thread ServerThread([&]() { Server.addHandler<DummyRPCAPI::VoidBool>( @@ -189,10 +218,10 @@ TEST(DummyRPC, TestAsyncVoidBool) { ServerThread.join(); } -TEST(DummyRPC, TestAsyncIntInt) { - Queue Q1, Q2; - DummyRPCEndpoint Client(Q1, Q2); - DummyRPCEndpoint Server(Q2, Q1); +TEST(DummyRPC, TestCallAsyncIntInt) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); std::thread ServerThread([&]() { Server.addHandler<DummyRPCAPI::IntInt>( @@ -234,10 +263,147 @@ TEST(DummyRPC, TestAsyncIntInt) { ServerThread.join(); } +TEST(DummyRPC, TestAsyncIntIntHandler) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); + + std::thread ServerThread([&]() { + Server.addAsyncHandler<DummyRPCAPI::IntInt>( + [](std::function<Error(Expected<int32_t>)> SendResult, + int32_t X) { + EXPECT_EQ(X, 21) << "Server int(int) receieved unexpected result"; + return SendResult(2 * X); + }); + + { + // Poke the server to handle the negotiate call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; + } + + { + // Poke the server to handle the VoidBool call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)"; + } + }); + + { + auto Err = Client.callAsync<DummyRPCAPI::IntInt>( + [](Expected<int> Result) { + EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; + EXPECT_EQ(*Result, 42) + << "Async int(int) response handler received incorrect result"; + return Error::success(); + }, 21); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for int(int)"; + } + + { + // Poke the client to process the result. + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; + } + + ServerThread.join(); +} + +TEST(DummyRPC, TestAsyncIntIntHandlerMethod) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); + + class Dummy { + public: + Error handler(std::function<Error(Expected<int32_t>)> SendResult, + int32_t X) { + EXPECT_EQ(X, 21) << "Server int(int) receieved unexpected result"; + return SendResult(2 * X); + } + }; + + std::thread ServerThread([&]() { + Dummy D; + Server.addAsyncHandler<DummyRPCAPI::IntInt>(D, &Dummy::handler); + + { + // Poke the server to handle the negotiate call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; + } + + { + // Poke the server to handle the VoidBool call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)"; + } + }); + + { + auto Err = Client.callAsync<DummyRPCAPI::IntInt>( + [](Expected<int> Result) { + EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; + EXPECT_EQ(*Result, 42) + << "Async int(int) response handler received incorrect result"; + return Error::success(); + }, 21); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for int(int)"; + } + + { + // Poke the client to process the result. + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; + } + + ServerThread.join(); +} + +TEST(DummyRPC, TestCallAsyncVoidString) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::VoidString>( + [](const std::string &S) { + EXPECT_EQ(S, "hello") + << "Server void(std::string) received unexpected result"; + }); + + // Poke the server to handle the negotiate call. + for (int I = 0; I < 4; ++I) { + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call"; + } + }); + + { + // Make an call using a std::string. + auto Err = Client.callB<DummyRPCAPI::VoidString>(std::string("hello")); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(std::string)"; + } + + { + // Make an call using a std::string. + auto Err = Client.callB<DummyRPCAPI::VoidString>(StringRef("hello")); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(std::string)"; + } + + { + // Make an call using a std::string. + auto Err = Client.callB<DummyRPCAPI::VoidString>("hello"); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(string)"; + } + + ServerThread.join(); +} + TEST(DummyRPC, TestSerialization) { - Queue Q1, Q2; - DummyRPCEndpoint Client(Q1, Q2); - DummyRPCEndpoint Server(Q2, Q1); + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); std::thread ServerThread([&]() { Server.addHandler<DummyRPCAPI::AllTheTypes>( @@ -300,9 +466,9 @@ TEST(DummyRPC, TestSerialization) { } TEST(DummyRPC, TestCustomType) { - Queue Q1, Q2; - DummyRPCEndpoint Client(Q1, Q2); - DummyRPCEndpoint Server(Q2, Q1); + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); std::thread ServerThread([&]() { Server.addHandler<DummyRPCAPI::CustomType>( @@ -343,9 +509,9 @@ TEST(DummyRPC, TestCustomType) { } TEST(DummyRPC, TestWithAltCustomType) { - Queue Q1, Q2; - DummyRPCEndpoint Client(Q1, Q2); - DummyRPCEndpoint Server(Q2, Q1); + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); std::thread ServerThread([&]() { Server.addHandler<DummyRPCAPI::CustomType>( @@ -385,10 +551,144 @@ TEST(DummyRPC, TestWithAltCustomType) { ServerThread.join(); } +TEST(DummyRPC, ReturnErrorSuccess) { + registerDummyErrorSerialization<QueueChannel>(); + + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::ErrorFunc>( + []() { + return Error::success(); + }); + + // Handle the negotiate plus one call. + for (unsigned I = 0; I != 2; ++I) + cantFail(Server.handleOne()); + }); + + cantFail(Client.callAsync<DummyRPCAPI::ErrorFunc>( + [&](Error Err) { + EXPECT_FALSE(!!Err) << "Expected success value"; + return Error::success(); + })); + + cantFail(Client.handleOne()); + + ServerThread.join(); +} + +TEST(DummyRPC, ReturnErrorFailure) { + registerDummyErrorSerialization<QueueChannel>(); + + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::ErrorFunc>( + []() { + return make_error<DummyError>(42); + }); + + // Handle the negotiate plus one call. + for (unsigned I = 0; I != 2; ++I) + cantFail(Server.handleOne()); + }); + + cantFail(Client.callAsync<DummyRPCAPI::ErrorFunc>( + [&](Error Err) { + EXPECT_TRUE(Err.isA<DummyError>()) + << "Incorrect error type"; + return handleErrors( + std::move(Err), + [](const DummyError &DE) { + EXPECT_EQ(DE.getValue(), 42ULL) + << "Incorrect DummyError serialization"; + }); + })); + + cantFail(Client.handleOne()); + + ServerThread.join(); +} + +TEST(DummyRPC, ReturnExpectedSuccess) { + registerDummyErrorSerialization<QueueChannel>(); + + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::ExpectedFunc>( + []() -> uint32_t { + return 42; + }); + + // Handle the negotiate plus one call. + for (unsigned I = 0; I != 2; ++I) + cantFail(Server.handleOne()); + }); + + cantFail(Client.callAsync<DummyRPCAPI::ExpectedFunc>( + [&](Expected<uint32_t> ValOrErr) { + EXPECT_TRUE(!!ValOrErr) + << "Expected success value"; + EXPECT_EQ(*ValOrErr, 42ULL) + << "Incorrect Expected<uint32_t> deserialization"; + return Error::success(); + })); + + cantFail(Client.handleOne()); + + ServerThread.join(); +} + +TEST(DummyRPC, ReturnExpectedFailure) { + registerDummyErrorSerialization<QueueChannel>(); + + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::ExpectedFunc>( + []() -> Expected<uint32_t> { + return make_error<DummyError>(7); + }); + + // Handle the negotiate plus one call. + for (unsigned I = 0; I != 2; ++I) + cantFail(Server.handleOne()); + }); + + cantFail(Client.callAsync<DummyRPCAPI::ExpectedFunc>( + [&](Expected<uint32_t> ValOrErr) { + EXPECT_FALSE(!!ValOrErr) + << "Expected failure value"; + auto Err = ValOrErr.takeError(); + EXPECT_TRUE(Err.isA<DummyError>()) + << "Incorrect error type"; + return handleErrors( + std::move(Err), + [](const DummyError &DE) { + EXPECT_EQ(DE.getValue(), 7ULL) + << "Incorrect DummyError serialization"; + }); + })); + + cantFail(Client.handleOne()); + + ServerThread.join(); +} + TEST(DummyRPC, TestParallelCallGroup) { - Queue Q1, Q2; - DummyRPCEndpoint Client(Q1, Q2); - DummyRPCEndpoint Server(Q2, Q1); + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); std::thread ServerThread([&]() { Server.addHandler<DummyRPCAPI::IntInt>( @@ -405,10 +705,11 @@ TEST(DummyRPC, TestParallelCallGroup) { { int A, B, C; - ParallelCallGroup<DummyRPCEndpoint> PCG(Client); + ParallelCallGroup PCG; { - auto Err = PCG.appendCall<DummyRPCAPI::IntInt>( + auto Err = PCG.call( + rpcAsyncDispatch<DummyRPCAPI::IntInt>(Client), [&A](Expected<int> Result) { EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; A = *Result; @@ -418,7 +719,8 @@ TEST(DummyRPC, TestParallelCallGroup) { } { - auto Err = PCG.appendCall<DummyRPCAPI::IntInt>( + auto Err = PCG.call( + rpcAsyncDispatch<DummyRPCAPI::IntInt>(Client), [&B](Expected<int> Result) { EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; B = *Result; @@ -428,7 +730,8 @@ TEST(DummyRPC, TestParallelCallGroup) { } { - auto Err = PCG.appendCall<DummyRPCAPI::IntInt>( + auto Err = PCG.call( + rpcAsyncDispatch<DummyRPCAPI::IntInt>(Client), [&C](Expected<int> Result) { EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; C = *Result; @@ -443,10 +746,7 @@ TEST(DummyRPC, TestParallelCallGroup) { EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; } - { - auto Err = PCG.wait(); - EXPECT_FALSE(!!Err) << "Third parallel call failed for int(int)"; - } + PCG.wait(); EXPECT_EQ(A, 2) << "First parallel call returned bogus result"; EXPECT_EQ(B, 4) << "Second parallel call returned bogus result"; @@ -468,9 +768,9 @@ TEST(DummyRPC, TestAPICalls) { static_assert(!DummyCalls1::Contains<DummyRPCAPI::CustomType>::value, "Contains<Func> template should return false here"); - Queue Q1, Q2; - DummyRPCEndpoint Client(Q1, Q2); - DummyRPCEndpoint Server(Q2, Q1); + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Client(*Channels.first); + DummyRPCEndpoint Server(*Channels.second); std::thread ServerThread( [&]() { @@ -496,11 +796,37 @@ TEST(DummyRPC, TestAPICalls) { { auto Err = DummyCallsAll::negotiate(Client); - EXPECT_EQ(errorToErrorCode(std::move(Err)).value(), - static_cast<int>(OrcErrorCode::UnknownRPCFunction)) - << "Expected 'UnknownRPCFunction' error for attempted negotiate of " + EXPECT_TRUE(Err.isA<CouldNotNegotiate>()) + << "Expected CouldNotNegotiate error for attempted negotiate of " "unsupported function"; + consumeError(std::move(Err)); } ServerThread.join(); } + +TEST(DummyRPC, TestRemoveHandler) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Server(*Channels.second); + + Server.addHandler<DummyRPCAPI::VoidBool>( + [](bool B) { + EXPECT_EQ(B, true) + << "Server void(bool) received unexpected result"; + }); + + Server.removeHandler<DummyRPCAPI::VoidBool>(); +} + +TEST(DummyRPC, TestClearHandlers) { + auto Channels = createPairedQueueChannels(); + DummyRPCEndpoint Server(*Channels.second); + + Server.addHandler<DummyRPCAPI::VoidBool>( + [](bool B) { + EXPECT_EQ(B, true) + << "Server void(bool) received unexpected result"; + }); + + Server.clearHandlers(); +} diff --git a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp b/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp index 44b44f6041590..de99c022fb9dc 100644 --- a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp @@ -1,4 +1,4 @@ -//===-- ObjectLinkingLayerTest.cpp - Unit tests for object linking layer --===// +//===- RTDyldObjectLinkingLayerTest.cpp - RTDyld linking layer unit tests -===// // // The LLVM Compiler Infrastructure // @@ -13,7 +13,7 @@ #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/LambdaResolver.h" #include "llvm/ExecutionEngine/Orc/NullResolver.h" -#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/IR/Constants.h" #include "llvm/IR/LLVMContext.h" #include "gtest/gtest.h" @@ -23,8 +23,8 @@ using namespace llvm::orc; namespace { -class ObjectLinkingLayerExecutionTest : public testing::Test, - public OrcExecutionTest { +class RTDyldObjectLinkingLayerExecutionTest : public testing::Test, + public OrcExecutionTest { }; @@ -44,7 +44,7 @@ public: } }; -TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { +TEST(RTDyldObjectLinkingLayerTest, TestSetProcessAllSections) { class SectionMemoryManagerWrapper : public SectionMemoryManager { public: SectionMemoryManagerWrapper(bool &DebugSeen) : DebugSeen(DebugSeen) {} @@ -60,10 +60,10 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { IsReadOnly); } private: - bool DebugSeen; + bool &DebugSeen; }; - ObjectLinkingLayer<> ObjLayer; + RTDyldObjectLinkingLayer<> ObjLayer; LLVMContext Context; auto M = llvm::make_unique<Module>("", Context); @@ -75,6 +75,10 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { GV->setSection(".debug_str"); + + // Initialize the native target in case this is the first unit test + // to try to build a TM. + OrcNativeTarget::initialize(); std::unique_ptr<TargetMachine> TM( EngineBuilder().selectTarget(Triple(M->getTargetTriple()), "", "", SmallVector<std::string, 1>())); @@ -99,6 +103,7 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { { // Test with ProcessAllSections = false (the default). auto H = ObjLayer.addObjectSet(Objs, &SMMW, &*Resolver); + ObjLayer.emitAndFinalize(H); EXPECT_EQ(DebugSectionSeen, false) << "Unexpected debug info section"; ObjLayer.removeObjectSet(H); @@ -108,17 +113,18 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { // Test with ProcessAllSections = true. ObjLayer.setProcessAllSections(true); auto H = ObjLayer.addObjectSet(Objs, &SMMW, &*Resolver); + ObjLayer.emitAndFinalize(H); EXPECT_EQ(DebugSectionSeen, true) << "Expected debug info section not seen"; ObjLayer.removeObjectSet(H); } } -TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { +TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { if (!TM) return; - ObjectLinkingLayer<> ObjLayer; + RTDyldObjectLinkingLayer<> ObjLayer; SimpleCompiler Compile(*TM); // Create a pair of modules that will trigger recursive finalization: @@ -183,11 +189,11 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { << "Extra call to finalize"; } -TEST_F(ObjectLinkingLayerExecutionTest, NoPrematureAllocation) { +TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) { if (!TM) return; - ObjectLinkingLayer<> ObjLayer; + RTDyldObjectLinkingLayer<> ObjLayer; SimpleCompiler Compile(*TM); // Create a pair of unrelated modules: diff --git a/unittests/IR/AttributesTest.cpp b/unittests/IR/AttributesTest.cpp index 9f8013ff181cd..b5b221c63a173 100644 --- a/unittests/IR/AttributesTest.cpp +++ b/unittests/IR/AttributesTest.cpp @@ -21,13 +21,11 @@ TEST(Attributes, Uniquing) { Attribute AttrB = Attribute::get(C, Attribute::AlwaysInline); EXPECT_EQ(AttrA, AttrB); - AttributeSet ASs[] = { - AttributeSet::get(C, 1, Attribute::ZExt), - AttributeSet::get(C, 2, Attribute::SExt) - }; + AttributeList ASs[] = {AttributeList::get(C, 1, Attribute::ZExt), + AttributeList::get(C, 2, Attribute::SExt)}; - AttributeSet SetA = AttributeSet::get(C, ASs); - AttributeSet SetB = AttributeSet::get(C, ASs); + AttributeList SetA = AttributeList::get(C, ASs); + AttributeList SetB = AttributeList::get(C, ASs); EXPECT_EQ(SetA, SetB); } @@ -43,13 +41,11 @@ TEST(Attributes, Ordering) { EXPECT_TRUE(Align4 < Deref5); EXPECT_TRUE(Align5 < Deref4); - AttributeSet ASs[] = { - AttributeSet::get(C, 2, Attribute::ZExt), - AttributeSet::get(C, 1, Attribute::SExt) - }; + AttributeList ASs[] = {AttributeList::get(C, 2, Attribute::ZExt), + AttributeList::get(C, 1, Attribute::SExt)}; - AttributeSet SetA = AttributeSet::get(C, ASs); - AttributeSet SetB = SetA.removeAttributes(C, 1, ASs[1]); + AttributeList SetA = AttributeList::get(C, ASs); + AttributeList SetB = SetA.removeAttributes(C, 1, ASs[1]); EXPECT_NE(SetA, SetB); } diff --git a/unittests/IR/FunctionTest.cpp b/unittests/IR/FunctionTest.cpp index fb458597c37a4..6838d7e2527ff 100644 --- a/unittests/IR/FunctionTest.cpp +++ b/unittests/IR/FunctionTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" #include "gtest/gtest.h" using namespace llvm; @@ -109,4 +110,24 @@ TEST(FunctionTest, stealArgumentListFrom) { EXPECT_TRUE(F2->hasLazyArguments()); } +// Test setting and removing section information +TEST(FunctionTest, setSection) { + LLVMContext C; + Module M("test", C); + + llvm::Function *F = + Function::Create(llvm::FunctionType::get(llvm::Type::getVoidTy(C), false), + llvm::GlobalValue::ExternalLinkage, "F", &M); + + F->setSection(".text.test"); + EXPECT_TRUE(F->getSection() == ".text.test"); + EXPECT_TRUE(F->hasSection()); + F->setSection(""); + EXPECT_FALSE(F->hasSection()); + F->setSection(".text.test"); + F->setSection(".text.test2"); + EXPECT_TRUE(F->getSection() == ".text.test2"); + EXPECT_TRUE(F->hasSection()); +} + } // end namespace diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp index 1812cd39d135c..830ae9587691c 100644 --- a/unittests/IR/IRBuilderTest.cpp +++ b/unittests/IR/IRBuilderTest.cpp @@ -207,7 +207,26 @@ TEST_F(IRBuilderTest, FastMathFlags) { EXPECT_TRUE(FCmp->hasAllowReciprocal()); Builder.clearFastMathFlags(); - + + // Test FP-contract + FC = Builder.CreateFAdd(F, F); + ASSERT_TRUE(isa<Instruction>(FC)); + FAdd = cast<Instruction>(FC); + EXPECT_FALSE(FAdd->hasAllowContract()); + + FMF.clear(); + FMF.setAllowContract(true); + Builder.setFastMathFlags(FMF); + + FC = Builder.CreateFAdd(F, F); + EXPECT_TRUE(Builder.getFastMathFlags().any()); + EXPECT_TRUE(Builder.getFastMathFlags().AllowContract); + ASSERT_TRUE(isa<Instruction>(FC)); + FAdd = cast<Instruction>(FC); + EXPECT_TRUE(FAdd->hasAllowContract()); + + Builder.clearFastMathFlags(); + // Test a call with FMF. auto CalleeTy = FunctionType::get(Type::getFloatTy(Ctx), /*isVarArg=*/false); @@ -245,6 +264,7 @@ TEST_F(IRBuilderTest, FastMathFlags) { EXPECT_FALSE(FDiv->getFastMathFlags().any()); FDiv->setHasAllowReciprocal(true); FAdd->setHasAllowReciprocal(false); + FAdd->setHasNoNaNs(true); FDiv->copyFastMathFlags(FAdd); EXPECT_TRUE(FDiv->hasNoNaNs()); EXPECT_FALSE(FDiv->hasAllowReciprocal()); diff --git a/unittests/IR/InstructionsTest.cpp b/unittests/IR/InstructionsTest.cpp index 0dac7c1bcfb11..7c75aaec17539 100644 --- a/unittests/IR/InstructionsTest.cpp +++ b/unittests/IR/InstructionsTest.cpp @@ -19,6 +19,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Module.h" +#include "llvm/IR/NoFolder.h" #include "llvm/IR/Operator.h" #include "gtest/gtest.h" #include <memory> @@ -516,7 +517,8 @@ TEST(InstructionsTest, CloneCall) { { AttrBuilder AB; AB.addAttribute(Attribute::ReadOnly); - Call->setAttributes(AttributeSet::get(C, AttributeSet::FunctionIndex, AB)); + Call->setAttributes( + AttributeList::get(C, AttributeList::FunctionIndex, AB)); std::unique_ptr<CallInst> Clone(cast<CallInst>(Call->clone())); EXPECT_TRUE(Clone->onlyReadsMemory()); } @@ -534,7 +536,7 @@ TEST(InstructionsTest, AlterCallBundles) { Call->setTailCallKind(CallInst::TailCallKind::TCK_NoTail); AttrBuilder AB; AB.addAttribute(Attribute::Cold); - Call->setAttributes(AttributeSet::get(C, AttributeSet::FunctionIndex, AB)); + Call->setAttributes(AttributeList::get(C, AttributeList::FunctionIndex, AB)); Call->setDebugLoc(DebugLoc(MDNode::get(C, None))); OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7)); @@ -562,7 +564,8 @@ TEST(InstructionsTest, AlterInvokeBundles) { Callee, NormalDest.get(), UnwindDest.get(), Args, OldBundle, "result")); AttrBuilder AB; AB.addAttribute(Attribute::Cold); - Invoke->setAttributes(AttributeSet::get(C, AttributeSet::FunctionIndex, AB)); + Invoke->setAttributes( + AttributeList::get(C, AttributeList::FunctionIndex, AB)); Invoke->setDebugLoc(DebugLoc(MDNode::get(C, None))); OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7)); @@ -579,5 +582,163 @@ TEST(InstructionsTest, AlterInvokeBundles) { EXPECT_TRUE(Clone->getOperandBundle("after").hasValue()); } +TEST_F(ModuleWithFunctionTest, DropPoisonGeneratingFlags) { + auto *OnlyBB = BasicBlock::Create(Ctx, "bb", F); + auto *Arg0 = &*F->arg_begin(); + + IRBuilder<NoFolder> B(Ctx); + B.SetInsertPoint(OnlyBB); + + { + auto *UI = + cast<Instruction>(B.CreateUDiv(Arg0, Arg0, "", /*isExact*/ true)); + ASSERT_TRUE(UI->isExact()); + UI->dropPoisonGeneratingFlags(); + ASSERT_FALSE(UI->isExact()); + } + + { + auto *ShrI = + cast<Instruction>(B.CreateLShr(Arg0, Arg0, "", /*isExact*/ true)); + ASSERT_TRUE(ShrI->isExact()); + ShrI->dropPoisonGeneratingFlags(); + ASSERT_FALSE(ShrI->isExact()); + } + + { + auto *AI = cast<Instruction>( + B.CreateAdd(Arg0, Arg0, "", /*HasNUW*/ true, /*HasNSW*/ false)); + ASSERT_TRUE(AI->hasNoUnsignedWrap()); + AI->dropPoisonGeneratingFlags(); + ASSERT_FALSE(AI->hasNoUnsignedWrap()); + ASSERT_FALSE(AI->hasNoSignedWrap()); + } + + { + auto *SI = cast<Instruction>( + B.CreateAdd(Arg0, Arg0, "", /*HasNUW*/ false, /*HasNSW*/ true)); + ASSERT_TRUE(SI->hasNoSignedWrap()); + SI->dropPoisonGeneratingFlags(); + ASSERT_FALSE(SI->hasNoUnsignedWrap()); + ASSERT_FALSE(SI->hasNoSignedWrap()); + } + + { + auto *ShlI = cast<Instruction>( + B.CreateShl(Arg0, Arg0, "", /*HasNUW*/ true, /*HasNSW*/ true)); + ASSERT_TRUE(ShlI->hasNoSignedWrap()); + ASSERT_TRUE(ShlI->hasNoUnsignedWrap()); + ShlI->dropPoisonGeneratingFlags(); + ASSERT_FALSE(ShlI->hasNoUnsignedWrap()); + ASSERT_FALSE(ShlI->hasNoSignedWrap()); + } + + { + Value *GEPBase = Constant::getNullValue(B.getInt8PtrTy()); + auto *GI = cast<GetElementPtrInst>(B.CreateInBoundsGEP(GEPBase, {Arg0})); + ASSERT_TRUE(GI->isInBounds()); + GI->dropPoisonGeneratingFlags(); + ASSERT_FALSE(GI->isInBounds()); + } +} + +TEST(InstructionsTest, GEPIndices) { + LLVMContext Context; + IRBuilder<NoFolder> Builder(Context); + Type *ElementTy = Builder.getInt8Ty(); + Type *ArrTy = ArrayType::get(ArrayType::get(ElementTy, 64), 64); + Value *Indices[] = { + Builder.getInt32(0), + Builder.getInt32(13), + Builder.getInt32(42) }; + + Value *V = Builder.CreateGEP(ArrTy, UndefValue::get(PointerType::getUnqual(ArrTy)), + Indices); + ASSERT_TRUE(isa<GetElementPtrInst>(V)); + + auto *GEPI = cast<GetElementPtrInst>(V); + ASSERT_NE(GEPI->idx_begin(), GEPI->idx_end()); + ASSERT_EQ(GEPI->idx_end(), std::next(GEPI->idx_begin(), 3)); + EXPECT_EQ(Indices[0], GEPI->idx_begin()[0]); + EXPECT_EQ(Indices[1], GEPI->idx_begin()[1]); + EXPECT_EQ(Indices[2], GEPI->idx_begin()[2]); + EXPECT_EQ(GEPI->idx_begin(), GEPI->indices().begin()); + EXPECT_EQ(GEPI->idx_end(), GEPI->indices().end()); + + const auto *CGEPI = GEPI; + ASSERT_NE(CGEPI->idx_begin(), CGEPI->idx_end()); + ASSERT_EQ(CGEPI->idx_end(), std::next(CGEPI->idx_begin(), 3)); + EXPECT_EQ(Indices[0], CGEPI->idx_begin()[0]); + EXPECT_EQ(Indices[1], CGEPI->idx_begin()[1]); + EXPECT_EQ(Indices[2], CGEPI->idx_begin()[2]); + EXPECT_EQ(CGEPI->idx_begin(), CGEPI->indices().begin()); + EXPECT_EQ(CGEPI->idx_end(), CGEPI->indices().end()); + + delete GEPI; +} + +TEST(InstructionsTest, SwitchInst) { + LLVMContext C; + + std::unique_ptr<BasicBlock> BB1, BB2, BB3; + BB1.reset(BasicBlock::Create(C)); + BB2.reset(BasicBlock::Create(C)); + BB3.reset(BasicBlock::Create(C)); + + // We create block 0 after the others so that it gets destroyed first and + // clears the uses of the other basic blocks. + std::unique_ptr<BasicBlock> BB0(BasicBlock::Create(C)); + + auto *Int32Ty = Type::getInt32Ty(C); + + SwitchInst *SI = + SwitchInst::Create(UndefValue::get(Int32Ty), BB0.get(), 3, BB0.get()); + SI->addCase(ConstantInt::get(Int32Ty, 1), BB1.get()); + SI->addCase(ConstantInt::get(Int32Ty, 2), BB2.get()); + SI->addCase(ConstantInt::get(Int32Ty, 3), BB3.get()); + + auto CI = SI->case_begin(); + ASSERT_NE(CI, SI->case_end()); + EXPECT_EQ(1, CI->getCaseValue()->getSExtValue()); + EXPECT_EQ(BB1.get(), CI->getCaseSuccessor()); + EXPECT_EQ(2, (CI + 1)->getCaseValue()->getSExtValue()); + EXPECT_EQ(BB2.get(), (CI + 1)->getCaseSuccessor()); + EXPECT_EQ(3, (CI + 2)->getCaseValue()->getSExtValue()); + EXPECT_EQ(BB3.get(), (CI + 2)->getCaseSuccessor()); + EXPECT_EQ(CI + 1, std::next(CI)); + EXPECT_EQ(CI + 2, std::next(CI, 2)); + EXPECT_EQ(CI + 3, std::next(CI, 3)); + EXPECT_EQ(SI->case_end(), CI + 3); + EXPECT_EQ(0, CI - CI); + EXPECT_EQ(1, (CI + 1) - CI); + EXPECT_EQ(2, (CI + 2) - CI); + EXPECT_EQ(3, SI->case_end() - CI); + EXPECT_EQ(3, std::distance(CI, SI->case_end())); + + auto CCI = const_cast<const SwitchInst *>(SI)->case_begin(); + SwitchInst::ConstCaseIt CCE = SI->case_end(); + ASSERT_NE(CCI, SI->case_end()); + EXPECT_EQ(1, CCI->getCaseValue()->getSExtValue()); + EXPECT_EQ(BB1.get(), CCI->getCaseSuccessor()); + EXPECT_EQ(2, (CCI + 1)->getCaseValue()->getSExtValue()); + EXPECT_EQ(BB2.get(), (CCI + 1)->getCaseSuccessor()); + EXPECT_EQ(3, (CCI + 2)->getCaseValue()->getSExtValue()); + EXPECT_EQ(BB3.get(), (CCI + 2)->getCaseSuccessor()); + EXPECT_EQ(CCI + 1, std::next(CCI)); + EXPECT_EQ(CCI + 2, std::next(CCI, 2)); + EXPECT_EQ(CCI + 3, std::next(CCI, 3)); + EXPECT_EQ(CCE, CCI + 3); + EXPECT_EQ(0, CCI - CCI); + EXPECT_EQ(1, (CCI + 1) - CCI); + EXPECT_EQ(2, (CCI + 2) - CCI); + EXPECT_EQ(3, CCE - CCI); + EXPECT_EQ(3, std::distance(CCI, CCE)); + + // Make sure that the const iterator is compatible with a const auto ref. + const auto &Handle = *CCI; + EXPECT_EQ(1, Handle.getCaseValue()->getSExtValue()); + EXPECT_EQ(BB1.get(), Handle.getCaseSuccessor()); +} + } // end anonymous namespace } // end namespace llvm diff --git a/unittests/IR/LegacyPassManagerTest.cpp b/unittests/IR/LegacyPassManagerTest.cpp index 9dceb976c9375..0f67d3fb5ac9e 100644 --- a/unittests/IR/LegacyPassManagerTest.cpp +++ b/unittests/IR/LegacyPassManagerTest.cpp @@ -429,7 +429,7 @@ namespace llvm { /*Linkage=*/GlobalValue::ExternalLinkage, /*Name=*/"test1", mod); func_test1->setCallingConv(CallingConv::C); - AttributeSet func_test1_PAL; + AttributeList func_test1_PAL; func_test1->setAttributes(func_test1_PAL); Function* func_test2 = Function::Create( @@ -437,7 +437,7 @@ namespace llvm { /*Linkage=*/GlobalValue::ExternalLinkage, /*Name=*/"test2", mod); func_test2->setCallingConv(CallingConv::C); - AttributeSet func_test2_PAL; + AttributeList func_test2_PAL; func_test2->setAttributes(func_test2_PAL); Function* func_test3 = Function::Create( @@ -445,7 +445,7 @@ namespace llvm { /*Linkage=*/GlobalValue::ExternalLinkage, /*Name=*/"test3", mod); func_test3->setCallingConv(CallingConv::C); - AttributeSet func_test3_PAL; + AttributeList func_test3_PAL; func_test3->setAttributes(func_test3_PAL); Function* func_test4 = Function::Create( @@ -453,7 +453,7 @@ namespace llvm { /*Linkage=*/GlobalValue::ExternalLinkage, /*Name=*/"test4", mod); func_test4->setCallingConv(CallingConv::C); - AttributeSet func_test4_PAL; + AttributeList func_test4_PAL; func_test4->setAttributes(func_test4_PAL); // Global Variable Declarations @@ -474,7 +474,8 @@ namespace llvm { // Block entry (label_entry) CallInst* int32_3 = CallInst::Create(func_test2, "", label_entry); int32_3->setCallingConv(CallingConv::C); - int32_3->setTailCall(false);AttributeSet int32_3_PAL; + int32_3->setTailCall(false); + AttributeList int32_3_PAL; int32_3->setAttributes(int32_3_PAL); ReturnInst::Create(Context, int32_3, label_entry); @@ -489,7 +490,8 @@ namespace llvm { // Block entry (label_entry_5) CallInst* int32_6 = CallInst::Create(func_test3, "", label_entry_5); int32_6->setCallingConv(CallingConv::C); - int32_6->setTailCall(false);AttributeSet int32_6_PAL; + int32_6->setTailCall(false); + AttributeList int32_6_PAL; int32_6->setAttributes(int32_6_PAL); ReturnInst::Create(Context, int32_6, label_entry_5); @@ -504,7 +506,8 @@ namespace llvm { // Block entry (label_entry_8) CallInst* int32_9 = CallInst::Create(func_test1, "", label_entry_8); int32_9->setCallingConv(CallingConv::C); - int32_9->setTailCall(false);AttributeSet int32_9_PAL; + int32_9->setTailCall(false); + AttributeList int32_9_PAL; int32_9->setAttributes(int32_9_PAL); ReturnInst::Create(Context, int32_9, label_entry_8); diff --git a/unittests/IR/MetadataTest.cpp b/unittests/IR/MetadataTest.cpp index 7bb8d4010d38a..103ba4c92ddf1 100644 --- a/unittests/IR/MetadataTest.cpp +++ b/unittests/IR/MetadataTest.cpp @@ -95,7 +95,7 @@ protected: return DICompileUnit::getDistinct(Context, 1, getFile(), "clang", false, "-g", 2, "", DICompileUnit::FullDebug, getTuple(), getTuple(), getTuple(), - getTuple(), getTuple(), 0, true); + getTuple(), getTuple(), 0, true, false); } DIType *getBasicType(StringRef Name) { return DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type, Name); @@ -103,7 +103,7 @@ protected: DIType *getDerivedType() { return DIDerivedType::getDistinct( Context, dwarf::DW_TAG_pointer_type, "", nullptr, 0, nullptr, - getBasicType("basictype"), 1, 2, 0, DINode::FlagZero); + getBasicType("basictype"), 1, 2, 0, None, DINode::FlagZero); } Constant *getConstant() { return ConstantInt::get(Type::getInt32Ty(Context), Counter++); @@ -1053,12 +1053,14 @@ TEST_F(DIDerivedTypeTest, get) { DIScope *Scope = getSubprogram(); DIType *BaseType = getBasicType("basic"); MDTuple *ExtraData = getTuple(); + unsigned DWARFAddressSpace = 8; DINode::DIFlags Flags5 = static_cast<DINode::DIFlags>(5); DINode::DIFlags Flags4 = static_cast<DINode::DIFlags>(4); auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, - 1, Scope, BaseType, 2, 3, 4, Flags5, ExtraData); + 1, Scope, BaseType, 2, 3, 4, DWARFAddressSpace, Flags5, + ExtraData); EXPECT_EQ(dwarf::DW_TAG_pointer_type, N->getTag()); EXPECT_EQ("something", N->getName()); EXPECT_EQ(File, N->getFile()); @@ -1068,45 +1070,51 @@ TEST_F(DIDerivedTypeTest, get) { EXPECT_EQ(2u, N->getSizeInBits()); EXPECT_EQ(3u, N->getAlignInBits()); EXPECT_EQ(4u, N->getOffsetInBits()); + EXPECT_EQ(DWARFAddressSpace, N->getDWARFAddressSpace().getValue()); EXPECT_EQ(5u, N->getFlags()); EXPECT_EQ(ExtraData, N->getExtraData()); EXPECT_EQ(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, Flags5, ExtraData)); + 4, DWARFAddressSpace, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_reference_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, Flags5, ExtraData)); + 4, DWARFAddressSpace, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "else", - File, 1, Scope, BaseType, 2, 3, 4, Flags5, - ExtraData)); + File, 1, Scope, BaseType, 2, 3, + 4, DWARFAddressSpace, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", getFile(), 1, Scope, BaseType, 2, - 3, 4, Flags5, ExtraData)); + 3, 4, DWARFAddressSpace, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 2, Scope, BaseType, 2, 3, - 4, Flags5, ExtraData)); + 4, DWARFAddressSpace, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, getSubprogram(), - BaseType, 2, 3, 4, Flags5, ExtraData)); + BaseType, 2, 3, 4, DWARFAddressSpace, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get( Context, dwarf::DW_TAG_pointer_type, "something", File, 1, - Scope, getBasicType("basic2"), 2, 3, 4, Flags5, ExtraData)); + Scope, getBasicType("basic2"), 2, 3, 4, DWARFAddressSpace, + Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 3, 3, - 4, Flags5, ExtraData)); + 4, DWARFAddressSpace, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 2, - 4, Flags5, ExtraData)); + 4, DWARFAddressSpace, Flags5, ExtraData)); + EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, + "something", File, 1, Scope, BaseType, 2, 3, + 5, DWARFAddressSpace, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 5, Flags5, ExtraData)); + 4, DWARFAddressSpace + 1, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, Flags4, ExtraData)); + 4, DWARFAddressSpace, Flags4, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, Flags5, getTuple())); + 4, DWARFAddressSpace, Flags5, getTuple())); TempDIDerivedType Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1121,10 +1129,12 @@ TEST_F(DIDerivedTypeTest, getWithLargeValues) { auto *N = DIDerivedType::get( Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, - BaseType, UINT64_MAX, UINT32_MAX - 1, UINT64_MAX - 2, Flags, ExtraData); + BaseType, UINT64_MAX, UINT32_MAX - 1, UINT64_MAX - 2, UINT32_MAX - 3, + Flags, ExtraData); EXPECT_EQ(UINT64_MAX, N->getSizeInBits()); EXPECT_EQ(UINT32_MAX - 1, N->getAlignInBits()); EXPECT_EQ(UINT64_MAX - 2, N->getOffsetInBits()); + EXPECT_EQ(UINT32_MAX - 3, N->getDWARFAddressSpace().getValue()); } typedef MetadataTest DICompositeTypeTest; @@ -1406,7 +1416,8 @@ TEST_F(DICompileUnitTest, get) { auto *N = DICompileUnit::getDistinct( Context, SourceLanguage, File, Producer, IsOptimized, Flags, RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes, - RetainedTypes, GlobalVariables, ImportedEntities, Macros, DWOId, true); + RetainedTypes, GlobalVariables, ImportedEntities, Macros, DWOId, true, + false); EXPECT_EQ(dwarf::DW_TAG_compile_unit, N->getTag()); EXPECT_EQ(SourceLanguage, N->getSourceLanguage()); @@ -1463,7 +1474,7 @@ TEST_F(DICompileUnitTest, replaceArrays) { auto *N = DICompileUnit::getDistinct( Context, SourceLanguage, File, Producer, IsOptimized, Flags, RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes, - RetainedTypes, nullptr, ImportedEntities, nullptr, DWOId, true); + RetainedTypes, nullptr, ImportedEntities, nullptr, DWOId, true, false); auto *GlobalVariables = MDTuple::getDistinct(Context, None); EXPECT_EQ(nullptr, N->getGlobalVariables().get()); diff --git a/unittests/IR/ValueHandleTest.cpp b/unittests/IR/ValueHandleTest.cpp index 59cd9d7ba37ae..1abc87c2fdc7f 100644 --- a/unittests/IR/ValueHandleTest.cpp +++ b/unittests/IR/ValueHandleTest.cpp @@ -412,4 +412,97 @@ TEST_F(ValueHandle, AssertingVHCheckedLast) { BitcastV.reset(); } +TEST_F(ValueHandle, PoisoningVH_BasicOperation) { + PoisoningVH<CastInst> VH(BitcastV.get()); + CastInst *implicit_to_exact_type = VH; + (void)implicit_to_exact_type; // Avoid warning. + + PoisoningVH<Value> GenericVH(BitcastV.get()); + EXPECT_EQ(BitcastV.get(), GenericVH); + GenericVH = ConstantV; + EXPECT_EQ(ConstantV, GenericVH); + + // Make sure I can call a method on the underlying CastInst. It + // doesn't matter which method. + EXPECT_FALSE(VH->mayWriteToMemory()); + EXPECT_FALSE((*VH).mayWriteToMemory()); +} + +TEST_F(ValueHandle, PoisoningVH_Const) { + const CastInst *ConstBitcast = BitcastV.get(); + PoisoningVH<const CastInst> VH(ConstBitcast); + const CastInst *implicit_to_exact_type = VH; + (void)implicit_to_exact_type; // Avoid warning. +} + +TEST_F(ValueHandle, PoisoningVH_Comparisons) { + PoisoningVH<Value> BitcastVH(BitcastV.get()); + PoisoningVH<Value> ConstantVH(ConstantV); + + EXPECT_TRUE(BitcastVH == BitcastVH); + EXPECT_TRUE(BitcastV.get() == BitcastVH); + EXPECT_TRUE(BitcastVH == BitcastV.get()); + EXPECT_FALSE(BitcastVH == ConstantVH); + + EXPECT_TRUE(BitcastVH != ConstantVH); + EXPECT_TRUE(BitcastV.get() != ConstantVH); + EXPECT_TRUE(BitcastVH != ConstantV); + EXPECT_FALSE(BitcastVH != BitcastVH); + + // Cast to Value* so comparisons work. + Value *BV = BitcastV.get(); + Value *CV = ConstantV; + EXPECT_EQ(BV < CV, BitcastVH < ConstantVH); + EXPECT_EQ(BV <= CV, BitcastVH <= ConstantVH); + EXPECT_EQ(BV > CV, BitcastVH > ConstantVH); + EXPECT_EQ(BV >= CV, BitcastVH >= ConstantVH); + + EXPECT_EQ(BV < CV, BitcastV.get() < ConstantVH); + EXPECT_EQ(BV <= CV, BitcastV.get() <= ConstantVH); + EXPECT_EQ(BV > CV, BitcastV.get() > ConstantVH); + EXPECT_EQ(BV >= CV, BitcastV.get() >= ConstantVH); + + EXPECT_EQ(BV < CV, BitcastVH < ConstantV); + EXPECT_EQ(BV <= CV, BitcastVH <= ConstantV); + EXPECT_EQ(BV > CV, BitcastVH > ConstantV); + EXPECT_EQ(BV >= CV, BitcastVH >= ConstantV); +} + +TEST_F(ValueHandle, PoisoningVH_DoesNotFollowRAUW) { + PoisoningVH<Value> VH(BitcastV.get()); + BitcastV->replaceAllUsesWith(ConstantV); + EXPECT_TRUE(DenseMapInfo<PoisoningVH<Value>>::isEqual(VH, BitcastV.get())); +} + +#ifdef NDEBUG + +TEST_F(ValueHandle, PoisoningVH_ReducesToPointer) { + EXPECT_EQ(sizeof(CastInst *), sizeof(PoisoningVH<CastInst>)); +} + +#else // !NDEBUG + +#ifdef GTEST_HAS_DEATH_TEST + +TEST_F(ValueHandle, PoisoningVH_Asserts) { + PoisoningVH<Value> VH(BitcastV.get()); + + // The poisoned handle shouldn't assert when the value is deleted. + BitcastV.reset(new BitCastInst(ConstantV, Type::getInt32Ty(Context))); + // But should when we access the handle. + EXPECT_DEATH((void)*VH, "Accessed a poisoned value handle!"); + + // Now check that poison catches RAUW. + VH = BitcastV.get(); + // The replace doesn't trigger anything immediately. + BitcastV->replaceAllUsesWith(ConstantV); + // But a use does. + EXPECT_DEATH((void)*VH, "Accessed a poisoned value handle!"); + + // Don't clear anything out here as destroying the handles should be fine. +} + +#endif // GTEST_HAS_DEATH_TEST + +#endif // NDEBUG } diff --git a/unittests/IR/ValueTest.cpp b/unittests/IR/ValueTest.cpp index 607b7a1bd2c9b..142444a809c6d 100644 --- a/unittests/IR/ValueTest.cpp +++ b/unittests/IR/ValueTest.cpp @@ -40,7 +40,7 @@ TEST(ValueTest, UsedInBasicBlock) { Function *F = M->getFunction("f"); EXPECT_FALSE(F->isUsedInBasicBlock(&F->front())); - EXPECT_TRUE((++F->arg_begin())->isUsedInBasicBlock(&F->front())); + EXPECT_TRUE(std::next(F->arg_begin())->isUsedInBasicBlock(&F->front())); EXPECT_TRUE(F->arg_begin()->isUsedInBasicBlock(&F->front())); } diff --git a/unittests/IR/VerifierTest.cpp b/unittests/IR/VerifierTest.cpp index ad6940afd05ef..188509aadf77a 100644 --- a/unittests/IR/VerifierTest.cpp +++ b/unittests/IR/VerifierTest.cpp @@ -52,9 +52,9 @@ TEST(VerifierTest, InvalidRetAttribute) { Module M("M", C); FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false); Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy)); - AttributeSet AS = F->getAttributes(); - F->setAttributes(AS.addAttribute(C, AttributeSet::ReturnIndex, - Attribute::UWTable)); + AttributeList AS = F->getAttributes(); + F->setAttributes( + AS.addAttribute(C, AttributeList::ReturnIndex, Attribute::UWTable)); std::string Error; raw_string_ostream ErrorOS(Error); diff --git a/unittests/Linker/LinkModulesTest.cpp b/unittests/Linker/LinkModulesTest.cpp index 92c483278be9d..f31409c501211 100644 --- a/unittests/Linker/LinkModulesTest.cpp +++ b/unittests/Linker/LinkModulesTest.cpp @@ -317,34 +317,34 @@ TEST_F(LinkModuleTest, RemangleIntrinsics) { const char *FooStr = "%struct.rtx_def = type { i16 }\n" "define void @foo(%struct.rtx_def* %a, i8 %b, i32 %c) {\n" - " call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n" + " call void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n" " ret void\n" "}\n" - "declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n"; + "declare void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def*, i8, i32, i32, i1)\n"; const char *BarStr = "%struct.rtx_def = type { i16 }\n" "define void @bar(%struct.rtx_def* %a, i8 %b, i32 %c) {\n" - " call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n" + " call void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n" " ret void\n" "}\n" - "declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n"; + "declare void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def*, i8, i32, i32, i1)\n"; std::unique_ptr<Module> Foo = parseAssemblyString(FooStr, Err, C); assert(Foo); ASSERT_TRUE(Foo.get()); // Foo is loaded first, so the type and the intrinsic have theis original // names. - ASSERT_TRUE(Foo->getFunction("llvm.memset.p0struct.rtx_def.i32")); - ASSERT_FALSE(Foo->getFunction("llvm.memset.p0struct.rtx_def.0.i32")); + ASSERT_TRUE(Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.i32")); + ASSERT_FALSE(Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.0.i32")); std::unique_ptr<Module> Bar = parseAssemblyString(BarStr, Err, C); assert(Bar); ASSERT_TRUE(Bar.get()); // Bar is loaded after Foo, so the type is renamed to struct.rtx_def.0. Check // that the intrinsic is also renamed. - ASSERT_FALSE(Bar->getFunction("llvm.memset.p0struct.rtx_def.i32")); - ASSERT_TRUE(Bar->getFunction("llvm.memset.p0struct.rtx_def.0.i32")); + ASSERT_FALSE(Bar->getFunction("llvm.memset.p0s_struct.rtx_defs.i32")); + ASSERT_TRUE(Bar->getFunction("llvm.memset.p0s_struct.rtx_def.0s.i32")); // Link two modules together. auto Dst = llvm::make_unique<Module>("Linked", C); @@ -356,7 +356,7 @@ TEST_F(LinkModuleTest, RemangleIntrinsics) { // "struct.rtx_def" from Foo and "struct.rtx_def.0" from Bar are isomorphic // types, so they must be uniquified by linker. Check that they use the same // intrinsic definition. - Function *F = Foo->getFunction("llvm.memset.p0struct.rtx_def.i32"); + Function *F = Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.i32"); ASSERT_EQ(F->getNumUses(), (unsigned)2); } diff --git a/unittests/MI/LiveIntervalTest.cpp b/unittests/MI/LiveIntervalTest.cpp index 1d6df97a32007..026fb42d345f7 100644 --- a/unittests/MI/LiveIntervalTest.cpp +++ b/unittests/MI/LiveIntervalTest.cpp @@ -142,15 +142,15 @@ static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) { legacy::PassManager PM; SmallString<160> S; - StringRef MIRString = (Twine( -"---\n" -"...\n" -"name: func\n" -"registers:\n" -" - { id: 0, class: sreg_64 }\n" -"body: |\n" -" bb.0:\n" - ) + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S); + StringRef MIRString = (Twine(R"MIR( +--- +... +name: func +registers: + - { id: 0, class: sreg_64 } +body: | + bb.0: +)MIR") + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S); std::unique_ptr<MIRParser> MIR; std::unique_ptr<Module> M = parseMIR(Context, PM, MIR, *TM, MIRString, "func"); @@ -167,66 +167,66 @@ INITIALIZE_PASS(TestPass, "testpass", "testpass", false, false) TEST(LiveIntervalTest, MoveUpDef) { // Value defined. - liveIntervalTest( -" S_NOP 0\n" -" S_NOP 0\n" -" early-clobber %0 = IMPLICIT_DEF\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + S_NOP 0 + S_NOP 0 + early-clobber %0 = IMPLICIT_DEF + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); } TEST(LiveIntervalTest, MoveUpRedef) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0 + %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); } TEST(LiveIntervalTest, MoveUpEarlyDef) { - liveIntervalTest( -" S_NOP 0\n" -" S_NOP 0\n" -" early-clobber %0 = IMPLICIT_DEF\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + S_NOP 0 + S_NOP 0 + early-clobber %0 = IMPLICIT_DEF + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); } TEST(LiveIntervalTest, MoveUpEarlyRedef) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0 + early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); } TEST(LiveIntervalTest, MoveUpKill) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); } TEST(LiveIntervalTest, MoveUpKillFollowing) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" S_NOP 0, implicit %0\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); } @@ -236,77 +236,77 @@ TEST(LiveIntervalTest, MoveUpKillFollowing) { TEST(LiveIntervalTest, MoveDownDef) { // Value defined. - liveIntervalTest( -" S_NOP 0\n" -" early-clobber %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + S_NOP 0 + early-clobber %0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); } TEST(LiveIntervalTest, MoveDownRedef) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" S_NOP 0\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0 + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); } TEST(LiveIntervalTest, MoveDownEarlyDef) { - liveIntervalTest( -" S_NOP 0\n" -" early-clobber %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + S_NOP 0 + early-clobber %0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); } TEST(LiveIntervalTest, MoveDownEarlyRedef) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" S_NOP 0\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0 + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); } TEST(LiveIntervalTest, MoveDownKill) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0, implicit %0\n" -" S_NOP 0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0, implicit %0 + S_NOP 0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); } TEST(LiveIntervalTest, MoveDownKillFollowing) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" S_NOP 0, implicit %0\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); } TEST(LiveIntervalTest, MoveUndefUse) { - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0, implicit undef %0\n" -" S_NOP 0, implicit %0\n" -" S_NOP 0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0, implicit undef %0 + S_NOP 0, implicit %0 + S_NOP 0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 3); }); } @@ -315,44 +315,44 @@ TEST(LiveIntervalTest, MoveUpValNos) { // handleMoveUp() had a bug where it would reuse the value number of the // destination segment, even though we have no guarntee that this valno wasn't // used in other segments. - liveIntervalTest( -" successors: %bb.1, %bb.2\n" -" %0 = IMPLICIT_DEF\n" -" S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc\n" -" S_BRANCH %bb.1\n" -" bb.2:\n" -" S_NOP 0, implicit %0\n" -" bb.1:\n" -" successors: %bb.2\n" -" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" S_BRANCH %bb.2\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + successors: %bb.1, %bb.2 + %0 = IMPLICIT_DEF + S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc + S_BRANCH %bb.1 + bb.2: + S_NOP 0, implicit %0 + bb.1: + successors: %bb.2 + %0 = IMPLICIT_DEF implicit %0(tied-def 0) + %0 = IMPLICIT_DEF implicit %0(tied-def 0) + %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_BRANCH %bb.2 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 0, 2); }); } TEST(LiveIntervalTest, MoveOverUndefUse0) { // findLastUseBefore() used by handleMoveUp() must ignore undef operands. - liveIntervalTest( -" %0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" S_NOP 0, implicit undef %0\n" -" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit undef %0 + %0 = IMPLICIT_DEF implicit %0(tied-def 0) +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 3, 1); }); } TEST(LiveIntervalTest, MoveOverUndefUse1) { // findLastUseBefore() used by handleMoveUp() must ignore undef operands. - liveIntervalTest( -" %sgpr0 = IMPLICIT_DEF\n" -" S_NOP 0\n" -" S_NOP 0, implicit undef %sgpr0\n" -" %sgpr0 = IMPLICIT_DEF implicit %sgpr0(tied-def 0)\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + %sgpr0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit undef %sgpr0 + %sgpr0 = IMPLICIT_DEF implicit %sgpr0(tied-def 0) +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 3, 1); }); } @@ -360,21 +360,21 @@ TEST(LiveIntervalTest, MoveOverUndefUse1) { TEST(LiveIntervalTest, SubRegMoveDown) { // Subregister ranges can have holes inside a basic block. Check for a // movement of the form 32->150 in a liverange [16, 32) [100,200). - liveIntervalTest( -" successors: %bb.1, %bb.2\n" -" %0 = IMPLICIT_DEF\n" -" S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc\n" -" S_BRANCH %bb.1\n" -" bb.2:\n" -" successors: %bb.1\n" -" S_NOP 0, implicit %0.sub0\n" -" S_NOP 0, implicit %0.sub1\n" -" S_NOP 0\n" -" undef %0.sub0 = IMPLICIT_DEF\n" -" %0.sub1 = IMPLICIT_DEF\n" -" bb.1:\n" -" S_NOP 0, implicit %0\n", - [](MachineFunction &MF, LiveIntervals &LIS) { + liveIntervalTest(R"MIR( + successors: %bb.1, %bb.2 + %0 = IMPLICIT_DEF + S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc + S_BRANCH %bb.1 + bb.2: + successors: %bb.1 + S_NOP 0, implicit %0.sub0 + S_NOP 0, implicit %0.sub1 + S_NOP 0 + undef %0.sub0 = IMPLICIT_DEF + %0.sub1 = IMPLICIT_DEF + bb.1: + S_NOP 0, implicit %0 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { // Scheduler behaviour: Clear def,read-undef flag and move. MachineInstr &MI = getMI(MF, 3, /*BlockNum=*/1); MI.getOperand(0).setIsUndef(false); @@ -382,6 +382,24 @@ TEST(LiveIntervalTest, SubRegMoveDown) { }); } +TEST(LiveIntervalTest, SubRegMoveUp) { + // handleMoveUp had a bug not updating valno of segment incoming to bb.2 + // after swapping subreg definitions. + liveIntervalTest(R"MIR( + successors: %bb.1, %bb.2 + undef %0.sub0 = IMPLICIT_DEF + %0.sub1 = IMPLICIT_DEF + S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc + S_BRANCH %bb.1 + bb.1: + S_NOP 0, implicit %0.sub1 + bb.2: + S_NOP 0, implicit %0.sub1 +)MIR", [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 1, 0); + }); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); initLLVM(); diff --git a/unittests/Object/CMakeLists.txt b/unittests/Object/CMakeLists.txt index 7a63c167a30b9..e1376bffbc0f5 100644 --- a/unittests/Object/CMakeLists.txt +++ b/unittests/Object/CMakeLists.txt @@ -4,5 +4,6 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(ObjectTests SymbolSizeTest.cpp + SymbolicFileTest.cpp ) diff --git a/unittests/Object/SymbolicFileTest.cpp b/unittests/Object/SymbolicFileTest.cpp new file mode 100644 index 0000000000000..ec954e5e67d08 --- /dev/null +++ b/unittests/Object/SymbolicFileTest.cpp @@ -0,0 +1,42 @@ +//===- SymbolicFileTest.cpp - Tests for SymbolicFile.cpp ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/SymbolicFile.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include <sstream> + +TEST(Object, DataRefImplOstream) { + std::string s; + llvm::raw_string_ostream OS(s); + llvm::object::DataRefImpl Data; + Data.d.a = 0xeeee0000; + Data.d.b = 0x0000ffff; + + static_assert(sizeof Data.p == sizeof(uint64_t) || + sizeof Data.p == sizeof(uint32_t), + "Test expected pointer type to be 32 or 64-bit."); + + char const *Expected; + + if (sizeof Data.p == sizeof(uint64_t)) { + Expected = llvm::sys::IsLittleEndianHost + ? "(0xffffeeee0000 (0xeeee0000, 0x0000ffff))" + : "(0xeeee00000000ffff (0xeeee0000, 0x0000ffff))"; + } + else { + Expected = "(0xeeee0000 (0xeeee0000, 0x0000ffff))"; + } + + OS << Data; + OS.flush(); + + EXPECT_EQ(Expected, s); +} diff --git a/unittests/Option/OptionParsingTest.cpp b/unittests/Option/OptionParsingTest.cpp index b0418a71c7866..8630ff1d303c0 100644 --- a/unittests/Option/OptionParsingTest.cpp +++ b/unittests/Option/OptionParsingTest.cpp @@ -97,11 +97,11 @@ TEST(Option, OptionParsing) { EXPECT_NE(std::string::npos, Help.find("-A")); // Test aliases. - arg_iterator Cs = AL.filtered_begin(OPT_C); - ASSERT_NE(AL.filtered_end(), Cs); - EXPECT_EQ("desu", StringRef((*Cs)->getValue())); + auto Cs = AL.filtered(OPT_C); + ASSERT_NE(Cs.begin(), Cs.end()); + EXPECT_EQ("desu", StringRef((*Cs.begin())->getValue())); ArgStringList ASL; - (*Cs)->render(AL, ASL); + (*Cs.begin())->render(AL, ASL); ASSERT_EQ(2u, ASL.size()); EXPECT_EQ("-C", StringRef(ASL[0])); EXPECT_EQ("desu", StringRef(ASL[1])); diff --git a/unittests/ProfileData/CoverageMappingTest.cpp b/unittests/ProfileData/CoverageMappingTest.cpp index 49eab4ad7887a..0783a23a67b06 100644 --- a/unittests/ProfileData/CoverageMappingTest.cpp +++ b/unittests/ProfileData/CoverageMappingTest.cpp @@ -328,7 +328,7 @@ TEST_P(CoverageMappingTest, load_coverage_for_several_functions) { loadCoverageMapping(); const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); - EXPECT_EQ(2U, std::distance(FunctionRecords.begin(), FunctionRecords.end())); + EXPECT_EQ(2, std::distance(FunctionRecords.begin(), FunctionRecords.end())); for (const auto &FunctionRecord : FunctionRecords) { CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord); std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); diff --git a/unittests/Support/ARMAttributeParser.cpp b/unittests/Support/ARMAttributeParser.cpp new file mode 100644 index 0000000000000..c2df6537ff63d --- /dev/null +++ b/unittests/Support/ARMAttributeParser.cpp @@ -0,0 +1,385 @@ +#include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/ARMAttributeParser.h" +#include "llvm/Support/LEB128.h" +#include "gtest/gtest.h" +#include <string> + +using namespace llvm; + +struct AttributeSection { + unsigned Tag; + unsigned Value; + + AttributeSection(unsigned tag, unsigned value) : Tag(tag), Value(value) { } + + void write(raw_ostream &OS) { + OS.flush(); + // length = length + "aeabi\0" + TagFile + ByteSize + Tag + Value; + // length = 17 bytes + + OS << 'A' << (uint8_t)17 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0; + OS << "aeabi" << '\0'; + OS << (uint8_t)1 << (uint8_t)7 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0; + OS << (uint8_t)Tag << (uint8_t)Value; + + } +}; + +bool testBuildAttr(unsigned Tag, unsigned Value, + unsigned ExpectedTag, unsigned ExpectedValue) { + std::string buffer; + raw_string_ostream OS(buffer); + AttributeSection Section(Tag, Value); + Section.write(OS); + ArrayRef<uint8_t> Bytes( + reinterpret_cast<const uint8_t*>(OS.str().c_str()), OS.str().size()); + + ARMAttributeParser Parser; + Parser.Parse(Bytes, true); + + return (Parser.hasAttribute(ExpectedTag) && + Parser.getAttributeValue(ExpectedTag) == ExpectedValue); +} + +bool testTagString(unsigned Tag, const char *name) { + return ARMBuildAttrs::AttrTypeAsString(Tag).str() == name; +} + +TEST(CPUArchBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(6, "Tag_CPU_arch")); + + EXPECT_TRUE(testBuildAttr(6, 0, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::Pre_v4)); + EXPECT_TRUE(testBuildAttr(6, 1, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v4)); + EXPECT_TRUE(testBuildAttr(6, 2, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v4T)); + EXPECT_TRUE(testBuildAttr(6, 3, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v5T)); + EXPECT_TRUE(testBuildAttr(6, 4, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v5TE)); + EXPECT_TRUE(testBuildAttr(6, 5, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v5TEJ)); + EXPECT_TRUE(testBuildAttr(6, 6, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v6)); + EXPECT_TRUE(testBuildAttr(6, 7, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v6KZ)); + EXPECT_TRUE(testBuildAttr(6, 8, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v6T2)); + EXPECT_TRUE(testBuildAttr(6, 9, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v6K)); + EXPECT_TRUE(testBuildAttr(6, 10, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v7)); + EXPECT_TRUE(testBuildAttr(6, 11, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v6_M)); + EXPECT_TRUE(testBuildAttr(6, 12, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v6S_M)); + EXPECT_TRUE(testBuildAttr(6, 13, ARMBuildAttrs::CPU_arch, + ARMBuildAttrs::v7E_M)); +} + +TEST(CPUArchProfileBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(7, "Tag_CPU_arch_profile")); + EXPECT_TRUE(testBuildAttr(7, 'A', ARMBuildAttrs::CPU_arch_profile, + ARMBuildAttrs::ApplicationProfile)); + EXPECT_TRUE(testBuildAttr(7, 'R', ARMBuildAttrs::CPU_arch_profile, + ARMBuildAttrs::RealTimeProfile)); + EXPECT_TRUE(testBuildAttr(7, 'M', ARMBuildAttrs::CPU_arch_profile, + ARMBuildAttrs::MicroControllerProfile)); + EXPECT_TRUE(testBuildAttr(7, 'S', ARMBuildAttrs::CPU_arch_profile, + ARMBuildAttrs::SystemProfile)); +} + +TEST(ARMISABuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(8, "Tag_ARM_ISA_use")); + EXPECT_TRUE(testBuildAttr(8, 0, ARMBuildAttrs::ARM_ISA_use, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(8, 1, ARMBuildAttrs::ARM_ISA_use, + ARMBuildAttrs::Allowed)); +} + +TEST(ThumbISABuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(9, "Tag_THUMB_ISA_use")); + EXPECT_TRUE(testBuildAttr(9, 0, ARMBuildAttrs::THUMB_ISA_use, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(9, 1, ARMBuildAttrs::THUMB_ISA_use, + ARMBuildAttrs::Allowed)); +} + +TEST(FPArchBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(10, "Tag_FP_arch")); + EXPECT_TRUE(testBuildAttr(10, 0, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(10, 1, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::Allowed)); + EXPECT_TRUE(testBuildAttr(10, 2, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::AllowFPv2)); + EXPECT_TRUE(testBuildAttr(10, 3, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::AllowFPv3A)); + EXPECT_TRUE(testBuildAttr(10, 4, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::AllowFPv3B)); + EXPECT_TRUE(testBuildAttr(10, 5, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::AllowFPv4A)); + EXPECT_TRUE(testBuildAttr(10, 6, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::AllowFPv4B)); + EXPECT_TRUE(testBuildAttr(10, 7, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::AllowFPARMv8A)); + EXPECT_TRUE(testBuildAttr(10, 8, ARMBuildAttrs::FP_arch, + ARMBuildAttrs::AllowFPARMv8B)); +} + +TEST(WMMXBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(11, "Tag_WMMX_arch")); + EXPECT_TRUE(testBuildAttr(11, 0, ARMBuildAttrs::WMMX_arch, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(11, 1, ARMBuildAttrs::WMMX_arch, + ARMBuildAttrs::AllowWMMXv1)); + EXPECT_TRUE(testBuildAttr(11, 2, ARMBuildAttrs::WMMX_arch, + ARMBuildAttrs::AllowWMMXv2)); +} + +TEST(SIMDBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(12, "Tag_Advanced_SIMD_arch")); + EXPECT_TRUE(testBuildAttr(12, 0, ARMBuildAttrs::Advanced_SIMD_arch, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(12, 1, ARMBuildAttrs::Advanced_SIMD_arch, + ARMBuildAttrs::AllowNeon)); + EXPECT_TRUE(testBuildAttr(12, 2, ARMBuildAttrs::Advanced_SIMD_arch, + ARMBuildAttrs::AllowNeon2)); + EXPECT_TRUE(testBuildAttr(12, 3, ARMBuildAttrs::Advanced_SIMD_arch, + ARMBuildAttrs::AllowNeonARMv8)); + EXPECT_TRUE(testBuildAttr(12, 4, ARMBuildAttrs::Advanced_SIMD_arch, + ARMBuildAttrs::AllowNeonARMv8_1a)); +} + +TEST(FPHPBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(36, "Tag_FP_HP_extension")); + EXPECT_TRUE(testBuildAttr(36, 0, ARMBuildAttrs::FP_HP_extension, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(36, 1, ARMBuildAttrs::FP_HP_extension, + ARMBuildAttrs::AllowHPFP)); +} + +TEST(CPUAlignBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(34, "Tag_CPU_unaligned_access")); + EXPECT_TRUE(testBuildAttr(34, 0, ARMBuildAttrs::CPU_unaligned_access, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(34, 1, ARMBuildAttrs::CPU_unaligned_access, + ARMBuildAttrs::Allowed)); +} + +TEST(T2EEBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(66, "Tag_T2EE_use")); + EXPECT_TRUE(testBuildAttr(66, 0, ARMBuildAttrs::T2EE_use, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(66, 1, ARMBuildAttrs::T2EE_use, + ARMBuildAttrs::Allowed)); +} + +TEST(VirtualizationBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(68, "Tag_Virtualization_use")); + EXPECT_TRUE(testBuildAttr(68, 0, ARMBuildAttrs::Virtualization_use, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(68, 1, ARMBuildAttrs::Virtualization_use, + ARMBuildAttrs::AllowTZ)); + EXPECT_TRUE(testBuildAttr(68, 2, ARMBuildAttrs::Virtualization_use, + ARMBuildAttrs::AllowVirtualization)); + EXPECT_TRUE(testBuildAttr(68, 3, ARMBuildAttrs::Virtualization_use, + ARMBuildAttrs::AllowTZVirtualization)); +} + +TEST(MPBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(42, "Tag_MPextension_use")); + EXPECT_TRUE(testBuildAttr(42, 0, ARMBuildAttrs::MPextension_use, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(42, 1, ARMBuildAttrs::MPextension_use, + ARMBuildAttrs::AllowMP)); +} + +TEST(DivBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(44, "Tag_DIV_use")); + EXPECT_TRUE(testBuildAttr(44, 0, ARMBuildAttrs::DIV_use, + ARMBuildAttrs::AllowDIVIfExists)); + EXPECT_TRUE(testBuildAttr(44, 1, ARMBuildAttrs::DIV_use, + ARMBuildAttrs::DisallowDIV)); + EXPECT_TRUE(testBuildAttr(44, 2, ARMBuildAttrs::DIV_use, + ARMBuildAttrs::AllowDIVExt)); +} + +TEST(PCS_ConfigBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(13, "Tag_PCS_config")); + EXPECT_TRUE(testBuildAttr(13, 0, ARMBuildAttrs::PCS_config, 0)); + EXPECT_TRUE(testBuildAttr(13, 1, ARMBuildAttrs::PCS_config, 1)); + EXPECT_TRUE(testBuildAttr(13, 2, ARMBuildAttrs::PCS_config, 2)); + EXPECT_TRUE(testBuildAttr(13, 3, ARMBuildAttrs::PCS_config, 3)); + EXPECT_TRUE(testBuildAttr(13, 4, ARMBuildAttrs::PCS_config, 4)); + EXPECT_TRUE(testBuildAttr(13, 5, ARMBuildAttrs::PCS_config, 5)); + EXPECT_TRUE(testBuildAttr(13, 6, ARMBuildAttrs::PCS_config, 6)); + EXPECT_TRUE(testBuildAttr(13, 7, ARMBuildAttrs::PCS_config, 7)); +} + +TEST(PCS_R9BuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(14, "Tag_ABI_PCS_R9_use")); + EXPECT_TRUE(testBuildAttr(14, 0, ARMBuildAttrs::ABI_PCS_R9_use, + ARMBuildAttrs::R9IsGPR)); + EXPECT_TRUE(testBuildAttr(14, 1, ARMBuildAttrs::ABI_PCS_R9_use, + ARMBuildAttrs::R9IsSB)); + EXPECT_TRUE(testBuildAttr(14, 2, ARMBuildAttrs::ABI_PCS_R9_use, + ARMBuildAttrs::R9IsTLSPointer)); + EXPECT_TRUE(testBuildAttr(14, 3, ARMBuildAttrs::ABI_PCS_R9_use, + ARMBuildAttrs::R9Reserved)); +} + +TEST(PCS_RWBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(15, "Tag_ABI_PCS_RW_data")); + EXPECT_TRUE(testBuildAttr(15, 0, ARMBuildAttrs::ABI_PCS_RW_data, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(15, 1, ARMBuildAttrs::ABI_PCS_RW_data, + ARMBuildAttrs::AddressRWPCRel)); + EXPECT_TRUE(testBuildAttr(15, 2, ARMBuildAttrs::ABI_PCS_RW_data, + ARMBuildAttrs::AddressRWSBRel)); + EXPECT_TRUE(testBuildAttr(15, 3, ARMBuildAttrs::ABI_PCS_RW_data, + ARMBuildAttrs::AddressRWNone)); +} + +TEST(PCS_ROBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(16, "Tag_ABI_PCS_RO_data")); + EXPECT_TRUE(testBuildAttr(16, 0, ARMBuildAttrs::ABI_PCS_RO_data, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(16, 1, ARMBuildAttrs::ABI_PCS_RO_data, + ARMBuildAttrs::AddressROPCRel)); + EXPECT_TRUE(testBuildAttr(16, 2, ARMBuildAttrs::ABI_PCS_RO_data, + ARMBuildAttrs::AddressRONone)); +} + +TEST(PCS_GOTBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(17, "Tag_ABI_PCS_GOT_use")); + EXPECT_TRUE(testBuildAttr(17, 0, ARMBuildAttrs::ABI_PCS_GOT_use, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(17, 1, ARMBuildAttrs::ABI_PCS_GOT_use, + ARMBuildAttrs::AddressDirect)); + EXPECT_TRUE(testBuildAttr(17, 2, ARMBuildAttrs::ABI_PCS_GOT_use, + ARMBuildAttrs::AddressGOT)); +} + +TEST(PCS_WCharBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(18, "Tag_ABI_PCS_wchar_t")); + EXPECT_TRUE(testBuildAttr(18, 0, ARMBuildAttrs::ABI_PCS_wchar_t, + ARMBuildAttrs::WCharProhibited)); + EXPECT_TRUE(testBuildAttr(18, 2, ARMBuildAttrs::ABI_PCS_wchar_t, + ARMBuildAttrs::WCharWidth2Bytes)); + EXPECT_TRUE(testBuildAttr(18, 4, ARMBuildAttrs::ABI_PCS_wchar_t, + ARMBuildAttrs::WCharWidth4Bytes)); +} + +TEST(EnumSizeBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(26, "Tag_ABI_enum_size")); + EXPECT_TRUE(testBuildAttr(26, 0, ARMBuildAttrs::ABI_enum_size, + ARMBuildAttrs::EnumProhibited)); + EXPECT_TRUE(testBuildAttr(26, 1, ARMBuildAttrs::ABI_enum_size, + ARMBuildAttrs::EnumSmallest)); + EXPECT_TRUE(testBuildAttr(26, 2, ARMBuildAttrs::ABI_enum_size, + ARMBuildAttrs::Enum32Bit)); + EXPECT_TRUE(testBuildAttr(26, 3, ARMBuildAttrs::ABI_enum_size, + ARMBuildAttrs::Enum32BitABI)); +} + +TEST(AlignNeededBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(24, "Tag_ABI_align_needed")); + EXPECT_TRUE(testBuildAttr(24, 0, ARMBuildAttrs::ABI_align_needed, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(24, 1, ARMBuildAttrs::ABI_align_needed, + ARMBuildAttrs::Align8Byte)); + EXPECT_TRUE(testBuildAttr(24, 2, ARMBuildAttrs::ABI_align_needed, + ARMBuildAttrs::Align4Byte)); + EXPECT_TRUE(testBuildAttr(24, 3, ARMBuildAttrs::ABI_align_needed, + ARMBuildAttrs::AlignReserved)); +} + +TEST(AlignPreservedBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(25, "Tag_ABI_align_preserved")); + EXPECT_TRUE(testBuildAttr(25, 0, ARMBuildAttrs::ABI_align_preserved, + ARMBuildAttrs::AlignNotPreserved)); + EXPECT_TRUE(testBuildAttr(25, 1, ARMBuildAttrs::ABI_align_preserved, + ARMBuildAttrs::AlignPreserve8Byte)); + EXPECT_TRUE(testBuildAttr(25, 2, ARMBuildAttrs::ABI_align_preserved, + ARMBuildAttrs::AlignPreserveAll)); + EXPECT_TRUE(testBuildAttr(25, 3, ARMBuildAttrs::ABI_align_preserved, + ARMBuildAttrs::AlignReserved)); +} + +TEST(FPRoundingBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(19, "Tag_ABI_FP_rounding")); + EXPECT_TRUE(testBuildAttr(19, 0, ARMBuildAttrs::ABI_FP_rounding, 0)); + EXPECT_TRUE(testBuildAttr(19, 1, ARMBuildAttrs::ABI_FP_rounding, 1)); +} + +TEST(FPDenormalBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(20, "Tag_ABI_FP_denormal")); + EXPECT_TRUE(testBuildAttr(20, 0, ARMBuildAttrs::ABI_FP_denormal, + ARMBuildAttrs::PositiveZero)); + EXPECT_TRUE(testBuildAttr(20, 1, ARMBuildAttrs::ABI_FP_denormal, + ARMBuildAttrs::IEEEDenormals)); + EXPECT_TRUE(testBuildAttr(20, 2, ARMBuildAttrs::ABI_FP_denormal, + ARMBuildAttrs::PreserveFPSign)); +} + +TEST(FPExceptionsBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(21, "Tag_ABI_FP_exceptions")); + EXPECT_TRUE(testBuildAttr(21, 0, ARMBuildAttrs::ABI_FP_exceptions, 0)); + EXPECT_TRUE(testBuildAttr(21, 1, ARMBuildAttrs::ABI_FP_exceptions, 1)); +} + +TEST(FPUserExceptionsBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(22, "Tag_ABI_FP_user_exceptions")); + EXPECT_TRUE(testBuildAttr(22, 0, ARMBuildAttrs::ABI_FP_user_exceptions, 0)); + EXPECT_TRUE(testBuildAttr(22, 1, ARMBuildAttrs::ABI_FP_user_exceptions, 1)); +} + +TEST(FPNumberModelBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(23, "Tag_ABI_FP_number_model")); + EXPECT_TRUE(testBuildAttr(23, 0, ARMBuildAttrs::ABI_FP_number_model, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(23, 1, ARMBuildAttrs::ABI_FP_number_model, + ARMBuildAttrs::AllowIEEENormal)); + EXPECT_TRUE(testBuildAttr(23, 2, ARMBuildAttrs::ABI_FP_number_model, + ARMBuildAttrs::AllowRTABI)); + EXPECT_TRUE(testBuildAttr(23, 3, ARMBuildAttrs::ABI_FP_number_model, + ARMBuildAttrs::AllowIEEE754)); +} + +TEST(FP16BuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(38, "Tag_ABI_FP_16bit_format")); + EXPECT_TRUE(testBuildAttr(38, 0, ARMBuildAttrs::ABI_FP_16bit_format, + ARMBuildAttrs::Not_Allowed)); + EXPECT_TRUE(testBuildAttr(38, 1, ARMBuildAttrs::ABI_FP_16bit_format, + ARMBuildAttrs::FP16FormatIEEE)); + EXPECT_TRUE(testBuildAttr(38, 2, ARMBuildAttrs::ABI_FP_16bit_format, + ARMBuildAttrs::FP16VFP3)); +} + +TEST(HardFPBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(27, "Tag_ABI_HardFP_use")); + EXPECT_TRUE(testBuildAttr(27, 0, ARMBuildAttrs::ABI_HardFP_use, + ARMBuildAttrs::HardFPImplied)); + EXPECT_TRUE(testBuildAttr(27, 1, ARMBuildAttrs::ABI_HardFP_use, + ARMBuildAttrs::HardFPSinglePrecision)); + EXPECT_TRUE(testBuildAttr(27, 2, ARMBuildAttrs::ABI_HardFP_use, 2)); +} + +TEST(VFPArgsBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(28, "Tag_ABI_VFP_args")); + EXPECT_TRUE(testBuildAttr(28, 0, ARMBuildAttrs::ABI_VFP_args, + ARMBuildAttrs::BaseAAPCS)); + EXPECT_TRUE(testBuildAttr(28, 1, ARMBuildAttrs::ABI_VFP_args, + ARMBuildAttrs::HardFPAAPCS)); + EXPECT_TRUE(testBuildAttr(28, 2, ARMBuildAttrs::ABI_VFP_args, 2)); + EXPECT_TRUE(testBuildAttr(28, 3, ARMBuildAttrs::ABI_VFP_args, 3)); +} + +TEST(WMMXArgsBuildAttr, testBuildAttr) { + EXPECT_TRUE(testTagString(29, "Tag_ABI_WMMX_args")); + EXPECT_TRUE(testBuildAttr(29, 0, ARMBuildAttrs::ABI_WMMX_args, 0)); + EXPECT_TRUE(testBuildAttr(29, 1, ARMBuildAttrs::ABI_WMMX_args, 1)); + EXPECT_TRUE(testBuildAttr(29, 2, ARMBuildAttrs::ABI_WMMX_args, 2)); +} diff --git a/unittests/Support/AllocatorTest.cpp b/unittests/Support/AllocatorTest.cpp index 4b544641e9bff..4897c47eb28ba 100644 --- a/unittests/Support/AllocatorTest.cpp +++ b/unittests/Support/AllocatorTest.cpp @@ -17,9 +17,9 @@ namespace { TEST(AllocatorTest, Basics) { BumpPtrAllocator Alloc; - int *a = (int*)Alloc.Allocate(sizeof(int), 1); - int *b = (int*)Alloc.Allocate(sizeof(int) * 10, 1); - int *c = (int*)Alloc.Allocate(sizeof(int), 1); + int *a = (int*)Alloc.Allocate(sizeof(int), alignof(int)); + int *b = (int*)Alloc.Allocate(sizeof(int) * 10, alignof(int)); + int *c = (int*)Alloc.Allocate(sizeof(int), alignof(int)); *a = 1; b[0] = 2; b[9] = 2; diff --git a/unittests/Support/BinaryStreamTest.cpp b/unittests/Support/BinaryStreamTest.cpp new file mode 100644 index 0000000000000..1e646a6cf9001 --- /dev/null +++ b/unittests/Support/BinaryStreamTest.cpp @@ -0,0 +1,711 @@ +//===- llvm/unittest/Support/BinaryStreamTest.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryItemStream.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "gtest/gtest.h" + +#include <unordered_map> + +using namespace llvm; +using namespace llvm::support; + +#define EXPECT_NO_ERROR(Err) \ + { \ + auto E = Err; \ + EXPECT_FALSE(static_cast<bool>(E)); \ + if (E) \ + consumeError(std::move(E)); \ + } + +#define ASSERT_NO_ERROR(Err) \ + { \ + auto E = Err; \ + ASSERT_FALSE(static_cast<bool>(E)); \ + if (E) \ + consumeError(std::move(E)); \ + } + +#define EXPECT_ERROR(Err) \ + { \ + auto E = Err; \ + EXPECT_TRUE(static_cast<bool>(E)); \ + if (E) \ + consumeError(std::move(E)); \ + } + +namespace { + +class BrokenStream : public WritableBinaryStream { +public: + BrokenStream(MutableArrayRef<uint8_t> Data, endianness Endian, + uint32_t Align) + : Data(Data), PartitionIndex(alignDown(Data.size() / 2, Align)), + Endian(Endian) {} + + endianness getEndian() const override { return Endian; } + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffset(Offset, Size)) + return EC; + uint32_t S = startIndex(Offset); + auto Ref = Data.drop_front(S); + if (Ref.size() >= Size) { + Buffer = Ref.take_front(Size); + return Error::success(); + } + + uint32_t BytesLeft = Size - Ref.size(); + uint8_t *Ptr = Allocator.Allocate<uint8_t>(Size); + ::memcpy(Ptr, Ref.data(), Ref.size()); + ::memcpy(Ptr + Ref.size(), Data.data(), BytesLeft); + Buffer = makeArrayRef<uint8_t>(Ptr, Size); + return Error::success(); + } + + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffset(Offset, 1)) + return EC; + uint32_t S = startIndex(Offset); + Buffer = Data.drop_front(S); + return Error::success(); + } + + uint32_t getLength() override { return Data.size(); } + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> SrcData) override { + if (auto EC = checkOffset(Offset, SrcData.size())) + return EC; + if (SrcData.empty()) + return Error::success(); + + uint32_t S = startIndex(Offset); + MutableArrayRef<uint8_t> Ref(Data); + Ref = Ref.drop_front(S); + if (Ref.size() >= SrcData.size()) { + ::memcpy(Ref.data(), SrcData.data(), SrcData.size()); + return Error::success(); + } + + uint32_t BytesLeft = SrcData.size() - Ref.size(); + ::memcpy(Ref.data(), SrcData.data(), Ref.size()); + ::memcpy(&Data[0], SrcData.data() + Ref.size(), BytesLeft); + return Error::success(); + } + Error commit() override { return Error::success(); } + +private: + uint32_t startIndex(uint32_t Offset) const { + return (Offset + PartitionIndex) % Data.size(); + } + + uint32_t endIndex(uint32_t Offset, uint32_t Size) const { + return (startIndex(Offset) + Size - 1) % Data.size(); + } + + // Buffer is organized like this: + // ------------------------------------------------- + // | N/2 | N/2+1 | ... | N-1 | 0 | 1 | ... | N-2-1 | + // ------------------------------------------------- + // So reads from the beginning actually come from the middle. + MutableArrayRef<uint8_t> Data; + uint32_t PartitionIndex = 0; + endianness Endian; + BumpPtrAllocator Allocator; +}; + +constexpr endianness Endians[] = {big, little, native}; +constexpr uint32_t NumEndians = llvm::array_lengthof(Endians); +constexpr uint32_t NumStreams = 2 * NumEndians; + +class BinaryStreamTest : public testing::Test { + +public: + BinaryStreamTest() {} + + void SetUp() override { + Streams.clear(); + Streams.resize(NumStreams); + for (uint32_t I = 0; I < NumStreams; ++I) + Streams[I].IsContiguous = (I % 2 == 0); + + InputData.clear(); + OutputData.clear(); + } + +protected: + struct StreamPair { + bool IsContiguous; + std::unique_ptr<BinaryStream> Input; + std::unique_ptr<WritableBinaryStream> Output; + }; + + void initializeInput(ArrayRef<uint8_t> Input, uint32_t Align) { + InputData = Input; + + BrokenInputData.resize(InputData.size()); + if (!Input.empty()) { + uint32_t PartitionIndex = alignDown(InputData.size() / 2, Align); + uint32_t RightBytes = InputData.size() - PartitionIndex; + uint32_t LeftBytes = PartitionIndex; + if (RightBytes > 0) + ::memcpy(&BrokenInputData[PartitionIndex], Input.data(), RightBytes); + if (LeftBytes > 0) + ::memcpy(&BrokenInputData[0], Input.data() + RightBytes, LeftBytes); + } + + for (uint32_t I = 0; I < NumEndians; ++I) { + auto InByteStream = + llvm::make_unique<BinaryByteStream>(InputData, Endians[I]); + auto InBrokenStream = llvm::make_unique<BrokenStream>( + BrokenInputData, Endians[I], Align); + + Streams[I * 2].Input = std::move(InByteStream); + Streams[I * 2 + 1].Input = std::move(InBrokenStream); + } + } + + void initializeOutput(uint32_t Size, uint32_t Align) { + OutputData.resize(Size); + BrokenOutputData.resize(Size); + + for (uint32_t I = 0; I < NumEndians; ++I) { + Streams[I * 2].Output = + llvm::make_unique<MutableBinaryByteStream>(OutputData, Endians[I]); + Streams[I * 2 + 1].Output = llvm::make_unique<BrokenStream>( + BrokenOutputData, Endians[I], Align); + } + } + + void initializeOutputFromInput(uint32_t Align) { + for (uint32_t I = 0; I < NumEndians; ++I) { + Streams[I * 2].Output = + llvm::make_unique<MutableBinaryByteStream>(InputData, Endians[I]); + Streams[I * 2 + 1].Output = llvm::make_unique<BrokenStream>( + BrokenInputData, Endians[I], Align); + } + } + + void initializeInputFromOutput(uint32_t Align) { + for (uint32_t I = 0; I < NumEndians; ++I) { + Streams[I * 2].Input = + llvm::make_unique<BinaryByteStream>(OutputData, Endians[I]); + Streams[I * 2 + 1].Input = llvm::make_unique<BrokenStream>( + BrokenOutputData, Endians[I], Align); + } + } + + std::vector<uint8_t> InputData; + std::vector<uint8_t> BrokenInputData; + + std::vector<uint8_t> OutputData; + std::vector<uint8_t> BrokenOutputData; + + std::vector<StreamPair> Streams; +}; + +// Tests that a we can read from a BinaryByteStream without a StreamReader. +TEST_F(BinaryStreamTest, BinaryByteStreamBounds) { + std::vector<uint8_t> InputData = {1, 2, 3, 4, 5}; + initializeInput(InputData, 1); + + for (auto &Stream : Streams) { + ArrayRef<uint8_t> Buffer; + + // 1. If the read fits it should work. + ASSERT_EQ(InputData.size(), Stream.Input->getLength()); + ASSERT_NO_ERROR(Stream.Input->readBytes(2, 1, Buffer)); + EXPECT_EQ(makeArrayRef(InputData).slice(2, 1), Buffer); + ASSERT_NO_ERROR(Stream.Input->readBytes(0, 4, Buffer)); + EXPECT_EQ(makeArrayRef(InputData).slice(0, 4), Buffer); + + // 2. Reading past the bounds of the input should fail. + EXPECT_ERROR(Stream.Input->readBytes(4, 2, Buffer)); + } +} + +TEST_F(BinaryStreamTest, StreamRefBounds) { + std::vector<uint8_t> InputData = {1, 2, 3, 4, 5}; + initializeInput(InputData, 1); + + for (const auto &Stream : Streams) { + ArrayRef<uint8_t> Buffer; + BinaryStreamRef Ref(*Stream.Input); + + // Read 1 byte from offset 2 should work + ASSERT_EQ(InputData.size(), Ref.getLength()); + ASSERT_NO_ERROR(Ref.readBytes(2, 1, Buffer)); + EXPECT_EQ(makeArrayRef(InputData).slice(2, 1), Buffer); + + // Reading everything from offset 2 on. + ASSERT_NO_ERROR(Ref.readLongestContiguousChunk(2, Buffer)); + if (Stream.IsContiguous) + EXPECT_EQ(makeArrayRef(InputData).slice(2), Buffer); + else + EXPECT_FALSE(Buffer.empty()); + + // Reading 6 bytes from offset 0 is too big. + EXPECT_ERROR(Ref.readBytes(0, 6, Buffer)); + EXPECT_ERROR(Ref.readLongestContiguousChunk(6, Buffer)); + + // Reading 1 byte from offset 2 after dropping 1 byte is the same as reading + // 1 byte from offset 3. + Ref = Ref.drop_front(1); + ASSERT_NO_ERROR(Ref.readBytes(2, 1, Buffer)); + if (Stream.IsContiguous) + EXPECT_EQ(makeArrayRef(InputData).slice(3, 1), Buffer); + else + EXPECT_FALSE(Buffer.empty()); + + // Reading everything from offset 2 on after dropping 1 byte. + ASSERT_NO_ERROR(Ref.readLongestContiguousChunk(2, Buffer)); + if (Stream.IsContiguous) + EXPECT_EQ(makeArrayRef(InputData).slice(3), Buffer); + else + EXPECT_FALSE(Buffer.empty()); + + // Reading 2 bytes from offset 2 after dropping 2 bytes is the same as + // reading 2 bytes from offset 4, and should fail. + Ref = Ref.drop_front(1); + EXPECT_ERROR(Ref.readBytes(2, 2, Buffer)); + + // But if we read the longest contiguous chunk instead, we should still + // get the 1 byte at the end. + ASSERT_NO_ERROR(Ref.readLongestContiguousChunk(2, Buffer)); + EXPECT_EQ(makeArrayRef(InputData).take_back(), Buffer); + } +} + +// Test that we can write to a BinaryStream without a StreamWriter. +TEST_F(BinaryStreamTest, MutableBinaryByteStreamBounds) { + std::vector<uint8_t> InputData = {'T', 'e', 's', 't', '\0'}; + initializeInput(InputData, 1); + initializeOutput(InputData.size(), 1); + + // For every combination of input stream and output stream. + for (auto &Stream : Streams) { + MutableArrayRef<uint8_t> Buffer; + ASSERT_EQ(InputData.size(), Stream.Input->getLength()); + + // 1. Try two reads that are supposed to work. One from offset 0, and one + // from the middle. + uint32_t Offsets[] = {0, 3}; + for (auto Offset : Offsets) { + uint32_t ExpectedSize = Stream.Input->getLength() - Offset; + + // Read everything from Offset until the end of the input data. + ArrayRef<uint8_t> Data; + ASSERT_NO_ERROR(Stream.Input->readBytes(Offset, ExpectedSize, Data)); + ASSERT_EQ(ExpectedSize, Data.size()); + + // Then write it to the destination. + ASSERT_NO_ERROR(Stream.Output->writeBytes(0, Data)); + + // Then we read back what we wrote, it should match the corresponding + // slice of the original input data. + ArrayRef<uint8_t> Data2; + ASSERT_NO_ERROR(Stream.Output->readBytes(Offset, ExpectedSize, Data2)); + EXPECT_EQ(makeArrayRef(InputData).drop_front(Offset), Data2); + } + + std::vector<uint8_t> BigData = {0, 1, 2, 3, 4}; + // 2. If the write is too big, it should fail. + EXPECT_ERROR(Stream.Output->writeBytes(3, BigData)); + } +} + +// Test that FixedStreamArray works correctly. +TEST_F(BinaryStreamTest, FixedStreamArray) { + std::vector<uint32_t> Ints = {90823, 12908, 109823, 209823}; + ArrayRef<uint8_t> IntBytes(reinterpret_cast<uint8_t *>(Ints.data()), + Ints.size() * sizeof(uint32_t)); + + initializeInput(IntBytes, alignof(uint32_t)); + + for (auto &Stream : Streams) { + MutableArrayRef<uint8_t> Buffer; + ASSERT_EQ(InputData.size(), Stream.Input->getLength()); + + FixedStreamArray<uint32_t> Array(*Stream.Input); + auto Iter = Array.begin(); + ASSERT_EQ(Ints[0], *Iter++); + ASSERT_EQ(Ints[1], *Iter++); + ASSERT_EQ(Ints[2], *Iter++); + ASSERT_EQ(Ints[3], *Iter++); + ASSERT_EQ(Array.end(), Iter); + } +} + +// Test that VarStreamArray works correctly. +TEST_F(BinaryStreamTest, VarStreamArray) { + StringLiteral Strings("1. Test2. Longer Test3. Really Long Test4. Super " + "Extra Longest Test Of All"); + ArrayRef<uint8_t> StringBytes( + reinterpret_cast<const uint8_t *>(Strings.data()), Strings.size()); + initializeInput(StringBytes, 1); + + struct StringExtractor { + public: + Error operator()(BinaryStreamRef Stream, uint32_t &Len, StringRef &Item) { + if (Index == 0) + Len = strlen("1. Test"); + else if (Index == 1) + Len = strlen("2. Longer Test"); + else if (Index == 2) + Len = strlen("3. Really Long Test"); + else + Len = strlen("4. Super Extra Longest Test Of All"); + ArrayRef<uint8_t> Bytes; + if (auto EC = Stream.readBytes(0, Len, Bytes)) + return EC; + Item = + StringRef(reinterpret_cast<const char *>(Bytes.data()), Bytes.size()); + ++Index; + return Error::success(); + } + + private: + uint32_t Index = 0; + }; + + for (auto &Stream : Streams) { + VarStreamArray<StringRef, StringExtractor> Array(*Stream.Input); + auto Iter = Array.begin(); + ASSERT_EQ("1. Test", *Iter++); + ASSERT_EQ("2. Longer Test", *Iter++); + ASSERT_EQ("3. Really Long Test", *Iter++); + ASSERT_EQ("4. Super Extra Longest Test Of All", *Iter++); + ASSERT_EQ(Array.end(), Iter); + } +} + +TEST_F(BinaryStreamTest, StreamReaderBounds) { + std::vector<uint8_t> Bytes; + + initializeInput(Bytes, 1); + for (auto &Stream : Streams) { + StringRef S; + BinaryStreamReader Reader(*Stream.Input); + EXPECT_EQ(0U, Reader.bytesRemaining()); + EXPECT_ERROR(Reader.readFixedString(S, 1)); + } + + Bytes.resize(5); + initializeInput(Bytes, 1); + for (auto &Stream : Streams) { + StringRef S; + BinaryStreamReader Reader(*Stream.Input); + EXPECT_EQ(Bytes.size(), Reader.bytesRemaining()); + EXPECT_NO_ERROR(Reader.readFixedString(S, 5)); + EXPECT_ERROR(Reader.readFixedString(S, 6)); + } +} + +TEST_F(BinaryStreamTest, StreamReaderIntegers) { + support::ulittle64_t Little{908234}; + support::ubig32_t Big{28907823}; + short NS = 2897; + int NI = -89723; + unsigned long NUL = 902309023UL; + constexpr uint32_t Size = + sizeof(Little) + sizeof(Big) + sizeof(NS) + sizeof(NI) + sizeof(NUL); + + initializeOutput(Size, alignof(support::ulittle64_t)); + initializeInputFromOutput(alignof(support::ulittle64_t)); + + for (auto &Stream : Streams) { + BinaryStreamWriter Writer(*Stream.Output); + ASSERT_NO_ERROR(Writer.writeObject(Little)); + ASSERT_NO_ERROR(Writer.writeObject(Big)); + ASSERT_NO_ERROR(Writer.writeInteger(NS)); + ASSERT_NO_ERROR(Writer.writeInteger(NI)); + ASSERT_NO_ERROR(Writer.writeInteger(NUL)); + + const support::ulittle64_t *Little2; + const support::ubig32_t *Big2; + short NS2; + int NI2; + unsigned long NUL2; + + // 1. Reading fields individually. + BinaryStreamReader Reader(*Stream.Input); + ASSERT_NO_ERROR(Reader.readObject(Little2)); + ASSERT_NO_ERROR(Reader.readObject(Big2)); + ASSERT_NO_ERROR(Reader.readInteger(NS2)); + ASSERT_NO_ERROR(Reader.readInteger(NI2)); + ASSERT_NO_ERROR(Reader.readInteger(NUL2)); + ASSERT_EQ(0U, Reader.bytesRemaining()); + + EXPECT_EQ(Little, *Little2); + EXPECT_EQ(Big, *Big2); + EXPECT_EQ(NS, NS2); + EXPECT_EQ(NI, NI2); + EXPECT_EQ(NUL, NUL2); + } +} + +TEST_F(BinaryStreamTest, StreamReaderIntegerArray) { + // 1. Arrays of integers + std::vector<int> Ints = {1, 2, 3, 4, 5}; + ArrayRef<uint8_t> IntBytes(reinterpret_cast<uint8_t *>(&Ints[0]), + Ints.size() * sizeof(int)); + + initializeInput(IntBytes, alignof(int)); + for (auto &Stream : Streams) { + BinaryStreamReader Reader(*Stream.Input); + ArrayRef<int> IntsRef; + ASSERT_NO_ERROR(Reader.readArray(IntsRef, Ints.size())); + ASSERT_EQ(0U, Reader.bytesRemaining()); + EXPECT_EQ(makeArrayRef(Ints), IntsRef); + + Reader.setOffset(0); + FixedStreamArray<int> FixedIntsRef; + ASSERT_NO_ERROR(Reader.readArray(FixedIntsRef, Ints.size())); + ASSERT_EQ(0U, Reader.bytesRemaining()); + ASSERT_EQ(Ints, std::vector<int>(FixedIntsRef.begin(), FixedIntsRef.end())); + } +} + +TEST_F(BinaryStreamTest, StreamReaderEnum) { + enum class MyEnum : int64_t { Foo = -10, Bar = 0, Baz = 10 }; + + std::vector<MyEnum> Enums = {MyEnum::Bar, MyEnum::Baz, MyEnum::Foo}; + + initializeOutput(Enums.size() * sizeof(MyEnum), alignof(MyEnum)); + initializeInputFromOutput(alignof(MyEnum)); + for (auto &Stream : Streams) { + BinaryStreamWriter Writer(*Stream.Output); + for (auto Value : Enums) + ASSERT_NO_ERROR(Writer.writeEnum(Value)); + + BinaryStreamReader Reader(*Stream.Input); + + ArrayRef<MyEnum> Array; + FixedStreamArray<MyEnum> FSA; + + for (size_t I = 0; I < Enums.size(); ++I) { + MyEnum Value; + ASSERT_NO_ERROR(Reader.readEnum(Value)); + EXPECT_EQ(Enums[I], Value); + } + ASSERT_EQ(0U, Reader.bytesRemaining()); + } +} + +TEST_F(BinaryStreamTest, StreamReaderObject) { + struct Foo { + int X; + double Y; + char Z; + + bool operator==(const Foo &Other) const { + return X == Other.X && Y == Other.Y && Z == Other.Z; + } + }; + + std::vector<Foo> Foos; + Foos.push_back({-42, 42.42, 42}); + Foos.push_back({100, 3.1415, static_cast<char>(-89)}); + Foos.push_back({200, 2.718, static_cast<char>(-12) }); + + const uint8_t *Bytes = reinterpret_cast<const uint8_t *>(&Foos[0]); + + initializeInput(makeArrayRef(Bytes, 3 * sizeof(Foo)), alignof(Foo)); + + for (auto &Stream : Streams) { + // 1. Reading object pointers. + BinaryStreamReader Reader(*Stream.Input); + const Foo *FPtrOut = nullptr; + const Foo *GPtrOut = nullptr; + const Foo *HPtrOut = nullptr; + ASSERT_NO_ERROR(Reader.readObject(FPtrOut)); + ASSERT_NO_ERROR(Reader.readObject(GPtrOut)); + ASSERT_NO_ERROR(Reader.readObject(HPtrOut)); + EXPECT_EQ(0U, Reader.bytesRemaining()); + EXPECT_EQ(Foos[0], *FPtrOut); + EXPECT_EQ(Foos[1], *GPtrOut); + EXPECT_EQ(Foos[2], *HPtrOut); + } +} + +TEST_F(BinaryStreamTest, StreamReaderStrings) { + std::vector<uint8_t> Bytes = {'O', 'n', 'e', '\0', 'T', 'w', 'o', + '\0', 'T', 'h', 'r', 'e', 'e', '\0', + 'F', 'o', 'u', 'r', '\0'}; + initializeInput(Bytes, 1); + + for (auto &Stream : Streams) { + BinaryStreamReader Reader(*Stream.Input); + + StringRef S1; + StringRef S2; + StringRef S3; + StringRef S4; + ASSERT_NO_ERROR(Reader.readCString(S1)); + ASSERT_NO_ERROR(Reader.readCString(S2)); + ASSERT_NO_ERROR(Reader.readCString(S3)); + ASSERT_NO_ERROR(Reader.readCString(S4)); + ASSERT_EQ(0U, Reader.bytesRemaining()); + + EXPECT_EQ("One", S1); + EXPECT_EQ("Two", S2); + EXPECT_EQ("Three", S3); + EXPECT_EQ("Four", S4); + + S1 = S2 = S3 = S4 = ""; + Reader.setOffset(0); + ASSERT_NO_ERROR(Reader.readFixedString(S1, 3)); + ASSERT_NO_ERROR(Reader.skip(1)); + ASSERT_NO_ERROR(Reader.readFixedString(S2, 3)); + ASSERT_NO_ERROR(Reader.skip(1)); + ASSERT_NO_ERROR(Reader.readFixedString(S3, 5)); + ASSERT_NO_ERROR(Reader.skip(1)); + ASSERT_NO_ERROR(Reader.readFixedString(S4, 4)); + ASSERT_NO_ERROR(Reader.skip(1)); + ASSERT_EQ(0U, Reader.bytesRemaining()); + + EXPECT_EQ("One", S1); + EXPECT_EQ("Two", S2); + EXPECT_EQ("Three", S3); + EXPECT_EQ("Four", S4); + } +} + +TEST_F(BinaryStreamTest, StreamWriterBounds) { + initializeOutput(5, 1); + + for (auto &Stream : Streams) { + BinaryStreamWriter Writer(*Stream.Output); + + // 1. Can write a string that exactly fills the buffer. + EXPECT_EQ(5U, Writer.bytesRemaining()); + EXPECT_NO_ERROR(Writer.writeFixedString("abcde")); + EXPECT_EQ(0U, Writer.bytesRemaining()); + + // 2. Can write an empty string even when you're full + EXPECT_NO_ERROR(Writer.writeFixedString("")); + EXPECT_ERROR(Writer.writeFixedString("a")); + + // 3. Can't write a string that is one character too long. + Writer.setOffset(0); + EXPECT_ERROR(Writer.writeFixedString("abcdef")); + } +} + +TEST_F(BinaryStreamTest, StreamWriterIntegerArrays) { + // 3. Arrays of integers + std::vector<int> SourceInts = {1, 2, 3, 4, 5}; + ArrayRef<uint8_t> SourceBytes(reinterpret_cast<uint8_t *>(&SourceInts[0]), + SourceInts.size() * sizeof(int)); + + initializeInput(SourceBytes, alignof(int)); + initializeOutputFromInput(alignof(int)); + + for (auto &Stream : Streams) { + BinaryStreamReader Reader(*Stream.Input); + BinaryStreamWriter Writer(*Stream.Output); + ArrayRef<int> Ints; + ArrayRef<int> Ints2; + // First read them, then write them, then read them back. + ASSERT_NO_ERROR(Reader.readArray(Ints, SourceInts.size())); + ASSERT_NO_ERROR(Writer.writeArray(Ints)); + + BinaryStreamReader ReaderBacker(*Stream.Output); + ASSERT_NO_ERROR(ReaderBacker.readArray(Ints2, SourceInts.size())); + + EXPECT_EQ(makeArrayRef(SourceInts), Ints2); + } +} + +TEST_F(BinaryStreamTest, StringWriterStrings) { + StringRef Strings[] = {"First", "Second", "Third", "Fourth"}; + + size_t Length = 0; + for (auto S : Strings) + Length += S.size() + 1; + initializeOutput(Length, 1); + initializeInputFromOutput(1); + + for (auto &Stream : Streams) { + BinaryStreamWriter Writer(*Stream.Output); + for (auto S : Strings) + ASSERT_NO_ERROR(Writer.writeCString(S)); + std::vector<StringRef> InStrings; + BinaryStreamReader Reader(*Stream.Input); + while (!Reader.empty()) { + StringRef S; + ASSERT_NO_ERROR(Reader.readCString(S)); + InStrings.push_back(S); + } + EXPECT_EQ(makeArrayRef(Strings), makeArrayRef(InStrings)); + } +} +} + +namespace { +struct BinaryItemStreamObject { + explicit BinaryItemStreamObject(ArrayRef<uint8_t> Bytes) : Bytes(Bytes) {} + + ArrayRef<uint8_t> Bytes; +}; +} + +namespace llvm { +template <> struct BinaryItemTraits<BinaryItemStreamObject> { + static size_t length(const BinaryItemStreamObject &Item) { + return Item.Bytes.size(); + } + + static ArrayRef<uint8_t> bytes(const BinaryItemStreamObject &Item) { + return Item.Bytes; + } +}; +} + +namespace { + +TEST_F(BinaryStreamTest, BinaryItemStream) { + std::vector<BinaryItemStreamObject> Objects; + + struct Foo { + int X; + double Y; + }; + std::vector<Foo> Foos = {{1, 1.0}, {2, 2.0}, {3, 3.0}}; + BumpPtrAllocator Allocator; + for (const auto &F : Foos) { + uint8_t *Ptr = static_cast<uint8_t *>(Allocator.Allocate(sizeof(Foo), + alignof(Foo))); + MutableArrayRef<uint8_t> Buffer(Ptr, sizeof(Foo)); + MutableBinaryByteStream Stream(Buffer, llvm::support::big); + BinaryStreamWriter Writer(Stream); + ASSERT_NO_ERROR(Writer.writeObject(F)); + Objects.push_back(BinaryItemStreamObject(Buffer)); + } + + BinaryItemStream<BinaryItemStreamObject> ItemStream(big); + ItemStream.setItems(Objects); + BinaryStreamReader Reader(ItemStream); + + for (const auto &F : Foos) { + const Foo *F2; + ASSERT_NO_ERROR(Reader.readObject(F2)); + + EXPECT_EQ(F.X, F2->X); + EXPECT_DOUBLE_EQ(F.Y, F2->Y); + } +} + +} // end anonymous namespace diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt index 6068de5514c7f..a7be18b6a3c53 100644 --- a/unittests/Support/CMakeLists.txt +++ b/unittests/Support/CMakeLists.txt @@ -5,9 +5,12 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(SupportTests AlignOfTest.cpp AllocatorTest.cpp + ARMAttributeParser.cpp ArrayRecyclerTest.cpp + BinaryStreamTest.cpp BlockFrequencyTest.cpp BranchProbabilityTest.cpp + CachePruningTest.cpp Casting.cpp Chrono.cpp CommandLineTest.cpp @@ -63,4 +66,4 @@ add_llvm_unittest(SupportTests ) # ManagedStatic.cpp uses <pthread>. -target_link_libraries(SupportTests ${PTHREAD_LIB}) +target_link_libraries(SupportTests ${LLVM_PTHREAD_LIB}) diff --git a/unittests/Support/CachePruningTest.cpp b/unittests/Support/CachePruningTest.cpp new file mode 100644 index 0000000000000..04ac0d09b4935 --- /dev/null +++ b/unittests/Support/CachePruningTest.cpp @@ -0,0 +1,71 @@ +//===- CachePruningTest.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CachePruning.h" +#include "llvm/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(CachePruningPolicyParser, Empty) { + auto P = parseCachePruningPolicy(""); + ASSERT_TRUE(bool(P)); + EXPECT_EQ(std::chrono::seconds(1200), P->Interval); + EXPECT_EQ(std::chrono::hours(7 * 24), P->Expiration); + EXPECT_EQ(75u, P->PercentageOfAvailableSpace); +} + +TEST(CachePruningPolicyParser, Interval) { + auto P = parseCachePruningPolicy("prune_interval=1s"); + ASSERT_TRUE(bool(P)); + EXPECT_EQ(std::chrono::seconds(1), P->Interval); + P = parseCachePruningPolicy("prune_interval=2m"); + ASSERT_TRUE(bool(P)); + EXPECT_EQ(std::chrono::minutes(2), P->Interval); + P = parseCachePruningPolicy("prune_interval=3h"); + ASSERT_TRUE(bool(P)); + EXPECT_EQ(std::chrono::hours(3), P->Interval); +} + +TEST(CachePruningPolicyParser, Expiration) { + auto P = parseCachePruningPolicy("prune_after=1s"); + ASSERT_TRUE(bool(P)); + EXPECT_EQ(std::chrono::seconds(1), P->Expiration); +} + +TEST(CachePruningPolicyParser, PercentageOfAvailableSpace) { + auto P = parseCachePruningPolicy("cache_size=100%"); + ASSERT_TRUE(bool(P)); + EXPECT_EQ(100u, P->PercentageOfAvailableSpace); +} + +TEST(CachePruningPolicyParser, Multiple) { + auto P = parseCachePruningPolicy("prune_after=1s:cache_size=50%"); + ASSERT_TRUE(bool(P)); + EXPECT_EQ(std::chrono::seconds(1200), P->Interval); + EXPECT_EQ(std::chrono::seconds(1), P->Expiration); + EXPECT_EQ(50u, P->PercentageOfAvailableSpace); +} + +TEST(CachePruningPolicyParser, Errors) { + EXPECT_EQ("Duration must not be empty", + toString(parseCachePruningPolicy("prune_interval=").takeError())); + EXPECT_EQ("'foo' not an integer", + toString(parseCachePruningPolicy("prune_interval=foos").takeError())); + EXPECT_EQ("'24x' must end with one of 's', 'm' or 'h'", + toString(parseCachePruningPolicy("prune_interval=24x").takeError())); + EXPECT_EQ("'foo' must be a percentage", + toString(parseCachePruningPolicy("cache_size=foo").takeError())); + EXPECT_EQ("'foo' not an integer", + toString(parseCachePruningPolicy("cache_size=foo%").takeError())); + EXPECT_EQ("'101' must be between 0 and 100", + toString(parseCachePruningPolicy("cache_size=101%").takeError())); + EXPECT_EQ("Unknown key: 'foo'", + toString(parseCachePruningPolicy("foo=bar").takeError())); +} diff --git a/unittests/Support/Casting.cpp b/unittests/Support/Casting.cpp index e6c35fc21eb7d..9a818f6bdebde 100644 --- a/unittests/Support/Casting.cpp +++ b/unittests/Support/Casting.cpp @@ -40,6 +40,14 @@ struct foo { }*/ }; +struct base { + virtual ~base() {} +}; + +struct derived : public base { + static bool classof(const base *B) { return true; } +}; + template <> struct isa_impl<foo, bar> { static inline bool doit(const bar &Val) { dbgs() << "Classof: " << &Val << "\n"; @@ -47,6 +55,10 @@ template <> struct isa_impl<foo, bar> { } }; +template <typename T> struct isa_impl<foo, T> { + static inline bool doit(const T &Val) { return false; } +}; + foo *bar::baz() { return cast<foo>(this); } @@ -123,6 +135,13 @@ TEST(CastingTest, cast) { // EXPECT_EQ(F7, null_foo); foo *F8 = B1.baz(); EXPECT_NE(F8, null_foo); + + std::unique_ptr<const bar> BP(B2); + auto FP = cast<foo>(std::move(BP)); + static_assert(std::is_same<std::unique_ptr<const foo>, decltype(FP)>::value, + "Incorrect deduced return type!"); + EXPECT_NE(FP.get(), null_foo); + FP.release(); } TEST(CastingTest, cast_or_null) { @@ -136,6 +155,10 @@ TEST(CastingTest, cast_or_null) { EXPECT_EQ(F14, null_foo); foo *F15 = B1.caz(); EXPECT_NE(F15, null_foo); + + std::unique_ptr<const bar> BP(fub()); + auto FP = cast_or_null<foo>(std::move(BP)); + EXPECT_EQ(FP.get(), null_foo); } TEST(CastingTest, dyn_cast) { @@ -165,6 +188,58 @@ TEST(CastingTest, dyn_cast_or_null) { EXPECT_NE(F5, null_foo); } +std::unique_ptr<derived> newd() { return llvm::make_unique<derived>(); } +std::unique_ptr<base> newb() { return llvm::make_unique<derived>(); } + +TEST(CastingTest, unique_dyn_cast) { + derived *OrigD = nullptr; + auto D = llvm::make_unique<derived>(); + OrigD = D.get(); + + // Converting from D to itself is valid, it should return a new unique_ptr + // and the old one should become nullptr. + auto NewD = unique_dyn_cast<derived>(D); + ASSERT_EQ(OrigD, NewD.get()); + ASSERT_EQ(nullptr, D); + + // Converting from D to B is valid, B should have a value and D should be + // nullptr. + auto B = unique_dyn_cast<base>(NewD); + ASSERT_EQ(OrigD, B.get()); + ASSERT_EQ(nullptr, NewD); + + // Converting from B to itself is valid, it should return a new unique_ptr + // and the old one should become nullptr. + auto NewB = unique_dyn_cast<base>(B); + ASSERT_EQ(OrigD, NewB.get()); + ASSERT_EQ(nullptr, B); + + // Converting from B to D is valid, D should have a value and B should be + // nullptr; + D = unique_dyn_cast<derived>(NewB); + ASSERT_EQ(OrigD, D.get()); + ASSERT_EQ(nullptr, NewB); + + // Converting between unrelated types should fail. The original value should + // remain unchanged and it should return nullptr. + auto F = unique_dyn_cast<foo>(D); + ASSERT_EQ(nullptr, F); + ASSERT_EQ(OrigD, D.get()); + + // All of the above should also hold for temporaries. + auto D2 = unique_dyn_cast<derived>(newd()); + EXPECT_NE(nullptr, D2); + + auto B2 = unique_dyn_cast<derived>(newb()); + EXPECT_NE(nullptr, B2); + + auto B3 = unique_dyn_cast<base>(newb()); + EXPECT_NE(nullptr, B3); + + auto F2 = unique_dyn_cast<foo>(newb()); + EXPECT_EQ(nullptr, F2); +} + // These lines are errors... //foo *F20 = cast<foo>(B2); // Yields const foo* //foo &F21 = cast<foo>(B3); // Yields const foo& diff --git a/unittests/Support/Chrono.cpp b/unittests/Support/Chrono.cpp index 3d5787807563e..1410baf848bb8 100644 --- a/unittests/Support/Chrono.cpp +++ b/unittests/Support/Chrono.cpp @@ -9,6 +9,7 @@ #include "llvm/Support/Chrono.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/FormatVariadic.h" #include "gtest/gtest.h" using namespace llvm; @@ -76,4 +77,34 @@ TEST(Chrono, ImplicitConversions) { EXPECT_EQ(TimeT, toTimeT(Nano)); } +TEST(Chrono, DurationFormat) { + EXPECT_EQ("1 h", formatv("{0}", hours(1)).str()); + EXPECT_EQ("1 m", formatv("{0}", minutes(1)).str()); + EXPECT_EQ("1 s", formatv("{0}", seconds(1)).str()); + EXPECT_EQ("1 ms", formatv("{0}", milliseconds(1)).str()); + EXPECT_EQ("1 us", formatv("{0}", microseconds(1)).str()); + EXPECT_EQ("1 ns", formatv("{0}", nanoseconds(1)).str()); + + EXPECT_EQ("1 s", formatv("{0:+}", seconds(1)).str()); + EXPECT_EQ("1", formatv("{0:-}", seconds(1)).str()); + + EXPECT_EQ("1000 ms", formatv("{0:ms}", seconds(1)).str()); + EXPECT_EQ("1000000 us", formatv("{0:us}", seconds(1)).str()); + EXPECT_EQ("1000", formatv("{0:ms-}", seconds(1)).str()); + + EXPECT_EQ("1,000 ms", formatv("{0:+n}", milliseconds(1000)).str()); + EXPECT_EQ("0x3e8", formatv("{0:-x}", milliseconds(1000)).str()); + EXPECT_EQ("010", formatv("{0:-3}", milliseconds(10)).str()); + EXPECT_EQ("10,000", formatv("{0:ms-n}", seconds(10)).str()); + + EXPECT_EQ("1.00 s", formatv("{0}", duration<float>(1)).str()); + EXPECT_EQ("0.123 s", formatv("{0:+3}", duration<float>(0.123f)).str()); + EXPECT_EQ("1.230e-01 s", formatv("{0:+e3}", duration<float>(0.123f)).str()); + + typedef duration<float, std::ratio<60 * 60 * 24 * 14, 1000000>> + microfortnights; + EXPECT_EQ("1.00", formatv("{0:-}", microfortnights(1)).str()); + EXPECT_EQ("1209.60 ms", formatv("{0:ms}", microfortnights(1)).str()); +} + } // anonymous namespace diff --git a/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp index 945eb1d4e1cfe..33573c4e69607 100644 --- a/unittests/Support/CommandLineTest.cpp +++ b/unittests/Support/CommandLineTest.cpp @@ -303,7 +303,8 @@ TEST(CommandLineTest, SetValueInSubcategories) { EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args[] = {"prog", "-top-level"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelOpt); EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); @@ -315,7 +316,8 @@ TEST(CommandLineTest, SetValueInSubcategories) { EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args2[] = {"prog", "sc1", "-sc1"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); EXPECT_FALSE(TopLevelOpt); EXPECT_TRUE(SC1Opt); EXPECT_FALSE(SC2Opt); @@ -327,7 +329,8 @@ TEST(CommandLineTest, SetValueInSubcategories) { EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args3[] = {"prog", "sc2", "-sc2"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args3, StringRef(), &llvm::nulls())); EXPECT_FALSE(TopLevelOpt); EXPECT_FALSE(SC1Opt); EXPECT_TRUE(SC2Opt); @@ -342,8 +345,13 @@ TEST(CommandLineTest, LookupFailsInWrongSubCommand) { StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false)); StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); + std::string Errs; + raw_string_ostream OS(Errs); + const char *args[] = {"prog", "sc1", "-sc2"}; - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); + OS.flush(); + EXPECT_FALSE(Errs.empty()); } TEST(CommandLineTest, AddToAllSubCommands) { @@ -358,23 +366,30 @@ TEST(CommandLineTest, AddToAllSubCommands) { const char *args2[] = {"prog", "sc1", "-everywhere"}; const char *args3[] = {"prog", "sc2", "-everywhere"}; + std::string Errs; + raw_string_ostream OS(Errs); + EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); EXPECT_TRUE(AllOpt); AllOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); EXPECT_TRUE(AllOpt); AllOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); EXPECT_TRUE(AllOpt); + + // Since all parsing succeeded, the error message should be empty. + OS.flush(); + EXPECT_TRUE(Errs.empty()); } TEST(CommandLineTest, ReparseCommandLineOptions) { @@ -386,14 +401,16 @@ TEST(CommandLineTest, ReparseCommandLineOptions) { const char *args[] = {"prog", "-top-level"}; EXPECT_FALSE(TopLevelOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelOpt); TopLevelOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(TopLevelOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelOpt); } @@ -406,14 +423,21 @@ TEST(CommandLineTest, RemoveFromRegularSubCommand) { const char *args[] = {"prog", "sc", "-remove-option"}; + std::string Errs; + raw_string_ostream OS(Errs); + EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); EXPECT_TRUE(RemoveOption); + OS.flush(); + EXPECT_TRUE(Errs.empty()); RemoveOption.removeArgument(); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); + OS.flush(); + EXPECT_FALSE(Errs.empty()); } TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { @@ -427,13 +451,15 @@ TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { const char *args[] = {"prog", "-top-level-remove"}; EXPECT_FALSE(TopLevelRemove); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelRemove); TopLevelRemove.removeArgument(); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); } TEST(CommandLineTest, RemoveFromAllSubCommands) { @@ -452,32 +478,38 @@ TEST(CommandLineTest, RemoveFromAllSubCommands) { // It should work for all subcommands including the top-level. EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); EXPECT_TRUE(RemoveOption); RemoveOption = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); EXPECT_TRUE(RemoveOption); RemoveOption = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); EXPECT_TRUE(RemoveOption); RemoveOption.removeArgument(); // It should not work for any subcommands including the top-level. cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); } TEST(CommandLineTest, GetRegisteredSubcommands) { @@ -491,7 +523,8 @@ TEST(CommandLineTest, GetRegisteredSubcommands) { const char *args0[] = {"prog", "sc1"}; const char *args1[] = {"prog", "sc2"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); EXPECT_FALSE(Opt1); EXPECT_FALSE(Opt2); for (auto *S : cl::getRegisteredSubcommands()) { @@ -500,7 +533,8 @@ TEST(CommandLineTest, GetRegisteredSubcommands) { } cl::ResetAllOptionOccurrences(); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls())); EXPECT_FALSE(Opt1); EXPECT_FALSE(Opt2); for (auto *S : cl::getRegisteredSubcommands()) { diff --git a/unittests/Support/CompressionTest.cpp b/unittests/Support/CompressionTest.cpp index 36b84d85f22be..18a6175460d36 100644 --- a/unittests/Support/CompressionTest.cpp +++ b/unittests/Support/CompressionTest.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Compression.h" +#include "llvm/Support/Error.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Config/config.h" @@ -26,15 +27,21 @@ namespace { void TestZlibCompression(StringRef Input, zlib::CompressionLevel Level) { SmallString<32> Compressed; SmallString<32> Uncompressed; - EXPECT_EQ(zlib::StatusOK, zlib::compress(Input, Compressed, Level)); + + Error E = zlib::compress(Input, Compressed, Level); + EXPECT_FALSE(E); + consumeError(std::move(E)); + // Check that uncompressed buffer is the same as original. - EXPECT_EQ(zlib::StatusOK, - zlib::uncompress(Compressed, Uncompressed, Input.size())); + E = zlib::uncompress(Compressed, Uncompressed, Input.size()); + EXPECT_FALSE(E); + consumeError(std::move(E)); + EXPECT_EQ(Input, Uncompressed); if (Input.size() > 0) { // Uncompression fails if expected length is too short. - EXPECT_EQ(zlib::StatusBufferTooShort, - zlib::uncompress(Compressed, Uncompressed, Input.size() - 1)); + E = zlib::uncompress(Compressed, Uncompressed, Input.size() - 1); + EXPECT_EQ("zlib error: Z_BUF_ERROR", llvm::toString(std::move(E))); } } diff --git a/unittests/Support/ErrorTest.cpp b/unittests/Support/ErrorTest.cpp index 29a173a058b61..382346cd231ac 100644 --- a/unittests/Support/ErrorTest.cpp +++ b/unittests/Support/ErrorTest.cpp @@ -469,6 +469,34 @@ TEST(Error, ExitOnError) { << "exitOnError returned an unexpected error result"; } +// Test that the ExitOnError utility works as expected. +TEST(Error, CantFailSuccess) { + cantFail(Error::success()); + + int X = cantFail(Expected<int>(42)); + EXPECT_EQ(X, 42) << "Expected value modified by cantFail"; +} + +// Test that cantFail results in a crash if you pass it a failure value. +#if LLVM_ENABLE_ABI_BREAKING_CHECKS +TEST(Error, CantFailDeath) { + EXPECT_DEATH( + cantFail(make_error<StringError>("foo", inconvertibleErrorCode())), + "Failure value returned from cantFail wrapped call") + << "cantFail(Error) did not cause an abort for failure value"; + + EXPECT_DEATH( + { + auto IEC = inconvertibleErrorCode(); + int X = cantFail(Expected<int>(make_error<StringError>("foo", IEC))); + (void)X; + }, + "Failure value returned from cantFail wrapped call") + << "cantFail(Expected<int>) did not cause an abort for failure value"; +} +#endif + + // Test Checked Expected<T> in success mode. TEST(Error, CheckedExpectedInSuccessMode) { Expected<int> A = 7; diff --git a/unittests/Support/FormatVariadicTest.cpp b/unittests/Support/FormatVariadicTest.cpp index 9307c6d8e09b5..b0c843870afc2 100644 --- a/unittests/Support/FormatVariadicTest.cpp +++ b/unittests/Support/FormatVariadicTest.cpp @@ -324,11 +324,13 @@ TEST(FormatVariadicTest, StringFormatting) { const char FooArray[] = "FooArray"; const char *FooPtr = "FooPtr"; llvm::StringRef FooRef("FooRef"); + constexpr StringLiteral FooLiteral("FooLiteral"); std::string FooString("FooString"); // 1. Test that we can print various types of strings. EXPECT_EQ(FooArray, formatv("{0}", FooArray).str()); EXPECT_EQ(FooPtr, formatv("{0}", FooPtr).str()); EXPECT_EQ(FooRef, formatv("{0}", FooRef).str()); + EXPECT_EQ(FooLiteral, formatv("{0}", FooLiteral).str()); EXPECT_EQ(FooString, formatv("{0}", FooString).str()); // 2. Test that the precision specifier prints the correct number of diff --git a/unittests/Support/Host.cpp b/unittests/Support/Host.cpp index 934a604954272..fd53697793c7e 100644 --- a/unittests/Support/Host.cpp +++ b/unittests/Support/Host.cpp @@ -17,25 +17,17 @@ using namespace llvm; class HostTest : public testing::Test { Triple Host; - SmallVector<std::pair<Triple::ArchType, Triple::OSType>, 4> SupportedArchAndOSs; protected: bool isSupportedArchAndOS() { - if (is_contained(SupportedArchAndOSs, std::make_pair(Host.getArch(), Host.getOS()))) - return true; - - return false; - } - - HostTest() { - Host.setTriple(Triple::normalize(sys::getProcessTriple())); - // Initially this is only testing detection of the number of // physical cores, which is currently only supported/tested for // x86_64 Linux and Darwin. - SupportedArchAndOSs.push_back(std::make_pair(Triple::x86_64, Triple::Linux)); - SupportedArchAndOSs.push_back(std::make_pair(Triple::x86_64, Triple::Darwin)); + return (Host.getArch() == Triple::x86_64 && + (Host.isOSDarwin() || Host.getOS() == Triple::Linux)); } + + HostTest() : Host(Triple::normalize(sys::getProcessTriple())) {} }; TEST_F(HostTest, NumPhysicalCores) { @@ -46,3 +38,79 @@ TEST_F(HostTest, NumPhysicalCores) { else ASSERT_EQ(Num, -1); } + +TEST(getLinuxHostCPUName, ARM) { + StringRef CortexA9ProcCpuinfo = R"( +processor : 0 +model name : ARMv7 Processor rev 10 (v7l) +BogoMIPS : 1393.66 +Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 +CPU implementer : 0x41 +CPU architecture: 7 +CPU variant : 0x2 +CPU part : 0xc09 +CPU revision : 10 + +processor : 1 +model name : ARMv7 Processor rev 10 (v7l) +BogoMIPS : 1393.66 +Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 +CPU implementer : 0x41 +CPU architecture: 7 +CPU variant : 0x2 +CPU part : 0xc09 +CPU revision : 10 + +Hardware : Generic OMAP4 (Flattened Device Tree) +Revision : 0000 +Serial : 0000000000000000 +)"; + + EXPECT_EQ(sys::detail::getHostCPUNameForARM(CortexA9ProcCpuinfo), + "cortex-a9"); + EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x41\n" + "CPU part : 0xc0f"), + "cortex-a15"); + // Verify that both CPU implementer and CPU part are checked: + EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x40\n" + "CPU part : 0xc0f"), + "generic"); + EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n" + "CPU part : 0x06f"), + "krait"); +} + +TEST(getLinuxHostCPUName, AArch64) { + EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x41\n" + "CPU part : 0xd03"), + "cortex-a53"); + // Verify that both CPU implementer and CPU part are checked: + EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x40\n" + "CPU part : 0xd03"), + "generic"); + EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n" + "CPU part : 0x201"), + "kryo"); + + // MSM8992/4 weirdness + StringRef MSM8992ProcCpuInfo = R"( +Processor : AArch64 Processor rev 3 (aarch64) +processor : 0 +processor : 1 +processor : 2 +processor : 3 +processor : 4 +processor : 5 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 +CPU implementer : 0x41 +CPU architecture: 8 +CPU variant : 0x0 +CPU part : 0xd03 +CPU revision : 3 + +Hardware : Qualcomm Technologies, Inc MSM8992 +)"; + + EXPECT_EQ(sys::detail::getHostCPUNameForARM(MSM8992ProcCpuInfo), + "cortex-a53"); +} diff --git a/unittests/Support/LEB128Test.cpp b/unittests/Support/LEB128Test.cpp index 76b63e5a83817..061936df1d19d 100644 --- a/unittests/Support/LEB128Test.cpp +++ b/unittests/Support/LEB128Test.cpp @@ -17,26 +17,45 @@ using namespace llvm; namespace { TEST(LEB128Test, EncodeSLEB128) { -#define EXPECT_SLEB128_EQ(EXPECTED, VALUE) \ +#define EXPECT_SLEB128_EQ(EXPECTED, VALUE, PAD) \ do { \ - /* encodeSLEB128(uint64_t, raw_ostream &) */ \ std::string Expected(EXPECTED, sizeof(EXPECTED) - 1); \ - std::string Actual; \ - raw_string_ostream Stream(Actual); \ - encodeSLEB128(VALUE, Stream); \ + \ + /* encodeSLEB128(uint64_t, raw_ostream &, unsigned) */ \ + std::string Actual1; \ + raw_string_ostream Stream(Actual1); \ + encodeSLEB128(VALUE, Stream, PAD); \ Stream.flush(); \ - EXPECT_EQ(Expected, Actual); \ + EXPECT_EQ(Expected, Actual1); \ + \ + /* encodeSLEB128(uint64_t, uint8_t *, unsigned) */ \ + uint8_t Buffer[32]; \ + unsigned Size = encodeSLEB128(VALUE, Buffer, PAD); \ + std::string Actual2(reinterpret_cast<const char *>(Buffer), Size); \ + EXPECT_EQ(Expected, Actual2); \ } while (0) // Encode SLEB128 - EXPECT_SLEB128_EQ("\x00", 0); - EXPECT_SLEB128_EQ("\x01", 1); - EXPECT_SLEB128_EQ("\x7f", -1); - EXPECT_SLEB128_EQ("\x3f", 63); - EXPECT_SLEB128_EQ("\x41", -63); - EXPECT_SLEB128_EQ("\x40", -64); - EXPECT_SLEB128_EQ("\xbf\x7f", -65); - EXPECT_SLEB128_EQ("\xc0\x00", 64); + EXPECT_SLEB128_EQ("\x00", 0, 0); + EXPECT_SLEB128_EQ("\x01", 1, 0); + EXPECT_SLEB128_EQ("\x7f", -1, 0); + EXPECT_SLEB128_EQ("\x3f", 63, 0); + EXPECT_SLEB128_EQ("\x41", -63, 0); + EXPECT_SLEB128_EQ("\x40", -64, 0); + EXPECT_SLEB128_EQ("\xbf\x7f", -65, 0); + EXPECT_SLEB128_EQ("\xc0\x00", 64, 0); + + // Encode SLEB128 with some extra padding bytes + EXPECT_SLEB128_EQ("\x80\x00", 0, 1); + EXPECT_SLEB128_EQ("\x80\x80\x00", 0, 2); + EXPECT_SLEB128_EQ("\xff\x80\x00", 0x7f, 1); + EXPECT_SLEB128_EQ("\xff\x80\x80\x00", 0x7f, 2); + EXPECT_SLEB128_EQ("\x80\x81\x00", 0x80, 1); + EXPECT_SLEB128_EQ("\x80\x81\x80\x00", 0x80, 2); + EXPECT_SLEB128_EQ("\xc0\x7f", -0x40, 1); + EXPECT_SLEB128_EQ("\xc0\xff\x7f", -0x40, 2); + EXPECT_SLEB128_EQ("\x80\xff\x7f", -0x80, 1); + EXPECT_SLEB128_EQ("\x80\xff\xff\x7f", -0x80, 2); #undef EXPECT_SLEB128_EQ } diff --git a/unittests/Support/MD5Test.cpp b/unittests/Support/MD5Test.cpp index 4d790254503e0..fa9372fde33fa 100644 --- a/unittests/Support/MD5Test.cpp +++ b/unittests/Support/MD5Test.cpp @@ -63,8 +63,10 @@ TEST(MD5HashTest, MD5) { std::array<uint8_t, 16> Vec = MD5::hash(Input); MD5::MD5Result MD5Res; SmallString<32> Res; - memcpy(MD5Res, Vec.data(), Vec.size()); + memcpy(MD5Res.Bytes.data(), Vec.data(), Vec.size()); MD5::stringifyResult(MD5Res, Res); EXPECT_EQ(Res, "c3fcd3d76192e4007dfb496cca67e13b"); + EXPECT_EQ(0x3be167ca6c49fb7dULL, MD5Res.high()); + EXPECT_EQ(0x00e49261d7d3fcc3ULL, MD5Res.low()); } } diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index 30eaa8b278abf..86ad57f3f3ffc 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -8,12 +8,15 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Path.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" @@ -25,6 +28,7 @@ #endif #ifdef LLVM_ON_UNIX +#include <pwd.h> #include <sys/stat.h> #endif @@ -50,6 +54,9 @@ TEST(is_separator, Works) { EXPECT_FALSE(path::is_separator('-')); EXPECT_FALSE(path::is_separator(' ')); + EXPECT_TRUE(path::is_separator('\\', path::Style::windows)); + EXPECT_FALSE(path::is_separator('\\', path::Style::posix)); + #ifdef LLVM_ON_WIN32 EXPECT_TRUE(path::is_separator('\\')); #else @@ -249,7 +256,6 @@ TEST(Support, AbsolutePathDotIterator) { } } -#ifdef LLVM_ON_WIN32 TEST(Support, AbsolutePathIteratorWin32) { SmallString<64> Path(StringRef("c:\\c\\e\\foo.txt")); typedef SmallVector<StringRef, 4> PathComponents; @@ -262,8 +268,9 @@ TEST(Support, AbsolutePathIteratorWin32) { // when iterating. ExpectedPathComponents.insert(ExpectedPathComponents.begin()+1, "\\"); - for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E; - ++I) { + for (path::const_iterator I = path::begin(Path, path::Style::windows), + E = path::end(Path); + I != E; ++I) { ActualPathComponents.push_back(*I); } @@ -273,34 +280,29 @@ TEST(Support, AbsolutePathIteratorWin32) { EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str()); } } -#endif // LLVM_ON_WIN32 TEST(Support, AbsolutePathIteratorEnd) { // Trailing slashes are converted to '.' unless they are part of the root path. - SmallVector<StringRef, 4> Paths; - Paths.push_back("/foo/"); - Paths.push_back("/foo//"); - Paths.push_back("//net//"); -#ifdef LLVM_ON_WIN32 - Paths.push_back("c:\\\\"); -#endif - - for (StringRef Path : Paths) { - StringRef LastComponent = *path::rbegin(Path); + SmallVector<std::pair<StringRef, path::Style>, 4> Paths; + Paths.emplace_back("/foo/", path::Style::native); + Paths.emplace_back("/foo//", path::Style::native); + Paths.emplace_back("//net//", path::Style::native); + Paths.emplace_back("c:\\\\", path::Style::windows); + + for (auto &Path : Paths) { + StringRef LastComponent = *path::rbegin(Path.first, Path.second); EXPECT_EQ(".", LastComponent); } - SmallVector<StringRef, 3> RootPaths; - RootPaths.push_back("/"); - RootPaths.push_back("//net/"); -#ifdef LLVM_ON_WIN32 - RootPaths.push_back("c:\\"); -#endif + SmallVector<std::pair<StringRef, path::Style>, 3> RootPaths; + RootPaths.emplace_back("/", path::Style::native); + RootPaths.emplace_back("//net/", path::Style::native); + RootPaths.emplace_back("c:\\", path::Style::windows); - for (StringRef Path : RootPaths) { - StringRef LastComponent = *path::rbegin(Path); + for (auto &Path : RootPaths) { + StringRef LastComponent = *path::rbegin(Path.first, Path.second); EXPECT_EQ(1u, LastComponent.size()); - EXPECT_TRUE(path::is_separator(LastComponent[0])); + EXPECT_TRUE(path::is_separator(LastComponent[0], Path.second)); } } @@ -327,6 +329,36 @@ TEST(Support, HomeDirectory) { } } +#ifdef LLVM_ON_UNIX +TEST(Support, HomeDirectoryWithNoEnv) { + std::string OriginalStorage; + char const *OriginalEnv = ::getenv("HOME"); + if (OriginalEnv) { + // We're going to unset it, so make a copy and save a pointer to the copy + // so that we can reset it at the end of the test. + OriginalStorage = OriginalEnv; + OriginalEnv = OriginalStorage.c_str(); + } + + // Don't run the test if we have nothing to compare against. + struct passwd *pw = getpwuid(getuid()); + if (!pw || !pw->pw_dir) return; + + ::unsetenv("HOME"); + EXPECT_EQ(nullptr, ::getenv("HOME")); + std::string PwDir = pw->pw_dir; + + SmallString<128> HomeDir; + auto status = path::home_directory(HomeDir); + EXPECT_TRUE(status); + EXPECT_EQ(PwDir, HomeDir); + + // Now put the environment back to its original state (meaning that if it was + // unset before, we don't reset it). + if (OriginalEnv) ::setenv("HOME", OriginalEnv, 1); +} +#endif + TEST(Support, UserCacheDirectory) { SmallString<13> CacheDir; SmallString<20> CacheDir2; @@ -496,6 +528,41 @@ TEST_F(FileSystemTest, Unique) { ASSERT_NO_ERROR(fs::remove(TempPath)); } +TEST_F(FileSystemTest, RealPath) { + ASSERT_NO_ERROR( + fs::create_directories(Twine(TestDirectory) + "/test1/test2/test3")); + ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/test1/test2/test3")); + + SmallString<64> RealBase; + SmallString<64> Expected; + SmallString<64> Actual; + + // TestDirectory itself might be under a symlink or have been specified with + // a different case than the existing temp directory. In such cases real_path + // on the concatenated path will differ in the TestDirectory portion from + // how we specified it. Make sure to compare against the real_path of the + // TestDirectory, and not just the value of TestDirectory. + ASSERT_NO_ERROR(fs::real_path(TestDirectory, RealBase)); + path::native(Twine(RealBase) + "/test1/test2", Expected); + + ASSERT_NO_ERROR(fs::real_path( + Twine(TestDirectory) + "/././test1/../test1/test2/./test3/..", Actual)); + + EXPECT_EQ(Expected, Actual); + + SmallString<64> HomeDir; + bool Result = llvm::sys::path::home_directory(HomeDir); + if (Result) { + ASSERT_NO_ERROR(fs::real_path(HomeDir, Expected)); + ASSERT_NO_ERROR(fs::real_path("~", Actual, true)); + EXPECT_EQ(Expected, Actual); + ASSERT_NO_ERROR(fs::real_path("~/", Actual, true)); + EXPECT_EQ(Expected, Actual); + } + + ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/test1")); +} + TEST_F(FileSystemTest, TempFiles) { // Create a temp file. int FileDescriptor; @@ -740,6 +807,118 @@ TEST_F(FileSystemTest, DirectoryIteration) { ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel")); } +#ifdef LLVM_ON_UNIX +TEST_F(FileSystemTest, BrokenSymlinkDirectoryIteration) { + // Create a known hierarchy to recurse over. + ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/symlink")); + ASSERT_NO_ERROR( + fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/a")); + ASSERT_NO_ERROR( + fs::create_directories(Twine(TestDirectory) + "/symlink/b/bb")); + ASSERT_NO_ERROR( + fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/ba")); + ASSERT_NO_ERROR( + fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/bc")); + ASSERT_NO_ERROR( + fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/c")); + ASSERT_NO_ERROR( + fs::create_directories(Twine(TestDirectory) + "/symlink/d/dd/ddd")); + ASSERT_NO_ERROR(fs::create_link(Twine(TestDirectory) + "/symlink/d/dd", + Twine(TestDirectory) + "/symlink/d/da")); + ASSERT_NO_ERROR( + fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/e")); + + typedef std::vector<std::string> v_t; + v_t visited; + + // The directory iterator doesn't stat the file, so we should be able to + // iterate over the whole directory. + std::error_code ec; + for (fs::directory_iterator i(Twine(TestDirectory) + "/symlink", ec), e; + i != e; i.increment(ec)) { + ASSERT_NO_ERROR(ec); + visited.push_back(path::filename(i->path())); + } + std::sort(visited.begin(), visited.end()); + v_t expected = {"a", "b", "c", "d", "e"}; + ASSERT_TRUE(visited.size() == expected.size()); + ASSERT_TRUE(std::equal(visited.begin(), visited.end(), expected.begin())); + visited.clear(); + + // The recursive directory iterator has to stat the file, so we need to skip + // the broken symlinks. + for (fs::recursive_directory_iterator + i(Twine(TestDirectory) + "/symlink", ec), + e; + i != e; i.increment(ec)) { + ASSERT_NO_ERROR(ec); + + fs::file_status status; + if (i->status(status) == + std::make_error_code(std::errc::no_such_file_or_directory)) { + i.no_push(); + continue; + } + + visited.push_back(path::filename(i->path())); + } + std::sort(visited.begin(), visited.end()); + expected = {"b", "bb", "d", "da", "dd", "ddd", "ddd"}; + ASSERT_TRUE(visited.size() == expected.size()); + ASSERT_TRUE(std::equal(visited.begin(), visited.end(), expected.begin())); + visited.clear(); + + // This recursive directory iterator doesn't follow symlinks, so we don't need + // to skip them. + for (fs::recursive_directory_iterator + i(Twine(TestDirectory) + "/symlink", ec, /*follow_symlinks=*/false), + e; + i != e; i.increment(ec)) { + ASSERT_NO_ERROR(ec); + visited.push_back(path::filename(i->path())); + } + std::sort(visited.begin(), visited.end()); + expected = {"a", "b", "ba", "bb", "bc", "c", "d", "da", "dd", "ddd", "e"}; + ASSERT_TRUE(visited.size() == expected.size()); + ASSERT_TRUE(std::equal(visited.begin(), visited.end(), expected.begin())); + + ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/symlink")); +} +#endif + +TEST_F(FileSystemTest, Remove) { + SmallString<64> BaseDir; + SmallString<64> Paths[4]; + int fds[4]; + ASSERT_NO_ERROR(fs::createUniqueDirectory("fs_remove", BaseDir)); + + ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/baz")); + ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/buzz")); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[0], Paths[0])); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[1], Paths[1])); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[2], Paths[2])); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[3], Paths[3])); + + for (int fd : fds) + ::close(fd); + + EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/baz")); + EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/buzz")); + EXPECT_TRUE(fs::exists(Paths[0])); + EXPECT_TRUE(fs::exists(Paths[1])); + EXPECT_TRUE(fs::exists(Paths[2])); + EXPECT_TRUE(fs::exists(Paths[3])); + + ASSERT_NO_ERROR(fs::remove_directories("D:/footest")); + + ASSERT_NO_ERROR(fs::remove_directories(BaseDir)); + ASSERT_FALSE(fs::exists(BaseDir)); +} + const char archive[] = "!<arch>\x0A"; const char bitcode[] = "\xde\xc0\x17\x0b"; const char coff_object[] = "\x00\x00......"; @@ -863,6 +1042,20 @@ TEST_F(FileSystemTest, Resize) { ASSERT_NO_ERROR(fs::remove(TempPath)); } +TEST_F(FileSystemTest, MD5) { + int FD; + SmallString<64> TempPath; + ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath)); + StringRef Data("abcdefghijklmnopqrstuvwxyz"); + write(FD, Data.data(), Data.size()); + lseek(FD, 0, SEEK_SET); + auto Hash = fs::md5_contents(FD); + ::close(FD); + ASSERT_NO_ERROR(Hash.getError()); + + EXPECT_STREQ("c3fcd3d76192e4007dfb496cca67e13b", Hash->digest().c_str()); +} + TEST_F(FileSystemTest, FileMapping) { // Create a temp file. int FileDescriptor; @@ -906,40 +1099,50 @@ TEST_F(FileSystemTest, FileMapping) { } TEST(Support, NormalizePath) { + using TestTuple = std::tuple<const char *, const char *, const char *>; + std::vector<TestTuple> Tests; + Tests.emplace_back("a", "a", "a"); + Tests.emplace_back("a/b", "a\\b", "a/b"); + Tests.emplace_back("a\\b", "a\\b", "a/b"); + Tests.emplace_back("a\\\\b", "a\\\\b", "a\\\\b"); + Tests.emplace_back("\\a", "\\a", "/a"); + Tests.emplace_back("a\\", "a\\", "a/"); + + for (auto &T : Tests) { + SmallString<64> Win(std::get<0>(T)); + SmallString<64> Posix(Win); + path::native(Win, path::Style::windows); + path::native(Posix, path::Style::posix); + EXPECT_EQ(std::get<1>(T), Win); + EXPECT_EQ(std::get<2>(T), Posix); + } + #if defined(LLVM_ON_WIN32) -#define EXPECT_PATH_IS(path__, windows__, not_windows__) \ - EXPECT_EQ(path__, windows__); -#else -#define EXPECT_PATH_IS(path__, windows__, not_windows__) \ - EXPECT_EQ(path__, not_windows__); + SmallString<64> PathHome; + path::home_directory(PathHome); + + const char *Path7a = "~/aaa"; + SmallString<64> Path7(Path7a); + path::native(Path7); + EXPECT_TRUE(Path7.endswith("\\aaa")); + EXPECT_TRUE(Path7.startswith(PathHome)); + EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1)); + + const char *Path8a = "~"; + SmallString<64> Path8(Path8a); + path::native(Path8); + EXPECT_EQ(Path8, PathHome); + + const char *Path9a = "~aaa"; + SmallString<64> Path9(Path9a); + path::native(Path9); + EXPECT_EQ(Path9, "~aaa"); + + const char *Path10a = "aaa/~/b"; + SmallString<64> Path10(Path10a); + path::native(Path10); + EXPECT_EQ(Path10, "aaa\\~\\b"); #endif - - SmallString<64> Path1("a"); - SmallString<64> Path2("a/b"); - SmallString<64> Path3("a\\b"); - SmallString<64> Path4("a\\\\b"); - SmallString<64> Path5("\\a"); - SmallString<64> Path6("a\\"); - - path::native(Path1); - EXPECT_PATH_IS(Path1, "a", "a"); - - path::native(Path2); - EXPECT_PATH_IS(Path2, "a\\b", "a/b"); - - path::native(Path3); - EXPECT_PATH_IS(Path3, "a\\b", "a/b"); - - path::native(Path4); - EXPECT_PATH_IS(Path4, "a\\\\b", "a\\\\b"); - - path::native(Path5); - EXPECT_PATH_IS(Path5, "\\a", "/a"); - - path::native(Path6); - EXPECT_PATH_IS(Path6, "a\\", "a/"); - -#undef EXPECT_PATH_IS } TEST(Support, RemoveLeadingDotSlash) { @@ -952,43 +1155,48 @@ TEST(Support, RemoveLeadingDotSlash) { EXPECT_EQ(Path2, ""); } -static std::string remove_dots(StringRef path, - bool remove_dot_dot) { +static std::string remove_dots(StringRef path, bool remove_dot_dot, + path::Style style) { SmallString<256> buffer(path); - path::remove_dots(buffer, remove_dot_dot); + path::remove_dots(buffer, remove_dot_dot, style); return buffer.str(); } TEST(Support, RemoveDots) { -#if defined(LLVM_ON_WIN32) - EXPECT_EQ("foolz\\wat", remove_dots(".\\.\\\\foolz\\wat", false)); - EXPECT_EQ("", remove_dots(".\\\\\\\\\\", false)); - - EXPECT_EQ("a\\..\\b\\c", remove_dots(".\\a\\..\\b\\c", false)); - EXPECT_EQ("b\\c", remove_dots(".\\a\\..\\b\\c", true)); - EXPECT_EQ("c", remove_dots(".\\.\\c", true)); - EXPECT_EQ("..\\a\\c", remove_dots("..\\a\\b\\..\\c", true)); - EXPECT_EQ("..\\..\\a\\c", remove_dots("..\\..\\a\\b\\..\\c", true)); + EXPECT_EQ("foolz\\wat", + remove_dots(".\\.\\\\foolz\\wat", false, path::Style::windows)); + EXPECT_EQ("", remove_dots(".\\\\\\\\\\", false, path::Style::windows)); + + EXPECT_EQ("a\\..\\b\\c", + remove_dots(".\\a\\..\\b\\c", false, path::Style::windows)); + EXPECT_EQ("b\\c", remove_dots(".\\a\\..\\b\\c", true, path::Style::windows)); + EXPECT_EQ("c", remove_dots(".\\.\\c", true, path::Style::windows)); + EXPECT_EQ("..\\a\\c", + remove_dots("..\\a\\b\\..\\c", true, path::Style::windows)); + EXPECT_EQ("..\\..\\a\\c", + remove_dots("..\\..\\a\\b\\..\\c", true, path::Style::windows)); SmallString<64> Path1(".\\.\\c"); - EXPECT_TRUE(path::remove_dots(Path1, true)); - EXPECT_EQ("c", Path1); -#else - EXPECT_EQ("foolz/wat", remove_dots("././/foolz/wat", false)); - EXPECT_EQ("", remove_dots("./////", false)); - - EXPECT_EQ("a/../b/c", remove_dots("./a/../b/c", false)); - EXPECT_EQ("b/c", remove_dots("./a/../b/c", true)); - EXPECT_EQ("c", remove_dots("././c", true)); - EXPECT_EQ("../a/c", remove_dots("../a/b/../c", true)); - EXPECT_EQ("../../a/c", remove_dots("../../a/b/../c", true)); - EXPECT_EQ("/a/c", remove_dots("/../../a/c", true)); - EXPECT_EQ("/a/c", remove_dots("/../a/b//../././/c", true)); - - SmallString<64> Path1("././c"); - EXPECT_TRUE(path::remove_dots(Path1, true)); + EXPECT_TRUE(path::remove_dots(Path1, true, path::Style::windows)); EXPECT_EQ("c", Path1); -#endif + + EXPECT_EQ("foolz/wat", + remove_dots("././/foolz/wat", false, path::Style::posix)); + EXPECT_EQ("", remove_dots("./////", false, path::Style::posix)); + + EXPECT_EQ("a/../b/c", remove_dots("./a/../b/c", false, path::Style::posix)); + EXPECT_EQ("b/c", remove_dots("./a/../b/c", true, path::Style::posix)); + EXPECT_EQ("c", remove_dots("././c", true, path::Style::posix)); + EXPECT_EQ("../a/c", remove_dots("../a/b/../c", true, path::Style::posix)); + EXPECT_EQ("../../a/c", + remove_dots("../../a/b/../c", true, path::Style::posix)); + EXPECT_EQ("/a/c", remove_dots("/../../a/c", true, path::Style::posix)); + EXPECT_EQ("/a/c", + remove_dots("/../a/b//../././/c", true, path::Style::posix)); + + SmallString<64> Path2("././c"); + EXPECT_TRUE(path::remove_dots(Path2, true, path::Style::posix)); + EXPECT_EQ("c", Path2); } TEST(Support, ReplacePathPrefix) { @@ -1135,4 +1343,198 @@ TEST_F(FileSystemTest, OpenFileForRead) { ::close(FileDescriptor); } + +TEST_F(FileSystemTest, set_current_path) { + SmallString<128> path; + + ASSERT_NO_ERROR(fs::current_path(path)); + ASSERT_NE(TestDirectory, path); + + struct RestorePath { + SmallString<128> path; + RestorePath(const SmallString<128> &path) : path(path) {} + ~RestorePath() { fs::set_current_path(path); } + } restore_path(path); + + ASSERT_NO_ERROR(fs::set_current_path(TestDirectory)); + + ASSERT_NO_ERROR(fs::current_path(path)); + + fs::UniqueID D1, D2; + ASSERT_NO_ERROR(fs::getUniqueID(TestDirectory, D1)); + ASSERT_NO_ERROR(fs::getUniqueID(path, D2)); + ASSERT_EQ(D1, D2) << "D1: " << TestDirectory << "\nD2: " << path; +} + +TEST_F(FileSystemTest, permissions) { + int FD; + SmallString<64> TempPath; + ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath)); + FileRemover Cleanup(TempPath); + + // Make sure it exists. + ASSERT_TRUE(fs::exists(Twine(TempPath))); + + auto CheckPermissions = [&](fs::perms Expected) { + ErrorOr<fs::perms> Actual = fs::getPermissions(TempPath); + return Actual && *Actual == Expected; + }; + + std::error_code NoError; + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::all_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_read | fs::all_exe)); + +#if defined(LLVM_ON_WIN32) + fs::perms ReadOnly = fs::all_read | fs::all_exe; + EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, ReadOnly | fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(ReadOnly)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_all)); +#else + EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError); + EXPECT_TRUE(CheckPermissions(fs::no_perms)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::owner_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::group_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError); + EXPECT_TRUE(CheckPermissions(fs::others_all)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_read)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_write)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError); + EXPECT_TRUE(CheckPermissions(fs::set_gid_on_exe)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError); + EXPECT_TRUE(CheckPermissions(fs::sticky_bit)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe | fs::set_gid_on_exe | + fs::sticky_bit)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::set_uid_on_exe | + fs::set_gid_on_exe | + fs::sticky_bit), + NoError); + EXPECT_TRUE(CheckPermissions(fs::all_read | fs::set_uid_on_exe | + fs::set_gid_on_exe | fs::sticky_bit)); + + EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError); + EXPECT_TRUE(CheckPermissions(fs::all_perms)); +#endif +} + } // anonymous namespace diff --git a/unittests/Support/ProgramTest.cpp b/unittests/Support/ProgramTest.cpp index 886ead8305bc1..3926ceb92b3ed 100644 --- a/unittests/Support/ProgramTest.cpp +++ b/unittests/Support/ProgramTest.cpp @@ -87,6 +87,7 @@ protected: EXPECT_TRUE(convStatus); return EnvStorage.back().c_str(); #else + (void)this; return Var; #endif }; diff --git a/unittests/Support/TargetParserTest.cpp b/unittests/Support/TargetParserTest.cpp index a3d806f76fb51..f0bfe7dbde964 100644 --- a/unittests/Support/TargetParserTest.cpp +++ b/unittests/Support/TargetParserTest.cpp @@ -17,17 +17,17 @@ using namespace llvm; namespace { const char *ARMArch[] = { - "armv2", "armv2a", "armv3", "armv3m", "armv4", - "armv4t", "armv5", "armv5t", "armv5e", "armv5te", - "armv5tej", "armv6", "armv6j", "armv6k", "armv6hl", - "armv6t2", "armv6kz", "armv6z", "armv6zk", "armv6-m", - "armv6m", "armv6sm", "armv6s-m", "armv7-a", "armv7", - "armv7a", "armv7hl", "armv7l", "armv7-r", "armv7r", - "armv7-m", "armv7m", "armv7k", "armv7s", "armv7e-m", - "armv7em", "armv8-a", "armv8", "armv8a", "armv8.1-a", - "armv8.1a", "armv8.2-a", "armv8.2a", "armv8-r", "armv8r", - "armv8-m.base", "armv8m.base", "armv8-m.main", "armv8m.main", "iwmmxt", - "iwmmxt2", "xscale"}; + "armv2", "armv2a", "armv3", "armv3m", "armv4", + "armv4t", "armv5", "armv5t", "armv5e", "armv5te", + "armv5tej", "armv6", "armv6j", "armv6k", "armv6hl", + "armv6t2", "armv6kz", "armv6z", "armv6zk", "armv6-m", + "armv6m", "armv6sm", "armv6s-m", "armv7-a", "armv7", + "armv7a", "armv7ve", "armv7hl", "armv7l", "armv7-r", + "armv7r", "armv7-m", "armv7m", "armv7k", "armv7s", + "armv7e-m", "armv7em", "armv8-a", "armv8", "armv8a", + "armv8.1-a", "armv8.1a", "armv8.2-a", "armv8.2a", "armv8-r", + "armv8r", "armv8-m.base", "armv8m.base", "armv8-m.main", "armv8m.main", + "iwmmxt", "iwmmxt2", "xscale"}; bool testARMCPU(StringRef CPUName, StringRef ExpectedArch, StringRef ExpectedFPU, unsigned ExpectedFlags, @@ -246,6 +246,10 @@ TEST(TargetParserTest, testARMCPU) { ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_DSP, "8-A")); + EXPECT_TRUE(testARMCPU("cortex-m23", "armv8-m.base", "none", + ARM::AEK_HWDIV, "8-M.Baseline")); + EXPECT_TRUE(testARMCPU("cortex-m33", "armv8-m.main", "fpv5-sp-d16", + ARM::AEK_HWDIV | ARM::AEK_DSP, "8-M.Mainline")); EXPECT_TRUE(testARMCPU("iwmmxt", "iwmmxt", "none", ARM::AEK_NONE, "iwmmxt")); EXPECT_TRUE(testARMCPU("xscale", "xscale", "none", @@ -311,6 +315,9 @@ TEST(TargetParserTest, testARMArch) { testARMArch("armv7-a", "cortex-a8", "v7", ARMBuildAttrs::CPUArch::v7)); EXPECT_TRUE( + testARMArch("armv7ve", "generic", "v7ve", + ARMBuildAttrs::CPUArch::v7)); + EXPECT_TRUE( testARMArch("armv7-r", "cortex-r4", "v7r", ARMBuildAttrs::CPUArch::v7)); EXPECT_TRUE( @@ -498,12 +505,12 @@ TEST(TargetParserTest, ARMparseHWDiv) { TEST(TargetParserTest, ARMparseArchEndianAndISA) { const char *Arch[] = { - "v2", "v2a", "v3", "v3m", "v4", "v4t", "v5", "v5t", - "v5e", "v5te", "v5tej", "v6", "v6j", "v6k", "v6hl", "v6t2", - "v6kz", "v6z", "v6zk", "v6-m", "v6m", "v6sm", "v6s-m", "v7-a", - "v7", "v7a", "v7hl", "v7l", "v7-r", "v7r", "v7-m", "v7m", - "v7k", "v7s", "v7e-m", "v7em", "v8-a", "v8", "v8a", "v8.1-a", - "v8.1a", "v8.2-a", "v8.2a", "v8-r"}; + "v2", "v2a", "v3", "v3m", "v4", "v4t", "v5", "v5t", + "v5e", "v5te", "v5tej", "v6", "v6j", "v6k", "v6hl", "v6t2", + "v6kz", "v6z", "v6zk", "v6-m", "v6m", "v6sm", "v6s-m", "v7-a", + "v7", "v7a", "v7ve", "v7hl", "v7l", "v7-r", "v7r", "v7-m", + "v7m", "v7k", "v7s", "v7e-m", "v7em", "v8-a", "v8", "v8a", + "v8.1-a", "v8.1a", "v8.2-a", "v8.2a", "v8-r"}; for (unsigned i = 0; i < array_lengthof(Arch); i++) { std::string arm_1 = "armeb" + (std::string)(Arch[i]); @@ -555,6 +562,7 @@ TEST(TargetParserTest, ARMparseArchProfile) { EXPECT_EQ(ARM::PK_R, ARM::parseArchProfile(ARMArch[i])); continue; case ARM::AK_ARMV7A: + case ARM::AK_ARMV7VE: case ARM::AK_ARMV7K: case ARM::AK_ARMV8A: case ARM::AK_ARMV8_1A: @@ -635,8 +643,29 @@ TEST(TargetParserTest, testAArch64CPU) { "kryo", "armv8-a", "crypto-neon-fp-armv8", AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); EXPECT_TRUE(testAArch64CPU( - "vulcan", "armv8.1-a", "crypto-neon-fp-armv8", - AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8.1-A")); + "thunderx2t99", "armv8.1-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_LSE | + AArch64::AEK_SIMD, "8.1-A")); + EXPECT_TRUE(testAArch64CPU( + "thunderx", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD | + AArch64::AEK_FP | AArch64::AEK_PROFILE, + "8-A")); + EXPECT_TRUE(testAArch64CPU( + "thunderxt81", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD | + AArch64::AEK_FP | AArch64::AEK_PROFILE, + "8-A")); + EXPECT_TRUE(testAArch64CPU( + "thunderxt83", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD | + AArch64::AEK_FP | AArch64::AEK_PROFILE, + "8-A")); + EXPECT_TRUE(testAArch64CPU( + "thunderxt88", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD | + AArch64::AEK_FP | AArch64::AEK_PROFILE, + "8-A")); } bool testAArch64Arch(StringRef Arch, StringRef DefaultCPU, StringRef SubArch, @@ -672,7 +701,11 @@ TEST(TargetParserTest, testAArch64Extension) { EXPECT_FALSE(testAArch64Extension("cyclone", 0, "ras")); EXPECT_FALSE(testAArch64Extension("exynos-m1", 0, "ras")); EXPECT_FALSE(testAArch64Extension("kryo", 0, "ras")); - EXPECT_FALSE(testAArch64Extension("vulcan", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("thunderx2t99", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("thunderx", 0, "lse")); + EXPECT_FALSE(testAArch64Extension("thunderxt81", 0, "lse")); + EXPECT_FALSE(testAArch64Extension("thunderxt83", 0, "lse")); + EXPECT_FALSE(testAArch64Extension("thunderxt88", 0, "lse")); EXPECT_FALSE(testAArch64Extension( "generic", static_cast<unsigned>(AArch64::ArchKind::AK_ARMV8A), "ras")); diff --git a/unittests/Support/ThreadPool.cpp b/unittests/Support/ThreadPool.cpp index 8e03aacfb1e03..0da33ad50c078 100644 --- a/unittests/Support/ThreadPool.cpp +++ b/unittests/Support/ThreadPool.cpp @@ -90,7 +90,7 @@ TEST_F(ThreadPoolTest, AsyncBarrier) { ThreadPool Pool; for (size_t i = 0; i < 5; ++i) { - Pool.async([this, &checked_in, i] { + Pool.async([this, &checked_in] { waitForMainThread(); ++checked_in; }); @@ -154,7 +154,7 @@ TEST_F(ThreadPoolTest, PoolDestruction) { { ThreadPool Pool; for (size_t i = 0; i < 5; ++i) { - Pool.async([this, &checked_in, i] { + Pool.async([this, &checked_in] { waitForMainThread(); ++checked_in; }); diff --git a/unittests/Support/TrailingObjectsTest.cpp b/unittests/Support/TrailingObjectsTest.cpp index cb5c47d1b25be..23acc54d23761 100644 --- a/unittests/Support/TrailingObjectsTest.cpp +++ b/unittests/Support/TrailingObjectsTest.cpp @@ -236,3 +236,24 @@ TEST(TrailingObjects, Realignment) { reinterpret_cast<char *>(C + 1) + 1, alignof(long)))); } } + +// Test the use of TrailingObjects with a template class. This +// previously failed to compile due to a bug in MSVC's member access +// control/lookup handling for OverloadToken. +template <typename Derived> +class Class5Tmpl : private llvm::TrailingObjects<Derived, float, int> { + using TrailingObjects = typename llvm::TrailingObjects<Derived, float>; + friend TrailingObjects; + + size_t numTrailingObjects( + typename TrailingObjects::template OverloadToken<float>) const { + return 1; + } + + size_t numTrailingObjects( + typename TrailingObjects::template OverloadToken<int>) const { + return 2; + } +}; + +class Class5 : public Class5Tmpl<Class5> {}; diff --git a/unittests/Support/YAMLIOTest.cpp b/unittests/Support/YAMLIOTest.cpp index dc7c5d47cba96..5a0280c8ca5ba 100644 --- a/unittests/Support/YAMLIOTest.cpp +++ b/unittests/Support/YAMLIOTest.cpp @@ -1740,7 +1740,7 @@ TEST(YAMLIO, TestFlagsReadError) { // // Test error handling reading built-in uint8_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(uint8_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(uint8_t) TEST(YAMLIO, TestReadBuiltInTypesUint8Error) { std::vector<uint8_t> seq; Input yin("---\n" @@ -1759,7 +1759,7 @@ TEST(YAMLIO, TestReadBuiltInTypesUint8Error) { // // Test error handling reading built-in uint16_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(uint16_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(uint16_t) TEST(YAMLIO, TestReadBuiltInTypesUint16Error) { std::vector<uint16_t> seq; Input yin("---\n" @@ -1778,7 +1778,7 @@ TEST(YAMLIO, TestReadBuiltInTypesUint16Error) { // // Test error handling reading built-in uint32_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(uint32_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(uint32_t) TEST(YAMLIO, TestReadBuiltInTypesUint32Error) { std::vector<uint32_t> seq; Input yin("---\n" @@ -1797,7 +1797,7 @@ TEST(YAMLIO, TestReadBuiltInTypesUint32Error) { // // Test error handling reading built-in uint64_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(uint64_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(uint64_t) TEST(YAMLIO, TestReadBuiltInTypesUint64Error) { std::vector<uint64_t> seq; Input yin("---\n" @@ -1816,7 +1816,7 @@ TEST(YAMLIO, TestReadBuiltInTypesUint64Error) { // // Test error handling reading built-in int8_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(int8_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(int8_t) TEST(YAMLIO, TestReadBuiltInTypesint8OverError) { std::vector<int8_t> seq; Input yin("---\n" @@ -1854,7 +1854,7 @@ TEST(YAMLIO, TestReadBuiltInTypesint8UnderError) { // // Test error handling reading built-in int16_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(int16_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(int16_t) TEST(YAMLIO, TestReadBuiltInTypesint16UnderError) { std::vector<int16_t> seq; Input yin("---\n" @@ -1893,7 +1893,7 @@ TEST(YAMLIO, TestReadBuiltInTypesint16OverError) { // // Test error handling reading built-in int32_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(int32_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(int32_t) TEST(YAMLIO, TestReadBuiltInTypesint32UnderError) { std::vector<int32_t> seq; Input yin("---\n" @@ -1931,7 +1931,7 @@ TEST(YAMLIO, TestReadBuiltInTypesint32OverError) { // // Test error handling reading built-in int64_t type // -LLVM_YAML_IS_SEQUENCE_VECTOR(int64_t) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(int64_t) TEST(YAMLIO, TestReadBuiltInTypesint64UnderError) { std::vector<int64_t> seq; Input yin("---\n" diff --git a/unittests/Support/raw_ostream_test.cpp b/unittests/Support/raw_ostream_test.cpp index f87d2f60d169e..777e555949eed 100644 --- a/unittests/Support/raw_ostream_test.cpp +++ b/unittests/Support/raw_ostream_test.cpp @@ -9,6 +9,7 @@ #include "gtest/gtest.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" @@ -330,4 +331,11 @@ TEST(raw_ostreamTest, FormattedHexBytes) { "0007: 68 69 6a 6b 6c |hijkl|", format_bytes_with_ascii_str(B.take_front(12), 0, 7, 1)); } + +TEST(raw_fd_ostreamTest, multiple_raw_fd_ostream_to_stdout) { + std::error_code EC; + + { raw_fd_ostream("-", EC, sys::fs::OpenFlags::F_None); } + { raw_fd_ostream("-", EC, sys::fs::OpenFlags::F_None); } +} } diff --git a/unittests/Transforms/Scalar/LoopPassManagerTest.cpp b/unittests/Transforms/Scalar/LoopPassManagerTest.cpp index a099e35c7f195..227060f0a46e1 100644 --- a/unittests/Transforms/Scalar/LoopPassManagerTest.cpp +++ b/unittests/Transforms/Scalar/LoopPassManagerTest.cpp @@ -46,7 +46,10 @@ public: DerivedT *Handle; - Analysis(DerivedT &Handle) : Handle(&Handle) {} + Analysis(DerivedT &Handle) : Handle(&Handle) { + static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value, + "Must pass the derived type to this template!"); + } public: class Result { @@ -152,7 +155,10 @@ public: DerivedT *Handle; - Pass(DerivedT &Handle) : Handle(&Handle) {} + Pass(DerivedT &Handle) : Handle(&Handle) { + static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value, + "Must pass the derived type to this template!"); + } public: PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM, @@ -244,27 +250,38 @@ protected: public: LoopPassManagerTest() - : M(parseIR(Context, "define void @f() {\n" - "entry:\n" - " br label %loop.0\n" - "loop.0:\n" - " br i1 undef, label %loop.0.0, label %end\n" - "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.1\n" - "loop.0.1:\n" - " br i1 undef, label %loop.0.1, label %loop.0\n" - "end:\n" - " ret void\n" - "}\n" - "\n" - "define void @g() {\n" - "entry:\n" - " br label %loop.g.0\n" - "loop.g.0:\n" - " br i1 undef, label %loop.g.0, label %end\n" - "end:\n" - " ret void\n" - "}\n")), + : M(parseIR(Context, + "define void @f(i1* %ptr) {\n" + "entry:\n" + " br label %loop.0\n" + "loop.0:\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %end\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" + "loop.0.0:\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n" + "loop.0.1.ph:\n" + " br label %loop.0.1\n" + "loop.0.1:\n" + " %cond.0.1 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.1, label %loop.0.1, label %loop.0.latch\n" + "loop.0.latch:\n" + " br label %loop.0\n" + "end:\n" + " ret void\n" + "}\n" + "\n" + "define void @g(i1* %ptr) {\n" + "entry:\n" + " br label %loop.g.0\n" + "loop.g.0:\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.g.0, label %end\n" + "end:\n" + " ret void\n" + "}\n")), LAM(true), FAM(true), MAM(true) { // Register our mock analysis. LAM.registerPass([&] { return MLAHandle.getAnalysis(); }); @@ -551,7 +568,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); // Not preserving `AAManager`. - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); PA.preserve<LoopAnalysis>(); PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -568,24 +584,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - // Not preserving `AssumptionAnalysis`. - PA.preserve<DominatorTreeAnalysis>(); - PA.preserve<LoopAnalysis>(); - PA.preserve<LoopAnalysisManagerFunctionProxy>(); - PA.preserve<ScalarEvolutionAnalysis>(); - return PA; - })); - EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _)); - EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _)); - EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _)); - FPM.addPass(MFPHandle.getPass()); - FPM.addPass(createFunctionToLoopPassAdaptor( - RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())); - - EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { - auto PA = PreservedAnalyses::none(); - PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); // Not preserving `DominatorTreeAnalysis`. PA.preserve<LoopAnalysis>(); PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -602,7 +600,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); // Not preserving the `LoopAnalysis`. PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -619,7 +616,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); PA.preserve<LoopAnalysis>(); // Not preserving the `LoopAnalysisManagerFunctionProxy`. @@ -636,7 +632,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); PA.preserve<LoopAnalysis>(); PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -654,7 +649,7 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { // 'g' once with a requires pass and then run our mock pass over g a bunch // but just get cached results each time. EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _)); - EXPECT_CALL(MFPHandle, run(HasName("g"), _)).Times(7); + EXPECT_CALL(MFPHandle, run(HasName("g"), _)).Times(6); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); MPM.run(*M, MAM); @@ -842,17 +837,29 @@ TEST_F(LoopPassManagerTest, IndirectOuterPassInvalidation) { TEST_F(LoopPassManagerTest, LoopChildInsertion) { // Super boring module with three loops in a single loop nest. - M = parseIR(Context, "define void @f() {\n" + M = parseIR(Context, "define void @f(i1* %ptr) {\n" "entry:\n" " br label %loop.0\n" "loop.0:\n" - " br i1 undef, label %loop.0.0, label %end\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %end\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.1\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n" + "loop.0.1.ph:\n" + " br label %loop.0.1\n" "loop.0.1:\n" - " br i1 undef, label %loop.0.1, label %loop.0.2\n" + " %cond.0.1 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n" + "loop.0.2.ph:\n" + " br label %loop.0.2\n" "loop.0.2:\n" - " br i1 undef, label %loop.0.2, label %loop.0\n" + " %cond.0.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n" + "loop.0.latch:\n" + " br label %loop.0\n" "end:\n" " ret void\n" "}\n"); @@ -861,20 +868,34 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { // easily. Function &F = *M->begin(); ASSERT_THAT(F, HasName("f")); + Argument &Ptr = *F.arg_begin(); auto BBI = F.begin(); BasicBlock &EntryBB = *BBI++; ASSERT_THAT(EntryBB, HasName("entry")); BasicBlock &Loop0BB = *BBI++; ASSERT_THAT(Loop0BB, HasName("loop.0")); + BasicBlock &Loop00PHBB = *BBI++; + ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph")); BasicBlock &Loop00BB = *BBI++; ASSERT_THAT(Loop00BB, HasName("loop.0.0")); + BasicBlock &Loop01PHBB = *BBI++; + ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph")); BasicBlock &Loop01BB = *BBI++; ASSERT_THAT(Loop01BB, HasName("loop.0.1")); + BasicBlock &Loop02PHBB = *BBI++; + ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph")); BasicBlock &Loop02BB = *BBI++; ASSERT_THAT(Loop02BB, HasName("loop.0.2")); + BasicBlock &Loop0LatchBB = *BBI++; + ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch")); BasicBlock &EndBB = *BBI++; ASSERT_THAT(EndBB, HasName("end")); ASSERT_THAT(BBI, F.end()); + auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB, + const char *Name, BasicBlock *BB) { + auto *Cond = new LoadInst(&Ptr, Name, /*isVolatile*/ true, BB); + BranchInst::Create(TrueBB, FalseBB, Cond, BB); + }; // Build the pass managers and register our pipeline. We build a single loop // pass pipeline consisting of three mock pass runs over each loop. After @@ -909,20 +930,33 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { // When running over the middle loop, the second run inserts two new child // loops, inserting them and itself into the worklist. - BasicBlock *NewLoop010BB; + BasicBlock *NewLoop010BB, *NewLoop01LatchBB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)) .WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { auto *NewLoop = new Loop(); L.addChildLoop(NewLoop); - NewLoop010BB = BasicBlock::Create(Context, "loop.0.1.0", &F, &Loop02BB); - BranchInst::Create(&Loop01BB, NewLoop010BB, - UndefValue::get(Type::getInt1Ty(Context)), - NewLoop010BB); - Loop01BB.getTerminator()->replaceUsesOfWith(&Loop01BB, NewLoop010BB); - AR.DT.addNewBlock(NewLoop010BB, &Loop01BB); + auto *NewLoop010PHBB = + BasicBlock::Create(Context, "loop.0.1.0.ph", &F, &Loop02PHBB); + NewLoop010BB = + BasicBlock::Create(Context, "loop.0.1.0", &F, &Loop02PHBB); + NewLoop01LatchBB = + BasicBlock::Create(Context, "loop.0.1.latch", &F, &Loop02PHBB); + Loop01BB.getTerminator()->replaceUsesOfWith(&Loop01BB, NewLoop010PHBB); + BranchInst::Create(NewLoop010BB, NewLoop010PHBB); + CreateCondBr(NewLoop01LatchBB, NewLoop010BB, "cond.0.1.0", + NewLoop010BB); + BranchInst::Create(&Loop01BB, NewLoop01LatchBB); + AR.DT.addNewBlock(NewLoop010PHBB, &Loop01BB); + AR.DT.addNewBlock(NewLoop010BB, NewLoop010PHBB); + AR.DT.addNewBlock(NewLoop01LatchBB, NewLoop010BB); + AR.DT.verifyDomTree(); + L.addBasicBlockToLoop(NewLoop010PHBB, AR.LI); NewLoop->addBasicBlockToLoop(NewLoop010BB, AR.LI); + L.addBasicBlockToLoop(NewLoop01LatchBB, AR.LI); + NewLoop->verifyLoop(); + L.verifyLoop(); Updater.addChildLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -943,21 +977,27 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { // In the second run over the middle loop after we've visited the new child, // we add another child to check that we can repeatedly add children, and add // children to a loop that already has children. - BasicBlock *NewLoop011BB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)) .WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { auto *NewLoop = new Loop(); L.addChildLoop(NewLoop); - NewLoop011BB = BasicBlock::Create(Context, "loop.0.1.1", &F, &Loop02BB); - BranchInst::Create(&Loop01BB, NewLoop011BB, - UndefValue::get(Type::getInt1Ty(Context)), - NewLoop011BB); - NewLoop010BB->getTerminator()->replaceUsesOfWith(&Loop01BB, - NewLoop011BB); - AR.DT.addNewBlock(NewLoop011BB, NewLoop010BB); + auto *NewLoop011PHBB = BasicBlock::Create(Context, "loop.0.1.1.ph", &F, NewLoop01LatchBB); + auto *NewLoop011BB = BasicBlock::Create(Context, "loop.0.1.1", &F, NewLoop01LatchBB); + NewLoop010BB->getTerminator()->replaceUsesOfWith(NewLoop01LatchBB, + NewLoop011PHBB); + BranchInst::Create(NewLoop011BB, NewLoop011PHBB); + CreateCondBr(NewLoop01LatchBB, NewLoop011BB, "cond.0.1.1", + NewLoop011BB); + AR.DT.addNewBlock(NewLoop011PHBB, NewLoop010BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop011BB, NewLoop011PHBB); + AR.DT.changeImmediateDominator(AR.DT[NewLoop01LatchBB], NewDTNode); + AR.DT.verifyDomTree(); + L.addBasicBlockToLoop(NewLoop011PHBB, AR.LI); NewLoop->addBasicBlockToLoop(NewLoop011BB, AR.LI); + NewLoop->verifyLoop(); + L.verifyLoop(); Updater.addChildLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -999,17 +1039,29 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { TEST_F(LoopPassManagerTest, LoopPeerInsertion) { // Super boring module with two loop nests and loop nest with two child // loops. - M = parseIR(Context, "define void @f() {\n" + M = parseIR(Context, "define void @f(i1* %ptr) {\n" "entry:\n" " br label %loop.0\n" "loop.0:\n" - " br i1 undef, label %loop.0.0, label %loop.2\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %loop.2.ph\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.2\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.2.ph\n" + "loop.0.2.ph:\n" + " br label %loop.0.2\n" "loop.0.2:\n" - " br i1 undef, label %loop.0.2, label %loop.0\n" + " %cond.0.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n" + "loop.0.latch:\n" + " br label %loop.0\n" + "loop.2.ph:\n" + " br label %loop.2\n" "loop.2:\n" - " br i1 undef, label %loop.2, label %end\n" + " %cond.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.2, label %loop.2, label %end\n" "end:\n" " ret void\n" "}\n"); @@ -1018,21 +1070,34 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { // easily. Function &F = *M->begin(); ASSERT_THAT(F, HasName("f")); + Argument &Ptr = *F.arg_begin(); auto BBI = F.begin(); BasicBlock &EntryBB = *BBI++; ASSERT_THAT(EntryBB, HasName("entry")); BasicBlock &Loop0BB = *BBI++; ASSERT_THAT(Loop0BB, HasName("loop.0")); + BasicBlock &Loop00PHBB = *BBI++; + ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph")); BasicBlock &Loop00BB = *BBI++; ASSERT_THAT(Loop00BB, HasName("loop.0.0")); + BasicBlock &Loop02PHBB = *BBI++; + ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph")); BasicBlock &Loop02BB = *BBI++; ASSERT_THAT(Loop02BB, HasName("loop.0.2")); + BasicBlock &Loop0LatchBB = *BBI++; + ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch")); + BasicBlock &Loop2PHBB = *BBI++; + ASSERT_THAT(Loop2PHBB, HasName("loop.2.ph")); BasicBlock &Loop2BB = *BBI++; ASSERT_THAT(Loop2BB, HasName("loop.2")); BasicBlock &EndBB = *BBI++; ASSERT_THAT(EndBB, HasName("end")); ASSERT_THAT(BBI, F.end()); - Constant *Undefi1 = UndefValue::get(Type::getInt1Ty(Context)); + auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB, + const char *Name, BasicBlock *BB) { + auto *Cond = new LoadInst(&Ptr, Name, /*isVolatile*/ true, BB); + BranchInst::Create(TrueBB, FalseBB, Cond, BB); + }; // Build the pass managers and register our pipeline. We build a single loop // pass pipeline consisting of three mock pass runs over each loop. After @@ -1059,19 +1124,24 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _)); // On the second run, we insert a sibling loop. - BasicBlock *NewLoop01BB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)) .WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { auto *NewLoop = new Loop(); L.getParentLoop()->addChildLoop(NewLoop); - NewLoop01BB = BasicBlock::Create(Context, "loop.0.1", &F, &Loop02BB); - BranchInst::Create(&Loop02BB, NewLoop01BB, Undefi1, NewLoop01BB); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02BB, NewLoop01BB); - auto *NewDTNode = AR.DT.addNewBlock(NewLoop01BB, &Loop00BB); - AR.DT.changeImmediateDominator(AR.DT[&Loop02BB], NewDTNode); + auto *NewLoop01PHBB = BasicBlock::Create(Context, "loop.0.1.ph", &F, &Loop02PHBB); + auto *NewLoop01BB = BasicBlock::Create(Context, "loop.0.1", &F, &Loop02PHBB); + BranchInst::Create(NewLoop01BB, NewLoop01PHBB); + CreateCondBr(&Loop02PHBB, NewLoop01BB, "cond.0.1", NewLoop01BB); + Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02PHBB, NewLoop01PHBB); + AR.DT.addNewBlock(NewLoop01PHBB, &Loop00BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop01BB, NewLoop01PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop02PHBB], NewDTNode); + AR.DT.verifyDomTree(); + L.getParentLoop()->addBasicBlockToLoop(NewLoop01PHBB, AR.LI); NewLoop->addBasicBlockToLoop(NewLoop01BB, AR.LI); + L.getParentLoop()->verifyLoop(); Updater.addSiblingLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -1104,22 +1174,45 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { L.getParentLoop()->addChildLoop(NewLoops[0]); L.getParentLoop()->addChildLoop(NewLoops[1]); NewLoops[1]->addChildLoop(NewLoops[2]); + auto *NewLoop03PHBB = + BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB); auto *NewLoop03BB = - BasicBlock::Create(Context, "loop.0.3", &F, &Loop2BB); + BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB); + auto *NewLoop04PHBB = + BasicBlock::Create(Context, "loop.0.4.ph", &F, &Loop0LatchBB); auto *NewLoop04BB = - BasicBlock::Create(Context, "loop.0.4", &F, &Loop2BB); + BasicBlock::Create(Context, "loop.0.4", &F, &Loop0LatchBB); + auto *NewLoop040PHBB = + BasicBlock::Create(Context, "loop.0.4.0.ph", &F, &Loop0LatchBB); auto *NewLoop040BB = - BasicBlock::Create(Context, "loop.0.4.0", &F, &Loop2BB); - Loop02BB.getTerminator()->replaceUsesOfWith(&Loop0BB, NewLoop03BB); - BranchInst::Create(NewLoop04BB, NewLoop03BB, Undefi1, NewLoop03BB); - BranchInst::Create(&Loop0BB, NewLoop040BB, Undefi1, NewLoop04BB); - BranchInst::Create(NewLoop04BB, NewLoop040BB, Undefi1, NewLoop040BB); - AR.DT.addNewBlock(NewLoop03BB, &Loop02BB); - AR.DT.addNewBlock(NewLoop04BB, NewLoop03BB); - AR.DT.addNewBlock(NewLoop040BB, NewLoop04BB); + BasicBlock::Create(Context, "loop.0.4.0", &F, &Loop0LatchBB); + auto *NewLoop04LatchBB = + BasicBlock::Create(Context, "loop.0.4.latch", &F, &Loop0LatchBB); + Loop02BB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB, NewLoop03PHBB); + BranchInst::Create(NewLoop03BB, NewLoop03PHBB); + CreateCondBr(NewLoop04PHBB, NewLoop03BB, "cond.0.3", NewLoop03BB); + BranchInst::Create(NewLoop04BB, NewLoop04PHBB); + CreateCondBr(&Loop0LatchBB, NewLoop040PHBB, "cond.0.4", NewLoop04BB); + BranchInst::Create(NewLoop040BB, NewLoop040PHBB); + CreateCondBr(NewLoop04LatchBB, NewLoop040BB, "cond.0.4.0", NewLoop040BB); + BranchInst::Create(NewLoop04BB, NewLoop04LatchBB); + AR.DT.addNewBlock(NewLoop03PHBB, &Loop02BB); + AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB); + AR.DT.addNewBlock(NewLoop04PHBB, NewLoop03BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop04BB, NewLoop04PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB], NewDTNode); + AR.DT.addNewBlock(NewLoop040PHBB, NewLoop04BB); + AR.DT.addNewBlock(NewLoop040BB, NewLoop040PHBB); + AR.DT.addNewBlock(NewLoop04LatchBB, NewLoop040BB); + AR.DT.verifyDomTree(); + L.getParentLoop()->addBasicBlockToLoop(NewLoop03PHBB, AR.LI); NewLoops[0]->addBasicBlockToLoop(NewLoop03BB, AR.LI); + L.getParentLoop()->addBasicBlockToLoop(NewLoop04PHBB, AR.LI); NewLoops[1]->addBasicBlockToLoop(NewLoop04BB, AR.LI); + NewLoops[1]->addBasicBlockToLoop(NewLoop040PHBB, AR.LI); NewLoops[2]->addBasicBlockToLoop(NewLoop040BB, AR.LI); + NewLoops[1]->addBasicBlockToLoop(NewLoop04LatchBB, AR.LI); + L.getParentLoop()->verifyLoop(); Updater.addSiblingLoops({NewLoops[0], NewLoops[1]}); return PreservedAnalyses::all(); })); @@ -1158,12 +1251,17 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { LPMUpdater &Updater) { auto *NewLoop = new Loop(); AR.LI.addTopLevelLoop(NewLoop); + auto *NewLoop1PHBB = BasicBlock::Create(Context, "loop.1.ph", &F, &Loop2BB); auto *NewLoop1BB = BasicBlock::Create(Context, "loop.1", &F, &Loop2BB); - BranchInst::Create(&Loop2BB, NewLoop1BB, Undefi1, NewLoop1BB); - Loop0BB.getTerminator()->replaceUsesOfWith(&Loop2BB, NewLoop1BB); - auto *NewDTNode = AR.DT.addNewBlock(NewLoop1BB, &Loop0BB); - AR.DT.changeImmediateDominator(AR.DT[&Loop2BB], NewDTNode); + BranchInst::Create(NewLoop1BB, NewLoop1PHBB); + CreateCondBr(&Loop2PHBB, NewLoop1BB, "cond.1", NewLoop1BB); + Loop0BB.getTerminator()->replaceUsesOfWith(&Loop2PHBB, NewLoop1PHBB); + AR.DT.addNewBlock(NewLoop1PHBB, &Loop0BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop1BB, NewLoop1PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop2PHBB], NewDTNode); + AR.DT.verifyDomTree(); NewLoop->addBasicBlockToLoop(NewLoop1BB, AR.LI); + NewLoop->verifyLoop(); Updater.addSiblingLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -1193,19 +1291,36 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { // Build a module with a single loop nest that contains one outer loop with // three subloops, and one of those with its own subloop. We will // incrementally delete all of these to test different deletion scenarios. - M = parseIR(Context, "define void @f() {\n" + M = parseIR(Context, "define void @f(i1* %ptr) {\n" "entry:\n" " br label %loop.0\n" "loop.0:\n" - " br i1 undef, label %loop.0.0, label %end\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %end\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.1\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n" + "loop.0.1.ph:\n" + " br label %loop.0.1\n" "loop.0.1:\n" - " br i1 undef, label %loop.0.1, label %loop.0.2\n" + " %cond.0.1 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n" + "loop.0.2.ph:\n" + " br label %loop.0.2\n" "loop.0.2:\n" - " br i1 undef, label %loop.0.2.0, label %loop.0\n" + " %cond.0.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2, label %loop.0.2.0.ph, label %loop.0.latch\n" + "loop.0.2.0.ph:\n" + " br label %loop.0.2.0\n" "loop.0.2.0:\n" - " br i1 undef, label %loop.0.2.0, label %loop.0.2\n" + " %cond.0.2.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2.0, label %loop.0.2.0, label %loop.0.2.latch\n" + "loop.0.2.latch:\n" + " br label %loop.0.2\n" + "loop.0.latch:\n" + " br label %loop.0\n" "end:\n" " ret void\n" "}\n"); @@ -1214,44 +1329,64 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { // easily. Function &F = *M->begin(); ASSERT_THAT(F, HasName("f")); + Argument &Ptr = *F.arg_begin(); auto BBI = F.begin(); BasicBlock &EntryBB = *BBI++; ASSERT_THAT(EntryBB, HasName("entry")); BasicBlock &Loop0BB = *BBI++; ASSERT_THAT(Loop0BB, HasName("loop.0")); + BasicBlock &Loop00PHBB = *BBI++; + ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph")); BasicBlock &Loop00BB = *BBI++; ASSERT_THAT(Loop00BB, HasName("loop.0.0")); + BasicBlock &Loop01PHBB = *BBI++; + ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph")); BasicBlock &Loop01BB = *BBI++; ASSERT_THAT(Loop01BB, HasName("loop.0.1")); + BasicBlock &Loop02PHBB = *BBI++; + ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph")); BasicBlock &Loop02BB = *BBI++; ASSERT_THAT(Loop02BB, HasName("loop.0.2")); + BasicBlock &Loop020PHBB = *BBI++; + ASSERT_THAT(Loop020PHBB, HasName("loop.0.2.0.ph")); BasicBlock &Loop020BB = *BBI++; ASSERT_THAT(Loop020BB, HasName("loop.0.2.0")); + BasicBlock &Loop02LatchBB = *BBI++; + ASSERT_THAT(Loop02LatchBB, HasName("loop.0.2.latch")); + BasicBlock &Loop0LatchBB = *BBI++; + ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch")); BasicBlock &EndBB = *BBI++; ASSERT_THAT(EndBB, HasName("end")); ASSERT_THAT(BBI, F.end()); - Constant *Undefi1 = UndefValue::get(Type::getInt1Ty(Context)); // Helper to do the actual deletion of a loop. We directly encode this here // to isolate ourselves from the rest of LLVM and for simplicity. Here we can // egregiously cheat based on knowledge of the test case. For example, we // have no PHI nodes and there is always a single i-dom. - auto DeleteLoopBlocks = [](Loop &L, BasicBlock &IDomBB, + auto RemoveLoop = [](Loop &L, BasicBlock &IDomBB, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { - for (BasicBlock *LoopBB : L.blocks()) { + assert(L.empty() && "Can only delete leaf loops with this routine!"); + SmallVector<BasicBlock *, 4> LoopBBs(L.block_begin(), L.block_end()); + Updater.markLoopAsDeleted(L); + IDomBB.getTerminator()->replaceUsesOfWith(L.getHeader(), + L.getUniqueExitBlock()); + for (BasicBlock *LoopBB : LoopBBs) { SmallVector<DomTreeNode *, 4> ChildNodes(AR.DT[LoopBB]->begin(), AR.DT[LoopBB]->end()); for (DomTreeNode *ChildNode : ChildNodes) AR.DT.changeImmediateDominator(ChildNode, AR.DT[&IDomBB]); AR.DT.eraseNode(LoopBB); + AR.LI.removeBlock(LoopBB); LoopBB->dropAllReferences(); } - SmallVector<BasicBlock *, 4> LoopBBs(L.block_begin(), L.block_end()); - Updater.markLoopAsDeleted(L); - AR.LI.markAsRemoved(&L); for (BasicBlock *LoopBB : LoopBBs) LoopBB->eraseFromParent(); + + if (Loop *ParentL = L.getParentLoop()) + return ParentL->removeChildLoop(find(*ParentL, &L)); + + return AR.LI.removeLoop(find(AR.LI, &L)); }; // Build up the pass managers. @@ -1294,9 +1429,10 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { .WillOnce( Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { + Loop *ParentL = L.getParentLoop(); AR.SE.forgetLoop(&L); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop01BB, &Loop02BB); - DeleteLoopBlocks(L, Loop00BB, AR, Updater); + delete RemoveLoop(L, Loop01PHBB, AR, Updater); + ParentL->verifyLoop(); return PreservedAnalyses::all(); })); @@ -1337,38 +1473,40 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)) .WillOnce(Invoke(getLoopAnalysisResult)); - BasicBlock *NewLoop03BB; + BasicBlock *NewLoop03PHBB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)) .WillOnce( Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { - // Delete the inner loop first. we also do this manually because we - // want to preserve the loop object and reuse it. + // Remove the inner loop first but retain it to reuse later. AR.SE.forgetLoop(*L.begin()); - Loop02BB.getTerminator()->replaceUsesOfWith(&Loop020BB, &Loop02BB); - assert(std::next((*L.begin())->block_begin()) == - (*L.begin())->block_end() && - "There should only be one block."); - assert(AR.DT[&Loop020BB]->getNumChildren() == 0 && - "Cannot have children in the domtree!"); - AR.DT.eraseNode(&Loop020BB); - Updater.markLoopAsDeleted(**L.begin()); - AR.LI.removeBlock(&Loop020BB); - auto *OldL = L.removeChildLoop(L.begin()); - Loop020BB.eraseFromParent(); + auto *OldL = RemoveLoop(**L.begin(), Loop020PHBB, AR, Updater); auto *ParentL = L.getParentLoop(); AR.SE.forgetLoop(&L); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02BB, &Loop0BB); - DeleteLoopBlocks(L, Loop00BB, AR, Updater); + delete RemoveLoop(L, Loop02PHBB, AR, Updater); // Now insert a new sibling loop, reusing a loop pointer. ParentL->addChildLoop(OldL); - NewLoop03BB = BasicBlock::Create(Context, "loop.0.3", &F, &EndBB); - BranchInst::Create(&Loop0BB, NewLoop03BB, Undefi1, NewLoop03BB); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop0BB, NewLoop03BB); - AR.DT.addNewBlock(NewLoop03BB, &Loop00BB); + NewLoop03PHBB = + BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB); + auto *NewLoop03BB = + BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB); + BranchInst::Create(NewLoop03BB, NewLoop03PHBB); + auto *Cond = new LoadInst(&Ptr, "cond.0.3", /*isVolatile*/ true, + NewLoop03BB); + BranchInst::Create(&Loop0LatchBB, NewLoop03BB, Cond, NewLoop03BB); + Loop02PHBB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB, + NewLoop03PHBB); + AR.DT.addNewBlock(NewLoop03PHBB, &Loop02PHBB); + AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB], + AR.DT[NewLoop03BB]); + AR.DT.verifyDomTree(); + ParentL->addBasicBlockToLoop(NewLoop03PHBB, AR.LI); OldL->addBasicBlockToLoop(NewLoop03BB, AR.LI); + OldL->verifyLoop(); + ParentL->verifyLoop(); Updater.addSiblingLoops({OldL}); return PreservedAnalyses::all(); })); @@ -1401,8 +1539,7 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { AR.SE.forgetLoop(&L); - Loop0BB.getTerminator()->replaceUsesOfWith(&Loop00BB, NewLoop03BB); - DeleteLoopBlocks(L, Loop0BB, AR, Updater); + delete RemoveLoop(L, Loop00PHBB, AR, Updater); return PreservedAnalyses::all(); })); @@ -1413,8 +1550,7 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { AR.SE.forgetLoop(&L); - Loop0BB.getTerminator()->replaceUsesOfWith(NewLoop03BB, &Loop0BB); - DeleteLoopBlocks(L, Loop0BB, AR, Updater); + delete RemoveLoop(L, *NewLoop03PHBB, AR, Updater); return PreservedAnalyses::all(); })); @@ -1425,8 +1561,7 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { AR.SE.forgetLoop(&L); - EntryBB.getTerminator()->replaceUsesOfWith(&Loop0BB, &EndBB); - DeleteLoopBlocks(L, EntryBB, AR, Updater); + delete RemoveLoop(L, EntryBB, AR, Updater); return PreservedAnalyses::all(); })); diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt index c0f37418e4928..0fc19ef09fb01 100644 --- a/unittests/Transforms/Utils/CMakeLists.txt +++ b/unittests/Transforms/Utils/CMakeLists.txt @@ -11,6 +11,5 @@ add_llvm_unittest(UtilsTests FunctionComparator.cpp IntegerDivision.cpp Local.cpp - MemorySSA.cpp ValueMapperTest.cpp ) diff --git a/unittests/Transforms/Utils/Cloning.cpp b/unittests/Transforms/Utils/Cloning.cpp index 216bd32c50d2a..403c9c06c18a2 100644 --- a/unittests/Transforms/Utils/Cloning.cpp +++ b/unittests/Transforms/Utils/Cloning.cpp @@ -163,7 +163,7 @@ TEST_F(CloneInstruction, Attributes) { Function *F2 = Function::Create(FT1, Function::ExternalLinkage); Attribute::AttrKind AK[] = { Attribute::NoCapture }; - AttributeSet AS = AttributeSet::get(context, 0, AK); + AttributeList AS = AttributeList::get(context, 0, AK); Argument *A = &*F1->arg_begin(); A->addAttr(AS); @@ -201,6 +201,53 @@ TEST_F(CloneInstruction, CallingConvention) { delete F2; } +TEST_F(CloneInstruction, DuplicateInstructionsToSplit) { + Type *ArgTy1[] = {Type::getInt32PtrTy(context)}; + FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false); + V = new Argument(Type::getInt32Ty(context)); + + Function *F = Function::Create(FT, Function::ExternalLinkage); + + BasicBlock *BB1 = BasicBlock::Create(context, "", F); + IRBuilder<> Builder1(BB1); + + BasicBlock *BB2 = BasicBlock::Create(context, "", F); + IRBuilder<> Builder2(BB2); + + Builder1.CreateBr(BB2); + + Instruction *AddInst = cast<Instruction>(Builder2.CreateAdd(V, V)); + Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V)); + Instruction *SubInst = cast<Instruction>(Builder2.CreateSub(MulInst, V)); + Builder2.CreateRetVoid(); + + ValueToValueMapTy Mapping; + + auto Split = DuplicateInstructionsInSplitBetween(BB2, BB1, SubInst, Mapping); + + EXPECT_TRUE(Split); + EXPECT_EQ(Mapping.size(), 2u); + EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end()); + EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end()); + + auto AddSplit = dyn_cast<Instruction>(Mapping[AddInst]); + EXPECT_TRUE(AddSplit); + EXPECT_EQ(AddSplit->getOperand(0), V); + EXPECT_EQ(AddSplit->getOperand(1), V); + EXPECT_EQ(AddSplit->getParent(), Split); + + auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]); + EXPECT_TRUE(MulSplit); + EXPECT_EQ(MulSplit->getOperand(0), AddSplit); + EXPECT_EQ(MulSplit->getOperand(1), V); + EXPECT_EQ(MulSplit->getParent(), Split); + + EXPECT_EQ(AddSplit->getNextNode(), MulSplit); + EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator()); + + delete F; +} + class CloneFunc : public ::testing::Test { protected: void SetUp() override { @@ -405,10 +452,14 @@ protected: void SetupModule() { OldM = new Module("", C); } void CreateOldModule() { + auto *CD = OldM->getOrInsertComdat("comdat"); + CD->setSelectionKind(Comdat::ExactMatch); + auto GV = new GlobalVariable( *OldM, Type::getInt32Ty(C), false, GlobalValue::ExternalLinkage, ConstantInt::get(Type::getInt32Ty(C), 1), "gv"); GV->addMetadata(LLVMContext::MD_type, *MDNode::get(C, {})); + GV->setComdat(CD); DIBuilder DBuilder(*OldM); IRBuilder<> IBuilder(C); @@ -419,6 +470,7 @@ protected: auto *F = Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", OldM); F->setPersonalityFn(PersFn); + F->setComdat(CD); // Create debug info auto *File = DBuilder.createFile("filename.c", "/file/dir/"); @@ -472,4 +524,15 @@ TEST_F(CloneModule, GlobalMetadata) { GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); EXPECT_NE(nullptr, NewGV->getMetadata(LLVMContext::MD_type)); } + +TEST_F(CloneModule, Comdat) { + GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); + auto *CD = NewGV->getComdat(); + ASSERT_NE(nullptr, CD); + EXPECT_EQ("comdat", CD->getName()); + EXPECT_EQ(Comdat::ExactMatch, CD->getSelectionKind()); + + Function *NewF = NewM->getFunction("f"); + EXPECT_EQ(CD, NewF->getComdat()); +} } diff --git a/unittests/Transforms/Utils/IntegerDivision.cpp b/unittests/Transforms/Utils/IntegerDivision.cpp index b6b1b1665ab1f..e337b9f547a89 100644 --- a/unittests/Transforms/Utils/IntegerDivision.cpp +++ b/unittests/Transforms/Utils/IntegerDivision.cpp @@ -29,7 +29,7 @@ TEST(IntegerDivision, SDiv) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -59,7 +59,7 @@ TEST(IntegerDivision, UDiv) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -89,7 +89,7 @@ TEST(IntegerDivision, SRem) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -119,7 +119,7 @@ TEST(IntegerDivision, URem) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -150,7 +150,7 @@ TEST(IntegerDivision, SDiv64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -180,7 +180,7 @@ TEST(IntegerDivision, UDiv64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -210,7 +210,7 @@ TEST(IntegerDivision, SRem64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -240,7 +240,7 @@ TEST(IntegerDivision, URem64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); diff --git a/unittests/XRay/CMakeLists.txt b/unittests/XRay/CMakeLists.txt new file mode 100644 index 0000000000000..30bccd1bbe626 --- /dev/null +++ b/unittests/XRay/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +set(XRAYSources + GraphTest.cpp + ) + +add_llvm_unittest(XRayTests + ${XRAYSources} + ) + +add_dependencies(XRayTests intrinsics_gen) diff --git a/unittests/XRay/GraphTest.cpp b/unittests/XRay/GraphTest.cpp new file mode 100644 index 0000000000000..b17858f0c1c0b --- /dev/null +++ b/unittests/XRay/GraphTest.cpp @@ -0,0 +1,261 @@ +//===- llvm/unittest/XRay/GraphTest.cpp - XRay Graph unit tests -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/XRay/Graph.h" +#include "gtest/gtest.h" +#include <iostream> +#include <set> +#include <type_traits> + +using namespace llvm; +using namespace xray; + +namespace { +struct VAttr { + unsigned VA; +}; +struct EAttr { + unsigned EA; +}; +typedef Graph<VAttr, EAttr, unsigned> GraphT; +typedef typename GraphT::VertexIdentifier VI; +typedef typename GraphT::EdgeIdentifier EI; + +// Test Fixture +template <typename T> class GraphTest : public testing::Test { +protected: + T Graph = getTestGraph(); + +private: + static T getTestGraph() { + using std::make_pair; + typename std::remove_const<T>::type G; + G.insert(make_pair(1u, VAttr({3u}))); + G.insert(make_pair(2u, VAttr({5u}))); + G.insert(make_pair(3u, VAttr({7u}))); + G.insert(make_pair(4u, VAttr({11u}))); + G.insert(make_pair(5u, VAttr({13u}))); + G.insert(make_pair(6u, VAttr({17u}))); + + G.insert(std::make_pair(EI(1u, 2u), EAttr({3u * 5u}))); + G.insert(std::make_pair(EI(2u, 3u), EAttr({5u * 7u}))); + G.insert(std::make_pair(EI(6u, 3u), EAttr({2u * 7u * 17u}))); + G.insert(std::make_pair(EI(4u, 6u), EAttr({11u * 17u}))); + G.insert(std::make_pair(EI(2u, 4u), EAttr({5u * 11u}))); + G.insert(std::make_pair(EI(2u, 5u), EAttr({5u * 13u}))); + G.insert(std::make_pair(EI(4u, 5u), EAttr({11u * 13u}))); + + return G; + } +}; + +typedef ::testing::Types<GraphT, const GraphT> GraphTestTypes; + +using VVT = typename GraphT::VertexValueType; +using EVT = typename GraphT::EdgeValueType; + +TYPED_TEST_CASE(GraphTest, GraphTestTypes); + +template <typename T> void graphVertexTester(T &G) { + std::set<unsigned> V({1u, 2u, 3u, 4u, 5u, 6u}); + std::vector<unsigned> VA({0u, 3u, 5u, 7u, 11u, 13u, 17u}); + + EXPECT_EQ(V.size(), G.vertices().size()); + EXPECT_FALSE(G.vertices().empty()); + for (unsigned u : V) { + auto EVV = G.at(u); + ASSERT_TRUE(!!EVV); + EXPECT_EQ(1u, G.count(u)); + EXPECT_EQ(VA[u], EVV->VA); + EXPECT_NE(G.vertices().end(), + std::find_if(G.vertices().begin(), G.vertices().end(), + [&](const VVT &VV) { return VV.first == u; })); + consumeError(EVV.takeError()); + } + + for (auto &VVT : G.vertices()) { + EXPECT_EQ(1u, V.count(VVT.first)); + EXPECT_EQ(VA[VVT.first], VVT.second.VA); + } +} + +template <typename T> void graphEdgeTester(T &G) { + std::set<unsigned> V({1u, 2u, 3u, 4u, 5u, 6u}); + + std::set<std::pair<unsigned, unsigned>> E( + {{1u, 2u}, {2u, 3u}, {6u, 3u}, {4u, 6u}, {2u, 4u}, {2u, 5u}, {4u, 5u}}); + std::vector<unsigned> VA({0u, 3u, 5u, 7u, 11u, 13u, 17u}); + + EXPECT_EQ(E.size(), G.edges().size()); + EXPECT_FALSE(G.edges().empty()); + for (std::pair<unsigned, unsigned> u : E) { + auto EEV = G.at(u); + ASSERT_TRUE(!!EEV); + EXPECT_EQ(1u, G.count(u)); + EXPECT_EQ(VA[u.first] * VA[u.second] * ((u.first > u.second) ? 2 : 1), + EEV->EA); + auto Pred = [&](const EVT &EV) { return EV.first == u; }; + EXPECT_NE(G.edges().end(), + std::find_if(G.edges().begin(), G.edges().end(), Pred)); + consumeError(EEV.takeError()); + } + + for (auto &EV : G.edges()) { + EXPECT_EQ(1u, E.count(EV.first)); + EXPECT_EQ(VA[EV.first.first] * VA[EV.first.second] * + ((EV.first.first > EV.first.second) ? 2 : 1), + EV.second.EA); + const auto &IE = G.inEdges(EV.first.second); + const auto &OE = G.outEdges(EV.first.first); + EXPECT_NE(IE.size(), 0u); + EXPECT_NE(OE.size(), 0u); + EXPECT_NE(IE.begin(), IE.end()); + EXPECT_NE(OE.begin(), OE.end()); + { + auto It = std::find_if( + G.inEdges(EV.first.second).begin(), G.inEdges(EV.first.second).end(), + [&](const EVT &EVI) { return EVI.first == EV.first; }); + EXPECT_NE(G.inEdges(EV.first.second).end(), It); + } + { + auto It = std::find_if( + G.inEdges(EV.first.first).begin(), G.inEdges(EV.first.first).end(), + [&](const EVT &EVI) { return EVI.first == EV.first; }); + EXPECT_EQ(G.inEdges(EV.first.first).end(), It); + } + { + auto It = + std::find_if(G.outEdges(EV.first.second).begin(), + G.outEdges(EV.first.second).end(), + [&](const EVT &EVI) { return EVI.first == EV.first; }); + EXPECT_EQ(G.outEdges(EV.first.second).end(), It); + } + { + auto It = std::find_if( + G.outEdges(EV.first.first).begin(), G.outEdges(EV.first.first).end(), + [&](const EVT &EVI) { return EVI.first == EV.first; }); + EXPECT_NE(G.outEdges(EV.first.first).end(), It); + } + } +} + +TYPED_TEST(GraphTest, TestGraphEdge) { + auto &G = this->Graph; + + graphEdgeTester(G); +} + +TYPED_TEST(GraphTest, TestGraphVertex) { + auto &G = this->Graph; + + graphVertexTester(G); +} + +TYPED_TEST(GraphTest, TestCopyConstructor) { + TypeParam G(this->Graph); + + graphEdgeTester(G); + graphVertexTester(G); +} + +TYPED_TEST(GraphTest, TestCopyAssign) { + TypeParam G = this->Graph; + + graphEdgeTester(G); + graphVertexTester(G); +} + +TYPED_TEST(GraphTest, TestMoveConstructor) { + TypeParam G(std::move(this->Graph)); + + graphEdgeTester(G); + graphVertexTester(G); +} + +// Tests the incremental Construction of a graph +TEST(GraphTest, TestConstruction) { + GraphT MG; + const GraphT &G = MG; + EXPECT_EQ(0u, G.count(0u)); + EXPECT_EQ(0u, G.count({0u, 1u})); + auto VE = G.at(0); + auto EE = G.at({0, 0}); + EXPECT_FALSE(VE); // G.at[0] returns an error + EXPECT_FALSE(EE); // G.at[{0,0}] returns an error + consumeError(VE.takeError()); + consumeError(EE.takeError()); + EXPECT_TRUE(G.vertices().empty()); + EXPECT_TRUE(G.edges().empty()); + EXPECT_EQ(G.vertices().begin(), G.vertices().end()); + EXPECT_EQ(G.edges().begin(), G.edges().end()); +} + +TEST(GraphTest, TestiVertexAccessOperator) { + GraphT MG; + const GraphT &G = MG; + + MG[0u] = {1u}; + EXPECT_EQ(1u, MG[0u].VA); + EXPECT_EQ(1u, G.count(0u)); + EXPECT_EQ(0u, G.count(1u)); + EXPECT_EQ(1u, MG[0u].VA); + auto T = G.at(0u); + EXPECT_TRUE(!!T); + EXPECT_EQ(1u, T->VA); + + EXPECT_EQ(1u, G.vertices().size()); + EXPECT_EQ(0u, G.edges().size()); + EXPECT_FALSE(G.vertices().empty()); + EXPECT_TRUE(G.edges().empty()); + EXPECT_NE(G.vertices().begin(), G.vertices().end()); + EXPECT_EQ(G.edges().begin(), G.edges().end()); + EXPECT_EQ(1u, G.vertices().begin()->second.VA); + EXPECT_EQ(0u, G.vertices().begin()->first); + EXPECT_EQ(0u, G.outEdges(0u).size()); + EXPECT_TRUE(G.outEdges(0u).empty()); + EXPECT_EQ(G.outEdges(0u).begin(), G.outEdges(0u).end()); + EXPECT_EQ(0u, G.inEdges(0u).size()); + EXPECT_TRUE(G.inEdges(0u).empty()); + EXPECT_EQ(G.inEdges(0u).begin(), G.inEdges(0u).end()); +} + +TEST(GraphTest, TestEdgeAccessOperator) { + GraphT MG; + const GraphT &G = MG; + + MG[{0u, 0u}] = {2u}; + EI EdgeIdent({0u, 0u}); + EXPECT_EQ(2u, MG[EdgeIdent].EA); + EXPECT_EQ(1u, G.count({0u, 0u})); + EXPECT_EQ(0u, G.count({0u, 1u})); + EXPECT_EQ(1u, G.count(0u)); + EXPECT_NE(1u, G.count(1u)); + auto T = G.at({0u, 0u}); + EXPECT_TRUE(T && T->EA == 2u); + EXPECT_EQ(1u, G.edges().size()); + EXPECT_EQ(1u, G.vertices().size()); + EXPECT_FALSE(G.edges().empty()); + EXPECT_FALSE(G.vertices().empty()); + EXPECT_NE(G.edges().begin(), G.edges().end()); + EXPECT_EQ(EI(0u, 0u), G.edges().begin()->first); + EXPECT_EQ(2u, G.edges().begin()->second.EA); + EXPECT_EQ(1u, G.outEdges(0u).size()); + EXPECT_FALSE(G.outEdges(0u).empty()); + EXPECT_NE(G.outEdges(0u).begin(), G.outEdges(0u).end()); + EXPECT_EQ(EI(0u, 0u), G.outEdges(0u).begin()->first); + EXPECT_EQ(2u, G.outEdges(0u).begin()->second.EA); + EXPECT_EQ(++(G.outEdges(0u).begin()), G.outEdges(0u).end()); + EXPECT_EQ(1u, G.inEdges(0u).size()); + EXPECT_FALSE(G.inEdges(0u).empty()); + EXPECT_NE(G.inEdges(0u).begin(), G.inEdges(0u).end()); + EXPECT_EQ(EI(0u, 0u), G.inEdges(0u).begin()->first); + EXPECT_EQ(2u, G.inEdges(0u).begin()->second.EA); + EXPECT_EQ(++(G.inEdges(0u).begin()), G.inEdges(0u).end()); +} +} |