diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:41:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:41:05 +0000 |
commit | 01095a5d43bbfde13731688ddcf6048ebb8b7721 (patch) | |
tree | 4def12e759965de927d963ac65840d663ef9d1ea /unittests | |
parent | f0f4822ed4b66e3579e92a89f368f8fb860e218e (diff) |
Notes
Diffstat (limited to 'unittests')
144 files changed, 10383 insertions, 2005 deletions
diff --git a/unittests/ADT/APFloatTest.cpp b/unittests/ADT/APFloatTest.cpp index 55c3f48f00d4..18734eb72b82 100644 --- a/unittests/ADT/APFloatTest.cpp +++ b/unittests/ADT/APFloatTest.cpp @@ -9,7 +9,6 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" @@ -140,14 +139,14 @@ TEST(APFloatTest, next) { test = APFloat(APFloat::IEEEquad, "0x0.0000000000000000000000000001p-16382"); expected = APFloat::getZero(APFloat::IEEEquad, false); EXPECT_EQ(test.next(true), APFloat::opOK); - EXPECT_TRUE(test.isZero() && !test.isNegative()); + EXPECT_TRUE(test.isPosZero()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(-getSmallest()) = -0. test = APFloat(APFloat::IEEEquad, "-0x0.0000000000000000000000000001p-16382"); expected = APFloat::getZero(APFloat::IEEEquad, true); EXPECT_EQ(test.next(false), APFloat::opOK); - EXPECT_TRUE(test.isZero() && test.isNegative()); + EXPECT_TRUE(test.isNegZero()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-getSmallest()) = -nextUp(getSmallest()) = -getSmallest() - inc. @@ -1314,7 +1313,7 @@ TEST(APFloatTest, roundToIntegral) { P.roundToIntegral(APFloat::rmTowardZero); EXPECT_TRUE(std::isinf(P.convertToDouble()) && P.convertToDouble() < 0.0); } - + TEST(APFloatTest, isInteger) { APFloat T(-0.0); EXPECT_TRUE(T.isInteger()); @@ -2821,6 +2820,19 @@ TEST(APFloatTest, abs) { } TEST(APFloatTest, ilogb) { + EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble, false))); + EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble, true))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1024"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023"))); + EXPECT_EQ(-51, ilogb(APFloat(APFloat::IEEEdouble, "0x1p-51"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1023"))); + EXPECT_EQ(-2, ilogb(APFloat(APFloat::IEEEdouble, "0x0.ffffp-1"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.fffep-1023"))); + EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble, false))); + EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble, true))); + + EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle, "0x1p+0"))); EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle, "-0x1p+0"))); EXPECT_EQ(42, ilogb(APFloat(APFloat::IEEEsingle, "0x1p+42"))); @@ -2841,8 +2853,9 @@ TEST(APFloatTest, ilogb) { EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle, false))); EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle, true))); - EXPECT_EQ(-126, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, false))); - EXPECT_EQ(-126, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, true))); + + EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, false))); + EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, true))); EXPECT_EQ(-126, ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle, false))); EXPECT_EQ(-126, @@ -2850,15 +2863,17 @@ TEST(APFloatTest, ilogb) { } TEST(APFloatTest, scalbn) { + + const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven; EXPECT_TRUE( APFloat(APFloat::IEEEsingle, "0x1p+0") - .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 0))); + .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 0, RM))); EXPECT_TRUE( APFloat(APFloat::IEEEsingle, "0x1p+42") - .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 42))); + .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 42, RM))); EXPECT_TRUE( APFloat(APFloat::IEEEsingle, "0x1p-42") - .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -42))); + .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -42, RM))); APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false); APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true); @@ -2868,27 +2883,279 @@ TEST(APFloatTest, scalbn) { APFloat QMNaN = APFloat::getNaN(APFloat::IEEEsingle, true); APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false); - EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0))); - EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0))); - EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(PZero, 0))); - EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(MZero, 0))); - EXPECT_TRUE(QPNaN.bitwiseIsEqual(scalbn(QPNaN, 0))); - EXPECT_TRUE(QMNaN.bitwiseIsEqual(scalbn(QMNaN, 0))); - EXPECT_TRUE(SNaN.bitwiseIsEqual(scalbn(SNaN, 0))); - - EXPECT_TRUE( - PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 128))); + EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0, RM))); + EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0, RM))); + EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(PZero, 0, RM))); + EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(MZero, 0, RM))); + EXPECT_TRUE(QPNaN.bitwiseIsEqual(scalbn(QPNaN, 0, RM))); + EXPECT_TRUE(QMNaN.bitwiseIsEqual(scalbn(QMNaN, 0, RM))); + EXPECT_FALSE(scalbn(SNaN, 0, RM).isSignaling()); + + APFloat ScalbnSNaN = scalbn(SNaN, 1, RM); + EXPECT_TRUE(ScalbnSNaN.isNaN() && !ScalbnSNaN.isSignaling()); + + // Make sure highest bit of payload is preserved. + const APInt Payload(64, (UINT64_C(1) << 50) | + (UINT64_C(1) << 49) | + (UINT64_C(1234) << 32) | + 1); + + APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble, false, + &Payload); + APFloat QuietPayload = scalbn(SNaNWithPayload, 1, RM); + EXPECT_TRUE(QuietPayload.isNaN() && !QuietPayload.isSignaling()); + EXPECT_EQ(Payload, QuietPayload.bitcastToAPInt().getLoBits(51)); + + EXPECT_TRUE(PInf.bitwiseIsEqual( + scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 128, RM))); EXPECT_TRUE(MInf.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), 128))); - EXPECT_TRUE( - PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+127"), 1))); + scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), 128, RM))); + EXPECT_TRUE(PInf.bitwiseIsEqual( + scalbn(APFloat(APFloat::IEEEsingle, "0x1p+127"), 1, RM))); EXPECT_TRUE(PZero.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -127))); + scalbn(APFloat(APFloat::IEEEsingle, "0x1p-127"), -127, RM))); EXPECT_TRUE(MZero.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), -127))); + scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -127, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "-0x1p-149").bitwiseIsEqual( + scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -22, RM))); EXPECT_TRUE(PZero.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -1))); - EXPECT_TRUE(PZero.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -1))); + scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -24, RM))); + + + APFloat SmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, false); + APFloat NegSmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, true); + + APFloat LargestF64 = APFloat::getLargest(APFloat::IEEEdouble, false); + APFloat NegLargestF64 = APFloat::getLargest(APFloat::IEEEdouble, true); + + APFloat SmallestNormalizedF64 + = APFloat::getSmallestNormalized(APFloat::IEEEdouble, false); + APFloat NegSmallestNormalizedF64 + = APFloat::getSmallestNormalized(APFloat::IEEEdouble, true); + + APFloat LargestDenormalF64(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023"); + APFloat NegLargestDenormalF64(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023"); + + + EXPECT_TRUE(SmallestF64.bitwiseIsEqual( + scalbn(APFloat(APFloat::IEEEdouble, "0x1p-1074"), 0, RM))); + EXPECT_TRUE(NegSmallestF64.bitwiseIsEqual( + scalbn(APFloat(APFloat::IEEEdouble, "-0x1p-1074"), 0, RM))); + + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023") + .bitwiseIsEqual(scalbn(SmallestF64, 2097, RM))); + + EXPECT_TRUE(scalbn(SmallestF64, -2097, RM).isPosZero()); + EXPECT_TRUE(scalbn(SmallestF64, -2098, RM).isPosZero()); + EXPECT_TRUE(scalbn(SmallestF64, -2099, RM).isPosZero()); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1022") + .bitwiseIsEqual(scalbn(SmallestF64, 2096, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023") + .bitwiseIsEqual(scalbn(SmallestF64, 2097, RM))); + EXPECT_TRUE(scalbn(SmallestF64, 2098, RM).isInfinity()); + EXPECT_TRUE(scalbn(SmallestF64, 2099, RM).isInfinity()); + + // Test for integer overflows when adding to exponent. + EXPECT_TRUE(scalbn(SmallestF64, -INT_MAX, RM).isPosZero()); + EXPECT_TRUE(scalbn(LargestF64, INT_MAX, RM).isInfinity()); + + EXPECT_TRUE(LargestDenormalF64 + .bitwiseIsEqual(scalbn(LargestDenormalF64, 0, RM))); + EXPECT_TRUE(NegLargestDenormalF64 + .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 0, RM))); + + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1022") + .bitwiseIsEqual(scalbn(LargestDenormalF64, 1, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1021") + .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 2, RM))); + + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1") + .bitwiseIsEqual(scalbn(LargestDenormalF64, 1024, RM))); + EXPECT_TRUE(scalbn(LargestDenormalF64, -1023, RM).isPosZero()); + EXPECT_TRUE(scalbn(LargestDenormalF64, -1024, RM).isPosZero()); + EXPECT_TRUE(scalbn(LargestDenormalF64, -2048, RM).isPosZero()); + EXPECT_TRUE(scalbn(LargestDenormalF64, 2047, RM).isInfinity()); + EXPECT_TRUE(scalbn(LargestDenormalF64, 2098, RM).isInfinity()); + EXPECT_TRUE(scalbn(LargestDenormalF64, 2099, RM).isInfinity()); + + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-2") + .bitwiseIsEqual(scalbn(LargestDenormalF64, 1021, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1") + .bitwiseIsEqual(scalbn(LargestDenormalF64, 1022, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+0") + .bitwiseIsEqual(scalbn(LargestDenormalF64, 1023, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1023") + .bitwiseIsEqual(scalbn(LargestDenormalF64, 2046, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+974") + .bitwiseIsEqual(scalbn(SmallestF64, 2048, RM))); + + APFloat RandomDenormalF64(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+51"); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-972") + .bitwiseIsEqual(scalbn(RandomDenormalF64, -1023, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1") + .bitwiseIsEqual(scalbn(RandomDenormalF64, -52, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-2") + .bitwiseIsEqual(scalbn(RandomDenormalF64, -53, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+0") + .bitwiseIsEqual(scalbn(RandomDenormalF64, -51, RM))); + + EXPECT_TRUE(scalbn(RandomDenormalF64, -2097, RM).isPosZero()); + EXPECT_TRUE(scalbn(RandomDenormalF64, -2090, RM).isPosZero()); + + + EXPECT_TRUE( + APFloat(APFloat::IEEEdouble, "-0x1p-1073") + .bitwiseIsEqual(scalbn(NegLargestF64, -2097, RM))); + + EXPECT_TRUE( + APFloat(APFloat::IEEEdouble, "-0x1p-1024") + .bitwiseIsEqual(scalbn(NegLargestF64, -2048, RM))); + + EXPECT_TRUE( + APFloat(APFloat::IEEEdouble, "0x1p-1073") + .bitwiseIsEqual(scalbn(LargestF64, -2097, RM))); + + EXPECT_TRUE( + APFloat(APFloat::IEEEdouble, "0x1p-1074") + .bitwiseIsEqual(scalbn(LargestF64, -2098, RM))); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1074") + .bitwiseIsEqual(scalbn(NegLargestF64, -2098, RM))); + EXPECT_TRUE(scalbn(NegLargestF64, -2099, RM).isNegZero()); + EXPECT_TRUE(scalbn(LargestF64, 1, RM).isInfinity()); + + + EXPECT_TRUE( + APFloat(APFloat::IEEEdouble, "0x1p+0") + .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble, "0x1p+52"), -52, RM))); + + EXPECT_TRUE( + APFloat(APFloat::IEEEdouble, "0x1p-103") + .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble, "0x1p-51"), -52, RM))); +} + +TEST(APFloatTest, frexp) { + const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven; + + APFloat PZero = APFloat::getZero(APFloat::IEEEdouble, false); + APFloat MZero = APFloat::getZero(APFloat::IEEEdouble, true); + APFloat One(1.0); + APFloat MOne(-1.0); + APFloat Two(2.0); + APFloat MTwo(-2.0); + + APFloat LargestDenormal(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023"); + APFloat NegLargestDenormal(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023"); + + APFloat Smallest = APFloat::getSmallest(APFloat::IEEEdouble, false); + APFloat NegSmallest = APFloat::getSmallest(APFloat::IEEEdouble, true); + + APFloat Largest = APFloat::getLargest(APFloat::IEEEdouble, false); + APFloat NegLargest = APFloat::getLargest(APFloat::IEEEdouble, true); + + APFloat PInf = APFloat::getInf(APFloat::IEEEdouble, false); + APFloat MInf = APFloat::getInf(APFloat::IEEEdouble, true); + + APFloat QPNaN = APFloat::getNaN(APFloat::IEEEdouble, false); + APFloat QMNaN = APFloat::getNaN(APFloat::IEEEdouble, true); + APFloat SNaN = APFloat::getSNaN(APFloat::IEEEdouble, false); + + // Make sure highest bit of payload is preserved. + const APInt Payload(64, (UINT64_C(1) << 50) | + (UINT64_C(1) << 49) | + (UINT64_C(1234) << 32) | + 1); + + APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble, false, + &Payload); + + APFloat SmallestNormalized + = APFloat::getSmallestNormalized(APFloat::IEEEdouble, false); + APFloat NegSmallestNormalized + = APFloat::getSmallestNormalized(APFloat::IEEEdouble, true); + + int Exp; + APFloat Frac(APFloat::IEEEdouble); + + + Frac = frexp(PZero, Exp, RM); + EXPECT_EQ(0, Exp); + EXPECT_TRUE(Frac.isPosZero()); + + Frac = frexp(MZero, Exp, RM); + EXPECT_EQ(0, Exp); + EXPECT_TRUE(Frac.isNegZero()); + + + Frac = frexp(One, Exp, RM); + EXPECT_EQ(1, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac)); + + Frac = frexp(MOne, Exp, RM); + EXPECT_EQ(1, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1").bitwiseIsEqual(Frac)); + + Frac = frexp(LargestDenormal, Exp, RM); + EXPECT_EQ(-1022, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1").bitwiseIsEqual(Frac)); + + Frac = frexp(NegLargestDenormal, Exp, RM); + EXPECT_EQ(-1022, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1").bitwiseIsEqual(Frac)); + + + Frac = frexp(Smallest, Exp, RM); + EXPECT_EQ(-1073, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac)); + + Frac = frexp(NegSmallest, Exp, RM); + EXPECT_EQ(-1073, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1").bitwiseIsEqual(Frac)); + + + Frac = frexp(Largest, Exp, RM); + EXPECT_EQ(1024, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.fffffffffffffp-1").bitwiseIsEqual(Frac)); + + Frac = frexp(NegLargest, Exp, RM); + EXPECT_EQ(1024, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.fffffffffffffp-1").bitwiseIsEqual(Frac)); + + + Frac = frexp(PInf, Exp, RM); + EXPECT_EQ(INT_MAX, Exp); + EXPECT_TRUE(Frac.isInfinity() && !Frac.isNegative()); + + Frac = frexp(MInf, Exp, RM); + EXPECT_EQ(INT_MAX, Exp); + EXPECT_TRUE(Frac.isInfinity() && Frac.isNegative()); + + Frac = frexp(QPNaN, Exp, RM); + EXPECT_EQ(INT_MIN, Exp); + EXPECT_TRUE(Frac.isNaN()); + + Frac = frexp(QMNaN, Exp, RM); + EXPECT_EQ(INT_MIN, Exp); + EXPECT_TRUE(Frac.isNaN()); + + Frac = frexp(SNaN, Exp, RM); + EXPECT_EQ(INT_MIN, Exp); + EXPECT_TRUE(Frac.isNaN() && !Frac.isSignaling()); + + Frac = frexp(SNaNWithPayload, Exp, RM); + EXPECT_EQ(INT_MIN, Exp); + EXPECT_TRUE(Frac.isNaN() && !Frac.isSignaling()); + EXPECT_EQ(Payload, Frac.bitcastToAPInt().getLoBits(51)); + + Frac = frexp(APFloat(APFloat::IEEEdouble, "0x0.ffffp-1"), Exp, RM); + EXPECT_EQ(-1, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.fffep-1").bitwiseIsEqual(Frac)); + + Frac = frexp(APFloat(APFloat::IEEEdouble, "0x1p-51"), Exp, RM); + EXPECT_EQ(-50, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac)); + + Frac = frexp(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+51"), Exp, RM); + EXPECT_EQ(52, Exp); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac)); } } diff --git a/unittests/ADT/APIntTest.cpp b/unittests/ADT/APIntTest.cpp index 0002dad8555f..b0d80c3c6819 100644 --- a/unittests/ADT/APIntTest.cpp +++ b/unittests/ADT/APIntTest.cpp @@ -8,10 +8,10 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "gtest/gtest.h" #include <array> -#include <ostream> using namespace llvm; @@ -388,6 +388,34 @@ TEST(APIntTest, compareWithHalfInt64Max) { EXPECT_TRUE( a.sge(edgeM1)); } +TEST(APIntTest, compareLargeIntegers) { + // Make sure all the combinations of signed comparisons work with big ints. + auto One = APInt{128, static_cast<uint64_t>(1), true}; + auto Two = APInt{128, static_cast<uint64_t>(2), true}; + auto MinusOne = APInt{128, static_cast<uint64_t>(-1), true}; + auto MinusTwo = APInt{128, static_cast<uint64_t>(-2), true}; + + EXPECT_TRUE(!One.slt(One)); + EXPECT_TRUE(!Two.slt(One)); + EXPECT_TRUE(MinusOne.slt(One)); + EXPECT_TRUE(MinusTwo.slt(One)); + + EXPECT_TRUE(One.slt(Two)); + EXPECT_TRUE(!Two.slt(Two)); + EXPECT_TRUE(MinusOne.slt(Two)); + EXPECT_TRUE(MinusTwo.slt(Two)); + + EXPECT_TRUE(!One.slt(MinusOne)); + EXPECT_TRUE(!Two.slt(MinusOne)); + EXPECT_TRUE(!MinusOne.slt(MinusOne)); + EXPECT_TRUE(MinusTwo.slt(MinusOne)); + + EXPECT_TRUE(!One.slt(MinusTwo)); + EXPECT_TRUE(!Two.slt(MinusTwo)); + EXPECT_TRUE(!MinusOne.slt(MinusTwo)); + EXPECT_TRUE(!MinusTwo.slt(MinusTwo)); +} + // Tests different div/rem varaints using scheme (a * b + c) / a void testDiv(APInt a, APInt b, APInt c) { @@ -994,6 +1022,23 @@ TEST(APIntTest, IsSplat) { EXPECT_TRUE(E.isSplat(32)); } +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))); + + for (int N : { 1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256 }) { + EXPECT_FALSE(APIntOps::isMask(APInt(N, 0))); + + APInt One(N, 1); + for (int I = 1; I <= N; ++I) { + APInt MaskVal = One.shl(I) - 1; + EXPECT_TRUE(APIntOps::isMask(MaskVal)); + } + } +} + #if defined(__clang__) // Disable the pragma warning from versions of Clang without -Wself-move #pragma clang diagnostic push @@ -1023,3 +1068,45 @@ TEST(APIntTest, SelfMoveAssignment) { #pragma clang diagnostic pop #endif } + +TEST(APIntTest, reverseBits) { + EXPECT_EQ(1, APInt(1, 1).reverseBits()); + EXPECT_EQ(0, APInt(1, 0).reverseBits()); + + EXPECT_EQ(3, APInt(2, 3).reverseBits()); + EXPECT_EQ(3, APInt(2, 3).reverseBits()); + + EXPECT_EQ(0xb, APInt(4, 0xd).reverseBits()); + EXPECT_EQ(0xd, APInt(4, 0xb).reverseBits()); + EXPECT_EQ(0xf, APInt(4, 0xf).reverseBits()); + + EXPECT_EQ(0x30, APInt(7, 0x6).reverseBits()); + EXPECT_EQ(0x5a, APInt(7, 0x2d).reverseBits()); + + EXPECT_EQ(0x0f, APInt(8, 0xf0).reverseBits()); + EXPECT_EQ(0xf0, APInt(8, 0x0f).reverseBits()); + + EXPECT_EQ(0x0f0f, APInt(16, 0xf0f0).reverseBits()); + EXPECT_EQ(0xf0f0, APInt(16, 0x0f0f).reverseBits()); + + EXPECT_EQ(0x0f0f0f0f, APInt(32, 0xf0f0f0f0).reverseBits()); + EXPECT_EQ(0xf0f0f0f0, APInt(32, 0x0f0f0f0f).reverseBits()); + + EXPECT_EQ(0x402880a0 >> 1, APInt(31, 0x05011402).reverseBits()); + + EXPECT_EQ(0x0f0f0f0f, APInt(32, 0xf0f0f0f0).reverseBits()); + EXPECT_EQ(0xf0f0f0f0, APInt(32, 0x0f0f0f0f).reverseBits()); + + EXPECT_EQ(0x0f0f0f0f0f0f0f0f, APInt(64, 0xf0f0f0f0f0f0f0f0).reverseBits()); + EXPECT_EQ(0xf0f0f0f0f0f0f0f0, APInt(64, 0x0f0f0f0f0f0f0f0f).reverseBits()); + + for (unsigned N : { 1, 8, 16, 24, 31, 32, 33, + 63, 64, 65, 127, 128, 257, 1024 }) { + for (unsigned I = 0; I < N; ++I) { + APInt X = APInt::getOneBitSet(N, I); + APInt Y = APInt::getOneBitSet(N, N - (I + 1)); + EXPECT_EQ(Y, X.reverseBits()); + EXPECT_EQ(X, Y.reverseBits()); + } + } +} diff --git a/unittests/ADT/ArrayRefTest.cpp b/unittests/ADT/ArrayRefTest.cpp index 6cbadd6bc228..b5b71f06f65b 100644 --- a/unittests/ADT/ArrayRefTest.cpp +++ b/unittests/ADT/ArrayRefTest.cpp @@ -65,6 +65,21 @@ TEST(ArrayRefTest, DropBack) { ArrayRef<int> AR1(TheNumbers); ArrayRef<int> AR2(TheNumbers, AR1.size() - 1); EXPECT_TRUE(AR1.drop_back().equals(AR2)); + + // Check that drop_back accepts size_t-sized numbers. + ArrayRef<char> AR3((const char *)0x10000, SIZE_MAX - 0x10000); + EXPECT_EQ(1U, AR3.drop_back(AR3.size() - 1).size()); +} + +TEST(ArrayRefTest, DropFront) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef<int> AR1(TheNumbers); + ArrayRef<int> AR2(&TheNumbers[2], AR1.size() - 2); + EXPECT_TRUE(AR1.drop_front(2).equals(AR2)); + + // Check that drop_front accepts size_t-sized numbers. + ArrayRef<char> AR3((const char *)0x10000, SIZE_MAX - 0x10000); + EXPECT_EQ(1U, AR3.drop_front(AR3.size() - 1).size()); } TEST(ArrayRefTest, Equals) { @@ -94,6 +109,13 @@ TEST(ArrayRefTest, EmptyEquals) { EXPECT_TRUE(ArrayRef<unsigned>() == ArrayRef<unsigned>()); } +TEST(ArrayRefTest, Slice) { + // Check that slice accepts size_t-sized numbers. + ArrayRef<char> AR((const char *)0x10000, SIZE_MAX - 0x10000); + EXPECT_EQ(1U, AR.slice(AR.size() - 1).size()); + EXPECT_EQ(AR.size() - 1, AR.slice(1, AR.size() - 1).size()); +} + TEST(ArrayRefTest, ConstConvert) { int buf[4]; for (int i = 0; i < 4; ++i) diff --git a/unittests/ADT/BitVectorTest.cpp b/unittests/ADT/BitVectorTest.cpp index 95ff93fa9c4c..78fd5ce65677 100644 --- a/unittests/ADT/BitVectorTest.cpp +++ b/unittests/ADT/BitVectorTest.cpp @@ -399,5 +399,31 @@ TYPED_TEST(BitVectorTest, CompoundTestReset) { C.reset(C); EXPECT_TRUE(C.none()); } + +TYPED_TEST(BitVectorTest, MoveConstructor) { + TypeParam A(10, true); + TypeParam B(std::move(A)); + // Check that the move ctor leaves the moved-from object in a valid state. + // The following line used to crash. + A = B; + + TypeParam C(10, true); + EXPECT_EQ(C, A); + EXPECT_EQ(C, B); +} + +TYPED_TEST(BitVectorTest, MoveAssignment) { + TypeParam A(10, true); + TypeParam B; + B = std::move(A); + // Check that move assignment leaves the moved-from object in a valid state. + // The following line used to crash. + A = B; + + TypeParam C(10, true); + EXPECT_EQ(C, A); + EXPECT_EQ(C, B); +} + } #endif diff --git a/unittests/ADT/BitmaskEnumTest.cpp b/unittests/ADT/BitmaskEnumTest.cpp new file mode 100644 index 000000000000..77635c638e93 --- /dev/null +++ b/unittests/ADT/BitmaskEnumTest.cpp @@ -0,0 +1,134 @@ +//===- llvm/unittest/ADT/BitmaskEnumTest.cpp - BitmaskEnum 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/ADT/BitmaskEnum.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { +enum Flags { + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 4, + F4 = 8, + LLVM_MARK_AS_BITMASK_ENUM(F4) +}; + +TEST(BitmaskEnumTest, BitwiseOr) { + Flags f = F1 | F2; + EXPECT_EQ(3, f); + + f = f | F3; + EXPECT_EQ(7, f); +} + +TEST(BitmaskEnumTest, BitwiseOrEquals) { + Flags f = F1; + f |= F3; + EXPECT_EQ(5, f); + + // |= should return a reference to the LHS. + f = F2; + (f |= F3) = F1; + EXPECT_EQ(F1, f); +} + +TEST(BitmaskEnumTest, BitwiseAnd) { + Flags f = static_cast<Flags>(3) & F2; + EXPECT_EQ(F2, f); + + f = (f | F3) & (F1 | F2 | F3); + EXPECT_EQ(6, f); +} + +TEST(BitmaskEnumTest, BitwiseAndEquals) { + Flags f = F1 | F2 | F3; + f &= F1 | F2; + EXPECT_EQ(3, f); + + // &= should return a reference to the LHS. + (f &= F1) = F3; + EXPECT_EQ(F3, f); +} + +TEST(BitmaskEnumTest, BitwiseXor) { + Flags f = (F1 | F2) ^ (F2 | F3); + EXPECT_EQ(5, f); + + f = f ^ F1; + EXPECT_EQ(4, f); +} + +TEST(BitmaskEnumTest, BitwiseXorEquals) { + Flags f = (F1 | F2); + f ^= (F2 | F4); + EXPECT_EQ(9, f); + + // ^= should return a reference to the LHS. + (f ^= F4) = F3; + EXPECT_EQ(F3, f); +} + +TEST(BitmaskEnumTest, BitwiseNot) { + Flags f = ~F1; + EXPECT_EQ(14, f); // Largest value for f is 15. + EXPECT_EQ(15, ~F0); +} + +enum class FlagsClass { + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 4, + LLVM_MARK_AS_BITMASK_ENUM(F3) +}; + +TEST(BitmaskEnumTest, ScopedEnum) { + FlagsClass f = (FlagsClass::F1 & ~FlagsClass::F0) | FlagsClass::F2; + f |= FlagsClass::F3; + EXPECT_EQ(7, static_cast<int>(f)); +} + +struct Container { + enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, LLVM_MARK_AS_BITMASK_ENUM(F3) }; + + static Flags getFlags() { + Flags f = F0 | F1; + f |= F2; + return f; + } +}; + +TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); } + +} // namespace + +namespace foo { +namespace bar { +namespace { +enum FlagsInNamespace { + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 4, + LLVM_MARK_AS_BITMASK_ENUM(F3) +}; +} // namespace +} // namespace foo +} // namespace bar + +namespace { +TEST(BitmaskEnumTest, EnumInNamespace) { + foo::bar::FlagsInNamespace f = ~foo::bar::F0 & (foo::bar::F1 | foo::bar::F2); + f |= foo::bar::F3; + EXPECT_EQ(7, f); +} +} // namespace diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index bce1bf93a338..ca1644b5346e 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -7,6 +7,7 @@ set(ADTSources APIntTest.cpp APSIntTest.cpp ArrayRefTest.cpp + BitmaskEnumTest.cpp BitVectorTest.cpp DAGDeltaAlgorithmTest.cpp DeltaAlgorithmTest.cpp @@ -30,8 +31,11 @@ set(ADTSources PointerSumTypeTest.cpp PointerUnionTest.cpp PostOrderIteratorTest.cpp + PriorityWorklistTest.cpp RangeAdapterTest.cpp SCCIteratorTest.cpp + SequenceTest.cpp + SetVectorTest.cpp SmallPtrSetTest.cpp SmallStringTest.cpp SmallVectorTest.cpp diff --git a/unittests/ADT/DenseMapTest.cpp b/unittests/ADT/DenseMapTest.cpp index f3dcf95e92fd..db00f8cf8e57 100644 --- a/unittests/ADT/DenseMapTest.cpp +++ b/unittests/ADT/DenseMapTest.cpp @@ -339,6 +339,138 @@ TYPED_TEST(DenseMapTest, ConstIteratorTest) { EXPECT_TRUE(cit == cit2); } +namespace { +// Simple class that counts how many moves and copy happens when growing a map +struct CountCopyAndMove { + static int Move; + static int Copy; + CountCopyAndMove() {} + + CountCopyAndMove(const CountCopyAndMove &) { Copy++; } + CountCopyAndMove &operator=(const CountCopyAndMove &) { + Copy++; + return *this; + } + CountCopyAndMove(CountCopyAndMove &&) { Move++; } + CountCopyAndMove &operator=(const CountCopyAndMove &&) { + Move++; + return *this; + } +}; +int CountCopyAndMove::Copy = 0; +int CountCopyAndMove::Move = 0; + +} // anonymous namespace + +// Test for the default minimum size of a DenseMap +TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) { + // IF THIS VALUE CHANGE, please update InitialSizeTest, InitFromIterator, and + // ReserveTest as well! + const int ExpectedInitialBucketCount = 64; + // Formula from DenseMap::getMinBucketToReserveForEntries() + const int ExpectedMaxInitialEntries = ExpectedInitialBucketCount * 3 / 4 - 1; + + DenseMap<int, CountCopyAndMove> Map; + // Will allocate 64 buckets + Map.reserve(1); + unsigned MemorySize = Map.getMemorySize(); + CountCopyAndMove::Copy = 0; + CountCopyAndMove::Move = 0; + for (int i = 0; i < ExpectedMaxInitialEntries; ++i) + Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct, + std::forward_as_tuple(i), + std::forward_as_tuple())); + // Check that we didn't grow + EXPECT_EQ(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + EXPECT_EQ(ExpectedMaxInitialEntries, CountCopyAndMove::Move); + // Check that no copy occured + EXPECT_EQ(0, CountCopyAndMove::Copy); + + // Adding one extra element should grow the map + Map.insert(std::pair<int, CountCopyAndMove>( + std::piecewise_construct, + std::forward_as_tuple(ExpectedMaxInitialEntries), + std::forward_as_tuple())); + // Check that we grew + EXPECT_NE(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + // This relies on move-construction elision, and cannot be reliably tested. + // EXPECT_EQ(ExpectedMaxInitialEntries + 2, CountCopyAndMove::Move); + // Check that no copy occured + EXPECT_EQ(0, CountCopyAndMove::Copy); +} + +// Make sure creating the map with an initial size of N actually gives us enough +// buckets to insert N items without increasing allocation size. +TEST(DenseMapCustomTest, InitialSizeTest) { + // Test a few different sizes, 48 is *not* a random choice: we need a value + // that is 2/3 of a power of two to stress the grow() condition, and the power + // of two has to be at least 64 because of minimum size allocation in the + // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the + // 64 default init. + for (auto Size : {1, 2, 48, 66}) { + DenseMap<int, CountCopyAndMove> Map(Size); + unsigned MemorySize = Map.getMemorySize(); + CountCopyAndMove::Copy = 0; + CountCopyAndMove::Move = 0; + for (int i = 0; i < Size; ++i) + Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct, + std::forward_as_tuple(i), + std::forward_as_tuple())); + // Check that we didn't grow + EXPECT_EQ(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + EXPECT_EQ(Size, CountCopyAndMove::Move); + // Check that no copy occured + EXPECT_EQ(0, CountCopyAndMove::Copy); + } +} + +// Make sure creating the map with a iterator range does not trigger grow() +TEST(DenseMapCustomTest, InitFromIterator) { + std::vector<std::pair<int, CountCopyAndMove>> Values; + // The size is a random value greater than 64 (hardcoded DenseMap min init) + const int Count = 65; + for (int i = 0; i < Count; i++) + Values.emplace_back(i, CountCopyAndMove()); + + CountCopyAndMove::Move = 0; + CountCopyAndMove::Copy = 0; + DenseMap<int, CountCopyAndMove> Map(Values.begin(), Values.end()); + // Check that no move occured + EXPECT_EQ(0, CountCopyAndMove::Move); + // Check that copy was called the expected number of times + EXPECT_EQ(Count, CountCopyAndMove::Copy); +} + +// Make sure reserve actually gives us enough buckets to insert N items +// without increasing allocation size. +TEST(DenseMapCustomTest, ReserveTest) { + // Test a few different size, 48 is *not* a random choice: we need a value + // that is 2/3 of a power of two to stress the grow() condition, and the power + // of two has to be at least 64 because of minimum size allocation in the + // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the + // 64 default init. + for (auto Size : {1, 2, 48, 66}) { + DenseMap<int, CountCopyAndMove> Map; + Map.reserve(Size); + unsigned MemorySize = Map.getMemorySize(); + CountCopyAndMove::Copy = 0; + CountCopyAndMove::Move = 0; + for (int i = 0; i < Size; ++i) + Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct, + std::forward_as_tuple(i), + std::forward_as_tuple())); + // Check that we didn't grow + EXPECT_EQ(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + EXPECT_EQ(Size, CountCopyAndMove::Move); + // Check that no copy occured + EXPECT_EQ(0, CountCopyAndMove::Copy); + } +} + // Make sure DenseMap works with StringRef keys. TEST(DenseMapCustomTest, StringRefTest) { DenseMap<StringRef, int> M; @@ -364,6 +496,55 @@ TEST(DenseMapCustomTest, StringRefTest) { EXPECT_EQ(42, M.lookup(StringRef("a", 0))); } +struct CachedHashTest { + unsigned Val; + unsigned *Counter = nullptr; + CachedHashTest(unsigned Val) : Val(Val) {} + CachedHashTest(unsigned Val, unsigned *Counter) + : Val(Val), Counter(Counter) {} +}; +} +namespace llvm { +template <> struct DenseMapInfo<CachedHashTest> { + static CachedHashTest getEmptyKey() { return ~0; } + static CachedHashTest getTombstoneKey() { return ~0U - 1; } + static unsigned getHashValue(const CachedHashTest &X) { + ++*X.Counter; + return X.Val; + } + static bool isEqual(const CachedHashTest &LHS, const CachedHashTest &RHS) { + return LHS.Val == RHS.Val; + } +}; +} +namespace { + +TEST(DenseMapCustomTest, CachedHashTest) { + unsigned Counter = 0; + CachedHashTest Val(0, &Counter); + DenseMap<CachedHashTest, int> Map; + + Map[Val] = 0; + ASSERT_EQ(1u, Counter); + + Map.reserve(64); + ASSERT_EQ(2u, Counter); +} + +// Like above, but now cache the hash. +TEST(DenseMapCustomTest, CachedHashTest2) { + unsigned Counter = 0; + CachedHashTest Val(0, &Counter); + typedef CachedHash<CachedHashTest> Cached; + DenseMap<Cached, int> Map; + + Map[Val] = 0; + ASSERT_EQ(1u, Counter); + + Map.reserve(64); + ASSERT_EQ(1u, Counter); +} + // Key traits that allows lookup with either an unsigned or char* key; // In the latter case, "a" == 0, "b" == 1 and so on. struct TestDenseMapInfo { diff --git a/unittests/ADT/FoldingSet.cpp b/unittests/ADT/FoldingSet.cpp index 5addf27e1363..927ef313cb93 100644 --- a/unittests/ADT/FoldingSet.cpp +++ b/unittests/ADT/FoldingSet.cpp @@ -35,5 +35,138 @@ TEST(FoldingSetTest, UnalignedStringTest) { EXPECT_EQ(a.ComputeHash(), b.ComputeHash()); } +struct TrivialPair : public FoldingSetNode { + unsigned Key = 0; + unsigned Value = 0; + TrivialPair(unsigned K, unsigned V) : FoldingSetNode(), Key(K), Value(V) {} + + void Profile(FoldingSetNodeID &ID) const { + ID.AddInteger(Key); + ID.AddInteger(Value); + } +}; + +TEST(FoldingSetTest, IDComparison) { + FoldingSet<TrivialPair> Trivial; + + TrivialPair T(99, 42); + Trivial.InsertNode(&T); + + void *InsertPos = nullptr; + FoldingSetNodeID ID; + T.Profile(ID); + TrivialPair *N = Trivial.FindNodeOrInsertPos(ID, InsertPos); + EXPECT_EQ(&T, N); + EXPECT_EQ(nullptr, InsertPos); +} + +TEST(FoldingSetTest, MissedIDComparison) { + FoldingSet<TrivialPair> Trivial; + + TrivialPair S(100, 42); + TrivialPair T(99, 42); + Trivial.InsertNode(&T); + + void *InsertPos = nullptr; + FoldingSetNodeID ID; + S.Profile(ID); + TrivialPair *N = Trivial.FindNodeOrInsertPos(ID, InsertPos); + EXPECT_EQ(nullptr, N); + EXPECT_NE(nullptr, InsertPos); +} + +TEST(FoldingSetTest, RemoveNodeThatIsPresent) { + FoldingSet<TrivialPair> Trivial; + + TrivialPair T(99, 42); + Trivial.InsertNode(&T); + EXPECT_EQ(Trivial.size(), 1U); + + bool WasThere = Trivial.RemoveNode(&T); + EXPECT_TRUE(WasThere); + EXPECT_EQ(0U, Trivial.size()); +} + +TEST(FoldingSetTest, RemoveNodeThatIsAbsent) { + FoldingSet<TrivialPair> Trivial; + + TrivialPair T(99, 42); + bool WasThere = Trivial.RemoveNode(&T); + EXPECT_FALSE(WasThere); + EXPECT_EQ(0U, Trivial.size()); +} + +TEST(FoldingSetTest, GetOrInsertInserting) { + FoldingSet<TrivialPair> Trivial; + + TrivialPair T(99, 42); + TrivialPair *N = Trivial.GetOrInsertNode(&T); + EXPECT_EQ(&T, N); +} + +TEST(FoldingSetTest, GetOrInsertGetting) { + FoldingSet<TrivialPair> Trivial; + + TrivialPair T(99, 42); + TrivialPair T2(99, 42); + Trivial.InsertNode(&T); + TrivialPair *N = Trivial.GetOrInsertNode(&T2); + EXPECT_EQ(&T, N); +} + +TEST(FoldingSetTest, InsertAtPos) { + FoldingSet<TrivialPair> Trivial; + + void *InsertPos = nullptr; + TrivialPair Finder(99, 42); + FoldingSetNodeID ID; + Finder.Profile(ID); + Trivial.FindNodeOrInsertPos(ID, InsertPos); + + TrivialPair T(99, 42); + Trivial.InsertNode(&T, InsertPos); + EXPECT_EQ(1U, Trivial.size()); +} + +TEST(FoldingSetTest, EmptyIsTrue) { + FoldingSet<TrivialPair> Trivial; + EXPECT_TRUE(Trivial.empty()); +} + +TEST(FoldingSetTest, EmptyIsFalse) { + FoldingSet<TrivialPair> Trivial; + TrivialPair T(99, 42); + Trivial.InsertNode(&T); + EXPECT_FALSE(Trivial.empty()); +} + +TEST(FoldingSetTest, ClearOnEmpty) { + FoldingSet<TrivialPair> Trivial; + Trivial.clear(); + EXPECT_TRUE(Trivial.empty()); +} + +TEST(FoldingSetTest, ClearOnNonEmpty) { + FoldingSet<TrivialPair> Trivial; + TrivialPair T(99, 42); + Trivial.InsertNode(&T); + Trivial.clear(); + EXPECT_TRUE(Trivial.empty()); +} + +TEST(FoldingSetTest, CapacityLargerThanReserve) { + FoldingSet<TrivialPair> Trivial; + auto OldCapacity = Trivial.capacity(); + Trivial.reserve(OldCapacity + 1); + EXPECT_GE(Trivial.capacity(), OldCapacity + 1); +} + +TEST(FoldingSetTest, SmallReserveChangesNothing) { + FoldingSet<TrivialPair> Trivial; + auto OldCapacity = Trivial.capacity(); + Trivial.reserve(OldCapacity - 1); + EXPECT_EQ(Trivial.capacity(), OldCapacity); +} + } diff --git a/unittests/ADT/ImmutableSetTest.cpp b/unittests/ADT/ImmutableSetTest.cpp index febd441db166..a6eb405db720 100644 --- a/unittests/ADT/ImmutableSetTest.cpp +++ b/unittests/ADT/ImmutableSetTest.cpp @@ -181,19 +181,22 @@ TEST_F(ImmutableSetTest, IterLongSetTest) { int i = 0; for (ImmutableSet<long>::iterator I = S.begin(), E = S.end(); I != E; ++I) { - ASSERT_EQ(i++, *I); + ASSERT_EQ(i, *I); + i++; } ASSERT_EQ(0, i); i = 0; for (ImmutableSet<long>::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { - ASSERT_EQ(i++, *I); + ASSERT_EQ(i, *I); + i++; } ASSERT_EQ(3, i); i = 0; for (ImmutableSet<long>::iterator I = S3.begin(), E = S3.end(); I != E; I++) { - ASSERT_EQ(i++, *I); + ASSERT_EQ(i, *I); + i++; } ASSERT_EQ(6, i); } diff --git a/unittests/ADT/Makefile b/unittests/ADT/Makefile deleted file mode 100644 index c255a0b44d09..000000000000 --- a/unittests/ADT/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -##===- unittests/ADT/Makefile ------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = ADT -LINK_COMPONENTS := support - -include $(LEVEL)/Makefile.config - -# Xfail BitVectorTest for now on PPC Darwin. 7598360. -ifeq ($(ARCH),PowerPC) -ifeq ($(TARGET_OS),Darwin) -CPP.Flags += -DXFAIL -endif -endif - -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/ADT/PointerEmbeddedIntTest.cpp b/unittests/ADT/PointerEmbeddedIntTest.cpp index b10365a2f618..9c27f8ee655f 100644 --- a/unittests/ADT/PointerEmbeddedIntTest.cpp +++ b/unittests/ADT/PointerEmbeddedIntTest.cpp @@ -43,4 +43,38 @@ TEST(PointerEmbeddedIntTest, Basic) { EXPECT_FALSE(42 >= J); } +TEST(PointerEmbeddedIntTest, intptr_t) { + PointerEmbeddedInt<intptr_t, CHAR_BIT> IPos = 42, INeg = -42; + EXPECT_EQ(42, IPos); + EXPECT_EQ(-42, INeg); + + PointerEmbeddedInt<uintptr_t, CHAR_BIT> U = 42, USaturated = 255; + EXPECT_EQ(42U, U); + EXPECT_EQ(255U, USaturated); + + PointerEmbeddedInt<intptr_t, std::numeric_limits<intptr_t>::digits> + IMax = std::numeric_limits<intptr_t>::max() >> 1, + IMin = std::numeric_limits<intptr_t>::min() >> 1; + EXPECT_EQ(std::numeric_limits<intptr_t>::max() >> 1, IMax); + EXPECT_EQ(std::numeric_limits<intptr_t>::min() >> 1, IMin); + + PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1> + UMax = std::numeric_limits<uintptr_t>::max() >> 1, + UMin = std::numeric_limits<uintptr_t>::min() >> 1; + EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1, UMax); + EXPECT_EQ(std::numeric_limits<uintptr_t>::min() >> 1, UMin); +} + +TEST(PointerEmbeddedIntTest, PointerLikeTypeTraits) { + PointerEmbeddedInt<int, CHAR_BIT> I = 42; + using ITraits = PointerLikeTypeTraits<decltype(I)>; + EXPECT_EQ(42, ITraits::getFromVoidPointer(ITraits::getAsVoidPointer(I))); + + PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1> + Max = std::numeric_limits<uintptr_t>::max() >> 1; + using MaxTraits = PointerLikeTypeTraits<decltype(Max)>; + EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1, + MaxTraits::getFromVoidPointer(MaxTraits::getAsVoidPointer(Max))); +} + } // end anonymous namespace diff --git a/unittests/ADT/PriorityWorklistTest.cpp b/unittests/ADT/PriorityWorklistTest.cpp new file mode 100644 index 000000000000..bbe026434c63 --- /dev/null +++ b/unittests/ADT/PriorityWorklistTest.cpp @@ -0,0 +1,106 @@ +//===- llvm/unittest/ADT/PriorityWorklist.cpp -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// PriorityWorklist unit tests. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/PriorityWorklist.h" +#include "gtest/gtest.h" + +namespace { + +using namespace llvm; + +template <typename T> class PriorityWorklistTest : public ::testing::Test {}; +typedef ::testing::Types<PriorityWorklist<int>, SmallPriorityWorklist<int, 2>> + TestTypes; +TYPED_TEST_CASE(PriorityWorklistTest, TestTypes); + +TYPED_TEST(PriorityWorklistTest, Basic) { + TypeParam W; + EXPECT_TRUE(W.empty()); + EXPECT_EQ(0u, W.size()); + EXPECT_FALSE(W.count(42)); + + EXPECT_TRUE(W.insert(21)); + EXPECT_TRUE(W.insert(42)); + EXPECT_TRUE(W.insert(17)); + + EXPECT_FALSE(W.empty()); + EXPECT_EQ(3u, W.size()); + EXPECT_TRUE(W.count(42)); + + EXPECT_FALSE(W.erase(75)); + EXPECT_EQ(3u, W.size()); + EXPECT_EQ(17, W.back()); + + EXPECT_TRUE(W.erase(17)); + EXPECT_FALSE(W.count(17)); + EXPECT_EQ(2u, W.size()); + EXPECT_EQ(42, W.back()); + + W.clear(); + EXPECT_TRUE(W.empty()); + EXPECT_EQ(0u, W.size()); + + EXPECT_TRUE(W.insert(21)); + EXPECT_TRUE(W.insert(42)); + EXPECT_TRUE(W.insert(12)); + EXPECT_TRUE(W.insert(17)); + EXPECT_TRUE(W.count(12)); + EXPECT_TRUE(W.count(17)); + EXPECT_EQ(4u, W.size()); + EXPECT_EQ(17, W.back()); + EXPECT_TRUE(W.erase(12)); + EXPECT_FALSE(W.count(12)); + EXPECT_TRUE(W.count(17)); + EXPECT_EQ(3u, W.size()); + EXPECT_EQ(17, W.back()); + + EXPECT_FALSE(W.insert(42)); + EXPECT_EQ(3u, W.size()); + EXPECT_EQ(42, W.pop_back_val()); + EXPECT_EQ(17, W.pop_back_val()); + EXPECT_EQ(21, W.pop_back_val()); + EXPECT_TRUE(W.empty()); +} + +TYPED_TEST(PriorityWorklistTest, EraseIf) { + TypeParam W; + W.insert(23); + W.insert(10); + W.insert(47); + W.insert(42); + W.insert(23); + W.insert(13); + W.insert(26); + W.insert(42); + EXPECT_EQ(6u, W.size()); + + EXPECT_FALSE(W.erase_if([](int i) { return i > 100; })); + EXPECT_EQ(6u, W.size()); + EXPECT_EQ(42, W.back()); + + EXPECT_TRUE(W.erase_if([](int i) { + assert(i != 0 && "Saw a null value!"); + return (i & 1) == 0; + })); + EXPECT_EQ(3u, W.size()); + EXPECT_FALSE(W.count(42)); + EXPECT_FALSE(W.count(26)); + EXPECT_FALSE(W.count(10)); + EXPECT_FALSE(W.insert(47)); + EXPECT_FALSE(W.insert(23)); + EXPECT_EQ(23, W.pop_back_val()); + EXPECT_EQ(47, W.pop_back_val()); + EXPECT_EQ(13, W.pop_back_val()); +} + +} diff --git a/unittests/ADT/SequenceTest.cpp b/unittests/ADT/SequenceTest.cpp new file mode 100644 index 000000000000..cc82f86c30d5 --- /dev/null +++ b/unittests/ADT/SequenceTest.cpp @@ -0,0 +1,41 @@ +//===- SequenceTest.cpp - Unit tests for a sequence abstraciton -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Sequence.h" +#include "gtest/gtest.h" + +#include <list> + +using namespace llvm; + +namespace { + +TEST(SequenceTest, Basic) { + int x = 0; + for (int i : seq(0, 10)) { + EXPECT_EQ(x, i); + x++; + } + EXPECT_EQ(10, x); + + auto my_seq = seq(0, 4); + EXPECT_EQ(4, my_seq.end() - my_seq.begin()); + for (int i : {0, 1, 2, 3}) + EXPECT_EQ(i, (int)my_seq.begin()[i]); + + EXPECT_TRUE(my_seq.begin() < my_seq.end()); + + auto adjusted_begin = my_seq.begin() + 2; + auto adjusted_end = my_seq.end() - 2; + EXPECT_TRUE(adjusted_begin == adjusted_end); + EXPECT_EQ(2, *adjusted_begin); + EXPECT_EQ(2, *adjusted_end); +} + +} // anonymous namespace diff --git a/unittests/ADT/SetVectorTest.cpp b/unittests/ADT/SetVectorTest.cpp new file mode 100644 index 000000000000..b8cac0dc311e --- /dev/null +++ b/unittests/ADT/SetVectorTest.cpp @@ -0,0 +1,34 @@ +//===- llvm/unittest/ADT/SetVector.cpp ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// SetVector unit tests. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SetVector.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(SetVector, EraseTest) { + SetVector<int> S; + S.insert(0); + S.insert(1); + S.insert(2); + + auto I = S.erase(std::next(S.begin())); + + // Test that the returned iterator is the expected one-after-erase + // and the size/contents is the expected sequence {0, 2}. + EXPECT_EQ(std::next(S.begin()), I); + EXPECT_EQ(2u, S.size()); + EXPECT_EQ(0, *S.begin()); + EXPECT_EQ(2, *std::next(S.begin())); +} + diff --git a/unittests/ADT/SmallPtrSetTest.cpp b/unittests/ADT/SmallPtrSetTest.cpp index fdd1cbb60040..d8d07b16cfe3 100644 --- a/unittests/ADT/SmallPtrSetTest.cpp +++ b/unittests/ADT/SmallPtrSetTest.cpp @@ -167,13 +167,29 @@ TEST(SmallPtrSetTest, SwapTest) { a.insert(&buf[1]); b.insert(&buf[2]); + EXPECT_EQ(2U, a.size()); + EXPECT_EQ(1U, b.size()); + EXPECT_TRUE(a.count(&buf[0])); + EXPECT_TRUE(a.count(&buf[1])); + EXPECT_FALSE(a.count(&buf[2])); + EXPECT_FALSE(a.count(&buf[3])); + EXPECT_FALSE(b.count(&buf[0])); + EXPECT_FALSE(b.count(&buf[1])); + EXPECT_TRUE(b.count(&buf[2])); + EXPECT_FALSE(b.count(&buf[3])); + std::swap(a, b); EXPECT_EQ(1U, a.size()); EXPECT_EQ(2U, b.size()); + EXPECT_FALSE(a.count(&buf[0])); + EXPECT_FALSE(a.count(&buf[1])); EXPECT_TRUE(a.count(&buf[2])); + EXPECT_FALSE(a.count(&buf[3])); EXPECT_TRUE(b.count(&buf[0])); EXPECT_TRUE(b.count(&buf[1])); + EXPECT_FALSE(b.count(&buf[2])); + EXPECT_FALSE(b.count(&buf[3])); b.insert(&buf[3]); std::swap(a, b); @@ -182,16 +198,24 @@ TEST(SmallPtrSetTest, SwapTest) { EXPECT_EQ(1U, b.size()); EXPECT_TRUE(a.count(&buf[0])); EXPECT_TRUE(a.count(&buf[1])); + EXPECT_FALSE(a.count(&buf[2])); EXPECT_TRUE(a.count(&buf[3])); + EXPECT_FALSE(b.count(&buf[0])); + EXPECT_FALSE(b.count(&buf[1])); EXPECT_TRUE(b.count(&buf[2])); + EXPECT_FALSE(b.count(&buf[3])); std::swap(a, b); EXPECT_EQ(1U, a.size()); EXPECT_EQ(3U, b.size()); + EXPECT_FALSE(a.count(&buf[0])); + EXPECT_FALSE(a.count(&buf[1])); EXPECT_TRUE(a.count(&buf[2])); + EXPECT_FALSE(a.count(&buf[3])); EXPECT_TRUE(b.count(&buf[0])); EXPECT_TRUE(b.count(&buf[1])); + EXPECT_FALSE(b.count(&buf[2])); EXPECT_TRUE(b.count(&buf[3])); a.insert(&buf[4]); @@ -210,3 +234,42 @@ TEST(SmallPtrSetTest, SwapTest) { EXPECT_TRUE(a.count(&buf[1])); EXPECT_TRUE(a.count(&buf[3])); } + +void checkEraseAndIterators(SmallPtrSetImpl<int*> &S) { + int buf[3]; + + S.insert(&buf[0]); + S.insert(&buf[1]); + S.insert(&buf[2]); + + // Iterators must still be valid after erase() calls; + auto B = S.begin(); + auto M = std::next(B); + auto E = S.end(); + EXPECT_TRUE(*B == &buf[0] || *B == &buf[1] || *B == &buf[2]); + EXPECT_TRUE(*M == &buf[0] || *M == &buf[1] || *M == &buf[2]); + EXPECT_TRUE(*B != *M); + int *Removable = *std::next(M); + // No iterator points to Removable now. + EXPECT_TRUE(Removable == &buf[0] || Removable == &buf[1] || + Removable == &buf[2]); + EXPECT_TRUE(Removable != *B && Removable != *M); + + S.erase(Removable); + + // B,M,E iterators should still be valid + EXPECT_EQ(B, S.begin()); + EXPECT_EQ(M, std::next(B)); + EXPECT_EQ(E, S.end()); + EXPECT_EQ(std::next(M), E); +} + +TEST(SmallPtrSetTest, EraseTest) { + // Test when set stays small. + SmallPtrSet<int *, 8> B; + checkEraseAndIterators(B); + + // Test when set grows big. + SmallPtrSet<int *, 2> A; + checkEraseAndIterators(A); +} diff --git a/unittests/ADT/SmallVectorTest.cpp b/unittests/ADT/SmallVectorTest.cpp index 46f7021ac165..7367ad470e3a 100644 --- a/unittests/ADT/SmallVectorTest.cpp +++ b/unittests/ADT/SmallVectorTest.cpp @@ -459,7 +459,8 @@ TYPED_TEST(SmallVectorTest, EraseTest) { SCOPED_TRACE("EraseTest"); this->makeSequence(this->theVector, 1, 3); - this->theVector.erase(this->theVector.begin()); + const auto &theConstVector = this->theVector; + this->theVector.erase(theConstVector.begin()); this->assertValuesInOrder(this->theVector, 2u, 2, 3); } @@ -468,7 +469,8 @@ TYPED_TEST(SmallVectorTest, EraseRangeTest) { SCOPED_TRACE("EraseRangeTest"); this->makeSequence(this->theVector, 1, 3); - this->theVector.erase(this->theVector.begin(), this->theVector.begin() + 2); + const auto &theConstVector = this->theVector; + this->theVector.erase(theConstVector.begin(), theConstVector.begin() + 2); this->assertValuesInOrder(this->theVector, 1u, 3); } diff --git a/unittests/ADT/SparseSetTest.cpp b/unittests/ADT/SparseSetTest.cpp index eb0e0db283b7..4db7a7d61fb2 100644 --- a/unittests/ADT/SparseSetTest.cpp +++ b/unittests/ADT/SparseSetTest.cpp @@ -183,4 +183,24 @@ TEST(SparseSetTest, AltStructSet) { EXPECT_FALSE(Set.erase(5)); EXPECT_TRUE(Set.erase(6)); } + +TEST(SparseSetTest, PopBack) { + USet Set; + const unsigned UpperBound = 300; + Set.setUniverse(UpperBound); + for (unsigned i = 0; i < UpperBound; ++i) + Set.insert(i); + + // Make sure pop back returns the values in the reverse order we + // inserted them. + unsigned Expected = UpperBound; + while (!Set.empty()) + ASSERT_TRUE(--Expected == Set.pop_back_val()); + + // Insert again the same elements in the sparse set and make sure + // each insertion actually inserts the elements. I.e., check + // that the underlying data structure are properly cleared. + for (unsigned i = 0; i < UpperBound; ++i) + ASSERT_TRUE(Set.insert(i).second); +} } // namespace diff --git a/unittests/ADT/StringMapTest.cpp b/unittests/ADT/StringMapTest.cpp index 4ed0b76f0f41..6ca701bb23a5 100644 --- a/unittests/ADT/StringMapTest.cpp +++ b/unittests/ADT/StringMapTest.cpp @@ -7,9 +7,10 @@ // //===----------------------------------------------------------------------===// -#include "gtest/gtest.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/DataTypes.h" +#include "gtest/gtest.h" #include <tuple> using namespace llvm; @@ -157,6 +158,33 @@ TEST_F(StringMapTest, SmallFullMapTest) { EXPECT_EQ(5, Map.lookup("funf")); } +TEST_F(StringMapTest, CopyCtorTest) { + llvm::StringMap<int> Map; + + Map["eins"] = 1; + Map["zwei"] = 2; + Map["drei"] = 3; + Map.erase("drei"); + Map.erase("eins"); + Map["veir"] = 4; + Map["funf"] = 5; + + EXPECT_EQ(3u, Map.size()); + EXPECT_EQ(0, Map.lookup("eins")); + EXPECT_EQ(2, Map.lookup("zwei")); + EXPECT_EQ(0, Map.lookup("drei")); + EXPECT_EQ(4, Map.lookup("veir")); + EXPECT_EQ(5, Map.lookup("funf")); + + llvm::StringMap<int> Map2(Map); + EXPECT_EQ(3u, Map2.size()); + EXPECT_EQ(0, Map2.lookup("eins")); + EXPECT_EQ(2, Map2.lookup("zwei")); + EXPECT_EQ(0, Map2.lookup("drei")); + EXPECT_EQ(4, Map2.lookup("veir")); + EXPECT_EQ(5, Map2.lookup("funf")); +} + // A more complex iteration test. TEST_F(StringMapTest, IterationTest) { bool visited[100]; @@ -231,12 +259,12 @@ TEST_F(StringMapTest, InsertRehashingPairTest) { // moved to a different bucket during internal rehashing. This depends on // the particular key, and the implementation of StringMap and HashString. // Changes to those might result in this test not actually checking that. - StringMap<uint32_t> t(1); - EXPECT_EQ(1u, t.getNumBuckets()); + StringMap<uint32_t> t(0); + EXPECT_EQ(0u, t.getNumBuckets()); StringMap<uint32_t>::iterator It = t.insert(std::make_pair("abcdef", 42)).first; - EXPECT_EQ(2u, t.getNumBuckets()); + EXPECT_EQ(16u, t.getNumBuckets()); EXPECT_EQ("abcdef", It->first()); EXPECT_EQ(42u, It->second); } @@ -356,4 +384,83 @@ TEST_F(StringMapTest, MoveDtor) { ASSERT_TRUE(B.empty()); } +namespace { +// Simple class that counts how many moves and copy happens when growing a map +struct CountCtorCopyAndMove { + static unsigned Ctor; + static unsigned Move; + static unsigned Copy; + int Data = 0; + CountCtorCopyAndMove(int Data) : Data(Data) { Ctor++; } + CountCtorCopyAndMove() { Ctor++; } + + CountCtorCopyAndMove(const CountCtorCopyAndMove &) { Copy++; } + CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &) { + Copy++; + return *this; + } + CountCtorCopyAndMove(CountCtorCopyAndMove &&) { Move++; } + CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &&) { + Move++; + return *this; + } +}; +unsigned CountCtorCopyAndMove::Copy = 0; +unsigned CountCtorCopyAndMove::Move = 0; +unsigned CountCtorCopyAndMove::Ctor = 0; + +} // anonymous namespace + +// Make sure creating the map with an initial size of N actually gives us enough +// buckets to insert N items without increasing allocation size. +TEST(StringMapCustomTest, InitialSizeTest) { + // 1 is an "edge value", 32 is an arbitrary power of two, and 67 is an + // arbitrary prime, picked without any good reason. + for (auto Size : {1, 32, 67}) { + StringMap<CountCtorCopyAndMove> Map(Size); + auto NumBuckets = Map.getNumBuckets(); + CountCtorCopyAndMove::Move = 0; + CountCtorCopyAndMove::Copy = 0; + for (int i = 0; i < Size; ++i) + 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. + EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move); + // We copy once the pair from the Elts vector + EXPECT_EQ(0u, CountCtorCopyAndMove::Copy); + // Check that the map didn't grow + EXPECT_EQ(Map.getNumBuckets(), NumBuckets); + } +} + +TEST(StringMapCustomTest, BracketOperatorCtor) { + StringMap<CountCtorCopyAndMove> Map; + CountCtorCopyAndMove::Ctor = 0; + Map["abcd"]; + EXPECT_EQ(1u, CountCtorCopyAndMove::Ctor); + // Test that operator[] does not create a value when it is already in the map + CountCtorCopyAndMove::Ctor = 0; + Map["abcd"]; + EXPECT_EQ(0u, CountCtorCopyAndMove::Ctor); +} + +namespace { +struct NonMoveableNonCopyableType { + int Data = 0; + NonMoveableNonCopyableType() = default; + NonMoveableNonCopyableType(int Data) : Data(Data) {} + NonMoveableNonCopyableType(const NonMoveableNonCopyableType &) = delete; + NonMoveableNonCopyableType(NonMoveableNonCopyableType &&) = delete; +}; +} + +// Test that we can "emplace" an element in the map without involving map/move +TEST(StringMapCustomTest, EmplaceTest) { + StringMap<NonMoveableNonCopyableType> Map; + Map.emplace_second("abcd", 42); + EXPECT_EQ(1u, Map.count("abcd")); + EXPECT_EQ(42, Map["abcd"].Data); +} + } // end anonymous namespace diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 6cf2e6a0454d..66e5944b56eb 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -301,7 +301,7 @@ TEST(StringRefTest, Trim) { EXPECT_EQ(StringRef(""), StringRef(" ").trim()); EXPECT_EQ(StringRef("\0", 1), StringRef(" \0 ", 3).trim()); EXPECT_EQ(StringRef("\0\0", 2), StringRef("\0\0", 2).trim()); - EXPECT_EQ(StringRef("x"), StringRef("\0\0x\0\0", 5).trim(StringRef("\0", 1))); + EXPECT_EQ(StringRef("x"), StringRef("\0\0x\0\0", 5).trim('\0')); } TEST(StringRefTest, StartsWith) { @@ -589,6 +589,15 @@ TEST(StringRefTest, joinStrings) { TEST(StringRefTest, AllocatorCopy) { BumpPtrAllocator Alloc; + // First test empty strings. We don't want these to allocate anything on the + // allocator. + StringRef StrEmpty = ""; + StringRef StrEmptyc = StrEmpty.copy(Alloc); + EXPECT_TRUE(StrEmpty.equals(StrEmptyc)); + EXPECT_EQ(StrEmptyc.data(), nullptr); + EXPECT_EQ(StrEmptyc.size(), 0u); + EXPECT_EQ(Alloc.getTotalMemory(), 0u); + StringRef Str1 = "hello"; StringRef Str2 = "bye"; StringRef Str1c = Str1.copy(Alloc); diff --git a/unittests/ADT/TinyPtrVectorTest.cpp b/unittests/ADT/TinyPtrVectorTest.cpp index 294dfac0c633..26189b76394f 100644 --- a/unittests/ADT/TinyPtrVectorTest.cpp +++ b/unittests/ADT/TinyPtrVectorTest.cpp @@ -14,11 +14,9 @@ #include "llvm/ADT/TinyPtrVector.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/Support/type_traits.h" #include "gtest/gtest.h" #include <algorithm> -#include <list> #include <vector> using namespace llvm; diff --git a/unittests/ADT/TripleTest.cpp b/unittests/ADT/TripleTest.cpp index ac4fa2274e9e..984f4a2a595a 100644 --- a/unittests/ADT/TripleTest.cpp +++ b/unittests/ADT/TripleTest.cpp @@ -93,6 +93,12 @@ TEST(TripleTest, ParsedIDs) { EXPECT_EQ(Triple::Linux, T.getOS()); EXPECT_EQ(Triple::GNU, T.getEnvironment()); + T = Triple("x86_64-pc-linux-musl"); + EXPECT_EQ(Triple::x86_64, T.getArch()); + EXPECT_EQ(Triple::PC, T.getVendor()); + EXPECT_EQ(Triple::Linux, T.getOS()); + EXPECT_EQ(Triple::Musl, T.getEnvironment()); + T = Triple("powerpc-bgp-linux"); EXPECT_EQ(Triple::ppc, T.getArch()); EXPECT_EQ(Triple::BGP, T.getVendor()); @@ -135,6 +141,12 @@ TEST(TripleTest, ParsedIDs) { EXPECT_EQ(Triple::UnknownOS, T.getOS()); EXPECT_EQ(Triple::EABI, T.getEnvironment()); + T = Triple("arm-none-linux-musleabi"); + EXPECT_EQ(Triple::arm, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::Linux, T.getOS()); + EXPECT_EQ(Triple::MuslEABI, T.getEnvironment()); + T = Triple("armv6hl-none-linux-gnueabi"); EXPECT_EQ(Triple::arm, T.getArch()); EXPECT_EQ(Triple::Linux, T.getOS()); @@ -212,6 +224,30 @@ TEST(TripleTest, ParsedIDs) { EXPECT_EQ(Triple::UnknownOS, T.getOS()); EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("lanai-unknown-unknown"); + EXPECT_EQ(Triple::lanai, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("lanai"); + EXPECT_EQ(Triple::lanai, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("amdgcn-mesa-mesa3d"); + EXPECT_EQ(Triple::amdgcn, T.getArch()); + EXPECT_EQ(Triple::Mesa, T.getVendor()); + EXPECT_EQ(Triple::Mesa3D, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("amdgcn-amd-amdhsa"); + EXPECT_EQ(Triple::amdgcn, T.getArch()); + EXPECT_EQ(Triple::AMD, T.getVendor()); + EXPECT_EQ(Triple::AMDHSA, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("huh"); EXPECT_EQ(Triple::UnknownArch, T.getArch()); } @@ -486,6 +522,11 @@ TEST(TripleTest, BitWidthPredicates) { EXPECT_TRUE(T.isArch16Bit()); EXPECT_FALSE(T.isArch32Bit()); EXPECT_FALSE(T.isArch64Bit()); + + T.setArch(Triple::lanai); + EXPECT_FALSE(T.isArch16Bit()); + EXPECT_TRUE(T.isArch32Bit()); + EXPECT_FALSE(T.isArch64Bit()); } TEST(TripleTest, BitWidthArchVariants) { @@ -602,6 +643,14 @@ TEST(TripleTest, EndianArchVariants) { T.setArch(Triple::arm); EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch()); EXPECT_EQ(Triple::arm, T.getLittleEndianArchVariant().getArch()); + T = Triple("arm"); + EXPECT_TRUE(T.isLittleEndian()); + T = Triple("thumb"); + EXPECT_TRUE(T.isLittleEndian()); + T = Triple("armeb"); + EXPECT_FALSE(T.isLittleEndian()); + T = Triple("thumbeb"); + EXPECT_FALSE(T.isLittleEndian()); T.setArch(Triple::bpfeb); EXPECT_EQ(Triple::bpfeb, T.getBigEndianArchVariant().getArch()); @@ -654,6 +703,10 @@ TEST(TripleTest, EndianArchVariants) { T.setArch(Triple::thumbeb); EXPECT_EQ(Triple::thumbeb, T.getBigEndianArchVariant().getArch()); EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch()); + + T.setArch(Triple::lanai); + EXPECT_EQ(Triple::lanai, T.getBigEndianArchVariant().getArch()); + EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch()); } TEST(TripleTest, getOSVersion) { @@ -962,6 +1015,18 @@ TEST(TripleTest, getARMCPUForArch) { EXPECT_EQ("swift", Triple.getARMCPUForArch()); } { + llvm::Triple Triple("armv7k-apple-ios9"); + EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch()); + } + { + llvm::Triple Triple("armv7k-apple-watchos3"); + EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch()); + } + { + llvm::Triple Triple("armv7k-apple-tvos9"); + EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch()); + } + { llvm::Triple Triple("armv7em-apple-ios7"); EXPECT_EQ("cortex-m4", Triple.getARMCPUForArch()); } diff --git a/unittests/Analysis/AliasAnalysisTest.cpp b/unittests/Analysis/AliasAnalysisTest.cpp index ee116992fe76..84a04257bc27 100644 --- a/unittests/Analysis/AliasAnalysisTest.cpp +++ b/unittests/Analysis/AliasAnalysisTest.cpp @@ -14,12 +14,11 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Constants.h" -#include "llvm/IR/Instructions.h" #include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" @@ -80,9 +79,8 @@ struct TestCustomAAResult : AAResultBase<TestCustomAAResult> { std::function<void()> CB; - explicit TestCustomAAResult(const TargetLibraryInfo &TLI, - std::function<void()> CB) - : AAResultBase(TLI), CB(std::move(CB)) {} + explicit TestCustomAAResult(std::function<void()> CB) + : AAResultBase(), CB(std::move(CB)) {} TestCustomAAResult(TestCustomAAResult &&Arg) : AAResultBase(std::move(Arg)), CB(std::move(Arg.CB)) {} @@ -117,8 +115,7 @@ public: } bool doInitialization(Module &M) override { - Result.reset(new TestCustomAAResult( - getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(), std::move(CB))); + Result.reset(new TestCustomAAResult(std::move(CB))); return true; } @@ -155,7 +152,7 @@ protected: AAResults &getAAResults(Function &F) { // Reset the Function AA results first to clear out any references. - AAR.reset(new AAResults()); + AAR.reset(new AAResults(TLI)); // Build the various AA results and register them. AC.reset(new AssumptionCache(F)); @@ -181,12 +178,12 @@ TEST_F(AliasAnalysisTest, getModRefInfo) { auto *Load1 = new LoadInst(Addr, "load", BB); auto *Add1 = BinaryOperator::CreateAdd(Value, Value, "add", BB); auto *VAArg1 = new VAArgInst(Addr, PtrType, "vaarg", BB); - auto *CmpXChg1 = new AtomicCmpXchgInst(Addr, ConstantInt::get(IntType, 0), - ConstantInt::get(IntType, 1), - Monotonic, Monotonic, CrossThread, BB); + auto *CmpXChg1 = new AtomicCmpXchgInst( + Addr, ConstantInt::get(IntType, 0), ConstantInt::get(IntType, 1), + AtomicOrdering::Monotonic, AtomicOrdering::Monotonic, CrossThread, BB); auto *AtomicRMW = new AtomicRMWInst(AtomicRMWInst::Xchg, Addr, ConstantInt::get(IntType, 1), - Monotonic, CrossThread, BB); + AtomicOrdering::Monotonic, CrossThread, BB); ReturnInst::Create(C, nullptr, BB); @@ -209,14 +206,13 @@ TEST_F(AliasAnalysisTest, getModRefInfo) { class AAPassInfraTest : public testing::Test { protected: - LLVMContext &C; + LLVMContext C; SMDiagnostic Err; std::unique_ptr<Module> M; public: AAPassInfraTest() - : C(getGlobalContext()), - M(parseAssemblyString("define i32 @f(i32* %x, i32* %y) {\n" + : M(parseAssemblyString("define i32 @f(i32* %x, i32* %y) {\n" "entry:\n" " %lx = load i32, i32* %x\n" " %ly = load i32, i32* %y\n" diff --git a/unittests/Analysis/BlockFrequencyInfoTest.cpp b/unittests/Analysis/BlockFrequencyInfoTest.cpp new file mode 100644 index 000000000000..b3b0fcfb049b --- /dev/null +++ b/unittests/Analysis/BlockFrequencyInfoTest.cpp @@ -0,0 +1,86 @@ +//===- BlockFrequencyInfoTest.cpp - BlockFrequencyInfo 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/AsmParser/Parser.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +namespace llvm { +namespace { + +class BlockFrequencyInfoTest : public testing::Test { +protected: + std::unique_ptr<BranchProbabilityInfo> BPI; + std::unique_ptr<DominatorTree> DT; + std::unique_ptr<LoopInfo> LI; + LLVMContext C; + + 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 *ModuleStrig = "define i32 @f(i32 %x) {\n" + "bb0:\n" + " %y1 = icmp eq i32 %x, 0 \n" + " br i1 %y1, label %bb1, label %bb2 \n" + "bb1:\n" + " br label %bb3\n" + "bb2:\n" + " br label %bb3\n" + "bb3:\n" + " %y2 = phi i32 [0, %bb1], [1, %bb2] \n" + " ret i32 %y2\n" + "}\n"; + SMDiagnostic Err; + return parseAssemblyString(ModuleStrig, Err, C); + } +}; + +TEST_F(BlockFrequencyInfoTest, Basic) { + auto M = makeLLVMModule(); + Function *F = M->getFunction("f"); + F->setEntryCount(100); + + BlockFrequencyInfo BFI = buildBFI(*F); + BasicBlock &BB0 = F->getEntryBlock(); + BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0); + BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1); + BasicBlock *BB3 = BB1->getSingleSuccessor(); + + uint64_t BB0Freq = BFI.getBlockFreq(&BB0).getFrequency(); + uint64_t BB1Freq = BFI.getBlockFreq(BB1).getFrequency(); + uint64_t BB2Freq = BFI.getBlockFreq(BB2).getFrequency(); + uint64_t BB3Freq = BFI.getBlockFreq(BB3).getFrequency(); + + EXPECT_EQ(BB0Freq, BB3Freq); + EXPECT_EQ(BB0Freq, BB1Freq + BB2Freq); + EXPECT_EQ(BB0Freq, BB3Freq); + + EXPECT_EQ(BFI.getBlockProfileCount(&BB0).getValue(), UINT64_C(100)); + 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); +} + +} // end anonymous namespace +} // end namespace llvm diff --git a/unittests/Analysis/CFGTest.cpp b/unittests/Analysis/CFGTest.cpp index 44f0fe681dff..c60044fa52df 100644 --- a/unittests/Analysis/CFGTest.cpp +++ b/unittests/Analysis/CFGTest.cpp @@ -31,7 +31,7 @@ class IsPotentiallyReachableTest : public testing::Test { protected: void ParseAssembly(const char *Assembly) { SMDiagnostic Error; - M = parseAssemblyString(Assembly, Error, getGlobalContext()); + M = parseAssemblyString(Assembly, Error, Context); std::string errMsg; raw_string_ostream os(errMsg); @@ -112,6 +112,7 @@ protected: PM.run(*M); } + LLVMContext Context; std::unique_ptr<Module> M; Instruction *A, *B; }; diff --git a/unittests/Analysis/CGSCCPassManagerTest.cpp b/unittests/Analysis/CGSCCPassManagerTest.cpp new file mode 100644 index 000000000000..224f2df13181 --- /dev/null +++ b/unittests/Analysis/CGSCCPassManagerTest.cpp @@ -0,0 +1,311 @@ +//===- CGSCCPassManagerTest.cpp -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/SourceMgr.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class TestModuleAnalysis { +public: + struct Result { + Result(int Count) : FunctionCount(Count) {} + int FunctionCount; + }; + + static void *ID() { return (void *)&PassID; } + static StringRef name() { return "TestModuleAnalysis"; } + + TestModuleAnalysis(int &Runs) : Runs(Runs) {} + + Result run(Module &M, ModuleAnalysisManager &AM) { + ++Runs; + return Result(M.size()); + } + +private: + static char PassID; + + int &Runs; +}; + +char TestModuleAnalysis::PassID; + +class TestSCCAnalysis { +public: + struct Result { + Result(int Count) : FunctionCount(Count) {} + int FunctionCount; + }; + + static void *ID() { return (void *)&PassID; } + static StringRef name() { return "TestSCCAnalysis"; } + + TestSCCAnalysis(int &Runs) : Runs(Runs) {} + + Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM) { + ++Runs; + return Result(C.size()); + } + +private: + static char PassID; + + int &Runs; +}; + +char TestSCCAnalysis::PassID; + +class TestFunctionAnalysis { +public: + struct Result { + Result(int Count) : InstructionCount(Count) {} + int InstructionCount; + }; + + static void *ID() { return (void *)&PassID; } + static StringRef name() { return "TestFunctionAnalysis"; } + + TestFunctionAnalysis(int &Runs) : Runs(Runs) {} + + Result run(Function &F, FunctionAnalysisManager &AM) { + ++Runs; + int Count = 0; + for (Instruction &I : instructions(F)) { + (void)I; + ++Count; + } + return Result(Count); + } + +private: + static char PassID; + + int &Runs; +}; + +char TestFunctionAnalysis::PassID; + +class TestImmutableFunctionAnalysis { +public: + struct Result { + bool invalidate(Function &, const PreservedAnalyses &) { return false; } + }; + + static void *ID() { return (void *)&PassID; } + static StringRef name() { return "TestImmutableFunctionAnalysis"; } + + TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {} + + Result run(Function &F, FunctionAnalysisManager &AM) { + ++Runs; + return Result(); + } + +private: + static char PassID; + + int &Runs; +}; + +char TestImmutableFunctionAnalysis::PassID; + +struct TestModulePass { + TestModulePass(int &RunCount) : RunCount(RunCount) {} + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { + ++RunCount; + (void)AM.getResult<TestModuleAnalysis>(M); + return PreservedAnalyses::all(); + } + + static StringRef name() { return "TestModulePass"; } + + int &RunCount; +}; + +struct TestSCCPass { + TestSCCPass(int &RunCount, int &AnalyzedInstrCount, + int &AnalyzedSCCFunctionCount, int &AnalyzedModuleFunctionCount, + bool OnlyUseCachedResults = false) + : RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount), + AnalyzedSCCFunctionCount(AnalyzedSCCFunctionCount), + AnalyzedModuleFunctionCount(AnalyzedModuleFunctionCount), + OnlyUseCachedResults(OnlyUseCachedResults) {} + + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM) { + ++RunCount; + + const ModuleAnalysisManager &MAM = + AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C).getManager(); + FunctionAnalysisManager &FAM = + AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C).getManager(); + if (TestModuleAnalysis::Result *TMA = + MAM.getCachedResult<TestModuleAnalysis>( + *C.begin()->getFunction().getParent())) + AnalyzedModuleFunctionCount += TMA->FunctionCount; + + if (OnlyUseCachedResults) { + // Hack to force the use of the cached interface. + if (TestSCCAnalysis::Result *AR = AM.getCachedResult<TestSCCAnalysis>(C)) + AnalyzedSCCFunctionCount += AR->FunctionCount; + for (LazyCallGraph::Node &N : C) + if (TestFunctionAnalysis::Result *FAR = + FAM.getCachedResult<TestFunctionAnalysis>(N.getFunction())) + AnalyzedInstrCount += FAR->InstructionCount; + } else { + // Typical path just runs the analysis as needed. + TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C); + AnalyzedSCCFunctionCount += AR.FunctionCount; + for (LazyCallGraph::Node &N : C) { + TestFunctionAnalysis::Result &FAR = + FAM.getResult<TestFunctionAnalysis>(N.getFunction()); + AnalyzedInstrCount += FAR.InstructionCount; + + // Just ensure we get the immutable results. + (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction()); + } + } + + return PreservedAnalyses::all(); + } + + static StringRef name() { return "TestSCCPass"; } + + int &RunCount; + int &AnalyzedInstrCount; + int &AnalyzedSCCFunctionCount; + int &AnalyzedModuleFunctionCount; + bool OnlyUseCachedResults; +}; + +struct TestFunctionPass { + TestFunctionPass(int &RunCount) : RunCount(RunCount) {} + + PreservedAnalyses run(Function &F, AnalysisManager<Function> &) { + ++RunCount; + return PreservedAnalyses::none(); + } + + static StringRef name() { return "TestFunctionPass"; } + + int &RunCount; +}; + +std::unique_ptr<Module> parseIR(const char *IR) { + // We just use a static context here. This is never called from multiple + // threads so it is harmless no matter how it is implemented. We just need + // the context to outlive the module which it does. + static LLVMContext C; + SMDiagnostic Err; + return parseAssemblyString(IR, Err, C); +} + +TEST(CGSCCPassManagerTest, Basic) { + auto M = parseIR("define void @f() {\n" + "entry:\n" + " call void @g()\n" + " call void @h1()\n" + " ret void\n" + "}\n" + "define void @g() {\n" + "entry:\n" + " call void @g()\n" + " call void @x()\n" + " ret void\n" + "}\n" + "define void @h1() {\n" + "entry:\n" + " call void @h2()\n" + " ret void\n" + "}\n" + "define void @h2() {\n" + "entry:\n" + " call void @h3()\n" + " call void @x()\n" + " ret void\n" + "}\n" + "define void @h3() {\n" + "entry:\n" + " call void @h1()\n" + " ret void\n" + "}\n" + "define void @x() {\n" + "entry:\n" + " ret void\n" + "}\n"); + FunctionAnalysisManager FAM(/*DebugLogging*/ true); + int FunctionAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + int ImmutableFunctionAnalysisRuns = 0; + FAM.registerPass([&] { + return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns); + }); + + CGSCCAnalysisManager CGAM(/*DebugLogging*/ true); + int SCCAnalysisRuns = 0; + CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); + + ModuleAnalysisManager MAM(/*DebugLogging*/ true); + int ModuleAnalysisRuns = 0; + MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); + + MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); + MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); + CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(FAM); }); + CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); }); + FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + int ModulePassRunCount1 = 0; + MPM.addPass(TestModulePass(ModulePassRunCount1)); + + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + int SCCPassRunCount1 = 0; + int AnalyzedInstrCount1 = 0; + int AnalyzedSCCFunctionCount1 = 0; + int AnalyzedModuleFunctionCount1 = 0; + CGPM1.addPass(TestSCCPass(SCCPassRunCount1, AnalyzedInstrCount1, + AnalyzedSCCFunctionCount1, + AnalyzedModuleFunctionCount1)); + + FunctionPassManager FPM1(/*DebugLogging*/ true); + int FunctionPassRunCount1 = 0; + FPM1.addPass(TestFunctionPass(FunctionPassRunCount1)); + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + MPM.run(*M, MAM); + + EXPECT_EQ(1, ModulePassRunCount1); + + EXPECT_EQ(1, ModuleAnalysisRuns); + EXPECT_EQ(4, SCCAnalysisRuns); + EXPECT_EQ(6, FunctionAnalysisRuns); + EXPECT_EQ(6, ImmutableFunctionAnalysisRuns); + + EXPECT_EQ(4, SCCPassRunCount1); + EXPECT_EQ(14, AnalyzedInstrCount1); + EXPECT_EQ(6, AnalyzedSCCFunctionCount1); + EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1); +} + +} diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt index 06560cf14d4a..2292a454c27a 100644 --- a/unittests/Analysis/CMakeLists.txt +++ b/unittests/Analysis/CMakeLists.txt @@ -7,10 +7,14 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(AnalysisTests AliasAnalysisTest.cpp + BlockFrequencyInfoTest.cpp CallGraphTest.cpp CFGTest.cpp + CGSCCPassManagerTest.cpp LazyCallGraphTest.cpp + LoopPassManagerTest.cpp ScalarEvolutionTest.cpp MixedTBAATest.cpp ValueTrackingTest.cpp + UnrollAnalyzer.cpp ) diff --git a/unittests/Analysis/CallGraphTest.cpp b/unittests/Analysis/CallGraphTest.cpp index 777907a55b11..af46291074c2 100644 --- a/unittests/Analysis/CallGraphTest.cpp +++ b/unittests/Analysis/CallGraphTest.cpp @@ -44,14 +44,16 @@ template <typename Ty> void canSpecializeGraphTraitsIterators(Ty *G) { } TEST(CallGraphTest, GraphTraitsSpecialization) { - Module M("", getGlobalContext()); + LLVMContext Context; + Module M("", Context); CallGraph CG(M); canSpecializeGraphTraitsIterators(&CG); } TEST(CallGraphTest, GraphTraitsConstSpecialization) { - Module M("", getGlobalContext()); + LLVMContext Context; + Module M("", Context); CallGraph CG(M); canSpecializeGraphTraitsIterators(const_cast<const CallGraph *>(&CG)); diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp index 6caccb892395..224a9458cc88 100644 --- a/unittests/Analysis/LazyCallGraphTest.cpp +++ b/unittests/Analysis/LazyCallGraphTest.cpp @@ -21,10 +21,10 @@ using namespace llvm; namespace { -std::unique_ptr<Module> parseAssembly(const char *Assembly) { +std::unique_ptr<Module> parseAssembly(LLVMContext &Context, + const char *Assembly) { SMDiagnostic Error; - std::unique_ptr<Module> M = - parseAssemblyString(Assembly, Error, getGlobalContext()); + std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context); std::string ErrMsg; raw_string_ostream OS(ErrMsg); @@ -121,36 +121,37 @@ static const char DiamondOfTriangles[] = "}\n"; TEST(LazyCallGraphTest, BasicGraphFormation) { - std::unique_ptr<Module> M = parseAssembly(DiamondOfTriangles); + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); LazyCallGraph CG(*M); // The order of the entry nodes should be stable w.r.t. the source order of // 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++; + LazyCallGraph::Node &A1 = (I++)->getNode(CG); EXPECT_EQ("a1", A1.getFunction().getName()); - LazyCallGraph::Node &A2 = *I++; + LazyCallGraph::Node &A2 = (I++)->getNode(CG); EXPECT_EQ("a2", A2.getFunction().getName()); - LazyCallGraph::Node &A3 = *I++; + LazyCallGraph::Node &A3 = (I++)->getNode(CG); EXPECT_EQ("a3", A3.getFunction().getName()); - LazyCallGraph::Node &B1 = *I++; + LazyCallGraph::Node &B1 = (I++)->getNode(CG); EXPECT_EQ("b1", B1.getFunction().getName()); - LazyCallGraph::Node &B2 = *I++; + LazyCallGraph::Node &B2 = (I++)->getNode(CG); EXPECT_EQ("b2", B2.getFunction().getName()); - LazyCallGraph::Node &B3 = *I++; + LazyCallGraph::Node &B3 = (I++)->getNode(CG); EXPECT_EQ("b3", B3.getFunction().getName()); - LazyCallGraph::Node &C1 = *I++; + LazyCallGraph::Node &C1 = (I++)->getNode(CG); EXPECT_EQ("c1", C1.getFunction().getName()); - LazyCallGraph::Node &C2 = *I++; + LazyCallGraph::Node &C2 = (I++)->getNode(CG); EXPECT_EQ("c2", C2.getFunction().getName()); - LazyCallGraph::Node &C3 = *I++; + LazyCallGraph::Node &C3 = (I++)->getNode(CG); EXPECT_EQ("c3", C3.getFunction().getName()); - LazyCallGraph::Node &D1 = *I++; + LazyCallGraph::Node &D1 = (I++)->getNode(CG); EXPECT_EQ("d1", D1.getFunction().getName()); - LazyCallGraph::Node &D2 = *I++; + LazyCallGraph::Node &D2 = (I++)->getNode(CG); EXPECT_EQ("d2", D2.getFunction().getName()); - LazyCallGraph::Node &D3 = *I++; + LazyCallGraph::Node &D3 = (I++)->getNode(CG); EXPECT_EQ("d3", D3.getFunction().getName()); EXPECT_EQ(CG.end(), I); @@ -158,8 +159,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { // independent of order. std::vector<std::string> Nodes; - for (LazyCallGraph::Node &N : A1) - Nodes.push_back(N.getFunction().getName()); + for (LazyCallGraph::Edge &E : A1) + Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("a2", Nodes[0]); EXPECT_EQ("b2", Nodes[1]); @@ -171,8 +172,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_EQ(A3.end(), std::next(A3.begin())); EXPECT_EQ("a1", A3.begin()->getFunction().getName()); - for (LazyCallGraph::Node &N : B1) - Nodes.push_back(N.getFunction().getName()); + for (LazyCallGraph::Edge &E : B1) + Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("b2", Nodes[0]); EXPECT_EQ("d3", Nodes[1]); @@ -183,8 +184,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_EQ(B3.end(), std::next(B3.begin())); EXPECT_EQ("b1", B3.begin()->getFunction().getName()); - for (LazyCallGraph::Node &N : C1) - Nodes.push_back(N.getFunction().getName()); + for (LazyCallGraph::Edge &E : C1) + Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("c2", Nodes[0]); EXPECT_EQ("d2", Nodes[1]); @@ -202,12 +203,13 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_EQ(D3.end(), std::next(D3.begin())); EXPECT_EQ("d1", D3.begin()->getFunction().getName()); - // Now lets look at the SCCs. - auto SCCI = CG.postorder_scc_begin(); + // Now lets look at the RefSCCs and SCCs. + auto J = CG.postorder_ref_scc_begin(); - LazyCallGraph::SCC &D = *SCCI++; - for (LazyCallGraph::Node *N : D) - Nodes.push_back(N->getFunction().getName()); + LazyCallGraph::RefSCC &D = *J++; + ASSERT_EQ(1, D.size()); + for (LazyCallGraph::Node &N : *D.begin()) + Nodes.push_back(N.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ(3u, Nodes.size()); EXPECT_EQ("d1", Nodes[0]); @@ -219,9 +221,10 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_FALSE(D.isAncestorOf(D)); EXPECT_FALSE(D.isDescendantOf(D)); - LazyCallGraph::SCC &C = *SCCI++; - for (LazyCallGraph::Node *N : C) - Nodes.push_back(N->getFunction().getName()); + LazyCallGraph::RefSCC &C = *J++; + ASSERT_EQ(1, C.size()); + for (LazyCallGraph::Node &N : *C.begin()) + Nodes.push_back(N.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ(3u, Nodes.size()); EXPECT_EQ("c1", Nodes[0]); @@ -233,9 +236,10 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_TRUE(C.isAncestorOf(D)); EXPECT_FALSE(C.isDescendantOf(D)); - LazyCallGraph::SCC &B = *SCCI++; - for (LazyCallGraph::Node *N : B) - Nodes.push_back(N->getFunction().getName()); + LazyCallGraph::RefSCC &B = *J++; + ASSERT_EQ(1, B.size()); + for (LazyCallGraph::Node &N : *B.begin()) + Nodes.push_back(N.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ(3u, Nodes.size()); EXPECT_EQ("b1", Nodes[0]); @@ -249,9 +253,10 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_FALSE(B.isAncestorOf(C)); EXPECT_FALSE(C.isAncestorOf(B)); - LazyCallGraph::SCC &A = *SCCI++; - for (LazyCallGraph::Node *N : A) - Nodes.push_back(N->getFunction().getName()); + LazyCallGraph::RefSCC &A = *J++; + ASSERT_EQ(1, A.size()); + for (LazyCallGraph::Node &N : *A.begin()) + Nodes.push_back(N.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ(3u, Nodes.size()); EXPECT_EQ("a1", Nodes[0]); @@ -265,7 +270,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_TRUE(A.isAncestorOf(C)); EXPECT_TRUE(A.isAncestorOf(D)); - EXPECT_EQ(CG.postorder_scc_end(), SCCI); + EXPECT_EQ(CG.postorder_ref_scc_end(), J); } static Function &lookupFunction(Module &M, StringRef Name) { @@ -276,21 +281,21 @@ static Function &lookupFunction(Module &M, StringRef Name) { } TEST(LazyCallGraphTest, BasicGraphMutation) { - std::unique_ptr<Module> M = parseAssembly( - "define void @a() {\n" - "entry:\n" - " call void @b()\n" - " call void @c()\n" - " ret void\n" - "}\n" - "define void @b() {\n" - "entry:\n" - " ret void\n" - "}\n" - "define void @c() {\n" - "entry:\n" - " ret void\n" - "}\n"); + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " call void @b()\n" + " call void @c()\n" + " ret void\n" + "}\n" + "define void @b() {\n" + "entry:\n" + " ret void\n" + "}\n" + "define void @c() {\n" + "entry:\n" + " ret void\n" + "}\n"); LazyCallGraph CG(*M); LazyCallGraph::Node &A = CG.get(lookupFunction(*M, "a")); @@ -298,23 +303,23 @@ TEST(LazyCallGraphTest, BasicGraphMutation) { EXPECT_EQ(2, std::distance(A.begin(), A.end())); EXPECT_EQ(0, std::distance(B.begin(), B.end())); - CG.insertEdge(B, lookupFunction(*M, "c")); + CG.insertEdge(B, lookupFunction(*M, "c"), LazyCallGraph::Edge::Call); EXPECT_EQ(1, std::distance(B.begin(), B.end())); - LazyCallGraph::Node &C = *B.begin(); + LazyCallGraph::Node &C = B.begin()->getNode(CG); EXPECT_EQ(0, std::distance(C.begin(), C.end())); - CG.insertEdge(C, B.getFunction()); + CG.insertEdge(C, B.getFunction(), LazyCallGraph::Edge::Call); EXPECT_EQ(1, std::distance(C.begin(), C.end())); - EXPECT_EQ(&B, &*C.begin()); + EXPECT_EQ(&B, C.begin()->getNode()); - CG.insertEdge(C, C.getFunction()); + CG.insertEdge(C, C.getFunction(), LazyCallGraph::Edge::Call); EXPECT_EQ(2, std::distance(C.begin(), C.end())); - EXPECT_EQ(&B, &*C.begin()); - EXPECT_EQ(&C, &*std::next(C.begin())); + 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()); + EXPECT_EQ(&C, C.begin()->getNode()); CG.removeEdge(C, C.getFunction()); EXPECT_EQ(0, std::distance(C.begin(), C.end())); @@ -323,83 +328,157 @@ TEST(LazyCallGraphTest, BasicGraphMutation) { EXPECT_EQ(0, std::distance(B.begin(), B.end())); } +TEST(LazyCallGraphTest, InnerSCCFormation) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); + LazyCallGraph CG(*M); + + // 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); + + // 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. + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + // Now walk the four SCCs which should be in post-order. + auto J = RC.begin(); + LazyCallGraph::SCC &D = *J++; + for (LazyCallGraph::Node &N : D) + Nodes.push_back(N.getFunction().getName()); + std::sort(Nodes.begin(), Nodes.end()); + EXPECT_EQ(3u, Nodes.size()); + EXPECT_EQ("d1", Nodes[0]); + EXPECT_EQ("d2", Nodes[1]); + EXPECT_EQ("d3", Nodes[2]); + Nodes.clear(); + + LazyCallGraph::SCC &B = *J++; + for (LazyCallGraph::Node &N : B) + Nodes.push_back(N.getFunction().getName()); + std::sort(Nodes.begin(), Nodes.end()); + EXPECT_EQ(3u, Nodes.size()); + EXPECT_EQ("b1", Nodes[0]); + EXPECT_EQ("b2", Nodes[1]); + EXPECT_EQ("b3", Nodes[2]); + Nodes.clear(); + + LazyCallGraph::SCC &C = *J++; + for (LazyCallGraph::Node &N : C) + Nodes.push_back(N.getFunction().getName()); + std::sort(Nodes.begin(), Nodes.end()); + EXPECT_EQ(3u, Nodes.size()); + EXPECT_EQ("c1", Nodes[0]); + EXPECT_EQ("c2", Nodes[1]); + EXPECT_EQ("c3", Nodes[2]); + Nodes.clear(); + + LazyCallGraph::SCC &A = *J++; + for (LazyCallGraph::Node &N : A) + Nodes.push_back(N.getFunction().getName()); + std::sort(Nodes.begin(), Nodes.end()); + EXPECT_EQ(3u, Nodes.size()); + EXPECT_EQ("a1", Nodes[0]); + EXPECT_EQ("a2", Nodes[1]); + EXPECT_EQ("a3", Nodes[2]); + Nodes.clear(); + + EXPECT_EQ(RC.end(), J); +} + TEST(LazyCallGraphTest, MultiArmSCC) { + LLVMContext Context; // Two interlocking cycles. The really useful thing about this SCC is that it // will require Tarjan's DFS to backtrack and finish processing all of the - // children of each node in the SCC. - std::unique_ptr<Module> M = parseAssembly( - "define void @a() {\n" - "entry:\n" - " call void @b()\n" - " call void @d()\n" - " ret void\n" - "}\n" - "define void @b() {\n" - "entry:\n" - " call void @c()\n" - " ret void\n" - "}\n" - "define void @c() {\n" - "entry:\n" - " call void @a()\n" - " ret void\n" - "}\n" - "define void @d() {\n" - "entry:\n" - " call void @e()\n" - " ret void\n" - "}\n" - "define void @e() {\n" - "entry:\n" - " call void @a()\n" - " ret void\n" - "}\n"); + // children of each node in the SCC. Since this involves call edges, both + // Tarjan implementations will have to successfully navigate the structure. + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f1() {\n" + "entry:\n" + " call void @f2()\n" + " call void @f4()\n" + " ret void\n" + "}\n" + "define void @f2() {\n" + "entry:\n" + " call void @f3()\n" + " ret void\n" + "}\n" + "define void @f3() {\n" + "entry:\n" + " call void @f1()\n" + " ret void\n" + "}\n" + "define void @f4() {\n" + "entry:\n" + " call void @f5()\n" + " ret void\n" + "}\n" + "define void @f5() {\n" + "entry:\n" + " call void @f1()\n" + " ret void\n" + "}\n"); LazyCallGraph CG(*M); // Force the graph to be fully expanded. - auto SCCI = CG.postorder_scc_begin(); - LazyCallGraph::SCC &SCC = *SCCI++; - EXPECT_EQ(CG.postorder_scc_end(), SCCI); - - LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); - LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); - LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c")); - LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d")); - LazyCallGraph::Node &E = *CG.lookup(lookupFunction(*M, "e")); - EXPECT_EQ(&SCC, CG.lookupSCC(A)); - EXPECT_EQ(&SCC, CG.lookupSCC(B)); - EXPECT_EQ(&SCC, CG.lookupSCC(C)); - EXPECT_EQ(&SCC, CG.lookupSCC(D)); - EXPECT_EQ(&SCC, CG.lookupSCC(E)); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + LazyCallGraph::Node &N1 = *CG.lookup(lookupFunction(*M, "f1")); + LazyCallGraph::Node &N2 = *CG.lookup(lookupFunction(*M, "f2")); + LazyCallGraph::Node &N3 = *CG.lookup(lookupFunction(*M, "f3")); + LazyCallGraph::Node &N4 = *CG.lookup(lookupFunction(*M, "f4")); + LazyCallGraph::Node &N5 = *CG.lookup(lookupFunction(*M, "f4")); + EXPECT_EQ(&RC, CG.lookupRefSCC(N1)); + EXPECT_EQ(&RC, CG.lookupRefSCC(N2)); + EXPECT_EQ(&RC, CG.lookupRefSCC(N3)); + EXPECT_EQ(&RC, CG.lookupRefSCC(N4)); + EXPECT_EQ(&RC, CG.lookupRefSCC(N5)); + + ASSERT_EQ(1, RC.size()); + + LazyCallGraph::SCC &C = *RC.begin(); + EXPECT_EQ(&C, CG.lookupSCC(N1)); + EXPECT_EQ(&C, CG.lookupSCC(N2)); + EXPECT_EQ(&C, CG.lookupSCC(N3)); + EXPECT_EQ(&C, CG.lookupSCC(N4)); + EXPECT_EQ(&C, CG.lookupSCC(N5)); } -TEST(LazyCallGraphTest, OutgoingSCCEdgeInsertion) { - std::unique_ptr<Module> M = parseAssembly( - "define void @a() {\n" - "entry:\n" - " call void @b()\n" - " call void @c()\n" - " ret void\n" - "}\n" - "define void @b() {\n" - "entry:\n" - " call void @d()\n" - " ret void\n" - "}\n" - "define void @c() {\n" - "entry:\n" - " call void @d()\n" - " ret void\n" - "}\n" - "define void @d() {\n" - "entry:\n" - " ret void\n" - "}\n"); +TEST(LazyCallGraphTest, OutgoingEdgeMutation) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " call void @b()\n" + " call void @c()\n" + " ret void\n" + "}\n" + "define void @b() {\n" + "entry:\n" + " call void @d()\n" + " ret void\n" + "}\n" + "define void @c() {\n" + "entry:\n" + " call void @d()\n" + " ret void\n" + "}\n" + "define void @d() {\n" + "entry:\n" + " ret void\n" + "}\n"); LazyCallGraph CG(*M); // Force the graph to be fully expanded. - for (LazyCallGraph::SCC &C : CG.postorder_sccs()) - (void)C; + for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) + (void)RC; LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); @@ -409,24 +488,96 @@ TEST(LazyCallGraphTest, OutgoingSCCEdgeInsertion) { LazyCallGraph::SCC &BC = *CG.lookupSCC(B); LazyCallGraph::SCC &CC = *CG.lookupSCC(C); LazyCallGraph::SCC &DC = *CG.lookupSCC(D); - EXPECT_TRUE(AC.isAncestorOf(BC)); - EXPECT_TRUE(AC.isAncestorOf(CC)); - EXPECT_TRUE(AC.isAncestorOf(DC)); - EXPECT_TRUE(DC.isDescendantOf(AC)); - EXPECT_TRUE(DC.isDescendantOf(BC)); - EXPECT_TRUE(DC.isDescendantOf(CC)); + LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A); + LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B); + LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C); + LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D); + EXPECT_TRUE(ARC.isParentOf(BRC)); + EXPECT_TRUE(ARC.isParentOf(CRC)); + EXPECT_FALSE(ARC.isParentOf(DRC)); + EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_FALSE(DRC.isChildOf(ARC)); + EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_TRUE(DRC.isChildOf(BRC)); + EXPECT_TRUE(DRC.isChildOf(CRC)); EXPECT_EQ(2, std::distance(A.begin(), A.end())); - AC.insertOutgoingEdge(A, D); + ARC.insertOutgoingEdge(A, D, LazyCallGraph::Edge::Call); EXPECT_EQ(3, std::distance(A.begin(), A.end())); - EXPECT_TRUE(AC.isParentOf(DC)); + const LazyCallGraph::Edge &NewE = A[D]; + EXPECT_TRUE(NewE); + EXPECT_TRUE(NewE.isCall()); + EXPECT_EQ(&D, NewE.getNode()); + + // Only the parent and child tests sholud have changed. The rest of the graph + // remains the same. + EXPECT_TRUE(ARC.isParentOf(DRC)); + EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(DRC.isChildOf(ARC)); + EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_EQ(&AC, CG.lookupSCC(A)); + EXPECT_EQ(&BC, CG.lookupSCC(B)); + EXPECT_EQ(&CC, CG.lookupSCC(C)); + EXPECT_EQ(&DC, CG.lookupSCC(D)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(A)); + EXPECT_EQ(&BRC, CG.lookupRefSCC(B)); + EXPECT_EQ(&CRC, CG.lookupRefSCC(C)); + EXPECT_EQ(&DRC, CG.lookupRefSCC(D)); + + ARC.switchOutgoingEdgeToRef(A, D); + EXPECT_FALSE(NewE.isCall()); + + // Verify the graph remains the same. + EXPECT_TRUE(ARC.isParentOf(DRC)); + EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(DRC.isChildOf(ARC)); + EXPECT_TRUE(DRC.isDescendantOf(ARC)); EXPECT_EQ(&AC, CG.lookupSCC(A)); EXPECT_EQ(&BC, CG.lookupSCC(B)); EXPECT_EQ(&CC, CG.lookupSCC(C)); EXPECT_EQ(&DC, CG.lookupSCC(D)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(A)); + EXPECT_EQ(&BRC, CG.lookupRefSCC(B)); + EXPECT_EQ(&CRC, CG.lookupRefSCC(C)); + EXPECT_EQ(&DRC, CG.lookupRefSCC(D)); + + ARC.switchOutgoingEdgeToCall(A, D); + EXPECT_TRUE(NewE.isCall()); + + // Verify the graph remains the same. + EXPECT_TRUE(ARC.isParentOf(DRC)); + EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(DRC.isChildOf(ARC)); + EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_EQ(&AC, CG.lookupSCC(A)); + EXPECT_EQ(&BC, CG.lookupSCC(B)); + EXPECT_EQ(&CC, CG.lookupSCC(C)); + EXPECT_EQ(&DC, CG.lookupSCC(D)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(A)); + EXPECT_EQ(&BRC, CG.lookupRefSCC(B)); + EXPECT_EQ(&CRC, CG.lookupRefSCC(C)); + EXPECT_EQ(&DRC, CG.lookupRefSCC(D)); + + ARC.removeOutgoingEdge(A, D); + 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)); + EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_FALSE(DRC.isChildOf(ARC)); + EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_EQ(&AC, CG.lookupSCC(A)); + EXPECT_EQ(&BC, CG.lookupSCC(B)); + EXPECT_EQ(&CC, CG.lookupSCC(C)); + EXPECT_EQ(&DC, CG.lookupSCC(D)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(A)); + EXPECT_EQ(&BRC, CG.lookupRefSCC(B)); + EXPECT_EQ(&CRC, CG.lookupRefSCC(C)); + EXPECT_EQ(&DRC, CG.lookupRefSCC(D)); } -TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) { +TEST(LazyCallGraphTest, IncomingEdgeInsertion) { + LLVMContext Context; // We want to ensure we can add edges even across complex diamond graphs, so // we use the diamond of triangles graph defined above. The ascii diagram is // repeated here for easy reference. @@ -443,12 +594,12 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) { // / \ | // a3--a2 | // - std::unique_ptr<Module> M = parseAssembly(DiamondOfTriangles); + std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); LazyCallGraph CG(*M); // Force the graph to be fully expanded. - for (LazyCallGraph::SCC &C : CG.postorder_sccs()) - (void)C; + for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) + (void)RC; LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1")); LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2")); @@ -462,18 +613,18 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) { LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1")); LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2")); LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3")); - LazyCallGraph::SCC &AC = *CG.lookupSCC(A1); - LazyCallGraph::SCC &BC = *CG.lookupSCC(B1); - LazyCallGraph::SCC &CC = *CG.lookupSCC(C1); - LazyCallGraph::SCC &DC = *CG.lookupSCC(D1); - ASSERT_EQ(&AC, CG.lookupSCC(A2)); - ASSERT_EQ(&AC, CG.lookupSCC(A3)); - ASSERT_EQ(&BC, CG.lookupSCC(B2)); - ASSERT_EQ(&BC, CG.lookupSCC(B3)); - ASSERT_EQ(&CC, CG.lookupSCC(C2)); - ASSERT_EQ(&CC, CG.lookupSCC(C3)); - ASSERT_EQ(&DC, CG.lookupSCC(D2)); - ASSERT_EQ(&DC, CG.lookupSCC(D3)); + LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A1); + LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B1); + LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C1); + LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D1); + ASSERT_EQ(&ARC, CG.lookupRefSCC(A2)); + ASSERT_EQ(&ARC, CG.lookupRefSCC(A3)); + ASSERT_EQ(&BRC, CG.lookupRefSCC(B2)); + ASSERT_EQ(&BRC, CG.lookupRefSCC(B3)); + ASSERT_EQ(&CRC, CG.lookupRefSCC(C2)); + 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())); // Add an edge to make the graph: @@ -489,47 +640,52 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) { // a1 | // / \ | // a3--a2 | - CC.insertIncomingEdge(D2, C2); + auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2); // Make sure we connected the nodes. - EXPECT_EQ(2, std::distance(D2.begin(), D2.end())); + 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 SCC sets. - EXPECT_EQ(&AC, CG.lookupSCC(A1)); - EXPECT_EQ(&AC, CG.lookupSCC(A2)); - EXPECT_EQ(&AC, CG.lookupSCC(A3)); - EXPECT_EQ(&BC, CG.lookupSCC(B1)); - EXPECT_EQ(&BC, CG.lookupSCC(B2)); - EXPECT_EQ(&BC, CG.lookupSCC(B3)); - EXPECT_EQ(&CC, CG.lookupSCC(C1)); - EXPECT_EQ(&CC, CG.lookupSCC(C2)); - EXPECT_EQ(&CC, CG.lookupSCC(C3)); - EXPECT_EQ(&CC, CG.lookupSCC(D1)); - EXPECT_EQ(&CC, CG.lookupSCC(D2)); - EXPECT_EQ(&CC, CG.lookupSCC(D3)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(A1)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(A2)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(A3)); + EXPECT_EQ(&BRC, CG.lookupRefSCC(B1)); + EXPECT_EQ(&BRC, CG.lookupRefSCC(B2)); + EXPECT_EQ(&BRC, CG.lookupRefSCC(B3)); + 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)); // And that ancestry tests have been updated. - EXPECT_TRUE(AC.isParentOf(BC)); - EXPECT_TRUE(AC.isParentOf(CC)); - EXPECT_FALSE(AC.isAncestorOf(DC)); - EXPECT_FALSE(BC.isAncestorOf(DC)); - EXPECT_FALSE(CC.isAncestorOf(DC)); + EXPECT_TRUE(ARC.isParentOf(CRC)); + EXPECT_TRUE(BRC.isParentOf(CRC)); } -TEST(LazyCallGraphTest, IncomingSCCEdgeInsertionMidTraversal) { +TEST(LazyCallGraphTest, IncomingEdgeInsertionMidTraversal) { + LLVMContext Context; // This is the same fundamental test as the previous, but we perform it - // having only partially walked the SCCs of the graph. - std::unique_ptr<Module> M = parseAssembly(DiamondOfTriangles); + // having only partially walked the RefSCCs of the graph. + std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); LazyCallGraph CG(*M); - // Walk the SCCs until we find the one containing 'c1'. - auto SCCI = CG.postorder_scc_begin(), SCCE = CG.postorder_scc_end(); - ASSERT_NE(SCCI, SCCE); - LazyCallGraph::SCC &DC = *SCCI; - ASSERT_NE(&DC, nullptr); - ++SCCI; - ASSERT_NE(SCCI, SCCE); - LazyCallGraph::SCC &CC = *SCCI; - ASSERT_NE(&CC, nullptr); + // 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"))); @@ -543,178 +699,607 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertionMidTraversal) { 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(&CC, CG.lookupSCC(C1)); - ASSERT_EQ(&CC, CG.lookupSCC(C2)); - ASSERT_EQ(&CC, CG.lookupSCC(C3)); - ASSERT_EQ(&DC, CG.lookupSCC(D1)); - ASSERT_EQ(&DC, CG.lookupSCC(D2)); - ASSERT_EQ(&DC, CG.lookupSCC(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())); - CC.insertIncomingEdge(D2, C2); - EXPECT_EQ(2, 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)); + + // Check that we can form the last two RefSCCs now in a coherent way. + ++I; + EXPECT_NE(I, E); + LazyCallGraph::RefSCC &BRC = *I; + 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_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)); + ++I; + EXPECT_EQ(E, I); +} - // Make sure we have the correct nodes in the SCC sets. - EXPECT_EQ(&CC, CG.lookupSCC(C1)); - EXPECT_EQ(&CC, CG.lookupSCC(C2)); - EXPECT_EQ(&CC, CG.lookupSCC(C3)); - EXPECT_EQ(&CC, CG.lookupSCC(D1)); - EXPECT_EQ(&CC, CG.lookupSCC(D2)); - EXPECT_EQ(&CC, CG.lookupSCC(D3)); - - // Check that we can form the last two SCCs now in a coherent way. - ++SCCI; - EXPECT_NE(SCCI, SCCE); - LazyCallGraph::SCC &BC = *SCCI; - EXPECT_NE(&BC, nullptr); - EXPECT_EQ(&BC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "b1")))); - EXPECT_EQ(&BC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "b2")))); - EXPECT_EQ(&BC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "b3")))); - ++SCCI; - EXPECT_NE(SCCI, SCCE); - LazyCallGraph::SCC &AC = *SCCI; - EXPECT_NE(&AC, nullptr); - EXPECT_EQ(&AC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "a1")))); - EXPECT_EQ(&AC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "a2")))); - EXPECT_EQ(&AC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "a3")))); - ++SCCI; - EXPECT_EQ(SCCI, SCCE); +TEST(LazyCallGraphTest, InternalEdgeMutation) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " call void @b()\n" + " ret void\n" + "}\n" + "define void @b() {\n" + "entry:\n" + " call void @c()\n" + " ret void\n" + "}\n" + "define void @c() {\n" + "entry:\n" + " call void @a()\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c")); + EXPECT_EQ(&RC, CG.lookupRefSCC(A)); + EXPECT_EQ(&RC, CG.lookupRefSCC(B)); + EXPECT_EQ(&RC, CG.lookupRefSCC(C)); + EXPECT_EQ(1, RC.size()); + EXPECT_EQ(&*RC.begin(), CG.lookupSCC(A)); + EXPECT_EQ(&*RC.begin(), CG.lookupSCC(B)); + EXPECT_EQ(&*RC.begin(), CG.lookupSCC(C)); + + // 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(&RC, CG.lookupRefSCC(A)); + EXPECT_EQ(&RC, CG.lookupRefSCC(B)); + EXPECT_EQ(&RC, CG.lookupRefSCC(C)); + EXPECT_EQ(1, RC.size()); + EXPECT_EQ(&*RC.begin(), CG.lookupSCC(A)); + EXPECT_EQ(&*RC.begin(), CG.lookupSCC(B)); + EXPECT_EQ(&*RC.begin(), CG.lookupSCC(C)); + + // Switch the call edge from 'b' to 'c' to a ref edge. This will break the + // call cycle and cause us to form more SCCs. The RefSCC will remain the same + // though. + RC.switchInternalEdgeToRef(B, C); + EXPECT_EQ(&RC, CG.lookupRefSCC(A)); + EXPECT_EQ(&RC, CG.lookupRefSCC(B)); + EXPECT_EQ(&RC, CG.lookupRefSCC(C)); + auto J = RC.begin(); + // The SCCs must be in *post-order* which means successors before + // predecessors. At this point we have call edges from C to A and from A to + // B. The only valid postorder is B, A, C. + EXPECT_EQ(&*J++, CG.lookupSCC(B)); + EXPECT_EQ(&*J++, CG.lookupSCC(A)); + EXPECT_EQ(&*J++, CG.lookupSCC(C)); + EXPECT_EQ(RC.end(), J); + + // Test turning the ref edge from A to C into a call edge. This will form an + // SCC out of A and C. Since we previously had a call edge from C to A, the + // C SCC should be preserved and have A merged into it while the A SCC should + // be invalidated. + LazyCallGraph::SCC &AC = *CG.lookupSCC(A); + LazyCallGraph::SCC &CC = *CG.lookupSCC(C); + auto InvalidatedSCCs = RC.switchInternalEdgeToCall(A, C); + ASSERT_EQ(1u, InvalidatedSCCs.size()); + EXPECT_EQ(&AC, InvalidatedSCCs[0]); + EXPECT_EQ(2, CC.size()); + EXPECT_EQ(&CC, CG.lookupSCC(A)); + EXPECT_EQ(&CC, CG.lookupSCC(C)); + J = RC.begin(); + EXPECT_EQ(&*J++, CG.lookupSCC(B)); + EXPECT_EQ(&*J++, CG.lookupSCC(C)); + EXPECT_EQ(RC.end(), J); } -TEST(LazyCallGraphTest, InterSCCEdgeRemoval) { +TEST(LazyCallGraphTest, InternalEdgeRemoval) { + LLVMContext Context; + // A nice fully connected (including self-edges) RefSCC. std::unique_ptr<Module> M = parseAssembly( - "define void @a() {\n" - "entry:\n" - " call void @b()\n" - " ret void\n" - "}\n" - "define void @b() {\n" - "entry:\n" - " ret void\n" - "}\n"); + Context, "define void @a(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @b(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n" + " ret void\n" + "}\n" + "define void @c(i8** %ptr) {\n" + "entry:\n" + " store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" + " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n" + " ret void\n" + "}\n"); LazyCallGraph CG(*M); // Force the graph to be fully expanded. - for (LazyCallGraph::SCC &C : CG.postorder_sccs()) - (void)C; + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); - LazyCallGraph::SCC &AC = *CG.lookupSCC(A); - LazyCallGraph::SCC &BC = *CG.lookupSCC(B); + LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c")); + EXPECT_EQ(&RC, CG.lookupRefSCC(A)); + EXPECT_EQ(&RC, CG.lookupRefSCC(B)); + EXPECT_EQ(&RC, CG.lookupRefSCC(C)); + + // Remove the edge from b -> a, which should leave the 3 functions still in + // a single connected component because of a -> b -> c -> a. + SmallVector<LazyCallGraph::RefSCC *, 1> NewRCs = + RC.removeInternalRefEdge(B, A); + EXPECT_EQ(0u, NewRCs.size()); + EXPECT_EQ(&RC, CG.lookupRefSCC(A)); + EXPECT_EQ(&RC, CG.lookupRefSCC(B)); + EXPECT_EQ(&RC, CG.lookupRefSCC(C)); + + // Remove the edge from c -> a, which should leave 'a' in the original RefSCC + // and form a new RefSCC for 'b' and 'c'. + NewRCs = RC.removeInternalRefEdge(C, A); + EXPECT_EQ(1u, NewRCs.size()); + EXPECT_EQ(&RC, CG.lookupRefSCC(A)); + EXPECT_EQ(1, std::distance(RC.begin(), RC.end())); + LazyCallGraph::RefSCC *RC2 = CG.lookupRefSCC(B); + EXPECT_EQ(RC2, CG.lookupRefSCC(C)); + EXPECT_EQ(RC2, NewRCs[0]); +} + +TEST(LazyCallGraphTest, InternalCallEdgeToRef) { + LLVMContext Context; + // A nice fully connected (including self-edges) SCC (and RefSCC) + std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " call void @a()\n" + " call void @b()\n" + " call void @c()\n" + " ret void\n" + "}\n" + "define void @b() {\n" + "entry:\n" + " call void @a()\n" + " call void @b()\n" + " call void @c()\n" + " ret void\n" + "}\n" + "define void @c() {\n" + "entry:\n" + " call void @a()\n" + " call void @b()\n" + " call void @c()\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); - EXPECT_EQ("b", A.begin()->getFunction().getName()); - EXPECT_EQ(B.end(), B.begin()); - EXPECT_EQ(&AC, &*BC.parent_begin()); + EXPECT_EQ(1, RC.size()); + LazyCallGraph::SCC &CallC = *RC.begin(); - AC.removeInterSCCEdge(A, B); + LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c")); + EXPECT_EQ(&CallC, CG.lookupSCC(A)); + EXPECT_EQ(&CallC, CG.lookupSCC(B)); + EXPECT_EQ(&CallC, CG.lookupSCC(C)); + + // Remove the call edge from b -> a to a ref edge, which should leave the + // 3 functions still in a single connected component because of a -> b -> + // c -> a. + RC.switchInternalEdgeToRef(B, A); + EXPECT_EQ(1, RC.size()); + EXPECT_EQ(&CallC, CG.lookupSCC(A)); + EXPECT_EQ(&CallC, CG.lookupSCC(B)); + EXPECT_EQ(&CallC, CG.lookupSCC(C)); - EXPECT_EQ(A.end(), A.begin()); - EXPECT_EQ(B.end(), B.begin()); - EXPECT_EQ(BC.parent_end(), BC.parent_begin()); + // Remove the edge from c -> a, which should leave 'a' in the original SCC + // and form a new SCC for 'b' and 'c'. + RC.switchInternalEdgeToRef(C, A); + EXPECT_EQ(2, RC.size()); + EXPECT_EQ(&CallC, CG.lookupSCC(A)); + LazyCallGraph::SCC &BCallC = *CG.lookupSCC(B); + EXPECT_NE(&BCallC, &CallC); + EXPECT_EQ(&BCallC, CG.lookupSCC(C)); + auto J = RC.find(CallC); + EXPECT_EQ(&CallC, &*J); + --J; + EXPECT_EQ(&BCallC, &*J); + EXPECT_EQ(RC.begin(), J); + + // Remove the edge from c -> b, which should leave 'b' in the original SCC + // and form a new SCC for 'c'. It shouldn't change 'a's SCC. + RC.switchInternalEdgeToRef(C, B); + EXPECT_EQ(3, RC.size()); + EXPECT_EQ(&CallC, CG.lookupSCC(A)); + EXPECT_EQ(&BCallC, CG.lookupSCC(B)); + LazyCallGraph::SCC &CCallC = *CG.lookupSCC(C); + EXPECT_NE(&CCallC, &CallC); + EXPECT_NE(&CCallC, &BCallC); + J = RC.find(CallC); + EXPECT_EQ(&CallC, &*J); + --J; + EXPECT_EQ(&BCallC, &*J); + --J; + EXPECT_EQ(&CCallC, &*J); + EXPECT_EQ(RC.begin(), J); } -TEST(LazyCallGraphTest, IntraSCCEdgeInsertion) { - std::unique_ptr<Module> M1 = parseAssembly( - "define void @a() {\n" - "entry:\n" - " call void @b()\n" - " ret void\n" - "}\n" - "define void @b() {\n" - "entry:\n" - " call void @c()\n" - " ret void\n" - "}\n" - "define void @c() {\n" - "entry:\n" - " call void @a()\n" - " ret void\n" - "}\n"); - LazyCallGraph CG1(*M1); +TEST(LazyCallGraphTest, InternalRefEdgeToCall) { + LLVMContext Context; + // Basic tests for making a ref edge a call. This hits the basics of the + // process only. + std::unique_ptr<Module> M = + parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " call void @b()\n" + " call void @c()\n" + " store void()* @d, void()** undef\n" + " ret void\n" + "}\n" + "define void @b() {\n" + "entry:\n" + " store void()* @c, void()** undef\n" + " call void @d()\n" + " ret void\n" + "}\n" + "define void @c() {\n" + "entry:\n" + " store void()* @b, void()** undef\n" + " call void @d()\n" + " ret void\n" + "}\n" + "define void @d() {\n" + "entry:\n" + " store void()* @a, void()** undef\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); // Force the graph to be fully expanded. - auto SCCI = CG1.postorder_scc_begin(); - LazyCallGraph::SCC &SCC = *SCCI++; - EXPECT_EQ(CG1.postorder_scc_end(), SCCI); - - LazyCallGraph::Node &A = *CG1.lookup(lookupFunction(*M1, "a")); - LazyCallGraph::Node &B = *CG1.lookup(lookupFunction(*M1, "b")); - LazyCallGraph::Node &C = *CG1.lookup(lookupFunction(*M1, "c")); - EXPECT_EQ(&SCC, CG1.lookupSCC(A)); - EXPECT_EQ(&SCC, CG1.lookupSCC(B)); - EXPECT_EQ(&SCC, CG1.lookupSCC(C)); - - // Insert an edge from 'a' to 'c'. Nothing changes about the SCCs. - SCC.insertIntraSCCEdge(A, C); - EXPECT_EQ(2, std::distance(A.begin(), A.end())); - EXPECT_EQ(&SCC, CG1.lookupSCC(A)); - EXPECT_EQ(&SCC, CG1.lookupSCC(B)); - EXPECT_EQ(&SCC, CG1.lookupSCC(C)); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); - // Insert a self edge from 'a' back to 'a'. - SCC.insertIntraSCCEdge(A, A); - EXPECT_EQ(3, std::distance(A.begin(), A.end())); - EXPECT_EQ(&SCC, CG1.lookupSCC(A)); - EXPECT_EQ(&SCC, CG1.lookupSCC(B)); - EXPECT_EQ(&SCC, CG1.lookupSCC(C)); + LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c")); + LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d")); + LazyCallGraph::SCC &AC = *CG.lookupSCC(A); + LazyCallGraph::SCC &BC = *CG.lookupSCC(B); + LazyCallGraph::SCC &CC = *CG.lookupSCC(C); + LazyCallGraph::SCC &DC = *CG.lookupSCC(D); + + // Check the initial post-order. Note that B and C could be flipped here (and + // in our mutation) without changing the nature of this test. + ASSERT_EQ(4, RC.size()); + EXPECT_EQ(&DC, &RC[0]); + EXPECT_EQ(&BC, &RC[1]); + EXPECT_EQ(&CC, &RC[2]); + EXPECT_EQ(&AC, &RC[3]); + + // Switch the ref edge from A -> D to a call edge. This should have no + // effect as it is already in postorder and no new cycles are formed. + auto MergedCs = RC.switchInternalEdgeToCall(A, D); + EXPECT_EQ(0u, MergedCs.size()); + ASSERT_EQ(4, RC.size()); + EXPECT_EQ(&DC, &RC[0]); + EXPECT_EQ(&BC, &RC[1]); + EXPECT_EQ(&CC, &RC[2]); + EXPECT_EQ(&AC, &RC[3]); + + // Switch B -> C to a call edge. This doesn't form any new cycles but does + // require reordering the SCCs. + MergedCs = RC.switchInternalEdgeToCall(B, C); + EXPECT_EQ(0u, MergedCs.size()); + ASSERT_EQ(4, RC.size()); + EXPECT_EQ(&DC, &RC[0]); + EXPECT_EQ(&CC, &RC[1]); + EXPECT_EQ(&BC, &RC[2]); + EXPECT_EQ(&AC, &RC[3]); + + // Switch C -> B to a call edge. This forms a cycle and forces merging SCCs. + MergedCs = RC.switchInternalEdgeToCall(C, B); + ASSERT_EQ(1u, MergedCs.size()); + EXPECT_EQ(&CC, MergedCs[0]); + ASSERT_EQ(3, RC.size()); + EXPECT_EQ(&DC, &RC[0]); + EXPECT_EQ(&BC, &RC[1]); + EXPECT_EQ(&AC, &RC[2]); + EXPECT_EQ(2, BC.size()); + EXPECT_EQ(&BC, CG.lookupSCC(B)); + EXPECT_EQ(&BC, CG.lookupSCC(C)); } -TEST(LazyCallGraphTest, IntraSCCEdgeRemoval) { - // A nice fully connected (including self-edges) SCC. - std::unique_ptr<Module> M1 = parseAssembly( - "define void @a() {\n" - "entry:\n" - " call void @a()\n" - " call void @b()\n" - " call void @c()\n" - " ret void\n" - "}\n" - "define void @b() {\n" - "entry:\n" - " call void @a()\n" - " call void @b()\n" - " call void @c()\n" - " ret void\n" - "}\n" - "define void @c() {\n" - "entry:\n" - " call void @a()\n" - " call void @b()\n" - " call void @c()\n" - " ret void\n" - "}\n"); - LazyCallGraph CG1(*M1); +TEST(LazyCallGraphTest, InternalRefEdgeToCallNoCycleInterleaved) { + LLVMContext Context; + // Test for having a post-order prior to changing a ref edge to a call edge + // with SCCs connecting to the source and connecting to the target, but not + // connecting to both, interleaved between the source and target. This + // ensures we correctly partition the range rather than simply moving one or + // the other. + std::unique_ptr<Module> M = + parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " call void @b1()\n" + " call void @c1()\n" + " ret void\n" + "}\n" + "define void @b1() {\n" + "entry:\n" + " call void @c1()\n" + " call void @b2()\n" + " ret void\n" + "}\n" + "define void @c1() {\n" + "entry:\n" + " call void @b2()\n" + " call void @c2()\n" + " ret void\n" + "}\n" + "define void @b2() {\n" + "entry:\n" + " call void @c2()\n" + " call void @b3()\n" + " ret void\n" + "}\n" + "define void @c2() {\n" + "entry:\n" + " call void @b3()\n" + " call void @c3()\n" + " ret void\n" + "}\n" + "define void @b3() {\n" + "entry:\n" + " call void @c3()\n" + " call void @d()\n" + " ret void\n" + "}\n" + "define void @c3() {\n" + "entry:\n" + " store void()* @b1, void()** undef\n" + " call void @d()\n" + " ret void\n" + "}\n" + "define void @d() {\n" + "entry:\n" + " store void()* @a, void()** undef\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); // Force the graph to be fully expanded. - auto SCCI = CG1.postorder_scc_begin(); - LazyCallGraph::SCC &SCC = *SCCI++; - EXPECT_EQ(CG1.postorder_scc_end(), SCCI); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); - LazyCallGraph::Node &A = *CG1.lookup(lookupFunction(*M1, "a")); - LazyCallGraph::Node &B = *CG1.lookup(lookupFunction(*M1, "b")); - LazyCallGraph::Node &C = *CG1.lookup(lookupFunction(*M1, "c")); - EXPECT_EQ(&SCC, CG1.lookupSCC(A)); - EXPECT_EQ(&SCC, CG1.lookupSCC(B)); - EXPECT_EQ(&SCC, CG1.lookupSCC(C)); + LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &B1 = *CG.lookup(lookupFunction(*M, "b1")); + LazyCallGraph::Node &B2 = *CG.lookup(lookupFunction(*M, "b2")); + LazyCallGraph::Node &B3 = *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 &D = *CG.lookup(lookupFunction(*M, "d")); + LazyCallGraph::SCC &AC = *CG.lookupSCC(A); + LazyCallGraph::SCC &B1C = *CG.lookupSCC(B1); + LazyCallGraph::SCC &B2C = *CG.lookupSCC(B2); + LazyCallGraph::SCC &B3C = *CG.lookupSCC(B3); + LazyCallGraph::SCC &C1C = *CG.lookupSCC(C1); + LazyCallGraph::SCC &C2C = *CG.lookupSCC(C2); + LazyCallGraph::SCC &C3C = *CG.lookupSCC(C3); + LazyCallGraph::SCC &DC = *CG.lookupSCC(D); - // Remove the edge from b -> a, which should leave the 3 functions still in - // a single connected component because of a -> b -> c -> a. - SmallVector<LazyCallGraph::SCC *, 1> NewSCCs = SCC.removeIntraSCCEdge(B, A); - EXPECT_EQ(0u, NewSCCs.size()); - EXPECT_EQ(&SCC, CG1.lookupSCC(A)); - EXPECT_EQ(&SCC, CG1.lookupSCC(B)); - EXPECT_EQ(&SCC, CG1.lookupSCC(C)); + // Several call edges are initially present to force a particual post-order. + // Remove them now, leaving an interleaved post-order pattern. + RC.switchInternalEdgeToRef(B3, C3); + RC.switchInternalEdgeToRef(C2, B3); + RC.switchInternalEdgeToRef(B2, C2); + RC.switchInternalEdgeToRef(C1, B2); + RC.switchInternalEdgeToRef(B1, C1); + + // Check the initial post-order. We ensure this order with the extra edges + // that are nuked above. + ASSERT_EQ(8, RC.size()); + EXPECT_EQ(&DC, &RC[0]); + EXPECT_EQ(&C3C, &RC[1]); + EXPECT_EQ(&B3C, &RC[2]); + EXPECT_EQ(&C2C, &RC[3]); + EXPECT_EQ(&B2C, &RC[4]); + EXPECT_EQ(&C1C, &RC[5]); + EXPECT_EQ(&B1C, &RC[6]); + EXPECT_EQ(&AC, &RC[7]); + + // Switch C3 -> B1 to a call edge. This doesn't form any new cycles but does + // require reordering the SCCs in the face of tricky internal node + // structures. + auto MergedCs = RC.switchInternalEdgeToCall(C3, B1); + EXPECT_EQ(0u, MergedCs.size()); + ASSERT_EQ(8, RC.size()); + EXPECT_EQ(&DC, &RC[0]); + EXPECT_EQ(&B3C, &RC[1]); + EXPECT_EQ(&B2C, &RC[2]); + EXPECT_EQ(&B1C, &RC[3]); + EXPECT_EQ(&C3C, &RC[4]); + EXPECT_EQ(&C2C, &RC[5]); + EXPECT_EQ(&C1C, &RC[6]); + EXPECT_EQ(&AC, &RC[7]); +} - // Remove the edge from c -> a, which should leave 'a' in the original SCC - // and form a new SCC for 'b' and 'c'. - NewSCCs = SCC.removeIntraSCCEdge(C, A); - EXPECT_EQ(1u, NewSCCs.size()); - EXPECT_EQ(&SCC, CG1.lookupSCC(A)); - EXPECT_EQ(1, std::distance(SCC.begin(), SCC.end())); - LazyCallGraph::SCC *SCC2 = CG1.lookupSCC(B); - EXPECT_EQ(SCC2, CG1.lookupSCC(C)); - EXPECT_EQ(SCC2, NewSCCs[0]); +TEST(LazyCallGraphTest, InternalRefEdgeToCallBothPartitionAndMerge) { + LLVMContext Context; + // Test for having a postorder where between the source and target are all + // three kinds of other SCCs: + // 1) One connected to the target only that have to be shifted below the + // source. + // 2) One connected to the source only that have to be shifted below the + // target. + // 3) One connected to both source and target that has to remain and get + // merged away. + // + // To achieve this we construct a heavily connected graph to force + // a particular post-order. Then we remove the forcing edges and connect + // a cycle. + // + // Diagram for the graph we want on the left and the graph we use to force + // the ordering on the right. Edges ponit down or right. + // + // A | A | + // / \ | / \ | + // B E | B \ | + // |\ | | |\ | | + // | D | | C-D-E | + // | \| | | \| | + // C F | \ F | + // \ / | \ / | + // G | G | + // + // And we form a cycle by connecting F to B. + std::unique_ptr<Module> M = + parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " call void @b()\n" + " call void @e()\n" + " ret void\n" + "}\n" + "define void @b() {\n" + "entry:\n" + " call void @c()\n" + " call void @d()\n" + " ret void\n" + "}\n" + "define void @c() {\n" + "entry:\n" + " call void @d()\n" + " call void @g()\n" + " ret void\n" + "}\n" + "define void @d() {\n" + "entry:\n" + " call void @e()\n" + " call void @f()\n" + " ret void\n" + "}\n" + "define void @e() {\n" + "entry:\n" + " call void @f()\n" + " ret void\n" + "}\n" + "define void @f() {\n" + "entry:\n" + " store void()* @b, void()** undef\n" + " call void @g()\n" + " ret void\n" + "}\n" + "define void @g() {\n" + "entry:\n" + " store void()* @a, void()** undef\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &RC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c")); + LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d")); + LazyCallGraph::Node &E = *CG.lookup(lookupFunction(*M, "e")); + LazyCallGraph::Node &F = *CG.lookup(lookupFunction(*M, "f")); + LazyCallGraph::Node &G = *CG.lookup(lookupFunction(*M, "g")); + LazyCallGraph::SCC &AC = *CG.lookupSCC(A); + LazyCallGraph::SCC &BC = *CG.lookupSCC(B); + LazyCallGraph::SCC &CC = *CG.lookupSCC(C); + LazyCallGraph::SCC &DC = *CG.lookupSCC(D); + LazyCallGraph::SCC &EC = *CG.lookupSCC(E); + LazyCallGraph::SCC &FC = *CG.lookupSCC(F); + LazyCallGraph::SCC &GC = *CG.lookupSCC(G); + + // Remove the extra edges that were used to force a particular post-order. + RC.switchInternalEdgeToRef(C, D); + RC.switchInternalEdgeToRef(D, E); + + // Check the initial post-order. We ensure this order with the extra edges + // that are nuked above. + ASSERT_EQ(7, RC.size()); + EXPECT_EQ(&GC, &RC[0]); + EXPECT_EQ(&FC, &RC[1]); + EXPECT_EQ(&EC, &RC[2]); + EXPECT_EQ(&DC, &RC[3]); + EXPECT_EQ(&CC, &RC[4]); + EXPECT_EQ(&BC, &RC[5]); + EXPECT_EQ(&AC, &RC[6]); + + // Switch F -> B to a call edge. This merges B, D, and F into a single SCC, + // and has to place the C and E SCCs on either side of it: + // A A | + // / \ / \ | + // B E | E | + // |\ | \ / | + // | D | -> B | + // | \| / \ | + // C F C | | + // \ / \ / | + // G G | + auto MergedCs = RC.switchInternalEdgeToCall(F, B); + ASSERT_EQ(2u, MergedCs.size()); + EXPECT_EQ(&FC, MergedCs[0]); + EXPECT_EQ(&DC, MergedCs[1]); + EXPECT_EQ(3, BC.size()); + + // And make sure the postorder was updated. + ASSERT_EQ(5, RC.size()); + EXPECT_EQ(&GC, &RC[0]); + EXPECT_EQ(&CC, &RC[1]); + EXPECT_EQ(&BC, &RC[2]); + EXPECT_EQ(&EC, &RC[3]); + EXPECT_EQ(&AC, &RC[4]); } } diff --git a/unittests/Analysis/LoopPassManagerTest.cpp b/unittests/Analysis/LoopPassManagerTest.cpp new file mode 100644 index 000000000000..5858e174aabb --- /dev/null +++ b/unittests/Analysis/LoopPassManagerTest.cpp @@ -0,0 +1,205 @@ +//===- llvm/unittest/Analysis/LoopPassManagerTest.cpp - LPM tests ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/Analysis/LoopPassManager.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/SourceMgr.h" + +using namespace llvm; + +namespace { + +class TestLoopAnalysis { + /// \brief Private static data to provide unique ID. + static char PassID; + + int &Runs; + +public: + struct Result { + Result(int Count) : BlockCount(Count) {} + int BlockCount; + }; + + /// \brief Returns an opaque, unique ID for this pass type. + static void *ID() { return (void *)&PassID; } + + /// \brief Returns the name of the analysis. + static StringRef name() { return "TestLoopAnalysis"; } + + TestLoopAnalysis(int &Runs) : Runs(Runs) {} + + /// \brief Run the analysis pass over the loop and return a result. + Result run(Loop &L, AnalysisManager<Loop> &AM) { + ++Runs; + int Count = 0; + + for (auto I = L.block_begin(), E = L.block_end(); I != E; ++I) + ++Count; + return Result(Count); + } +}; + +char TestLoopAnalysis::PassID; + +class TestLoopPass { + std::vector<StringRef> &VisitedLoops; + int &AnalyzedBlockCount; + bool OnlyUseCachedResults; + +public: + TestLoopPass(std::vector<StringRef> &VisitedLoops, int &AnalyzedBlockCount, + bool OnlyUseCachedResults = false) + : VisitedLoops(VisitedLoops), AnalyzedBlockCount(AnalyzedBlockCount), + OnlyUseCachedResults(OnlyUseCachedResults) {} + + PreservedAnalyses run(Loop &L, AnalysisManager<Loop> &AM) { + VisitedLoops.push_back(L.getName()); + + if (OnlyUseCachedResults) { + // Hack to force the use of the cached interface. + if (auto *AR = AM.getCachedResult<TestLoopAnalysis>(L)) + AnalyzedBlockCount += AR->BlockCount; + } else { + // Typical path just runs the analysis as needed. + auto &AR = AM.getResult<TestLoopAnalysis>(L); + AnalyzedBlockCount += AR.BlockCount; + } + + return PreservedAnalyses::all(); + } + + static StringRef name() { return "TestLoopPass"; } +}; + +// A test loop pass that invalidates the analysis for loops with the given name. +class TestLoopInvalidatingPass { + StringRef Name; + +public: + TestLoopInvalidatingPass(StringRef LoopName) : Name(LoopName) {} + + PreservedAnalyses run(Loop &L, AnalysisManager<Loop> &AM) { + return L.getName() == Name ? getLoopPassPreservedAnalyses() + : PreservedAnalyses::all(); + } + + static StringRef name() { return "TestLoopInvalidatingPass"; } +}; + +std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { + SMDiagnostic Err; + return parseAssemblyString(IR, Err, C); +} + +class LoopPassManagerTest : public ::testing::Test { +protected: + LLVMContext Context; + std::unique_ptr<Module> M; + +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")) {} +}; + +#define EXPECT_N_ELEMENTS_EQ(N, EXPECTED, ACTUAL) \ + do { \ + EXPECT_EQ(N##UL, ACTUAL.size()); \ + for (int I = 0; I < N; ++I) \ + EXPECT_TRUE(EXPECTED[I] == ACTUAL[I]) << "Element " << I << " is " \ + << ACTUAL[I] << ". Expected " \ + << EXPECTED[I] << "."; \ + } while (0) + +TEST_F(LoopPassManagerTest, Basic) { + LoopAnalysisManager LAM(true); + int LoopAnalysisRuns = 0; + LAM.registerPass([&] { return TestLoopAnalysis(LoopAnalysisRuns); }); + + FunctionAnalysisManager FAM(true); + // We need DominatorTreeAnalysis for LoopAnalysis. + FAM.registerPass([&] { return DominatorTreeAnalysis(); }); + FAM.registerPass([&] { return LoopAnalysis(); }); + FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); }); + LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); }); + + ModuleAnalysisManager MAM(true); + MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); + + ModulePassManager MPM(true); + FunctionPassManager FPM(true); + + // Visit all of the loops. + std::vector<StringRef> VisitedLoops1; + int AnalyzedBlockCount1 = 0; + { + LoopPassManager LPM; + LPM.addPass(TestLoopPass(VisitedLoops1, AnalyzedBlockCount1)); + + FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM))); + } + + // Only use cached analyses. + std::vector<StringRef> VisitedLoops2; + int AnalyzedBlockCount2 = 0; + { + LoopPassManager LPM; + LPM.addPass(TestLoopInvalidatingPass("loop.g.0")); + LPM.addPass(TestLoopPass(VisitedLoops2, AnalyzedBlockCount2, + /*OnlyUseCachedResults=*/true)); + + FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM))); + } + + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + MPM.run(*M, MAM); + + StringRef ExpectedLoops[] = {"loop.0.0", "loop.0.1", "loop.0", "loop.g.0"}; + + // Validate the counters and order of loops visited. + // loop.0 has 3 blocks whereas loop.0.0, loop.0.1, and loop.g.0 each have 1. + EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops1); + EXPECT_EQ(6, AnalyzedBlockCount1); + + EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops2); + // The block from loop.g.0 won't be counted, since it wasn't cached. + EXPECT_EQ(5, AnalyzedBlockCount2); + + // The first LPM runs the loop analysis for all four loops, the second uses + // cached results for everything. + EXPECT_EQ(4, LoopAnalysisRuns); +} +} diff --git a/unittests/Analysis/Makefile b/unittests/Analysis/Makefile deleted file mode 100644 index 527f4525e87e..000000000000 --- a/unittests/Analysis/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/Analysis/Makefile -------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = Analysis -LINK_COMPONENTS := analysis asmparser - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Analysis/MixedTBAATest.cpp b/unittests/Analysis/MixedTBAATest.cpp index d0cfa59f6459..d70324f2c6ab 100644 --- a/unittests/Analysis/MixedTBAATest.cpp +++ b/unittests/Analysis/MixedTBAATest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/TypeBasedAliasAnalysis.h" +#include "llvm/Analysis/AliasAnalysisEvaluator.h" #include "llvm/Analysis/Passes.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" diff --git a/unittests/Analysis/ScalarEvolutionTest.cpp b/unittests/Analysis/ScalarEvolutionTest.cpp index 938dafe60384..c4cd74f01c72 100644 --- a/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/unittests/Analysis/ScalarEvolutionTest.cpp @@ -237,5 +237,32 @@ TEST_F(ScalarEvolutionsTest, SCEVMultiplyAddRecs) { EXPECT_EQ(Product->getOperand(8), SE.getAddExpr(Sum)); } +TEST_F(ScalarEvolutionsTest, SimplifiedPHI) { + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), + std::vector<Type *>(), false); + Function *F = cast<Function>(M.getOrInsertFunction("f", FTy)); + BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F); + BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F); + BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F); + BranchInst::Create(LoopBB, EntryBB); + BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), + LoopBB); + ReturnInst::Create(Context, nullptr, ExitBB); + auto *Ty = Type::getInt32Ty(Context); + auto *PN = PHINode::Create(Ty, 2, "", &*LoopBB->begin()); + PN->addIncoming(Constant::getNullValue(Ty), EntryBB); + PN->addIncoming(UndefValue::get(Ty), LoopBB); + ScalarEvolution SE = buildSE(*F); + auto *S1 = SE.getSCEV(PN); + auto *S2 = SE.getSCEV(PN); + auto *ZeroConst = SE.getConstant(Ty, 0); + + // At some point, only the first call to getSCEV returned the simplified + // SCEVConstant and later calls just returned a SCEVUnknown referencing the + // PHI node. + EXPECT_EQ(S1, ZeroConst); + EXPECT_EQ(S1, S2); +} + } // end anonymous namespace } // end namespace llvm diff --git a/unittests/Analysis/UnrollAnalyzer.cpp b/unittests/Analysis/UnrollAnalyzer.cpp new file mode 100644 index 000000000000..6d11ab1f2f1b --- /dev/null +++ b/unittests/Analysis/UnrollAnalyzer.cpp @@ -0,0 +1,331 @@ +//===- UnrollAnalyzerTest.cpp - UnrollAnalyzer 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/AsmParser/Parser.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Analysis/LoopUnrollAnalyzer.h" +#include "llvm/IR/Dominators.h" +#include "gtest/gtest.h" + +using namespace llvm; +namespace llvm { +void initializeUnrollAnalyzerTestPass(PassRegistry &); + +static SmallVector<DenseMap<Value *, Constant *>, 16> SimplifiedValuesVector; +static unsigned TripCount = 0; + +namespace { +struct UnrollAnalyzerTest : public FunctionPass { + static char ID; + bool runOnFunction(Function &F) override { + LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); + ScalarEvolution *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE(); + + Function::iterator FI = F.begin(); + FI++; // First basic block is entry - skip it. + BasicBlock *Header = &*FI++; + Loop *L = LI->getLoopFor(Header); + BasicBlock *Exiting = L->getExitingBlock(); + + SimplifiedValuesVector.clear(); + TripCount = SE->getSmallConstantTripCount(L, Exiting); + for (unsigned Iteration = 0; Iteration < TripCount; Iteration++) { + DenseMap<Value *, Constant *> SimplifiedValues; + UnrolledInstAnalyzer Analyzer(Iteration, SimplifiedValues, *SE, L); + for (auto *BB : L->getBlocks()) + for (Instruction &I : *BB) + Analyzer.visit(I); + SimplifiedValuesVector.push_back(SimplifiedValues); + } + return false; + } + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired<DominatorTreeWrapperPass>(); + AU.addRequired<LoopInfoWrapperPass>(); + AU.addRequired<ScalarEvolutionWrapperPass>(); + AU.setPreservesAll(); + } + UnrollAnalyzerTest() : FunctionPass(ID) { + initializeUnrollAnalyzerTestPass(*PassRegistry::getPassRegistry()); + } +}; +} + +char UnrollAnalyzerTest::ID = 0; + +std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context, + UnrollAnalyzerTest *P, + const char *ModuleStr) { + SMDiagnostic Err; + return parseAssemblyString(ModuleStr, Err, Context); +} + +TEST(UnrollAnalyzerTest, BasicSimplifications) { + const char *ModuleStr = + "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n" + "define i64 @propagate_loop_phis() {\n" + "entry:\n" + " br label %loop\n" + "loop:\n" + " %iv = phi i64 [ 0, %entry ], [ %inc, %loop ]\n" + " %x0 = phi i64 [ 0, %entry ], [ %x2, %loop ]\n" + " %x1 = or i64 %x0, 1\n" + " %x2 = or i64 %x1, 2\n" + " %inc = add nuw nsw i64 %iv, 1\n" + " %cond = icmp sge i64 %inc, 8\n" + " br i1 %cond, label %loop.end, label %loop\n" + "loop.end:\n" + " %x.lcssa = phi i64 [ %x2, %loop ]\n" + " ret i64 %x.lcssa\n" + "}\n"; + UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + legacy::PassManager Passes; + Passes.add(P); + Passes.run(*M); + + // Perform checks + Module::iterator MI = M->begin(); + Function *F = &*MI++; + Function::iterator FI = F->begin(); + FI++; // First basic block is entry - skip it. + BasicBlock *Header = &*FI++; + + BasicBlock::iterator BBI = Header->begin(); + std::advance(BBI, 4); + Instruction *Y1 = &*BBI++; + Instruction *Y2 = &*BBI++; + // Check simplification expected on the 1st iteration. + // Check that "%inc = add nuw nsw i64 %iv, 1" is simplified to 1 + auto I1 = SimplifiedValuesVector[0].find(Y1); + EXPECT_TRUE(I1 != SimplifiedValuesVector[0].end()); + EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 1U); + + // Check that "%cond = icmp sge i64 %inc, 10" is simplified to false + auto I2 = SimplifiedValuesVector[0].find(Y2); + EXPECT_TRUE(I2 != SimplifiedValuesVector[0].end()); + EXPECT_FALSE(cast<ConstantInt>((*I2).second)->getZExtValue()); + + // Check simplification expected on the last iteration. + // Check that "%inc = add nuw nsw i64 %iv, 1" is simplified to 8 + I1 = SimplifiedValuesVector[TripCount - 1].find(Y1); + EXPECT_TRUE(I1 != SimplifiedValuesVector[TripCount - 1].end()); + EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), TripCount); + + // Check that "%cond = icmp sge i64 %inc, 10" is simplified to false + I2 = SimplifiedValuesVector[TripCount - 1].find(Y2); + EXPECT_TRUE(I2 != SimplifiedValuesVector[TripCount - 1].end()); + EXPECT_TRUE(cast<ConstantInt>((*I2).second)->getZExtValue()); +} + +TEST(UnrollAnalyzerTest, OuterLoopSimplification) { + const char *ModuleStr = + "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n" + "define void @foo() {\n" + "entry:\n" + " br label %outer.loop\n" + "outer.loop:\n" + " %iv.outer = phi i64 [ 0, %entry ], [ %iv.outer.next, %outer.loop.latch ]\n" + " %iv.outer.next = add nuw nsw i64 %iv.outer, 1\n" + " br label %inner.loop\n" + "inner.loop:\n" + " %iv.inner = phi i64 [ 0, %outer.loop ], [ %iv.inner.next, %inner.loop ]\n" + " %iv.inner.next = add nuw nsw i64 %iv.inner, 1\n" + " %exitcond.inner = icmp eq i64 %iv.inner.next, 1000\n" + " br i1 %exitcond.inner, label %outer.loop.latch, label %inner.loop\n" + "outer.loop.latch:\n" + " %exitcond.outer = icmp eq i64 %iv.outer.next, 40\n" + " br i1 %exitcond.outer, label %exit, label %outer.loop\n" + "exit:\n" + " ret void\n" + "}\n"; + + UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + legacy::PassManager Passes; + Passes.add(P); + Passes.run(*M); + + Module::iterator MI = M->begin(); + Function *F = &*MI++; + Function::iterator FI = F->begin(); + FI++; + BasicBlock *Header = &*FI++; + BasicBlock *InnerBody = &*FI++; + + BasicBlock::iterator BBI = Header->begin(); + BBI++; + Instruction *Y1 = &*BBI; + BBI = InnerBody->begin(); + BBI++; + Instruction *Y2 = &*BBI; + // Check that we can simplify IV of the outer loop, but can't simplify the IV + // of the inner loop if we only know the iteration number of the outer loop. + // + // Y1 is %iv.outer.next, Y2 is %iv.inner.next + auto I1 = SimplifiedValuesVector[0].find(Y1); + EXPECT_TRUE(I1 != SimplifiedValuesVector[0].end()); + auto I2 = SimplifiedValuesVector[0].find(Y2); + EXPECT_TRUE(I2 == SimplifiedValuesVector[0].end()); +} +TEST(UnrollAnalyzerTest, CmpSimplifications) { + const char *ModuleStr = + "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n" + "define void @branch_iv_trunc() {\n" + "entry:\n" + " br label %for.body\n" + "for.body:\n" + " %indvars.iv = phi i64 [ 0, %entry ], [ %tmp3, %for.body ]\n" + " %tmp2 = trunc i64 %indvars.iv to i32\n" + " %cmp3 = icmp eq i32 %tmp2, 5\n" + " %tmp3 = add nuw nsw i64 %indvars.iv, 1\n" + " %exitcond = icmp eq i64 %tmp3, 10\n" + " br i1 %exitcond, label %for.end, label %for.body\n" + "for.end:\n" + " ret void\n" + "}\n"; + UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + legacy::PassManager Passes; + Passes.add(P); + Passes.run(*M); + + // Perform checks + Module::iterator MI = M->begin(); + Function *F = &*MI++; + Function::iterator FI = F->begin(); + FI++; // First basic block is entry - skip it. + BasicBlock *Header = &*FI++; + + BasicBlock::iterator BBI = Header->begin(); + BBI++; + Instruction *Y1 = &*BBI++; + Instruction *Y2 = &*BBI++; + // Check simplification expected on the 5th iteration. + // Check that "%tmp2 = trunc i64 %indvars.iv to i32" is simplified to 5 + // and "%cmp3 = icmp eq i32 %tmp2, 5" is simplified to 1 (i.e. true). + auto I1 = SimplifiedValuesVector[5].find(Y1); + EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 5U); + auto I2 = SimplifiedValuesVector[5].find(Y2); + EXPECT_TRUE(I2 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast<ConstantInt>((*I2).second)->getZExtValue(), 1U); +} +TEST(UnrollAnalyzerTest, PtrCmpSimplifications) { + const char *ModuleStr = + "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n" + "define void @ptr_cmp(i8 *%a) {\n" + "entry:\n" + " %limit = getelementptr i8, i8* %a, i64 40\n" + " %start.iv2 = getelementptr i8, i8* %a, i64 7\n" + " br label %loop.body\n" + "loop.body:\n" + " %iv.0 = phi i8* [ %a, %entry ], [ %iv.1, %loop.body ]\n" + " %iv2.0 = phi i8* [ %start.iv2, %entry ], [ %iv2.1, %loop.body ]\n" + " %cmp = icmp eq i8* %iv2.0, %iv.0\n" + " %iv.1 = getelementptr inbounds i8, i8* %iv.0, i64 1\n" + " %iv2.1 = getelementptr inbounds i8, i8* %iv2.0, i64 1\n" + " %exitcond = icmp ne i8* %iv.1, %limit\n" + " br i1 %exitcond, label %loop.body, label %loop.exit\n" + "loop.exit:\n" + " ret void\n" + "}\n"; + UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + legacy::PassManager Passes; + Passes.add(P); + Passes.run(*M); + + // Perform checks + Module::iterator MI = M->begin(); + Function *F = &*MI++; + Function::iterator FI = F->begin(); + FI++; // First basic block is entry - skip it. + BasicBlock *Header = &*FI; + + BasicBlock::iterator BBI = Header->begin(); + std::advance(BBI, 2); + Instruction *Y1 = &*BBI; + // Check simplification expected on the 5th iteration. + // Check that "%cmp = icmp eq i8* %iv2.0, %iv.0" is simplified to 0. + auto I1 = SimplifiedValuesVector[5].find(Y1); + EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 0U); +} +TEST(UnrollAnalyzerTest, CastSimplifications) { + const char *ModuleStr = + "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n" + "@known_constant = internal unnamed_addr constant [10 x i32] [i32 0, i32 1, i32 0, i32 1, i32 0, i32 259, i32 0, i32 1, i32 0, i32 1], align 16\n" + "define void @const_load_cast() {\n" + "entry:\n" + " br label %loop\n" + "\n" + "loop:\n" + " %iv = phi i64 [ 0, %entry ], [ %inc, %loop ]\n" + " %array_const_idx = getelementptr inbounds [10 x i32], [10 x i32]* @known_constant, i64 0, i64 %iv\n" + " %const_array_element = load i32, i32* %array_const_idx, align 4\n" + " %se = sext i32 %const_array_element to i64\n" + " %ze = zext i32 %const_array_element to i64\n" + " %tr = trunc i32 %const_array_element to i8\n" + " %inc = add nuw nsw i64 %iv, 1\n" + " %exitcond86.i = icmp eq i64 %inc, 10\n" + " br i1 %exitcond86.i, label %loop.end, label %loop\n" + "\n" + "loop.end:\n" + " ret void\n" + "}\n"; + + UnrollAnalyzerTest *P = new UnrollAnalyzerTest(); + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr); + legacy::PassManager Passes; + Passes.add(P); + Passes.run(*M); + + // Perform checks + Module::iterator MI = M->begin(); + Function *F = &*MI++; + Function::iterator FI = F->begin(); + FI++; // First basic block is entry - skip it. + BasicBlock *Header = &*FI++; + + BasicBlock::iterator BBI = Header->begin(); + std::advance(BBI, 3); + Instruction *Y1 = &*BBI++; + Instruction *Y2 = &*BBI++; + Instruction *Y3 = &*BBI++; + // Check simplification expected on the 5th iteration. + // "%se = sext i32 %const_array_element to i64" should be simplified to 259, + // "%ze = zext i32 %const_array_element to i64" should be simplified to 259, + // "%tr = trunc i32 %const_array_element to i8" should be simplified to 3. + auto I1 = SimplifiedValuesVector[5].find(Y1); + EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 259U); + auto I2 = SimplifiedValuesVector[5].find(Y2); + EXPECT_TRUE(I2 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast<ConstantInt>((*I2).second)->getZExtValue(), 259U); + auto I3 = SimplifiedValuesVector[5].find(Y3); + EXPECT_TRUE(I3 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast<ConstantInt>((*I3).second)->getZExtValue(), 3U); +} + +} // end namespace llvm + +INITIALIZE_PASS_BEGIN(UnrollAnalyzerTest, "unrollanalyzertestpass", + "unrollanalyzertestpass", false, false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) +INITIALIZE_PASS_END(UnrollAnalyzerTest, "unrollanalyzertestpass", + "unrollanalyzertestpass", false, false) diff --git a/unittests/Analysis/ValueTrackingTest.cpp b/unittests/Analysis/ValueTrackingTest.cpp index 3af856ea2039..f429c3b99149 100644 --- a/unittests/Analysis/ValueTrackingTest.cpp +++ b/unittests/Analysis/ValueTrackingTest.cpp @@ -25,7 +25,7 @@ class MatchSelectPatternTest : public testing::Test { protected: void parseAssembly(const char *Assembly) { SMDiagnostic Error; - M = parseAssemblyString(Assembly, Error, getGlobalContext()); + M = parseAssemblyString(Assembly, Error, Context); std::string errMsg; raw_string_ostream os(errMsg); @@ -59,6 +59,7 @@ protected: EXPECT_EQ(P.Ordered, R.Ordered); } + LLVMContext Context; std::unique_ptr<Module> M; Instruction *A, *B; }; diff --git a/unittests/AsmParser/AsmParserTest.cpp b/unittests/AsmParser/AsmParserTest.cpp index 4189310fda23..ddbedd05c7ba 100644 --- a/unittests/AsmParser/AsmParserTest.cpp +++ b/unittests/AsmParser/AsmParserTest.cpp @@ -21,7 +21,7 @@ using namespace llvm; namespace { TEST(AsmParserTest, NullTerminatedInput) { - LLVMContext &Ctx = getGlobalContext(); + LLVMContext Ctx; StringRef Source = "; Empty module \n"; SMDiagnostic Error; auto Mod = parseAssemblyString(Source, Error, Ctx); @@ -34,7 +34,7 @@ TEST(AsmParserTest, NullTerminatedInput) { #ifndef NDEBUG TEST(AsmParserTest, NonNullTerminatedInput) { - LLVMContext &Ctx = getGlobalContext(); + LLVMContext Ctx; StringRef Source = "; Empty module \n\1\2"; SMDiagnostic Error; std::unique_ptr<Module> Mod; @@ -47,7 +47,7 @@ TEST(AsmParserTest, NonNullTerminatedInput) { #endif TEST(AsmParserTest, SlotMappingTest) { - LLVMContext &Ctx = getGlobalContext(); + LLVMContext Ctx; StringRef Source = "@0 = global i32 0\n !0 = !{}\n !42 = !{i32 42}"; SMDiagnostic Error; SlotMapping Mapping; @@ -66,7 +66,7 @@ TEST(AsmParserTest, SlotMappingTest) { } TEST(AsmParserTest, TypeAndConstantValueParsing) { - LLVMContext &Ctx = getGlobalContext(); + LLVMContext Ctx; SMDiagnostic Error; StringRef Source = "define void @test() {\n entry:\n ret void\n}"; auto Mod = parseAssemblyString(Source, Error, Ctx); @@ -117,7 +117,7 @@ TEST(AsmParserTest, TypeAndConstantValueParsing) { } TEST(AsmParserTest, TypeAndConstantValueWithSlotMappingParsing) { - LLVMContext &Ctx = getGlobalContext(); + LLVMContext Ctx; SMDiagnostic Error; StringRef Source = "%st = type { i32, i32 }\n" @@ -152,4 +152,267 @@ TEST(AsmParserTest, TypeAndConstantValueWithSlotMappingParsing) { ASSERT_TRUE(isa<ConstantExpr>(V)); } +TEST(AsmParserTest, TypeWithSlotMappingParsing) { + LLVMContext Ctx; + SMDiagnostic Error; + StringRef Source = + "%st = type { i32, i32 }\n" + "@v = common global [50 x %st] zeroinitializer, align 16\n" + "%0 = type { i32, i32, i32, i32 }\n" + "@g = common global [50 x %0] zeroinitializer, align 16\n" + "define void @marker4(i64 %d) {\n" + "entry:\n" + " %conv = trunc i64 %d to i32\n" + " store i32 %conv, i32* getelementptr inbounds " + " ([50 x %st], [50 x %st]* @v, i64 0, i64 0, i32 0), align 16\n" + " store i32 %conv, i32* getelementptr inbounds " + " ([50 x %0], [50 x %0]* @g, i64 0, i64 0, i32 0), align 16\n" + " ret void\n" + "}"; + SlotMapping Mapping; + auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping); + ASSERT_TRUE(Mod != nullptr); + auto &M = *Mod; + + // Check we properly parse integer types. + Type *Ty; + Ty = parseType("i32", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + + // Check we properly parse integer types with exotic size. + Ty = parseType("i13", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13); + + // Check we properly parse floating point types. + Ty = parseType("float", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isFloatTy()); + + Ty = parseType("double", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isDoubleTy()); + + // Check we properly parse struct types. + // Named struct. + Ty = parseType("%st", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isStructTy()); + + // Check the details of the struct. + StructType *ST = cast<StructType>(Ty); + ASSERT_TRUE(ST->getNumElements() == 2); + for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { + Ty = ST->getElementType(i); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + } + + // Anonymous struct. + Ty = parseType("%0", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isStructTy()); + + // Check the details of the struct. + ST = cast<StructType>(Ty); + ASSERT_TRUE(ST->getNumElements() == 4); + for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { + Ty = ST->getElementType(i); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + } + + // Check we properly parse vector types. + Ty = parseType("<5 x i32>", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isVectorTy()); + + // Check the details of the vector. + VectorType *VT = cast<VectorType>(Ty); + ASSERT_TRUE(VT->getNumElements() == 5); + ASSERT_TRUE(VT->getBitWidth() == 160); + Ty = VT->getElementType(); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + + // Opaque struct. + Ty = parseType("%opaque", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isStructTy()); + + ST = cast<StructType>(Ty); + ASSERT_TRUE(ST->isOpaque()); + + // Check we properly parse pointer types. + // One indirection. + Ty = parseType("i32*", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isPointerTy()); + + PointerType *PT = cast<PointerType>(Ty); + Ty = PT->getElementType(); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + + // Two indirections. + Ty = parseType("i32**", Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isPointerTy()); + + PT = cast<PointerType>(Ty); + Ty = PT->getElementType(); + ASSERT_TRUE(Ty->isPointerTy()); + + PT = cast<PointerType>(Ty); + Ty = PT->getElementType(); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + + // Check that we reject types with garbage. + Ty = parseType("i32 garbage", Error, M, &Mapping); + ASSERT_TRUE(!Ty); +} + +TEST(AsmParserTest, TypeAtBeginningWithSlotMappingParsing) { + LLVMContext Ctx; + SMDiagnostic Error; + StringRef Source = + "%st = type { i32, i32 }\n" + "@v = common global [50 x %st] zeroinitializer, align 16\n" + "%0 = type { i32, i32, i32, i32 }\n" + "@g = common global [50 x %0] zeroinitializer, align 16\n" + "define void @marker4(i64 %d) {\n" + "entry:\n" + " %conv = trunc i64 %d to i32\n" + " store i32 %conv, i32* getelementptr inbounds " + " ([50 x %st], [50 x %st]* @v, i64 0, i64 0, i32 0), align 16\n" + " store i32 %conv, i32* getelementptr inbounds " + " ([50 x %0], [50 x %0]* @g, i64 0, i64 0, i32 0), align 16\n" + " ret void\n" + "}"; + SlotMapping Mapping; + auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping); + ASSERT_TRUE(Mod != nullptr); + auto &M = *Mod; + unsigned Read; + + // Check we properly parse integer types. + Type *Ty; + Ty = parseTypeAtBeginning("i32", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + ASSERT_TRUE(Read == 3); + + // Check we properly parse integer types with exotic size. + Ty = parseTypeAtBeginning("i13", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13); + ASSERT_TRUE(Read == 3); + + // Check we properly parse floating point types. + Ty = parseTypeAtBeginning("float", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isFloatTy()); + ASSERT_TRUE(Read == 5); + + Ty = parseTypeAtBeginning("double", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isDoubleTy()); + ASSERT_TRUE(Read == 6); + + // Check we properly parse struct types. + // Named struct. + Ty = parseTypeAtBeginning("%st", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isStructTy()); + ASSERT_TRUE(Read == 3); + + // Check the details of the struct. + StructType *ST = cast<StructType>(Ty); + ASSERT_TRUE(ST->getNumElements() == 2); + for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { + Ty = ST->getElementType(i); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + } + + // Anonymous struct. + Ty = parseTypeAtBeginning("%0", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isStructTy()); + ASSERT_TRUE(Read == 2); + + // Check the details of the struct. + ST = cast<StructType>(Ty); + ASSERT_TRUE(ST->getNumElements() == 4); + for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { + Ty = ST->getElementType(i); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + } + + // Check we properly parse vector types. + Ty = parseTypeAtBeginning("<5 x i32>", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isVectorTy()); + ASSERT_TRUE(Read == 9); + + // Check the details of the vector. + VectorType *VT = cast<VectorType>(Ty); + ASSERT_TRUE(VT->getNumElements() == 5); + ASSERT_TRUE(VT->getBitWidth() == 160); + Ty = VT->getElementType(); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + + // Opaque struct. + Ty = parseTypeAtBeginning("%opaque", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isStructTy()); + ASSERT_TRUE(Read == 7); + + ST = cast<StructType>(Ty); + ASSERT_TRUE(ST->isOpaque()); + + // Check we properly parse pointer types. + // One indirection. + Ty = parseTypeAtBeginning("i32*", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isPointerTy()); + ASSERT_TRUE(Read == 4); + + PointerType *PT = cast<PointerType>(Ty); + Ty = PT->getElementType(); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + + // Two indirections. + Ty = parseTypeAtBeginning("i32**", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isPointerTy()); + ASSERT_TRUE(Read == 5); + + PT = cast<PointerType>(Ty); + Ty = PT->getElementType(); + ASSERT_TRUE(Ty->isPointerTy()); + + PT = cast<PointerType>(Ty); + Ty = PT->getElementType(); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + + // Check that we reject types with garbage. + Ty = parseTypeAtBeginning("i32 garbage", Read, Error, M, &Mapping); + ASSERT_TRUE(Ty); + ASSERT_TRUE(Ty->isIntegerTy()); + ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); + // We go to the next token, i.e., we read "i32" + ' '. + ASSERT_TRUE(Read == 4); +} + } // end anonymous namespace diff --git a/unittests/AsmParser/Makefile b/unittests/AsmParser/Makefile deleted file mode 100644 index 41eadd430433..000000000000 --- a/unittests/AsmParser/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/AsmParser/Makefile ------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = AsmParser -LINK_COMPONENTS := AsmParser Core Support - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Bitcode/BitReaderTest.cpp b/unittests/Bitcode/BitReaderTest.cpp index 420aca2443bb..d0f33d12d5bc 100644 --- a/unittests/Bitcode/BitReaderTest.cpp +++ b/unittests/Bitcode/BitReaderTest.cpp @@ -29,10 +29,10 @@ using namespace llvm; namespace { -std::unique_ptr<Module> parseAssembly(const char *Assembly) { +std::unique_ptr<Module> parseAssembly(LLVMContext &Context, + const char *Assembly) { SMDiagnostic Error; - std::unique_ptr<Module> M = - parseAssemblyString(Assembly, Error, getGlobalContext()); + std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context); std::string ErrMsg; raw_string_ostream OS(ErrMsg); @@ -54,7 +54,7 @@ static void writeModuleToBuffer(std::unique_ptr<Module> Mod, static std::unique_ptr<Module> getLazyModuleFromAssembly(LLVMContext &Context, SmallString<1024> &Mem, const char *Assembly) { - writeModuleToBuffer(parseAssembly(Assembly), Mem); + writeModuleToBuffer(parseAssembly(Context, Assembly), Mem); std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Mem.str(), "test", false); ErrorOr<std::unique_ptr<Module>> ModuleOrErr = @@ -82,7 +82,7 @@ public: static std::unique_ptr<Module> getStreamedModuleFromAssembly(LLVMContext &Context, SmallString<1024> &Mem, const char *Assembly) { - writeModuleToBuffer(parseAssembly(Assembly), Mem); + writeModuleToBuffer(parseAssembly(Context, Assembly), Mem); std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Mem.str(), "test", false); auto Streamer = llvm::make_unique<BufferDataStreamer>(std::move(Buffer)); diff --git a/unittests/Bitcode/BitstreamReaderTest.cpp b/unittests/Bitcode/BitstreamReaderTest.cpp index b11d7fde7749..2be774cc5394 100644 --- a/unittests/Bitcode/BitstreamReaderTest.cpp +++ b/unittests/Bitcode/BitstreamReaderTest.cpp @@ -7,13 +7,31 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/StreamingMemoryObject.h" #include "gtest/gtest.h" using namespace llvm; namespace { +class BufferStreamer : public DataStreamer { + StringRef Buffer; + +public: + BufferStreamer(StringRef Buffer) : Buffer(Buffer) {} + size_t GetBytes(unsigned char *OutBuffer, size_t Length) override { + if (Length >= Buffer.size()) + Length = Buffer.size(); + + std::copy(Buffer.begin(), Buffer.begin() + Length, OutBuffer); + Buffer = Buffer.drop_front(Length); + return Length; + } +}; + TEST(BitstreamReaderTest, AtEndOfStream) { uint8_t Bytes[4] = { 0x00, 0x01, 0x02, 0x03 @@ -53,4 +71,171 @@ TEST(BitstreamReaderTest, AtEndOfStreamEmpty) { EXPECT_TRUE(Cursor.AtEndOfStream()); } +TEST(BitstreamReaderTest, getCurrentByteNo) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + for (unsigned I = 0, E = 33; I != E; ++I) { + EXPECT_EQ(I / 8, Cursor.getCurrentByteNo()); + (void)Cursor.Read(1); + } + EXPECT_EQ(4u, Cursor.getCurrentByteNo()); +} + +TEST(BitstreamReaderTest, getPointerToByte) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + for (unsigned I = 0, E = 8; I != E; ++I) { + EXPECT_EQ(Bytes + I, Cursor.getPointerToByte(I, 1)); + } +} + +TEST(BitstreamReaderTest, getPointerToBit) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + for (unsigned I = 0, E = 8; I != E; ++I) { + EXPECT_EQ(Bytes + I, Cursor.getPointerToBit(I * 8, 1)); + } +} + +TEST(BitstreamReaderTest, jumpToPointer) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + for (unsigned I : {0, 6, 2, 7}) { + Cursor.jumpToPointer(Bytes + I); + EXPECT_EQ(I, Cursor.getCurrentByteNo()); + } +} + +TEST(BitstreamReaderTest, setArtificialByteLimit) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + Cursor.setArtificialByteLimit(8); + EXPECT_EQ(8u, Cursor.getSizeIfKnown()); + while (!Cursor.AtEndOfStream()) + (void)Cursor.Read(1); + + EXPECT_EQ(8u, Cursor.getCurrentByteNo()); +} + +TEST(BitstreamReaderTest, setArtificialByteLimitNotWordBoundary) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + Cursor.setArtificialByteLimit(5); + EXPECT_EQ(8u, Cursor.getSizeIfKnown()); + while (!Cursor.AtEndOfStream()) + (void)Cursor.Read(1); + + EXPECT_EQ(8u, Cursor.getCurrentByteNo()); +} + +TEST(BitstreamReaderTest, setArtificialByteLimitPastTheEnd) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + // The size of the memory object isn't known yet. Set it too high and + // confirm that we don't read too far. + Cursor.setArtificialByteLimit(24); + EXPECT_EQ(24u, Cursor.getSizeIfKnown()); + while (!Cursor.AtEndOfStream()) + (void)Cursor.Read(1); + + EXPECT_EQ(12u, Cursor.getCurrentByteNo()); + EXPECT_EQ(12u, Cursor.getSizeIfKnown()); +} + +TEST(BitstreamReaderTest, setArtificialByteLimitPastTheEndKnown) { + uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b}; + BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); + SimpleBitstreamCursor Cursor(Reader); + + // Save the size of the memory object in the cursor. + while (!Cursor.AtEndOfStream()) + (void)Cursor.Read(1); + EXPECT_EQ(12u, Cursor.getCurrentByteNo()); + EXPECT_EQ(12u, Cursor.getSizeIfKnown()); + + Cursor.setArtificialByteLimit(20); + EXPECT_TRUE(Cursor.AtEndOfStream()); + EXPECT_EQ(12u, Cursor.getSizeIfKnown()); +} + +TEST(BitstreamReaderTest, readRecordWithBlobWhileStreaming) { + SmallVector<uint8_t, 1> BlobData; + for (unsigned I = 0, E = 1024; I != E; ++I) + BlobData.push_back(I); + + // Try a bunch of different sizes. + const unsigned Magic = 0x12345678; + const unsigned BlockID = bitc::FIRST_APPLICATION_BLOCKID; + const unsigned RecordID = 1; + for (unsigned I = 0, BlobSize = 0, E = BlobData.size(); BlobSize < E; + BlobSize += ++I) { + StringRef BlobIn((const char *)BlobData.begin(), BlobSize); + + // Write the bitcode. + SmallVector<char, 1> Buffer; + unsigned AbbrevID; + { + BitstreamWriter Stream(Buffer); + Stream.Emit(Magic, 32); + Stream.EnterSubblock(BlockID, 3); + + BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RecordID)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + AbbrevID = Stream.EmitAbbrev(Abbrev); + unsigned Record[] = {RecordID}; + Stream.EmitRecordWithBlob(AbbrevID, makeArrayRef(Record), BlobIn); + + Stream.ExitBlock(); + } + + // Stream the buffer into the reader. + BitstreamReader R(llvm::make_unique<StreamingMemoryObject>( + llvm::make_unique<BufferStreamer>( + StringRef(Buffer.begin(), Buffer.size())))); + BitstreamCursor Stream(R); + + // Header. Included in test so that we can run llvm-bcanalyzer to debug + // when there are problems. + ASSERT_EQ(Magic, Stream.Read(32)); + + // Block. + BitstreamEntry Entry = + Stream.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); + ASSERT_EQ(BitstreamEntry::SubBlock, Entry.Kind); + ASSERT_EQ(BlockID, Entry.ID); + ASSERT_FALSE(Stream.EnterSubBlock(BlockID)); + + // Abbreviation. + Entry = Stream.advance(); + ASSERT_EQ(BitstreamEntry::Record, Entry.Kind); + ASSERT_EQ(AbbrevID, Entry.ID); + + // Record. + StringRef BlobOut; + SmallVector<uint64_t, 1> Record; + ASSERT_EQ(RecordID, Stream.readRecord(Entry.ID, Record, &BlobOut)); + EXPECT_TRUE(Record.empty()); + EXPECT_EQ(BlobIn, BlobOut); + } +} + } // end anonymous namespace diff --git a/unittests/Bitcode/BitstreamWriterTest.cpp b/unittests/Bitcode/BitstreamWriterTest.cpp new file mode 100644 index 000000000000..f17cc157cde9 --- /dev/null +++ b/unittests/Bitcode/BitstreamWriterTest.cpp @@ -0,0 +1,59 @@ +//===- BitstreamWriterTest.cpp - Tests for BitstreamWriter ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(BitstreamWriterTest, emitBlob) { + SmallString<64> Buffer; + BitstreamWriter W(Buffer); + W.emitBlob("str", /* ShouldEmitSize */ false); + EXPECT_EQ(StringRef("str\0", 4), Buffer); +} + +TEST(BitstreamWriterTest, emitBlobWithSize) { + SmallString<64> Buffer; + { + BitstreamWriter W(Buffer); + W.emitBlob("str"); + } + SmallString<64> Expected; + { + BitstreamWriter W(Expected); + W.EmitVBR(3, 6); + W.FlushToWord(); + W.Emit('s', 8); + W.Emit('t', 8); + W.Emit('r', 8); + W.Emit(0, 8); + } + EXPECT_EQ(StringRef(Expected), Buffer); +} + +TEST(BitstreamWriterTest, emitBlobEmpty) { + SmallString<64> Buffer; + BitstreamWriter W(Buffer); + W.emitBlob("", /* ShouldEmitSize */ false); + EXPECT_EQ(StringRef(""), Buffer); +} + +TEST(BitstreamWriterTest, emitBlob4ByteAligned) { + SmallString<64> Buffer; + BitstreamWriter W(Buffer); + W.emitBlob("str0", /* ShouldEmitSize */ false); + EXPECT_EQ(StringRef("str0"), Buffer); +} + +} // end namespace diff --git a/unittests/Bitcode/CMakeLists.txt b/unittests/Bitcode/CMakeLists.txt index 09cbcdc7284d..4d06f8008d38 100644 --- a/unittests/Bitcode/CMakeLists.txt +++ b/unittests/Bitcode/CMakeLists.txt @@ -9,4 +9,5 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(BitcodeTests BitReaderTest.cpp BitstreamReaderTest.cpp + BitstreamWriterTest.cpp ) diff --git a/unittests/Bitcode/Makefile b/unittests/Bitcode/Makefile deleted file mode 100644 index 33b09b914b56..000000000000 --- a/unittests/Bitcode/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/Bitcode/Makefile --------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = Bitcode -LINK_COMPONENTS := AsmParser BitReader BitWriter Core Support - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index e5befcec6e0f..49a9b31b60d9 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -22,6 +22,8 @@ add_subdirectory(IR) add_subdirectory(LineEditor) add_subdirectory(Linker) add_subdirectory(MC) +add_subdirectory(MI) +add_subdirectory(ObjectYAML) add_subdirectory(Option) add_subdirectory(ProfileData) add_subdirectory(Support) diff --git a/unittests/CodeGen/DIEHashTest.cpp b/unittests/CodeGen/DIEHashTest.cpp index e3a9e5628276..dda08fcd6654 100644 --- a/unittests/CodeGen/DIEHashTest.cpp +++ b/unittests/CodeGen/DIEHashTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/DebugInfo/DWARFFormValueTest.cpp ---------------------===// +//===- llvm/unittest/CodeGen/DIEHashTest.cpp ------------------------------===// // // The LLVM Compiler Infrastructure // diff --git a/unittests/CodeGen/Makefile b/unittests/CodeGen/Makefile deleted file mode 100644 index 4f07017c2914..000000000000 --- a/unittests/CodeGen/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- unittests/DebugInfo/Makefile ------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = CodeGen -LINK_COMPONENTS := asmprinter codegen support - -include $(LEVEL)/Makefile.config - -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp index 371e2af95dec..eb9607ade16d 100644 --- a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/Host.h" diff --git a/unittests/DebugInfo/DWARF/Makefile b/unittests/DebugInfo/DWARF/Makefile deleted file mode 100644 index b0f40e1ca9d9..000000000000 --- a/unittests/DebugInfo/DWARF/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- unittests/DebugInfo/Makefile ------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -TESTNAME = DebugInfoDWARF -LINK_COMPONENTS := DebugInfoDWARF object support - -include $(LEVEL)/Makefile.config - -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/DebugInfo/Makefile b/unittests/DebugInfo/Makefile deleted file mode 100644 index 1889ad22db11..000000000000 --- a/unittests/DebugInfo/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/DebugInfo/Makefile ------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -LEVEL = ../.. - -include $(LEVEL)/Makefile.config - -PARALLEL_DIRS := DWARF PDB - -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/DebugInfo/PDB/CMakeLists.txt b/unittests/DebugInfo/PDB/CMakeLists.txt index 91924a5a8754..405b7bb9b1a6 100644 --- a/unittests/DebugInfo/PDB/CMakeLists.txt +++ b/unittests/DebugInfo/PDB/CMakeLists.txt @@ -1,8 +1,11 @@ set(LLVM_LINK_COMPONENTS + DebugInfoCodeView DebugInfoPDB ) set(DebugInfoPDBSources + MappedBlockStreamTest.cpp + MsfBuilderTest.cpp PDBApiTest.cpp ) diff --git a/unittests/DebugInfo/PDB/ErrorChecking.h b/unittests/DebugInfo/PDB/ErrorChecking.h new file mode 100644 index 000000000000..da734a9b1b5b --- /dev/null +++ b/unittests/DebugInfo/PDB/ErrorChecking.h @@ -0,0 +1,41 @@ +//===- ErrorChecking.h - Helpers for verifying llvm::Errors -----*- 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_DEBUGINFO_PDB_ERRORCHECKING_H +#define LLVM_UNITTESTS_DEBUGINFO_PDB_ERRORCHECKING_H + +#define EXPECT_NO_ERROR(Err) \ + { \ + auto E = Err; \ + EXPECT_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)); \ + } + +#define EXPECT_EXPECTED(Exp) \ + { \ + auto E = Exp.takeError(); \ + EXPECT_FALSE(static_cast<bool>(E)); \ + if (E) { \ + consumeError(std::move(E)); \ + return; \ + } \ + } + +#define EXPECT_UNEXPECTED(Exp) EXPECT_ERROR(Err) + +#endif diff --git a/unittests/DebugInfo/PDB/Makefile b/unittests/DebugInfo/PDB/Makefile deleted file mode 100644 index eb118a3fd2bd..000000000000 --- a/unittests/DebugInfo/PDB/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- unittests/DebugInfo/PDB/Makefile -------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -TESTNAME = DebugInfoPDB -LINK_COMPONENTS := DebugInfoPDB support - -include $(LEVEL)/Makefile.config - -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp new file mode 100644 index 000000000000..6f9e86c4f262 --- /dev/null +++ b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp @@ -0,0 +1,442 @@ +//===- llvm/unittest/DebugInfo/PDB/MappedBlockStreamTest.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/ByteStream.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamRef.h" +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/IPDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "gtest/gtest.h" + +#include <unordered_map> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +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 DiscontiguousFile : public IPDBFile { +public: + DiscontiguousFile(ArrayRef<uint32_t> Blocks, MutableArrayRef<uint8_t> Data) + : Blocks(Blocks.begin(), Blocks.end()), Data(Data.begin(), Data.end()) {} + + uint32_t getBlockSize() const override { return 1; } + uint32_t getBlockCount() const override { return Blocks.size(); } + uint32_t getNumStreams() const override { return 1; } + uint32_t getStreamByteSize(uint32_t StreamIndex) const override { + return getBlockCount() * getBlockSize(); + } + ArrayRef<support::ulittle32_t> + getStreamBlockList(uint32_t StreamIndex) const override { + if (StreamIndex != 0) + return ArrayRef<support::ulittle32_t>(); + return Blocks; + } + Expected<ArrayRef<uint8_t>> getBlockData(uint32_t BlockIndex, + uint32_t NumBytes) const override { + return ArrayRef<uint8_t>(&Data[BlockIndex], NumBytes); + } + + Error setBlockData(uint32_t BlockIndex, uint32_t Offset, + ArrayRef<uint8_t> SrcData) const override { + if (BlockIndex >= Blocks.size()) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + if (Offset > getBlockSize() - SrcData.size()) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + ::memcpy(&Data[BlockIndex] + Offset, SrcData.data(), SrcData.size()); + return Error::success(); + } + +private: + std::vector<support::ulittle32_t> Blocks; + MutableArrayRef<uint8_t> Data; +}; + +class MappedBlockStreamImpl : public MappedBlockStream { +public: + MappedBlockStreamImpl(std::unique_ptr<IPDBStreamData> Data, + const IPDBFile &File) + : MappedBlockStream(std::move(Data), File) {} +}; + +// Tests that a read which is entirely contained within a single block works +// and does not allocate. +TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StreamRef SR; + EXPECT_NO_ERROR(R.readStreamRef(SR, 0U)); + ArrayRef<uint8_t> Buffer; + EXPECT_ERROR(SR.readBytes(0U, 1U, Buffer)); + EXPECT_NO_ERROR(R.readStreamRef(SR, 1U)); + EXPECT_ERROR(SR.readBytes(1U, 1U, Buffer)); +} + +// Tests that a read which outputs into a full destination buffer works and +// does not fail due to the length of the output buffer. +TEST(MappedBlockStreamTest, ReadOntoNonEmptyBuffer) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA"; + EXPECT_NO_ERROR(R.readFixedString(Str, 1)); + EXPECT_EQ(Str, StringRef("A")); + EXPECT_EQ(0U, S.getNumBytesCopied()); +} + +// Tests that a read which crosses a block boundary, but where the subsequent +// blocks are still contiguous in memory to the previous block works and does +// not allocate memory. +TEST(MappedBlockStreamTest, ZeroCopyReadContiguousBreak) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str; + EXPECT_NO_ERROR(R.readFixedString(Str, 2)); + EXPECT_EQ(Str, StringRef("AB")); + EXPECT_EQ(0U, S.getNumBytesCopied()); + + R.setOffset(6); + EXPECT_NO_ERROR(R.readFixedString(Str, 4)); + EXPECT_EQ(Str, StringRef("GHIJ")); + EXPECT_EQ(0U, S.getNumBytesCopied()); +} + +// Tests that a read which crosses a block boundary and cannot be referenced +// contiguously works and allocates only the precise amount of bytes +// requested. +TEST(MappedBlockStreamTest, CopyReadNonContiguousBreak) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str; + EXPECT_NO_ERROR(R.readFixedString(Str, 10)); + EXPECT_EQ(Str, StringRef("ABCDEFGHIJ")); + EXPECT_EQ(10U, S.getNumBytesCopied()); +} + +// Test that an out of bounds read which doesn't cross a block boundary +// fails and allocates no memory. +TEST(MappedBlockStreamTest, InvalidReadSizeNoBreak) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str; + + R.setOffset(10); + EXPECT_ERROR(R.readFixedString(Str, 1)); + EXPECT_EQ(0U, S.getNumBytesCopied()); +} + +// Test that an out of bounds read which crosses a contiguous block boundary +// fails and allocates no memory. +TEST(MappedBlockStreamTest, InvalidReadSizeContiguousBreak) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str; + + R.setOffset(6); + EXPECT_ERROR(R.readFixedString(Str, 5)); + EXPECT_EQ(0U, S.getNumBytesCopied()); +} + +// Test that an out of bounds read which crosses a discontiguous block +// boundary fails and allocates no memory. +TEST(MappedBlockStreamTest, InvalidReadSizeNonContiguousBreak) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str; + + EXPECT_ERROR(R.readFixedString(Str, 11)); + EXPECT_EQ(0U, S.getNumBytesCopied()); +} + +// Tests that a read which is entirely contained within a single block but +// beyond the end of a StreamRef fails. +TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str; + EXPECT_NO_ERROR(R.readFixedString(Str, 1)); + EXPECT_EQ(Str, StringRef("A")); + EXPECT_EQ(0U, S.getNumBytesCopied()); +} + +// Tests that a read which is not aligned on the same boundary as a previous +// cached request, but which is known to overlap that request, shares the +// previous allocation. +TEST(MappedBlockStreamTest, UnalignedOverlappingRead) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str1; + StringRef Str2; + EXPECT_NO_ERROR(R.readFixedString(Str1, 7)); + EXPECT_EQ(Str1, StringRef("ABCDEFG")); + EXPECT_EQ(7U, S.getNumBytesCopied()); + + R.setOffset(2); + EXPECT_NO_ERROR(R.readFixedString(Str2, 3)); + EXPECT_EQ(Str2, StringRef("CDE")); + EXPECT_EQ(Str1.data() + 2, Str2.data()); + EXPECT_EQ(7U, S.getNumBytesCopied()); +} + +// Tests that a read which is not aligned on the same boundary as a previous +// cached request, but which only partially overlaps a previous cached request, +// still works correctly and allocates again from the shared pool. +TEST(MappedBlockStreamTest, UnalignedOverlappingReadFail) { + DiscontiguousFile F(BlocksAry, DataAry); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + StreamReader R(S); + StringRef Str1; + StringRef Str2; + EXPECT_NO_ERROR(R.readFixedString(Str1, 6)); + EXPECT_EQ(Str1, StringRef("ABCDEF")); + EXPECT_EQ(6U, S.getNumBytesCopied()); + + R.setOffset(4); + EXPECT_NO_ERROR(R.readFixedString(Str2, 4)); + EXPECT_EQ(Str2, StringRef("EFGH")); + EXPECT_EQ(10U, S.getNumBytesCopied()); +} + +TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) { + static uint8_t Data[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}; + static uint8_t LargeBuffer[] = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'A'}; + static uint8_t SmallBuffer[] = {'0', '1', '2'}; + static_assert(sizeof(LargeBuffer) > sizeof(Data), + "LargeBuffer is not big enough"); + + DiscontiguousFile F(BlocksAry, Data); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + ArrayRef<uint8_t> Buffer; + + EXPECT_ERROR(S.writeBytes(0, ArrayRef<uint8_t>(LargeBuffer))); + EXPECT_NO_ERROR(S.writeBytes(0, ArrayRef<uint8_t>(SmallBuffer))); + EXPECT_NO_ERROR(S.writeBytes(7, ArrayRef<uint8_t>(SmallBuffer))); + EXPECT_ERROR(S.writeBytes(8, ArrayRef<uint8_t>(SmallBuffer))); +} + +TEST(MappedBlockStreamTest, TestWriteBytesNoBreakBoundary) { + static uint8_t Data[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}; + DiscontiguousFile F(BlocksAry, Data); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + ArrayRef<uint8_t> Buffer; + + EXPECT_NO_ERROR(S.readBytes(0, 1, Buffer)); + EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A')); + EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer)); + EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J')); + + EXPECT_NO_ERROR(S.writeBytes(0, ArrayRef<uint8_t>('J'))); + EXPECT_NO_ERROR(S.writeBytes(9, ArrayRef<uint8_t>('A'))); + + EXPECT_NO_ERROR(S.readBytes(0, 1, Buffer)); + EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J')); + EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer)); + EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A')); + + EXPECT_NO_ERROR(S.writeBytes(0, ArrayRef<uint8_t>('A'))); + EXPECT_NO_ERROR(S.writeBytes(9, ArrayRef<uint8_t>('J'))); + + EXPECT_NO_ERROR(S.readBytes(0, 1, Buffer)); + EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A')); + EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer)); + EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J')); +} + +TEST(MappedBlockStreamTest, TestWriteBytesBreakBoundary) { + static uint8_t Data[] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; + static uint8_t TestData[] = {'T', 'E', 'S', 'T', 'I', 'N', 'G', '.'}; + static uint8_t Expected[] = {'T', 'E', 'S', 'N', 'I', + 'T', 'G', '.', '0', '0'}; + + DiscontiguousFile F(BlocksAry, Data); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + ArrayRef<uint8_t> Buffer; + + EXPECT_NO_ERROR(S.writeBytes(0, TestData)); + // First just compare the memory, then compare the result of reading the + // string out. + EXPECT_EQ(ArrayRef<uint8_t>(Data), ArrayRef<uint8_t>(Expected)); + + EXPECT_NO_ERROR(S.readBytes(0, 8, Buffer)); + EXPECT_EQ(Buffer, ArrayRef<uint8_t>(TestData)); +} + +TEST(MappedBlockStreamTest, TestWriteThenRead) { + std::vector<uint8_t> DataBytes(10); + MutableArrayRef<uint8_t> Data(DataBytes); + const uint32_t Blocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8}; + + DiscontiguousFile F(Blocks, Data); + MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + + enum class MyEnum : uint32_t { Val1 = 2908234, Val2 = 120891234 }; + using support::ulittle32_t; + + uint16_t u16[] = {31468, 0}; + uint32_t u32[] = {890723408, 0}; + MyEnum Enum[] = {MyEnum::Val1, MyEnum::Val2}; + StringRef ZStr[] = {"Zero Str", ""}; + StringRef FStr[] = {"Fixed Str", ""}; + uint8_t byteArray0[] = {'1', '2'}; + uint8_t byteArray1[] = {'0', '0'}; + ArrayRef<uint8_t> byteArrayRef0(byteArray0); + ArrayRef<uint8_t> byteArrayRef1(byteArray1); + ArrayRef<uint8_t> byteArray[] = { byteArrayRef0, byteArrayRef1 }; + ArrayRef<uint32_t> intArray[] = {{890723408, 29082234}, {0, 0}}; + + StreamReader Reader(S); + StreamWriter Writer(S); + EXPECT_NO_ERROR(Writer.writeInteger(u16[0])); + EXPECT_NO_ERROR(Reader.readInteger(u16[1])); + EXPECT_EQ(u16[0], u16[1]); + EXPECT_EQ(std::vector<uint8_t>({0, 0x7A, 0xEC, 0, 0, 0, 0, 0, 0, 0}), + DataBytes); + + Reader.setOffset(0); + Writer.setOffset(0); + ::memset(DataBytes.data(), 0, 10); + EXPECT_NO_ERROR(Writer.writeInteger(u32[0])); + EXPECT_NO_ERROR(Reader.readInteger(u32[1])); + EXPECT_EQ(u32[0], u32[1]); + EXPECT_EQ(std::vector<uint8_t>({0x17, 0x5C, 0x50, 0, 0, 0, 0x35, 0, 0, 0}), + DataBytes); + + Reader.setOffset(0); + Writer.setOffset(0); + ::memset(DataBytes.data(), 0, 10); + EXPECT_NO_ERROR(Writer.writeEnum(Enum[0])); + EXPECT_NO_ERROR(Reader.readEnum(Enum[1])); + EXPECT_EQ(Enum[0], Enum[1]); + EXPECT_EQ(std::vector<uint8_t>({0x2C, 0x60, 0x4A, 0, 0, 0, 0, 0, 0, 0}), + DataBytes); + + 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_EQ(ZStr[0], ZStr[1]); + EXPECT_EQ( + std::vector<uint8_t>({'r', 'e', 'Z', ' ', 'S', 't', 'o', 'r', 0, 0}), + DataBytes); + + Reader.setOffset(0); + Writer.setOffset(0); + ::memset(DataBytes.data(), 0, 10); + EXPECT_NO_ERROR(Writer.writeFixedString(FStr[0])); + EXPECT_NO_ERROR(Reader.readFixedString(FStr[1], FStr[0].size())); + EXPECT_EQ(FStr[0], FStr[1]); + EXPECT_EQ( + std::vector<uint8_t>({'x', 'i', 'F', 'd', ' ', 'S', 'e', 't', 0, 'r'}), + DataBytes); + + Reader.setOffset(0); + Writer.setOffset(0); + ::memset(DataBytes.data(), 0, 10); + EXPECT_NO_ERROR(Writer.writeArray(byteArray[0])); + EXPECT_NO_ERROR(Reader.readArray(byteArray[1], byteArray[0].size())); + EXPECT_EQ(byteArray[0], byteArray[1]); + EXPECT_EQ(std::vector<uint8_t>({0, 0x32, 0x31, 0, 0, 0, 0, 0, 0, 0}), + DataBytes); + + Reader.setOffset(0); + Writer.setOffset(0); + ::memset(DataBytes.data(), 0, 10); + EXPECT_NO_ERROR(Writer.writeArray(intArray[0])); + EXPECT_NO_ERROR(Reader.readArray(intArray[1], intArray[0].size())); + EXPECT_EQ(intArray[0], intArray[1]); +} + +TEST(MappedBlockStreamTest, TestWriteContiguousStreamRef) { + std::vector<uint8_t> DestDataBytes(10); + MutableArrayRef<uint8_t> DestData(DestDataBytes); + const uint32_t DestBlocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8}; + + std::vector<uint8_t> SrcDataBytes(10); + MutableArrayRef<uint8_t> SrcData(SrcDataBytes); + + DiscontiguousFile F(DestBlocks, DestData); + MappedBlockStreamImpl DestStream(llvm::make_unique<IndexedStreamData>(0, F), + F); + + // First write "Test Str" into the source stream. + ByteStream<true> SourceStream(SrcData); + StreamWriter SourceWriter(SourceStream); + EXPECT_NO_ERROR(SourceWriter.writeZeroString("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); + 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)); + EXPECT_EQ(Result, "Test Str"); +} + +TEST(MappedBlockStreamTest, TestWriteDiscontiguousStreamRef) { + std::vector<uint8_t> DestDataBytes(10); + MutableArrayRef<uint8_t> DestData(DestDataBytes); + const uint32_t DestBlocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8}; + + std::vector<uint8_t> SrcDataBytes(10); + MutableArrayRef<uint8_t> SrcData(SrcDataBytes); + const uint32_t SrcBlocks[] = {1, 0, 6, 3, 4, 5, 2, 7, 8, 9}; + + DiscontiguousFile DestFile(DestBlocks, DestData); + DiscontiguousFile SrcFile(SrcBlocks, SrcData); + + MappedBlockStreamImpl DestStream( + llvm::make_unique<IndexedStreamData>(0, DestFile), DestFile); + MappedBlockStreamImpl SrcStream( + llvm::make_unique<IndexedStreamData>(0, SrcFile), SrcFile); + + // First write "Test Str" into the source stream. + StreamWriter SourceWriter(SrcStream); + EXPECT_NO_ERROR(SourceWriter.writeZeroString("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(DestStream); + EXPECT_NO_ERROR(DestWriter.writeStreamRef(SrcStream)); + 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)); + EXPECT_EQ(Result, "Test Str"); +} + +} // end anonymous namespace diff --git a/unittests/DebugInfo/PDB/MsfBuilderTest.cpp b/unittests/DebugInfo/PDB/MsfBuilderTest.cpp new file mode 100644 index 000000000000..ac292a73a8fb --- /dev/null +++ b/unittests/DebugInfo/PDB/MsfBuilderTest.cpp @@ -0,0 +1,360 @@ +//===- MsfBuilderTest.cpp Tests manipulation of MSF stream metadata ------===// +// +// 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/Raw/MsfBuilder.h" +#include "llvm/DebugInfo/PDB/Raw/MsfCommon.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::pdb::msf; + +namespace { +class MsfBuilderTest : public testing::Test { +protected: + void initializeSimpleSuperBlock(msf::SuperBlock &SB) { + initializeSuperBlock(SB); + SB.NumBlocks = 1000; + SB.NumDirectoryBytes = 8192; + } + + void initializeSuperBlock(msf::SuperBlock &SB) { + ::memset(&SB, 0, sizeof(SB)); + + ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic)); + SB.BlockMapAddr = 1; + SB.BlockSize = 4096; + SB.NumDirectoryBytes = 0; + SB.NumBlocks = 2; // one for the Super Block, one for the directory + } + + BumpPtrAllocator Allocator; +}; +} + +TEST_F(MsfBuilderTest, ValidateSuperBlockAccept) { + // Test that a known good super block passes validation. + SuperBlock SB; + initializeSuperBlock(SB); + + EXPECT_NO_ERROR(msf::validateSuperBlock(SB)); +} + +TEST_F(MsfBuilderTest, ValidateSuperBlockReject) { + // Test that various known problems cause a super block to be rejected. + SuperBlock SB; + initializeSimpleSuperBlock(SB); + + // Mismatched magic + SB.MagicBytes[0] = 8; + EXPECT_ERROR(msf::validateSuperBlock(SB)); + initializeSimpleSuperBlock(SB); + + // Block 0 is reserved for super block, can't be occupied by the block map + SB.BlockMapAddr = 0; + EXPECT_ERROR(msf::validateSuperBlock(SB)); + initializeSimpleSuperBlock(SB); + + // Block sizes have to be powers of 2. + SB.BlockSize = 3120; + EXPECT_ERROR(msf::validateSuperBlock(SB)); + initializeSimpleSuperBlock(SB); + + // The directory itself has a maximum size. + SB.NumDirectoryBytes = SB.BlockSize * SB.BlockSize / 4; + EXPECT_NO_ERROR(msf::validateSuperBlock(SB)); + SB.NumDirectoryBytes = SB.NumDirectoryBytes + 4; + EXPECT_ERROR(msf::validateSuperBlock(SB)); +} + +TEST_F(MsfBuilderTest, TestUsedBlocksMarkedAsUsed) { + // Test that when assigning a stream to a known list of blocks, the blocks + // are correctly marked as used after adding, but no other incorrect blocks + // are accidentally marked as used. + + std::vector<uint32_t> Blocks = {4, 5, 6, 7, 8, 9, 10, 11, 12}; + // Allocate some extra blocks at the end so we can verify that they're free + // after the initialization. + uint32_t NumBlocks = msf::getMinimumBlockCount() + Blocks.size() + 10; + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096, NumBlocks); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.addStream(Blocks.size() * 4096, Blocks)); + + for (auto B : Blocks) { + EXPECT_FALSE(Msf.isBlockFree(B)); + } + + uint32_t FreeBlockStart = Blocks.back() + 1; + for (uint32_t I = FreeBlockStart; I < NumBlocks; ++I) { + EXPECT_TRUE(Msf.isBlockFree(I)); + } +} + +TEST_F(MsfBuilderTest, TestAddStreamNoDirectoryBlockIncrease) { + // Test that adding a new stream correctly updates the directory. This only + // tests the case where the directory *DOES NOT* grow large enough that it + // crosses a Block boundary. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + auto ExpectedL1 = Msf.build(); + EXPECT_EXPECTED(ExpectedL1); + Layout &L1 = *ExpectedL1; + + auto OldDirBlocks = L1.DirectoryBlocks; + EXPECT_EQ(1U, OldDirBlocks.size()); + + auto ExpectedMsf2 = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf2); + auto &Msf2 = *ExpectedMsf2; + + EXPECT_NO_ERROR(Msf2.addStream(4000)); + EXPECT_EQ(1U, Msf2.getNumStreams()); + EXPECT_EQ(4000U, Msf2.getStreamSize(0)); + auto Blocks = Msf2.getStreamBlocks(0); + EXPECT_EQ(1U, Blocks.size()); + + auto ExpectedL2 = Msf2.build(); + EXPECT_EXPECTED(ExpectedL2); + Layout &L2 = *ExpectedL2; + auto NewDirBlocks = L2.DirectoryBlocks; + EXPECT_EQ(1U, NewDirBlocks.size()); +} + +TEST_F(MsfBuilderTest, TestAddStreamWithDirectoryBlockIncrease) { + // Test that adding a new stream correctly updates the directory. This only + // tests the case where the directory *DOES* grow large enough that it + // crosses a Block boundary. This is because the newly added stream occupies + // so many Blocks that need to be indexed in the directory that the directory + // crosses a Block boundary. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.addStream(4096 * 4096 / sizeof(uint32_t))); + + auto ExpectedL1 = Msf.build(); + EXPECT_EXPECTED(ExpectedL1); + Layout &L1 = *ExpectedL1; + auto DirBlocks = L1.DirectoryBlocks; + EXPECT_EQ(2U, DirBlocks.size()); +} + +TEST_F(MsfBuilderTest, TestGrowStreamNoBlockIncrease) { + // Test growing an existing stream by a value that does not affect the number + // of blocks it occupies. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.addStream(1024)); + EXPECT_EQ(1024U, Msf.getStreamSize(0)); + auto OldStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(1U, OldStreamBlocks.size()); + + EXPECT_NO_ERROR(Msf.setStreamSize(0, 2048)); + EXPECT_EQ(2048U, Msf.getStreamSize(0)); + auto NewStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(1U, NewStreamBlocks.size()); + + EXPECT_EQ(OldStreamBlocks, NewStreamBlocks); +} + +TEST_F(MsfBuilderTest, TestGrowStreamWithBlockIncrease) { + // Test that growing an existing stream to a value large enough that it causes + // the need to allocate new Blocks to the stream correctly updates the + // stream's + // block list. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.addStream(2048)); + EXPECT_EQ(2048U, Msf.getStreamSize(0)); + std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(1U, OldStreamBlocks.size()); + + EXPECT_NO_ERROR(Msf.setStreamSize(0, 6144)); + EXPECT_EQ(6144U, Msf.getStreamSize(0)); + std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(2U, NewStreamBlocks.size()); + + EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]); + EXPECT_NE(NewStreamBlocks[0], NewStreamBlocks[1]); +} + +TEST_F(MsfBuilderTest, TestShrinkStreamNoBlockDecrease) { + // Test that shrinking an existing stream by a value that does not affect the + // number of Blocks it occupies makes no changes to stream's block list. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.addStream(2048)); + EXPECT_EQ(2048U, Msf.getStreamSize(0)); + std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(1U, OldStreamBlocks.size()); + + EXPECT_NO_ERROR(Msf.setStreamSize(0, 1024)); + EXPECT_EQ(1024U, Msf.getStreamSize(0)); + std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(1U, NewStreamBlocks.size()); + + EXPECT_EQ(OldStreamBlocks, NewStreamBlocks); +} + +TEST_F(MsfBuilderTest, TestShrinkStreamWithBlockDecrease) { + // Test that shrinking an existing stream to a value large enough that it + // causes the need to deallocate new Blocks to the stream correctly updates + // the stream's block list. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.addStream(6144)); + EXPECT_EQ(6144U, Msf.getStreamSize(0)); + std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(2U, OldStreamBlocks.size()); + + EXPECT_NO_ERROR(Msf.setStreamSize(0, 2048)); + EXPECT_EQ(2048U, Msf.getStreamSize(0)); + std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0); + EXPECT_EQ(1U, NewStreamBlocks.size()); + + EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]); +} + +TEST_F(MsfBuilderTest, TestRejectReusedStreamBlock) { + // Test that attempting to add a stream and assigning a block that is already + // in use by another stream fails. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.addStream(6144)); + + std::vector<uint32_t> Blocks = {2, 3}; + EXPECT_ERROR(Msf.addStream(6144, Blocks)); +} + +TEST_F(MsfBuilderTest, TestBlockCountsWhenAddingStreams) { + // Test that when adding multiple streams, the number of used and free Blocks + // allocated to the MSF file are as expected. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + // one for the super block, one for the directory block map + uint32_t NumUsedBlocks = Msf.getNumUsedBlocks(); + EXPECT_EQ(msf::getMinimumBlockCount(), NumUsedBlocks); + EXPECT_EQ(0U, Msf.getNumFreeBlocks()); + + const uint32_t StreamSizes[] = {4000, 6193, 189723}; + for (int I = 0; I < 3; ++I) { + EXPECT_NO_ERROR(Msf.addStream(StreamSizes[I])); + NumUsedBlocks += bytesToBlocks(StreamSizes[I], 4096); + EXPECT_EQ(NumUsedBlocks, Msf.getNumUsedBlocks()); + EXPECT_EQ(0U, Msf.getNumFreeBlocks()); + } +} + +TEST_F(MsfBuilderTest, BuildMsfLayout) { + // Test that we can generate an Msf Layout structure from a valid layout + // specification. + auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + const uint32_t StreamSizes[] = {4000, 6193, 189723}; + uint32_t ExpectedNumBlocks = msf::getMinimumBlockCount(); + for (int I = 0; I < 3; ++I) { + EXPECT_NO_ERROR(Msf.addStream(StreamSizes[I])); + ExpectedNumBlocks += bytesToBlocks(StreamSizes[I], 4096); + } + ++ExpectedNumBlocks; // The directory itself should use 1 block + + auto ExpectedLayout = Msf.build(); + EXPECT_EXPECTED(ExpectedLayout); + Layout &L = *ExpectedLayout; + EXPECT_EQ(4096U, L.SB->BlockSize); + EXPECT_EQ(ExpectedNumBlocks, L.SB->NumBlocks); + + EXPECT_EQ(1U, L.DirectoryBlocks.size()); + + EXPECT_EQ(3U, L.StreamMap.size()); + EXPECT_EQ(3U, L.StreamSizes.size()); + for (int I = 0; I < 3; ++I) { + EXPECT_EQ(StreamSizes[I], L.StreamSizes[I]); + uint32_t ExpectedNumBlocks = bytesToBlocks(StreamSizes[I], 4096); + EXPECT_EQ(ExpectedNumBlocks, L.StreamMap[I].size()); + } +} + +TEST_F(MsfBuilderTest, UseDirectoryBlockHint) { + Expected<MsfBuilder> ExpectedMsf = MsfBuilder::create( + Allocator, 4096, msf::getMinimumBlockCount() + 1, false); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + uint32_t B = msf::getFirstUnreservedBlock(); + EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({B + 1})); + EXPECT_NO_ERROR(Msf.addStream(2048, {B + 2})); + + auto ExpectedLayout = Msf.build(); + EXPECT_EXPECTED(ExpectedLayout); + Layout &L = *ExpectedLayout; + EXPECT_EQ(msf::getMinimumBlockCount() + 2, L.SB->NumBlocks); + EXPECT_EQ(1U, L.DirectoryBlocks.size()); + EXPECT_EQ(1U, L.StreamMap[0].size()); + + EXPECT_EQ(B + 1, L.DirectoryBlocks[0]); + EXPECT_EQ(B + 2, L.StreamMap[0].front()); +} + +TEST_F(MsfBuilderTest, DirectoryBlockHintInsufficient) { + Expected<MsfBuilder> ExpectedMsf = + MsfBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + uint32_t B = msf::getFirstUnreservedBlock(); + EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({B + 1})); + + uint32_t Size = 4096 * 4096 / 4; + EXPECT_NO_ERROR(Msf.addStream(Size)); + + auto ExpectedLayout = Msf.build(); + EXPECT_EXPECTED(ExpectedLayout); + Layout &L = *ExpectedLayout; + EXPECT_EQ(2U, L.DirectoryBlocks.size()); + EXPECT_EQ(B + 1, L.DirectoryBlocks[0]); +} + +TEST_F(MsfBuilderTest, DirectoryBlockHintOverestimated) { + Expected<MsfBuilder> ExpectedMsf = + MsfBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + uint32_t B = msf::getFirstUnreservedBlock(); + EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({B + 1, B + 2})); + + EXPECT_NO_ERROR(Msf.addStream(2048)); + + auto ExpectedLayout = Msf.build(); + EXPECT_EXPECTED(ExpectedLayout); + Layout &L = *ExpectedLayout; + EXPECT_EQ(1U, L.DirectoryBlocks.size()); + EXPECT_EQ(B + 1, L.DirectoryBlocks[0]); +} diff --git a/unittests/DebugInfo/PDB/PDBApiTest.cpp b/unittests/DebugInfo/PDB/PDBApiTest.cpp index ebd3d7bb6b3c..cd0f928a08ab 100644 --- a/unittests/DebugInfo/PDB/PDBApiTest.cpp +++ b/unittests/DebugInfo/PDB/PDBApiTest.cpp @@ -50,6 +50,7 @@ #include "llvm/DebugInfo/PDB/PDBTypes.h" #include "gtest/gtest.h" using namespace llvm; +using namespace llvm::pdb; namespace { @@ -78,9 +79,35 @@ class MockSession : public IPDBSession { return nullptr; } std::unique_ptr<IPDBEnumLineNumbers> + findLineNumbers(const PDBSymbolCompiland &Compiland, + const IPDBSourceFile &File) const override { + return nullptr; + } + std::unique_ptr<IPDBEnumLineNumbers> findLineNumbersByAddress(uint64_t Address, uint32_t Length) const override { return nullptr; } + std::unique_ptr<IPDBEnumSourceFiles> + findSourceFiles(const PDBSymbolCompiland *Compiland, llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override { + return nullptr; + } + std::unique_ptr<IPDBSourceFile> + findOneSourceFile(const PDBSymbolCompiland *Compiland, + llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override { + return nullptr; + } + std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>> + findCompilandsForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override { + return nullptr; + } + std::unique_ptr<PDBSymbolCompiland> + findOneCompilandForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override { + return nullptr; + } std::unique_ptr<IPDBEnumSourceFiles> getAllSourceFiles() const override { return nullptr; diff --git a/unittests/ExecutionEngine/ExecutionEngineTest.cpp b/unittests/ExecutionEngine/ExecutionEngineTest.cpp index bb47c4c00304..7cad84130692 100644 --- a/unittests/ExecutionEngine/ExecutionEngineTest.cpp +++ b/unittests/ExecutionEngine/ExecutionEngineTest.cpp @@ -28,7 +28,7 @@ private: protected: ExecutionEngineTest() { - auto Owner = make_unique<Module>("<main>", getGlobalContext()); + auto Owner = make_unique<Module>("<main>", Context); M = Owner.get(); Engine.reset(EngineBuilder(std::move(Owner)).setErrorStr(&Error).create()); } @@ -44,13 +44,13 @@ protected: } std::string Error; + LLVMContext Context; Module *M; // Owned by ExecutionEngine. std::unique_ptr<ExecutionEngine> Engine; }; TEST_F(ExecutionEngineTest, ForwardGlobalMapping) { - GlobalVariable *G1 = - NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1"); + GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); int32_t Mem1 = 3; Engine->addGlobalMapping(G1, &Mem1); EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G1)); @@ -63,8 +63,7 @@ TEST_F(ExecutionEngineTest, ForwardGlobalMapping) { Engine->updateGlobalMapping(G1, &Mem2); EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1)); - GlobalVariable *G2 = - NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1"); + GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G2)) << "The NULL return shouldn't depend on having called" << " updateGlobalMapping(..., NULL)"; @@ -76,8 +75,7 @@ TEST_F(ExecutionEngineTest, ForwardGlobalMapping) { } TEST_F(ExecutionEngineTest, ReverseGlobalMapping) { - GlobalVariable *G1 = - NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1"); + GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); int32_t Mem1 = 3; Engine->addGlobalMapping(G1, &Mem1); @@ -87,8 +85,7 @@ TEST_F(ExecutionEngineTest, ReverseGlobalMapping) { EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1)); EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2)); - GlobalVariable *G2 = - NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global2"); + GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2"); Engine->updateGlobalMapping(G2, &Mem1); EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1)); EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2)); @@ -104,8 +101,7 @@ TEST_F(ExecutionEngineTest, ReverseGlobalMapping) { } TEST_F(ExecutionEngineTest, ClearModuleMappings) { - GlobalVariable *G1 = - NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1"); + GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); int32_t Mem1 = 3; Engine->addGlobalMapping(G1, &Mem1); @@ -115,8 +111,7 @@ TEST_F(ExecutionEngineTest, ClearModuleMappings) { EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1)); - GlobalVariable *G2 = - NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global2"); + GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2"); // After clearing the module mappings, we can assign a new GV to the // same address. Engine->addGlobalMapping(G2, &Mem1); @@ -124,8 +119,7 @@ TEST_F(ExecutionEngineTest, ClearModuleMappings) { } TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) { - GlobalVariable *G1 = - NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1"); + GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); int32_t Mem1 = 3; Engine->addGlobalMapping(G1, &Mem1); // Make sure the reverse mapping is enabled. @@ -136,35 +130,23 @@ TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) { EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1)); } -TEST_F(ExecutionEngineTest, LookupWithMangledName) { - int x; - llvm::sys::DynamicLibrary::AddSymbol("x", &x); - - // Demonstrate that getSymbolAddress accepts mangled names and always strips - // the leading underscore. - EXPECT_EQ(reinterpret_cast<uint64_t>(&x), - RTDyldMemoryManager::getSymbolAddressInProcess("_x")); -} - TEST_F(ExecutionEngineTest, LookupWithMangledAndDemangledSymbol) { int x; int _x; llvm::sys::DynamicLibrary::AddSymbol("x", &x); llvm::sys::DynamicLibrary::AddSymbol("_x", &_x); - // Lookup the demangled name first, even if there's a demangled symbol that - // matches the input already. + // RTDyldMemoryManager::getSymbolAddressInProcess expects a mangled symbol, + // but DynamicLibrary is a wrapper for dlsym, which expects the unmangled C + // symbol name. This test verifies that getSymbolAddressInProcess strips the + // leading '_' on Darwin, but not on other platforms. +#ifdef __APPLE__ EXPECT_EQ(reinterpret_cast<uint64_t>(&x), RTDyldMemoryManager::getSymbolAddressInProcess("_x")); -} - -TEST_F(ExecutionEngineTest, LookupwithDemangledName) { - int _x; - llvm::sys::DynamicLibrary::AddSymbol("_x", &_x); - - // But do fallback to looking up a demangled name if there's no ambiguity +#else EXPECT_EQ(reinterpret_cast<uint64_t>(&_x), RTDyldMemoryManager::getSymbolAddressInProcess("_x")); +#endif } } diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp index c8c244d22ed1..cf63da3a22a5 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp @@ -285,7 +285,6 @@ protected: void buildAndRunPasses() { LLVMPassManagerRef pass = LLVMCreatePassManager(); - LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), pass); LLVMAddConstantPropagationPass(pass); LLVMAddInstructionCombiningPass(pass); LLVMRunPassManager(pass, Module); @@ -304,8 +303,6 @@ protected: LLVMPassManagerRef modulePasses = LLVMCreatePassManager(); - LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), modulePasses); - LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder, functionPasses); LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses); diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h index 609ac844c47d..e14201c2d783 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h +++ b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h @@ -282,7 +282,6 @@ protected: MCJITTestBase() : TrivialModuleBuilder(HostTriple) , OptLevel(CodeGenOpt::None) - , RelocModel(Reloc::Default) , CodeModel(CodeModel::Default) , MArch("") , MM(new SectionMemoryManager) @@ -322,7 +321,6 @@ protected: .setErrorStr(&Error) .setOptLevel(CodeGenOpt::None) .setCodeModel(CodeModel::JITDefault) - .setRelocationModel(Reloc::Default) .setMArch(MArch) .setMCPU(sys::getHostCPUName()) //.setMAttrs(MAttrs) @@ -332,7 +330,6 @@ protected: } CodeGenOpt::Level OptLevel; - Reloc::Model RelocModel; CodeModel::Model CodeModel; StringRef MArch; SmallVector<std::string, 1> MAttrs; diff --git a/unittests/ExecutionEngine/MCJIT/Makefile b/unittests/ExecutionEngine/MCJIT/Makefile deleted file mode 100644 index 2822b20cdda2..000000000000 --- a/unittests/ExecutionEngine/MCJIT/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -##===- unittests/ExecutionEngine/MCJIT/Makefile ------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -TESTNAME = MCJIT -LINK_COMPONENTS := core ipo mcjit native support - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest - -# Permit these tests to use the MCJIT's symbolic lookup. -LD.Flags += $(RDYNAMIC) diff --git a/unittests/ExecutionEngine/Makefile b/unittests/ExecutionEngine/Makefile deleted file mode 100644 index c19f8d688d1b..000000000000 --- a/unittests/ExecutionEngine/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -##===- unittests/ExecutionEngine/Makefile ------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = ExecutionEngine -LINK_COMPONENTS :=interpreter - -include $(LEVEL)/Makefile.config - -PARALLEL_DIRS = Orc - -ifeq ($(TARGET_HAS_JIT),1) - PARALLEL_DIRS += MCJIT -endif - -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/ExecutionEngine/Orc/CMakeLists.txt b/unittests/ExecutionEngine/Orc/CMakeLists.txt index 41fef24556b1..68f6d0c28d7c 100644 --- a/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -20,3 +20,5 @@ add_llvm_unittest(OrcJITTests OrcTestCommon.cpp RPCUtilsTest.cpp ) + +target_link_libraries(OrcJITTests ${PTHREAD_LIB}) diff --git a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp index a27e649b616f..8140a1ff2493 100644 --- a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp @@ -18,19 +18,20 @@ namespace { class DummyCallbackManager : public orc::JITCompileCallbackManager { public: - DummyCallbackManager() : JITCompileCallbackManager(0) { } + DummyCallbackManager() : JITCompileCallbackManager(0) {} + public: void grow() override { llvm_unreachable("not implemented"); } }; class DummyStubsManager : public orc::IndirectStubsManager { public: - std::error_code createStub(StringRef StubName, TargetAddress InitAddr, - JITSymbolFlags Flags) override { + Error createStub(StringRef StubName, TargetAddress InitAddr, + JITSymbolFlags Flags) override { llvm_unreachable("Not implemented"); } - std::error_code createStubs(const StubInitsMap &StubInits) override { + Error createStubs(const StubInitsMap &StubInits) override { llvm_unreachable("Not implemented"); } @@ -42,22 +43,20 @@ public: llvm_unreachable("Not implemented"); } - std::error_code updatePointer(StringRef Name, - TargetAddress NewAddr) override { + Error updatePointer(StringRef Name, TargetAddress NewAddr) override { llvm_unreachable("Not implemented"); } }; TEST(CompileOnDemandLayerTest, FindSymbol) { - auto MockBaseLayer = - createMockBaseLayer<int>(DoNothingAndReturn<int>(0), - DoNothingAndReturn<void>(), - [](const std::string &Name, bool) { - if (Name == "foo") - return JITSymbol(1, JITSymbolFlags::Exported); - return JITSymbol(nullptr); - }, - DoNothingAndReturn<JITSymbol>(nullptr)); + auto MockBaseLayer = createMockBaseLayer<int>( + DoNothingAndReturn<int>(0), DoNothingAndReturn<void>(), + [](const std::string &Name, bool) { + if (Name == "foo") + return JITSymbol(1, JITSymbolFlags::Exported); + return JITSymbol(nullptr); + }, + DoNothingAndReturn<JITSymbol>(nullptr)); typedef decltype(MockBaseLayer) MockBaseLayerT; DummyCallbackManager CallbackMgr; @@ -68,8 +67,7 @@ TEST(CompileOnDemandLayerTest, FindSymbol) { auto Sym = COD.findSymbol("foo", true); - EXPECT_TRUE(!!Sym) - << "CompileOnDemand::findSymbol should call findSymbol in the base layer."; + EXPECT_TRUE(!!Sym) << "CompileOnDemand::findSymbol should call findSymbol in " + "the base layer."; } - } diff --git a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp index 38b60ea7fcd4..ac847039d9fb 100644 --- a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp @@ -17,7 +17,8 @@ using namespace llvm; namespace { TEST(IndirectionUtilsTest, MakeStub) { - ModuleBuilder MB(getGlobalContext(), "x86_64-apple-macosx10.10", ""); + LLVMContext Context; + ModuleBuilder MB(Context, "x86_64-apple-macosx10.10", ""); Function *F = MB.createFunctionDecl<void(DummyStruct, DummyStruct)>(""); SmallVector<AttributeSet, 4> Attrs; Attrs.push_back( diff --git a/unittests/ExecutionEngine/Orc/Makefile b/unittests/ExecutionEngine/Orc/Makefile deleted file mode 100644 index c899728e5078..000000000000 --- a/unittests/ExecutionEngine/Orc/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- unittests/ExecutionEngine/Orc/Makefile --------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -TESTNAME = OrcJIT -LINK_COMPONENTS := core ipo mcjit orcjit native support - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest - diff --git a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp index 59ee01f36010..87928347d88e 100644 --- a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp @@ -12,6 +12,7 @@ #include "llvm/ExecutionEngine/SectionMemoryManager.h" #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/IR/Constants.h" #include "llvm/IR/LLVMContext.h" @@ -24,19 +25,26 @@ namespace { class ObjectLinkingLayerExecutionTest : public testing::Test, public OrcExecutionTest { + }; class SectionMemoryManagerWrapper : public SectionMemoryManager { public: int FinalizationCount = 0; - bool finalizeMemory(std::string *ErrMsg = 0) override { + int NeedsToReserveAllocationSpaceCount = 0; + + bool needsToReserveAllocationSpace() override { + ++NeedsToReserveAllocationSpaceCount; + return SectionMemoryManager::needsToReserveAllocationSpace(); + } + + bool finalizeMemory(std::string *ErrMsg = nullptr) override { ++FinalizationCount; return SectionMemoryManager::finalizeMemory(ErrMsg); } }; TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { - class SectionMemoryManagerWrapper : public SectionMemoryManager { public: SectionMemoryManagerWrapper(bool &DebugSeen) : DebugSeen(DebugSeen) {} @@ -57,9 +65,10 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { ObjectLinkingLayer<> ObjLayer; - auto M = llvm::make_unique<Module>("", getGlobalContext()); + LLVMContext Context; + auto M = llvm::make_unique<Module>("", Context); M->setTargetTriple("x86_64-unknown-linux-gnu"); - Type *Int32Ty = IntegerType::get(getGlobalContext(), 32); + Type *Int32Ty = IntegerType::get(Context, 32); GlobalVariable *GV = new GlobalVariable(*M, Int32Ty, false, GlobalValue::ExternalLinkage, ConstantInt::get(Int32Ty, 42), "foo"); @@ -105,9 +114,7 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { } } - TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { - if (!TM) return; @@ -120,15 +127,19 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { // Module 2: // int bar(); // int foo() { return bar(); } + // + // Verify that the memory manager is only finalized once (for Module 2). + // Failure suggests that finalize is being called on the inner RTDyld + // instance (for Module 1) which is unsafe, as it will prevent relocation of + // Module 2. - ModuleBuilder MB1(getGlobalContext(), "", "dummy"); + ModuleBuilder MB1(Context, "", "dummy"); { MB1.getModule()->setDataLayout(TM->createDataLayout()); Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("bar"); - BasicBlock *BarEntry = BasicBlock::Create(getGlobalContext(), "entry", - BarImpl); + BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl); IRBuilder<> Builder(BarEntry); - IntegerType *Int32Ty = IntegerType::get(getGlobalContext(), 32); + IntegerType *Int32Ty = IntegerType::get(Context, 32); Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42); Builder.CreateRet(FourtyTwo); } @@ -137,13 +148,12 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { std::vector<object::ObjectFile*> Obj1Set; Obj1Set.push_back(Obj1.getBinary()); - ModuleBuilder MB2(getGlobalContext(), "", "dummy"); + ModuleBuilder MB2(Context, "", "dummy"); { MB2.getModule()->setDataLayout(TM->createDataLayout()); Function *BarDecl = MB2.createFunctionDecl<int32_t(void)>("bar"); Function *FooImpl = MB2.createFunctionDecl<int32_t(void)>("foo"); - BasicBlock *FooEntry = BasicBlock::Create(getGlobalContext(), "entry", - FooImpl); + BasicBlock *FooEntry = BasicBlock::Create(Context, "entry", FooImpl); IRBuilder<> Builder(FooEntry); Builder.CreateRet(Builder.CreateCall(BarDecl)); } @@ -155,7 +165,7 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { createLambdaResolver( [&](const std::string &Name) { if (auto Sym = ObjLayer.findSymbol(Name, true)) - return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags()); + return Sym.toRuntimeDyldSymbol(); return RuntimeDyld::SymbolInfo(nullptr); }, [](const std::string &Name) { @@ -173,4 +183,65 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { << "Extra call to finalize"; } +TEST_F(ObjectLinkingLayerExecutionTest, NoPrematureAllocation) { + if (!TM) + return; + + ObjectLinkingLayer<> ObjLayer; + SimpleCompiler Compile(*TM); + + // Create a pair of unrelated modules: + // + // Module 1: + // int foo() { return 42; } + // Module 2: + // int bar() { return 7; } + // + // Both modules will share a memory manager. We want to verify that the + // second object is not loaded before the first one is finalized. To do this + // in a portable way, we abuse the + // RuntimeDyld::MemoryManager::needsToReserveAllocationSpace hook, which is + // called once per object before any sections are allocated. + + ModuleBuilder MB1(Context, "", "dummy"); + { + MB1.getModule()->setDataLayout(TM->createDataLayout()); + Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("foo"); + BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl); + IRBuilder<> Builder(BarEntry); + IntegerType *Int32Ty = IntegerType::get(Context, 32); + Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42); + Builder.CreateRet(FourtyTwo); + } + + auto Obj1 = Compile(*MB1.getModule()); + std::vector<object::ObjectFile*> Obj1Set; + Obj1Set.push_back(Obj1.getBinary()); + + ModuleBuilder MB2(Context, "", "dummy"); + { + MB2.getModule()->setDataLayout(TM->createDataLayout()); + Function *BarImpl = MB2.createFunctionDecl<int32_t(void)>("bar"); + BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl); + IRBuilder<> Builder(BarEntry); + IntegerType *Int32Ty = IntegerType::get(Context, 32); + Value *Seven = ConstantInt::getSigned(Int32Ty, 7); + Builder.CreateRet(Seven); + } + auto Obj2 = Compile(*MB2.getModule()); + std::vector<object::ObjectFile*> Obj2Set; + Obj2Set.push_back(Obj2.getBinary()); + + SectionMemoryManagerWrapper SMMW; + NullResolver NR; + auto H = ObjLayer.addObjectSet(std::move(Obj1Set), &SMMW, &NR); + ObjLayer.addObjectSet(std::move(Obj2Set), &SMMW, &NR); + ObjLayer.emitAndFinalize(H); + + // Only one call to needsToReserveAllocationSpace should have been made. + EXPECT_EQ(SMMW.NeedsToReserveAllocationSpaceCount, 1) + << "More than one call to needsToReserveAllocationSpace " + "(multiple unrelated objects loaded prior to finalization)"; } + +} // end anonymous namespace diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp index c88c94f17b1c..e1b1f2f92781 100644 --- a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp @@ -7,9 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#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/ObjectTransformLayer.h" +#include "llvm/Object/ObjectFile.h" #include "gtest/gtest.h" using namespace llvm::orc; @@ -51,13 +56,14 @@ public: template <typename ObjSetT, typename MemoryManagerPtrT, typename SymbolResolverPtrT> - ObjSetHandleT addObjectSet(ObjSetT &Objects, MemoryManagerPtrT MemMgr, + ObjSetHandleT addObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr, SymbolResolverPtrT Resolver) { EXPECT_EQ(MockManager, *MemMgr) << "MM should pass through"; EXPECT_EQ(MockResolver, *Resolver) << "Resolver should pass through"; size_t I = 0; for (auto &ObjPtr : Objects) { - EXPECT_EQ(MockObjects[I++] + 1, *ObjPtr) << "Transform should be applied"; + EXPECT_EQ(MockObjects[I] + 1, *ObjPtr) << "Transform should be applied"; + I++; } EXPECT_EQ(MockObjects.size(), I) << "Number of objects should match"; LastCalled = "addObjectSet"; @@ -216,13 +222,14 @@ TEST(ObjectTransformLayerTest, Main) { auto MM = llvm::make_unique<MockMemoryManager>(MockManager); auto SR = llvm::make_unique<MockSymbolResolver>(MockResolver); M.expectAddObjectSet(Objs1, MM.get(), SR.get()); - auto H = T1.addObjectSet(Objs1, std::move(MM), std::move(SR)); + auto H = T1.addObjectSet(std::move(Objs1), std::move(MM), std::move(SR)); M.verifyAddObjectSet(H); // Test addObjectSet with T2 (mutating, naked pointers) - llvm::SmallVector<MockObjectFile *, 2> Objs2; - Objs2.push_back(&MockObject1); - Objs2.push_back(&MockObject2); + llvm::SmallVector<MockObjectFile *, 2> Objs2Vec; + Objs2Vec.push_back(&MockObject1); + Objs2Vec.push_back(&MockObject2); + llvm::MutableArrayRef<MockObjectFile *> Objs2(Objs2Vec); M.expectAddObjectSet(Objs2, &MockManager, &MockResolver); H = T2.addObjectSet(Objs2, &MockManager, &MockResolver); M.verifyAddObjectSet(H); @@ -271,5 +278,61 @@ TEST(ObjectTransformLayerTest, Main) { const auto &T1C = T1; OwnedObj = T1C.getTransform()(std::move(OwnedObj)); EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform"; + + volatile bool RunStaticChecks = false; + if (!RunStaticChecks) + return; + + // Make sure that ObjectTransformLayer implements the object layer concept + // correctly by sandwitching one between an ObjectLinkingLayer and an + // IRCompileLayer, verifying that it compiles if we have a call to the + // IRComileLayer's addModuleSet that should call the transform layer's + // addObjectSet, and also calling the other public transform layer methods + // directly to make sure the methods they intend to forward to exist on + // the ObjectLinkingLayer. + + // We'll need a concrete MemoryManager class. + class NullManager : public llvm::RuntimeDyld::MemoryManager { + public: + uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned, + llvm::StringRef) override { + return nullptr; + } + uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef, + bool) override { + return nullptr; + } + void registerEHFrames(uint8_t *, uint64_t, size_t) override {} + void deregisterEHFrames(uint8_t *, uint64_t, size_t) override {} + bool finalizeMemory(std::string *) override { return false; } + }; + + // Construct the jit layers. + ObjectLinkingLayer<> BaseLayer; + auto IdentityTransform = []( + std::unique_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>> + Obj) { return Obj; }; + ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)> + TransformLayer(BaseLayer, IdentityTransform); + auto NullCompiler = [](llvm::Module &) { + return llvm::object::OwningBinary<llvm::object::ObjectFile>(); + }; + IRCompileLayer<decltype(TransformLayer)> CompileLayer(TransformLayer, + NullCompiler); + + // Make sure that the calls from IRCompileLayer to ObjectTransformLayer + // compile. + NullResolver Resolver; + NullManager Manager; + CompileLayer.addModuleSet(std::vector<llvm::Module *>(), &Manager, &Resolver); + + // Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer + // compile. + decltype(TransformLayer)::ObjSetHandleT ObjSet; + TransformLayer.emitAndFinalize(ObjSet); + TransformLayer.findSymbolIn(ObjSet, Name, false); + TransformLayer.findSymbol(Name, true); + TransformLayer.mapSectionAddress(ObjSet, nullptr, 0); + TransformLayer.removeObjectSet(ObjSet); } } diff --git a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp index 776d26970a31..305325b6c6ef 100644 --- a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp +++ b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp @@ -25,11 +25,11 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) class OrcCAPIExecutionTest : public testing::Test, public OrcExecutionTest { protected: std::unique_ptr<Module> createTestModule(const Triple &TT) { - ModuleBuilder MB(getGlobalContext(), TT.str(), ""); + ModuleBuilder MB(Context, TT.str(), ""); Function *TestFunc = MB.createFunctionDecl<int()>("testFunc"); Function *Main = MB.createFunctionDecl<int(int, char*[])>("main"); - Main->getBasicBlockList().push_back(BasicBlock::Create(getGlobalContext())); + Main->getBasicBlockList().push_back(BasicBlock::Create(Context)); IRBuilder<> B(&Main->back()); Value* Result = B.CreateCall(TestFunc); B.CreateRet(Result); diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/unittests/ExecutionEngine/Orc/OrcTestCommon.h index f480e0789ae5..fe3da88dc9d1 100644 --- a/unittests/ExecutionEngine/Orc/OrcTestCommon.h +++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.h @@ -46,12 +46,15 @@ public: if (TM) { // If we found a TargetMachine, check that it's one that Orc supports. const Triple& TT = TM->getTargetTriple(); - if (TT.getArch() != Triple::x86_64 || !TT.isOSDarwin()) + + if ((TT.getArch() != Triple::x86_64 && TT.getArch() != Triple::x86) || + TT.isOSWindows()) TM = nullptr; } }; protected: + LLVMContext Context; std::unique_ptr<TargetMachine> TM; private: static bool NativeTargetInitialized; diff --git a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp index 8215144a514d..7d55641e4ce2 100644 --- a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp @@ -17,131 +17,192 @@ using namespace llvm; using namespace llvm::orc; using namespace llvm::orc::remote; -class QueueChannel : public RPCChannel { +class Queue : public std::queue<char> { public: - QueueChannel(std::queue<char> &Queue) : Queue(Queue) {} + std::mutex &getLock() { return Lock; } + +private: + std::mutex Lock; +}; - std::error_code readBytes(char *Dst, unsigned Size) override { - while (Size--) { - *Dst++ = Queue.front(); - Queue.pop(); +class QueueChannel : public RPCChannel { +public: + QueueChannel(Queue &InQueue, Queue &OutQueue) + : InQueue(InQueue), OutQueue(OutQueue) {} + + Error readBytes(char *Dst, unsigned Size) override { + while (Size != 0) { + // If there's nothing to read then yield. + while (InQueue.empty()) + std::this_thread::yield(); + + // Lock the channel and read what we can. + std::lock_guard<std::mutex> Lock(InQueue.getLock()); + while (!InQueue.empty() && Size) { + *Dst++ = InQueue.front(); + --Size; + InQueue.pop(); + } } - return std::error_code(); + return Error::success(); } - std::error_code appendBytes(const char *Src, unsigned Size) override { + Error appendBytes(const char *Src, unsigned Size) override { + std::lock_guard<std::mutex> Lock(OutQueue.getLock()); while (Size--) - Queue.push(*Src++); - return std::error_code(); + OutQueue.push(*Src++); + return Error::success(); } - std::error_code send() override { return std::error_code(); } + Error send() override { return Error::success(); } private: - std::queue<char> &Queue; + Queue &InQueue; + Queue &OutQueue; }; -class DummyRPC : public testing::Test, - public RPC<QueueChannel> { +class DummyRPC : public testing::Test, public RPC<QueueChannel> { public: - typedef Procedure<1, bool> Proc1; - typedef Procedure<2, int8_t, - uint8_t, - int16_t, - uint16_t, - int32_t, - uint32_t, - int64_t, - uint64_t, - bool, - std::string, - std::vector<int>> AllTheTypes; + enum FuncId : uint32_t { + VoidBoolId = RPCFunctionIdTraits<FuncId>::FirstValidId, + IntIntId, + AllTheTypesId + }; + + typedef Function<VoidBoolId, void(bool)> VoidBool; + typedef Function<IntIntId, int32_t(int32_t)> IntInt; + typedef Function<AllTheTypesId, + void(int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, + int64_t, uint64_t, bool, std::string, std::vector<int>)> + AllTheTypes; }; +TEST_F(DummyRPC, TestAsyncVoidBool) { + Queue Q1, Q2; + QueueChannel C1(Q1, Q2); + QueueChannel C2(Q2, Q1); + + // Make an async call. + auto ResOrErr = callAsyncWithSeq<VoidBool>(C1, true); + EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed"; -TEST_F(DummyRPC, TestBasic) { - std::queue<char> Queue; - QueueChannel C(Queue); + { + // Expect a call to Proc1. + auto EC = expect<VoidBool>(C2, [&](bool &B) { + EXPECT_EQ(B, true) << "Bool serialization broken"; + return Error::success(); + }); + EXPECT_FALSE(EC) << "Simple expect over queue failed"; + } { - // Make a call to Proc1. - auto EC = call<Proc1>(C, true); - EXPECT_FALSE(EC) << "Simple call over queue failed"; + // Wait for the result. + auto EC = waitForResult(C1, ResOrErr->second, handleNone); + EXPECT_FALSE(EC) << "Could not read result."; } + // Verify that the function returned ok. + auto Val = ResOrErr->first.get(); + EXPECT_TRUE(Val) << "Remote void function failed to execute."; +} + +TEST_F(DummyRPC, TestAsyncIntInt) { + Queue Q1, Q2; + QueueChannel C1(Q1, Q2); + QueueChannel C2(Q2, Q1); + + // Make an async call. + auto ResOrErr = callAsyncWithSeq<IntInt>(C1, 21); + EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed"; + { // Expect a call to Proc1. - auto EC = expect<Proc1>(C, - [&](bool &B) { - EXPECT_EQ(B, true) - << "Bool serialization broken"; - return std::error_code(); - }); + auto EC = expect<IntInt>(C2, [&](int32_t I) -> Expected<int32_t> { + EXPECT_EQ(I, 21) << "Bool serialization broken"; + return 2 * I; + }); EXPECT_FALSE(EC) << "Simple expect over queue failed"; } + + { + // Wait for the result. + auto EC = waitForResult(C1, ResOrErr->second, handleNone); + EXPECT_FALSE(EC) << "Could not read result."; + } + + // Verify that the function returned ok. + auto Val = ResOrErr->first.get(); + EXPECT_TRUE(!!Val) << "Remote int function failed to execute."; + EXPECT_EQ(*Val, 42) << "Remote int function return wrong value."; } TEST_F(DummyRPC, TestSerialization) { - std::queue<char> Queue; - QueueChannel C(Queue); + Queue Q1, Q2; + QueueChannel C1(Q1, Q2); + QueueChannel C2(Q2, Q1); + + // Make a call to Proc1. + std::vector<int> v({42, 7}); + auto ResOrErr = callAsyncWithSeq<AllTheTypes>( + C1, -101, 250, -10000, 10000, -1000000000, 1000000000, -10000000000, + 10000000000, true, "foo", v); + EXPECT_TRUE(!!ResOrErr) << "Big (serialization test) call over queue failed"; { - // Make a call to Proc1. - std::vector<int> v({42, 7}); - auto EC = call<AllTheTypes>(C, - -101, - 250, - -10000, - 10000, - -1000000000, - 1000000000, - -10000000000, - 10000000000, - true, - "foo", - v); + // Expect a call to Proc1. + auto EC = expect<AllTheTypes>( + C2, [&](int8_t &s8, uint8_t &u8, int16_t &s16, uint16_t &u16, + int32_t &s32, uint32_t &u32, int64_t &s64, uint64_t &u64, + bool &b, std::string &s, std::vector<int> &v) { + + EXPECT_EQ(s8, -101) << "int8_t serialization broken"; + EXPECT_EQ(u8, 250) << "uint8_t serialization broken"; + EXPECT_EQ(s16, -10000) << "int16_t serialization broken"; + EXPECT_EQ(u16, 10000) << "uint16_t serialization broken"; + EXPECT_EQ(s32, -1000000000) << "int32_t serialization broken"; + EXPECT_EQ(u32, 1000000000ULL) << "uint32_t serialization broken"; + EXPECT_EQ(s64, -10000000000) << "int64_t serialization broken"; + EXPECT_EQ(u64, 10000000000ULL) << "uint64_t serialization broken"; + EXPECT_EQ(b, true) << "bool serialization broken"; + EXPECT_EQ(s, "foo") << "std::string serialization broken"; + EXPECT_EQ(v, std::vector<int>({42, 7})) + << "std::vector serialization broken"; + return Error::success(); + }); EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed"; } { - // Expect a call to Proc1. - auto EC = expect<AllTheTypes>(C, - [&](int8_t &s8, - uint8_t &u8, - int16_t &s16, - uint16_t &u16, - int32_t &s32, - uint32_t &u32, - int64_t &s64, - uint64_t &u64, - bool &b, - std::string &s, - std::vector<int> &v) { - - EXPECT_EQ(s8, -101) - << "int8_t serialization broken"; - EXPECT_EQ(u8, 250) - << "uint8_t serialization broken"; - EXPECT_EQ(s16, -10000) - << "int16_t serialization broken"; - EXPECT_EQ(u16, 10000) - << "uint16_t serialization broken"; - EXPECT_EQ(s32, -1000000000) - << "int32_t serialization broken"; - EXPECT_EQ(u32, 1000000000ULL) - << "uint32_t serialization broken"; - EXPECT_EQ(s64, -10000000000) - << "int64_t serialization broken"; - EXPECT_EQ(u64, 10000000000ULL) - << "uint64_t serialization broken"; - EXPECT_EQ(b, true) - << "bool serialization broken"; - EXPECT_EQ(s, "foo") - << "std::string serialization broken"; - EXPECT_EQ(v, std::vector<int>({42, 7})) - << "std::vector serialization broken"; - return std::error_code(); - }); - EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed"; + // Wait for the result. + auto EC = waitForResult(C1, ResOrErr->second, handleNone); + EXPECT_FALSE(EC) << "Could not read result."; } + + // Verify that the function returned ok. + auto Val = ResOrErr->first.get(); + EXPECT_TRUE(Val) << "Remote void function failed to execute."; } + +// Test the synchronous call API. +// FIXME: Re-enable once deadlock encountered on S390 has been debugged / fixed, +// see http://lab.llvm.org:8011/builders/clang-s390x-linux/builds/3459 +// TEST_F(DummyRPC, TestSynchronousCall) { +// Queue Q1, Q2; +// QueueChannel C1(Q1, Q2); +// QueueChannel C2(Q2, Q1); +// +// auto ServerResult = +// std::async(std::launch::async, +// [&]() { +// return expect<IntInt>(C2, [&](int32_t V) { return V; }); +// }); +// +// auto ValOrErr = callST<IntInt>(C1, 42); +// +// EXPECT_FALSE(!!ServerResult.get()) +// << "Server returned an error."; +// EXPECT_TRUE(!!ValOrErr) +// << "callST returned an error."; +// EXPECT_EQ(*ValOrErr, 42) +// << "Incorrect callST<IntInt> result"; +// } diff --git a/unittests/IR/AttributesTest.cpp b/unittests/IR/AttributesTest.cpp index ebcb772bc373..9f8013ff181c 100644 --- a/unittests/IR/AttributesTest.cpp +++ b/unittests/IR/AttributesTest.cpp @@ -34,6 +34,15 @@ TEST(Attributes, Uniquing) { TEST(Attributes, Ordering) { LLVMContext C; + Attribute Align4 = Attribute::get(C, Attribute::Alignment, 4); + Attribute Align5 = Attribute::get(C, Attribute::Alignment, 5); + Attribute Deref4 = Attribute::get(C, Attribute::Dereferenceable, 4); + Attribute Deref5 = Attribute::get(C, Attribute::Dereferenceable, 5); + EXPECT_TRUE(Align4 < Align5); + EXPECT_TRUE(Align4 < Deref4); + EXPECT_TRUE(Align4 < Deref5); + EXPECT_TRUE(Align5 < Deref4); + AttributeSet ASs[] = { AttributeSet::get(C, 2, Attribute::ZExt), AttributeSet::get(C, 1, Attribute::SExt) diff --git a/unittests/IR/CMakeLists.txt b/unittests/IR/CMakeLists.txt index 5aad8edc9134..2baa4370c70e 100644 --- a/unittests/IR/CMakeLists.txt +++ b/unittests/IR/CMakeLists.txt @@ -11,9 +11,12 @@ set(IRSources ConstantRangeTest.cpp ConstantsTest.cpp DebugInfoTest.cpp + DebugTypeODRUniquingTest.cpp DominatorTreeTest.cpp + FunctionTest.cpp IRBuilderTest.cpp InstructionsTest.cpp + IntrinsicsTest.cpp LegacyPassManagerTest.cpp MDBuilderTest.cpp MetadataTest.cpp diff --git a/unittests/IR/ConstantRangeTest.cpp b/unittests/IR/ConstantRangeTest.cpp index 1f32eea3e43b..f7a8a82043b9 100644 --- a/unittests/IR/ConstantRangeTest.cpp +++ b/unittests/IR/ConstantRangeTest.cpp @@ -450,6 +450,45 @@ TEST_F(ConstantRangeTest, SMax) { EXPECT_EQ(One.smax(One), One); } +TEST_F(ConstantRangeTest, UMin) { + EXPECT_EQ(Full.umin(Full), Full); + EXPECT_EQ(Full.umin(Empty), Empty); + EXPECT_EQ(Full.umin(Some), ConstantRange(APInt(16, 0), APInt(16, 0xaaa))); + EXPECT_EQ(Full.umin(Wrap), Full); + EXPECT_EQ(Empty.umin(Empty), Empty); + EXPECT_EQ(Empty.umin(Some), Empty); + EXPECT_EQ(Empty.umin(Wrap), Empty); + EXPECT_EQ(Empty.umin(One), Empty); + EXPECT_EQ(Some.umin(Some), Some); + EXPECT_EQ(Some.umin(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa))); + EXPECT_EQ(Some.umin(One), One); + // TODO: ConstantRange is currently over-conservative here. + EXPECT_EQ(Wrap.umin(Wrap), Full); + EXPECT_EQ(Wrap.umin(One), ConstantRange(APInt(16, 0), APInt(16, 0xb))); + EXPECT_EQ(One.umin(One), One); +} + +TEST_F(ConstantRangeTest, SMin) { + EXPECT_EQ(Full.smin(Full), Full); + EXPECT_EQ(Full.smin(Empty), Empty); + EXPECT_EQ(Full.smin(Some), ConstantRange(APInt(16, (uint64_t)INT16_MIN), + APInt(16, 0xaaa))); + EXPECT_EQ(Full.smin(Wrap), Full); + EXPECT_EQ(Empty.smin(Empty), Empty); + EXPECT_EQ(Empty.smin(Some), Empty); + EXPECT_EQ(Empty.smin(Wrap), Empty); + EXPECT_EQ(Empty.smin(One), Empty); + EXPECT_EQ(Some.smin(Some), Some); + EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN), + APInt(16, 0xaaa))); + EXPECT_EQ(Some.smin(One), One); + // TODO: ConstantRange is currently over-conservative here. + EXPECT_EQ(Wrap.smin(Wrap), Full); + EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN), + APInt(16, 0xb))); + EXPECT_EQ(One.smin(One), One); +} + TEST_F(ConstantRangeTest, UDiv) { EXPECT_EQ(Full.udiv(Full), Full); EXPECT_EQ(Full.udiv(Empty), Empty); @@ -569,7 +608,7 @@ TEST(ConstantRange, MakeSatisfyingICmpRegion) { ConstantRange(APInt(8, 4), APInt(8, -128))); } -TEST(ConstantRange, MakeOverflowingRegion) { +TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { const int IntMin4Bits = 8; const int IntMax4Bits = 7; typedef OverflowingBinaryOperator OBO; @@ -577,17 +616,17 @@ TEST(ConstantRange, MakeOverflowingRegion) { for (int Const : {0, -1, -2, 1, 2, IntMin4Bits, IntMax4Bits}) { APInt C(4, Const, true /* = isSigned */); - auto NUWRegion = - ConstantRange::makeNoWrapRegion(Instruction::Add, C, OBO::NoUnsignedWrap); + auto NUWRegion = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, C, OBO::NoUnsignedWrap); EXPECT_FALSE(NUWRegion.isEmptySet()); - auto NSWRegion = - ConstantRange::makeNoWrapRegion(Instruction::Add, C, OBO::NoSignedWrap); + auto NSWRegion = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, C, OBO::NoSignedWrap); EXPECT_FALSE(NSWRegion.isEmptySet()); - auto NoWrapRegion = ConstantRange::makeNoWrapRegion( + auto NoWrapRegion = ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Add, C, OBO::NoSignedWrap | OBO::NoUnsignedWrap); EXPECT_FALSE(NoWrapRegion.isEmptySet()); @@ -618,6 +657,109 @@ TEST(ConstantRange, MakeOverflowingRegion) { EXPECT_FALSE(Overflow); } } + + auto NSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, ConstantRange(32, /* isFullSet = */ true), + OBO::NoSignedWrap); + EXPECT_TRUE(NSWForAllValues.isSingleElement() && + NSWForAllValues.getSingleElement()->isMinValue()); + + auto NUWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, ConstantRange(32, /* isFullSet = */ true), + OBO::NoUnsignedWrap); + EXPECT_TRUE(NUWForAllValues.isSingleElement() && + NSWForAllValues.getSingleElement()->isMinValue()); + + auto NUWAndNSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, ConstantRange(32, /* isFullSet = */ true), + OBO::NoUnsignedWrap | OBO::NoSignedWrap); + EXPECT_TRUE(NUWAndNSWForAllValues.isSingleElement() && + NSWForAllValues.getSingleElement()->isMinValue()); + + ConstantRange OneToFive(APInt(32, 1), APInt(32, 6)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, OneToFive, OBO::NoSignedWrap), + ConstantRange(APInt::getSignedMinValue(32), + APInt::getSignedMaxValue(32) - 4)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, OneToFive, OBO::NoUnsignedWrap), + ConstantRange(APInt::getMinValue(32), APInt::getMinValue(32) - 5)); + EXPECT_EQ( + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, OneToFive, OBO::NoUnsignedWrap | OBO::NoSignedWrap), + ConstantRange(APInt::getMinValue(32), APInt::getSignedMaxValue(32) - 4)); + + ConstantRange MinusFiveToMinusTwo(APInt(32, -5), APInt(32, -1)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, MinusFiveToMinusTwo, OBO::NoSignedWrap), + ConstantRange(APInt::getSignedMinValue(32) + 5, + APInt::getSignedMinValue(32))); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, MinusFiveToMinusTwo, OBO::NoUnsignedWrap), + ConstantRange(APInt(32, 0), APInt(32, 2))); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, MinusFiveToMinusTwo, + OBO::NoUnsignedWrap | OBO::NoSignedWrap), + ConstantRange(APInt(32, 0), APInt(32, 2))); + + ConstantRange MinusOneToOne(APInt(32, -1), APInt(32, 2)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, MinusOneToOne, OBO::NoSignedWrap), + ConstantRange(APInt::getSignedMinValue(32) + 1, + APInt::getSignedMinValue(32) - 1)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, MinusOneToOne, OBO::NoUnsignedWrap), + ConstantRange(APInt(32, 0), APInt(32, 1))); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Add, MinusOneToOne, + OBO::NoUnsignedWrap | OBO::NoSignedWrap), + ConstantRange(APInt(32, 0), APInt(32, 1))); +} + +TEST(ConstantRange, GetEquivalentICmp) { + APInt RHS; + CmpInst::Predicate Pred; + + EXPECT_TRUE(ConstantRange(APInt::getMinValue(32), APInt(32, 100)) + .getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_ULT); + EXPECT_EQ(RHS, APInt(32, 100)); + + EXPECT_TRUE(ConstantRange(APInt::getSignedMinValue(32), APInt(32, 100)) + .getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_SLT); + EXPECT_EQ(RHS, APInt(32, 100)); + + EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getMinValue(32)) + .getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_UGE); + EXPECT_EQ(RHS, APInt(32, 100)); + + EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getSignedMinValue(32)) + .getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_SGE); + EXPECT_EQ(RHS, APInt(32, 100)); + + EXPECT_TRUE( + ConstantRange(32, /*isFullSet=*/true).getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_UGE); + EXPECT_EQ(RHS, APInt(32, 0)); + + EXPECT_TRUE( + ConstantRange(32, /*isFullSet=*/false).getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_ULT); + EXPECT_EQ(RHS, APInt(32, 0)); + + EXPECT_FALSE(ConstantRange(APInt(32, 100), APInt(32, 200)) + .getEquivalentICmp(Pred, RHS)); + + EXPECT_FALSE(ConstantRange(APInt::getSignedMinValue(32) - APInt(32, 100), + APInt::getSignedMinValue(32) + APInt(32, 100)) + .getEquivalentICmp(Pred, RHS)); + + EXPECT_FALSE(ConstantRange(APInt::getMinValue(32) - APInt(32, 100), + APInt::getMinValue(32) + APInt(32, 100)) + .getEquivalentICmp(Pred, RHS)); } } // anonymous namespace diff --git a/unittests/IR/ConstantsTest.cpp b/unittests/IR/ConstantsTest.cpp index 7471584097dd..6959ac85e49d 100644 --- a/unittests/IR/ConstantsTest.cpp +++ b/unittests/IR/ConstantsTest.cpp @@ -22,7 +22,8 @@ namespace llvm { namespace { TEST(ConstantsTest, Integer_i1) { - IntegerType* Int1 = IntegerType::get(getGlobalContext(), 1); + LLVMContext Context; + IntegerType *Int1 = IntegerType::get(Context, 1); Constant* One = ConstantInt::get(Int1, 1, true); Constant* Zero = ConstantInt::get(Int1, 0); Constant* NegOne = ConstantInt::get(Int1, static_cast<uint64_t>(-1), true); @@ -103,7 +104,8 @@ TEST(ConstantsTest, Integer_i1) { } TEST(ConstantsTest, IntSigns) { - IntegerType* Int8Ty = Type::getInt8Ty(getGlobalContext()); + LLVMContext Context; + IntegerType *Int8Ty = Type::getInt8Ty(Context); EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, false)->getSExtValue()); EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, true)->getSExtValue()); EXPECT_EQ(100, ConstantInt::getSigned(Int8Ty, 100)->getSExtValue()); @@ -116,16 +118,17 @@ TEST(ConstantsTest, IntSigns) { } TEST(ConstantsTest, FP128Test) { - Type *FP128Ty = Type::getFP128Ty(getGlobalContext()); + LLVMContext Context; + Type *FP128Ty = Type::getFP128Ty(Context); - IntegerType *Int128Ty = Type::getIntNTy(getGlobalContext(), 128); + IntegerType *Int128Ty = Type::getIntNTy(Context, 128); Constant *Zero128 = Constant::getNullValue(Int128Ty); Constant *X = ConstantExpr::getUIToFP(Zero128, FP128Ty); EXPECT_TRUE(isa<ConstantFP>(X)); } TEST(ConstantsTest, PointerCast) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Type *Int8PtrTy = Type::getInt8PtrTy(C); Type *Int32PtrTy = Type::getInt32PtrTy(C); Type *Int64Ty = Type::getInt64Ty(C); @@ -152,6 +155,27 @@ TEST(ConstantsTest, PointerCast) { EXPECT_EQ(Constant::getNullValue(Int32PtrVecTy), ConstantExpr::getPointerCast( Constant::getNullValue(Int8PtrVecTy), Int32PtrVecTy)); + + Type *Int32Ptr1Ty = Type::getInt32PtrTy(C, 1); + ConstantInt *K = ConstantInt::get(Type::getInt64Ty(C), 1234); + + // Make sure that addrspacecast of inttoptr is not folded away. + EXPECT_NE(K, + ConstantExpr::getAddrSpaceCast( + ConstantExpr::getIntToPtr(K, Int32PtrTy), Int32Ptr1Ty)); + EXPECT_NE(K, + ConstantExpr::getAddrSpaceCast( + ConstantExpr::getIntToPtr(K, Int32Ptr1Ty), Int32PtrTy)); + + Constant *NullInt32Ptr0 = Constant::getNullValue(Int32PtrTy); + Constant *NullInt32Ptr1 = Constant::getNullValue(Int32Ptr1Ty); + + // Make sure that addrspacecast of null is not folded away. + EXPECT_NE(Constant::getNullValue(Int32PtrTy), + ConstantExpr::getAddrSpaceCast(NullInt32Ptr0, Int32Ptr1Ty)); + + EXPECT_NE(Constant::getNullValue(Int32Ptr1Ty), + ConstantExpr::getAddrSpaceCast(NullInt32Ptr1, Int32PtrTy)); } #define CHECK(x, y) { \ @@ -165,14 +189,15 @@ TEST(ConstantsTest, PointerCast) { } TEST(ConstantsTest, AsInstructionsTest) { - std::unique_ptr<Module> M(new Module("MyModule", getGlobalContext())); + LLVMContext Context; + std::unique_ptr<Module> M(new Module("MyModule", Context)); - Type *Int64Ty = Type::getInt64Ty(getGlobalContext()); - Type *Int32Ty = Type::getInt32Ty(getGlobalContext()); - Type *Int16Ty = Type::getInt16Ty(getGlobalContext()); - Type *Int1Ty = Type::getInt1Ty(getGlobalContext()); - Type *FloatTy = Type::getFloatTy(getGlobalContext()); - Type *DoubleTy = Type::getDoubleTy(getGlobalContext()); + Type *Int64Ty = Type::getInt64Ty(Context); + Type *Int32Ty = Type::getInt32Ty(Context); + Type *Int16Ty = Type::getInt16Ty(Context); + Type *Int1Ty = Type::getInt1Ty(Context); + Type *FloatTy = Type::getFloatTy(Context); + Type *DoubleTy = Type::getDoubleTy(Context); Constant *Global = M->getOrInsertGlobal("dummy", PointerType::getUnqual(Int32Ty)); @@ -189,8 +214,7 @@ TEST(ConstantsTest, AsInstructionsTest) { Constant *One = ConstantInt::get(Int32Ty, 1); Constant *Two = ConstantInt::get(Int64Ty, 2); - Constant *Big = ConstantInt::get(getGlobalContext(), - APInt{256, uint64_t(-1), true}); + Constant *Big = ConstantInt::get(Context, APInt{256, uint64_t(-1), true}); Constant *Elt = ConstantInt::get(Int16Ty, 2015); Constant *Undef16 = UndefValue::get(Int16Ty); Constant *Undef64 = UndefValue::get(Int64Ty); @@ -278,9 +302,10 @@ TEST(ConstantsTest, AsInstructionsTest) { #ifdef GTEST_HAS_DEATH_TEST #ifndef NDEBUG TEST(ConstantsTest, ReplaceWithConstantTest) { - std::unique_ptr<Module> M(new Module("MyModule", getGlobalContext())); + LLVMContext Context; + std::unique_ptr<Module> M(new Module("MyModule", Context)); - Type *Int32Ty = Type::getInt32Ty(getGlobalContext()); + Type *Int32Ty = Type::getInt32Ty(Context); Constant *One = ConstantInt::get(Int32Ty, 1); Constant *Global = diff --git a/unittests/IR/DebugTypeODRUniquingTest.cpp b/unittests/IR/DebugTypeODRUniquingTest.cpp new file mode 100644 index 000000000000..2c899d85d1f3 --- /dev/null +++ b/unittests/IR/DebugTypeODRUniquingTest.cpp @@ -0,0 +1,156 @@ +//===- DebugTypeODRUniquingTest.cpp - Debug type ODR uniquing tests -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "gtest/gtest.h" +using namespace llvm; + +namespace { + +TEST(DebugTypeODRUniquingTest, enableDebugTypeODRUniquing) { + LLVMContext Context; + EXPECT_FALSE(Context.isODRUniquingDebugTypes()); + Context.enableDebugTypeODRUniquing(); + EXPECT_TRUE(Context.isODRUniquingDebugTypes()); + Context.disableDebugTypeODRUniquing(); + EXPECT_FALSE(Context.isODRUniquingDebugTypes()); +} + +TEST(DebugTypeODRUniquingTest, getODRType) { + LLVMContext Context; + MDString &UUID = *MDString::get(Context, "string"); + + // Without a type map, this should return null. + EXPECT_FALSE(DICompositeType::getODRType( + Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr, + nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr)); + + // Enable the mapping. There still shouldn't be a type. + Context.enableDebugTypeODRUniquing(); + EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID)); + + // Create some ODR-uniqued type. + auto &CT = *DICompositeType::getODRType( + Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr, + nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr); + EXPECT_EQ(UUID.getString(), CT.getIdentifier()); + + // Check that we get it back, even if we change a field. + EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID)); + EXPECT_EQ( + &CT, DICompositeType::getODRType(Context, UUID, dwarf::DW_TAG_class_type, + nullptr, nullptr, 0, nullptr, nullptr, 0, + 0, 0, 0, nullptr, 0, nullptr, nullptr)); + EXPECT_EQ(&CT, DICompositeType::getODRType( + Context, UUID, dwarf::DW_TAG_class_type, + MDString::get(Context, "name"), nullptr, 0, nullptr, + nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr)); + + // Check that it's discarded with the type map. + Context.disableDebugTypeODRUniquing(); + EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID)); + + // And it shouldn't magically reappear... + Context.enableDebugTypeODRUniquing(); + EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID)); +} + +TEST(DebugTypeODRUniquingTest, buildODRType) { + LLVMContext Context; + Context.enableDebugTypeODRUniquing(); + + // Build an ODR type that's a forward decl. + MDString &UUID = *MDString::get(Context, "Type"); + auto &CT = *DICompositeType::buildODRType( + Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr, + nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr); + EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID)); + EXPECT_EQ(dwarf::DW_TAG_class_type, CT.getTag()); + + // Update with another forward decl. This should be a no-op. + EXPECT_EQ(&CT, DICompositeType::buildODRType( + Context, UUID, dwarf::DW_TAG_structure_type, nullptr, nullptr, 0, nullptr, + nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr)); + EXPECT_EQ(dwarf::DW_TAG_class_type, CT.getTag()); + + // Update with a definition. This time we should see a change. + EXPECT_EQ(&CT, DICompositeType::buildODRType( + Context, UUID, dwarf::DW_TAG_structure_type, nullptr, nullptr, 0, nullptr, + nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr)); + EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag()); + + // Further updates should be ignored. + EXPECT_EQ(&CT, DICompositeType::buildODRType( + Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr, + nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr)); + EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag()); + EXPECT_EQ(&CT, DICompositeType::buildODRType( + Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr, + nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr)); + EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag()); +} + +TEST(DebugTypeODRUniquingTest, buildODRTypeFields) { + LLVMContext Context; + Context.enableDebugTypeODRUniquing(); + + // Build an ODR type that's a forward decl with no other fields set. + MDString &UUID = *MDString::get(Context, "UUID"); + auto &CT = *DICompositeType::buildODRType( + Context, UUID, 0, nullptr, nullptr, 0, nullptr, nullptr, 0, 0, 0, + DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr); + +// Create macros for running through all the fields except Identifier and Flags. +#define FOR_EACH_MDFIELD() \ + DO_FOR_FIELD(Name) \ + DO_FOR_FIELD(File) \ + DO_FOR_FIELD(Scope) \ + DO_FOR_FIELD(BaseType) \ + DO_FOR_FIELD(Elements) \ + DO_FOR_FIELD(VTableHolder) \ + DO_FOR_FIELD(TemplateParams) +#define FOR_EACH_INLINEFIELD() \ + DO_FOR_FIELD(Tag) \ + DO_FOR_FIELD(Line) \ + DO_FOR_FIELD(SizeInBits) \ + DO_FOR_FIELD(AlignInBits) \ + DO_FOR_FIELD(OffsetInBits) \ + DO_FOR_FIELD(RuntimeLang) + +// Create all the fields. +#define DO_FOR_FIELD(X) auto *X = MDString::get(Context, #X); + FOR_EACH_MDFIELD(); +#undef DO_FOR_FIELD + unsigned NonZeroInit = 0; +#define DO_FOR_FIELD(X) auto X = ++NonZeroInit; + FOR_EACH_INLINEFIELD(); +#undef DO_FOR_FIELD + + // Replace all the fields with new values that are distinct from each other. + EXPECT_EQ(&CT, + DICompositeType::buildODRType( + Context, UUID, Tag, Name, File, Line, Scope, BaseType, + SizeInBits, AlignInBits, OffsetInBits, DINode::FlagArtificial, + Elements, RuntimeLang, VTableHolder, TemplateParams)); + + // Confirm that all the right fields got updated. +#define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.getRaw##X()); + FOR_EACH_MDFIELD(); +#undef DO_FOR_FIELD +#undef FOR_EACH_MDFIELD +#define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.get##X()); + FOR_EACH_INLINEFIELD(); +#undef DO_FOR_FIELD +#undef FOR_EACH_INLINEFIELD + EXPECT_EQ(DINode::FlagArtificial, CT.getFlags()); + EXPECT_EQ(&UUID, CT.getRawIdentifier()); +} + +} // end namespace diff --git a/unittests/IR/DominatorTreeTest.cpp b/unittests/IR/DominatorTreeTest.cpp index 3aef4d64cbc2..6c49deb32d94 100644 --- a/unittests/IR/DominatorTreeTest.cpp +++ b/unittests/IR/DominatorTreeTest.cpp @@ -29,7 +29,8 @@ namespace llvm { bool runOnFunction(Function &F) override { DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree(); - PostDominatorTree *PDT = &getAnalysis<PostDominatorTree>(); + PostDominatorTree *PDT = + &getAnalysis<PostDominatorTreeWrapperPass>().getPostDomTree(); Function::iterator FI = F.begin(); BasicBlock *BB0 = &*FI++; @@ -206,7 +207,7 @@ namespace llvm { } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired<DominatorTreeWrapperPass>(); - AU.addRequired<PostDominatorTree>(); + AU.addRequired<PostDominatorTreeWrapperPass>(); } DPass() : FunctionPass(ID) { initializeDPassPass(*PassRegistry::getPassRegistry()); @@ -214,7 +215,7 @@ namespace llvm { }; char DPass::ID = 0; - std::unique_ptr<Module> makeLLVMModule(DPass *P) { + std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context, DPass *P) { const char *ModuleStrig = "declare i32 @g()\n" \ "define void @f(i32 %x) personality i32 ()* @g {\n" \ @@ -238,14 +239,14 @@ namespace llvm { " %y9 = phi i32 [0, %bb2], [%y4, %bb1]\n" " ret void\n" \ "}\n"; - LLVMContext &C = getGlobalContext(); SMDiagnostic Err; - return parseAssemblyString(ModuleStrig, Err, C); + return parseAssemblyString(ModuleStrig, Err, Context); } TEST(DominatorTree, Unreachable) { DPass *P = new DPass(); - std::unique_ptr<Module> M = makeLLVMModule(P); + LLVMContext Context; + std::unique_ptr<Module> M = makeLLVMModule(Context, P); legacy::PassManager Passes; Passes.add(P); Passes.run(*M); @@ -255,5 +256,5 @@ namespace llvm { INITIALIZE_PASS_BEGIN(DPass, "dpass", "dpass", false, false) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) -INITIALIZE_PASS_DEPENDENCY(PostDominatorTree) +INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) INITIALIZE_PASS_END(DPass, "dpass", "dpass", false, false) diff --git a/unittests/IR/FunctionTest.cpp b/unittests/IR/FunctionTest.cpp new file mode 100644 index 000000000000..fb458597c37a --- /dev/null +++ b/unittests/IR/FunctionTest.cpp @@ -0,0 +1,112 @@ +//===- FunctionTest.cpp - Function 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/IR/Function.h" +#include "gtest/gtest.h" +using namespace llvm; + +namespace { + +TEST(FunctionTest, hasLazyArguments) { + LLVMContext C; + + Type *ArgTypes[] = {Type::getInt8Ty(C), Type::getInt32Ty(C)}; + FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), ArgTypes, false); + + // Functions start out with lazy arguments. + std::unique_ptr<Function> F( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F")); + EXPECT_TRUE(F->hasLazyArguments()); + + // Checking for empty or size shouldn't force arguments to be instantiated. + EXPECT_FALSE(F->arg_empty()); + EXPECT_TRUE(F->hasLazyArguments()); + EXPECT_EQ(2u, F->arg_size()); + EXPECT_TRUE(F->hasLazyArguments()); + + // The argument list should be populated at first access. + (void)F->arg_begin(); + EXPECT_FALSE(F->hasLazyArguments()); +} + +TEST(FunctionTest, stealArgumentListFrom) { + LLVMContext C; + + Type *ArgTypes[] = {Type::getInt8Ty(C), Type::getInt32Ty(C)}; + FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), ArgTypes, false); + std::unique_ptr<Function> F1( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F1")); + std::unique_ptr<Function> F2( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F1")); + EXPECT_TRUE(F1->hasLazyArguments()); + EXPECT_TRUE(F2->hasLazyArguments()); + + // Steal arguments before they've been accessed. Nothing should change; both + // functions should still have lazy arguments. + // + // steal(empty); drop (empty) + F1->stealArgumentListFrom(*F2); + EXPECT_TRUE(F1->hasLazyArguments()); + EXPECT_TRUE(F2->hasLazyArguments()); + + // Save arguments from F1 for later assertions. F1 won't have lazy arguments + // anymore. + SmallVector<Argument *, 4> Args; + for (Argument &A : F1->args()) + Args.push_back(&A); + EXPECT_EQ(2u, Args.size()); + EXPECT_FALSE(F1->hasLazyArguments()); + + // Steal arguments from F1 to F2. F1's arguments should be lazy again. + // + // steal(real); drop (empty) + F2->stealArgumentListFrom(*F1); + EXPECT_TRUE(F1->hasLazyArguments()); + EXPECT_FALSE(F2->hasLazyArguments()); + unsigned I = 0; + for (Argument &A : F2->args()) { + EXPECT_EQ(Args[I], &A); + I++; + } + EXPECT_EQ(2u, I); + + // Check that arguments in F1 don't have pointer equality with the saved ones. + // This also instantiates F1's arguments. + I = 0; + for (Argument &A : F1->args()) { + EXPECT_NE(Args[I], &A); + I++; + } + EXPECT_EQ(2u, I); + EXPECT_FALSE(F1->hasLazyArguments()); + EXPECT_FALSE(F2->hasLazyArguments()); + + // Steal back from F2. F2's arguments should be lazy again. + // + // steal(real); drop (real) + F1->stealArgumentListFrom(*F2); + EXPECT_FALSE(F1->hasLazyArguments()); + EXPECT_TRUE(F2->hasLazyArguments()); + I = 0; + for (Argument &A : F1->args()) { + EXPECT_EQ(Args[I], &A); + I++; + } + EXPECT_EQ(2u, I); + + // Steal from F2 a second time. Now both functions should have lazy + // arguments. + // + // steal(empty); drop (real) + F1->stealArgumentListFrom(*F2); + EXPECT_TRUE(F1->hasLazyArguments()); + EXPECT_TRUE(F2->hasLazyArguments()); +} + +} // end namespace diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp index bd0eae0399a4..58fd71b8a357 100644 --- a/unittests/IR/IRBuilderTest.cpp +++ b/unittests/IR/IRBuilderTest.cpp @@ -252,7 +252,7 @@ TEST_F(IRBuilderTest, FastMathFlags) { } TEST_F(IRBuilderTest, WrapFlags) { - IRBuilder<true, NoFolder> Builder(BB); + IRBuilder<NoFolder> Builder(BB); // Test instructions. GlobalVariable *G = new GlobalVariable(*M, Builder.getInt32Ty(), true, @@ -418,4 +418,18 @@ TEST_F(IRBuilderTest, DebugLoc) { DIB.finalize(); } + +TEST_F(IRBuilderTest, DIImportedEntity) { + IRBuilder<> Builder(BB); + DIBuilder DIB(*M); + auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74, "F.CBL", "/", + "llvm-cobol74", true, "", 0); + DIB.createImportedDeclaration(CU, nullptr, 1); + DIB.createImportedDeclaration(CU, nullptr, 1); + DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, 2); + DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, 2); + DIB.finalize(); + EXPECT_TRUE(verifyModule(*M)); + EXPECT_TRUE(CU->getImportedEntities().size() == 2); +} } diff --git a/unittests/IR/InstructionsTest.cpp b/unittests/IR/InstructionsTest.cpp index 3ca3ad2b6e83..0dac7c1bcfb1 100644 --- a/unittests/IR/InstructionsTest.cpp +++ b/unittests/IR/InstructionsTest.cpp @@ -27,7 +27,7 @@ namespace llvm { namespace { TEST(InstructionsTest, ReturnInst) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; // test for PR6589 const ReturnInst* r0 = ReturnInst::Create(C); @@ -103,7 +103,7 @@ TEST_F(ModuleWithFunctionTest, InvokeInst) { } TEST(InstructionsTest, BranchInst) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; // Make a BasicBlocks BasicBlock* bb0 = BasicBlock::Create(C); @@ -169,7 +169,7 @@ TEST(InstructionsTest, BranchInst) { } TEST(InstructionsTest, CastInst) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Type *Int8Ty = Type::getInt8Ty(C); Type *Int16Ty = Type::getInt16Ty(C); @@ -281,14 +281,18 @@ TEST(InstructionsTest, CastInst) { // First form BasicBlock *BB = BasicBlock::Create(C); Constant *NullV2I32Ptr = Constant::getNullValue(V2Int32PtrTy); - CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty, "foo", BB); + auto Inst1 = CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty, "foo", BB); // Second form - CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty); + auto Inst2 = CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty); + + delete Inst2; + Inst1->eraseFromParent(); + delete BB; } TEST(InstructionsTest, VectorGep) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; // Type Definitions Type *I8Ty = IntegerType::get(C, 8); @@ -391,7 +395,7 @@ TEST(InstructionsTest, VectorGep) { } TEST(InstructionsTest, FPMathOperator) { - LLVMContext &Context = getGlobalContext(); + LLVMContext Context; IRBuilder<> Builder(Context); MDBuilder MDHelper(Context); Instruction *I = Builder.CreatePHI(Builder.getDoubleTy(), 0); @@ -406,7 +410,7 @@ TEST(InstructionsTest, FPMathOperator) { TEST(InstructionsTest, isEliminableCastPair) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Type* Int16Ty = Type::getInt16Ty(C); Type* Int32Ty = Type::getInt32Ty(C); @@ -486,7 +490,7 @@ TEST(InstructionsTest, isEliminableCastPair) { } TEST(InstructionsTest, CloneCall) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Type *Int32Ty = Type::getInt32Ty(C); Type *ArgTys[] = {Int32Ty, Int32Ty, Int32Ty}; Type *FnTy = FunctionType::get(Int32Ty, ArgTys, /*isVarArg=*/false); @@ -518,7 +522,62 @@ TEST(InstructionsTest, CloneCall) { } } -} // end anonymous namespace -} // end namespace llvm +TEST(InstructionsTest, AlterCallBundles) { + LLVMContext C; + Type *Int32Ty = Type::getInt32Ty(C); + Type *FnTy = FunctionType::get(Int32Ty, Int32Ty, /*isVarArg=*/false); + Value *Callee = Constant::getNullValue(FnTy->getPointerTo()); + Value *Args[] = {ConstantInt::get(Int32Ty, 42)}; + OperandBundleDef OldBundle("before", UndefValue::get(Int32Ty)); + std::unique_ptr<CallInst> Call( + CallInst::Create(Callee, Args, OldBundle, "result")); + Call->setTailCallKind(CallInst::TailCallKind::TCK_NoTail); + AttrBuilder AB; + AB.addAttribute(Attribute::Cold); + Call->setAttributes(AttributeSet::get(C, AttributeSet::FunctionIndex, AB)); + Call->setDebugLoc(DebugLoc(MDNode::get(C, None))); + + OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7)); + std::unique_ptr<CallInst> Clone(CallInst::Create(Call.get(), NewBundle)); + EXPECT_EQ(Call->getNumArgOperands(), Clone->getNumArgOperands()); + EXPECT_EQ(Call->getArgOperand(0), Clone->getArgOperand(0)); + EXPECT_EQ(Call->getCallingConv(), Clone->getCallingConv()); + EXPECT_EQ(Call->getTailCallKind(), Clone->getTailCallKind()); + EXPECT_TRUE(Clone->hasFnAttr(Attribute::AttrKind::Cold)); + EXPECT_EQ(Call->getDebugLoc(), Clone->getDebugLoc()); + EXPECT_EQ(Clone->getNumOperandBundles(), 1U); + EXPECT_TRUE(Clone->getOperandBundle("after").hasValue()); +} +TEST(InstructionsTest, AlterInvokeBundles) { + LLVMContext C; + Type *Int32Ty = Type::getInt32Ty(C); + Type *FnTy = FunctionType::get(Int32Ty, Int32Ty, /*isVarArg=*/false); + Value *Callee = Constant::getNullValue(FnTy->getPointerTo()); + Value *Args[] = {ConstantInt::get(Int32Ty, 42)}; + std::unique_ptr<BasicBlock> NormalDest(BasicBlock::Create(C)); + std::unique_ptr<BasicBlock> UnwindDest(BasicBlock::Create(C)); + OperandBundleDef OldBundle("before", UndefValue::get(Int32Ty)); + std::unique_ptr<InvokeInst> Invoke(InvokeInst::Create( + Callee, NormalDest.get(), UnwindDest.get(), Args, OldBundle, "result")); + AttrBuilder AB; + AB.addAttribute(Attribute::Cold); + Invoke->setAttributes(AttributeSet::get(C, AttributeSet::FunctionIndex, AB)); + Invoke->setDebugLoc(DebugLoc(MDNode::get(C, None))); + + OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7)); + std::unique_ptr<InvokeInst> Clone( + InvokeInst::Create(Invoke.get(), NewBundle)); + EXPECT_EQ(Invoke->getNormalDest(), Clone->getNormalDest()); + EXPECT_EQ(Invoke->getUnwindDest(), Clone->getUnwindDest()); + EXPECT_EQ(Invoke->getNumArgOperands(), Clone->getNumArgOperands()); + EXPECT_EQ(Invoke->getArgOperand(0), Clone->getArgOperand(0)); + EXPECT_EQ(Invoke->getCallingConv(), Clone->getCallingConv()); + EXPECT_TRUE(Clone->hasFnAttr(Attribute::AttrKind::Cold)); + EXPECT_EQ(Invoke->getDebugLoc(), Clone->getDebugLoc()); + EXPECT_EQ(Clone->getNumOperandBundles(), 1U); + EXPECT_TRUE(Clone->getOperandBundle("after").hasValue()); +} +} // end anonymous namespace +} // end namespace llvm diff --git a/unittests/IR/IntrinsicsTest.cpp b/unittests/IR/IntrinsicsTest.cpp new file mode 100644 index 000000000000..0d12126b1fcf --- /dev/null +++ b/unittests/IR/IntrinsicsTest.cpp @@ -0,0 +1,40 @@ +//===- llvm/unittest/IR/IntrinsicsTest.cpp - ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/IntrinsicInst.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +static const char *const NameTable1[] = { + "llvm.foo", + "llvm.foo.a", + "llvm.foo.b", + "llvm.foo.b.a", + "llvm.foo.c", +}; + +TEST(IntrinNameLookup, Basic) { + int I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo"); + EXPECT_EQ(0, I); + I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.f64"); + EXPECT_EQ(0, I); + I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.b"); + EXPECT_EQ(2, I); + I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.b.a"); + EXPECT_EQ(3, I); + I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.c"); + EXPECT_EQ(4, I); + I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.c.f64"); + EXPECT_EQ(4, I); +} + +} // end namespace diff --git a/unittests/IR/LegacyPassManagerTest.cpp b/unittests/IR/LegacyPassManagerTest.cpp index 1f88283dc0ce..9dceb976c937 100644 --- a/unittests/IR/LegacyPassManagerTest.cpp +++ b/unittests/IR/LegacyPassManagerTest.cpp @@ -14,7 +14,6 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/LegacyPassManager.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/CallGraphSCCPass.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" @@ -288,7 +287,8 @@ namespace llvm { char OnTheFlyTest::ID=0; TEST(PassManager, RunOnce) { - Module M("test-once", getGlobalContext()); + LLVMContext Context; + Module M("test-once", Context); struct ModuleNDNM *mNDNM = new ModuleNDNM(); struct ModuleDNM *mDNM = new ModuleDNM(); struct ModuleNDM *mNDM = new ModuleNDM(); @@ -311,7 +311,8 @@ namespace llvm { } TEST(PassManager, ReRun) { - Module M("test-rerun", getGlobalContext()); + LLVMContext Context; + Module M("test-rerun", Context); struct ModuleNDNM *mNDNM = new ModuleNDNM(); struct ModuleDNM *mDNM = new ModuleDNM(); struct ModuleNDM *mNDM = new ModuleNDM(); @@ -334,11 +335,12 @@ namespace llvm { EXPECT_EQ(1, mDNM->run); } - Module* makeLLVMModule(); + Module *makeLLVMModule(LLVMContext &Context); template<typename T> void MemoryTestHelper(int run) { - std::unique_ptr<Module> M(makeLLVMModule()); + LLVMContext Context; + std::unique_ptr<Module> M(makeLLVMModule(Context)); T *P = new T(); legacy::PassManager Passes; Passes.add(P); @@ -348,7 +350,8 @@ namespace llvm { template<typename T> void MemoryTestHelper(int run, int N) { - Module *M = makeLLVMModule(); + LLVMContext Context; + Module *M = makeLLVMModule(Context); T *P = new T(); legacy::PassManager Passes; Passes.add(P); @@ -383,7 +386,8 @@ namespace llvm { } TEST(PassManager, MemoryOnTheFly) { - Module *M = makeLLVMModule(); + LLVMContext Context; + Module *M = makeLLVMModule(Context); { SCOPED_TRACE("Running OnTheFlyTest"); struct OnTheFlyTest *O = new OnTheFlyTest(); @@ -396,9 +400,9 @@ namespace llvm { delete M; } - Module* makeLLVMModule() { + Module *makeLLVMModule(LLVMContext &Context) { // Module Construction - Module* mod = new Module("test-mem", getGlobalContext()); + Module *mod = new Module("test-mem", Context); mod->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" "i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-" "a:0:64-s:64:64-f80:128:128"); @@ -406,18 +410,17 @@ namespace llvm { // Type Definitions std::vector<Type*>FuncTy_0_args; - FunctionType* FuncTy_0 = FunctionType::get( - /*Result=*/IntegerType::get(getGlobalContext(), 32), - /*Params=*/FuncTy_0_args, - /*isVarArg=*/false); + FunctionType *FuncTy_0 = FunctionType::get( + /*Result=*/IntegerType::get(Context, 32), + /*Params=*/FuncTy_0_args, + /*isVarArg=*/false); std::vector<Type*>FuncTy_2_args; - FuncTy_2_args.push_back(IntegerType::get(getGlobalContext(), 1)); - FunctionType* FuncTy_2 = FunctionType::get( - /*Result=*/Type::getVoidTy(getGlobalContext()), - /*Params=*/FuncTy_2_args, - /*isVarArg=*/false); - + FuncTy_2_args.push_back(IntegerType::get(Context, 1)); + FunctionType *FuncTy_2 = FunctionType::get( + /*Result=*/Type::getVoidTy(Context), + /*Params=*/FuncTy_2_args, + /*isVarArg=*/false); // Function Declarations @@ -465,7 +468,8 @@ namespace llvm { // Function: test1 (func_test1) { - BasicBlock* label_entry = BasicBlock::Create(getGlobalContext(), "entry",func_test1,nullptr); + BasicBlock *label_entry = + BasicBlock::Create(Context, "entry", func_test1, nullptr); // Block entry (label_entry) CallInst* int32_3 = CallInst::Create(func_test2, "", label_entry); @@ -473,14 +477,14 @@ namespace llvm { int32_3->setTailCall(false);AttributeSet int32_3_PAL; int32_3->setAttributes(int32_3_PAL); - ReturnInst::Create(getGlobalContext(), int32_3, label_entry); - + ReturnInst::Create(Context, int32_3, label_entry); } // Function: test2 (func_test2) { - BasicBlock* label_entry_5 = BasicBlock::Create(getGlobalContext(), "entry",func_test2,nullptr); + BasicBlock *label_entry_5 = + BasicBlock::Create(Context, "entry", func_test2, nullptr); // Block entry (label_entry_5) CallInst* int32_6 = CallInst::Create(func_test3, "", label_entry_5); @@ -488,14 +492,14 @@ namespace llvm { int32_6->setTailCall(false);AttributeSet int32_6_PAL; int32_6->setAttributes(int32_6_PAL); - ReturnInst::Create(getGlobalContext(), int32_6, label_entry_5); - + ReturnInst::Create(Context, int32_6, label_entry_5); } // Function: test3 (func_test3) { - BasicBlock* label_entry_8 = BasicBlock::Create(getGlobalContext(), "entry",func_test3,nullptr); + BasicBlock *label_entry_8 = + BasicBlock::Create(Context, "entry", func_test3, nullptr); // Block entry (label_entry_8) CallInst* int32_9 = CallInst::Create(func_test1, "", label_entry_8); @@ -503,8 +507,7 @@ namespace llvm { int32_9->setTailCall(false);AttributeSet int32_9_PAL; int32_9->setAttributes(int32_9_PAL); - ReturnInst::Create(getGlobalContext(), int32_9, label_entry_8); - + ReturnInst::Create(Context, int32_9, label_entry_8); } // Function: test4 (func_test4) @@ -513,10 +516,14 @@ namespace llvm { Value *int1_f = &*args++; int1_f->setName("f"); - BasicBlock* label_entry_11 = BasicBlock::Create(getGlobalContext(), "entry",func_test4,nullptr); - BasicBlock* label_bb = BasicBlock::Create(getGlobalContext(), "bb",func_test4,nullptr); - BasicBlock* label_bb1 = BasicBlock::Create(getGlobalContext(), "bb1",func_test4,nullptr); - BasicBlock* label_return = BasicBlock::Create(getGlobalContext(), "return",func_test4,nullptr); + BasicBlock *label_entry_11 = + BasicBlock::Create(Context, "entry", func_test4, nullptr); + BasicBlock *label_bb = + BasicBlock::Create(Context, "bb", func_test4, nullptr); + BasicBlock *label_bb1 = + BasicBlock::Create(Context, "bb1", func_test4, nullptr); + BasicBlock *label_return = + BasicBlock::Create(Context, "return", func_test4, nullptr); // Block entry (label_entry_11) BranchInst::Create(label_bb, label_entry_11); @@ -528,8 +535,7 @@ namespace llvm { BranchInst::Create(label_bb1, label_return, int1_f, label_bb1); // Block return (label_return) - ReturnInst::Create(getGlobalContext(), label_return); - + ReturnInst::Create(Context, label_return); } return mod; } diff --git a/unittests/IR/Makefile b/unittests/IR/Makefile deleted file mode 100644 index 45aa8d68082e..000000000000 --- a/unittests/IR/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/IR/Makefile -------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = IR -LINK_COMPONENTS := core analysis asmparser - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/IR/MetadataTest.cpp b/unittests/IR/MetadataTest.cpp index a745b235a381..77a2dbaf2dfa 100644 --- a/unittests/IR/MetadataTest.cpp +++ b/unittests/IR/MetadataTest.cpp @@ -80,26 +80,29 @@ protected: MDTuple *getTuple() { return MDTuple::getDistinct(Context, None); } DISubroutineType *getSubroutineType() { - return DISubroutineType::getDistinct(Context, 0, getNode(nullptr)); + return DISubroutineType::getDistinct(Context, 0, 0, getNode(nullptr)); } DISubprogram *getSubprogram() { return DISubprogram::getDistinct(Context, nullptr, "", "", nullptr, 0, - nullptr, false, false, 0, nullptr, 0, 0, 0, - 0); + nullptr, false, false, 0, nullptr, + 0, 0, 0, 0, false, nullptr); } - DIScopeRef getSubprogramRef() { return getSubprogram()->getRef(); } DIFile *getFile() { return DIFile::getDistinct(Context, "file.c", "/path/to/dir"); } - DITypeRef getBasicType(StringRef Name) { - return DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type, Name) - ->getRef(); + DICompileUnit *getUnit() { + return DICompileUnit::getDistinct(Context, 1, getFile(), "clang", false, + "-g", 2, "", DICompileUnit::FullDebug, + getTuple(), getTuple(), getTuple(), + getTuple(), getTuple(), 0); } - DITypeRef getDerivedType() { + DIType *getBasicType(StringRef Name) { + return DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type, Name); + } + DIType *getDerivedType() { return DIDerivedType::getDistinct(Context, dwarf::DW_TAG_pointer_type, "", nullptr, 0, nullptr, - getBasicType("basictype"), 1, 2, 0, 0) - ->getRef(); + getBasicType("basictype"), 1, 2, 0, 0); } Constant *getConstant() { return ConstantInt::get(Type::getInt32Ty(Context), Counter++); @@ -107,11 +110,10 @@ protected: ConstantAsMetadata *getConstantAsMetadata() { return ConstantAsMetadata::get(getConstant()); } - DITypeRef getCompositeType() { + DIType *getCompositeType() { return DICompositeType::getDistinct( - Context, dwarf::DW_TAG_structure_type, "", nullptr, 0, nullptr, - nullptr, 32, 32, 0, 0, nullptr, 0, nullptr, nullptr, "") - ->getRef(); + Context, dwarf::DW_TAG_structure_type, "", nullptr, 0, nullptr, nullptr, + 32, 32, 0, 0, nullptr, 0, nullptr, nullptr, ""); } Function *getFunction(StringRef Name) { return cast<Function>(M.getOrInsertFunction( @@ -174,8 +176,8 @@ TEST_F(MDNodeTest, Simple) { MDString *s1 = MDString::get(Context, StringRef(&x[0], 3)); MDString *s2 = MDString::get(Context, StringRef(&y[0], 3)); - ConstantAsMetadata *CI = ConstantAsMetadata::get( - ConstantInt::get(getGlobalContext(), APInt(8, 0))); + ConstantAsMetadata *CI = + ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0))); std::vector<Metadata *> V; V.push_back(s1); @@ -206,8 +208,8 @@ TEST_F(MDNodeTest, Simple) { } TEST_F(MDNodeTest, Delete) { - Constant *C = ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 1); - Instruction *I = new BitCastInst(C, Type::getInt32Ty(getGlobalContext())); + Constant *C = ConstantInt::get(Type::getInt32Ty(Context), 1); + Instruction *I = new BitCastInst(C, Type::getInt32Ty(Context)); Metadata *const V = LocalAsMetadata::get(I); MDNode *n = MDNode::get(Context, V); @@ -494,20 +496,6 @@ TEST_F(MDNodeTest, isTemporary) { EXPECT_TRUE(T->isTemporary()); } -#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) - -TEST_F(MDNodeTest, deathOnNoReplaceTemporaryRAUW) { - auto Temp = MDNode::getTemporary(Context, None); - Temp->setCanReplace(false); - EXPECT_DEATH(Temp->replaceAllUsesWith(nullptr), - "Attempted to replace Metadata marked for no replacement"); - Temp->setCanReplace(true); - // Remove the references to Temp; required for teardown. - Temp->replaceAllUsesWith(nullptr); -} - -#endif - TEST_F(MDNodeTest, getDistinctWithUnresolvedOperands) { // temporary !{} auto Temp = MDTuple::getTemporary(Context, None); @@ -981,14 +969,14 @@ TEST_F(DITypeTest, setFlags) { Metadata *TypesOps[] = {nullptr}; Metadata *Types = MDTuple::get(Context, TypesOps); - DIType *D = DISubroutineType::getDistinct(Context, 0u, Types); + DIType *D = DISubroutineType::getDistinct(Context, 0u, 0, Types); EXPECT_EQ(0u, D->getFlags()); D->setFlags(DINode::FlagRValueReference); EXPECT_EQ(DINode::FlagRValueReference, D->getFlags()); D->setFlags(0u); EXPECT_EQ(0u, D->getFlags()); - TempDIType T = DISubroutineType::getTemporary(Context, 0u, Types); + TempDIType T = DISubroutineType::getTemporary(Context, 0u, 0, Types); EXPECT_EQ(0u, T->getFlags()); T->setFlags(DINode::FlagRValueReference); EXPECT_EQ(DINode::FlagRValueReference, T->getFlags()); @@ -1000,8 +988,8 @@ typedef MetadataTest DIDerivedTypeTest; TEST_F(DIDerivedTypeTest, get) { DIFile *File = getFile(); - DIScopeRef Scope = getSubprogramRef(); - DITypeRef BaseType = getBasicType("basic"); + DIScope *Scope = getSubprogram(); + DIType *BaseType = getBasicType("basic"); MDTuple *ExtraData = getTuple(); auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", @@ -1034,7 +1022,7 @@ TEST_F(DIDerivedTypeTest, get) { "something", File, 2, Scope, BaseType, 2, 3, 4, 5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, - "something", File, 1, getSubprogramRef(), + "something", File, 1, getSubprogram(), BaseType, 2, 3, 4, 5, ExtraData)); EXPECT_NE(N, DIDerivedType::get( Context, dwarf::DW_TAG_pointer_type, "something", File, 1, @@ -1061,8 +1049,8 @@ TEST_F(DIDerivedTypeTest, get) { TEST_F(DIDerivedTypeTest, getWithLargeValues) { DIFile *File = getFile(); - DIScopeRef Scope = getSubprogramRef(); - DITypeRef BaseType = getBasicType("basic"); + DIScope *Scope = getSubprogram(); + DIType *BaseType = getBasicType("basic"); MDTuple *ExtraData = getTuple(); auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", @@ -1080,15 +1068,15 @@ TEST_F(DICompositeTypeTest, get) { StringRef Name = "some name"; DIFile *File = getFile(); unsigned Line = 1; - DIScopeRef Scope = getSubprogramRef(); - DITypeRef BaseType = getCompositeType(); + DIScope *Scope = getSubprogram(); + DIType *BaseType = getCompositeType(); uint64_t SizeInBits = 2; uint64_t AlignInBits = 3; uint64_t OffsetInBits = 4; unsigned Flags = 5; MDTuple *Elements = getTuple(); unsigned RuntimeLang = 6; - DITypeRef VTableHolder = getCompositeType(); + DIType *VTableHolder = getCompositeType(); MDTuple *TemplateParams = getTuple(); StringRef Identifier = "some id"; @@ -1134,7 +1122,7 @@ TEST_F(DICompositeTypeTest, get) { OffsetInBits, Flags, Elements, RuntimeLang, VTableHolder, TemplateParams, Identifier)); EXPECT_NE(N, DICompositeType::get( - Context, Tag, Name, File, Line, getSubprogramRef(), BaseType, + Context, Tag, Name, File, Line, getSubprogram(), BaseType, SizeInBits, AlignInBits, OffsetInBits, Flags, Elements, RuntimeLang, VTableHolder, TemplateParams, Identifier)); EXPECT_NE(N, DICompositeType::get( @@ -1199,15 +1187,15 @@ TEST_F(DICompositeTypeTest, getWithLargeValues) { StringRef Name = "some name"; DIFile *File = getFile(); unsigned Line = 1; - DIScopeRef Scope = getSubprogramRef(); - DITypeRef BaseType = getCompositeType(); + DIScope *Scope = getSubprogram(); + DIType *BaseType = getCompositeType(); uint64_t SizeInBits = UINT64_MAX; uint64_t AlignInBits = UINT64_MAX - 1; uint64_t OffsetInBits = UINT64_MAX - 2; unsigned Flags = 5; MDTuple *Elements = getTuple(); unsigned RuntimeLang = 6; - DITypeRef VTableHolder = getCompositeType(); + DIType *VTableHolder = getCompositeType(); MDTuple *TemplateParams = getTuple(); StringRef Identifier = "some id"; @@ -1225,8 +1213,8 @@ TEST_F(DICompositeTypeTest, replaceOperands) { StringRef Name = "some name"; DIFile *File = getFile(); unsigned Line = 1; - DIScopeRef Scope = getSubprogramRef(); - DITypeRef BaseType = getCompositeType(); + DIScope *Scope = getSubprogram(); + DIType *BaseType = getCompositeType(); uint64_t SizeInBits = 2; uint64_t AlignInBits = 3; uint64_t OffsetInBits = 4; @@ -1245,7 +1233,7 @@ TEST_F(DICompositeTypeTest, replaceOperands) { N->replaceElements(nullptr); EXPECT_EQ(nullptr, N->getElements().get()); - DITypeRef VTableHolder = getCompositeType(); + DIType *VTableHolder = getCompositeType(); EXPECT_EQ(nullptr, N->getVTableHolder()); N->replaceVTableHolder(VTableHolder); EXPECT_EQ(VTableHolder, N->getVTableHolder()); @@ -1266,14 +1254,29 @@ TEST_F(DISubroutineTypeTest, get) { unsigned Flags = 1; MDTuple *TypeArray = getTuple(); - auto *N = DISubroutineType::get(Context, Flags, TypeArray); + auto *N = DISubroutineType::get(Context, Flags, 0, TypeArray); EXPECT_EQ(dwarf::DW_TAG_subroutine_type, N->getTag()); EXPECT_EQ(Flags, N->getFlags()); EXPECT_EQ(TypeArray, N->getTypeArray().get()); - EXPECT_EQ(N, DISubroutineType::get(Context, Flags, TypeArray)); - - EXPECT_NE(N, DISubroutineType::get(Context, Flags + 1, TypeArray)); - EXPECT_NE(N, DISubroutineType::get(Context, Flags, getTuple())); + EXPECT_EQ(N, DISubroutineType::get(Context, Flags, 0, TypeArray)); + + EXPECT_NE(N, DISubroutineType::get(Context, Flags + 1, 0, TypeArray)); + EXPECT_NE(N, DISubroutineType::get(Context, Flags, 0, getTuple())); + + // Test the hashing of calling conventions. + auto *Fast = DISubroutineType::get( + Context, Flags, dwarf::DW_CC_BORLAND_msfastcall, TypeArray); + auto *Std = DISubroutineType::get(Context, Flags, + dwarf::DW_CC_BORLAND_stdcall, TypeArray); + EXPECT_EQ(Fast, + DISubroutineType::get(Context, Flags, + dwarf::DW_CC_BORLAND_msfastcall, TypeArray)); + EXPECT_EQ(Std, DISubroutineType::get( + Context, Flags, dwarf::DW_CC_BORLAND_stdcall, TypeArray)); + + EXPECT_NE(N, Fast); + EXPECT_NE(N, Std); + EXPECT_NE(Fast, Std); TempDISubroutineType Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1319,10 +1322,9 @@ TEST_F(DICompileUnitTest, get) { StringRef Flags = "flag after flag"; unsigned RuntimeVersion = 2; StringRef SplitDebugFilename = "another/file"; - unsigned EmissionKind = 3; + auto EmissionKind = DICompileUnit::FullDebug; MDTuple *EnumTypes = getTuple(); MDTuple *RetainedTypes = getTuple(); - MDTuple *Subprograms = getTuple(); MDTuple *GlobalVariables = getTuple(); MDTuple *ImportedEntities = getTuple(); uint64_t DWOId = 0x10000000c0ffee; @@ -1330,7 +1332,7 @@ TEST_F(DICompileUnitTest, get) { auto *N = DICompileUnit::getDistinct( Context, SourceLanguage, File, Producer, IsOptimized, Flags, RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes, - RetainedTypes, Subprograms, GlobalVariables, ImportedEntities, Macros, + RetainedTypes, GlobalVariables, ImportedEntities, Macros, DWOId); EXPECT_EQ(dwarf::DW_TAG_compile_unit, N->getTag()); @@ -1344,7 +1346,6 @@ TEST_F(DICompileUnitTest, get) { EXPECT_EQ(EmissionKind, N->getEmissionKind()); EXPECT_EQ(EnumTypes, N->getEnumTypes().get()); EXPECT_EQ(RetainedTypes, N->getRetainedTypes().get()); - EXPECT_EQ(Subprograms, N->getSubprograms().get()); EXPECT_EQ(GlobalVariables, N->getGlobalVariables().get()); EXPECT_EQ(ImportedEntities, N->getImportedEntities().get()); EXPECT_EQ(Macros, N->getMacros().get()); @@ -1362,7 +1363,6 @@ TEST_F(DICompileUnitTest, get) { EXPECT_EQ(EmissionKind, Temp->getEmissionKind()); EXPECT_EQ(EnumTypes, Temp->getEnumTypes().get()); EXPECT_EQ(RetainedTypes, Temp->getRetainedTypes().get()); - EXPECT_EQ(Subprograms, Temp->getSubprograms().get()); EXPECT_EQ(GlobalVariables, Temp->getGlobalVariables().get()); EXPECT_EQ(ImportedEntities, Temp->getImportedEntities().get()); EXPECT_EQ(Macros, Temp->getMacros().get()); @@ -1382,7 +1382,7 @@ TEST_F(DICompileUnitTest, replaceArrays) { StringRef Flags = "flag after flag"; unsigned RuntimeVersion = 2; StringRef SplitDebugFilename = "another/file"; - unsigned EmissionKind = 3; + auto EmissionKind = DICompileUnit::FullDebug; MDTuple *EnumTypes = MDTuple::getDistinct(Context, None); MDTuple *RetainedTypes = MDTuple::getDistinct(Context, None); MDTuple *ImportedEntities = MDTuple::getDistinct(Context, None); @@ -1390,14 +1390,7 @@ TEST_F(DICompileUnitTest, replaceArrays) { auto *N = DICompileUnit::getDistinct( Context, SourceLanguage, File, Producer, IsOptimized, Flags, RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes, - RetainedTypes, nullptr, nullptr, ImportedEntities, nullptr, DWOId); - - auto *Subprograms = MDTuple::getDistinct(Context, None); - EXPECT_EQ(nullptr, N->getSubprograms().get()); - N->replaceSubprograms(Subprograms); - EXPECT_EQ(Subprograms, N->getSubprograms().get()); - N->replaceSubprograms(nullptr); - EXPECT_EQ(nullptr, N->getSubprograms().get()); + RetainedTypes, nullptr, ImportedEntities, nullptr, DWOId); auto *GlobalVariables = MDTuple::getDistinct(Context, None); EXPECT_EQ(nullptr, N->getGlobalVariables().get()); @@ -1417,7 +1410,7 @@ TEST_F(DICompileUnitTest, replaceArrays) { typedef MetadataTest DISubprogramTest; TEST_F(DISubprogramTest, get) { - DIScopeRef Scope = getCompositeType(); + DIScope *Scope = getCompositeType(); StringRef Name = "name"; StringRef LinkageName = "linkage"; DIFile *File = getFile(); @@ -1426,19 +1419,23 @@ TEST_F(DISubprogramTest, get) { bool IsLocalToUnit = false; bool IsDefinition = true; unsigned ScopeLine = 3; - DITypeRef ContainingType = getCompositeType(); - unsigned Virtuality = 4; + DIType *ContainingType = getCompositeType(); + unsigned Virtuality = 2; unsigned VirtualIndex = 5; + int ThisAdjustment = -3; unsigned Flags = 6; + unsigned NotFlags = (~Flags) & ((1 << 27) - 1); bool IsOptimized = false; MDTuple *TemplateParams = getTuple(); DISubprogram *Declaration = getSubprogram(); MDTuple *Variables = getTuple(); + DICompileUnit *Unit = getUnit(); - auto *N = DISubprogram::get( - Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, - IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, Flags, - IsOptimized, TemplateParams, Declaration, Variables); + auto *N = DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, + Type, IsLocalToUnit, IsDefinition, ScopeLine, + ContainingType, Virtuality, VirtualIndex, + ThisAdjustment, Flags, IsOptimized, Unit, + TemplateParams, Declaration, Variables); EXPECT_EQ(dwarf::DW_TAG_subprogram, N->getTag()); EXPECT_EQ(Scope, N->getScope()); @@ -1453,102 +1450,110 @@ TEST_F(DISubprogramTest, get) { EXPECT_EQ(ContainingType, N->getContainingType()); EXPECT_EQ(Virtuality, N->getVirtuality()); EXPECT_EQ(VirtualIndex, N->getVirtualIndex()); + EXPECT_EQ(ThisAdjustment, N->getThisAdjustment()); EXPECT_EQ(Flags, N->getFlags()); EXPECT_EQ(IsOptimized, N->isOptimized()); + EXPECT_EQ(Unit, N->getUnit()); EXPECT_EQ(TemplateParams, N->getTemplateParams().get()); EXPECT_EQ(Declaration, N->getDeclaration()); EXPECT_EQ(Variables, N->getVariables().get()); EXPECT_EQ(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - Flags, IsOptimized, TemplateParams, - Declaration, Variables)); - - EXPECT_NE(N, DISubprogram::get(Context, getCompositeType(), Name, LinkageName, - File, Line, Type, IsLocalToUnit, IsDefinition, - ScopeLine, ContainingType, Virtuality, - VirtualIndex, Flags, IsOptimized, - TemplateParams, Declaration, Variables)); - EXPECT_NE(N, DISubprogram::get(Context, Scope, "other", LinkageName, File, - Line, Type, IsLocalToUnit, IsDefinition, - ScopeLine, ContainingType, Virtuality, - VirtualIndex, Flags, IsOptimized, + ThisAdjustment, Flags, IsOptimized, Unit, TemplateParams, Declaration, Variables)); + + EXPECT_NE(N, DISubprogram::get( + Context, getCompositeType(), Name, LinkageName, File, Line, + Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, + Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized, + Unit, TemplateParams, Declaration, Variables)); + EXPECT_NE(N, DISubprogram::get( + Context, Scope, "other", LinkageName, File, Line, Type, + IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, + Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized, + Unit, TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, "other", File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - Flags, IsOptimized, TemplateParams, - Declaration, Variables)); - EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, getFile(), - Line, Type, IsLocalToUnit, IsDefinition, - ScopeLine, ContainingType, Virtuality, - VirtualIndex, Flags, IsOptimized, - TemplateParams, Declaration, Variables)); - EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, - Line + 1, Type, IsLocalToUnit, IsDefinition, - ScopeLine, ContainingType, Virtuality, - VirtualIndex, Flags, IsOptimized, - TemplateParams, Declaration, Variables)); - EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, - getSubroutineType(), IsLocalToUnit, - IsDefinition, ScopeLine, ContainingType, - Virtuality, VirtualIndex, Flags, IsOptimized, + ThisAdjustment, Flags, IsOptimized, Unit, TemplateParams, Declaration, Variables)); + EXPECT_NE(N, DISubprogram::get( + Context, Scope, Name, LinkageName, getFile(), Line, Type, + IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, + Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized, + Unit, TemplateParams, Declaration, Variables)); + EXPECT_NE(N, DISubprogram::get( + Context, Scope, Name, LinkageName, File, Line + 1, Type, + IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, + Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized, + Unit, TemplateParams, Declaration, Variables)); + EXPECT_NE(N, + DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, + getSubroutineType(), IsLocalToUnit, IsDefinition, + ScopeLine, ContainingType, Virtuality, + VirtualIndex, ThisAdjustment, Flags, IsOptimized, + Unit, TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, !IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - Flags, IsOptimized, TemplateParams, - Declaration, Variables)); + ThisAdjustment, Flags, IsOptimized, Unit, + TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, !IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - Flags, IsOptimized, TemplateParams, - Declaration, Variables)); - EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, - Type, IsLocalToUnit, IsDefinition, - ScopeLine + 1, ContainingType, Virtuality, - VirtualIndex, Flags, IsOptimized, + ThisAdjustment, Flags, IsOptimized, Unit, TemplateParams, Declaration, Variables)); + EXPECT_NE(N, DISubprogram::get( + Context, Scope, Name, LinkageName, File, Line, Type, + IsLocalToUnit, IsDefinition, ScopeLine + 1, ContainingType, + Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized, + Unit, TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, getCompositeType(), Virtuality, VirtualIndex, - Flags, IsOptimized, TemplateParams, - Declaration, Variables)); + ThisAdjustment, Flags, IsOptimized, Unit, + TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality + 1, VirtualIndex, - Flags, IsOptimized, TemplateParams, - Declaration, Variables)); + ThisAdjustment, Flags, IsOptimized, Unit, + TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex + 1, - Flags, IsOptimized, TemplateParams, - Declaration, Variables)); + ThisAdjustment, Flags, IsOptimized, Unit, + TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - ~Flags, IsOptimized, TemplateParams, - Declaration, Variables)); + ThisAdjustment, NotFlags, IsOptimized, Unit, + TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - Flags, !IsOptimized, TemplateParams, - Declaration, Variables)); - EXPECT_NE(N, - DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, - Type, IsLocalToUnit, IsDefinition, ScopeLine, - ContainingType, Virtuality, VirtualIndex, Flags, - IsOptimized, getTuple(), Declaration, Variables)); + ThisAdjustment, Flags, !IsOptimized, Unit, + TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - Flags, IsOptimized, TemplateParams, - getSubprogram(), Variables)); + ThisAdjustment, Flags, IsOptimized, nullptr, + TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - Flags, IsOptimized, TemplateParams, - Declaration, getTuple())); + ThisAdjustment, Flags, IsOptimized, Unit, + getTuple(), Declaration, Variables)); + EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, + Type, IsLocalToUnit, IsDefinition, ScopeLine, + ContainingType, Virtuality, VirtualIndex, + ThisAdjustment, Flags, IsOptimized, Unit, + TemplateParams, getSubprogram(), Variables)); + EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, + Type, IsLocalToUnit, IsDefinition, ScopeLine, + ContainingType, Virtuality, VirtualIndex, + ThisAdjustment, Flags, IsOptimized, Unit, + TemplateParams, Declaration, getTuple())); TempDISubprogram Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1697,7 +1702,7 @@ typedef MetadataTest DITemplateTypeParameterTest; TEST_F(DITemplateTypeParameterTest, get) { StringRef Name = "template"; - DITypeRef Type = getBasicType("basic"); + DIType *Type = getBasicType("basic"); auto *N = DITemplateTypeParameter::get(Context, Name, Type); @@ -1719,7 +1724,7 @@ typedef MetadataTest DITemplateValueParameterTest; TEST_F(DITemplateValueParameterTest, get) { unsigned Tag = dwarf::DW_TAG_template_value_parameter; StringRef Name = "template"; - DITypeRef Type = getBasicType("basic"); + DIType *Type = getBasicType("basic"); Metadata *Value = getConstantAsMetadata(); auto *N = DITemplateValueParameter::get(Context, Tag, Name, Type, Value); @@ -1751,7 +1756,7 @@ TEST_F(DIGlobalVariableTest, get) { StringRef LinkageName = "linkage"; DIFile *File = getFile(); unsigned Line = 5; - DITypeRef Type = getDerivedType(); + DIType *Type = getDerivedType(); bool IsLocalToUnit = false; bool IsDefinition = true; Constant *Variable = getConstant(); @@ -1824,9 +1829,10 @@ TEST_F(DILocalVariableTest, get) { StringRef Name = "name"; DIFile *File = getFile(); unsigned Line = 5; - DITypeRef Type = getDerivedType(); + DIType *Type = getDerivedType(); unsigned Arg = 6; unsigned Flags = 7; + unsigned NotFlags = (~Flags) & ((1 << 16) - 1); auto *N = DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg, Flags); @@ -1857,7 +1863,7 @@ TEST_F(DILocalVariableTest, get) { EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg + 1, Flags)); EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg, - ~Flags)); + NotFlags)); TempDILocalVariable Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1943,7 +1949,7 @@ TEST_F(DIObjCPropertyTest, get) { StringRef GetterName = "getter"; StringRef SetterName = "setter"; unsigned Attributes = 7; - DITypeRef Type = getBasicType("basic"); + DIType *Type = getBasicType("basic"); auto *N = DIObjCProperty::get(Context, Name, File, Line, GetterName, SetterName, Attributes, Type); @@ -1984,7 +1990,7 @@ typedef MetadataTest DIImportedEntityTest; TEST_F(DIImportedEntityTest, get) { unsigned Tag = dwarf::DW_TAG_imported_module; DIScope *Scope = getSubprogram(); - DINodeRef Entity = getCompositeType(); + DINode *Entity = getCompositeType(); unsigned Line = 5; StringRef Name = "name"; @@ -2073,10 +2079,27 @@ TEST_F(ValueAsMetadataTest, UpdatesOnRAUW) { EXPECT_TRUE(MD->getValue() == GV1.get()); } +TEST_F(ValueAsMetadataTest, TempTempReplacement) { + // Create a constant. + ConstantAsMetadata *CI = + ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0))); + + auto Temp1 = MDTuple::getTemporary(Context, None); + auto Temp2 = MDTuple::getTemporary(Context, {CI}); + auto *N = MDTuple::get(Context, {Temp1.get()}); + + // Test replacing a temporary node with another temporary node. + Temp1->replaceAllUsesWith(Temp2.get()); + EXPECT_EQ(N->getOperand(0), Temp2.get()); + + // Clean up Temp2 for teardown. + Temp2->replaceAllUsesWith(nullptr); +} + TEST_F(ValueAsMetadataTest, CollidingDoubleUpdates) { // Create a constant. - ConstantAsMetadata *CI = ConstantAsMetadata::get( - ConstantInt::get(getGlobalContext(), APInt(8, 0))); + ConstantAsMetadata *CI = + ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0))); // Create a temporary to prevent nodes from resolving. auto Temp = MDTuple::getTemporary(Context, None); @@ -2238,44 +2261,23 @@ TEST_F(FunctionAttachmentTest, getAll) { EXPECT_EQ(T2, MDs[3].second); } -TEST_F(FunctionAttachmentTest, dropUnknownMetadata) { - Function *F = getFunction("foo"); - - MDTuple *T1 = getTuple(); - MDTuple *T2 = getTuple(); - MDTuple *P = getTuple(); - DISubprogram *SP = getSubprogram(); - - F->setMetadata("other1", T1); - F->setMetadata(LLVMContext::MD_dbg, SP); - F->setMetadata("other2", T2); - F->setMetadata(LLVMContext::MD_prof, P); - - unsigned Known[] = {Context.getMDKindID("other2"), LLVMContext::MD_prof}; - F->dropUnknownMetadata(Known); - - EXPECT_EQ(T2, F->getMetadata("other2")); - EXPECT_EQ(P, F->getMetadata(LLVMContext::MD_prof)); - EXPECT_EQ(nullptr, F->getMetadata("other1")); - EXPECT_EQ(nullptr, F->getMetadata(LLVMContext::MD_dbg)); - - F->setMetadata("other2", nullptr); - F->setMetadata(LLVMContext::MD_prof, nullptr); - EXPECT_FALSE(F->hasMetadata()); -} - TEST_F(FunctionAttachmentTest, Verifier) { Function *F = getFunction("foo"); F->setMetadata("attach", getTuple()); + F->setIsMaterializable(true); + + // Confirm this is materializable. + ASSERT_TRUE(F->isMaterializable()); - // Confirm this has no body. - ASSERT_TRUE(F->empty()); + // Materializable functions cannot have metadata attachments. + EXPECT_TRUE(verifyFunction(*F)); - // Functions without a body cannot have metadata attachments (they also can't - // be verified directly, so check that the module fails to verify). - EXPECT_TRUE(verifyModule(*F->getParent())); + // Function declarations can. + F->setIsMaterializable(false); + EXPECT_FALSE(verifyModule(*F->getParent())); + EXPECT_FALSE(verifyFunction(*F)); - // Functions with a body can. + // So can definitions. (void)new UnreachableInst(Context, BasicBlock::Create(Context, "bb", F)); EXPECT_FALSE(verifyModule(*F->getParent())); EXPECT_FALSE(verifyFunction(*F)); @@ -2301,4 +2303,81 @@ TEST_F(FunctionAttachmentTest, SubprogramAttachment) { EXPECT_EQ(SP, F->getMetadata(LLVMContext::MD_dbg)); } +typedef MetadataTest DistinctMDOperandPlaceholderTest; +TEST_F(DistinctMDOperandPlaceholderTest, getID) { + EXPECT_EQ(7u, DistinctMDOperandPlaceholder(7).getID()); +} + +TEST_F(DistinctMDOperandPlaceholderTest, replaceUseWith) { + // Set up some placeholders. + DistinctMDOperandPlaceholder PH0(7); + DistinctMDOperandPlaceholder PH1(3); + DistinctMDOperandPlaceholder PH2(0); + Metadata *Ops[] = {&PH0, &PH1, &PH2}; + auto *D = MDTuple::getDistinct(Context, Ops); + ASSERT_EQ(&PH0, D->getOperand(0)); + ASSERT_EQ(&PH1, D->getOperand(1)); + ASSERT_EQ(&PH2, D->getOperand(2)); + + // Replace them. + auto *N0 = MDTuple::get(Context, None); + auto *N1 = MDTuple::get(Context, N0); + PH0.replaceUseWith(N0); + PH1.replaceUseWith(N1); + PH2.replaceUseWith(nullptr); + EXPECT_EQ(N0, D->getOperand(0)); + EXPECT_EQ(N1, D->getOperand(1)); + EXPECT_EQ(nullptr, D->getOperand(2)); +} + +TEST_F(DistinctMDOperandPlaceholderTest, replaceUseWithNoUser) { + // There is no user, but we can still call replace. + DistinctMDOperandPlaceholder(7).replaceUseWith(MDTuple::get(Context, None)); +} + +#ifndef NDEBUG +#ifdef GTEST_HAS_DEATH_TEST +TEST_F(DistinctMDOperandPlaceholderTest, MetadataAsValue) { + // This shouldn't crash. + DistinctMDOperandPlaceholder PH(7); + EXPECT_DEATH(MetadataAsValue::get(Context, &PH), + "Unexpected callback to owner"); } + +TEST_F(DistinctMDOperandPlaceholderTest, UniquedMDNode) { + // This shouldn't crash. + DistinctMDOperandPlaceholder PH(7); + EXPECT_DEATH(MDTuple::get(Context, &PH), "Unexpected callback to owner"); +} + +TEST_F(DistinctMDOperandPlaceholderTest, SecondDistinctMDNode) { + // This shouldn't crash. + DistinctMDOperandPlaceholder PH(7); + MDTuple::getDistinct(Context, &PH); + EXPECT_DEATH(MDTuple::getDistinct(Context, &PH), + "Placeholders can only be used once"); +} + +TEST_F(DistinctMDOperandPlaceholderTest, TrackingMDRefAndDistinctMDNode) { + // TrackingMDRef doesn't install an owner callback, so it can't be detected + // as an invalid use. However, using a placeholder in a TrackingMDRef *and* + // a distinct node isn't possible and we should assert. + // + // (There's no positive test for using TrackingMDRef because it's not a + // useful thing to do.) + { + DistinctMDOperandPlaceholder PH(7); + MDTuple::getDistinct(Context, &PH); + EXPECT_DEATH(TrackingMDRef Ref(&PH), "Placeholders can only be used once"); + } + { + DistinctMDOperandPlaceholder PH(7); + TrackingMDRef Ref(&PH); + EXPECT_DEATH(MDTuple::getDistinct(Context, &PH), + "Placeholders can only be used once"); + } +} +#endif +#endif + +} // end namespace diff --git a/unittests/IR/PassManagerTest.cpp b/unittests/IR/PassManagerTest.cpp index 41af0b0bd25c..c2ac863260ae 100644 --- a/unittests/IR/PassManagerTest.cpp +++ b/unittests/IR/PassManagerTest.cpp @@ -19,23 +19,17 @@ using namespace llvm; namespace { -class TestFunctionAnalysis { +class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> { public: struct Result { Result(int Count) : InstructionCount(Count) {} int InstructionCount; }; - /// \brief Returns an opaque, unique ID for this pass type. - static void *ID() { return (void *)&PassID; } - - /// \brief Returns the name of the analysis. - static StringRef name() { return "TestFunctionAnalysis"; } - TestFunctionAnalysis(int &Runs) : Runs(Runs) {} /// \brief Run the analysis pass over the function and return a result. - Result run(Function &F, FunctionAnalysisManager *AM) { + Result run(Function &F, FunctionAnalysisManager &AM) { ++Runs; int Count = 0; for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) @@ -46,7 +40,7 @@ public: } private: - /// \brief Private static data to provide unique ID. + friend AnalysisInfoMixin<TestFunctionAnalysis>; static char PassID; int &Runs; @@ -54,20 +48,16 @@ private: char TestFunctionAnalysis::PassID; -class TestModuleAnalysis { +class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> { public: struct Result { Result(int Count) : FunctionCount(Count) {} int FunctionCount; }; - static void *ID() { return (void *)&PassID; } - - static StringRef name() { return "TestModuleAnalysis"; } - TestModuleAnalysis(int &Runs) : Runs(Runs) {} - Result run(Module &M, ModuleAnalysisManager *AM) { + Result run(Module &M, ModuleAnalysisManager &AM) { ++Runs; int Count = 0; for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) @@ -76,6 +66,7 @@ public: } private: + friend AnalysisInfoMixin<TestModuleAnalysis>; static char PassID; int &Runs; @@ -83,40 +74,37 @@ private: char TestModuleAnalysis::PassID; -struct TestModulePass { +struct TestModulePass : PassInfoMixin<TestModulePass> { TestModulePass(int &RunCount) : RunCount(RunCount) {} - PreservedAnalyses run(Module &M) { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { ++RunCount; return PreservedAnalyses::none(); } - static StringRef name() { return "TestModulePass"; } - int &RunCount; }; -struct TestPreservingModulePass { - PreservedAnalyses run(Module &M) { return PreservedAnalyses::all(); } - - static StringRef name() { return "TestPreservingModulePass"; } +struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { + return PreservedAnalyses::all(); + } }; -struct TestMinPreservingModulePass { - PreservedAnalyses run(Module &M, ModuleAnalysisManager *AM) { +struct TestMinPreservingModulePass + : PassInfoMixin<TestMinPreservingModulePass> { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { PreservedAnalyses PA; // Force running an analysis. - (void)AM->getResult<TestModuleAnalysis>(M); + (void)AM.getResult<TestModuleAnalysis>(M); PA.preserve<FunctionAnalysisManagerModuleProxy>(); return PA; } - - static StringRef name() { return "TestMinPreservingModulePass"; } }; -struct TestFunctionPass { +struct TestFunctionPass : PassInfoMixin<TestFunctionPass> { TestFunctionPass(int &RunCount, int &AnalyzedInstrCount, int &AnalyzedFunctionCount, bool OnlyUseCachedResults = false) @@ -124,11 +112,11 @@ struct TestFunctionPass { AnalyzedFunctionCount(AnalyzedFunctionCount), OnlyUseCachedResults(OnlyUseCachedResults) {} - PreservedAnalyses run(Function &F, FunctionAnalysisManager *AM) { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { ++RunCount; const ModuleAnalysisManager &MAM = - AM->getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); + AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); if (TestModuleAnalysis::Result *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent())) AnalyzedFunctionCount += TMA->FunctionCount; @@ -136,19 +124,17 @@ struct TestFunctionPass { if (OnlyUseCachedResults) { // Hack to force the use of the cached interface. if (TestFunctionAnalysis::Result *AR = - AM->getCachedResult<TestFunctionAnalysis>(F)) + AM.getCachedResult<TestFunctionAnalysis>(F)) AnalyzedInstrCount += AR->InstructionCount; } else { // Typical path just runs the analysis as needed. - TestFunctionAnalysis::Result &AR = AM->getResult<TestFunctionAnalysis>(F); + TestFunctionAnalysis::Result &AR = AM.getResult<TestFunctionAnalysis>(F); AnalyzedInstrCount += AR.InstructionCount; } return PreservedAnalyses::all(); } - static StringRef name() { return "TestFunctionPass"; } - int &RunCount; int &AnalyzedInstrCount; int &AnalyzedFunctionCount; @@ -157,43 +143,42 @@ struct TestFunctionPass { // A test function pass that invalidates all function analyses for a function // with a specific name. -struct TestInvalidationFunctionPass { +struct TestInvalidationFunctionPass + : PassInfoMixin<TestInvalidationFunctionPass> { TestInvalidationFunctionPass(StringRef FunctionName) : Name(FunctionName) {} - PreservedAnalyses run(Function &F) { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { return F.getName() == Name ? PreservedAnalyses::none() : PreservedAnalyses::all(); } - static StringRef name() { return "TestInvalidationFunctionPass"; } - StringRef Name; }; -std::unique_ptr<Module> parseIR(const char *IR) { - LLVMContext &C = getGlobalContext(); +std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) { SMDiagnostic Err; - return parseAssemblyString(IR, Err, C); + return parseAssemblyString(IR, Err, Context); } class PassManagerTest : public ::testing::Test { protected: + LLVMContext Context; std::unique_ptr<Module> M; public: PassManagerTest() - : M(parseIR("define void @f() {\n" - "entry:\n" - " call void @g()\n" - " call void @h()\n" - " ret void\n" - "}\n" - "define void @g() {\n" - " ret void\n" - "}\n" - "define void @h() {\n" - " ret void\n" - "}\n")) {} + : M(parseIR(Context, "define void @f() {\n" + "entry:\n" + " call void @g()\n" + " call void @h()\n" + " ret void\n" + "}\n" + "define void @g() {\n" + " ret void\n" + "}\n" + "define void @h() {\n" + " ret void\n" + "}\n")) {} }; TEST_F(PassManagerTest, BasicPreservedAnalyses) { @@ -232,13 +217,13 @@ TEST_F(PassManagerTest, BasicPreservedAnalyses) { TEST_F(PassManagerTest, Basic) { FunctionAnalysisManager FAM; int FunctionAnalysisRuns = 0; - FAM.registerPass(TestFunctionAnalysis(FunctionAnalysisRuns)); + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); ModuleAnalysisManager MAM; int ModuleAnalysisRuns = 0; - MAM.registerPass(TestModuleAnalysis(ModuleAnalysisRuns)); - MAM.registerPass(FunctionAnalysisManagerModuleProxy(FAM)); - FAM.registerPass(ModuleAnalysisManagerFunctionProxy(MAM)); + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); + MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); ModulePassManager MPM; @@ -253,8 +238,8 @@ TEST_F(PassManagerTest, Basic) { { // Pointless scope to test move assignment. FunctionPassManager NestedFPM; - NestedFPM.addPass(TestFunctionPass(FunctionPassRunCount1, AnalyzedInstrCount1, - AnalyzedFunctionCount1)); + NestedFPM.addPass(TestFunctionPass( + FunctionPassRunCount1, AnalyzedInstrCount1, AnalyzedFunctionCount1)); FPM = std::move(NestedFPM); } NestedMPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); @@ -315,7 +300,7 @@ TEST_F(PassManagerTest, Basic) { MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } - MPM.run(*M, &MAM); + MPM.run(*M, MAM); // Validate module pass counters. EXPECT_EQ(1, ModulePassRunCount); diff --git a/unittests/IR/PatternMatch.cpp b/unittests/IR/PatternMatch.cpp index f3a27b8d2509..1121d6554db7 100644 --- a/unittests/IR/PatternMatch.cpp +++ b/unittests/IR/PatternMatch.cpp @@ -35,7 +35,7 @@ struct PatternMatchTest : ::testing::Test { std::unique_ptr<Module> M; Function *F; BasicBlock *BB; - IRBuilder<true, NoFolder> IRB; + IRBuilder<NoFolder> IRB; PatternMatchTest() : M(new Module("PatternMatchTestModule", Ctx)), diff --git a/unittests/IR/TypeBuilderTest.cpp b/unittests/IR/TypeBuilderTest.cpp index b7b3e45e35ed..f2dccac001a4 100644 --- a/unittests/IR/TypeBuilderTest.cpp +++ b/unittests/IR/TypeBuilderTest.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/TypeBuilder.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/IR/LLVMContext.h" #include "gtest/gtest.h" @@ -17,141 +16,175 @@ using namespace llvm; namespace { TEST(TypeBuilderTest, Void) { - EXPECT_EQ(Type::getVoidTy(getGlobalContext()), (TypeBuilder<void, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getVoidTy(getGlobalContext()), (TypeBuilder<void, false>::get(getGlobalContext()))); + LLVMContext Context; + EXPECT_EQ(Type::getVoidTy(Context), (TypeBuilder<void, true>::get(Context))); + EXPECT_EQ(Type::getVoidTy(Context), (TypeBuilder<void, false>::get(Context))); // Special cases for C compatibility: - EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()), - (TypeBuilder<void*, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()), - (TypeBuilder<const void*, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()), - (TypeBuilder<volatile void*, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()), - (TypeBuilder<const volatile void*, false>::get( - getGlobalContext()))); + EXPECT_EQ(Type::getInt8PtrTy(Context), + (TypeBuilder<void *, false>::get(Context))); + EXPECT_EQ(Type::getInt8PtrTy(Context), + (TypeBuilder<const void *, false>::get(Context))); + EXPECT_EQ(Type::getInt8PtrTy(Context), + (TypeBuilder<volatile void *, false>::get(Context))); + EXPECT_EQ(Type::getInt8PtrTy(Context), + (TypeBuilder<const volatile void *, false>::get(Context))); } TEST(TypeBuilderTest, HostIntegers) { - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), (TypeBuilder<int8_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), (TypeBuilder<uint8_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt16Ty(getGlobalContext()), (TypeBuilder<int16_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt16Ty(getGlobalContext()), (TypeBuilder<uint16_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (TypeBuilder<int32_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (TypeBuilder<uint32_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt64Ty(getGlobalContext()), (TypeBuilder<int64_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt64Ty(getGlobalContext()), (TypeBuilder<uint64_t, false>::get(getGlobalContext()))); - - EXPECT_EQ(IntegerType::get(getGlobalContext(), sizeof(size_t) * CHAR_BIT), - (TypeBuilder<size_t, false>::get(getGlobalContext()))); - EXPECT_EQ(IntegerType::get(getGlobalContext(), sizeof(ptrdiff_t) * CHAR_BIT), - (TypeBuilder<ptrdiff_t, false>::get(getGlobalContext()))); + LLVMContext Context; + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<int8_t, false>::get(Context))); + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<uint8_t, false>::get(Context))); + EXPECT_EQ(Type::getInt16Ty(Context), + (TypeBuilder<int16_t, false>::get(Context))); + EXPECT_EQ(Type::getInt16Ty(Context), + (TypeBuilder<uint16_t, false>::get(Context))); + EXPECT_EQ(Type::getInt32Ty(Context), + (TypeBuilder<int32_t, false>::get(Context))); + EXPECT_EQ(Type::getInt32Ty(Context), + (TypeBuilder<uint32_t, false>::get(Context))); + EXPECT_EQ(Type::getInt64Ty(Context), + (TypeBuilder<int64_t, false>::get(Context))); + EXPECT_EQ(Type::getInt64Ty(Context), + (TypeBuilder<uint64_t, false>::get(Context))); + + EXPECT_EQ(IntegerType::get(Context, sizeof(size_t) * CHAR_BIT), + (TypeBuilder<size_t, false>::get(Context))); + EXPECT_EQ(IntegerType::get(Context, sizeof(ptrdiff_t) * CHAR_BIT), + (TypeBuilder<ptrdiff_t, false>::get(Context))); } TEST(TypeBuilderTest, CrossCompilableIntegers) { - EXPECT_EQ(IntegerType::get(getGlobalContext(), 1), (TypeBuilder<types::i<1>, true>::get(getGlobalContext()))); - EXPECT_EQ(IntegerType::get(getGlobalContext(), 1), (TypeBuilder<types::i<1>, false>::get(getGlobalContext()))); - EXPECT_EQ(IntegerType::get(getGlobalContext(), 72), (TypeBuilder<types::i<72>, true>::get(getGlobalContext()))); - EXPECT_EQ(IntegerType::get(getGlobalContext(), 72), (TypeBuilder<types::i<72>, false>::get(getGlobalContext()))); + LLVMContext Context; + EXPECT_EQ(IntegerType::get(Context, 1), + (TypeBuilder<types::i<1>, true>::get(Context))); + EXPECT_EQ(IntegerType::get(Context, 1), + (TypeBuilder<types::i<1>, false>::get(Context))); + EXPECT_EQ(IntegerType::get(Context, 72), + (TypeBuilder<types::i<72>, true>::get(Context))); + EXPECT_EQ(IntegerType::get(Context, 72), + (TypeBuilder<types::i<72>, false>::get(Context))); } TEST(TypeBuilderTest, Float) { - EXPECT_EQ(Type::getFloatTy(getGlobalContext()), (TypeBuilder<float, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getDoubleTy(getGlobalContext()), (TypeBuilder<double, false>::get(getGlobalContext()))); + LLVMContext Context; + EXPECT_EQ(Type::getFloatTy(Context), + (TypeBuilder<float, false>::get(Context))); + EXPECT_EQ(Type::getDoubleTy(Context), + (TypeBuilder<double, false>::get(Context))); // long double isn't supported yet. - EXPECT_EQ(Type::getFloatTy(getGlobalContext()), (TypeBuilder<types::ieee_float, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getFloatTy(getGlobalContext()), (TypeBuilder<types::ieee_float, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getDoubleTy(getGlobalContext()), (TypeBuilder<types::ieee_double, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getDoubleTy(getGlobalContext()), (TypeBuilder<types::ieee_double, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getX86_FP80Ty(getGlobalContext()), (TypeBuilder<types::x86_fp80, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getX86_FP80Ty(getGlobalContext()), (TypeBuilder<types::x86_fp80, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getFP128Ty(getGlobalContext()), (TypeBuilder<types::fp128, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getFP128Ty(getGlobalContext()), (TypeBuilder<types::fp128, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getPPC_FP128Ty(getGlobalContext()), (TypeBuilder<types::ppc_fp128, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getPPC_FP128Ty(getGlobalContext()), (TypeBuilder<types::ppc_fp128, false>::get(getGlobalContext()))); + EXPECT_EQ(Type::getFloatTy(Context), + (TypeBuilder<types::ieee_float, true>::get(Context))); + EXPECT_EQ(Type::getFloatTy(Context), + (TypeBuilder<types::ieee_float, false>::get(Context))); + EXPECT_EQ(Type::getDoubleTy(Context), + (TypeBuilder<types::ieee_double, true>::get(Context))); + EXPECT_EQ(Type::getDoubleTy(Context), + (TypeBuilder<types::ieee_double, false>::get(Context))); + EXPECT_EQ(Type::getX86_FP80Ty(Context), + (TypeBuilder<types::x86_fp80, true>::get(Context))); + EXPECT_EQ(Type::getX86_FP80Ty(Context), + (TypeBuilder<types::x86_fp80, false>::get(Context))); + EXPECT_EQ(Type::getFP128Ty(Context), + (TypeBuilder<types::fp128, true>::get(Context))); + EXPECT_EQ(Type::getFP128Ty(Context), + (TypeBuilder<types::fp128, false>::get(Context))); + EXPECT_EQ(Type::getPPC_FP128Ty(Context), + (TypeBuilder<types::ppc_fp128, true>::get(Context))); + EXPECT_EQ(Type::getPPC_FP128Ty(Context), + (TypeBuilder<types::ppc_fp128, false>::get(Context))); } TEST(TypeBuilderTest, Derived) { - EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(getGlobalContext())), - (TypeBuilder<int8_t**, false>::get(getGlobalContext()))); - EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 7), - (TypeBuilder<int8_t[7], false>::get(getGlobalContext()))); - EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 0), - (TypeBuilder<int8_t[], false>::get(getGlobalContext()))); - - EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(getGlobalContext())), - (TypeBuilder<types::i<8>**, false>::get(getGlobalContext()))); - EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 7), - (TypeBuilder<types::i<8>[7], false>::get(getGlobalContext()))); - EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 0), - (TypeBuilder<types::i<8>[], false>::get(getGlobalContext()))); - - EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(getGlobalContext())), - (TypeBuilder<types::i<8>**, true>::get(getGlobalContext()))); - EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 7), - (TypeBuilder<types::i<8>[7], true>::get(getGlobalContext()))); - EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 0), - (TypeBuilder<types::i<8>[], true>::get(getGlobalContext()))); - - - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<const int8_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<volatile int8_t, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<const volatile int8_t, false>::get(getGlobalContext()))); - - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<const types::i<8>, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<volatile types::i<8>, false>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<const volatile types::i<8>, false>::get(getGlobalContext()))); - - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<const types::i<8>, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<volatile types::i<8>, true>::get(getGlobalContext()))); - EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), - (TypeBuilder<const volatile types::i<8>, true>::get(getGlobalContext()))); - - EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()), - (TypeBuilder<const volatile int8_t*const volatile, false>::get(getGlobalContext()))); + LLVMContext Context; + EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(Context)), + (TypeBuilder<int8_t **, false>::get(Context))); + EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 7), + (TypeBuilder<int8_t[7], false>::get(Context))); + EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 0), + (TypeBuilder<int8_t[], false>::get(Context))); + + EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(Context)), + (TypeBuilder<types::i<8> **, false>::get(Context))); + EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 7), + (TypeBuilder<types::i<8>[7], false>::get(Context))); + EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 0), + (TypeBuilder<types::i<8>[], false>::get(Context))); + + EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(Context)), + (TypeBuilder<types::i<8> **, true>::get(Context))); + EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 7), + (TypeBuilder<types::i<8>[7], true>::get(Context))); + EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 0), + (TypeBuilder<types::i<8>[], true>::get(Context))); + + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<const int8_t, false>::get(Context))); + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<volatile int8_t, false>::get(Context))); + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<const volatile int8_t, false>::get(Context))); + + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<const types::i<8>, false>::get(Context))); + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<volatile types::i<8>, false>::get(Context))); + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<const volatile types::i<8>, false>::get(Context))); + + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<const types::i<8>, true>::get(Context))); + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<volatile types::i<8>, true>::get(Context))); + EXPECT_EQ(Type::getInt8Ty(Context), + (TypeBuilder<const volatile types::i<8>, true>::get(Context))); + + EXPECT_EQ(Type::getInt8PtrTy(Context), + (TypeBuilder<const volatile int8_t *const volatile, false>::get( + Context))); } TEST(TypeBuilderTest, Functions) { + LLVMContext Context; std::vector<Type*> params; - EXPECT_EQ(FunctionType::get(Type::getVoidTy(getGlobalContext()), params, false), - (TypeBuilder<void(), true>::get(getGlobalContext()))); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true), - (TypeBuilder<int8_t(...), false>::get(getGlobalContext()))); - params.push_back(TypeBuilder<int32_t*, false>::get(getGlobalContext())); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false), - (TypeBuilder<int8_t(const int32_t*), false>::get(getGlobalContext()))); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true), - (TypeBuilder<int8_t(const int32_t*, ...), false>::get(getGlobalContext()))); - params.push_back(TypeBuilder<char*, false>::get(getGlobalContext())); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false), - (TypeBuilder<int8_t(int32_t*, void*), false>::get(getGlobalContext()))); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true), - (TypeBuilder<int8_t(int32_t*, char*, ...), false>::get(getGlobalContext()))); - params.push_back(TypeBuilder<char, false>::get(getGlobalContext())); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false), - (TypeBuilder<int8_t(int32_t*, void*, char), false>::get(getGlobalContext()))); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true), - (TypeBuilder<int8_t(int32_t*, char*, char, ...), false>::get(getGlobalContext()))); - params.push_back(TypeBuilder<char, false>::get(getGlobalContext())); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false), - (TypeBuilder<int8_t(int32_t*, void*, char, char), false>::get(getGlobalContext()))); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true), - (TypeBuilder<int8_t(int32_t*, char*, char, char, ...), - false>::get(getGlobalContext()))); - params.push_back(TypeBuilder<char, false>::get(getGlobalContext())); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false), - (TypeBuilder<int8_t(int32_t*, void*, char, char, char), - false>::get(getGlobalContext()))); - EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true), - (TypeBuilder<int8_t(int32_t*, char*, char, char, char, ...), - false>::get(getGlobalContext()))); + EXPECT_EQ(FunctionType::get(Type::getVoidTy(Context), params, false), + (TypeBuilder<void(), true>::get(Context))); + EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true), + (TypeBuilder<int8_t(...), false>::get(Context))); + params.push_back(TypeBuilder<int32_t *, false>::get(Context)); + EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, false), + (TypeBuilder<int8_t(const int32_t *), false>::get(Context))); + EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true), + (TypeBuilder<int8_t(const int32_t *, ...), false>::get(Context))); + params.push_back(TypeBuilder<char *, false>::get(Context)); + EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, false), + (TypeBuilder<int8_t(int32_t *, void *), false>::get(Context))); + EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true), + (TypeBuilder<int8_t(int32_t *, char *, ...), false>::get(Context))); + params.push_back(TypeBuilder<char, false>::get(Context)); + EXPECT_EQ( + FunctionType::get(Type::getInt8Ty(Context), params, false), + (TypeBuilder<int8_t(int32_t *, void *, char), false>::get(Context))); + EXPECT_EQ( + FunctionType::get(Type::getInt8Ty(Context), params, true), + (TypeBuilder<int8_t(int32_t *, char *, char, ...), false>::get(Context))); + params.push_back(TypeBuilder<char, false>::get(Context)); + EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, false), + (TypeBuilder<int8_t(int32_t *, void *, char, char), false>::get( + Context))); + EXPECT_EQ( + FunctionType::get(Type::getInt8Ty(Context), params, true), + (TypeBuilder<int8_t(int32_t *, char *, char, char, ...), false>::get( + Context))); + params.push_back(TypeBuilder<char, false>::get(Context)); + EXPECT_EQ( + FunctionType::get(Type::getInt8Ty(Context), params, false), + (TypeBuilder<int8_t(int32_t *, void *, char, char, char), false>::get( + Context))); + EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true), + (TypeBuilder<int8_t(int32_t *, char *, char, char, char, ...), + false>::get(Context))); } TEST(TypeBuilderTest, Context) { @@ -230,24 +263,24 @@ public: namespace { TEST(TypeBuilderTest, Extensions) { + LLVMContext Context; EXPECT_EQ(PointerType::getUnqual(StructType::get( - TypeBuilder<int, false>::get(getGlobalContext()), - TypeBuilder<int*, false>::get(getGlobalContext()), - TypeBuilder<void*[], false>::get(getGlobalContext()), - (void*)nullptr)), - (TypeBuilder<MyType*, false>::get(getGlobalContext()))); - EXPECT_EQ(PointerType::getUnqual(StructType::get( - TypeBuilder<types::i<32>, false>::get(getGlobalContext()), - TypeBuilder<types::i<32>*, false>::get(getGlobalContext()), - TypeBuilder<types::i<8>*[], false>::get(getGlobalContext()), - (void*)nullptr)), - (TypeBuilder<MyPortableType*, false>::get(getGlobalContext()))); - EXPECT_EQ(PointerType::getUnqual(StructType::get( - TypeBuilder<types::i<32>, false>::get(getGlobalContext()), - TypeBuilder<types::i<32>*, false>::get(getGlobalContext()), - TypeBuilder<types::i<8>*[], false>::get(getGlobalContext()), - (void*)nullptr)), - (TypeBuilder<MyPortableType*, true>::get(getGlobalContext()))); + TypeBuilder<int, false>::get(Context), + TypeBuilder<int *, false>::get(Context), + TypeBuilder<void *[], false>::get(Context), (void *)nullptr)), + (TypeBuilder<MyType *, false>::get(Context))); + EXPECT_EQ( + PointerType::getUnqual(StructType::get( + TypeBuilder<types::i<32>, false>::get(Context), + TypeBuilder<types::i<32> *, false>::get(Context), + TypeBuilder<types::i<8> *[], false>::get(Context), (void *)nullptr)), + (TypeBuilder<MyPortableType *, false>::get(Context))); + EXPECT_EQ( + PointerType::getUnqual(StructType::get( + TypeBuilder<types::i<32>, false>::get(Context), + TypeBuilder<types::i<32> *, false>::get(Context), + TypeBuilder<types::i<8> *[], false>::get(Context), (void *)nullptr)), + (TypeBuilder<MyPortableType *, true>::get(Context))); } } // anonymous namespace diff --git a/unittests/IR/UserTest.cpp b/unittests/IR/UserTest.cpp index 8d488389448a..7d875aa80d48 100644 --- a/unittests/IR/UserTest.cpp +++ b/unittests/IR/UserTest.cpp @@ -94,9 +94,9 @@ TEST(UserTest, ValueOpIteration) { } TEST(UserTest, PersonalityUser) { - Module M("", getGlobalContext()); - FunctionType *RetVoidTy = - FunctionType::get(Type::getVoidTy(getGlobalContext()), false); + LLVMContext Context; + Module M("", Context); + FunctionType *RetVoidTy = FunctionType::get(Type::getVoidTy(Context), false); Function *PersonalityF = Function::Create( RetVoidTy, GlobalValue::ExternalLinkage, "PersonalityFn", &M); Function *TestF = diff --git a/unittests/IR/ValueHandleTest.cpp b/unittests/IR/ValueHandleTest.cpp index e1d598bbc58b..59cd9d7ba37a 100644 --- a/unittests/IR/ValueHandleTest.cpp +++ b/unittests/IR/ValueHandleTest.cpp @@ -20,13 +20,13 @@ namespace { class ValueHandle : public testing::Test { protected: + LLVMContext Context; Constant *ConstantV; std::unique_ptr<BitCastInst> BitcastV; - ValueHandle() : - ConstantV(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0)), - BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext()))) { - } + ValueHandle() + : ConstantV(ConstantInt::get(Type::getInt32Ty(Context), 0)), + BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(Context))) {} }; class ConcreteCallbackVH final : public CallbackVH { @@ -42,8 +42,8 @@ TEST_F(ValueHandle, WeakVH_BasicOperation) { // Make sure I can call a method on the underlying Value. It // doesn't matter which method. - EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), WVH->getType()); - EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (*WVH).getType()); + EXPECT_EQ(Type::getInt32Ty(Context), WVH->getType()); + EXPECT_EQ(Type::getInt32Ty(Context), (*WVH).getType()); } TEST_F(ValueHandle, WeakVH_Comparisons) { @@ -197,8 +197,8 @@ TEST_F(ValueHandle, CallbackVH_BasicOperation) { // Make sure I can call a method on the underlying Value. It // doesn't matter which method. - EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), CVH->getType()); - EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (*CVH).getType()); + EXPECT_EQ(Type::getInt32Ty(Context), CVH->getType()); + EXPECT_EQ(Type::getInt32Ty(Context), (*CVH).getType()); } TEST_F(ValueHandle, CallbackVH_Comparisons) { @@ -297,15 +297,17 @@ TEST_F(ValueHandle, CallbackVH_DeletionCanRAUW) { Value *AURWArgument; LLVMContext *Context; - RecoveringVH() : DeletedCalls(0), AURWArgument(nullptr), - Context(&getGlobalContext()) {} - RecoveringVH(Value *V) - : CallbackVH(V), DeletedCalls(0), AURWArgument(nullptr), - Context(&getGlobalContext()) {} + RecoveringVH(LLVMContext &TheContext) + : DeletedCalls(0), AURWArgument(nullptr), Context(&TheContext) {} + + RecoveringVH(LLVMContext &TheContext, Value *V) + : CallbackVH(V), DeletedCalls(0), AURWArgument(nullptr), + Context(&TheContext) {} private: void deleted() override { - getValPtr()->replaceAllUsesWith(Constant::getNullValue(Type::getInt32Ty(getGlobalContext()))); + getValPtr()->replaceAllUsesWith( + Constant::getNullValue(Type::getInt32Ty(*Context))); setValPtr(nullptr); } void allUsesReplacedWith(Value *new_value) override { @@ -318,15 +320,15 @@ TEST_F(ValueHandle, CallbackVH_DeletionCanRAUW) { // Normally, if a value has uses, deleting it will crash. However, we can use // a CallbackVH to remove the uses before the check for no uses. - RecoveringVH RVH; - RVH = BitcastV.get(); - std::unique_ptr<BinaryOperator> BitcastUser( - BinaryOperator::CreateAdd(RVH, - Constant::getNullValue(Type::getInt32Ty(getGlobalContext())))); + RecoveringVH RVH(Context); + RVH = RecoveringVH(Context, BitcastV.get()); + std::unique_ptr<BinaryOperator> BitcastUser(BinaryOperator::CreateAdd( + RVH, Constant::getNullValue(Type::getInt32Ty(Context)))); EXPECT_EQ(BitcastV.get(), BitcastUser->getOperand(0)); BitcastV.reset(); // Would crash without the ValueHandler. - EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(getGlobalContext())), RVH.AURWArgument); - EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(getGlobalContext())), + EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(Context)), + RVH.AURWArgument); + EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(Context)), BitcastUser->getOperand(0)); } diff --git a/unittests/IR/ValueMapTest.cpp b/unittests/IR/ValueMapTest.cpp index 1431a8d87de4..28633b44b11d 100644 --- a/unittests/IR/ValueMapTest.cpp +++ b/unittests/IR/ValueMapTest.cpp @@ -22,15 +22,15 @@ namespace { template<typename T> class ValueMapTest : public testing::Test { protected: + LLVMContext Context; Constant *ConstantV; std::unique_ptr<BitCastInst> BitcastV; std::unique_ptr<BinaryOperator> AddV; - ValueMapTest() : - ConstantV(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0)), - BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext()))), - AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) { - } + ValueMapTest() + : ConstantV(ConstantInt::get(Type::getInt32Ty(Context), 0)), + BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(Context))), + AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) {} }; // Run everything on Value*, a subtype to make sure that casting works as @@ -292,4 +292,4 @@ TYPED_TEST(ValueMapTest, SurvivesModificationByConfig) { EXPECT_EQ(0u, VM.count(this->AddV.get())); } -} +} // end namespace diff --git a/unittests/IR/ValueTest.cpp b/unittests/IR/ValueTest.cpp index 9cf1306dae67..607b7a1bd2c9 100644 --- a/unittests/IR/ValueTest.cpp +++ b/unittests/IR/ValueTest.cpp @@ -45,7 +45,7 @@ TEST(ValueTest, UsedInBasicBlock) { } TEST(GlobalTest, CreateAddressSpace) { - LLVMContext &Ctx = getGlobalContext(); + LLVMContext Ctx; std::unique_ptr<Module> M(new Module("TestModule", Ctx)); Type *Int8Ty = Type::getInt8Ty(Ctx); Type *Int32Ty = Type::getInt32Ty(Ctx); @@ -92,7 +92,7 @@ TEST(GlobalTest, CreateAddressSpace) { #ifdef GTEST_HAS_DEATH_TEST #ifndef NDEBUG TEST(GlobalTest, AlignDeath) { - LLVMContext &Ctx = getGlobalContext(); + LLVMContext Ctx; std::unique_ptr<Module> M(new Module("TestModule", Ctx)); Type *Int32Ty = Type::getInt32Ty(Ctx); GlobalVariable *Var = diff --git a/unittests/IR/VerifierTest.cpp b/unittests/IR/VerifierTest.cpp index 4e94b4375f92..c33c92a6f7c5 100644 --- a/unittests/IR/VerifierTest.cpp +++ b/unittests/IR/VerifierTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests ------------===// +//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests --*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,22 +7,24 @@ // //===----------------------------------------------------------------------===// -#include "llvm/IR/Verifier.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" #include "gtest/gtest.h" namespace llvm { namespace { TEST(VerifierTest, Branch_i1) { - LLVMContext &C = getGlobalContext(); + LLVMContext C; Module M("M", C); FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false); Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy)); @@ -45,7 +47,7 @@ TEST(VerifierTest, Branch_i1) { } TEST(VerifierTest, InvalidRetAttribute) { - LLVMContext &C = getGlobalContext(); + LLVMContext C; Module M("M", C); FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false); Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy)); @@ -61,10 +63,10 @@ TEST(VerifierTest, InvalidRetAttribute) { } TEST(VerifierTest, CrossModuleRef) { - LLVMContext &C = getGlobalContext(); + LLVMContext C; Module M1("M1", C); Module M2("M2", C); - Module M3("M2", C); + Module M3("M3", C); FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false); Function *F1 = cast<Function>(M1.getOrInsertFunction("foo1", FTy)); Function *F2 = cast<Function>(M2.getOrInsertFunction("foo2", FTy)); @@ -86,7 +88,21 @@ TEST(VerifierTest, CrossModuleRef) { std::string Error; raw_string_ostream ErrorOS(Error); - EXPECT_FALSE(verifyModule(M2, &ErrorOS)); + EXPECT_TRUE(verifyModule(M2, &ErrorOS)); + EXPECT_TRUE(StringRef(ErrorOS.str()) + .equals("Global is used by function in a different module\n" + "i32 ()* @foo2\n" + "; ModuleID = 'M2'\n" + "i32 ()* @foo3\n" + "; ModuleID = 'M3'\n" + "Global is referenced in a different module!\n" + "i32 ()* @foo2\n" + "; ModuleID = 'M2'\n" + " %call = call i32 @foo2()\n" + "i32 ()* @foo1\n" + "; ModuleID = 'M1'\n")); + + Error.clear(); EXPECT_TRUE(verifyModule(M1, &ErrorOS)); EXPECT_TRUE(StringRef(ErrorOS.str()).equals( "Referencing function in another module!\n" @@ -105,7 +121,104 @@ TEST(VerifierTest, CrossModuleRef) { F3->eraseFromParent(); } +TEST(VerifierTest, CrossModuleMetadataRef) { + LLVMContext C; + Module M1("M1", C); + Module M2("M2", C); + GlobalVariable *newGV = + new GlobalVariable(M1, Type::getInt8Ty(C), false, + GlobalVariable::ExternalLinkage, nullptr, + "Some Global"); + + DIBuilder dbuilder(M2); + auto CU = dbuilder.createCompileUnit(dwarf::DW_LANG_Julia, "test.jl", ".", + "unittest", false, "", 0); + auto File = dbuilder.createFile("test.jl", "."); + auto Ty = dbuilder.createBasicType("Int8", 8, 8, dwarf::DW_ATE_signed); + dbuilder.createGlobalVariable(CU, "_SOME_GLOBAL", "_SOME_GLOBAL", File, 1, Ty, + false, newGV); + dbuilder.finalize(); + std::string Error; + raw_string_ostream ErrorOS(Error); + EXPECT_TRUE(verifyModule(M2, &ErrorOS)); + EXPECT_TRUE(StringRef(ErrorOS.str()) + .startswith("Referencing global in another module!")); +} +TEST(VerifierTest, InvalidVariableLinkage) { + LLVMContext C; + Module M("M", C); + new GlobalVariable(M, Type::getInt8Ty(C), false, + GlobalValue::LinkOnceODRLinkage, nullptr, "Some Global"); + std::string Error; + raw_string_ostream ErrorOS(Error); + EXPECT_TRUE(verifyModule(M, &ErrorOS)); + EXPECT_TRUE( + StringRef(ErrorOS.str()).startswith("Global is external, but doesn't " + "have external or weak linkage!")); } + +TEST(VerifierTest, InvalidFunctionLinkage) { + LLVMContext C; + Module M("M", C); + + FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false); + Function::Create(FTy, GlobalValue::LinkOnceODRLinkage, "foo", &M); + std::string Error; + raw_string_ostream ErrorOS(Error); + EXPECT_TRUE(verifyModule(M, &ErrorOS)); + EXPECT_TRUE( + StringRef(ErrorOS.str()).startswith("Global is external, but doesn't " + "have external or weak linkage!")); } + +#ifndef _MSC_VER +// FIXME: This test causes an ICE in MSVC 2013. +TEST(VerifierTest, StripInvalidDebugInfo) { + LLVMContext C; + Module M("M", C); + DIBuilder DIB(M); + DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/", + "unittest", false, "", 0); + DIB.finalize(); + EXPECT_FALSE(verifyModule(M)); + + // Now break it. + auto *File = DIB.createFile("not-a-CU.f", "."); + NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu"); + NMD->addOperand(File); + EXPECT_TRUE(verifyModule(M)); + + ModulePassManager MPM(true); + MPM.addPass(VerifierPass(false)); + ModuleAnalysisManager MAM(true); + MAM.registerPass([&] { return VerifierAnalysis(); }); + MPM.run(M, MAM); + EXPECT_FALSE(verifyModule(M)); +} +#endif + +TEST(VerifierTest, StripInvalidDebugInfoLegacy) { + LLVMContext C; + Module M("M", C); + DIBuilder DIB(M); + DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/", + "unittest", false, "", 0); + DIB.finalize(); + EXPECT_FALSE(verifyModule(M)); + + // Now break it. + auto *File = DIB.createFile("not-a-CU.f", "."); + NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu"); + NMD->addOperand(File); + EXPECT_TRUE(verifyModule(M)); + + legacy::PassManager Passes; + Passes.add(createVerifierPass(false)); + Passes.run(M); + EXPECT_FALSE(verifyModule(M)); +} + +} // end anonymous namespace +} // end namespace llvm diff --git a/unittests/IR/WaymarkTest.cpp b/unittests/IR/WaymarkTest.cpp index a8924efed3f5..4d2671c06c1d 100644 --- a/unittests/IR/WaymarkTest.cpp +++ b/unittests/IR/WaymarkTest.cpp @@ -19,16 +19,14 @@ namespace llvm { namespace { -Constant *char2constant(char c) { - return ConstantInt::get(Type::getInt8Ty(getGlobalContext()), c); -} - - TEST(WaymarkTest, NativeArray) { + LLVMContext Context; static uint8_t tail[22] = "s02s33s30y2y0s1x0syxS"; Value * values[22]; - std::transform(tail, tail + 22, values, char2constant); - FunctionType *FT = FunctionType::get(Type::getVoidTy(getGlobalContext()), true); + std::transform(tail, tail + 22, values, [&](char c) { + return ConstantInt::get(Type::getInt8Ty(Context), c); + }); + FunctionType *FT = FunctionType::get(Type::getVoidTy(Context), true); std::unique_ptr<Function> F( Function::Create(FT, GlobalValue::ExternalLinkage)); const CallInst *A = CallInst::Create(F.get(), makeArrayRef(values)); diff --git a/unittests/LineEditor/Makefile b/unittests/LineEditor/Makefile deleted file mode 100644 index 058b6e46eb93..000000000000 --- a/unittests/LineEditor/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/LineEditor/Makefile -----------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = LineEditor -LINK_COMPONENTS := lineeditor - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Linker/LinkModulesTest.cpp b/unittests/Linker/LinkModulesTest.cpp index 322a44f8aafe..92c483278be9 100644 --- a/unittests/Linker/LinkModulesTest.cpp +++ b/unittests/Linker/LinkModulesTest.cpp @@ -202,30 +202,6 @@ TEST_F(LinkModuleTest, TypeMerge) { M1->getNamedGlobal("t2")->getType()); } -TEST_F(LinkModuleTest, CAPISuccess) { - std::unique_ptr<Module> DestM(getExternal(Ctx, "foo")); - std::unique_ptr<Module> SourceM(getExternal(Ctx, "bar")); - char *errout = nullptr; - LLVMBool result = LLVMLinkModules(wrap(DestM.get()), wrap(SourceM.get()), - LLVMLinkerDestroySource, &errout); - EXPECT_EQ(0, result); - EXPECT_EQ(nullptr, errout); - // "bar" is present in destination module - EXPECT_NE(nullptr, DestM->getFunction("bar")); -} - -TEST_F(LinkModuleTest, CAPIFailure) { - // Symbol clash between two modules - std::unique_ptr<Module> DestM(getExternal(Ctx, "foo")); - std::unique_ptr<Module> SourceM(getExternal(Ctx, "foo")); - char *errout = nullptr; - LLVMBool result = LLVMLinkModules(wrap(DestM.get()), wrap(SourceM.get()), - LLVMLinkerDestroySource, &errout); - EXPECT_EQ(1, result); - EXPECT_STREQ("Linking globals named 'foo': symbol multiply defined!", errout); - LLVMDisposeMessage(errout); -} - TEST_F(LinkModuleTest, NewCAPISuccess) { std::unique_ptr<Module> DestM(getExternal(Ctx, "foo")); std::unique_ptr<Module> SourceM(getExternal(Ctx, "bar")); @@ -330,4 +306,58 @@ TEST_F(LinkModuleTest, MoveDistinctMDs) { EXPECT_EQ(M3, M4->getOperand(0)); } +TEST_F(LinkModuleTest, RemangleIntrinsics) { + LLVMContext C; + SMDiagnostic Err; + + // We load two modules inside the same context C. In both modules there is a + // "struct.rtx_def" type. In the module loaded the second (Bar) this type will + // be renamed to "struct.rtx_def.0". Check that the intrinsics which have this + // type in the signature are properly remangled. + 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" + " ret void\n" + "}\n" + "declare void @llvm.memset.p0struct.rtx_def.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" + " ret void\n" + "}\n" + "declare void @llvm.memset.p0struct.rtx_def.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")); + + 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")); + + // Link two modules together. + auto Dst = llvm::make_unique<Module>("Linked", C); + ASSERT_TRUE(Dst.get()); + Ctx.setDiagnosticHandler(expectNoDiags); + bool Failed = Linker::linkModules(*Foo, std::move(Bar)); + ASSERT_FALSE(Failed); + + // "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"); + ASSERT_EQ(F->getNumUses(), (unsigned)2); +} + } // end anonymous namespace diff --git a/unittests/Linker/Makefile b/unittests/Linker/Makefile deleted file mode 100644 index ddbce07d4a4d..000000000000 --- a/unittests/Linker/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/Linker/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = Linker -LINK_COMPONENTS := core linker asmparser - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/MC/CMakeLists.txt b/unittests/MC/CMakeLists.txt index f83eaf4779f9..c760c0267a9c 100644 --- a/unittests/MC/CMakeLists.txt +++ b/unittests/MC/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(MCTests Disassembler.cpp + DwarfLineTables.cpp StringTableBuilderTest.cpp - YAMLTest.cpp + TargetRegistry.cpp ) diff --git a/unittests/MC/DwarfLineTables.cpp b/unittests/MC/DwarfLineTables.cpp new file mode 100644 index 000000000000..4bfb5acea039 --- /dev/null +++ b/unittests/MC/DwarfLineTables.cpp @@ -0,0 +1,179 @@ +//===- llvm/unittest/MC/DwarfLineTables.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/Dwarf.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { +struct Context { + const char *Triple = "x86_64-pc-linux"; + std::unique_ptr<MCRegisterInfo> MRI; + std::unique_ptr<MCAsmInfo> MAI; + std::unique_ptr<MCContext> Ctx; + + Context() { + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + // If we didn't build x86, do not run the test. + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(Triple, Error); + if (!TheTarget) + return; + + MRI.reset(TheTarget->createMCRegInfo(Triple)); + MAI.reset(TheTarget->createMCAsmInfo(*MRI, Triple)); + Ctx = llvm::make_unique<MCContext>(MAI.get(), MRI.get(), nullptr); + } + + operator bool() { return Ctx.get(); } + operator MCContext &() { return *Ctx; }; +}; + +Context Ctxt; +} + +void verifyEncoding(MCDwarfLineTableParams Params, int LineDelta, int AddrDelta, + ArrayRef<uint8_t> ExpectedEncoding) { + SmallString<16> Buffer; + raw_svector_ostream EncodingOS(Buffer); + MCDwarfLineAddr::Encode(Ctxt, Params, LineDelta, AddrDelta, EncodingOS); + ArrayRef<uint8_t> Encoding(reinterpret_cast<uint8_t *>(Buffer.data()), + Buffer.size()); + EXPECT_EQ(ExpectedEncoding, Encoding); +} + +TEST(DwarfLineTables, TestDefaultParams) { + if (!Ctxt) + return; + + MCDwarfLineTableParams Params; + + // Minimal line offset expressible through extended opcode, 0 addr delta + const uint8_t Encoding0[] = {13}; // Special opcode Addr += 0, Line += -5 + verifyEncoding(Params, -5, 0, Encoding0); + + // Maximal line offset expressible through extended opcode, + const uint8_t Encoding1[] = {26}; // Special opcode Addr += 0, Line += +8 + verifyEncoding(Params, 8, 0, Encoding1); + + // Random value in the middle of the special ocode range + const uint8_t Encoding2[] = {146}; // Special opcode Addr += 9, Line += 2 + verifyEncoding(Params, 2, 9, Encoding2); + + // Minimal line offset expressible through extended opcode, max addr delta + const uint8_t Encoding3[] = {251}; // Special opcode Addr += 17, Line += -5 + verifyEncoding(Params, -5, 17, Encoding3); + + // Biggest special opcode + const uint8_t Encoding4[] = {255}; // Special opcode Addr += 17, Line += -1 + verifyEncoding(Params, -1, 17, Encoding4); + + // Line delta outside of the special opcode range, address delta in range + const uint8_t Encoding5[] = {dwarf::DW_LNS_advance_line, 9, + 158}; // Special opcode Addr += 10, Line += 0 + verifyEncoding(Params, 9, 10, Encoding5); + + // Address delta outside of the special opcode range, but small + // enough to do DW_LNS_const_add_pc + special opcode. + const uint8_t Encoding6[] = {dwarf::DW_LNS_const_add_pc, // pc += 17 + 62}; // Special opcode Addr += 3, Line += 2 + verifyEncoding(Params, 2, 20, Encoding6); + + // Address delta big enough to require the use of DW_LNS_advance_pc + // Line delta in special opcode range + const uint8_t Encoding7[] = {dwarf::DW_LNS_advance_pc, 100, + 20}; // Special opcode Addr += 0, Line += 2 + verifyEncoding(Params, 2, 100, Encoding7); + + // No special opcode possible. + const uint8_t Encoding8[] = {dwarf::DW_LNS_advance_line, 20, + dwarf::DW_LNS_advance_pc, 100, + dwarf::DW_LNS_copy}; + verifyEncoding(Params, 20, 100, Encoding8); +} + +TEST(DwarfLineTables, TestCustomParams) { + if (!Ctxt) + return; + + // Some tests against the example values given in the standard. + MCDwarfLineTableParams Params; + Params.DWARF2LineOpcodeBase = 13; + Params.DWARF2LineBase = -3; + Params.DWARF2LineRange = 12; + + // Minimal line offset expressible through extended opcode, 0 addr delta + const uint8_t Encoding0[] = {13}; // Special opcode Addr += 0, Line += -5 + verifyEncoding(Params, -3, 0, Encoding0); + + // Maximal line offset expressible through extended opcode, + const uint8_t Encoding1[] = {24}; // Special opcode Addr += 0, Line += +8 + verifyEncoding(Params, 8, 0, Encoding1); + + // Random value in the middle of the special ocode range + const uint8_t Encoding2[] = {126}; // Special opcode Addr += 9, Line += 2 + verifyEncoding(Params, 2, 9, Encoding2); + + // Minimal line offset expressible through extended opcode, max addr delta + const uint8_t Encoding3[] = {253}; // Special opcode Addr += 20, Line += -3 + verifyEncoding(Params, -3, 20, Encoding3); + + // Biggest special opcode + const uint8_t Encoding4[] = {255}; // Special opcode Addr += 17, Line += -1 + verifyEncoding(Params, -1, 20, Encoding4); + + // Line delta outside of the special opcode range, address delta in range + const uint8_t Encoding5[] = {dwarf::DW_LNS_advance_line, 9, + 136}; // Special opcode Addr += 10, Line += 0 + verifyEncoding(Params, 9, 10, Encoding5); + + // Address delta outside of the special opcode range, but small + // enough to do DW_LNS_const_add_pc + special opcode. + const uint8_t Encoding6[] = {dwarf::DW_LNS_const_add_pc, // pc += 20 + 138}; // Special opcode Addr += 10, Line += 2 + verifyEncoding(Params, 2, 30, Encoding6); + + // Address delta big enough to require the use of DW_LNS_advance_pc + // Line delta in special opcode range + const uint8_t Encoding7[] = {dwarf::DW_LNS_advance_pc, 100, + 18}; // Special opcode Addr += 0, Line += 2 + verifyEncoding(Params, 2, 100, Encoding7); + + // No special opcode possible. + const uint8_t Encoding8[] = {dwarf::DW_LNS_advance_line, 20, + dwarf::DW_LNS_advance_pc, 100, + dwarf::DW_LNS_copy}; + verifyEncoding(Params, 20, 100, Encoding8); +} + +TEST(DwarfLineTables, TestCustomParams2) { + if (!Ctxt) + return; + + // Corner case param values. + MCDwarfLineTableParams Params; + Params.DWARF2LineOpcodeBase = 13; + Params.DWARF2LineBase = 1; + Params.DWARF2LineRange = 255; + + const uint8_t Encoding0[] = {dwarf::DW_LNS_advance_line, 248, 1, + dwarf::DW_LNS_copy}; + verifyEncoding(Params, 248, 0, Encoding0); +} diff --git a/unittests/MC/Makefile b/unittests/MC/Makefile deleted file mode 100644 index 3f8d1ef9555c..000000000000 --- a/unittests/MC/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/IR/Makefile -------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = MC -LINK_COMPONENTS := all-targets MCDisassembler Object - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/MC/StringTableBuilderTest.cpp b/unittests/MC/StringTableBuilderTest.cpp index 4cc0bda0a03a..f78d3588ffff 100644 --- a/unittests/MC/StringTableBuilderTest.cpp +++ b/unittests/MC/StringTableBuilderTest.cpp @@ -68,4 +68,27 @@ TEST(StringTableBuilderTest, BasicWinCOFF) { EXPECT_EQ(23U, B.getOffset("river horse")); } +TEST(StringTableBuilderTest, ELFInOrder) { + StringTableBuilder B(StringTableBuilder::ELF); + EXPECT_EQ(1U, B.add("foo")); + EXPECT_EQ(5U, B.add("bar")); + EXPECT_EQ(9U, B.add("foobar")); + + B.finalizeInOrder(); + + std::string Expected; + Expected += '\x00'; + Expected += "foo"; + Expected += '\x00'; + Expected += "bar"; + Expected += '\x00'; + Expected += "foobar"; + Expected += '\x00'; + + EXPECT_EQ(Expected, B.data()); + EXPECT_EQ(1U, B.getOffset("foo")); + EXPECT_EQ(5U, B.getOffset("bar")); + EXPECT_EQ(9U, B.getOffset("foobar")); +} + } diff --git a/unittests/Support/TargetRegistry.cpp b/unittests/MC/TargetRegistry.cpp index ae89c8b64930..eb46b22f06c1 100644 --- a/unittests/Support/TargetRegistry.cpp +++ b/unittests/MC/TargetRegistry.cpp @@ -1,4 +1,4 @@ -//===- unittests/Support/TargetRegistry.cpp - -----------------------------===// +//===- unittests/MC/TargetRegistry.cpp ------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -7,6 +7,10 @@ // //===----------------------------------------------------------------------===// +// The target registry code lives in Support, but it relies on linking in all +// LLVM targets. We keep this test with the MC tests, which already do that, to +// keep the SupportTests target small. + #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "gtest/gtest.h" diff --git a/unittests/MI/CMakeLists.txt b/unittests/MI/CMakeLists.txt new file mode 100644 index 000000000000..595497f3d960 --- /dev/null +++ b/unittests/MI/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + CodeGen + Core + MC + MIRParser + Support + Target + ) + +add_llvm_unittest(MITests + LiveIntervalTest.cpp + ) diff --git a/unittests/MI/LiveIntervalTest.cpp b/unittests/MI/LiveIntervalTest.cpp new file mode 100644 index 000000000000..e0b3d5529afb --- /dev/null +++ b/unittests/MI/LiveIntervalTest.cpp @@ -0,0 +1,360 @@ +#include "gtest/gtest.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/LiveIntervalAnalysis.h" +#include "llvm/CodeGen/MIRParser/MIRParser.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Target/TargetRegisterInfo.h" +#include "llvm/IR/LegacyPassManager.h" + +using namespace llvm; + +namespace llvm { + void initializeTestPassPass(PassRegistry &); +} + +namespace { + +void initLLVM() { + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + PassRegistry *Registry = PassRegistry::getPassRegistry(); + initializeCore(*Registry); + initializeCodeGen(*Registry); +} + +/// Create a TargetMachine. As we lack a dedicated always available target for +/// unittests, we go for "x86_64" which should be available in most builds. +std::unique_ptr<TargetMachine> createTargetMachine() { + Triple TargetTriple("x86_64--"); + std::string Error; + const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error); + if (!T) + return nullptr; + + TargetOptions Options; + return std::unique_ptr<TargetMachine>( + T->createTargetMachine("x86_64", "", "", Options, None, + CodeModel::Default, CodeGenOpt::Aggressive)); +} + +std::unique_ptr<Module> parseMIR(LLVMContext &Context, + legacy::PassManagerBase &PM, std::unique_ptr<MIRParser> &MIR, + const TargetMachine &TM, StringRef MIRCode, const char *FuncName) { + SMDiagnostic Diagnostic; + std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode); + MIR = createMIRParser(std::move(MBuffer), Context); + if (!MIR) + return nullptr; + + std::unique_ptr<Module> M = MIR->parseLLVMModule(); + if (!M) + return nullptr; + + M->setDataLayout(TM.createDataLayout()); + + Function *F = M->getFunction(FuncName); + if (!F) + return nullptr; + + const LLVMTargetMachine &LLVMTM = static_cast<const LLVMTargetMachine&>(TM); + LLVMTM.addMachineModuleInfo(PM); + LLVMTM.addMachineFunctionAnalysis(PM, MIR.get()); + + return M; +} + +typedef std::function<void(MachineFunction&,LiveIntervals&)> LiveIntervalTest; + +struct TestPass : public MachineFunctionPass { + static char ID; + TestPass() : MachineFunctionPass(ID) { + // We should never call this but always use PM.add(new TestPass(...)) + abort(); + } + TestPass(LiveIntervalTest T) : MachineFunctionPass(ID), T(T) { + initializeTestPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override { + LiveIntervals &LIS = getAnalysis<LiveIntervals>(); + T(MF, LIS); + EXPECT_TRUE(MF.verify(this)); + return true; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + AU.addRequired<LiveIntervals>(); + AU.addPreserved<LiveIntervals>(); + MachineFunctionPass::getAnalysisUsage(AU); + } +private: + LiveIntervalTest T; +}; + +/** + * Move instruction number \p From in front of instruction number \p To and + * update affected liveness intervals with LiveIntervalAnalysis::handleMove(). + */ +static void testHandleMove(MachineFunction &MF, LiveIntervals &LIS, + unsigned From, unsigned To, unsigned BlockNum = 0) { + MachineBasicBlock &MBB = *MF.getBlockNumbered(BlockNum); + + unsigned I = 0; + MachineInstr *FromInstr = nullptr; + MachineInstr *ToInstr = nullptr; + for (MachineInstr &MI : MBB) { + if (I == From) + FromInstr = &MI; + if (I == To) + ToInstr = &MI; + ++I; + } + assert(FromInstr != nullptr && ToInstr != nullptr); + + MBB.splice(ToInstr->getIterator(), &MBB, FromInstr->getIterator()); + LIS.handleMove(*FromInstr, true); +} + +static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) { + LLVMContext Context; + std::unique_ptr<TargetMachine> TM = createTargetMachine(); + // This test is designed for the X86 backend; stop if it is not available. + if (!TM) + return; + + legacy::PassManager PM; + + SmallString<160> S; + StringRef MIRString = (Twine( +"---\n" +"...\n" +"name: func\n" +"registers:\n" +" - { id: 0, class: gr64 }\n" +"body: |\n" +" bb.0:\n" + ) + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S); + std::unique_ptr<MIRParser> MIR; + std::unique_ptr<Module> M = parseMIR(Context, PM, MIR, *TM, MIRString, + "func"); + + PM.add(new TestPass(T)); + + PM.run(*M); +} + +} // End of anonymous namespace. + +char TestPass::ID = 0; +INITIALIZE_PASS(TestPass, "testpass", "testpass", false, false) + +TEST(LiveIntervalTest, MoveUpDef) { + // Value defined. + liveIntervalTest( +" NOOP\n" +" NOOP\n" +" early-clobber %0 = IMPLICIT_DEF\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 2, 1); + }); +} + +TEST(LiveIntervalTest, MoveUpRedef) { + liveIntervalTest( +" %0 = IMPLICIT_DEF\n" +" NOOP\n" +" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 2, 1); + }); +} + +TEST(LiveIntervalTest, MoveUpEarlyDef) { + liveIntervalTest( +" NOOP\n" +" NOOP\n" +" early-clobber %0 = IMPLICIT_DEF\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 2, 1); + }); +} + +TEST(LiveIntervalTest, MoveUpEarlyRedef) { + liveIntervalTest( +" %0 = IMPLICIT_DEF\n" +" NOOP\n" +" early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 2, 1); + }); +} + +TEST(LiveIntervalTest, MoveUpKill) { + liveIntervalTest( +" %0 = IMPLICIT_DEF\n" +" NOOP\n" +" NOOP implicit %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 2, 1); + }); +} + +TEST(LiveIntervalTest, MoveUpKillFollowing) { + liveIntervalTest( +" %0 = IMPLICIT_DEF\n" +" NOOP\n" +" NOOP implicit %0\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 2, 1); + }); +} + +// TODO: Construct a situation where we have intervals following a hole +// while still having connected components. + +TEST(LiveIntervalTest, MoveDownDef) { + // Value defined. + liveIntervalTest( +" NOOP\n" +" early-clobber %0 = IMPLICIT_DEF\n" +" NOOP\n" +" RETQ %0\n", + [](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" +" NOOP\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 1, 2); + }); +} + +TEST(LiveIntervalTest, MoveDownEarlyDef) { + liveIntervalTest( +" NOOP\n" +" early-clobber %0 = IMPLICIT_DEF\n" +" NOOP\n" +" RETQ %0\n", + [](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" +" NOOP\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 1, 2); + }); +} + +TEST(LiveIntervalTest, MoveDownKill) { + liveIntervalTest( +" %0 = IMPLICIT_DEF\n" +" NOOP implicit %0\n" +" NOOP\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 1, 2); + }); +} + +TEST(LiveIntervalTest, MoveDownKillFollowing) { + liveIntervalTest( +" %0 = IMPLICIT_DEF\n" +" NOOP\n" +" NOOP implicit %0\n" +" RETQ %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 1, 2); + }); +} + +TEST(LiveIntervalTest, MoveUndefUse) { + liveIntervalTest( +" %0 = IMPLICIT_DEF\n" +" NOOP implicit undef %0\n" +" NOOP implicit %0\n" +" NOOP\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 1, 3); + }); +} + +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" +" JG_1 %bb.2, implicit %eflags\n" +" JMP_1 %bb.1\n" +" bb.2:\n" +" NOOP 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" +" JMP_1 %bb.2\n", + [](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" +" NOOP\n" +" NOOP implicit undef %0\n" +" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 3, 1); + }); +} + +TEST(LiveIntervalTest, MoveOverUndefUse1) { + // findLastUseBefore() used by handleMoveUp() must ignore undef operands. + liveIntervalTest( +" %rax = IMPLICIT_DEF\n" +" NOOP\n" +" NOOP implicit undef %rax\n" +" %rax = IMPLICIT_DEF implicit %rax(tied-def 0)\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + testHandleMove(MF, LIS, 3, 1); + }); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + initLLVM(); + return RUN_ALL_TESTS(); +} diff --git a/unittests/Makefile b/unittests/Makefile deleted file mode 100644 index bf2ed22efea7..000000000000 --- a/unittests/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -##===- unittests/Makefile ----------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = .. - -PARALLEL_DIRS = ADT Analysis AsmParser Bitcode CodeGen DebugInfo \ - ExecutionEngine IR LineEditor Linker MC Option ProfileData \ - Support Transforms - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest - -clean:: - $(Verb) $(RM) -f *Tests diff --git a/unittests/Makefile.unittest b/unittests/Makefile.unittest deleted file mode 100644 index a39edc675698..000000000000 --- a/unittests/Makefile.unittest +++ /dev/null @@ -1,69 +0,0 @@ -##===- unittests/Makefile.unittest -------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -# -# This file is included by all of the unit test makefiles. -# -##===----------------------------------------------------------------------===## - -ifndef MAKEFILE_UNITTEST_NO_INCLUDE_COMMON -include $(LEVEL)/Makefile.common -endif - -# Clean up out-of-tree stray unittests for Lit not to pick one up. -.PHONY: cleanup-local -cleanup-local: - -$(Verb) $(FIND) $(filter-out $(PARALLEL_DIRS), $(wildcard *)) -type f \ - -path '*/$(BuildMode)/*Tests$(EXEEXT)' \ - -exec rm -f '{}' \; - -all:: cleanup-local -clean:: cleanup-local - -# Set up variables for building a unit test. -ifdef TESTNAME - -LLVMUnitTestExe = $(BuildMode)/$(TESTNAME)Tests$(EXEEXT) - -# Note that these flags are duplicated when building GoogleTest itself in -# utils/unittest/googletest/Makefile; ensure that any changes are made to both. -CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include -CPP.Flags += $(NO_MISSING_FIELD_INITIALIZERS) $(NO_VARIADIC_MACROS) -CPP.Flags += -DGTEST_HAS_RTTI=0 -# libstdc++'s TR1 <tuple> header depends on RTTI and uses C++'0x features not -# supported by Clang, so force googletest to use its own tuple implementation. -CPP.Flags += -DGTEST_USE_OWN_TR1_TUPLE - -# Disable pthreads if LLVM was configured without them. -ifneq ($(HAVE_PTHREAD), 1) - CPP.Flags += -DGTEST_HAS_PTHREAD=0 -endif - -TESTLIBS = -lgtest -lgtest_main - -ifeq ($(ENABLE_SHARED), 1) - ifneq (,$(RPATH)) - # Add the absolute path to the dynamic library. This is ok because - # we'll never install unittests. - LD.Flags += $(RPATH) -Wl,$(SharedLibDir) - endif -endif - -$(LLVMUnitTestExe): $(ObjectsO) $(ProjLibsPaths) $(LLVMLibsPaths) - $(Echo) Linking $(BuildMode) unit test $(TESTNAME) $(StripWarnMsg) - $(Verb) $(Link) -o $@ $(TOOLLINKOPTS) $(ObjectsO) $(ProjLibsOptions) \ - $(TESTLIBS) $(LLVMLibsOptions) $(ExtraLibs) $(TOOLLINKOPTSB) $(LIBS) - $(Echo) ======= Finished Linking $(BuildMode) Unit test $(TESTNAME) \ - $(StripWarnMsg) - -all:: $(LLVMUnitTestExe) - -unitcheck:: $(LLVMUnitTestExe) - $(LLVMUnitTestExe) - -endif diff --git a/unittests/ObjectYAML/CMakeLists.txt b/unittests/ObjectYAML/CMakeLists.txt new file mode 100644 index 000000000000..baed10b8730f --- /dev/null +++ b/unittests/ObjectYAML/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + ObjectYAML + ) + +add_llvm_unittest(ObjectYAMLTests + YAMLTest.cpp + ) + diff --git a/unittests/MC/YAMLTest.cpp b/unittests/ObjectYAML/YAMLTest.cpp index 09709ad73fc7..606e160d19a5 100644 --- a/unittests/MC/YAMLTest.cpp +++ b/unittests/ObjectYAML/YAMLTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/Object/YAMLTest.cpp - Tests for Object YAML ----------===// +//===- YAMLTest.cpp - Tests for Object YAML -------------------------------===// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "llvm/MC/YAML.h" +#include "llvm/ObjectYAML/YAML.h" #include "llvm/Support/YAMLTraits.h" #include "gtest/gtest.h" diff --git a/unittests/Option/Makefile b/unittests/Option/Makefile deleted file mode 100644 index 8c90a83da872..000000000000 --- a/unittests/Option/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -##===- unittests/Option/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = Option -LINK_COMPONENTS := option support - -BUILT_SOURCES = Opts.inc -TABLEGEN_INC_FILES_COMMON = 1 - -include $(LEVEL)/Makefile.config - -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest - -$(ObjDir)/Opts.inc.tmp : Opts.td $(LLVM_TBLGEN) $(ObjDir)/.dir - $(Echo) "Building Driver Option tables with tblgen" - $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< diff --git a/unittests/Option/OptionParsingTest.cpp b/unittests/Option/OptionParsingTest.cpp index 5270dc940f96..b0418a71c786 100644 --- a/unittests/Option/OptionParsingTest.cpp +++ b/unittests/Option/OptionParsingTest.cpp @@ -79,32 +79,32 @@ TEST(Option, OptionParsing) { EXPECT_TRUE(AL.hasArg(OPT_G)); // Check the values. - EXPECT_EQ(AL.getLastArgValue(OPT_B), "hi"); - EXPECT_EQ(AL.getLastArgValue(OPT_C), "bye"); - EXPECT_EQ(AL.getLastArgValue(OPT_D), "adena"); + EXPECT_EQ("hi", AL.getLastArgValue(OPT_B)); + EXPECT_EQ("bye", AL.getLastArgValue(OPT_C)); + EXPECT_EQ("adena", AL.getLastArgValue(OPT_D)); std::vector<std::string> Es = AL.getAllArgValues(OPT_E); - EXPECT_EQ(Es[0], "apple"); - EXPECT_EQ(Es[1], "bloom"); - EXPECT_EQ(AL.getLastArgValue(OPT_F), "42"); + EXPECT_EQ("apple", Es[0]); + EXPECT_EQ("bloom", Es[1]); + EXPECT_EQ("42", AL.getLastArgValue(OPT_F)); std::vector<std::string> Gs = AL.getAllArgValues(OPT_G); - EXPECT_EQ(Gs[0], "chuu"); - EXPECT_EQ(Gs[1], "2"); + EXPECT_EQ("chuu", Gs[0]); + EXPECT_EQ("2", Gs[1]); // Check the help text. std::string Help; raw_string_ostream RSO(Help); T.PrintHelp(RSO, "test", "title!"); - EXPECT_NE(Help.find("-A"), std::string::npos); + EXPECT_NE(std::string::npos, Help.find("-A")); // Test aliases. arg_iterator Cs = AL.filtered_begin(OPT_C); - ASSERT_NE(Cs, AL.filtered_end()); - EXPECT_EQ(StringRef((*Cs)->getValue()), "desu"); + ASSERT_NE(AL.filtered_end(), Cs); + EXPECT_EQ("desu", StringRef((*Cs)->getValue())); ArgStringList ASL; (*Cs)->render(AL, ASL); - ASSERT_EQ(ASL.size(), 2u); - EXPECT_EQ(StringRef(ASL[0]), "-C"); - EXPECT_EQ(StringRef(ASL[1]), "desu"); + ASSERT_EQ(2u, ASL.size()); + EXPECT_EQ("-C", StringRef(ASL[0])); + EXPECT_EQ("desu", StringRef(ASL[1])); } TEST(Option, ParseWithFlagExclusions) { @@ -131,8 +131,8 @@ TEST(Option, ParseWithFlagExclusions) { AL = T.ParseArgs(NewArgs, MAI, MAC); EXPECT_TRUE(AL.hasArg(OPT_SLASH_C)); EXPECT_TRUE(AL.hasArg(OPT_C)); - EXPECT_EQ(AL.getLastArgValue(OPT_SLASH_C), "foo"); - EXPECT_EQ(AL.getLastArgValue(OPT_C), "bar"); + EXPECT_EQ("foo", AL.getLastArgValue(OPT_SLASH_C)); + EXPECT_EQ("bar", AL.getLastArgValue(OPT_C)); } TEST(Option, ParseAliasInGroup) { @@ -151,8 +151,8 @@ TEST(Option, AliasArgs) { const char *MyArgs[] = { "-J", "-Joo" }; InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); EXPECT_TRUE(AL.hasArg(OPT_B)); - EXPECT_EQ(AL.getAllArgValues(OPT_B)[0], "foo"); - EXPECT_EQ(AL.getAllArgValues(OPT_B)[1], "bar"); + EXPECT_EQ("foo", AL.getAllArgValues(OPT_B)[0]); + EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]); } TEST(Option, IgnoreCase) { @@ -183,7 +183,7 @@ TEST(Option, SlurpEmpty) { InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); EXPECT_TRUE(AL.hasArg(OPT_A)); EXPECT_TRUE(AL.hasArg(OPT_Slurp)); - EXPECT_EQ(AL.getAllArgValues(OPT_Slurp).size(), 0U); + EXPECT_EQ(0U, AL.getAllArgValues(OPT_Slurp).size()); } TEST(Option, Slurp) { @@ -196,10 +196,61 @@ TEST(Option, Slurp) { EXPECT_TRUE(AL.hasArg(OPT_A)); EXPECT_FALSE(AL.hasArg(OPT_B)); EXPECT_TRUE(AL.hasArg(OPT_Slurp)); - EXPECT_EQ(AL.getAllArgValues(OPT_Slurp).size(), 3U); - EXPECT_EQ(AL.getAllArgValues(OPT_Slurp)[0], "-B"); - EXPECT_EQ(AL.getAllArgValues(OPT_Slurp)[1], "--"); - EXPECT_EQ(AL.getAllArgValues(OPT_Slurp)[2], "foo"); + EXPECT_EQ(3U, AL.getAllArgValues(OPT_Slurp).size()); + EXPECT_EQ("-B", AL.getAllArgValues(OPT_Slurp)[0]); + EXPECT_EQ("--", AL.getAllArgValues(OPT_Slurp)[1]); + EXPECT_EQ("foo", AL.getAllArgValues(OPT_Slurp)[2]); +} + +TEST(Option, SlurpJoinedEmpty) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-A", "-slurpjoined" }; + InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); + EXPECT_TRUE(AL.hasArg(OPT_A)); + EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); + EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 0U); +} + +TEST(Option, SlurpJoinedOneJoined) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-A", "-slurpjoinedfoo" }; + InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); + EXPECT_TRUE(AL.hasArg(OPT_A)); + EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); + EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 1U); + EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined)[0], "foo"); +} + +TEST(Option, SlurpJoinedAndSeparate) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-A", "-slurpjoinedfoo", "bar", "baz" }; + InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); + EXPECT_TRUE(AL.hasArg(OPT_A)); + EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); + EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size()); + EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]); + EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]); + EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]); +} + +TEST(Option, SlurpJoinedButSeparate) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-A", "-slurpjoined", "foo", "bar", "baz" }; + InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); + EXPECT_TRUE(AL.hasArg(OPT_A)); + EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); + EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size()); + EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]); + EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]); + EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]); } TEST(Option, FlagAliasToJoined) { @@ -211,6 +262,6 @@ TEST(Option, FlagAliasToJoined) { InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); EXPECT_EQ(AL.size(), 1U); EXPECT_TRUE(AL.hasArg(OPT_B)); - EXPECT_EQ(AL.getAllArgValues(OPT_B).size(), 1U); - EXPECT_EQ(AL.getAllArgValues(OPT_B)[0], ""); + EXPECT_EQ(1U, AL.getAllArgValues(OPT_B).size()); + EXPECT_EQ("", AL.getAllArgValues(OPT_B)[0]); } diff --git a/unittests/Option/Opts.td b/unittests/Option/Opts.td index c96774a68e0b..25c98c6f6015 100644 --- a/unittests/Option/Opts.td +++ b/unittests/Option/Opts.td @@ -26,3 +26,5 @@ def Joo : Flag<["-"], "Joo">, Alias<B>, AliasArgs<["bar"]>; def K : Flag<["-"], "K">, Alias<B>; def Slurp : Option<["-"], "slurp", KIND_REMAINING_ARGS>; + +def SlurpJoined : Option<["-"], "slurpjoined", KIND_REMAINING_ARGS_JOINED>; diff --git a/unittests/ProfileData/CMakeLists.txt b/unittests/ProfileData/CMakeLists.txt index 011f8c581792..dd39ca7da3ad 100644 --- a/unittests/ProfileData/CMakeLists.txt +++ b/unittests/ProfileData/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS Core + Coverage ProfileData Support ) diff --git a/unittests/ProfileData/CoverageMappingTest.cpp b/unittests/ProfileData/CoverageMappingTest.cpp index 35b8626c494a..53b40ebae85a 100644 --- a/unittests/ProfileData/CoverageMappingTest.cpp +++ b/unittests/ProfileData/CoverageMappingTest.cpp @@ -7,24 +7,24 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ProfileData/CoverageMapping.h" -#include "llvm/ProfileData/CoverageMappingReader.h" -#include "llvm/ProfileData/CoverageMappingWriter.h" +#include "llvm/ProfileData/Coverage/CoverageMapping.h" +#include "llvm/ProfileData/Coverage/CoverageMappingReader.h" +#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/ProfileData/InstrProfWriter.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" -#include <sstream> +#include <ostream> using namespace llvm; using namespace coverage; -static ::testing::AssertionResult NoError(std::error_code EC) { - if (!EC) +static ::testing::AssertionResult NoError(Error E) { + if (!E) return ::testing::AssertionSuccess(); - return ::testing::AssertionFailure() << "error " << EC.value() - << ": " << EC.message(); + return ::testing::AssertionFailure() << "error: " << toString(std::move(E)) + << "\n"; } namespace llvm { @@ -49,41 +49,58 @@ void PrintTo(const CoverageSegment &S, ::std::ostream *os) { namespace { -struct OneFunctionCoverageReader : CoverageMappingReader { +struct OutputFunctionCoverageData { StringRef Name; uint64_t Hash; std::vector<StringRef> Filenames; - ArrayRef<CounterMappingRegion> Regions; - bool Done; - - OneFunctionCoverageReader(StringRef Name, uint64_t Hash, - ArrayRef<StringRef> Filenames, - ArrayRef<CounterMappingRegion> Regions) - : Name(Name), Hash(Hash), Filenames(Filenames), Regions(Regions), - Done(false) {} - - std::error_code readNextRecord(CoverageMappingRecord &Record) override { - if (Done) - return instrprof_error::eof; - Done = true; + std::vector<CounterMappingRegion> Regions; + void fillCoverageMappingRecord(CoverageMappingRecord &Record) const { Record.FunctionName = Name; Record.FunctionHash = Hash; Record.Filenames = Filenames; Record.Expressions = {}; Record.MappingRegions = Regions; - return instrprof_error::success; } }; +struct CoverageMappingReaderMock : CoverageMappingReader { + ArrayRef<OutputFunctionCoverageData> Functions; + + CoverageMappingReaderMock(ArrayRef<OutputFunctionCoverageData> Functions) + : Functions(Functions) {} + + Error readNextRecord(CoverageMappingRecord &Record) override { + if (Functions.empty()) + return make_error<CoverageMapError>(coveragemap_error::eof); + + Functions.front().fillCoverageMappingRecord(Record); + Functions = Functions.slice(1); + + return Error::success(); + } +}; + +struct InputFunctionCoverageData { + // Maps the global file index from CoverageMappingTest.Files + // to the index of that file within this function. We can't just use + // global file indexes here because local indexes have to be dense. + // This map is used during serialization to create the virtual file mapping + // (from local fileId to global Index) in the head of the per-function + // coverage mapping data. + SmallDenseMap<unsigned, unsigned> ReverseVirtualFileMapping; + std::string Name; + uint64_t Hash; + std::vector<CounterMappingRegion> Regions; + + InputFunctionCoverageData(std::string Name, uint64_t Hash) + : Name(std::move(Name)), Hash(Hash) {} +}; + struct CoverageMappingTest : ::testing::Test { StringMap<unsigned> Files; - unsigned NextFile; - std::vector<CounterMappingRegion> InputCMRs; - - std::vector<StringRef> OutputFiles; - std::vector<CounterExpression> OutputExpressions; - std::vector<CounterMappingRegion> OutputCMRs; + std::vector<InputFunctionCoverageData> InputFunctions; + std::vector<OutputFunctionCoverageData> OutputFunctions; InstrProfWriter ProfileWriter; std::unique_ptr<IndexedInstrProfReader> ProfileReader; @@ -91,112 +108,242 @@ struct CoverageMappingTest : ::testing::Test { std::unique_ptr<CoverageMapping> LoadedCoverage; void SetUp() override { - NextFile = 0; + ProfileWriter.setOutputSparse(false); } - unsigned getFile(StringRef Name) { + unsigned getGlobalFileIndex(StringRef Name) { auto R = Files.find(Name); if (R != Files.end()) return R->second; - Files[Name] = NextFile; - return NextFile++; + unsigned Index = Files.size(); + Files.emplace_second(Name, Index); + return Index; + } + + // Return the file index of file 'Name' for the current function. + // Add the file into the global map if necesary. + // See also InputFunctionCoverageData::ReverseVirtualFileMapping + // for additional comments. + unsigned getFileIndexForFunction(StringRef Name) { + unsigned GlobalIndex = getGlobalFileIndex(Name); + auto &CurrentFunctionFileMapping = + InputFunctions.back().ReverseVirtualFileMapping; + auto R = CurrentFunctionFileMapping.find(GlobalIndex); + if (R != CurrentFunctionFileMapping.end()) + return R->second; + unsigned IndexInFunction = CurrentFunctionFileMapping.size(); + CurrentFunctionFileMapping.insert( + std::make_pair(GlobalIndex, IndexInFunction)); + return IndexInFunction; + } + + void startFunction(StringRef FuncName, uint64_t Hash) { + InputFunctions.emplace_back(FuncName.str(), Hash); } void addCMR(Counter C, StringRef File, unsigned LS, unsigned CS, unsigned LE, unsigned CE) { - InputCMRs.push_back( - CounterMappingRegion::makeRegion(C, getFile(File), LS, CS, LE, CE)); + InputFunctions.back().Regions.push_back(CounterMappingRegion::makeRegion( + C, getFileIndexForFunction(File), LS, CS, LE, CE)); } void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS, unsigned CS, unsigned LE, unsigned CE) { - InputCMRs.push_back(CounterMappingRegion::makeExpansion( - getFile(File), getFile(ExpandedFile), LS, CS, LE, CE)); + InputFunctions.back().Regions.push_back(CounterMappingRegion::makeExpansion( + getFileIndexForFunction(File), getFileIndexForFunction(ExpandedFile), + LS, CS, LE, CE)); } - std::string writeCoverageRegions() { - SmallVector<unsigned, 8> FileIDs; - for (const auto &E : Files) - FileIDs.push_back(E.getValue()); + std::string writeCoverageRegions(InputFunctionCoverageData &Data) { + SmallVector<unsigned, 8> FileIDs(Data.ReverseVirtualFileMapping.size()); + for (const auto &E : Data.ReverseVirtualFileMapping) + FileIDs[E.second] = E.first; std::string Coverage; llvm::raw_string_ostream OS(Coverage); - CoverageMappingWriter(FileIDs, None, InputCMRs).write(OS); + CoverageMappingWriter(FileIDs, None, Data.Regions).write(OS); return OS.str(); } - void readCoverageRegions(std::string Coverage) { - SmallVector<StringRef, 8> Filenames; + void readCoverageRegions(std::string Coverage, + OutputFunctionCoverageData &Data) { + SmallVector<StringRef, 8> Filenames(Files.size()); for (const auto &E : Files) - Filenames.push_back(E.getKey()); - RawCoverageMappingReader Reader(Coverage, Filenames, OutputFiles, - OutputExpressions, OutputCMRs); + Filenames[E.getValue()] = E.getKey(); + std::vector<CounterExpression> Expressions; + RawCoverageMappingReader Reader(Coverage, Filenames, Data.Filenames, + Expressions, Data.Regions); ASSERT_TRUE(NoError(Reader.read())); } + void writeAndReadCoverageRegions(bool EmitFilenames = true) { + OutputFunctions.resize(InputFunctions.size()); + for (unsigned I = 0; I < InputFunctions.size(); ++I) { + std::string Regions = writeCoverageRegions(InputFunctions[I]); + readCoverageRegions(Regions, OutputFunctions[I]); + OutputFunctions[I].Name = InputFunctions[I].Name; + OutputFunctions[I].Hash = InputFunctions[I].Hash; + if (!EmitFilenames) + OutputFunctions[I].Filenames.clear(); + } + } + void readProfCounts() { auto Profile = ProfileWriter.writeBuffer(); auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile)); - ASSERT_TRUE(NoError(ReaderOrErr.getError())); + ASSERT_TRUE(NoError(ReaderOrErr.takeError())); ProfileReader = std::move(ReaderOrErr.get()); } - void loadCoverageMapping(StringRef FuncName, uint64_t Hash) { - std::string Regions = writeCoverageRegions(); - readCoverageRegions(Regions); + void loadCoverageMapping(bool EmitFilenames = true) { + readProfCounts(); + writeAndReadCoverageRegions(EmitFilenames); - SmallVector<StringRef, 8> Filenames; - for (const auto &E : Files) - Filenames.push_back(E.getKey()); - OneFunctionCoverageReader CovReader(FuncName, Hash, Filenames, OutputCMRs); + CoverageMappingReaderMock CovReader(OutputFunctions); auto CoverageOrErr = CoverageMapping::load(CovReader, *ProfileReader); - ASSERT_TRUE(NoError(CoverageOrErr.getError())); + ASSERT_TRUE(NoError(CoverageOrErr.takeError())); LoadedCoverage = std::move(CoverageOrErr.get()); } }; -TEST_F(CoverageMappingTest, basic_write_read) { +struct MaybeSparseCoverageMappingTest + : public CoverageMappingTest, + public ::testing::WithParamInterface<bool> { + void SetUp() { + CoverageMappingTest::SetUp(); + ProfileWriter.setOutputSparse(GetParam()); + } +}; + +TEST_P(MaybeSparseCoverageMappingTest, basic_write_read) { + startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1); addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2); addCMR(Counter::getZero(), "foo", 3, 1, 3, 4); addCMR(Counter::getCounter(2), "foo", 4, 1, 4, 8); addCMR(Counter::getCounter(3), "bar", 1, 2, 3, 4); - std::string Coverage = writeCoverageRegions(); - readCoverageRegions(Coverage); - size_t N = makeArrayRef(InputCMRs).size(); - ASSERT_EQ(N, OutputCMRs.size()); + writeAndReadCoverageRegions(); + ASSERT_EQ(1u, InputFunctions.size()); + ASSERT_EQ(1u, OutputFunctions.size()); + InputFunctionCoverageData &Input = InputFunctions.back(); + OutputFunctionCoverageData &Output = OutputFunctions.back(); + + size_t N = makeArrayRef(Input.Regions).size(); + ASSERT_EQ(N, Output.Regions.size()); for (size_t I = 0; I < N; ++I) { - ASSERT_EQ(InputCMRs[I].Count, OutputCMRs[I].Count); - ASSERT_EQ(InputCMRs[I].FileID, OutputCMRs[I].FileID); - ASSERT_EQ(InputCMRs[I].startLoc(), OutputCMRs[I].startLoc()); - ASSERT_EQ(InputCMRs[I].endLoc(), OutputCMRs[I].endLoc()); - ASSERT_EQ(InputCMRs[I].Kind, OutputCMRs[I].Kind); + ASSERT_EQ(Input.Regions[I].Count, Output.Regions[I].Count); + ASSERT_EQ(Input.Regions[I].FileID, Output.Regions[I].FileID); + ASSERT_EQ(Input.Regions[I].startLoc(), Output.Regions[I].startLoc()); + ASSERT_EQ(Input.Regions[I].endLoc(), Output.Regions[I].endLoc()); + ASSERT_EQ(Input.Regions[I].Kind, Output.Regions[I].Kind); + } +} + +TEST_P(MaybeSparseCoverageMappingTest, + correct_deserialize_for_more_than_two_files) { + const char *FileNames[] = {"bar", "baz", "foo"}; + static const unsigned N = array_lengthof(FileNames); + + startFunction("func", 0x1234); + for (unsigned I = 0; I < N; ++I) + // Use LineStart to hold the index of the file name + // in order to preserve that information during possible sorting of CMRs. + addCMR(Counter::getCounter(0), FileNames[I], I, 1, I, 1); + + writeAndReadCoverageRegions(); + ASSERT_EQ(1u, OutputFunctions.size()); + OutputFunctionCoverageData &Output = OutputFunctions.back(); + + ASSERT_EQ(N, Output.Regions.size()); + ASSERT_EQ(N, Output.Filenames.size()); + + for (unsigned I = 0; I < N; ++I) { + ASSERT_GT(N, Output.Regions[I].FileID); + ASSERT_GT(N, Output.Regions[I].LineStart); + EXPECT_EQ(FileNames[Output.Regions[I].LineStart], + Output.Filenames[Output.Regions[I].FileID]); + } +} + +TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_more_than_two_files) { + InstrProfRecord Record("func", 0x1234, {0}); + NoError(ProfileWriter.addRecord(std::move(Record))); + + const char *FileNames[] = {"bar", "baz", "foo"}; + static const unsigned N = array_lengthof(FileNames); + + startFunction("func", 0x1234); + for (unsigned I = 0; I < N; ++I) + // Use LineStart to hold the index of the file name + // in order to preserve that information during possible sorting of CMRs. + addCMR(Counter::getCounter(0), FileNames[I], I, 1, I, 1); + + loadCoverageMapping(); + + for (unsigned I = 0; I < N; ++I) { + CoverageData Data = LoadedCoverage->getCoverageForFile(FileNames[I]); + ASSERT_TRUE(!Data.empty()); + EXPECT_EQ(I, Data.begin()->Line); } } -TEST_F(CoverageMappingTest, expansion_gets_first_counter) { +TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_several_functions) { + InstrProfRecord RecordFunc1("func1", 0x1234, {10}); + NoError(ProfileWriter.addRecord(std::move(RecordFunc1))); + InstrProfRecord RecordFunc2("func2", 0x2345, {20}); + NoError(ProfileWriter.addRecord(std::move(RecordFunc2))); + + startFunction("func1", 0x1234); + addCMR(Counter::getCounter(0), "foo", 1, 1, 5, 5); + + startFunction("func2", 0x2345); + addCMR(Counter::getCounter(0), "bar", 2, 2, 6, 6); + + loadCoverageMapping(); + + const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); + EXPECT_EQ(2U, std::distance(FunctionRecords.begin(), FunctionRecords.end())); + for (const auto &FunctionRecord : FunctionRecords) { + CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord); + std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); + ASSERT_EQ(2U, Segments.size()); + if (FunctionRecord.Name == "func1") { + EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]); + EXPECT_EQ(CoverageSegment(5, 5, false), Segments[1]); + } else { + ASSERT_EQ("func2", FunctionRecord.Name); + EXPECT_EQ(CoverageSegment(2, 2, 20, true), Segments[0]); + EXPECT_EQ(CoverageSegment(6, 6, false), Segments[1]); + } + } +} + +TEST_P(MaybeSparseCoverageMappingTest, expansion_gets_first_counter) { + startFunction("func", 0x1234); addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2); // This starts earlier in "foo", so the expansion should get its counter. addCMR(Counter::getCounter(2), "foo", 1, 1, 20, 1); addExpansionCMR("bar", "foo", 3, 3, 3, 3); - std::string Coverage = writeCoverageRegions(); - readCoverageRegions(Coverage); - ASSERT_EQ(CounterMappingRegion::ExpansionRegion, OutputCMRs[2].Kind); - ASSERT_EQ(Counter::getCounter(2), OutputCMRs[2].Count); - ASSERT_EQ(3U, OutputCMRs[2].LineStart); + writeAndReadCoverageRegions(); + ASSERT_EQ(1u, OutputFunctions.size()); + OutputFunctionCoverageData &Output = OutputFunctions.back(); + + ASSERT_EQ(CounterMappingRegion::ExpansionRegion, Output.Regions[2].Kind); + ASSERT_EQ(Counter::getCounter(2), Output.Regions[2].Count); + ASSERT_EQ(3U, Output.Regions[2].LineStart); } -TEST_F(CoverageMappingTest, basic_coverage_iteration) { +TEST_P(MaybeSparseCoverageMappingTest, basic_coverage_iteration) { InstrProfRecord Record("func", 0x1234, {30, 20, 10, 0}); - ProfileWriter.addRecord(std::move(Record)); - readProfCounts(); + NoError(ProfileWriter.addRecord(std::move(Record))); + startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7); addCMR(Counter::getCounter(2), "file1", 5, 8, 9, 1); addCMR(Counter::getCounter(3), "file1", 10, 10, 11, 11); - loadCoverageMapping("func", 0x1234); + loadCoverageMapping(); CoverageData Data = LoadedCoverage->getCoverageForFile("file1"); std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); @@ -210,11 +357,10 @@ TEST_F(CoverageMappingTest, basic_coverage_iteration) { ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]); } -TEST_F(CoverageMappingTest, uncovered_function) { - readProfCounts(); - +TEST_P(MaybeSparseCoverageMappingTest, uncovered_function) { + startFunction("func", 0x1234); addCMR(Counter::getZero(), "file1", 1, 2, 3, 4); - loadCoverageMapping("func", 0x1234); + loadCoverageMapping(); CoverageData Data = LoadedCoverage->getCoverageForFile("file1"); std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); @@ -223,12 +369,11 @@ TEST_F(CoverageMappingTest, uncovered_function) { ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]); } -TEST_F(CoverageMappingTest, uncovered_function_with_mapping) { - readProfCounts(); - +TEST_P(MaybeSparseCoverageMappingTest, uncovered_function_with_mapping) { + startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7); - loadCoverageMapping("func", 0x1234); + loadCoverageMapping(); CoverageData Data = LoadedCoverage->getCoverageForFile("file1"); std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); @@ -238,15 +383,15 @@ TEST_F(CoverageMappingTest, uncovered_function_with_mapping) { ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]); } -TEST_F(CoverageMappingTest, combine_regions) { +TEST_P(MaybeSparseCoverageMappingTest, combine_regions) { InstrProfRecord Record("func", 0x1234, {10, 20, 30}); - ProfileWriter.addRecord(std::move(Record)); - readProfCounts(); + NoError(ProfileWriter.addRecord(std::move(Record))); + startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4); addCMR(Counter::getCounter(2), "file1", 3, 3, 4, 4); - loadCoverageMapping("func", 0x1234); + loadCoverageMapping(); CoverageData Data = LoadedCoverage->getCoverageForFile("file1"); std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); @@ -257,16 +402,40 @@ TEST_F(CoverageMappingTest, combine_regions) { ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]); } -TEST_F(CoverageMappingTest, dont_combine_expansions) { - InstrProfRecord Record("func", 0x1234, {10, 20}); - ProfileWriter.addRecord(std::move(Record)); - readProfCounts(); +TEST_P(MaybeSparseCoverageMappingTest, + restore_combined_counter_after_nested_region) { + InstrProfRecord Record("func", 0x1234, {10, 20, 40}); + NoError(ProfileWriter.addRecord(std::move(Record))); + + startFunction("func", 0x1234); + addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); + addCMR(Counter::getCounter(1), "file1", 1, 1, 9, 9); + addCMR(Counter::getCounter(2), "file1", 3, 3, 5, 5); + loadCoverageMapping(); + + CoverageData Data = LoadedCoverage->getCoverageForFile("file1"); + std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); + ASSERT_EQ(4U, Segments.size()); + EXPECT_EQ(CoverageSegment(1, 1, 30, true), Segments[0]); + EXPECT_EQ(CoverageSegment(3, 3, 40, true), Segments[1]); + EXPECT_EQ(CoverageSegment(5, 5, 30, false), Segments[2]); + EXPECT_EQ(CoverageSegment(9, 9, false), Segments[3]); +} + +// If CodeRegions and ExpansionRegions cover the same area, +// only counts of CodeRegions should be used. +TEST_P(MaybeSparseCoverageMappingTest, dont_combine_expansions) { + InstrProfRecord Record1("func", 0x1234, {10, 20}); + InstrProfRecord Record2("func", 0x1234, {0, 0}); + NoError(ProfileWriter.addRecord(std::move(Record1))); + NoError(ProfileWriter.addRecord(std::move(Record2))); + startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4); addCMR(Counter::getCounter(1), "include1", 6, 6, 7, 7); addExpansionCMR("file1", "include1", 3, 3, 4, 4); - loadCoverageMapping("func", 0x1234); + loadCoverageMapping(); CoverageData Data = LoadedCoverage->getCoverageForFile("file1"); std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); @@ -277,13 +446,51 @@ TEST_F(CoverageMappingTest, dont_combine_expansions) { ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]); } -TEST_F(CoverageMappingTest, strip_filename_prefix) { - InstrProfRecord Record("file1:func", 0x1234, {10}); - ProfileWriter.addRecord(std::move(Record)); - readProfCounts(); +// If an area is covered only by ExpansionRegions, they should be combinated. +TEST_P(MaybeSparseCoverageMappingTest, combine_expansions) { + InstrProfRecord Record("func", 0x1234, {2, 3, 7}); + NoError(ProfileWriter.addRecord(std::move(Record))); + startFunction("func", 0x1234); + addCMR(Counter::getCounter(1), "include1", 1, 1, 1, 10); + addCMR(Counter::getCounter(2), "include2", 1, 1, 1, 10); + addCMR(Counter::getCounter(0), "file", 1, 1, 5, 5); + addExpansionCMR("file", "include1", 3, 1, 3, 5); + addExpansionCMR("file", "include2", 3, 1, 3, 5); + + loadCoverageMapping(); + + CoverageData Data = LoadedCoverage->getCoverageForFile("file"); + std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); + ASSERT_EQ(4U, Segments.size()); + EXPECT_EQ(CoverageSegment(1, 1, 2, true), Segments[0]); + EXPECT_EQ(CoverageSegment(3, 1, 10, true), Segments[1]); + EXPECT_EQ(CoverageSegment(3, 5, 2, false), Segments[2]); + EXPECT_EQ(CoverageSegment(5, 5, false), Segments[3]); +} + +TEST_P(MaybeSparseCoverageMappingTest, strip_filename_prefix) { + InstrProfRecord Record("file1:func", 0x1234, {0}); + NoError(ProfileWriter.addRecord(std::move(Record))); + + startFunction("file1:func", 0x1234); addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); - loadCoverageMapping("file1:func", 0x1234); + loadCoverageMapping(); + + std::vector<std::string> Names; + for (const auto &Func : LoadedCoverage->getCoveredFunctions()) + Names.push_back(Func.Name); + ASSERT_EQ(1U, Names.size()); + ASSERT_EQ("func", Names[0]); +} + +TEST_P(MaybeSparseCoverageMappingTest, strip_unknown_filename_prefix) { + InstrProfRecord Record("<unknown>:func", 0x1234, {0}); + NoError(ProfileWriter.addRecord(std::move(Record))); + + startFunction("<unknown>:func", 0x1234); + addCMR(Counter::getCounter(0), "", 1, 1, 9, 9); + loadCoverageMapping(/*EmitFilenames=*/false); std::vector<std::string> Names; for (const auto &Func : LoadedCoverage->getCoveredFunctions()) @@ -292,4 +499,45 @@ TEST_F(CoverageMappingTest, strip_filename_prefix) { ASSERT_EQ("func", Names[0]); } +TEST_P(MaybeSparseCoverageMappingTest, dont_detect_false_instantiations) { + InstrProfRecord Record1("foo", 0x1234, {10}); + InstrProfRecord Record2("bar", 0x2345, {20}); + NoError(ProfileWriter.addRecord(std::move(Record1))); + NoError(ProfileWriter.addRecord(std::move(Record2))); + + startFunction("foo", 0x1234); + addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10); + addExpansionCMR("main", "expanded", 4, 1, 4, 5); + + startFunction("bar", 0x2345); + addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10); + addExpansionCMR("main", "expanded", 9, 1, 9, 5); + + loadCoverageMapping(); + + std::vector<const FunctionRecord *> Instantiations = + LoadedCoverage->getInstantiations("expanded"); + ASSERT_TRUE(Instantiations.empty()); +} + +TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_expanded_file) { + InstrProfRecord Record("func", 0x1234, {10}); + NoError(ProfileWriter.addRecord(std::move(Record))); + + startFunction("func", 0x1234); + addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10); + addExpansionCMR("main", "expanded", 4, 1, 4, 5); + + loadCoverageMapping(); + + CoverageData Data = LoadedCoverage->getCoverageForFile("expanded"); + std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); + ASSERT_EQ(2U, Segments.size()); + EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]); + EXPECT_EQ(CoverageSegment(1, 10, false), Segments[1]); +} + +INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseCoverageMappingTest, + ::testing::Bool()); + } // end anonymous namespace diff --git a/unittests/ProfileData/InstrProfTest.cpp b/unittests/ProfileData/InstrProfTest.cpp index 51f52f2a0771..c13f31251de4 100644 --- a/unittests/ProfileData/InstrProfTest.cpp +++ b/unittests/ProfileData/InstrProfTest.cpp @@ -7,28 +7,36 @@ // //===----------------------------------------------------------------------===// +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/ProfileData/InstrProfWriter.h" #include "llvm/Support/Compression.h" #include "gtest/gtest.h" - #include <cstdarg> using namespace llvm; -static ::testing::AssertionResult NoError(std::error_code EC) { - if (!EC) +static ::testing::AssertionResult NoError(Error E) { + if (!E) return ::testing::AssertionSuccess(); - return ::testing::AssertionFailure() << "error " << EC.value() - << ": " << EC.message(); + return ::testing::AssertionFailure() << "error: " << toString(std::move(E)) + << "\n"; } -static ::testing::AssertionResult ErrorEquals(std::error_code Expected, - std::error_code Found) { +static ::testing::AssertionResult ErrorEquals(instrprof_error Expected, + Error E) { + instrprof_error Found; + std::string FoundMsg; + handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { + Found = IPE.get(); + FoundMsg = IPE.message(); + }); if (Expected == Found) return ::testing::AssertionSuccess(); - return ::testing::AssertionFailure() << "error " << Found.value() - << ": " << Found.message(); + return ::testing::AssertionFailure() << "error: " << FoundMsg << "\n"; } namespace { @@ -37,22 +45,33 @@ struct InstrProfTest : ::testing::Test { InstrProfWriter Writer; std::unique_ptr<IndexedInstrProfReader> Reader; + void SetUp() { Writer.setOutputSparse(false); } + void readProfile(std::unique_ptr<MemoryBuffer> Profile) { auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile)); - ASSERT_TRUE(NoError(ReaderOrErr.getError())); + ASSERT_TRUE(NoError(ReaderOrErr.takeError())); Reader = std::move(ReaderOrErr.get()); } }; -TEST_F(InstrProfTest, write_and_read_empty_profile) { +struct SparseInstrProfTest : public InstrProfTest { + void SetUp() { Writer.setOutputSparse(true); } +}; + +struct MaybeSparseInstrProfTest : public InstrProfTest, + public ::testing::WithParamInterface<bool> { + void SetUp() { Writer.setOutputSparse(GetParam()); } +}; + +TEST_P(MaybeSparseInstrProfTest, write_and_read_empty_profile) { auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); ASSERT_TRUE(Reader->begin() == Reader->end()); } -TEST_F(InstrProfTest, write_and_read_one_function) { +TEST_P(MaybeSparseInstrProfTest, write_and_read_one_function) { InstrProfRecord Record("foo", 0x1234, {1, 2, 3, 4}); - Writer.addRecord(std::move(Record)); + NoError(Writer.addRecord(std::move(Record))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); @@ -68,38 +87,38 @@ TEST_F(InstrProfTest, write_and_read_one_function) { ASSERT_TRUE(++I == E); } -TEST_F(InstrProfTest, get_instr_prof_record) { +TEST_P(MaybeSparseInstrProfTest, get_instr_prof_record) { InstrProfRecord Record1("foo", 0x1234, {1, 2}); InstrProfRecord Record2("foo", 0x1235, {3, 4}); - Writer.addRecord(std::move(Record1)); - Writer.addRecord(std::move(Record2)); + NoError(Writer.addRecord(std::move(Record1))); + NoError(Writer.addRecord(std::move(Record2))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); - ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("foo", 0x1234); - ASSERT_TRUE(NoError(R.getError())); - ASSERT_EQ(2U, R.get().Counts.size()); - ASSERT_EQ(1U, R.get().Counts[0]); - ASSERT_EQ(2U, R.get().Counts[1]); + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("foo", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(2U, R->Counts.size()); + ASSERT_EQ(1U, R->Counts[0]); + ASSERT_EQ(2U, R->Counts[1]); R = Reader->getInstrProfRecord("foo", 0x1235); - ASSERT_TRUE(NoError(R.getError())); - ASSERT_EQ(2U, R.get().Counts.size()); - ASSERT_EQ(3U, R.get().Counts[0]); - ASSERT_EQ(4U, R.get().Counts[1]); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(2U, R->Counts.size()); + ASSERT_EQ(3U, R->Counts[0]); + ASSERT_EQ(4U, R->Counts[1]); R = Reader->getInstrProfRecord("foo", 0x5678); - ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, R.getError())); + ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, R.takeError())); R = Reader->getInstrProfRecord("bar", 0x1234); - ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, R.getError())); + ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, R.takeError())); } -TEST_F(InstrProfTest, get_function_counts) { +TEST_P(MaybeSparseInstrProfTest, get_function_counts) { InstrProfRecord Record1("foo", 0x1234, {1, 2}); InstrProfRecord Record2("foo", 0x1235, {3, 4}); - Writer.addRecord(std::move(Record1)); - Writer.addRecord(std::move(Record2)); + NoError(Writer.addRecord(std::move(Record1))); + NoError(Writer.addRecord(std::move(Record2))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); @@ -114,15 +133,85 @@ TEST_F(InstrProfTest, get_function_counts) { ASSERT_EQ(3U, Counts[0]); ASSERT_EQ(4U, Counts[1]); - std::error_code EC; - EC = Reader->getFunctionCounts("foo", 0x5678, Counts); - ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, EC)); + Error E1 = Reader->getFunctionCounts("foo", 0x5678, Counts); + ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, std::move(E1))); - EC = Reader->getFunctionCounts("bar", 0x1234, Counts); - ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, EC)); + Error E2 = Reader->getFunctionCounts("bar", 0x1234, Counts); + ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, std::move(E2))); } -TEST_F(InstrProfTest, get_icall_data_read_write) { +// Profile data is copied from general.proftext +TEST_F(InstrProfTest, get_profile_summary) { + InstrProfRecord Record1("func1", 0x1234, {97531}); + InstrProfRecord Record2("func2", 0x1234, {0, 0}); + InstrProfRecord Record3("func3", 0x1234, + {2305843009213693952, 1152921504606846976, + 576460752303423488, 288230376151711744, + 144115188075855872, 72057594037927936}); + InstrProfRecord Record4("func4", 0x1234, {0}); + NoError(Writer.addRecord(std::move(Record1))); + NoError(Writer.addRecord(std::move(Record2))); + NoError(Writer.addRecord(std::move(Record3))); + NoError(Writer.addRecord(std::move(Record4))); + auto Profile = Writer.writeBuffer(); + readProfile(std::move(Profile)); + + auto VerifySummary = [](ProfileSummary &IPS) mutable { + ASSERT_EQ(ProfileSummary::PSK_Instr, IPS.getKind()); + ASSERT_EQ(2305843009213693952U, IPS.getMaxFunctionCount()); + ASSERT_EQ(2305843009213693952U, IPS.getMaxCount()); + ASSERT_EQ(10U, IPS.getNumCounts()); + ASSERT_EQ(4539628424389557499U, IPS.getTotalCount()); + std::vector<ProfileSummaryEntry> &Details = IPS.getDetailedSummary(); + uint32_t Cutoff = 800000; + auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) { + return PE.Cutoff == Cutoff; + }; + auto EightyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + Cutoff = 900000; + auto NinetyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + Cutoff = 950000; + auto NinetyFivePerc = + std::find_if(Details.begin(), Details.end(), Predicate); + Cutoff = 990000; + auto NinetyNinePerc = + std::find_if(Details.begin(), Details.end(), Predicate); + ASSERT_EQ(576460752303423488U, EightyPerc->MinCount); + ASSERT_EQ(288230376151711744U, NinetyPerc->MinCount); + ASSERT_EQ(288230376151711744U, NinetyFivePerc->MinCount); + ASSERT_EQ(72057594037927936U, NinetyNinePerc->MinCount); + }; + ProfileSummary &PS = Reader->getSummary(); + VerifySummary(PS); + + // Test that conversion of summary to and from Metadata works. + LLVMContext Context; + Metadata *MD = PS.getMD(Context); + ASSERT_TRUE(MD); + ProfileSummary *PSFromMD = ProfileSummary::getFromMD(MD); + ASSERT_TRUE(PSFromMD); + VerifySummary(*PSFromMD); + delete PSFromMD; + + // Test that summary can be attached to and read back from module. + Module M("my_module", Context); + M.setProfileSummary(MD); + MD = M.getProfileSummary(); + ASSERT_TRUE(MD); + PSFromMD = ProfileSummary::getFromMD(MD); + ASSERT_TRUE(PSFromMD); + VerifySummary(*PSFromMD); + delete PSFromMD; +} + +static const char callee1[] = "callee1"; +static const char callee2[] = "callee2"; +static const char callee3[] = "callee3"; +static const char callee4[] = "callee4"; +static const char callee5[] = "callee5"; +static const char callee6[] = "callee6"; + +TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write) { InstrProfRecord Record1("caller", 0x1234, {1, 2}); InstrProfRecord Record2("callee1", 0x1235, {3, 4}); InstrProfRecord Record3("callee2", 0x1235, {3, 4}); @@ -130,46 +219,142 @@ TEST_F(InstrProfTest, get_icall_data_read_write) { // 4 value sites. Record1.reserveSites(IPVK_IndirectCallTarget, 4); - InstrProfValueData VD0[] = {{(uint64_t) "callee1", 1}, - {(uint64_t) "callee2", 2}, - {(uint64_t) "callee3", 3}}; + InstrProfValueData VD0[] = { + {(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}}; Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr); // No value profile data at the second site. Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr); - InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}, - {(uint64_t) "callee2", 2}}; + InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}}; Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr); - InstrProfValueData VD3[] = {{(uint64_t) "callee1", 1}}; + InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}}; Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr); - Writer.addRecord(std::move(Record1)); - Writer.addRecord(std::move(Record2)); - Writer.addRecord(std::move(Record3)); - Writer.addRecord(std::move(Record4)); + NoError(Writer.addRecord(std::move(Record1))); + NoError(Writer.addRecord(std::move(Record2))); + NoError(Writer.addRecord(std::move(Record3))); + NoError(Writer.addRecord(std::move(Record4))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); - ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); - ASSERT_TRUE(NoError(R.getError())); - ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget)); - ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); - ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); - ASSERT_EQ(2U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); - ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); + ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); + ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); + ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); + uint64_t TotalC; std::unique_ptr<InstrProfValueData[]> VD = - R.get().getValueForSite(IPVK_IndirectCallTarget, 0); + R->getValueForSite(IPVK_IndirectCallTarget, 0, &TotalC); ASSERT_EQ(3U, VD[0].Count); ASSERT_EQ(2U, VD[1].Count); ASSERT_EQ(1U, VD[2].Count); + ASSERT_EQ(6U, TotalC); ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3")); ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2")); ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1")); } -TEST_F(InstrProfTest, get_icall_data_read_write_with_weight) { +TEST_P(MaybeSparseInstrProfTest, annotate_vp_data) { + InstrProfRecord Record("caller", 0x1234, {1, 2}); + Record.reserveSites(IPVK_IndirectCallTarget, 1); + InstrProfValueData VD0[] = {{1000, 1}, {2000, 2}, {3000, 3}, {5000, 5}, + {4000, 4}, {6000, 6}}; + Record.addValueData(IPVK_IndirectCallTarget, 0, VD0, 6, nullptr); + NoError(Writer.addRecord(std::move(Record))); + auto Profile = Writer.writeBuffer(); + readProfile(std::move(Profile)); + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + + LLVMContext Ctx; + std::unique_ptr<Module> M(new Module("MyModule", Ctx)); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), + /*isVarArg=*/false); + Function *F = + Function::Create(FTy, Function::ExternalLinkage, "caller", M.get()); + BasicBlock *BB = BasicBlock::Create(Ctx, "", F); + + IRBuilder<> Builder(BB); + BasicBlock *TBB = BasicBlock::Create(Ctx, "", F); + BasicBlock *FBB = BasicBlock::Create(Ctx, "", F); + + // Use branch instruction to annotate with value profile data for simplicity + Instruction *Inst = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB); + Instruction *Inst2 = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB); + annotateValueSite(*M, *Inst, R.get(), IPVK_IndirectCallTarget, 0); + + InstrProfValueData ValueData[5]; + uint32_t N; + uint64_t T; + bool Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5, + ValueData, N, T); + ASSERT_TRUE(Res); + ASSERT_EQ(3U, N); + ASSERT_EQ(21U, T); + // The result should be sorted already: + ASSERT_EQ(6000U, ValueData[0].Value); + ASSERT_EQ(6U, ValueData[0].Count); + ASSERT_EQ(5000U, ValueData[1].Value); + ASSERT_EQ(5U, ValueData[1].Count); + ASSERT_EQ(4000U, ValueData[2].Value); + ASSERT_EQ(4U, ValueData[2].Count); + Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 1, ValueData, + N, T); + ASSERT_TRUE(Res); + ASSERT_EQ(1U, N); + ASSERT_EQ(21U, T); + + Res = getValueProfDataFromInst(*Inst2, IPVK_IndirectCallTarget, 5, ValueData, + N, T); + ASSERT_FALSE(Res); + + // Remove the MD_prof metadata + Inst->setMetadata(LLVMContext::MD_prof, 0); + // Annotate 5 records this time. + annotateValueSite(*M, *Inst, R.get(), IPVK_IndirectCallTarget, 0, 5); + Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5, + ValueData, N, T); + ASSERT_TRUE(Res); + ASSERT_EQ(5U, N); + ASSERT_EQ(21U, T); + ASSERT_EQ(6000U, ValueData[0].Value); + ASSERT_EQ(6U, ValueData[0].Count); + ASSERT_EQ(5000U, ValueData[1].Value); + ASSERT_EQ(5U, ValueData[1].Count); + ASSERT_EQ(4000U, ValueData[2].Value); + ASSERT_EQ(4U, ValueData[2].Count); + ASSERT_EQ(3000U, ValueData[3].Value); + ASSERT_EQ(3U, ValueData[3].Count); + ASSERT_EQ(2000U, ValueData[4].Value); + ASSERT_EQ(2U, ValueData[4].Count); + + // Remove the MD_prof metadata + Inst->setMetadata(LLVMContext::MD_prof, 0); + // Annotate with 4 records. + InstrProfValueData VD0Sorted[] = {{1000, 6}, {2000, 5}, {3000, 4}, {4000, 3}, + {5000, 2}, {6000, 1}}; + annotateValueSite(*M, *Inst, makeArrayRef(VD0Sorted).slice(2), 10, + IPVK_IndirectCallTarget, 5); + Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5, + ValueData, N, T); + ASSERT_TRUE(Res); + ASSERT_EQ(4U, N); + ASSERT_EQ(10U, T); + ASSERT_EQ(3000U, ValueData[0].Value); + ASSERT_EQ(4U, ValueData[0].Count); + ASSERT_EQ(4000U, ValueData[1].Value); + ASSERT_EQ(3U, ValueData[1].Count); + ASSERT_EQ(5000U, ValueData[2].Value); + ASSERT_EQ(2U, ValueData[2].Count); + ASSERT_EQ(6000U, ValueData[3].Value); + ASSERT_EQ(1U, ValueData[3].Count); +} + +TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_with_weight) { InstrProfRecord Record1("caller", 0x1234, {1, 2}); InstrProfRecord Record2("callee1", 0x1235, {3, 4}); InstrProfRecord Record3("callee2", 0x1235, {3, 4}); @@ -177,45 +362,45 @@ TEST_F(InstrProfTest, get_icall_data_read_write_with_weight) { // 4 value sites. Record1.reserveSites(IPVK_IndirectCallTarget, 4); - InstrProfValueData VD0[] = {{(uint64_t) "callee1", 1}, - {(uint64_t) "callee2", 2}, - {(uint64_t) "callee3", 3}}; + InstrProfValueData VD0[] = { + {(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}}; Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr); // No value profile data at the second site. Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr); - InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}, - {(uint64_t) "callee2", 2}}; + InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}}; Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr); - InstrProfValueData VD3[] = {{(uint64_t) "callee1", 1}}; + InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}}; Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr); - Writer.addRecord(std::move(Record1), 10); - Writer.addRecord(std::move(Record2)); - Writer.addRecord(std::move(Record3)); - Writer.addRecord(std::move(Record4)); + NoError(Writer.addRecord(std::move(Record1), 10)); + NoError(Writer.addRecord(std::move(Record2))); + NoError(Writer.addRecord(std::move(Record3))); + NoError(Writer.addRecord(std::move(Record4))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); - ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); - ASSERT_TRUE(NoError(R.getError())); - ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget)); - ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); - ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); - ASSERT_EQ(2U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); - ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); + ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); + ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); + ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); + uint64_t TotalC; std::unique_ptr<InstrProfValueData[]> VD = - R.get().getValueForSite(IPVK_IndirectCallTarget, 0); + R->getValueForSite(IPVK_IndirectCallTarget, 0, &TotalC); ASSERT_EQ(30U, VD[0].Count); ASSERT_EQ(20U, VD[1].Count); ASSERT_EQ(10U, VD[2].Count); + ASSERT_EQ(60U, TotalC); ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3")); ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2")); ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1")); } -TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) { +TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_big_endian) { InstrProfRecord Record1("caller", 0x1234, {1, 2}); InstrProfRecord Record2("callee1", 0x1235, {3, 4}); InstrProfRecord Record3("callee2", 0x1235, {3, 4}); @@ -223,22 +408,20 @@ TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) { // 4 value sites. Record1.reserveSites(IPVK_IndirectCallTarget, 4); - InstrProfValueData VD0[] = {{(uint64_t) "callee1", 1}, - {(uint64_t) "callee2", 2}, - {(uint64_t) "callee3", 3}}; + InstrProfValueData VD0[] = { + {(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}}; Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr); // No value profile data at the second site. Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr); - InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}, - {(uint64_t) "callee2", 2}}; + InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}}; Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr); - InstrProfValueData VD3[] = {{(uint64_t) "callee1", 1}}; + InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}}; Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr); - Writer.addRecord(std::move(Record1)); - Writer.addRecord(std::move(Record2)); - Writer.addRecord(std::move(Record3)); - Writer.addRecord(std::move(Record4)); + NoError(Writer.addRecord(std::move(Record1))); + NoError(Writer.addRecord(std::move(Record2))); + NoError(Writer.addRecord(std::move(Record3))); + NoError(Writer.addRecord(std::move(Record4))); // Set big endian output. Writer.setValueProfDataEndianness(support::big); @@ -249,16 +432,16 @@ TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) { // Set big endian input. Reader->setValueProfDataEndianness(support::big); - ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); - ASSERT_TRUE(NoError(R.getError())); - ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget)); - ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); - ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); - ASSERT_EQ(2U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); - ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); + ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); + ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); + ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); std::unique_ptr<InstrProfValueData[]> VD = - R.get().getValueForSite(IPVK_IndirectCallTarget, 0); + R->getValueForSite(IPVK_IndirectCallTarget, 0); ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3")); ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2")); ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1")); @@ -267,13 +450,8 @@ TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) { Writer.setValueProfDataEndianness(support::little); } -TEST_F(InstrProfTest, get_icall_data_merge1) { +TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1) { static const char caller[] = "caller"; - static const char callee1[] = "callee1"; - static const char callee2[] = "callee2"; - static const char callee3[] = "callee3"; - static const char callee4[] = "callee4"; - InstrProfRecord Record11(caller, 0x1234, {1, 2}); InstrProfRecord Record12(caller, 0x1234, {1, 2}); InstrProfRecord Record2(callee1, 0x1235, {3, 4}); @@ -305,7 +483,7 @@ TEST_F(InstrProfTest, get_icall_data_merge1) { {uint64_t(callee3), 3}}; Record11.addValueData(IPVK_IndirectCallTarget, 4, VD4, 3, nullptr); - // A differnt record for the same caller. + // A different record for the same caller. Record12.reserveSites(IPVK_IndirectCallTarget, 5); InstrProfValueData VD02[] = {{uint64_t(callee2), 5}, {uint64_t(callee3), 3}}; Record12.addValueData(IPVK_IndirectCallTarget, 0, VD02, 2, nullptr); @@ -324,29 +502,29 @@ TEST_F(InstrProfTest, get_icall_data_merge1) { {uint64_t(callee3), 3}}; Record12.addValueData(IPVK_IndirectCallTarget, 4, VD42, 3, nullptr); - Writer.addRecord(std::move(Record11)); + NoError(Writer.addRecord(std::move(Record11))); // Merge profile data. - Writer.addRecord(std::move(Record12)); + NoError(Writer.addRecord(std::move(Record12))); - Writer.addRecord(std::move(Record2)); - Writer.addRecord(std::move(Record3)); - Writer.addRecord(std::move(Record4)); - Writer.addRecord(std::move(Record5)); - Writer.addRecord(std::move(Record6)); + NoError(Writer.addRecord(std::move(Record2))); + NoError(Writer.addRecord(std::move(Record3))); + NoError(Writer.addRecord(std::move(Record4))); + NoError(Writer.addRecord(std::move(Record5))); + NoError(Writer.addRecord(std::move(Record6))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); - ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); - ASSERT_TRUE(NoError(R.getError())); - ASSERT_EQ(5U, R.get().getNumValueSites(IPVK_IndirectCallTarget)); - ASSERT_EQ(4U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); - ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); - ASSERT_EQ(4U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); - ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); - ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 4)); + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(5U, R->getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_EQ(4U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); + ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); + ASSERT_EQ(4U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); + ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); + ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 4)); std::unique_ptr<InstrProfValueData[]> VD = - R.get().getValueForSite(IPVK_IndirectCallTarget, 0); + R->getValueForSite(IPVK_IndirectCallTarget, 0); ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee2")); ASSERT_EQ(7U, VD[0].Count); ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee3")); @@ -357,7 +535,7 @@ TEST_F(InstrProfTest, get_icall_data_merge1) { ASSERT_EQ(1U, VD[3].Count); std::unique_ptr<InstrProfValueData[]> VD_2( - R.get().getValueForSite(IPVK_IndirectCallTarget, 2)); + R->getValueForSite(IPVK_IndirectCallTarget, 2)); ASSERT_EQ(StringRef((const char *)VD_2[0].Value, 7), StringRef("callee3")); ASSERT_EQ(6U, VD_2[0].Count); ASSERT_EQ(StringRef((const char *)VD_2[1].Value, 7), StringRef("callee4")); @@ -368,12 +546,12 @@ TEST_F(InstrProfTest, get_icall_data_merge1) { ASSERT_EQ(1U, VD_2[3].Count); std::unique_ptr<InstrProfValueData[]> VD_3( - R.get().getValueForSite(IPVK_IndirectCallTarget, 3)); + R->getValueForSite(IPVK_IndirectCallTarget, 3)); ASSERT_EQ(StringRef((const char *)VD_3[0].Value, 7), StringRef("callee1")); ASSERT_EQ(1U, VD_3[0].Count); std::unique_ptr<InstrProfValueData[]> VD_4( - R.get().getValueForSite(IPVK_IndirectCallTarget, 4)); + R->getValueForSite(IPVK_IndirectCallTarget, 4)); ASSERT_EQ(StringRef((const char *)VD_4[0].Value, 7), StringRef("callee3")); ASSERT_EQ(6U, VD_4[0].Count); ASSERT_EQ(StringRef((const char *)VD_4[1].Value, 7), StringRef("callee2")); @@ -382,30 +560,34 @@ TEST_F(InstrProfTest, get_icall_data_merge1) { ASSERT_EQ(2U, VD_4[2].Count); } -TEST_F(InstrProfTest, get_icall_data_merge1_saturation) { +TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1_saturation) { static const char bar[] = "bar"; const uint64_t Max = std::numeric_limits<uint64_t>::max(); InstrProfRecord Record1("foo", 0x1234, {1}); auto Result1 = Writer.addRecord(std::move(Record1)); - ASSERT_EQ(Result1, instrprof_error::success); + ASSERT_EQ(InstrProfError::take(std::move(Result1)), + instrprof_error::success); // Verify counter overflow. InstrProfRecord Record2("foo", 0x1234, {Max}); auto Result2 = Writer.addRecord(std::move(Record2)); - ASSERT_EQ(Result2, instrprof_error::counter_overflow); + ASSERT_EQ(InstrProfError::take(std::move(Result2)), + instrprof_error::counter_overflow); InstrProfRecord Record3(bar, 0x9012, {8}); auto Result3 = Writer.addRecord(std::move(Record3)); - ASSERT_EQ(Result3, instrprof_error::success); + ASSERT_EQ(InstrProfError::take(std::move(Result3)), + instrprof_error::success); InstrProfRecord Record4("baz", 0x5678, {3, 4}); Record4.reserveSites(IPVK_IndirectCallTarget, 1); InstrProfValueData VD4[] = {{uint64_t(bar), 1}}; Record4.addValueData(IPVK_IndirectCallTarget, 0, VD4, 1, nullptr); auto Result4 = Writer.addRecord(std::move(Record4)); - ASSERT_EQ(Result4, instrprof_error::success); + ASSERT_EQ(InstrProfError::take(std::move(Result4)), + instrprof_error::success); // Verify value data counter overflow. InstrProfRecord Record5("baz", 0x5678, {5, 6}); @@ -413,22 +595,24 @@ TEST_F(InstrProfTest, get_icall_data_merge1_saturation) { InstrProfValueData VD5[] = {{uint64_t(bar), Max}}; Record5.addValueData(IPVK_IndirectCallTarget, 0, VD5, 1, nullptr); auto Result5 = Writer.addRecord(std::move(Record5)); - ASSERT_EQ(Result5, instrprof_error::counter_overflow); + ASSERT_EQ(InstrProfError::take(std::move(Result5)), + instrprof_error::counter_overflow); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); // Verify saturation of counts. - ErrorOr<InstrProfRecord> ReadRecord1 = + Expected<InstrProfRecord> ReadRecord1 = Reader->getInstrProfRecord("foo", 0x1234); - ASSERT_TRUE(NoError(ReadRecord1.getError())); - ASSERT_EQ(Max, ReadRecord1.get().Counts[0]); + ASSERT_TRUE(NoError(ReadRecord1.takeError())); + ASSERT_EQ(Max, ReadRecord1->Counts[0]); - ErrorOr<InstrProfRecord> ReadRecord2 = + Expected<InstrProfRecord> ReadRecord2 = Reader->getInstrProfRecord("baz", 0x5678); - ASSERT_EQ(1U, ReadRecord2.get().getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_TRUE(bool(ReadRecord2)); + ASSERT_EQ(1U, ReadRecord2->getNumValueSites(IPVK_IndirectCallTarget)); std::unique_ptr<InstrProfValueData[]> VD = - ReadRecord2.get().getValueForSite(IPVK_IndirectCallTarget, 0); + ReadRecord2->getValueForSite(IPVK_IndirectCallTarget, 0); ASSERT_EQ(StringRef("bar"), StringRef((const char *)VD[0].Value, 3)); ASSERT_EQ(Max, VD[0].Count); } @@ -436,7 +620,7 @@ TEST_F(InstrProfTest, get_icall_data_merge1_saturation) { // This test tests that when there are too many values // for a given site, the merged results are properly // truncated. -TEST_F(InstrProfTest, get_icall_data_merge_site_trunc) { +TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge_site_trunc) { static const char caller[] = "caller"; InstrProfRecord Record11(caller, 0x1234, {1, 2}); @@ -463,57 +647,56 @@ TEST_F(InstrProfTest, get_icall_data_merge_site_trunc) { Record12.addValueData(IPVK_IndirectCallTarget, 0, VD1, 255, nullptr); Record12.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr); - Writer.addRecord(std::move(Record11)); + NoError(Writer.addRecord(std::move(Record11))); // Merge profile data. - Writer.addRecord(std::move(Record12)); + NoError(Writer.addRecord(std::move(Record12))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); - ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); - ASSERT_TRUE(NoError(R.getError())); + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); std::unique_ptr<InstrProfValueData[]> VD( - R.get().getValueForSite(IPVK_IndirectCallTarget, 0)); - ASSERT_EQ(2U, R.get().getNumValueSites(IPVK_IndirectCallTarget)); - ASSERT_EQ(255U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); + R->getValueForSite(IPVK_IndirectCallTarget, 0)); + ASSERT_EQ(2U, R->getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_EQ(255U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); for (unsigned I = 0; I < 255; I++) { ASSERT_EQ(VD[I].Value, 509 - I); ASSERT_EQ(VD[I].Count, 1509 - I); } } -// Synthesize runtime value profile data. -ValueProfNode Site1Values[5] = {{{uint64_t("callee1"), 400}, &Site1Values[1]}, - {{uint64_t("callee2"), 1000}, &Site1Values[2]}, - {{uint64_t("callee3"), 500}, &Site1Values[3]}, - {{uint64_t("callee4"), 300}, &Site1Values[4]}, - {{uint64_t("callee5"), 100}, 0}}; - -ValueProfNode Site2Values[4] = {{{uint64_t("callee5"), 800}, &Site2Values[1]}, - {{uint64_t("callee3"), 1000}, &Site2Values[2]}, - {{uint64_t("callee2"), 2500}, &Site2Values[3]}, - {{uint64_t("callee1"), 1300}, 0}}; - -ValueProfNode Site3Values[3] = {{{uint64_t("callee6"), 800}, &Site3Values[1]}, - {{uint64_t("callee3"), 1000}, &Site3Values[2]}, - {{uint64_t("callee4"), 5500}, 0}}; - -ValueProfNode Site4Values[2] = {{{uint64_t("callee2"), 1800}, &Site4Values[1]}, - {{uint64_t("callee3"), 2000}, 0}}; - -static ValueProfNode *ValueProfNodes[5] = {&Site1Values[0], &Site2Values[0], - &Site3Values[0], &Site4Values[0], 0}; -static uint16_t NumValueSites[IPVK_Last + 1] = {5}; -TEST_F(InstrProfTest, runtime_value_prof_data_read_write) { - ValueProfRuntimeRecord RTRecord; - initializeValueProfRuntimeRecord(&RTRecord, &NumValueSites[0], - &ValueProfNodes[0]); +static void addValueProfData(InstrProfRecord &Record) { + Record.reserveSites(IPVK_IndirectCallTarget, 5); + InstrProfValueData VD0[] = {{uint64_t(callee1), 400}, + {uint64_t(callee2), 1000}, + {uint64_t(callee3), 500}, + {uint64_t(callee4), 300}, + {uint64_t(callee5), 100}}; + Record.addValueData(IPVK_IndirectCallTarget, 0, VD0, 5, nullptr); + InstrProfValueData VD1[] = {{uint64_t(callee5), 800}, + {uint64_t(callee3), 1000}, + {uint64_t(callee2), 2500}, + {uint64_t(callee1), 1300}}; + Record.addValueData(IPVK_IndirectCallTarget, 1, VD1, 4, nullptr); + InstrProfValueData VD2[] = {{uint64_t(callee6), 800}, + {uint64_t(callee3), 1000}, + {uint64_t(callee4), 5500}}; + Record.addValueData(IPVK_IndirectCallTarget, 2, VD2, 3, nullptr); + InstrProfValueData VD3[] = {{uint64_t(callee2), 1800}, + {uint64_t(callee3), 2000}}; + Record.addValueData(IPVK_IndirectCallTarget, 3, VD3, 2, nullptr); + Record.addValueData(IPVK_IndirectCallTarget, 4, nullptr, 0, nullptr); +} - ValueProfData *VPData = serializeValueProfDataFromRT(&RTRecord, nullptr); +TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write) { + InstrProfRecord SrcRecord("caller", 0x1234, {1ULL << 31, 2}); + addValueProfData(SrcRecord); + std::unique_ptr<ValueProfData> VPData = + ValueProfData::serializeFrom(SrcRecord); InstrProfRecord Record("caller", 0x1234, {1ULL << 31, 2}); - - VPData->deserializeTo(Record, 0); + VPData->deserializeTo(Record, nullptr); // Now read data from Record and sanity check the data ASSERT_EQ(5U, Record.getNumValueSites(IPVK_IndirectCallTarget)); @@ -569,29 +752,65 @@ TEST_F(InstrProfTest, runtime_value_prof_data_read_write) { ASSERT_EQ(2000U, VD_3[0].Count); ASSERT_EQ(StringRef((const char *)VD_3[1].Value, 7), StringRef("callee2")); ASSERT_EQ(1800U, VD_3[1].Count); +} + +TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write_mapping) { + + InstrProfRecord SrcRecord("caller", 0x1234, {1ULL << 31, 2}); + addValueProfData(SrcRecord); + std::unique_ptr<ValueProfData> VPData = + ValueProfData::serializeFrom(SrcRecord); + + InstrProfRecord Record("caller", 0x1234, {1ULL << 31, 2}); + InstrProfSymtab Symtab; + Symtab.mapAddress(uint64_t(callee1), 0x1000ULL); + Symtab.mapAddress(uint64_t(callee2), 0x2000ULL); + Symtab.mapAddress(uint64_t(callee3), 0x3000ULL); + Symtab.mapAddress(uint64_t(callee4), 0x4000ULL); + // Missing mapping for callee5 + Symtab.finalizeSymtab(); + + VPData->deserializeTo(Record, &Symtab.getAddrHashMap()); + + // Now read data from Record and sanity check the data + ASSERT_EQ(5U, Record.getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_EQ(5U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); + + auto Cmp = [](const InstrProfValueData &VD1, const InstrProfValueData &VD2) { + return VD1.Count > VD2.Count; + }; + std::unique_ptr<InstrProfValueData[]> VD_0( + Record.getValueForSite(IPVK_IndirectCallTarget, 0)); + std::sort(&VD_0[0], &VD_0[5], Cmp); + ASSERT_EQ(VD_0[0].Value, 0x2000ULL); + ASSERT_EQ(1000U, VD_0[0].Count); + ASSERT_EQ(VD_0[1].Value, 0x3000ULL); + ASSERT_EQ(500U, VD_0[1].Count); + ASSERT_EQ(VD_0[2].Value, 0x1000ULL); + ASSERT_EQ(400U, VD_0[2].Count); - finalizeValueProfRuntimeRecord(&RTRecord); - free(VPData); + // callee5 does not have a mapped value -- default to 0. + ASSERT_EQ(VD_0[4].Value, 0ULL); } -TEST_F(InstrProfTest, get_max_function_count) { +TEST_P(MaybeSparseInstrProfTest, get_max_function_count) { InstrProfRecord Record1("foo", 0x1234, {1ULL << 31, 2}); InstrProfRecord Record2("bar", 0, {1ULL << 63}); InstrProfRecord Record3("baz", 0x5678, {0, 0, 0, 0}); - Writer.addRecord(std::move(Record1)); - Writer.addRecord(std::move(Record2)); - Writer.addRecord(std::move(Record3)); + NoError(Writer.addRecord(std::move(Record1))); + NoError(Writer.addRecord(std::move(Record2))); + NoError(Writer.addRecord(std::move(Record3))); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount()); } -TEST_F(InstrProfTest, get_weighted_function_counts) { +TEST_P(MaybeSparseInstrProfTest, get_weighted_function_counts) { InstrProfRecord Record1("foo", 0x1234, {1, 2}); InstrProfRecord Record2("foo", 0x1235, {3, 4}); - Writer.addRecord(std::move(Record1), 3); - Writer.addRecord(std::move(Record2), 5); + NoError(Writer.addRecord(std::move(Record1), 3)); + NoError(Writer.addRecord(std::move(Record2), 5)); auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); @@ -607,7 +826,8 @@ TEST_F(InstrProfTest, get_weighted_function_counts) { ASSERT_EQ(20U, Counts[1]); } -TEST_F(InstrProfTest, instr_prof_symtab_test) { +// Testing symtab creator interface used by indexed profile reader. +TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_test) { std::vector<StringRef> FuncNames; FuncNames.push_back("func1"); FuncNames.push_back("func2"); @@ -630,6 +850,12 @@ TEST_F(InstrProfTest, instr_prof_symtab_test) { R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar3")); ASSERT_EQ(StringRef("bar3"), R); + // negative tests + R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar4")); + ASSERT_EQ(StringRef(), R); + R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("foo4")); + ASSERT_EQ(StringRef(), R); + // Now incrementally update the symtab Symtab.addFuncName("blah_1"); Symtab.addFuncName("blah_2"); @@ -658,16 +884,54 @@ TEST_F(InstrProfTest, instr_prof_symtab_test) { ASSERT_EQ(StringRef("bar3"), R); } -TEST_F(InstrProfTest, instr_prof_symtab_compression_test) { +// Testing symtab creator interface used by value profile transformer. +TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_module_test) { + LLVMContext Ctx; + std::unique_ptr<Module> M = llvm::make_unique<Module>("MyModule.cpp", Ctx); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), + /*isVarArg=*/false); + Function::Create(FTy, Function::ExternalLinkage, "Gfoo", M.get()); + Function::Create(FTy, Function::ExternalLinkage, "Gblah", M.get()); + Function::Create(FTy, Function::ExternalLinkage, "Gbar", M.get()); + Function::Create(FTy, Function::InternalLinkage, "Ifoo", M.get()); + Function::Create(FTy, Function::InternalLinkage, "Iblah", M.get()); + Function::Create(FTy, Function::InternalLinkage, "Ibar", M.get()); + Function::Create(FTy, Function::PrivateLinkage, "Pfoo", M.get()); + Function::Create(FTy, Function::PrivateLinkage, "Pblah", M.get()); + Function::Create(FTy, Function::PrivateLinkage, "Pbar", M.get()); + Function::Create(FTy, Function::WeakODRLinkage, "Wfoo", M.get()); + Function::Create(FTy, Function::WeakODRLinkage, "Wblah", M.get()); + Function::Create(FTy, Function::WeakODRLinkage, "Wbar", M.get()); + + InstrProfSymtab ProfSymtab; + ProfSymtab.create(*M); + + StringRef Funcs[] = {"Gfoo", "Gblah", "Gbar", "Ifoo", "Iblah", "Ibar", + "Pfoo", "Pblah", "Pbar", "Wfoo", "Wblah", "Wbar"}; + + for (unsigned I = 0; I < sizeof(Funcs) / sizeof(*Funcs); I++) { + Function *F = M->getFunction(Funcs[I]); + ASSERT_TRUE(F != nullptr); + std::string PGOName = getPGOFuncName(*F); + uint64_t Key = IndexedInstrProf::ComputeHash(PGOName); + ASSERT_EQ(StringRef(PGOName), + ProfSymtab.getFuncName(Key)); + ASSERT_EQ(StringRef(Funcs[I]), ProfSymtab.getOrigFuncName(Key)); + } +} + +// Testing symtab serialization and creator/deserialization interface +// used by coverage map reader, and raw profile reader. +TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_compression_test) { std::vector<std::string> FuncNames1; std::vector<std::string> FuncNames2; - for (int I = 0; I < 10 * 1024; I++) { + for (int I = 0; I < 3; I++) { std::string str; raw_string_ostream OS(str); OS << "func_" << I; FuncNames1.push_back(OS.str()); str.clear(); - OS << "fooooooooooooooo_" << I; + OS << "f oooooooooooooo_" << I; FuncNames1.push_back(OS.str()); str.clear(); OS << "BAR_" << I; @@ -677,21 +941,19 @@ TEST_F(InstrProfTest, instr_prof_symtab_compression_test) { FuncNames2.push_back(OS.str()); } - for (int Padding = 0; Padding < 10; Padding++) { - for (int DoCompression = 0; DoCompression < 2; DoCompression++) { - // Compressing: - std::string FuncNameStrings1; - collectPGOFuncNameStrings(FuncNames1, - (DoCompression != 0 && zlib::isAvailable()), - FuncNameStrings1); - - // Compressing: - std::string FuncNameStrings2; - collectPGOFuncNameStrings(FuncNames2, - (DoCompression != 0 && zlib::isAvailable()), - FuncNameStrings2); - - // Join with paddings: + for (bool DoCompression : {false, true}) { + // Compressing: + std::string FuncNameStrings1; + NoError(collectPGOFuncNameStrings( + FuncNames1, (DoCompression && zlib::isAvailable()), FuncNameStrings1)); + + // Compressing: + std::string FuncNameStrings2; + NoError(collectPGOFuncNameStrings( + FuncNames2, (DoCompression && zlib::isAvailable()), FuncNameStrings2)); + + for (int Padding = 0; Padding < 2; Padding++) { + // Join with paddings : std::string FuncNameStrings = FuncNameStrings1; for (int P = 0; P < Padding; P++) { FuncNameStrings.push_back('\0'); @@ -700,23 +962,15 @@ TEST_F(InstrProfTest, instr_prof_symtab_compression_test) { // Now decompress: InstrProfSymtab Symtab; - Symtab.create(StringRef(FuncNameStrings)); + NoError(Symtab.create(StringRef(FuncNameStrings))); // Now do the checks: // First sampling some data points: StringRef R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[0])); ASSERT_EQ(StringRef("func_0"), R); R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[1])); - ASSERT_EQ(StringRef("fooooooooooooooo_0"), R); - R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[998])); - ASSERT_EQ(StringRef("func_499"), R); - R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[999])); - ASSERT_EQ(StringRef("fooooooooooooooo_499"), R); - R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames2[100])); - ASSERT_EQ(StringRef("BAR_50"), R); - R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames2[101])); - ASSERT_EQ(StringRef("BlahblahBlahblahBar_50"), R); - for (int I = 0; I < 10 * 1024; I++) { + ASSERT_EQ(StringRef("f oooooooooooooo_0"), R); + for (int I = 0; I < 3; I++) { std::string N[4]; N[0] = FuncNames1[2 * I]; N[1] = FuncNames1[2 * I + 1]; @@ -731,4 +985,22 @@ TEST_F(InstrProfTest, instr_prof_symtab_compression_test) { } } +TEST_F(SparseInstrProfTest, preserve_no_records) { + InstrProfRecord Record1("foo", 0x1234, {0}); + InstrProfRecord Record2("bar", 0x4321, {0, 0}); + InstrProfRecord Record3("bar", 0x4321, {0, 0, 0}); + + NoError(Writer.addRecord(std::move(Record1))); + NoError(Writer.addRecord(std::move(Record2))); + NoError(Writer.addRecord(std::move(Record3))); + auto Profile = Writer.writeBuffer(); + readProfile(std::move(Profile)); + + auto I = Reader->begin(), E = Reader->end(); + ASSERT_TRUE(I == E); +} + +INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseInstrProfTest, + ::testing::Bool()); + } // end anonymous namespace diff --git a/unittests/ProfileData/Makefile b/unittests/ProfileData/Makefile deleted file mode 100644 index d017c15c00ad..000000000000 --- a/unittests/ProfileData/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/ProfileData/Makefile ----------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = ProfileData -LINK_COMPONENTS := ProfileData Core Support - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/ProfileData/SampleProfTest.cpp b/unittests/ProfileData/SampleProfTest.cpp index cc3c2f5306e4..175852b93fb2 100644 --- a/unittests/ProfileData/SampleProfTest.cpp +++ b/unittests/ProfileData/SampleProfTest.cpp @@ -1,5 +1,4 @@ -//===- unittest/ProfileData/SampleProfTest.cpp -------------------*- C++ -//-*-===// +//===- unittest/ProfileData/SampleProfTest.cpp ------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -8,11 +7,27 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/ProfileData/ProfileCommon.h" +#include "llvm/ProfileData/SampleProf.h" #include "llvm/ProfileData/SampleProfReader.h" #include "llvm/ProfileData/SampleProfWriter.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" - -#include <cstdarg> +#include <algorithm> +#include <cstdint> +#include <limits> +#include <memory> +#include <string> +#include <system_error> +#include <vector> using namespace llvm; using namespace sampleprof; @@ -28,6 +43,7 @@ namespace { struct SampleProfTest : ::testing::Test { std::string Data; + LLVMContext Context; std::unique_ptr<raw_ostream> OS; std::unique_ptr<SampleProfileWriter> Writer; std::unique_ptr<SampleProfileReader> Reader; @@ -42,7 +58,7 @@ struct SampleProfTest : ::testing::Test { } void readProfile(std::unique_ptr<MemoryBuffer> &Profile) { - auto ReaderOrErr = SampleProfileReader::create(Profile, getGlobalContext()); + auto ReaderOrErr = SampleProfileReader::create(Profile, Context); ASSERT_TRUE(NoError(ReaderOrErr.getError())); Reader = std::move(ReaderOrErr.get()); } @@ -52,12 +68,18 @@ struct SampleProfTest : ::testing::Test { StringRef FooName("_Z3fooi"); FunctionSamples FooSamples; + FooSamples.setName(FooName); FooSamples.addTotalSamples(7711); FooSamples.addHeadSamples(610); FooSamples.addBodySamples(1, 0, 610); + FooSamples.addBodySamples(2, 0, 600); + FooSamples.addBodySamples(4, 0, 60000); + FooSamples.addBodySamples(8, 0, 60351); + FooSamples.addBodySamples(10, 0, 605); StringRef BarName("_Z3bari"); FunctionSamples BarSamples; + BarSamples.setName(BarName); BarSamples.addTotalSamples(20301); BarSamples.addHeadSamples(1437); BarSamples.addBodySamples(1, 0, 1437); @@ -88,6 +110,55 @@ struct SampleProfTest : ::testing::Test { FunctionSamples &ReadBarSamples = ReadProfiles[BarName]; ASSERT_EQ(20301u, ReadBarSamples.getTotalSamples()); ASSERT_EQ(1437u, ReadBarSamples.getHeadSamples()); + + auto VerifySummary = [](ProfileSummary &Summary) mutable { + ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind()); + ASSERT_EQ(123603u, Summary.getTotalCount()); + ASSERT_EQ(6u, Summary.getNumCounts()); + ASSERT_EQ(2u, Summary.getNumFunctions()); + ASSERT_EQ(1437u, Summary.getMaxFunctionCount()); + ASSERT_EQ(60351u, Summary.getMaxCount()); + + uint32_t Cutoff = 800000; + auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) { + return PE.Cutoff == Cutoff; + }; + std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary(); + auto EightyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + Cutoff = 900000; + auto NinetyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + Cutoff = 950000; + auto NinetyFivePerc = + std::find_if(Details.begin(), Details.end(), Predicate); + Cutoff = 990000; + auto NinetyNinePerc = + std::find_if(Details.begin(), Details.end(), Predicate); + ASSERT_EQ(60000u, EightyPerc->MinCount); + ASSERT_EQ(60000u, NinetyPerc->MinCount); + ASSERT_EQ(60000u, NinetyFivePerc->MinCount); + ASSERT_EQ(610u, NinetyNinePerc->MinCount); + }; + + ProfileSummary &Summary = Reader->getSummary(); + VerifySummary(Summary); + + // Test that conversion of summary to and from Metadata works. + Metadata *MD = Summary.getMD(Context); + ASSERT_TRUE(MD); + ProfileSummary *PS = ProfileSummary::getFromMD(MD); + ASSERT_TRUE(PS); + VerifySummary(*PS); + delete PS; + + // Test that summary can be attached to and read back from module. + Module M("my_module", Context); + M.setProfileSummary(MD); + MD = M.getProfileSummary(); + ASSERT_TRUE(MD); + PS = ProfileSummary::getFromMD(MD); + ASSERT_TRUE(PS); + VerifySummary(*PS); + delete PS; } }; diff --git a/unittests/Support/AlignOfTest.cpp b/unittests/Support/AlignOfTest.cpp index be208f7d28ea..74b03f0e7dfd 100644 --- a/unittests/Support/AlignOfTest.cpp +++ b/unittests/Support/AlignOfTest.cpp @@ -1,4 +1,4 @@ -//=== - llvm/unittest/Support/AlignOfTest.cpp - Alignment utility tests ----===// +//=== - llvm/unittest/Support/AlignOfTest.cpp - Alignment utility tests ---===// // // The LLVM Compiler Infrastructure // @@ -97,7 +97,7 @@ struct Abstract1 { }; struct Abstract2 : Abstract1 { - virtual ~Abstract2() {} + ~Abstract2() override = default; double d; }; @@ -354,4 +354,4 @@ TEST(AlignOfTest, BasicAlignedArray) { EXPECT_EQ(2u, sizeof(AlignedCharArray<2, 2>)); EXPECT_EQ(16u, sizeof(AlignedCharArray<2, 16>)); } -} +} // end anonymous namespace diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt index 3ab98d58d5f0..9a4a14450911 100644 --- a/unittests/Support/CMakeLists.txt +++ b/unittests/Support/CMakeLists.txt @@ -1,5 +1,4 @@ set(LLVM_LINK_COMPONENTS - ${LLVM_TARGETS_TO_BUILD} Support ) @@ -17,6 +16,7 @@ add_llvm_unittest(SupportTests DwarfTest.cpp EndianStreamTest.cpp EndianTest.cpp + ErrorTest.cpp ErrorOrTest.cpp FileOutputBufferTest.cpp IteratorTest.cpp @@ -36,14 +36,15 @@ add_llvm_unittest(SupportTests ScaledNumberTest.cpp SourceMgrTest.cpp SpecialCaseListTest.cpp - StreamingMemoryObject.cpp + StreamingMemoryObjectTest.cpp StringPool.cpp SwapByteOrderTest.cpp - TargetRegistry.cpp + TargetParserTest.cpp ThreadLocalTest.cpp ThreadPool.cpp TimerTest.cpp TimeValueTest.cpp + TypeNameTest.cpp TrailingObjectsTest.cpp UnicodeTest.cpp YAMLIOTest.cpp @@ -51,9 +52,8 @@ add_llvm_unittest(SupportTests formatted_raw_ostream_test.cpp raw_ostream_test.cpp raw_pwrite_stream_test.cpp + raw_sha1_ostream_test.cpp ) # ManagedStatic.cpp uses <pthread>. -if(LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD) - target_link_libraries(SupportTests pthread) -endif() +target_link_libraries(SupportTests ${PTHREAD_LIB}) diff --git a/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp index eac669f467b7..9b24c1e40bca 100644 --- a/unittests/Support/CommandLineTest.cpp +++ b/unittests/Support/CommandLineTest.cpp @@ -67,6 +67,22 @@ public: : Base(M0, M1, M2, M3) {} ~StackOption() override { this->removeArgument(); } + + template <class DT> StackOption<T> &operator=(const DT &V) { + this->setValue(V); + return *this; + } +}; + +class StackSubCommand : public cl::SubCommand { +public: + StackSubCommand(const char *const Name, + const char *const Description = nullptr) + : SubCommand(Name, Description) {} + + StackSubCommand() : SubCommand() {} + + ~StackSubCommand() { unregisterSubCommand(); } }; @@ -78,7 +94,8 @@ TEST(CommandLineTest, ModifyExisitingOption) { const char ArgString[] = "new-test-option"; const char ValueString[] = "Integer"; - StringMap<cl::Option *> &Map = cl::getRegisteredOptions(); + StringMap<cl::Option *> &Map = + cl::getRegisteredOptions(*cl::TopLevelSubCommand); ASSERT_TRUE(Map.count("test-option") == 1) << "Could not find option in map."; @@ -165,11 +182,12 @@ void testCommandLineTokenizer(ParserFunction *parse, const char *Input, } TEST(CommandLineTest, TokenizeGNUCommandLine) { - const char *Input = "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' " - "foo\"bar\"baz C:\\src\\foo.cpp \"C:\\src\\foo.cpp\""; - const char *const Output[] = { "foo bar", "foo bar", "foo bar", "foo\\bar", - "foobarbaz", "C:\\src\\foo.cpp", - "C:\\src\\foo.cpp" }; + const char *Input = + "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' -DFOO=bar\\(\\) " + "foo\"bar\"baz C:\\\\src\\\\foo.cpp \"C:\\src\\foo.cpp\""; + const char *const Output[] = { + "foo bar", "foo bar", "foo bar", "foo\\bar", + "-DFOO=bar()", "foobarbaz", "C:\\src\\foo.cpp", "C:srcfoo.cpp"}; testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output, array_lengthof(Output)); } @@ -236,7 +254,8 @@ TEST(CommandLineTest, HideUnrelatedOptions) { ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag()) << "Hid extra option that should be visable."; - StringMap<cl::Option *> &Map = cl::getRegisteredOptions(); + StringMap<cl::Option *> &Map = + cl::getRegisteredOptions(*cl::TopLevelSubCommand); ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag()) << "Hid default option that should be visable."; } @@ -260,9 +279,201 @@ TEST(CommandLineTest, HideUnrelatedOptionsMulti) { ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag()) << "Hid extra option that should be visable."; - StringMap<cl::Option *> &Map = cl::getRegisteredOptions(); + StringMap<cl::Option *> &Map = + cl::getRegisteredOptions(*cl::TopLevelSubCommand); ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag()) << "Hid default option that should be visable."; } +TEST(CommandLineTest, SetValueInSubcategories) { + cl::ResetCommandLineParser(); + + StackSubCommand SC1("sc1", "First subcommand"); + StackSubCommand SC2("sc2", "Second subcommand"); + + StackOption<bool> TopLevelOpt("top-level", cl::init(false)); + StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false)); + StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); + + EXPECT_FALSE(TopLevelOpt); + EXPECT_FALSE(SC1Opt); + EXPECT_FALSE(SC2Opt); + const char *args[] = {"prog", "-top-level"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(TopLevelOpt); + EXPECT_FALSE(SC1Opt); + EXPECT_FALSE(SC2Opt); + + TopLevelOpt = false; + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(TopLevelOpt); + EXPECT_FALSE(SC1Opt); + EXPECT_FALSE(SC2Opt); + const char *args2[] = {"prog", "sc1", "-sc1"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); + EXPECT_FALSE(TopLevelOpt); + EXPECT_TRUE(SC1Opt); + EXPECT_FALSE(SC2Opt); + + SC1Opt = false; + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(TopLevelOpt); + EXPECT_FALSE(SC1Opt); + EXPECT_FALSE(SC2Opt); + const char *args3[] = {"prog", "sc2", "-sc2"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true)); + EXPECT_FALSE(TopLevelOpt); + EXPECT_FALSE(SC1Opt); + EXPECT_TRUE(SC2Opt); +} + +TEST(CommandLineTest, LookupFailsInWrongSubCommand) { + cl::ResetCommandLineParser(); + + StackSubCommand SC1("sc1", "First subcommand"); + StackSubCommand SC2("sc2", "Second subcommand"); + + StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false)); + StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); + + const char *args[] = {"prog", "sc1", "-sc2"}; + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true)); +} + +TEST(CommandLineTest, AddToAllSubCommands) { + cl::ResetCommandLineParser(); + + StackSubCommand SC1("sc1", "First subcommand"); + StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands), + cl::init(false)); + StackSubCommand SC2("sc2", "Second subcommand"); + + const char *args[] = {"prog", "-everywhere"}; + const char *args2[] = {"prog", "sc1", "-everywhere"}; + const char *args3[] = {"prog", "sc2", "-everywhere"}; + + EXPECT_FALSE(AllOpt); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(AllOpt); + + AllOpt = false; + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(AllOpt); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); + EXPECT_TRUE(AllOpt); + + AllOpt = false; + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(AllOpt); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true)); + EXPECT_TRUE(AllOpt); +} + +TEST(CommandLineTest, ReparseCommandLineOptions) { + cl::ResetCommandLineParser(); + + StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand), + cl::init(false)); + + const char *args[] = {"prog", "-top-level"}; + + EXPECT_FALSE(TopLevelOpt); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(TopLevelOpt); + + TopLevelOpt = false; + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(TopLevelOpt); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(TopLevelOpt); +} + +TEST(CommandLineTest, RemoveFromRegularSubCommand) { + cl::ResetCommandLineParser(); + + StackSubCommand SC("sc", "Subcommand"); + StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false)); + StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false)); + + const char *args[] = {"prog", "sc", "-remove-option"}; + + EXPECT_FALSE(RemoveOption); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, nullptr, true)); + EXPECT_TRUE(RemoveOption); + + RemoveOption.removeArgument(); + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true)); +} + +TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { + cl::ResetCommandLineParser(); + + StackOption<bool> TopLevelRemove( + "top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false)); + StackOption<bool> TopLevelKeep( + "top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false)); + + const char *args[] = {"prog", "-top-level-remove"}; + + EXPECT_FALSE(TopLevelRemove); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(TopLevelRemove); + + TopLevelRemove.removeArgument(); + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, nullptr, true)); +} + +TEST(CommandLineTest, RemoveFromAllSubCommands) { + cl::ResetCommandLineParser(); + + StackSubCommand SC1("sc1", "First Subcommand"); + StackSubCommand SC2("sc2", "Second Subcommand"); + StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands), + cl::init(false)); + StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands), + cl::init(false)); + + const char *args0[] = {"prog", "-remove-option"}; + const char *args1[] = {"prog", "sc1", "-remove-option"}; + const char *args2[] = {"prog", "sc2", "-remove-option"}; + + // It should work for all subcommands including the top-level. + EXPECT_FALSE(RemoveOption); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, nullptr, true)); + EXPECT_TRUE(RemoveOption); + + RemoveOption = false; + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(RemoveOption); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, nullptr, true)); + EXPECT_TRUE(RemoveOption); + + RemoveOption = false; + + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(RemoveOption); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); + EXPECT_TRUE(RemoveOption); + + RemoveOption.removeArgument(); + + // It should not work for any subcommands including the top-level. + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, nullptr, true)); + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, nullptr, true)); + cl::ResetAllOptionOccurrences(); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); +} + } // anonymous namespace diff --git a/unittests/Support/ConvertUTFTest.cpp b/unittests/Support/ConvertUTFTest.cpp index d436fc022896..0af09e98a217 100644 --- a/unittests/Support/ConvertUTFTest.cpp +++ b/unittests/Support/ConvertUTFTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/ConvertUTF.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Format.h" #include "gtest/gtest.h" #include <string> @@ -59,7 +60,7 @@ TEST(ConvertUTFTest, OddLengthInput) { TEST(ConvertUTFTest, Empty) { std::string Result; - bool Success = convertUTF16ToUTF8String(None, Result); + bool Success = convertUTF16ToUTF8String(llvm::ArrayRef<char>(None), Result); EXPECT_TRUE(Success); EXPECT_TRUE(Result.empty()); } @@ -80,6 +81,41 @@ TEST(ConvertUTFTest, HasUTF16BOM) { EXPECT_FALSE(HasBOM); } +TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) { + // Src is the look of disapproval. + static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; + ArrayRef<UTF16> SrcRef = makeArrayRef((const UTF16 *)Src, 4); + std::string Result; + bool Success = convertUTF16ToUTF8String(SrcRef, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result); +} + +TEST(ConvertUTFTest, ConvertUTF8toWide) { + // Src is the look of disapproval. + static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0"; + std::wstring Result; + bool Success = ConvertUTF8toWide((const char*)Src, Result); + EXPECT_TRUE(Success); + std::wstring Expected(L"\x0ca0_\x0ca0"); + EXPECT_EQ(Expected, Result); + Result.clear(); + Success = ConvertUTF8toWide(StringRef(Src, 7), Result); + EXPECT_TRUE(Success); + EXPECT_EQ(Expected, Result); +} + +TEST(ConvertUTFTest, convertWideToUTF8) { + // Src is the look of disapproval. + static const wchar_t Src[] = L"\x0ca0_\x0ca0"; + std::string Result; + bool Success = convertWideToUTF8(Src, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result); +} + struct ConvertUTFResultContainer { ConversionResult ErrorCode; std::vector<unsigned> UnicodeScalars; diff --git a/unittests/Support/EndianStreamTest.cpp b/unittests/Support/EndianStreamTest.cpp index 6a69be55f926..ea6c308c560a 100644 --- a/unittests/Support/EndianStreamTest.cpp +++ b/unittests/Support/EndianStreamTest.cpp @@ -153,5 +153,56 @@ TEST(EndianStream, WriteDoubleBE) { EXPECT_EQ(static_cast<uint8_t>(data[7]), 0x20); } +TEST(EndianStream, WriteArrayLE) { + SmallString<16> Data; + + { + raw_svector_ostream OS(Data); + endian::Writer<little> LE(OS); + LE.write<uint16_t>({0x1234, 0x5678}); + } + + EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x34); + EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0x12); + EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x78); + EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x56); +} + +TEST(EndianStream, WriteVectorLE) { + SmallString<16> Data; + + { + raw_svector_ostream OS(Data); + endian::Writer<little> LE(OS); + std::vector<uint16_t> Vec{0x1234, 0x5678}; + LE.write<uint16_t>(Vec); + } + + EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x34); + EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0x12); + EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x78); + EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x56); +} + +TEST(EndianStream, WriteFloatArrayLE) { + SmallString<16> Data; + + { + raw_svector_ostream OS(Data); + endian::Writer<little> LE(OS); + LE.write<float>({12345.0f, 12346.0f}); + } + + EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x00); + EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0xE4); + EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x40); + EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x46); + + EXPECT_EQ(static_cast<uint8_t>(Data[4]), 0x00); + EXPECT_EQ(static_cast<uint8_t>(Data[5]), 0xE8); + EXPECT_EQ(static_cast<uint8_t>(Data[6]), 0x40); + EXPECT_EQ(static_cast<uint8_t>(Data[7]), 0x46); +} + } // end anon namespace diff --git a/unittests/Support/ErrorOrTest.cpp b/unittests/Support/ErrorOrTest.cpp index 73d0e3f2bb71..87dcab7b181e 100644 --- a/unittests/Support/ErrorOrTest.cpp +++ b/unittests/Support/ErrorOrTest.cpp @@ -16,7 +16,7 @@ using namespace llvm; namespace { -ErrorOr<int> t1() {return 1;} +ErrorOr<int> t1() { return 1; } ErrorOr<int> t2() { return errc::invalid_argument; } TEST(ErrorOr, SimpleValue) { @@ -71,6 +71,46 @@ TEST(ErrorOr, Comparison) { EXPECT_EQ(x, errc::no_such_file_or_directory); } +TEST(ErrorOr, ImplicitConversion) { + ErrorOr<std::string> x("string literal"); + EXPECT_TRUE(!!x); +} + +TEST(ErrorOr, ImplicitConversionCausesMove) { + struct Source {}; + struct Destination { + Destination(const Source&) {} + Destination(Source&&) = delete; + }; + Source s; + ErrorOr<Destination> x = s; + EXPECT_TRUE(!!x); +} + +TEST(ErrorOr, ImplicitConversionNoAmbiguity) { + struct CastsToErrorCode { + CastsToErrorCode() = default; + CastsToErrorCode(std::error_code) {} + operator std::error_code() { return errc::invalid_argument; } + } casts_to_error_code; + ErrorOr<CastsToErrorCode> x1(casts_to_error_code); + ErrorOr<CastsToErrorCode> x2 = casts_to_error_code; + ErrorOr<CastsToErrorCode> x3 = {casts_to_error_code}; + ErrorOr<CastsToErrorCode> x4{casts_to_error_code}; + ErrorOr<CastsToErrorCode> x5(errc::no_such_file_or_directory); + ErrorOr<CastsToErrorCode> x6 = errc::no_such_file_or_directory; + ErrorOr<CastsToErrorCode> x7 = {errc::no_such_file_or_directory}; + ErrorOr<CastsToErrorCode> x8{errc::no_such_file_or_directory}; + EXPECT_TRUE(!!x1); + EXPECT_TRUE(!!x2); + EXPECT_TRUE(!!x3); + EXPECT_TRUE(!!x4); + EXPECT_FALSE(x5); + EXPECT_FALSE(x6); + EXPECT_FALSE(x7); + EXPECT_FALSE(x8); +} + // ErrorOr<int*> x(nullptr); // ErrorOr<std::unique_ptr<int>> y = x; // invalid conversion static_assert( diff --git a/unittests/Support/ErrorTest.cpp b/unittests/Support/ErrorTest.cpp new file mode 100644 index 000000000000..205092683744 --- /dev/null +++ b/unittests/Support/ErrorTest.cpp @@ -0,0 +1,627 @@ +//===----- unittests/ErrorTest.cpp - Error.h tests ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Error.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "gtest/gtest.h" +#include <memory> + +using namespace llvm; + +namespace { + +// Custom error class with a default base class and some random 'info' attached. +class CustomError : public ErrorInfo<CustomError> { +public: + // Create an error with some info attached. + CustomError(int Info) : Info(Info) {} + + // Get the info attached to this error. + int getInfo() const { return Info; } + + // Log this error to a stream. + void log(raw_ostream &OS) const override { + OS << "CustomError { " << getInfo() << "}"; + } + + std::error_code convertToErrorCode() const override { + llvm_unreachable("CustomError doesn't support ECError conversion"); + } + + // Used by ErrorInfo::classID. + static char ID; + +protected: + // This error is subclassed below, but we can't use inheriting constructors + // yet, so we can't propagate the constructors through ErrorInfo. Instead + // we have to have a default constructor and have the subclass initialize all + // fields. + CustomError() : Info(0) {} + + int Info; +}; + +char CustomError::ID = 0; + +// Custom error class with a custom base class and some additional random +// 'info'. +class CustomSubError : public ErrorInfo<CustomSubError, CustomError> { +public: + // Create a sub-error with some info attached. + CustomSubError(int Info, int ExtraInfo) : ExtraInfo(ExtraInfo) { + this->Info = Info; + } + + // Get the extra info attached to this error. + int getExtraInfo() const { return ExtraInfo; } + + // Log this error to a stream. + void log(raw_ostream &OS) const override { + OS << "CustomSubError { " << getInfo() << ", " << getExtraInfo() << "}"; + } + + std::error_code convertToErrorCode() const override { + llvm_unreachable("CustomSubError doesn't support ECError conversion"); + } + + // Used by ErrorInfo::classID. + static char ID; + +protected: + int ExtraInfo; +}; + +char CustomSubError::ID = 0; + +static Error handleCustomError(const CustomError &CE) { return Error(); } + +static void handleCustomErrorVoid(const CustomError &CE) {} + +static Error handleCustomErrorUP(std::unique_ptr<CustomError> CE) { + return Error(); +} + +static void handleCustomErrorUPVoid(std::unique_ptr<CustomError> CE) {} + +// Test that success values implicitly convert to false, and don't cause crashes +// once they've been implicitly converted. +TEST(Error, CheckedSuccess) { + Error E; + EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'"; +} + +// Test that unchecked succes values cause an abort. +#ifndef NDEBUG +TEST(Error, UncheckedSuccess) { + EXPECT_DEATH({ Error E; }, "Program aborted due to an unhandled Error:") + << "Unchecked Error Succes value did not cause abort()"; +} +#endif + +// ErrorAsOutParameter tester. +void errAsOutParamHelper(Error &Err) { + ErrorAsOutParameter ErrAsOutParam(Err); + // Verify that checked flag is raised - assignment should not crash. + Err = Error::success(); + // Raise the checked bit manually - caller should still have to test the + // error. + (void)!!Err; +} + +// Test that ErrorAsOutParameter sets the checked flag on construction. +TEST(Error, ErrorAsOutParameterChecked) { + Error E; + errAsOutParamHelper(E); + (void)!!E; +} + +// Test that ErrorAsOutParameter clears the checked flag on destruction. +#ifndef NDEBUG +TEST(Error, ErrorAsOutParameterUnchecked) { + EXPECT_DEATH({ Error E; errAsOutParamHelper(E); }, + "Program aborted due to an unhandled Error:") + << "ErrorAsOutParameter did not clear the checked flag on destruction."; +} +#endif + +// Check that we abort on unhandled failure cases. (Force conversion to bool +// to make sure that we don't accidentally treat checked errors as handled). +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, UncheckedError) { + auto DropUnhandledError = []() { + Error E = make_error<CustomError>(42); + (void)!E; + }; + EXPECT_DEATH(DropUnhandledError(), + "Program aborted due to an unhandled Error:") + << "Unhandled Error failure value did not cause abort()"; +} +#endif + +// Check 'Error::isA<T>' method handling. +TEST(Error, IsAHandling) { + // Check 'isA' handling. + Error E = make_error<CustomError>(1); + Error F = make_error<CustomSubError>(1, 2); + Error G = Error::success(); + + EXPECT_TRUE(E.isA<CustomError>()); + EXPECT_FALSE(E.isA<CustomSubError>()); + EXPECT_TRUE(F.isA<CustomError>()); + EXPECT_TRUE(F.isA<CustomSubError>()); + EXPECT_FALSE(G.isA<CustomError>()); + + consumeError(std::move(E)); + consumeError(std::move(F)); + consumeError(std::move(G)); +} + +// Check that we can handle a custom error. +TEST(Error, HandleCustomError) { + int CaughtErrorInfo = 0; + handleAllErrors(make_error<CustomError>(42), [&](const CustomError &CE) { + CaughtErrorInfo = CE.getInfo(); + }); + + EXPECT_TRUE(CaughtErrorInfo == 42) << "Wrong result from CustomError handler"; +} + +// Check that handler type deduction also works for handlers +// of the following types: +// void (const Err&) +// Error (const Err&) mutable +// void (const Err&) mutable +// Error (Err&) +// void (Err&) +// Error (Err&) mutable +// void (Err&) mutable +// Error (unique_ptr<Err>) +// void (unique_ptr<Err>) +// Error (unique_ptr<Err>) mutable +// void (unique_ptr<Err>) mutable +TEST(Error, HandlerTypeDeduction) { + + handleAllErrors(make_error<CustomError>(42), [](const CustomError &CE) {}); + + handleAllErrors( + make_error<CustomError>(42), + [](const CustomError &CE) mutable { return Error::success(); }); + + handleAllErrors(make_error<CustomError>(42), + [](const CustomError &CE) mutable {}); + + handleAllErrors(make_error<CustomError>(42), + [](CustomError &CE) { return Error::success(); }); + + handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) {}); + + handleAllErrors(make_error<CustomError>(42), + [](CustomError &CE) mutable { return Error::success(); }); + + handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) mutable {}); + + handleAllErrors( + make_error<CustomError>(42), + [](std::unique_ptr<CustomError> CE) { return Error::success(); }); + + handleAllErrors(make_error<CustomError>(42), + [](std::unique_ptr<CustomError> CE) {}); + + handleAllErrors( + make_error<CustomError>(42), + [](std::unique_ptr<CustomError> CE) mutable { return Error::success(); }); + + handleAllErrors(make_error<CustomError>(42), + [](std::unique_ptr<CustomError> CE) mutable {}); + + // Check that named handlers of type 'Error (const Err&)' work. + handleAllErrors(make_error<CustomError>(42), handleCustomError); + + // Check that named handlers of type 'void (const Err&)' work. + handleAllErrors(make_error<CustomError>(42), handleCustomErrorVoid); + + // Check that named handlers of type 'Error (std::unique_ptr<Err>)' work. + handleAllErrors(make_error<CustomError>(42), handleCustomErrorUP); + + // Check that named handlers of type 'Error (std::unique_ptr<Err>)' work. + handleAllErrors(make_error<CustomError>(42), handleCustomErrorUPVoid); +} + +// Test that we can handle errors with custom base classes. +TEST(Error, HandleCustomErrorWithCustomBaseClass) { + int CaughtErrorInfo = 0; + int CaughtErrorExtraInfo = 0; + handleAllErrors(make_error<CustomSubError>(42, 7), + [&](const CustomSubError &SE) { + CaughtErrorInfo = SE.getInfo(); + CaughtErrorExtraInfo = SE.getExtraInfo(); + }); + + EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7) + << "Wrong result from CustomSubError handler"; +} + +// Check that we trigger only the first handler that applies. +TEST(Error, FirstHandlerOnly) { + int DummyInfo = 0; + int CaughtErrorInfo = 0; + int CaughtErrorExtraInfo = 0; + + handleAllErrors(make_error<CustomSubError>(42, 7), + [&](const CustomSubError &SE) { + CaughtErrorInfo = SE.getInfo(); + CaughtErrorExtraInfo = SE.getExtraInfo(); + }, + [&](const CustomError &CE) { DummyInfo = CE.getInfo(); }); + + EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7 && + DummyInfo == 0) + << "Activated the wrong Error handler(s)"; +} + +// Check that general handlers shadow specific ones. +TEST(Error, HandlerShadowing) { + int CaughtErrorInfo = 0; + int DummyInfo = 0; + int DummyExtraInfo = 0; + + handleAllErrors( + make_error<CustomSubError>(42, 7), + [&](const CustomError &CE) { CaughtErrorInfo = CE.getInfo(); }, + [&](const CustomSubError &SE) { + DummyInfo = SE.getInfo(); + DummyExtraInfo = SE.getExtraInfo(); + }); + + EXPECT_TRUE(CaughtErrorInfo == 42 && DummyInfo == 0 && DummyExtraInfo == 0) + << "General Error handler did not shadow specific handler"; +} + +// Test joinErrors. +TEST(Error, CheckJoinErrors) { + int CustomErrorInfo1 = 0; + int CustomErrorInfo2 = 0; + int CustomErrorExtraInfo = 0; + Error E = + joinErrors(make_error<CustomError>(7), make_error<CustomSubError>(42, 7)); + + handleAllErrors(std::move(E), + [&](const CustomSubError &SE) { + CustomErrorInfo2 = SE.getInfo(); + CustomErrorExtraInfo = SE.getExtraInfo(); + }, + [&](const CustomError &CE) { + // Assert that the CustomError instance above is handled + // before the + // CustomSubError - joinErrors should preserve error + // ordering. + EXPECT_EQ(CustomErrorInfo2, 0) + << "CustomErrorInfo2 should be 0 here. " + "joinErrors failed to preserve ordering.\n"; + CustomErrorInfo1 = CE.getInfo(); + }); + + EXPECT_TRUE(CustomErrorInfo1 == 7 && CustomErrorInfo2 == 42 && + CustomErrorExtraInfo == 7) + << "Failed handling compound Error."; + + // Test appending a single item to a list. + { + int Sum = 0; + handleAllErrors( + joinErrors( + joinErrors(make_error<CustomError>(7), + make_error<CustomError>(7)), + make_error<CustomError>(7)), + [&](const CustomError &CE) { + Sum += CE.getInfo(); + }); + EXPECT_EQ(Sum, 21) << "Failed to correctly append error to error list."; + } + + // Test prepending a single item to a list. + { + int Sum = 0; + handleAllErrors( + joinErrors( + make_error<CustomError>(7), + joinErrors(make_error<CustomError>(7), + make_error<CustomError>(7))), + [&](const CustomError &CE) { + Sum += CE.getInfo(); + }); + EXPECT_EQ(Sum, 21) << "Failed to correctly prepend error to error list."; + } + + // Test concatenating two error lists. + { + int Sum = 0; + handleAllErrors( + joinErrors( + joinErrors( + make_error<CustomError>(7), + make_error<CustomError>(7)), + joinErrors( + make_error<CustomError>(7), + make_error<CustomError>(7))), + [&](const CustomError &CE) { + Sum += CE.getInfo(); + }); + EXPECT_EQ(Sum, 28) << "Failed to correctly concatenate erorr lists."; + } +} + +// Test that we can consume success values. +TEST(Error, ConsumeSuccess) { + Error E; + consumeError(std::move(E)); +} + +TEST(Error, ConsumeError) { + Error E = make_error<CustomError>(7); + consumeError(std::move(E)); +} + +// Test that handleAllUnhandledErrors crashes if an error is not caught. +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, FailureToHandle) { + auto FailToHandle = []() { + handleAllErrors(make_error<CustomError>(7), [&](const CustomSubError &SE) { + errs() << "This should never be called"; + exit(1); + }); + }; + + EXPECT_DEATH(FailToHandle(), "Program aborted due to an unhandled Error:") + << "Unhandled Error in handleAllErrors call did not cause an " + "abort()"; +} +#endif + +// Test that handleAllUnhandledErrors crashes if an error is returned from a +// handler. +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, FailureFromHandler) { + auto ReturnErrorFromHandler = []() { + handleAllErrors(make_error<CustomError>(7), + [&](std::unique_ptr<CustomSubError> SE) { + return Error(std::move(SE)); + }); + }; + + EXPECT_DEATH(ReturnErrorFromHandler(), + "Program aborted due to an unhandled Error:") + << " Error returned from handler in handleAllErrors call did not " + "cause abort()"; +} +#endif + +// Test that we can return values from handleErrors. +TEST(Error, CatchErrorFromHandler) { + int ErrorInfo = 0; + + Error E = handleErrors( + make_error<CustomError>(7), + [&](std::unique_ptr<CustomError> CE) { return Error(std::move(CE)); }); + + handleAllErrors(std::move(E), + [&](const CustomError &CE) { ErrorInfo = CE.getInfo(); }); + + EXPECT_EQ(ErrorInfo, 7) + << "Failed to handle Error returned from handleErrors."; +} + +TEST(Error, StringError) { + std::string Msg; + raw_string_ostream S(Msg); + logAllUnhandledErrors(make_error<StringError>("foo" + Twine(42), + inconvertibleErrorCode()), + S, ""); + EXPECT_EQ(S.str(), "foo42\n") << "Unexpected StringError log result"; + + auto EC = + errorToErrorCode(make_error<StringError>("", errc::invalid_argument)); + EXPECT_EQ(EC, errc::invalid_argument) + << "Failed to convert StringError to error_code."; +} + +// Test that the ExitOnError utility works as expected. +TEST(Error, ExitOnError) { + ExitOnError ExitOnErr; + ExitOnErr.setBanner("Error in tool:"); + ExitOnErr.setExitCodeMapper([](const Error &E) { + if (E.isA<CustomSubError>()) + return 2; + return 1; + }); + + // Make sure we don't bail on success. + ExitOnErr(Error::success()); + EXPECT_EQ(ExitOnErr(Expected<int>(7)), 7) + << "exitOnError returned an invalid value for Expected"; + + int A = 7; + int &B = ExitOnErr(Expected<int&>(A)); + EXPECT_EQ(&A, &B) << "ExitOnError failed to propagate reference"; + + // Exit tests. + EXPECT_EXIT(ExitOnErr(make_error<CustomError>(7)), + ::testing::ExitedWithCode(1), "Error in tool:") + << "exitOnError returned an unexpected error result"; + + EXPECT_EXIT(ExitOnErr(Expected<int>(make_error<CustomSubError>(0, 0))), + ::testing::ExitedWithCode(2), "Error in tool:") + << "exitOnError returned an unexpected error result"; +} + +// Test Checked Expected<T> in success mode. +TEST(Error, CheckedExpectedInSuccessMode) { + Expected<int> A = 7; + EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'"; + // Access is safe in second test, since we checked the error in the first. + EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value"; +} + +// Test Expected with reference type. +TEST(Error, ExpectedWithReferenceType) { + int A = 7; + Expected<int&> B = A; + // 'Check' B. + (void)!!B; + int &C = *B; + EXPECT_EQ(&A, &C) << "Expected failed to propagate reference"; +} + +// Test Unchecked Expected<T> in success mode. +// We expect this to blow up the same way Error would. +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, UncheckedExpectedInSuccessModeDestruction) { + EXPECT_DEATH({ Expected<int> A = 7; }, + "Expected<T> must be checked before access or destruction.") + << "Unchecekd Expected<T> success value did not cause an abort()."; +} +#endif + +// Test Unchecked Expected<T> in success mode. +// We expect this to blow up the same way Error would. +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, UncheckedExpectedInSuccessModeAccess) { + EXPECT_DEATH({ Expected<int> A = 7; *A; }, + "Expected<T> must be checked before access or destruction.") + << "Unchecekd Expected<T> success value did not cause an abort()."; +} +#endif + +// Test Unchecked Expected<T> in success mode. +// We expect this to blow up the same way Error would. +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, UncheckedExpectedInSuccessModeAssignment) { + EXPECT_DEATH({ Expected<int> A = 7; A = 7; }, + "Expected<T> must be checked before access or destruction.") + << "Unchecekd Expected<T> success value did not cause an abort()."; +} +#endif + +// Test Expected<T> in failure mode. +TEST(Error, ExpectedInFailureMode) { + Expected<int> A = make_error<CustomError>(42); + EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'"; + Error E = A.takeError(); + EXPECT_TRUE(E.isA<CustomError>()) << "Incorrect Expected error value"; + consumeError(std::move(E)); +} + +// Check that an Expected instance with an error value doesn't allow access to +// operator*. +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, AccessExpectedInFailureMode) { + Expected<int> A = make_error<CustomError>(42); + EXPECT_DEATH(*A, "Expected<T> must be checked before access or destruction.") + << "Incorrect Expected error value"; + consumeError(A.takeError()); +} +#endif + +// Check that an Expected instance with an error triggers an abort if +// unhandled. +// Test runs in debug mode only. +#ifndef NDEBUG +TEST(Error, UnhandledExpectedInFailureMode) { + EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); }, + "Expected<T> must be checked before access or destruction.") + << "Unchecked Expected<T> failure value did not cause an abort()"; +} +#endif + +// Test covariance of Expected. +TEST(Error, ExpectedCovariance) { + class B {}; + class D : public B {}; + + Expected<B *> A1(Expected<D *>(nullptr)); + // Check A1 by converting to bool before assigning to it. + (void)!!A1; + A1 = Expected<D *>(nullptr); + // Check A1 again before destruction. + (void)!!A1; + + Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr)); + // Check A2 by converting to bool before assigning to it. + (void)!!A2; + A2 = Expected<std::unique_ptr<D>>(nullptr); + // Check A2 again before destruction. + (void)!!A2; +} + +TEST(Error, ErrorCodeConversions) { + // Round-trip a success value to check that it converts correctly. + EXPECT_EQ(errorToErrorCode(errorCodeToError(std::error_code())), + std::error_code()) + << "std::error_code() should round-trip via Error conversions"; + + // Round-trip an error value to check that it converts correctly. + EXPECT_EQ(errorToErrorCode(errorCodeToError(errc::invalid_argument)), + errc::invalid_argument) + << "std::error_code error value should round-trip via Error " + "conversions"; + + // Round-trip a success value through ErrorOr/Expected to check that it + // converts correctly. + { + auto Orig = ErrorOr<int>(42); + auto RoundTripped = + expectedToErrorOr(errorOrToExpected(ErrorOr<int>(42))); + EXPECT_EQ(*Orig, *RoundTripped) + << "ErrorOr<T> success value should round-trip via Expected<T> " + "conversions."; + } + + // Round-trip a failure value through ErrorOr/Expected to check that it + // converts correctly. + { + auto Orig = ErrorOr<int>(errc::invalid_argument); + auto RoundTripped = + expectedToErrorOr( + errorOrToExpected(ErrorOr<int>(errc::invalid_argument))); + EXPECT_EQ(Orig.getError(), RoundTripped.getError()) + << "ErrorOr<T> failure value should round-trip via Expected<T> " + "conversions."; + } +} + +// Test that error messages work. +TEST(Error, ErrorMessage) { + EXPECT_EQ(toString(Error::success()).compare(""), 0); + + Error E1 = make_error<CustomError>(0); + EXPECT_EQ(toString(std::move(E1)).compare("CustomError { 0}"), 0); + + Error E2 = make_error<CustomError>(0); + handleAllErrors(std::move(E2), [](const CustomError &CE) { + EXPECT_EQ(CE.message().compare("CustomError { 0}"), 0); + }); + + Error E3 = joinErrors(make_error<CustomError>(0), make_error<CustomError>(1)); + EXPECT_EQ(toString(std::move(E3)) + .compare("CustomError { 0}\n" + "CustomError { 1}"), + 0); +} + +} // end anon namespace diff --git a/unittests/Support/Makefile b/unittests/Support/Makefile deleted file mode 100644 index 21657f12e3dc..000000000000 --- a/unittests/Support/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/Support/Makefile --------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TESTNAME = Support -LINK_COMPONENTS := all-targets core support - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Support/MathExtrasTest.cpp b/unittests/Support/MathExtrasTest.cpp index 97309f8d31f5..d373030881ec 100644 --- a/unittests/Support/MathExtrasTest.cpp +++ b/unittests/Support/MathExtrasTest.cpp @@ -106,6 +106,40 @@ TEST(MathExtras, findLastSet) { EXPECT_EQ(5u, findLastSet(NZ64)); } +TEST(MathExtras, isIntN) { + EXPECT_TRUE(isIntN(16, 32767)); + EXPECT_FALSE(isIntN(16, 32768)); +} + +TEST(MathExtras, isUIntN) { + EXPECT_TRUE(isUIntN(16, 65535)); + EXPECT_FALSE(isUIntN(16, 65536)); + EXPECT_TRUE(isUIntN(1, 0)); + EXPECT_TRUE(isUIntN(6, 63)); +} + +TEST(MathExtras, maxIntN) { + EXPECT_EQ(32767, maxIntN(16)); + EXPECT_EQ(2147483647, maxIntN(32)); + EXPECT_EQ(std::numeric_limits<int32_t>::max(), maxIntN(32)); + EXPECT_EQ(std::numeric_limits<int64_t>::max(), maxIntN(64)); +} + +TEST(MathExtras, minIntN) { + EXPECT_EQ(-32768LL, minIntN(16)); + EXPECT_EQ(-64LL, minIntN(7)); + EXPECT_EQ(std::numeric_limits<int32_t>::min(), minIntN(32)); + EXPECT_EQ(std::numeric_limits<int64_t>::min(), minIntN(64)); +} + +TEST(MathExtras, maxUIntN) { + EXPECT_EQ(0xffffULL, maxUIntN(16)); + EXPECT_EQ(0xffffffffULL, maxUIntN(32)); + EXPECT_EQ(0xffffffffffffffffULL, maxUIntN(64)); + EXPECT_EQ(1ULL, maxUIntN(1)); + EXPECT_EQ(0x0fULL, maxUIntN(4)); +} + TEST(MathExtras, reverseBits) { uint8_t NZ8 = 42; uint16_t NZ16 = 42; @@ -163,7 +197,7 @@ TEST(MathExtras, FloatBits) { TEST(MathExtras, DoubleBits) { static const double kValue = 87987234.983498; - EXPECT_FLOAT_EQ(kValue, BitsToDouble(DoubleToBits(kValue))); + EXPECT_DOUBLE_EQ(kValue, BitsToDouble(DoubleToBits(kValue))); } TEST(MathExtras, MinAlign) { @@ -179,15 +213,15 @@ TEST(MathExtras, NextPowerOf2) { EXPECT_EQ(256u, NextPowerOf2(128)); } -TEST(MathExtras, RoundUpToAlignment) { - EXPECT_EQ(8u, RoundUpToAlignment(5, 8)); - EXPECT_EQ(24u, RoundUpToAlignment(17, 8)); - EXPECT_EQ(0u, RoundUpToAlignment(~0LL, 8)); +TEST(MathExtras, alignTo) { + EXPECT_EQ(8u, alignTo(5, 8)); + EXPECT_EQ(24u, alignTo(17, 8)); + EXPECT_EQ(0u, alignTo(~0LL, 8)); - EXPECT_EQ(7u, RoundUpToAlignment(5, 8, 7)); - EXPECT_EQ(17u, RoundUpToAlignment(17, 8, 1)); - EXPECT_EQ(3u, RoundUpToAlignment(~0LL, 8, 3)); - EXPECT_EQ(552u, RoundUpToAlignment(321, 255, 42)); + EXPECT_EQ(7u, alignTo(5, 8, 7)); + EXPECT_EQ(17u, alignTo(17, 8, 1)); + EXPECT_EQ(3u, alignTo(~0LL, 8, 3)); + EXPECT_EQ(552u, alignTo(321, 255, 42)); } template<typename T> @@ -358,4 +392,42 @@ TEST(MathExtras, SaturatingMultiplyAdd) { SaturatingMultiplyAddTestHelper<uint64_t>(); } +TEST(MathExtras, IsShiftedUInt) { + EXPECT_TRUE((isShiftedUInt<1, 0>(0))); + EXPECT_TRUE((isShiftedUInt<1, 0>(1))); + EXPECT_FALSE((isShiftedUInt<1, 0>(2))); + EXPECT_FALSE((isShiftedUInt<1, 0>(3))); + EXPECT_FALSE((isShiftedUInt<1, 0>(0x8000000000000000))); + EXPECT_TRUE((isShiftedUInt<1, 63>(0x8000000000000000))); + EXPECT_TRUE((isShiftedUInt<2, 62>(0xC000000000000000))); + EXPECT_FALSE((isShiftedUInt<2, 62>(0xE000000000000000))); + + // 0x201 is ten bits long and has a 1 in the MSB and LSB. + EXPECT_TRUE((isShiftedUInt<10, 5>(uint64_t(0x201) << 5))); + EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 4))); + EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 6))); } + +TEST(MathExtras, IsShiftedInt) { + EXPECT_TRUE((isShiftedInt<1, 0>(0))); + EXPECT_TRUE((isShiftedInt<1, 0>(-1))); + EXPECT_FALSE((isShiftedInt<1, 0>(2))); + EXPECT_FALSE((isShiftedInt<1, 0>(3))); + EXPECT_FALSE((isShiftedInt<1, 0>(0x8000000000000000))); + EXPECT_TRUE((isShiftedInt<1, 63>(0x8000000000000000))); + EXPECT_TRUE((isShiftedInt<2, 62>(0xC000000000000000))); + EXPECT_FALSE((isShiftedInt<2, 62>(0xE000000000000000))); + + // 0x201 is ten bits long and has a 1 in the MSB and LSB. + EXPECT_TRUE((isShiftedInt<11, 5>(int64_t(0x201) << 5))); + EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 3))); + EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 6))); + EXPECT_TRUE((isShiftedInt<11, 5>(-(int64_t(0x201) << 5)))); + EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 3)))); + EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 6)))); + + EXPECT_TRUE((isShiftedInt<6, 10>(-(int64_t(1) << 15)))); + EXPECT_FALSE((isShiftedInt<6, 10>(int64_t(1) << 15))); +} + +} // namespace diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index 3f626f87888a..1a6ffa50e983 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -17,6 +17,7 @@ #include "gtest/gtest.h" #ifdef LLVM_ON_WIN32 +#include "llvm/ADT/ArrayRef.h" #include <windows.h> #include <winerror.h> #endif @@ -416,7 +417,7 @@ TEST(SupportDeathTest, TempDirectoryOnWindows) { SmallString<270> Expected{"C:\\Temp\\AB\\123456789"}; while (Expected.size() < 260) Expected.append("\\DirNameWith19Charss"); - ASSERT_EQ(260, Expected.size()); + ASSERT_EQ(260U, Expected.size()); EXPECT_TEMP_DIR(_putenv_s("TMP", Expected.c_str()), Expected.c_str()); } #endif @@ -715,6 +716,20 @@ TEST_F(FileSystemTest, DirectoryIteration) { ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0/za1")); ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0")); ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive")); + + // Test recursive_directory_iterator level() + ASSERT_NO_ERROR( + fs::create_directories(Twine(TestDirectory) + "/reclevel/a/b/c")); + fs::recursive_directory_iterator I(Twine(TestDirectory) + "/reclevel", ec), E; + for (int l = 0; I != E; I.increment(ec), ++l) { + ASSERT_NO_ERROR(ec); + EXPECT_EQ(I.level(), l); + } + EXPECT_EQ(I, E); + ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b/c")); + ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b")); + ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a")); + ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel")); } const char archive[] = "!<arch>\x0A"; @@ -725,22 +740,30 @@ const char coff_bigobj[] = "\x00\x00\xff\xff\x00\x02......" const char coff_import_library[] = "\x00\x00\xff\xff...."; const char elf_relocatable[] = { 0x7f, 'E', 'L', 'F', 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; -const char macho_universal_binary[] = "\xca\xfe\xba\xbe...\0x00"; -const char macho_object[] = "\xfe\xed\xfa\xce..........\x00\x01"; -const char macho_executable[] = "\xfe\xed\xfa\xce..........\x00\x02"; +const char macho_universal_binary[] = "\xca\xfe\xba\xbe...\x00"; +const char macho_object[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x01............"; +const char macho_executable[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x02............"; const char macho_fixed_virtual_memory_shared_lib[] = - "\xfe\xed\xfa\xce..........\x00\x03"; -const char macho_core[] = "\xfe\xed\xfa\xce..........\x00\x04"; -const char macho_preload_executable[] = "\xfe\xed\xfa\xce..........\x00\x05"; + "\xfe\xed\xfa\xce........\x00\x00\x00\x03............"; +const char macho_core[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x04............"; +const char macho_preload_executable[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x05............"; const char macho_dynamically_linked_shared_lib[] = - "\xfe\xed\xfa\xce..........\x00\x06"; -const char macho_dynamic_linker[] = "\xfe\xed\xfa\xce..........\x00\x07"; -const char macho_bundle[] = "\xfe\xed\xfa\xce..........\x00\x08"; -const char macho_dsym_companion[] = "\xfe\xed\xfa\xce..........\x00\x0a"; -const char macho_kext_bundle[] = "\xfe\xed\xfa\xce..........\x00\x0b"; + "\xfe\xed\xfa\xce........\x00\x00\x00\x06............"; +const char macho_dynamic_linker[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x07............"; +const char macho_bundle[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x08............"; +const char macho_dsym_companion[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x0a............"; +const char macho_kext_bundle[] = + "\xfe\xed\xfa\xce........\x00\x00\x00\x0b............"; const char windows_resource[] = "\x00\x00\x00\x00\x020\x00\x00\x00\xff"; const char macho_dynamically_linked_shared_lib_stub[] = - "\xfe\xed\xfa\xce..........\x00\x09"; + "\xfe\xed\xfa\xce........\x00\x00\x00\x09............"; TEST_F(FileSystemTest, Magic) { struct type { @@ -947,4 +970,145 @@ TEST(Support, RemoveDots) { EXPECT_EQ("c", Path1); #endif } + +TEST(Support, ReplacePathPrefix) { + SmallString<64> Path1("/foo"); + SmallString<64> Path2("/old/foo"); + SmallString<64> OldPrefix("/old"); + SmallString<64> NewPrefix("/new"); + SmallString<64> NewPrefix2("/longernew"); + SmallString<64> EmptyPrefix(""); + + SmallString<64> Path = Path1; + path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_EQ(Path, "/foo"); + Path = Path2; + path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_EQ(Path, "/new/foo"); + Path = Path2; + path::replace_path_prefix(Path, OldPrefix, NewPrefix2); + EXPECT_EQ(Path, "/longernew/foo"); + Path = Path1; + path::replace_path_prefix(Path, EmptyPrefix, NewPrefix); + EXPECT_EQ(Path, "/new/foo"); + Path = Path2; + path::replace_path_prefix(Path, OldPrefix, EmptyPrefix); + EXPECT_EQ(Path, "/foo"); +} + +TEST_F(FileSystemTest, PathFromFD) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + ASSERT_NO_ERROR( + fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + + // Make sure it exists. + ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); + + // Try to get the path from the file descriptor + SmallString<64> ResultPath; + std::error_code ErrorCode = + fs::getPathFromOpenFD(FileDescriptor, ResultPath); + + // If we succeeded, check that the paths are the same (modulo case): + if (!ErrorCode) { + // The paths returned by createTemporaryFile and getPathFromOpenFD + // should reference the same file on disk. + fs::UniqueID D1, D2; + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1)); + ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2)); + ASSERT_EQ(D1, D2); + } + + ::close(FileDescriptor); +} + +TEST_F(FileSystemTest, PathFromFDWin32) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + ASSERT_NO_ERROR( + fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + + // Make sure it exists. + ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); + + SmallVector<char, 8> ResultPath; + std::error_code ErrorCode = + fs::getPathFromOpenFD(FileDescriptor, ResultPath); + + if (!ErrorCode) { + // Now that we know how much space is required for the path, create a path + // buffer with exactly enough space (sans null terminator, which should not + // be present), and call getPathFromOpenFD again to ensure that the API + // properly handles exactly-sized buffers. + SmallVector<char, 8> ExactSizedPath(ResultPath.size()); + ErrorCode = fs::getPathFromOpenFD(FileDescriptor, ExactSizedPath); + ResultPath = ExactSizedPath; + } + + if (!ErrorCode) { + fs::UniqueID D1, D2; + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1)); + ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2)); + ASSERT_EQ(D1, D2); + } + ::close(FileDescriptor); +} + +TEST_F(FileSystemTest, PathFromFDUnicode) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + + // Test Unicode: "<temp directory>/(pi)r^2<temp rand chars>.aleth.0" + ASSERT_NO_ERROR( + fs::createTemporaryFile("\xCF\x80r\xC2\xB2", + "\xE2\x84\xB5.0", FileDescriptor, TempPath)); + + // Make sure it exists. + ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); + + SmallVector<char, 8> ResultPath; + std::error_code ErrorCode = + fs::getPathFromOpenFD(FileDescriptor, ResultPath); + + if (!ErrorCode) { + fs::UniqueID D1, D2; + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1)); + ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2)); + ASSERT_EQ(D1, D2); + } + ::close(FileDescriptor); +} + +TEST_F(FileSystemTest, OpenFileForRead) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + ASSERT_NO_ERROR( + fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + + // Make sure it exists. + ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); + + // Open the file for read + int FileDescriptor2; + SmallString<64> ResultPath; + ASSERT_NO_ERROR( + fs::openFileForRead(Twine(TempPath), FileDescriptor2, &ResultPath)) + + // If we succeeded, check that the paths are the same (modulo case): + if (!ResultPath.empty()) { + // The paths returned by createTemporaryFile and getPathFromOpenFD + // should reference the same file on disk. + fs::UniqueID D1, D2; + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1)); + ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2)); + ASSERT_EQ(D1, D2); + } + + ::close(FileDescriptor); +} } // anonymous namespace diff --git a/unittests/Support/ProgramTest.cpp b/unittests/Support/ProgramTest.cpp index 47a3dbb5fb1f..886ead8305bc 100644 --- a/unittests/Support/ProgramTest.cpp +++ b/unittests/Support/ProgramTest.cpp @@ -223,7 +223,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) { ProcessInfo PI1 = ExecuteNoWait(Executable, argv, getEnviron(), nullptr, 0, &Error, &ExecutionFailed); ASSERT_FALSE(ExecutionFailed) << Error; - ASSERT_NE(PI1.Pid, 0) << "Invalid process id"; + ASSERT_NE(PI1.Pid, ProcessInfo::InvalidPid) << "Invalid process id"; unsigned LoopCount = 0; @@ -231,7 +231,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) { // LoopCount should only be incremented once. while (true) { ++LoopCount; - ProcessInfo WaitResult = Wait(PI1, 0, true, &Error); + ProcessInfo WaitResult = llvm::sys::Wait(PI1, 0, true, &Error); ASSERT_TRUE(Error.empty()); if (WaitResult.Pid == PI1.Pid) break; @@ -242,13 +242,13 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) { ProcessInfo PI2 = ExecuteNoWait(Executable, argv, getEnviron(), nullptr, 0, &Error, &ExecutionFailed); ASSERT_FALSE(ExecutionFailed) << Error; - ASSERT_NE(PI2.Pid, 0) << "Invalid process id"; + ASSERT_NE(PI2.Pid, ProcessInfo::InvalidPid) << "Invalid process id"; // Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this // cse, LoopCount should be greater than 1 (more than one increment occurs). while (true) { ++LoopCount; - ProcessInfo WaitResult = Wait(PI2, 0, false, &Error); + ProcessInfo WaitResult = llvm::sys::Wait(PI2, 0, false, &Error); ASSERT_TRUE(Error.empty()); if (WaitResult.Pid == PI2.Pid) break; @@ -304,7 +304,7 @@ TEST(ProgramTest, TestExecuteNegative) { bool ExecutionFailed; ProcessInfo PI = ExecuteNoWait(Executable, argv, nullptr, nullptr, 0, &Error, &ExecutionFailed); - ASSERT_EQ(PI.Pid, 0) + ASSERT_EQ(PI.Pid, ProcessInfo::InvalidPid) << "On error ExecuteNoWait should return an invalid ProcessInfo"; ASSERT_TRUE(ExecutionFailed); ASSERT_FALSE(Error.empty()); diff --git a/unittests/Support/StreamingMemoryObject.cpp b/unittests/Support/StreamingMemoryObject.cpp deleted file mode 100644 index e86aa9cae51e..000000000000 --- a/unittests/Support/StreamingMemoryObject.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//===- llvm/unittest/Support/StreamingMemoryObject.cpp - 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/ADT/STLExtras.h" -#include "llvm/Support/StreamingMemoryObject.h" -#include "gtest/gtest.h" -#include <string.h> - -using namespace llvm; - -namespace { -class NullDataStreamer : public DataStreamer { - size_t GetBytes(unsigned char *buf, size_t len) override { - memset(buf, 0, len); - return len; - } -}; -} - -TEST(StreamingMemoryObject, Test) { - auto DS = make_unique<NullDataStreamer>(); - StreamingMemoryObject O(std::move(DS)); - EXPECT_TRUE(O.isValidAddress(32 * 1024)); -} - -TEST(StreamingMemoryObject, TestSetKnownObjectSize) { - auto DS = make_unique<NullDataStreamer>(); - StreamingMemoryObject O(std::move(DS)); - uint8_t Buf[32]; - EXPECT_EQ((uint64_t) 16, O.readBytes(Buf, 16, 0)); - O.setKnownObjectSize(24); - EXPECT_EQ((uint64_t) 8, O.readBytes(Buf, 16, 16)); -} diff --git a/unittests/Support/StreamingMemoryObjectTest.cpp b/unittests/Support/StreamingMemoryObjectTest.cpp new file mode 100644 index 000000000000..836dfa9084f5 --- /dev/null +++ b/unittests/Support/StreamingMemoryObjectTest.cpp @@ -0,0 +1,68 @@ +//===- unittests/Support/StreamingMemoryObjectTest.cpp --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/StreamingMemoryObject.h" +#include "gtest/gtest.h" +#include <string.h> + +using namespace llvm; + +namespace { + +class NullDataStreamer : public DataStreamer { + size_t GetBytes(unsigned char *Buffer, size_t Length) override { + memset(Buffer, 0, Length); + return Length; + } +}; + +class BufferStreamer : public DataStreamer { + StringRef Buffer; + +public: + BufferStreamer(StringRef Buffer) : Buffer(Buffer) {} + size_t GetBytes(unsigned char *OutBuffer, size_t Length) override { + if (Length >= Buffer.size()) + Length = Buffer.size(); + + std::copy(Buffer.begin(), Buffer.begin() + Length, OutBuffer); + Buffer = Buffer.drop_front(Length); + return Length; + } +}; + +TEST(StreamingMemoryObjectTest, isValidAddress) { + auto DS = make_unique<NullDataStreamer>(); + StreamingMemoryObject O(std::move(DS)); + EXPECT_TRUE(O.isValidAddress(32 * 1024)); +} + +TEST(StreamingMemoryObjectTest, setKnownObjectSize) { + auto DS = make_unique<NullDataStreamer>(); + StreamingMemoryObject O(std::move(DS)); + uint8_t Buf[32]; + EXPECT_EQ(16u, O.readBytes(Buf, 16, 0)); + O.setKnownObjectSize(24); + EXPECT_EQ(8u, O.readBytes(Buf, 16, 16)); +} + +TEST(StreamingMemoryObjectTest, getPointer) { + uint8_t InputBuffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + StreamingMemoryObject O(make_unique<BufferStreamer>(StringRef( + reinterpret_cast<const char *>(InputBuffer), sizeof(InputBuffer)))); + + EXPECT_TRUE(std::equal(InputBuffer + 1, InputBuffer + 2, O.getPointer(1, 2))); + EXPECT_TRUE(std::equal(InputBuffer + 3, InputBuffer + 7, O.getPointer(3, 4))); + EXPECT_TRUE(std::equal(InputBuffer + 4, InputBuffer + 8, O.getPointer(4, 5))); + EXPECT_TRUE(std::equal(InputBuffer, InputBuffer + 8, O.getPointer(0, 20))); +} + +} // end namespace diff --git a/unittests/Support/TargetParserTest.cpp b/unittests/Support/TargetParserTest.cpp new file mode 100644 index 000000000000..21994f27b73c --- /dev/null +++ b/unittests/Support/TargetParserTest.cpp @@ -0,0 +1,92 @@ +//===----------- TargetParser.cpp - Target Parser -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/Support/TargetParser.h" + +using namespace llvm; + +namespace { +static const unsigned kAArch64ArchKinds[] = { +#define AARCH64_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, \ + ARCH_BASE_EXT) \ + llvm::ARM::ID, +#include "llvm/Support/AArch64TargetParser.def" +#undef AARCH64_ARCH +}; + +template <typename T, size_t N> +bool contains(const T (&array)[N], const T element) { + return std::find(std::begin(array), std::end(array), element) != + std::end(array); +} + +TEST(TargetParserTest, ARMArchName) { + for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0); + AK <= ARM::ArchKind::AK_LAST; + AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1)) + EXPECT_TRUE(AK == ARM::AK_LAST ? ARM::getArchName(AK).empty() + : !ARM::getArchName(AK).empty()); +} + +TEST(TargetParserTest, ARMCPUAttr) { + for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0); + AK <= ARM::ArchKind::AK_LAST; + AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1)) + EXPECT_TRUE((AK == ARM::AK_INVALID || AK == ARM::AK_LAST) + ? ARM::getCPUAttr(AK).empty() + : !ARM::getCPUAttr(AK).empty()); +} + +TEST(TargetParserTest, ARMSubArch) { + for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0); + AK <= ARM::ArchKind::AK_LAST; + AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1)) + EXPECT_TRUE((AK == ARM::AK_INVALID || AK == ARM::AK_IWMMXT || + AK == ARM::AK_IWMMXT2 || AK == ARM::AK_LAST) + ? ARM::getSubArch(AK).empty() + : !ARM::getSubArch(AK).empty()); +} + +TEST(TargetParserTest, ARMFPUName) { + for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0); + FK <= ARM::FPUKind::FK_LAST; + FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1)) + EXPECT_TRUE(FK == ARM::FK_LAST ? ARM::getFPUName(FK).empty() + : !ARM::getFPUName(FK).empty()); +} + +TEST(TargetParserTest, AArch64ArchName) { + for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0); + AK <= ARM::ArchKind::AK_LAST; + AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1)) + EXPECT_TRUE(contains(kAArch64ArchKinds, static_cast<unsigned>(AK)) + ? !AArch64::getArchName(AK).empty() + : AArch64::getArchName(AK).empty()); +} + +TEST(TargetParserTest, AArch64CPUAttr) { + for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0); + AK <= ARM::ArchKind::AK_LAST; + AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1)) + EXPECT_TRUE(contains(kAArch64ArchKinds, static_cast<unsigned>(AK)) + ? !AArch64::getCPUAttr(AK).empty() + : AArch64::getCPUAttr(AK).empty()); +} + +TEST(TargetParserTest, AArch64SubArch) { + for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0); + AK <= ARM::ArchKind::AK_LAST; + AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1)) + EXPECT_TRUE(contains(kAArch64ArchKinds, static_cast<unsigned>(AK)) + ? !AArch64::getSubArch(AK).empty() + : AArch64::getSubArch(AK).empty()); +} +} + diff --git a/unittests/Support/ThreadPool.cpp b/unittests/Support/ThreadPool.cpp index 0f36c383d494..69a24bc5444c 100644 --- a/unittests/Support/ThreadPool.cpp +++ b/unittests/Support/ThreadPool.cpp @@ -135,7 +135,7 @@ TEST_F(ThreadPoolTest, Async) { TEST_F(ThreadPoolTest, GetFuture) { CHECK_UNSUPPORTED(); - ThreadPool Pool; + ThreadPool Pool{2}; std::atomic_int i{0}; Pool.async([this, &i] { waitForMainThread(); diff --git a/unittests/Support/TrailingObjectsTest.cpp b/unittests/Support/TrailingObjectsTest.cpp index 866ff1e6e88d..a1d3e7b3c864 100644 --- a/unittests/Support/TrailingObjectsTest.cpp +++ b/unittests/Support/TrailingObjectsTest.cpp @@ -34,6 +34,7 @@ public: void *Mem = ::operator new(totalSizeToAlloc<short>(NumShorts)); return new (Mem) Class1(ShortArray, NumShorts); } + void operator delete(void *p) { ::operator delete(p); } short get(unsigned Num) const { return getTrailingObjects<short>()[Num]; } @@ -78,6 +79,7 @@ public: *C->getTrailingObjects<double>() = D; return C; } + void operator delete(void *p) { ::operator delete(p); } short getShort() const { if (!HasShort) @@ -118,8 +120,8 @@ TEST(TrailingObjects, TwoArg) { Class2 *C1 = Class2::create(4); Class2 *C2 = Class2::create(0, 4.2); - EXPECT_EQ(sizeof(Class2), llvm::RoundUpToAlignment(sizeof(bool) * 2, - llvm::alignOf<double>())); + EXPECT_EQ(sizeof(Class2), + llvm::alignTo(sizeof(bool) * 2, llvm::alignOf<double>())); EXPECT_EQ(llvm::alignOf<Class2>(), llvm::alignOf<double>()); EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(1, 0)), @@ -162,8 +164,7 @@ class Class3 final : public TrailingObjects<Class3, double, short, bool> { TEST(TrailingObjects, ThreeArg) { EXPECT_EQ((Class3::additionalSizeToAlloc<double, short, bool>(1, 1, 3)), sizeof(double) + sizeof(short) + 3 * sizeof(bool)); - EXPECT_EQ(sizeof(Class3), - llvm::RoundUpToAlignment(1, llvm::alignOf<double>())); + EXPECT_EQ(sizeof(Class3), llvm::alignTo(1, llvm::alignOf<double>())); std::unique_ptr<char[]> P(new char[1000]); Class3 *C = reinterpret_cast<Class3 *>(P.get()); EXPECT_EQ(C->getTrailingObjects<double>(), reinterpret_cast<double *>(C + 1)); @@ -183,8 +184,8 @@ class Class4 final : public TrailingObjects<Class4, char, long> { TEST(TrailingObjects, Realignment) { EXPECT_EQ((Class4::additionalSizeToAlloc<char, long>(1, 1)), - llvm::RoundUpToAlignment(sizeof(long) + 1, llvm::alignOf<long>())); - EXPECT_EQ(sizeof(Class4), llvm::RoundUpToAlignment(1, llvm::alignOf<long>())); + llvm::alignTo(sizeof(long) + 1, llvm::alignOf<long>())); + EXPECT_EQ(sizeof(Class4), llvm::alignTo(1, llvm::alignOf<long>())); std::unique_ptr<char[]> P(new char[1000]); Class4 *C = reinterpret_cast<Class4 *>(P.get()); EXPECT_EQ(C->getTrailingObjects<char>(), reinterpret_cast<char *>(C + 1)); diff --git a/unittests/Support/TypeNameTest.cpp b/unittests/Support/TypeNameTest.cpp new file mode 100644 index 000000000000..63381d46f02f --- /dev/null +++ b/unittests/Support/TypeNameTest.cpp @@ -0,0 +1,49 @@ +//===- TypeNameTest.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/TypeName.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { +namespace N1 { +struct S1 {}; +class C1 {}; +union U1 {}; +} + +TEST(TypeNameTest, Names) { + struct S2 {}; + + StringRef S1Name = getTypeName<N1::S1>(); + StringRef C1Name = getTypeName<N1::C1>(); + StringRef U1Name = getTypeName<N1::U1>(); + StringRef S2Name = getTypeName<S2>(); + +#if defined(__clang__) || defined(__GNUC__) || defined(__INTEL_COMPILER) || \ + defined(_MSC_VER) + EXPECT_TRUE(S1Name.endswith("::N1::S1")) << S1Name.str(); + EXPECT_TRUE(C1Name.endswith("::N1::C1")) << C1Name.str(); + EXPECT_TRUE(U1Name.endswith("::N1::U1")) << U1Name.str(); +#ifdef __clang__ + EXPECT_TRUE(S2Name.endswith("S2")) << S2Name.str(); +#else + EXPECT_TRUE(S2Name.endswith("::S2")) << S2Name.str(); +#endif +#else + EXPECT_EQ("UNKNOWN_TYPE", S1Name); + EXPECT_EQ("UNKNOWN_TYPE", C1Name); + EXPECT_EQ("UNKNOWN_TYPE", U1Name); + EXPECT_EQ("UNKNOWN_TYPE", S2Name); +#endif +} + +} // end anonymous namespace diff --git a/unittests/Support/YAMLIOTest.cpp b/unittests/Support/YAMLIOTest.cpp index e7affa1698dc..5f35a802caa5 100644 --- a/unittests/Support/YAMLIOTest.cpp +++ b/unittests/Support/YAMLIOTest.cpp @@ -7,9 +7,9 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/YAMLTraits.h" #include "gtest/gtest.h" @@ -389,6 +389,111 @@ TEST(YAMLIO, TestReadWriteBuiltInTypes) { } } +//===----------------------------------------------------------------------===// +// Test endian-aware types +//===----------------------------------------------------------------------===// + +struct EndianTypes { + typedef llvm::support::detail::packed_endian_specific_integral< + float, llvm::support::little, llvm::support::unaligned> + ulittle_float; + typedef llvm::support::detail::packed_endian_specific_integral< + double, llvm::support::little, llvm::support::unaligned> + ulittle_double; + + llvm::support::ulittle64_t u64; + llvm::support::ulittle32_t u32; + llvm::support::ulittle16_t u16; + llvm::support::little64_t s64; + llvm::support::little32_t s32; + llvm::support::little16_t s16; + ulittle_float f; + ulittle_double d; +}; + +namespace llvm { +namespace yaml { +template <> struct MappingTraits<EndianTypes> { + static void mapping(IO &io, EndianTypes &et) { + io.mapRequired("u64", et.u64); + io.mapRequired("u32", et.u32); + io.mapRequired("u16", et.u16); + io.mapRequired("s64", et.s64); + io.mapRequired("s32", et.s32); + io.mapRequired("s16", et.s16); + io.mapRequired("f", et.f); + io.mapRequired("d", et.d); + } +}; +} +} + +// +// Test the reading of all endian scalar conversions +// +TEST(YAMLIO, TestReadEndianTypes) { + EndianTypes map; + Input yin("---\n" + "u64: 5000000000\n" + "u32: 4000000000\n" + "u16: 65000\n" + "s64: -5000000000\n" + "s32: -2000000000\n" + "s16: -32000\n" + "f: 3.25\n" + "d: -2.8625\n" + "...\n"); + yin >> map; + + EXPECT_FALSE(yin.error()); + EXPECT_EQ(map.u64, 5000000000ULL); + EXPECT_EQ(map.u32, 4000000000U); + EXPECT_EQ(map.u16, 65000); + EXPECT_EQ(map.s64, -5000000000LL); + EXPECT_EQ(map.s32, -2000000000L); + EXPECT_EQ(map.s16, -32000); + EXPECT_EQ(map.f, 3.25f); + EXPECT_EQ(map.d, -2.8625); +} + +// +// Test writing then reading back all endian-aware scalar types +// +TEST(YAMLIO, TestReadWriteEndianTypes) { + std::string intermediate; + { + EndianTypes map; + map.u64 = 6000000000ULL; + map.u32 = 3000000000U; + map.u16 = 50000; + map.s64 = -6000000000LL; + map.s32 = -2000000000; + map.s16 = -32000; + map.f = 3.25f; + map.d = -2.8625; + + llvm::raw_string_ostream ostr(intermediate); + Output yout(ostr); + yout << map; + } + + { + Input yin(intermediate); + EndianTypes map; + yin >> map; + + EXPECT_FALSE(yin.error()); + EXPECT_EQ(map.u64, 6000000000ULL); + EXPECT_EQ(map.u32, 3000000000U); + EXPECT_EQ(map.u16, 50000); + EXPECT_EQ(map.s64, -6000000000LL); + EXPECT_EQ(map.s32, -2000000000L); + EXPECT_EQ(map.s16, -32000); + EXPECT_EQ(map.f, 3.25f); + EXPECT_EQ(map.d, -2.8625); + } +} + struct StringTypes { llvm::StringRef str1; llvm::StringRef str2; diff --git a/unittests/Support/YAMLParserTest.cpp b/unittests/Support/YAMLParserTest.cpp index 41ad649699ca..3f12a53fd9c5 100644 --- a/unittests/Support/YAMLParserTest.cpp +++ b/unittests/Support/YAMLParserTest.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" #include "llvm/Support/MemoryBuffer.h" diff --git a/unittests/Support/raw_ostream_test.cpp b/unittests/Support/raw_ostream_test.cpp index ff986025b2cc..ed6ddabe4634 100644 --- a/unittests/Support/raw_ostream_test.cpp +++ b/unittests/Support/raw_ostream_test.cpp @@ -70,8 +70,8 @@ TEST(raw_ostreamTest, Types_Buffered) { // void* EXPECT_EQ("0x0", printToString((void*) nullptr)); - EXPECT_EQ("0xbeef", printToString((void*) 0xbeef)); - EXPECT_EQ("0xdeadbeef", printToString((void*) 0xdeadbeef)); + EXPECT_EQ("0xbeef", printToString((void*) 0xbeefLL)); + EXPECT_EQ("0xdeadbeef", printToString((void*) 0xdeadbeefLL)); // Min and max. EXPECT_EQ("18446744073709551615", printToString(UINT64_MAX)); @@ -101,8 +101,8 @@ TEST(raw_ostreamTest, Types_Unbuffered) { // void* EXPECT_EQ("0x0", printToStringUnbuffered((void*) nullptr)); - EXPECT_EQ("0xbeef", printToStringUnbuffered((void*) 0xbeef)); - EXPECT_EQ("0xdeadbeef", printToStringUnbuffered((void*) 0xdeadbeef)); + EXPECT_EQ("0xbeef", printToStringUnbuffered((void*) 0xbeefLL)); + EXPECT_EQ("0xdeadbeef", printToStringUnbuffered((void*) 0xdeadbeefLL)); // Min and max. EXPECT_EQ("18446744073709551615", printToStringUnbuffered(UINT64_MAX)); diff --git a/unittests/Support/raw_sha1_ostream_test.cpp b/unittests/Support/raw_sha1_ostream_test.cpp new file mode 100644 index 000000000000..db2a3e9ab643 --- /dev/null +++ b/unittests/Support/raw_sha1_ostream_test.cpp @@ -0,0 +1,71 @@ +//===- llvm/unittest/Support/raw_ostream_test.cpp - raw_ostream tests -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_sha1_ostream.h" + +#include <string> + +using namespace llvm; + +static std::string toHex(StringRef Input) { + static const char *const LUT = "0123456789ABCDEF"; + size_t Length = Input.size(); + + std::string Output; + Output.reserve(2 * Length); + for (size_t i = 0; i < Length; ++i) { + const unsigned char c = Input[i]; + Output.push_back(LUT[c >> 4]); + Output.push_back(LUT[c & 15]); + } + return Output; +} + +TEST(raw_sha1_ostreamTest, Basic) { + llvm::raw_sha1_ostream Sha1Stream; + Sha1Stream << "Hello World!"; + auto Hash = toHex(Sha1Stream.sha1()); + + ASSERT_EQ("2EF7BDE608CE5404E97D5F042F95F89F1C232871", Hash); +} + +// Check that getting the intermediate hash in the middle of the stream does +// not invalidate the final result. +TEST(raw_sha1_ostreamTest, Intermediate) { + llvm::raw_sha1_ostream Sha1Stream; + Sha1Stream << "Hello"; + auto Hash = toHex(Sha1Stream.sha1()); + + ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash); + Sha1Stream << " World!"; + Hash = toHex(Sha1Stream.sha1()); + + // Compute the non-split hash separately as a reference. + llvm::raw_sha1_ostream NonSplitSha1Stream; + NonSplitSha1Stream << "Hello World!"; + auto NonSplitHash = toHex(NonSplitSha1Stream.sha1()); + + ASSERT_EQ(NonSplitHash, Hash); +} + +TEST(raw_sha1_ostreamTest, Reset) { + llvm::raw_sha1_ostream Sha1Stream; + Sha1Stream << "Hello"; + auto Hash = toHex(Sha1Stream.sha1()); + + ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash); + + Sha1Stream.resetHash(); + Sha1Stream << " World!"; + Hash = toHex(Sha1Stream.sha1()); + + ASSERT_EQ("7447F2A5A42185C8CF91E632789C431830B59067", Hash); +} diff --git a/unittests/Transforms/IPO/CMakeLists.txt b/unittests/Transforms/IPO/CMakeLists.txt index 58b71b2bce03..ee33a5fcd1b3 100644 --- a/unittests/Transforms/IPO/CMakeLists.txt +++ b/unittests/Transforms/IPO/CMakeLists.txt @@ -5,5 +5,6 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_unittest(IPOTests - LowerBitSets.cpp + LowerTypeTests.cpp + WholeProgramDevirt.cpp ) diff --git a/unittests/Transforms/IPO/LowerBitSets.cpp b/unittests/Transforms/IPO/LowerTypeTests.cpp index 49a42cd20d7a..66c9de6bd662 100644 --- a/unittests/Transforms/IPO/LowerBitSets.cpp +++ b/unittests/Transforms/IPO/LowerTypeTests.cpp @@ -1,4 +1,4 @@ -//===- LowerBitSets.cpp - Unit tests for bitset lowering ------------------===// +//===- LowerTypeTests.cpp - Unit tests for type test lowering -------------===// // // The LLVM Compiler Infrastructure // @@ -7,12 +7,13 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Transforms/IPO/LowerBitSets.h" +#include "llvm/Transforms/IPO/LowerTypeTests.h" #include "gtest/gtest.h" using namespace llvm; +using namespace lowertypetests; -TEST(LowerBitSets, BitSetBuilder) { +TEST(LowerTypeTests, BitSetBuilder) { struct { std::vector<uint64_t> Offsets; std::set<uint64_t> Bits; @@ -79,7 +80,7 @@ TEST(LowerBitSets, BitSetBuilder) { } } -TEST(LowerBitSets, GlobalLayoutBuilder) { +TEST(LowerTypeTests, GlobalLayoutBuilder) { struct { uint64_t NumObjects; std::vector<std::set<uint64_t>> Fragments; @@ -106,7 +107,7 @@ TEST(LowerBitSets, GlobalLayoutBuilder) { } } -TEST(LowerBitSets, ByteArrayBuilder) { +TEST(LowerTypeTests, ByteArrayBuilder) { struct BABAlloc { std::set<uint64_t> Bits; uint64_t BitSize; diff --git a/unittests/Transforms/IPO/Makefile b/unittests/Transforms/IPO/Makefile deleted file mode 100644 index f807879c2b5f..000000000000 --- a/unittests/Transforms/IPO/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/Transforms/IPO/Makefile -------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -TESTNAME = IPO -LINK_COMPONENTS := IPO - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Transforms/IPO/WholeProgramDevirt.cpp b/unittests/Transforms/IPO/WholeProgramDevirt.cpp new file mode 100644 index 000000000000..7e7a6bf45964 --- /dev/null +++ b/unittests/Transforms/IPO/WholeProgramDevirt.cpp @@ -0,0 +1,165 @@ +//===- WholeProgramDevirt.cpp - Unit tests for whole-program devirt -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/IPO/WholeProgramDevirt.h" +#include "llvm/ADT/ArrayRef.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace wholeprogramdevirt; + +TEST(WholeProgramDevirt, findLowestOffset) { + VTableBits VT1; + VT1.ObjectSize = 8; + VT1.Before.BytesUsed = {1 << 0}; + VT1.After.BytesUsed = {1 << 1}; + + VTableBits VT2; + VT2.ObjectSize = 8; + VT2.Before.BytesUsed = {1 << 1}; + VT2.After.BytesUsed = {1 << 0}; + + TypeMemberInfo TM1{&VT1, 0}; + TypeMemberInfo TM2{&VT2, 0}; + VirtualCallTarget Targets[] = { + {&TM1, /*IsBigEndian=*/false}, + {&TM2, /*IsBigEndian=*/false}, + }; + + EXPECT_EQ(2ull, findLowestOffset(Targets, /*IsAfter=*/false, 1)); + EXPECT_EQ(66ull, findLowestOffset(Targets, /*IsAfter=*/true, 1)); + + EXPECT_EQ(8ull, findLowestOffset(Targets, /*IsAfter=*/false, 8)); + EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/true, 8)); + + TM1.Offset = 4; + EXPECT_EQ(33ull, findLowestOffset(Targets, /*IsAfter=*/false, 1)); + EXPECT_EQ(65ull, findLowestOffset(Targets, /*IsAfter=*/true, 1)); + + EXPECT_EQ(40ull, findLowestOffset(Targets, /*IsAfter=*/false, 8)); + EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/true, 8)); + + TM1.Offset = 8; + TM2.Offset = 8; + EXPECT_EQ(66ull, findLowestOffset(Targets, /*IsAfter=*/false, 1)); + EXPECT_EQ(2ull, findLowestOffset(Targets, /*IsAfter=*/true, 1)); + + EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/false, 8)); + EXPECT_EQ(8ull, findLowestOffset(Targets, /*IsAfter=*/true, 8)); + + VT1.After.BytesUsed = {0xff, 0, 0, 0, 0xff}; + VT2.After.BytesUsed = {0xff, 1, 0, 0, 0}; + EXPECT_EQ(16ull, findLowestOffset(Targets, /*IsAfter=*/true, 16)); + EXPECT_EQ(40ull, findLowestOffset(Targets, /*IsAfter=*/true, 32)); +} + +TEST(WholeProgramDevirt, setReturnValues) { + VTableBits VT1; + VT1.ObjectSize = 8; + + VTableBits VT2; + VT2.ObjectSize = 8; + + TypeMemberInfo TM1{&VT1, 0}; + TypeMemberInfo TM2{&VT2, 0}; + VirtualCallTarget Targets[] = { + {&TM1, /*IsBigEndian=*/false}, + {&TM2, /*IsBigEndian=*/false}, + }; + + TM1.Offset = 4; + TM2.Offset = 4; + + int64_t OffsetByte; + uint64_t OffsetBit; + + Targets[0].RetVal = 1; + Targets[1].RetVal = 0; + setBeforeReturnValues(Targets, 32, 1, OffsetByte, OffsetBit); + EXPECT_EQ(-5ll, OffsetByte); + EXPECT_EQ(0ull, OffsetBit); + EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.Bytes); + EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.BytesUsed); + EXPECT_EQ(std::vector<uint8_t>{0}, VT2.Before.Bytes); + EXPECT_EQ(std::vector<uint8_t>{1}, VT2.Before.BytesUsed); + + Targets[0].RetVal = 0; + Targets[1].RetVal = 1; + setBeforeReturnValues(Targets, 39, 1, OffsetByte, OffsetBit); + EXPECT_EQ(-5ll, OffsetByte); + EXPECT_EQ(7ull, OffsetBit); + EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.Bytes); + EXPECT_EQ(std::vector<uint8_t>{0x81}, VT1.Before.BytesUsed); + EXPECT_EQ(std::vector<uint8_t>{0x80}, VT2.Before.Bytes); + EXPECT_EQ(std::vector<uint8_t>{0x81}, VT2.Before.BytesUsed); + + Targets[0].RetVal = 12; + Targets[1].RetVal = 34; + setBeforeReturnValues(Targets, 40, 8, OffsetByte, OffsetBit); + EXPECT_EQ(-6ll, OffsetByte); + EXPECT_EQ(0ull, OffsetBit); + EXPECT_EQ((std::vector<uint8_t>{1, 12}), VT1.Before.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT1.Before.BytesUsed); + EXPECT_EQ((std::vector<uint8_t>{0x80, 34}), VT2.Before.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT2.Before.BytesUsed); + + Targets[0].RetVal = 56; + Targets[1].RetVal = 78; + setBeforeReturnValues(Targets, 48, 16, OffsetByte, OffsetBit); + EXPECT_EQ(-8ll, OffsetByte); + EXPECT_EQ(0ull, OffsetBit); + EXPECT_EQ((std::vector<uint8_t>{1, 12, 0, 56}), VT1.Before.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}), + VT1.Before.BytesUsed); + EXPECT_EQ((std::vector<uint8_t>{0x80, 34, 0, 78}), VT2.Before.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}), + VT2.Before.BytesUsed); + + Targets[0].RetVal = 1; + Targets[1].RetVal = 0; + setAfterReturnValues(Targets, 32, 1, OffsetByte, OffsetBit); + EXPECT_EQ(4ll, OffsetByte); + EXPECT_EQ(0ull, OffsetBit); + EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.Bytes); + EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.BytesUsed); + EXPECT_EQ(std::vector<uint8_t>{0}, VT2.After.Bytes); + EXPECT_EQ(std::vector<uint8_t>{1}, VT2.After.BytesUsed); + + Targets[0].RetVal = 0; + Targets[1].RetVal = 1; + setAfterReturnValues(Targets, 39, 1, OffsetByte, OffsetBit); + EXPECT_EQ(4ll, OffsetByte); + EXPECT_EQ(7ull, OffsetBit); + EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.Bytes); + EXPECT_EQ(std::vector<uint8_t>{0x81}, VT1.After.BytesUsed); + EXPECT_EQ(std::vector<uint8_t>{0x80}, VT2.After.Bytes); + EXPECT_EQ(std::vector<uint8_t>{0x81}, VT2.After.BytesUsed); + + Targets[0].RetVal = 12; + Targets[1].RetVal = 34; + setAfterReturnValues(Targets, 40, 8, OffsetByte, OffsetBit); + EXPECT_EQ(5ll, OffsetByte); + EXPECT_EQ(0ull, OffsetBit); + EXPECT_EQ((std::vector<uint8_t>{1, 12}), VT1.After.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT1.After.BytesUsed); + EXPECT_EQ((std::vector<uint8_t>{0x80, 34}), VT2.After.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT2.After.BytesUsed); + + Targets[0].RetVal = 56; + Targets[1].RetVal = 78; + setAfterReturnValues(Targets, 48, 16, OffsetByte, OffsetBit); + EXPECT_EQ(6ll, OffsetByte); + EXPECT_EQ(0ull, OffsetBit); + EXPECT_EQ((std::vector<uint8_t>{1, 12, 56, 0}), VT1.After.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}), + VT1.After.BytesUsed); + EXPECT_EQ((std::vector<uint8_t>{0x80, 34, 78, 0}), VT2.After.Bytes); + EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}), + VT2.After.BytesUsed); +} diff --git a/unittests/Transforms/Makefile b/unittests/Transforms/Makefile deleted file mode 100644 index 3a2cdfc2c74a..000000000000 --- a/unittests/Transforms/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -##===- unittests/Transforms/Makefile -----------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. - -PARALLEL_DIRS = IPO Utils - -include $(LEVEL)/Makefile.common - -clean:: - $(Verb) $(RM) -f *Tests diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt index 517ff99ea46b..657d151048e8 100644 --- a/unittests/Transforms/Utils/CMakeLists.txt +++ b/unittests/Transforms/Utils/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + Analysis Core Support TransformUtils @@ -9,5 +10,6 @@ add_llvm_unittest(UtilsTests Cloning.cpp IntegerDivision.cpp Local.cpp + MemorySSA.cpp ValueMapperTest.cpp ) diff --git a/unittests/Transforms/Utils/Cloning.cpp b/unittests/Transforms/Utils/Cloning.cpp index 25e322ee5a8e..f53e0a95e94d 100644 --- a/unittests/Transforms/Utils/Cloning.cpp +++ b/unittests/Transforms/Utils/Cloning.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/IR/Argument.h" @@ -275,8 +274,7 @@ protected: void CreateNewFunc() { ValueToValueMapTy VMap; - NewFunc = CloneFunction(OldFunc, VMap, true, nullptr); - M->getFunctionList().push_back(NewFunc); + NewFunc = CloneFunction(OldFunc, VMap, nullptr); } void SetupFinder() { @@ -302,31 +300,13 @@ TEST_F(CloneFunc, Subprogram) { EXPECT_FALSE(verifyModule(*M)); unsigned SubprogramCount = Finder->subprogram_count(); - EXPECT_EQ(2U, SubprogramCount); + EXPECT_EQ(1U, SubprogramCount); auto Iter = Finder->subprograms().begin(); - auto *Sub1 = cast<DISubprogram>(*Iter); - Iter++; - auto *Sub2 = cast<DISubprogram>(*Iter); + auto *Sub = cast<DISubprogram>(*Iter); - EXPECT_TRUE( - (Sub1 == OldFunc->getSubprogram() && Sub2 == NewFunc->getSubprogram()) || - (Sub1 == NewFunc->getSubprogram() && Sub2 == OldFunc->getSubprogram())); -} - -// Test that the new subprogram entry was not added to the CU which doesn't -// contain the old subprogram entry. -TEST_F(CloneFunc, SubprogramInRightCU) { - EXPECT_FALSE(verifyModule(*M)); - - EXPECT_EQ(2U, Finder->compile_unit_count()); - - auto Iter = Finder->compile_units().begin(); - auto *CU1 = cast<DICompileUnit>(*Iter); - Iter++; - auto *CU2 = cast<DICompileUnit>(*Iter); - EXPECT_TRUE(CU1->getSubprograms().size() == 0 || - CU2->getSubprograms().size() == 0); + EXPECT_TRUE(Sub == OldFunc->getSubprogram()); + EXPECT_TRUE(Sub == NewFunc->getSubprogram()); } // Test that instructions in the old function still belong to it in the @@ -423,6 +403,7 @@ protected: void SetupModule() { OldM = new Module("", C); } void CreateOldModule() { + DIBuilder DBuilder(*OldM); IRBuilder<> IBuilder(C); auto *FuncType = FunctionType::get(Type::getVoidTy(C), false); @@ -431,9 +412,25 @@ protected: auto *F = Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", OldM); F->setPersonalityFn(PersFn); + + // Create debug info + auto *File = DBuilder.createFile("filename.c", "/file/dir/"); + DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None); + DISubroutineType *DFuncType = DBuilder.createSubroutineType(ParamTypes); + auto *CU = + DBuilder.createCompileUnit(dwarf::DW_LANG_C99, "filename.c", + "/file/dir", "CloneModule", false, "", 0); + // Function DI + auto *Subprogram = DBuilder.createFunction(CU, "f", "f", File, 4, DFuncType, + true, true, 3, 0, false); + F->setSubprogram(Subprogram); + auto *Entry = BasicBlock::Create(C, "", F); IBuilder.SetInsertPoint(Entry); IBuilder.CreateRetVoid(); + + // Finalize the debug info + DBuilder.finalize(); } void CreateNewModule() { NewM = llvm::CloneModule(OldM).release(); } @@ -447,4 +444,18 @@ TEST_F(CloneModule, Verify) { EXPECT_FALSE(verifyModule(*NewM)); } +TEST_F(CloneModule, OldModuleUnchanged) { + DebugInfoFinder Finder; + Finder.processModule(*OldM); + EXPECT_EQ(1U, Finder.subprogram_count()); +} + +TEST_F(CloneModule, Subprogram) { + Function *NewF = NewM->getFunction("f"); + DISubprogram *SP = NewF->getSubprogram(); + EXPECT_TRUE(SP != nullptr); + EXPECT_EQ(SP->getName(), "f"); + EXPECT_EQ(SP->getFile()->getFilename(), "filename.c"); + EXPECT_EQ(SP->getLine(), (unsigned)4); +} } diff --git a/unittests/Transforms/Utils/IntegerDivision.cpp b/unittests/Transforms/Utils/IntegerDivision.cpp index 4cda2b4e5892..b6b1b1665ab1 100644 --- a/unittests/Transforms/Utils/IntegerDivision.cpp +++ b/unittests/Transforms/Utils/IntegerDivision.cpp @@ -21,7 +21,7 @@ namespace { TEST(IntegerDivision, SDiv) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test division", C); IRBuilder<> Builder(C); @@ -51,7 +51,7 @@ TEST(IntegerDivision, SDiv) { } TEST(IntegerDivision, UDiv) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test division", C); IRBuilder<> Builder(C); @@ -81,7 +81,7 @@ TEST(IntegerDivision, UDiv) { } TEST(IntegerDivision, SRem) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test remainder", C); IRBuilder<> Builder(C); @@ -111,7 +111,7 @@ TEST(IntegerDivision, SRem) { } TEST(IntegerDivision, URem) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test remainder", C); IRBuilder<> Builder(C); @@ -142,7 +142,7 @@ TEST(IntegerDivision, URem) { TEST(IntegerDivision, SDiv64) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test division", C); IRBuilder<> Builder(C); @@ -172,7 +172,7 @@ TEST(IntegerDivision, SDiv64) { } TEST(IntegerDivision, UDiv64) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test division", C); IRBuilder<> Builder(C); @@ -202,7 +202,7 @@ TEST(IntegerDivision, UDiv64) { } TEST(IntegerDivision, SRem64) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test remainder", C); IRBuilder<> Builder(C); @@ -232,7 +232,7 @@ TEST(IntegerDivision, SRem64) { } TEST(IntegerDivision, URem64) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; Module M("test remainder", C); IRBuilder<> Builder(C); diff --git a/unittests/Transforms/Utils/Local.cpp b/unittests/Transforms/Utils/Local.cpp index 2ff560475551..5164bdbb2a4e 100644 --- a/unittests/Transforms/Utils/Local.cpp +++ b/unittests/Transforms/Utils/Local.cpp @@ -17,7 +17,7 @@ using namespace llvm; TEST(Local, RecursivelyDeleteDeadPHINodes) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; IRBuilder<> builder(C); @@ -60,7 +60,7 @@ TEST(Local, RecursivelyDeleteDeadPHINodes) { } TEST(Local, RemoveDuplicatePHINodes) { - LLVMContext &C(getGlobalContext()); + LLVMContext C; IRBuilder<> B(C); std::unique_ptr<Function> F( diff --git a/unittests/Transforms/Utils/Makefile b/unittests/Transforms/Utils/Makefile deleted file mode 100644 index e6c2a2c133aa..000000000000 --- a/unittests/Transforms/Utils/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- unittests/Transforms/Utils/Makefile -----------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -TESTNAME = Utils -LINK_COMPONENTS := TransformUtils - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Transforms/Utils/MemorySSA.cpp b/unittests/Transforms/Utils/MemorySSA.cpp new file mode 100644 index 000000000000..c21121f78705 --- /dev/null +++ b/unittests/Transforms/Utils/MemorySSA.cpp @@ -0,0 +1,310 @@ +//===- MemorySSA.cpp - Unit tests for MemorySSA ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/Transforms/Utils/MemorySSA.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/BasicAliasAnalysis.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "gtest/gtest.h" + +using namespace llvm; + +const static char DLString[] = "e-i64:64-f80:128-n8:16:32:64-S128"; + +/// There's a lot of common setup between these tests. This fixture helps reduce +/// that. Tests should mock up a function, store it in F, and then call +/// setupAnalyses(). +class MemorySSATest : public testing::Test { +protected: + // N.B. Many of these members depend on each other (e.g. the Module depends on + // the Context, etc.). So, order matters here (and in TestAnalyses). + LLVMContext C; + Module M; + IRBuilder<> B; + DataLayout DL; + TargetLibraryInfoImpl TLII; + TargetLibraryInfo TLI; + Function *F; + + // Things that we need to build after the function is created. + struct TestAnalyses { + DominatorTree DT; + AssumptionCache AC; + AAResults AA; + BasicAAResult BAA; + MemorySSA MSSA; + MemorySSAWalker *Walker; + + TestAnalyses(MemorySSATest &Test) + : DT(*Test.F), AC(*Test.F), AA(Test.TLI), + BAA(Test.DL, Test.TLI, AC, &DT), MSSA(*Test.F, &AA, &DT) { + AA.addAAResult(BAA); + Walker = MSSA.getWalker(); + } + }; + + std::unique_ptr<TestAnalyses> Analyses; + + void setupAnalyses() { + assert(F); + Analyses.reset(new TestAnalyses(*this)); + } + +public: + MemorySSATest() + : M("MemorySSATest", C), B(C), DL(DLString), TLI(TLII), F(nullptr) {} +}; + +TEST_F(MemorySSATest, CreateALoadAndPhi) { + // We create a diamond where there is a store on one side, and then after + // running memory ssa, create a load after the merge point, and use it to test + // updating by creating an access for the load and a memoryphi. + 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); + Argument *PointerArg = &*F->arg_begin(); + StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); + BranchInst::Create(Merge, Left); + BranchInst::Create(Merge, Right); + + setupAnalyses(); + MemorySSA &MSSA = Analyses->MSSA; + // Add the load + B.SetInsertPoint(Merge); + LoadInst *LoadInst = B.CreateLoad(PointerArg); + // Should be no phi to start + EXPECT_EQ(MSSA.getMemoryAccess(Merge), nullptr); + + // Create the phi + MemoryPhi *MP = MSSA.createMemoryPhi(Merge); + MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst)); + MP->addIncoming(StoreAccess, Left); + MP->addIncoming(MSSA.getLiveOnEntryDef(), Right); + + // Create the load memory acccess + MemoryUse *LoadAccess = cast<MemoryUse>( + MSSA.createMemoryAccessInBB(LoadInst, MP, Merge, MemorySSA::Beginning)); + MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); + EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); + MSSA.verifyMemorySSA(); +} + +TEST_F(MemorySSATest, RemoveAPhi) { + // We create a diamond where there is a store on one side, and then a load + // after the merge point. This enables us to test a bunch of different + // removal cases. + 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); + Argument *PointerArg = &*F->arg_begin(); + StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); + BranchInst::Create(Merge, Left); + BranchInst::Create(Merge, Right); + B.SetInsertPoint(Merge); + LoadInst *LoadInst = B.CreateLoad(PointerArg); + + setupAnalyses(); + MemorySSA &MSSA = Analyses->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); + MemoryPhi *MP = cast<MemoryPhi>(DefiningAccess); + // Verify the phi ended up as liveonentry, liveonentry + for (auto &Op : MP->incoming_values()) + EXPECT_TRUE(MSSA.isLiveOnEntryDef(cast<MemoryAccess>(Op.get()))); + // Replace the phi uses with the live on entry def + MP->replaceAllUsesWith(MSSA.getLiveOnEntryDef()); + // Verify the load is now defined by liveOnEntryDef + EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess())); + // Remove the PHI + MSSA.removeMemoryAccess(MP); + MSSA.verifyMemorySSA(); +} + +TEST_F(MemorySSATest, RemoveMemoryAccess) { + // We create a diamond where there is a store on one side, and then a load + // after the merge point. This enables us to test a bunch of different + // removal cases. + 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); + Argument *PointerArg = &*F->arg_begin(); + StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); + BranchInst::Create(Merge, Left); + BranchInst::Create(Merge, Right); + B.SetInsertPoint(Merge); + LoadInst *LoadInst = B.CreateLoad(PointerArg); + + setupAnalyses(); + MemorySSA &MSSA = Analyses->MSSA; + MemorySSAWalker *Walker = Analyses->Walker; + + // Before, the load will be a use of a phi<store, liveonentry>. It should be + // the same after. + MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst)); + MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst)); + MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); + EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); + // 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); + MSSA.verifyMemorySSA(); + // After the removeaccess, let's see if we got the right accesses + // The load should still point to the phi ... + EXPECT_EQ(DefiningAccess, LoadAccess->getDefiningAccess()); + // but we should now get live on entry for the clobbering definition of the + // load, since it will walk past the phi node since every argument is the + // same. + EXPECT_TRUE( + MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst))); + + // The phi should now be a two entry phi with two live on entry defs. + for (const auto &Op : DefiningAccess->operands()) { + MemoryAccess *Operand = cast<MemoryAccess>(&*Op); + EXPECT_TRUE(MSSA.isLiveOnEntryDef(Operand)); + } + + // Now we try to remove the single valued phi + MSSA.removeMemoryAccess(DefiningAccess); + MSSA.verifyMemorySSA(); + // Now the load should be a load of live on entry. + EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess())); +} + +// We had a bug with caching where the walker would report MemoryDef#3's clobber +// (below) was MemoryDef#1. +// +// define void @F(i8*) { +// %A = alloca i8, i8 1 +// ; 1 = MemoryDef(liveOnEntry) +// store i8 0, i8* %A +// ; 2 = MemoryDef(1) +// store i8 1, i8* %A +// ; 3 = MemoryDef(2) +// store i8 2, i8* %A +// } +TEST_F(MemorySSATest, TestTripleStore) { + F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), + GlobalValue::ExternalLinkage, "F", &M); + B.SetInsertPoint(BasicBlock::Create(C, "", F)); + Type *Int8 = Type::getInt8Ty(C); + Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); + StoreInst *S1 = B.CreateStore(ConstantInt::get(Int8, 0), Alloca); + StoreInst *S2 = B.CreateStore(ConstantInt::get(Int8, 1), Alloca); + StoreInst *S3 = B.CreateStore(ConstantInt::get(Int8, 2), Alloca); + + setupAnalyses(); + MemorySSA &MSSA = Analyses->MSSA; + MemorySSAWalker *Walker = Analyses->Walker; + + unsigned I = 0; + for (StoreInst *V : {S1, S2, S3}) { + // Everything should be clobbered by its defining access + MemoryAccess *DefiningAccess = + cast<MemoryUseOrDef>(MSSA.getMemoryAccess(V))->getDefiningAccess(); + MemoryAccess *WalkerClobber = Walker->getClobberingMemoryAccess(V); + EXPECT_EQ(DefiningAccess, WalkerClobber) + << "Store " << I << " doesn't have the correct clobbering access"; + // EXPECT_EQ expands such that if we increment I above, it won't get + // incremented except when we try to print the error message. + ++I; + } +} + +// ...And fixing the above bug made it obvious that, when walking, MemorySSA's +// walker was caching the initial node it walked. This was fine (albeit +// mostly redundant) unless the initial node being walked is a clobber for the +// query. In that case, we'd cache that the node clobbered itself. +TEST_F(MemorySSATest, TestStoreAndLoad) { + F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), + GlobalValue::ExternalLinkage, "F", &M); + B.SetInsertPoint(BasicBlock::Create(C, "", F)); + Type *Int8 = Type::getInt8Ty(C); + Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); + Instruction *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca); + Instruction *LI = B.CreateLoad(Alloca); + + setupAnalyses(); + MemorySSA &MSSA = Analyses->MSSA; + MemorySSAWalker *Walker = Analyses->Walker; + + MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LI); + EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SI)); + EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SI))); +} + +// Another bug (related to the above two fixes): It was noted that, given the +// following code: +// ; 1 = MemoryDef(liveOnEntry) +// store i8 0, i8* %1 +// +// ...A query to getClobberingMemoryAccess(MemoryAccess*, MemoryLocation) would +// hand back the store (correctly). A later call to +// getClobberingMemoryAccess(const Instruction*) would also hand back the store +// (incorrectly; it should return liveOnEntry). +// +// This test checks that repeated calls to either function returns what they're +// meant to. +TEST_F(MemorySSATest, TestStoreDoubleQuery) { + F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), + GlobalValue::ExternalLinkage, "F", &M); + B.SetInsertPoint(BasicBlock::Create(C, "", F)); + Type *Int8 = Type::getInt8Ty(C); + Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); + StoreInst *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca); + + setupAnalyses(); + MemorySSA &MSSA = Analyses->MSSA; + MemorySSAWalker *Walker = Analyses->Walker; + + MemoryAccess *StoreAccess = MSSA.getMemoryAccess(SI); + MemoryLocation StoreLoc = MemoryLocation::get(SI); + MemoryAccess *Clobber = + Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc); + MemoryAccess *LiveOnEntry = Walker->getClobberingMemoryAccess(SI); + + EXPECT_EQ(Clobber, StoreAccess); + EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry)); + + // Try again (with entries in the cache already) for good measure... + Clobber = Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc); + LiveOnEntry = Walker->getClobberingMemoryAccess(SI); + EXPECT_EQ(Clobber, StoreAccess); + EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry)); +} diff --git a/unittests/Transforms/Utils/ValueMapperTest.cpp b/unittests/Transforms/Utils/ValueMapperTest.cpp index 9dbe4dbc56de..34b62bb930d9 100644 --- a/unittests/Transforms/Utils/ValueMapperTest.cpp +++ b/unittests/Transforms/Utils/ValueMapperTest.cpp @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/Transforms/Utils/ValueMapper.h" @@ -16,31 +19,117 @@ using namespace llvm; namespace { -TEST(ValueMapperTest, MapMetadataUnresolved) { +TEST(ValueMapperTest, mapMDNode) { + LLVMContext Context; + auto *U = MDTuple::get(Context, None); + + // The node should be unchanged. + ValueToValueMapTy VM; + EXPECT_EQ(U, ValueMapper(VM).mapMDNode(*U)); +} + +TEST(ValueMapperTest, mapMDNodeCycle) { + LLVMContext Context; + MDNode *U0; + MDNode *U1; + { + Metadata *Ops[] = {nullptr}; + auto T = MDTuple::getTemporary(Context, Ops); + Ops[0] = T.get(); + U0 = MDTuple::get(Context, Ops); + T->replaceOperandWith(0, U0); + U1 = MDNode::replaceWithUniqued(std::move(T)); + U0->resolveCycles(); + } + + EXPECT_TRUE(U0->isResolved()); + EXPECT_TRUE(U0->isUniqued()); + EXPECT_TRUE(U1->isResolved()); + EXPECT_TRUE(U1->isUniqued()); + EXPECT_EQ(U1, U0->getOperand(0)); + EXPECT_EQ(U0, U1->getOperand(0)); + + // Cycles shouldn't be duplicated. + { + ValueToValueMapTy VM; + EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0)); + EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1)); + } + + // Check the other order. + { + ValueToValueMapTy VM; + EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1)); + EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0)); + } +} + +TEST(ValueMapperTest, mapMDNodeDuplicatedCycle) { + LLVMContext Context; + auto *PtrTy = Type::getInt8Ty(Context)->getPointerTo(); + std::unique_ptr<GlobalVariable> G0 = llvm::make_unique<GlobalVariable>( + PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G0"); + std::unique_ptr<GlobalVariable> G1 = llvm::make_unique<GlobalVariable>( + PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G1"); + + // Create a cycle that references G0. + MDNode *N0; // !0 = !{!1} + MDNode *N1; // !1 = !{!0, i8* @G0} + { + auto T0 = MDTuple::getTemporary(Context, nullptr); + Metadata *Ops1[] = {T0.get(), ConstantAsMetadata::get(G0.get())}; + N1 = MDTuple::get(Context, Ops1); + T0->replaceOperandWith(0, N1); + N0 = MDNode::replaceWithUniqued(std::move(T0)); + } + + // Resolve N0 and N1. + ASSERT_FALSE(N0->isResolved()); + ASSERT_FALSE(N1->isResolved()); + N0->resolveCycles(); + ASSERT_TRUE(N0->isResolved()); + ASSERT_TRUE(N1->isResolved()); + + // Seed the value map to map G0 to G1 and map the nodes. The output should + // have new nodes that reference G1 (instead of G0). + ValueToValueMapTy VM; + VM[G0.get()] = G1.get(); + MDNode *MappedN0 = ValueMapper(VM).mapMDNode(*N0); + MDNode *MappedN1 = ValueMapper(VM).mapMDNode(*N1); + EXPECT_NE(N0, MappedN0); + EXPECT_NE(N1, MappedN1); + EXPECT_EQ(ConstantAsMetadata::get(G1.get()), MappedN1->getOperand(1)); + + // Check that the output nodes are resolved. + EXPECT_TRUE(MappedN0->isResolved()); + EXPECT_TRUE(MappedN1->isResolved()); +} + +TEST(ValueMapperTest, mapMDNodeUnresolved) { LLVMContext Context; TempMDTuple T = MDTuple::getTemporary(Context, None); ValueToValueMapTy VM; - EXPECT_EQ(T.get(), MapMetadata(T.get(), VM, RF_NoModuleLevelChanges)); + EXPECT_EQ(T.get(), ValueMapper(VM, RF_NoModuleLevelChanges).mapMDNode(*T)); } -TEST(ValueMapperTest, MapMetadataDistinct) { +TEST(ValueMapperTest, mapMDNodeDistinct) { LLVMContext Context; auto *D = MDTuple::getDistinct(Context, None); { // The node should be cloned. ValueToValueMapTy VM; - EXPECT_NE(D, MapMetadata(D, VM, RF_None)); + EXPECT_NE(D, ValueMapper(VM).mapMDNode(*D)); } { // The node should be moved. ValueToValueMapTy VM; - EXPECT_EQ(D, MapMetadata(D, VM, RF_MoveDistinctMDs)); + EXPECT_EQ(D, ValueMapper(VM, RF_MoveDistinctMDs).mapMDNode(*D)); } } -TEST(ValueMapperTest, MapMetadataDistinctOperands) { +TEST(ValueMapperTest, mapMDNodeDistinctOperands) { LLVMContext Context; Metadata *Old = MDTuple::getDistinct(Context, None); auto *D = MDTuple::getDistinct(Context, Old); @@ -51,8 +140,211 @@ TEST(ValueMapperTest, MapMetadataDistinctOperands) { VM.MD()[Old].reset(New); // Make sure operands are updated. - EXPECT_EQ(D, MapMetadata(D, VM, RF_MoveDistinctMDs)); + EXPECT_EQ(D, ValueMapper(VM, RF_MoveDistinctMDs).mapMDNode(*D)); EXPECT_EQ(New, D->getOperand(0)); } +TEST(ValueMapperTest, mapMDNodeSeeded) { + LLVMContext Context; + auto *D = MDTuple::getDistinct(Context, None); + + // The node should be moved. + ValueToValueMapTy VM; + EXPECT_EQ(None, VM.getMappedMD(D)); + + VM.MD().insert(std::make_pair(D, TrackingMDRef(D))); + EXPECT_EQ(D, *VM.getMappedMD(D)); + EXPECT_EQ(D, ValueMapper(VM).mapMDNode(*D)); +} + +TEST(ValueMapperTest, mapMDNodeSeededWithNull) { + LLVMContext Context; + auto *D = MDTuple::getDistinct(Context, None); + + // The node should be moved. + ValueToValueMapTy VM; + EXPECT_EQ(None, VM.getMappedMD(D)); + + VM.MD().insert(std::make_pair(D, TrackingMDRef())); + EXPECT_EQ(nullptr, *VM.getMappedMD(D)); + EXPECT_EQ(nullptr, ValueMapper(VM).mapMDNode(*D)); } + +TEST(ValueMapperTest, mapMetadataNullMapGlobalWithIgnoreMissingLocals) { + LLVMContext C; + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false); + std::unique_ptr<Function> F( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F")); + + ValueToValueMapTy VM; + RemapFlags Flags = RF_IgnoreMissingLocals | RF_NullMapMissingGlobalValues; + EXPECT_EQ(nullptr, ValueMapper(VM, Flags).mapValue(*F)); +} + +TEST(ValueMapperTest, mapMetadataMDString) { + LLVMContext C; + auto *S1 = MDString::get(C, "S1"); + ValueToValueMapTy VM; + + // Make sure S1 maps to itself, but isn't memoized. + EXPECT_EQ(S1, ValueMapper(VM).mapMetadata(*S1)); + EXPECT_EQ(None, VM.getMappedMD(S1)); + + // We still expect VM.MD() to be respected. + auto *S2 = MDString::get(C, "S2"); + VM.MD()[S1].reset(S2); + EXPECT_EQ(S2, ValueMapper(VM).mapMetadata(*S1)); +} + +TEST(ValueMapperTest, mapMetadataGetMappedMD) { + LLVMContext C; + auto *N0 = MDTuple::get(C, None); + auto *N1 = MDTuple::get(C, N0); + + // Make sure hasMD and getMappedMD work correctly. + ValueToValueMapTy VM; + EXPECT_FALSE(VM.hasMD()); + EXPECT_EQ(N0, ValueMapper(VM).mapMetadata(*N0)); + EXPECT_EQ(N1, ValueMapper(VM).mapMetadata(*N1)); + EXPECT_TRUE(VM.hasMD()); + ASSERT_NE(None, VM.getMappedMD(N0)); + ASSERT_NE(None, VM.getMappedMD(N1)); + EXPECT_EQ(N0, *VM.getMappedMD(N0)); + EXPECT_EQ(N1, *VM.getMappedMD(N1)); +} + +TEST(ValueMapperTest, mapMetadataNoModuleLevelChanges) { + LLVMContext C; + auto *N0 = MDTuple::get(C, None); + auto *N1 = MDTuple::get(C, N0); + + // Nothing should be memoized when RF_NoModuleLevelChanges. + ValueToValueMapTy VM; + EXPECT_FALSE(VM.hasMD()); + EXPECT_EQ(N0, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N0)); + EXPECT_EQ(N1, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N1)); + EXPECT_FALSE(VM.hasMD()); + EXPECT_EQ(None, VM.getMappedMD(N0)); + EXPECT_EQ(None, VM.getMappedMD(N1)); +} + +TEST(ValueMapperTest, mapMetadataConstantAsMetadata) { + LLVMContext C; + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false); + std::unique_ptr<Function> F( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F")); + + auto *CAM = ConstantAsMetadata::get(F.get()); + { + // ConstantAsMetadata shouldn't be memoized. + ValueToValueMapTy VM; + EXPECT_EQ(CAM, ValueMapper(VM).mapMetadata(*CAM)); + EXPECT_FALSE(VM.MD().count(CAM)); + EXPECT_EQ(CAM, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM)); + EXPECT_FALSE(VM.MD().count(CAM)); + + // But it should respect a mapping that gets seeded. + auto *N = MDTuple::get(C, None); + VM.MD()[CAM].reset(N); + EXPECT_EQ(N, ValueMapper(VM).mapMetadata(*CAM)); + EXPECT_EQ(N, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM)); + } + + std::unique_ptr<Function> F2( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F2")); + ValueToValueMapTy VM; + VM[F.get()] = F2.get(); + auto *F2MD = ValueMapper(VM).mapMetadata(*CAM); + EXPECT_FALSE(VM.MD().count(CAM)); + EXPECT_TRUE(F2MD); + EXPECT_EQ(F2.get(), cast<ConstantAsMetadata>(F2MD)->getValue()); +} + +#ifdef GTEST_HAS_DEATH_TEST +#ifndef NDEBUG +TEST(ValueMapperTest, mapMetadataLocalAsMetadata) { + LLVMContext C; + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false); + std::unique_ptr<Function> F( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F")); + Argument &A = *F->arg_begin(); + + // mapMetadata doesn't support LocalAsMetadata. The only valid container for + // LocalAsMetadata is a MetadataAsValue instance, so use it directly. + auto *LAM = LocalAsMetadata::get(&A); + ValueToValueMapTy VM; + EXPECT_DEATH(ValueMapper(VM).mapMetadata(*LAM), "Unexpected local metadata"); + EXPECT_DEATH(ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*LAM), + "Unexpected local metadata"); +} +#endif +#endif + +TEST(ValueMapperTest, mapValueLocalAsMetadata) { + LLVMContext C; + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false); + std::unique_ptr<Function> F( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F")); + Argument &A = *F->arg_begin(); + + auto *LAM = LocalAsMetadata::get(&A); + auto *MAV = MetadataAsValue::get(C, LAM); + + // The principled answer to a LocalAsMetadata of an unmapped SSA value would + // be to return nullptr (regardless of RF_IgnoreMissingLocals). + // + // However, algorithms that use RemapInstruction assume that each instruction + // only references SSA values from previous instructions. Arguments of + // such as "metadata i32 %x" don't currently successfully maintain that + // property. To keep RemapInstruction from crashing we need a non-null + // return here, but we also shouldn't reference the unmapped local. Use + // "metadata !{}". + auto *N0 = MDTuple::get(C, None); + auto *N0AV = MetadataAsValue::get(C, N0); + ValueToValueMapTy VM; + EXPECT_EQ(N0AV, ValueMapper(VM).mapValue(*MAV)); + EXPECT_EQ(nullptr, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV)); + EXPECT_FALSE(VM.count(MAV)); + EXPECT_FALSE(VM.count(&A)); + EXPECT_EQ(None, VM.getMappedMD(LAM)); + + VM[MAV] = MAV; + EXPECT_EQ(MAV, ValueMapper(VM).mapValue(*MAV)); + EXPECT_EQ(MAV, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV)); + EXPECT_TRUE(VM.count(MAV)); + EXPECT_FALSE(VM.count(&A)); + + VM[MAV] = &A; + EXPECT_EQ(&A, ValueMapper(VM).mapValue(*MAV)); + EXPECT_EQ(&A, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV)); + EXPECT_TRUE(VM.count(MAV)); + EXPECT_FALSE(VM.count(&A)); +} + +TEST(ValueMapperTest, mapValueLocalAsMetadataToConstant) { + LLVMContext Context; + auto *Int8 = Type::getInt8Ty(Context); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), Int8, false); + std::unique_ptr<Function> F( + Function::Create(FTy, GlobalValue::ExternalLinkage, "F")); + + // Map a local value to a constant. + Argument &A = *F->arg_begin(); + Constant &C = *ConstantInt::get(Int8, 42); + ValueToValueMapTy VM; + VM[&A] = &C; + + // Look up the metadata-as-value wrapper. Don't crash. + auto *MDA = MetadataAsValue::get(Context, ValueAsMetadata::get(&A)); + auto *MDC = MetadataAsValue::get(Context, ValueAsMetadata::get(&C)); + EXPECT_TRUE(isa<LocalAsMetadata>(MDA->getMetadata())); + EXPECT_TRUE(isa<ConstantAsMetadata>(MDC->getMetadata())); + EXPECT_EQ(&C, ValueMapper(VM).mapValue(A)); + EXPECT_EQ(MDC, ValueMapper(VM).mapValue(*MDA)); +} + +} // end namespace |