diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:17:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:17:04 +0000 |
commit | b915e9e0fc85ba6f398b3fab0db6a81a8913af94 (patch) | |
tree | 98b8f811c7aff2547cab8642daf372d6c59502fb /unittests | |
parent | 6421cca32f69ac849537a3cff78c352195e99f1b (diff) | |
download | src-test2-b915e9e0fc85ba6f398b3fab0db6a81a8913af94.tar.gz src-test2-b915e9e0fc85ba6f398b3fab0db6a81a8913af94.zip |
Notes
Diffstat (limited to 'unittests')
129 files changed, 14559 insertions, 3303 deletions
diff --git a/unittests/ADT/APFloatTest.cpp b/unittests/ADT/APFloatTest.cpp index 18734eb72b82..a273005cd1f3 100644 --- a/unittests/ADT/APFloatTest.cpp +++ b/unittests/ADT/APFloatTest.cpp @@ -10,11 +10,13 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" #include <cmath> #include <ostream> #include <string> +#include <tuple> using namespace llvm; @@ -38,20 +40,20 @@ TEST(APFloatTest, isSignaling) { // positive/negative distinction is included only since the getQNaN/getSNaN // API provides the option. APInt payload = APInt::getOneBitSet(4, 2); - EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle, false).isSignaling()); - EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle, true).isSignaling()); - EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle, false, &payload).isSignaling()); - EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle, true, &payload).isSignaling()); - EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle, false).isSignaling()); - EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle, true).isSignaling()); - EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle, false, &payload).isSignaling()); - EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle, true, &payload).isSignaling()); + EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), false).isSignaling()); + EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), true).isSignaling()); + EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), false, &payload).isSignaling()); + EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), true, &payload).isSignaling()); + EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isSignaling()); + EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), true).isSignaling()); + EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), false, &payload).isSignaling()); + EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), true, &payload).isSignaling()); } TEST(APFloatTest, next) { - APFloat test(APFloat::IEEEquad, APFloat::uninitialized); - APFloat expected(APFloat::IEEEquad, APFloat::uninitialized); + APFloat test(APFloat::IEEEquad(), APFloat::uninitialized); + APFloat expected(APFloat::IEEEquad(), APFloat::uninitialized); // 1. Test Special Cases Values. // @@ -69,37 +71,37 @@ TEST(APFloatTest, next) { // 10. -0 // nextUp(+inf) = +inf. - test = APFloat::getInf(APFloat::IEEEquad, false); - expected = APFloat::getInf(APFloat::IEEEquad, false); + test = APFloat::getInf(APFloat::IEEEquad(), false); + expected = APFloat::getInf(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isInfinity()); EXPECT_TRUE(!test.isNegative()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(+inf) = -nextUp(-inf) = -(-getLargest()) = getLargest() - test = APFloat::getInf(APFloat::IEEEquad, false); - expected = APFloat::getLargest(APFloat::IEEEquad, false); + test = APFloat::getInf(APFloat::IEEEquad(), false); + expected = APFloat::getLargest(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(!test.isNegative()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(-inf) = -getLargest() - test = APFloat::getInf(APFloat::IEEEquad, true); - expected = APFloat::getLargest(APFloat::IEEEquad, true); + test = APFloat::getInf(APFloat::IEEEquad(), true); + expected = APFloat::getLargest(APFloat::IEEEquad(), true); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isNegative()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-inf) = -nextUp(+inf) = -(+inf) = -inf. - test = APFloat::getInf(APFloat::IEEEquad, true); - expected = APFloat::getInf(APFloat::IEEEquad, true); + test = APFloat::getInf(APFloat::IEEEquad(), true); + expected = APFloat::getInf(APFloat::IEEEquad(), true); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.isInfinity() && test.isNegative()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(getLargest()) = +inf - test = APFloat::getLargest(APFloat::IEEEquad, false); - expected = APFloat::getInf(APFloat::IEEEquad, false); + test = APFloat::getLargest(APFloat::IEEEquad(), false); + expected = APFloat::getInf(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isInfinity() && !test.isNegative()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); @@ -107,100 +109,100 @@ TEST(APFloatTest, next) { // nextDown(getLargest()) = -nextUp(-getLargest()) // = -(-getLargest() + inc) // = getLargest() - inc. - test = APFloat::getLargest(APFloat::IEEEquad, false); - expected = APFloat(APFloat::IEEEquad, + test = APFloat::getLargest(APFloat::IEEEquad(), false); + expected = APFloat(APFloat::IEEEquad(), "0x1.fffffffffffffffffffffffffffep+16383"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(!test.isInfinity() && !test.isNegative()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(-getLargest()) = -getLargest() + inc. - test = APFloat::getLargest(APFloat::IEEEquad, true); - expected = APFloat(APFloat::IEEEquad, + test = APFloat::getLargest(APFloat::IEEEquad(), true); + expected = APFloat(APFloat::IEEEquad(), "-0x1.fffffffffffffffffffffffffffep+16383"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-getLargest()) = -nextUp(getLargest()) = -(inf) = -inf. - test = APFloat::getLargest(APFloat::IEEEquad, true); - expected = APFloat::getInf(APFloat::IEEEquad, true); + test = APFloat::getLargest(APFloat::IEEEquad(), true); + expected = APFloat::getInf(APFloat::IEEEquad(), true); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.isInfinity() && test.isNegative()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(getSmallest()) = getSmallest() + inc. - test = APFloat(APFloat::IEEEquad, "0x0.0000000000000000000000000001p-16382"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x0.0000000000000000000000000001p-16382"); + expected = APFloat(APFloat::IEEEquad(), "0x0.0000000000000000000000000002p-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(getSmallest()) = -nextUp(-getSmallest()) = -(-0) = +0. - test = APFloat(APFloat::IEEEquad, "0x0.0000000000000000000000000001p-16382"); - expected = APFloat::getZero(APFloat::IEEEquad, false); + test = APFloat(APFloat::IEEEquad(), "0x0.0000000000000000000000000001p-16382"); + expected = APFloat::getZero(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(true), APFloat::opOK); 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); + test = APFloat(APFloat::IEEEquad(), "-0x0.0000000000000000000000000001p-16382"); + expected = APFloat::getZero(APFloat::IEEEquad(), true); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isNegZero()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-getSmallest()) = -nextUp(getSmallest()) = -getSmallest() - inc. - test = APFloat(APFloat::IEEEquad, "-0x0.0000000000000000000000000001p-16382"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x0.0000000000000000000000000001p-16382"); + expected = APFloat(APFloat::IEEEquad(), "-0x0.0000000000000000000000000002p-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(qNaN) = qNaN - test = APFloat::getQNaN(APFloat::IEEEquad, false); - expected = APFloat::getQNaN(APFloat::IEEEquad, false); + test = APFloat::getQNaN(APFloat::IEEEquad(), false); + expected = APFloat::getQNaN(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(qNaN) = qNaN - test = APFloat::getQNaN(APFloat::IEEEquad, false); - expected = APFloat::getQNaN(APFloat::IEEEquad, false); + test = APFloat::getQNaN(APFloat::IEEEquad(), false); + expected = APFloat::getQNaN(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(sNaN) = qNaN - test = APFloat::getSNaN(APFloat::IEEEquad, false); - expected = APFloat::getQNaN(APFloat::IEEEquad, false); + test = APFloat::getSNaN(APFloat::IEEEquad(), false); + expected = APFloat::getQNaN(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(false), APFloat::opInvalidOp); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(sNaN) = qNaN - test = APFloat::getSNaN(APFloat::IEEEquad, false); - expected = APFloat::getQNaN(APFloat::IEEEquad, false); + test = APFloat::getSNaN(APFloat::IEEEquad(), false); + expected = APFloat::getQNaN(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(true), APFloat::opInvalidOp); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(+0) = +getSmallest() - test = APFloat::getZero(APFloat::IEEEquad, false); - expected = APFloat::getSmallest(APFloat::IEEEquad, false); + test = APFloat::getZero(APFloat::IEEEquad(), false); + expected = APFloat::getSmallest(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(+0) = -nextUp(-0) = -getSmallest() - test = APFloat::getZero(APFloat::IEEEquad, false); - expected = APFloat::getSmallest(APFloat::IEEEquad, true); + test = APFloat::getZero(APFloat::IEEEquad(), false); + expected = APFloat::getSmallest(APFloat::IEEEquad(), true); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(-0) = +getSmallest() - test = APFloat::getZero(APFloat::IEEEquad, true); - expected = APFloat::getSmallest(APFloat::IEEEquad, false); + test = APFloat::getZero(APFloat::IEEEquad(), true); + expected = APFloat::getSmallest(APFloat::IEEEquad(), false); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-0) = -nextUp(0) = -getSmallest() - test = APFloat::getZero(APFloat::IEEEquad, true); - expected = APFloat::getSmallest(APFloat::IEEEquad, true); + test = APFloat::getZero(APFloat::IEEEquad(), true); + expected = APFloat::getSmallest(APFloat::IEEEquad(), true); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); @@ -213,35 +215,35 @@ TEST(APFloatTest, next) { // * nextDown(+Smallest Normal) -> +Largest Denormal. // nextUp(+Largest Denormal) -> +Smallest Normal. - test = APFloat(APFloat::IEEEquad, "0x0.ffffffffffffffffffffffffffffp-16382"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffffffffp-16382"); + expected = APFloat(APFloat::IEEEquad(), "0x1.0000000000000000000000000000p-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_FALSE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-Largest Denormal) -> -Smallest Normal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffffffffp-16382"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "-0x1.0000000000000000000000000000p-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_FALSE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(-Smallest Normal) -> -LargestDenormal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x1.0000000000000000000000000000p-16382"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffffffffp-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(+Smallest Normal) -> +Largest Denormal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "+0x1.0000000000000000000000000000p-16382"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "+0x0.ffffffffffffffffffffffffffffp-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); @@ -254,27 +256,27 @@ TEST(APFloatTest, next) { // * nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. // nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. - test = APFloat(APFloat::IEEEquad, "-0x1p+1"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x1p+1"); + expected = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffffffffp+0"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. - test = APFloat(APFloat::IEEEquad, "0x1p+1"); - expected = APFloat(APFloat::IEEEquad, "0x1.ffffffffffffffffffffffffffffp+0"); + test = APFloat(APFloat::IEEEquad(), "0x1p+1"); + expected = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffffffffp+0"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. - test = APFloat(APFloat::IEEEquad, "0x1.ffffffffffffffffffffffffffffp+0"); - expected = APFloat(APFloat::IEEEquad, "0x1p+1"); + test = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffffffffp+0"); + expected = APFloat(APFloat::IEEEquad(), "0x1p+1"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. - test = APFloat(APFloat::IEEEquad, "-0x1.ffffffffffffffffffffffffffffp+0"); - expected = APFloat(APFloat::IEEEquad, "-0x1p+1"); + test = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffffffffp+0"); + expected = APFloat(APFloat::IEEEquad(), "-0x1p+1"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); @@ -290,8 +292,8 @@ TEST(APFloatTest, next) { // * nextDown(-Smallest Normal) -> -Smallest Normal - inc. // nextUp(-Largest Denormal) -> -Largest Denormal + inc. - test = APFloat(APFloat::IEEEquad, "-0x0.ffffffffffffffffffffffffffffp-16382"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffffffffp-16382"); + expected = APFloat(APFloat::IEEEquad(), "-0x0.fffffffffffffffffffffffffffep-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); @@ -299,8 +301,8 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(+Largest Denormal) -> +Largest Denormal - inc. - test = APFloat(APFloat::IEEEquad, "0x0.ffffffffffffffffffffffffffffp-16382"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffffffffp-16382"); + expected = APFloat(APFloat::IEEEquad(), "0x0.fffffffffffffffffffffffffffep-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); @@ -308,8 +310,8 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(+Smallest Normal) -> +Smallest Normal + inc. - test = APFloat(APFloat::IEEEquad, "0x1.0000000000000000000000000000p-16382"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x1.0000000000000000000000000000p-16382"); + expected = APFloat(APFloat::IEEEquad(), "0x1.0000000000000000000000000001p-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(!test.isDenormal()); @@ -317,8 +319,8 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-Smallest Normal) -> -Smallest Normal - inc. - test = APFloat(APFloat::IEEEquad, "-0x1.0000000000000000000000000000p-16382"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x1.0000000000000000000000000000p-16382"); + expected = APFloat(APFloat::IEEEquad(), "-0x1.0000000000000000000000000001p-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(!test.isDenormal()); @@ -335,28 +337,28 @@ TEST(APFloatTest, next) { // * nextDown(0x1p-16382) -> 0x1.ffffffffffffffffffffffffffffp-16382 // nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 - test = APFloat(APFloat::IEEEquad, "-0x1p-16381"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x1p-16381"); + expected = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffffffffp-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> // -0x1p-16381 - test = APFloat(APFloat::IEEEquad, "-0x1.ffffffffffffffffffffffffffffp-16382"); - expected = APFloat(APFloat::IEEEquad, "-0x1p-16381"); + test = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffffffffp-16382"); + expected = APFloat(APFloat::IEEEquad(), "-0x1p-16381"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16381 - test = APFloat(APFloat::IEEEquad, "0x1.ffffffffffffffffffffffffffffp-16382"); - expected = APFloat(APFloat::IEEEquad, "0x1p-16381"); + test = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffffffffp-16382"); + expected = APFloat(APFloat::IEEEquad(), "0x1p-16381"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(0x1p-16381) -> 0x1.ffffffffffffffffffffffffffffp-16382 - test = APFloat(APFloat::IEEEquad, "0x1p-16381"); - expected = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x1p-16381"); + expected = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffffffffp-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.bitwiseIsEqual(expected)); @@ -373,9 +375,9 @@ TEST(APFloatTest, next) { // * nextDown(-Normal) -> -Normal. // nextUp(+Denormal) -> +Denormal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffff000cp-16382"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffff000dp-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); @@ -383,9 +385,9 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(+Denormal) -> +Denormal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffff000cp-16382"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffff000bp-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); @@ -393,9 +395,9 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(-Denormal) -> -Denormal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffff000cp-16382"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffff000bp-16382"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); @@ -403,9 +405,9 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-Denormal) -> -Denormal - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffff000cp-16382"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffff000dp-16382"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(test.isDenormal()); @@ -413,9 +415,9 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(+Normal) -> +Normal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffff000cp-16000"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffff000dp-16000"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(!test.isDenormal()); @@ -423,9 +425,9 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(+Normal) -> +Normal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffff000cp-16000"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffff000bp-16000"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(!test.isDenormal()); @@ -433,9 +435,9 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextUp(-Normal) -> -Normal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffff000cp-16000"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffff000bp-16000"); EXPECT_EQ(test.next(false), APFloat::opOK); EXPECT_TRUE(!test.isDenormal()); @@ -443,9 +445,9 @@ TEST(APFloatTest, next) { EXPECT_TRUE(test.bitwiseIsEqual(expected)); // nextDown(-Normal) -> -Normal. - test = APFloat(APFloat::IEEEquad, + test = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffff000cp-16000"); - expected = APFloat(APFloat::IEEEquad, + expected = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffff000dp-16000"); EXPECT_EQ(test.next(true), APFloat::opOK); EXPECT_TRUE(!test.isDenormal()); @@ -508,8 +510,8 @@ TEST(APFloatTest, FMA) { // Test -ve sign preservation when small negative results underflow. { - APFloat f1(APFloat::IEEEdouble, "-0x1p-1074"); - APFloat f2(APFloat::IEEEdouble, "+0x1p-1074"); + APFloat f1(APFloat::IEEEdouble(), "-0x1p-1074"); + APFloat f2(APFloat::IEEEdouble(), "+0x1p-1074"); APFloat f3(0.0); f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven); EXPECT_TRUE(f1.isNegative() && f1.isZero()); @@ -517,13 +519,13 @@ TEST(APFloatTest, FMA) { // Test x87 extended precision case from http://llvm.org/PR20728. { - APFloat M1(APFloat::x87DoubleExtended, 1.0); - APFloat M2(APFloat::x87DoubleExtended, 1.0); - APFloat A(APFloat::x87DoubleExtended, 3.0); + APFloat M1(APFloat::x87DoubleExtended(), 1.0); + APFloat M2(APFloat::x87DoubleExtended(), 1.0); + APFloat A(APFloat::x87DoubleExtended(), 3.0); bool losesInfo = false; M1.fusedMultiplyAdd(M1, A, APFloat::rmNearestTiesToEven); - M1.convert(APFloat::IEEEsingle, APFloat::rmNearestTiesToEven, &losesInfo); + M1.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_FALSE(losesInfo); EXPECT_EQ(4.0f, M1.convertToFloat()); } @@ -532,7 +534,7 @@ TEST(APFloatTest, FMA) { TEST(APFloatTest, MinNum) { APFloat f1(1.0); APFloat f2(2.0); - APFloat nan = APFloat::getNaN(APFloat::IEEEdouble); + APFloat nan = APFloat::getNaN(APFloat::IEEEdouble()); EXPECT_EQ(1.0, minnum(f1, f2).convertToDouble()); EXPECT_EQ(1.0, minnum(f2, f1).convertToDouble()); @@ -543,7 +545,7 @@ TEST(APFloatTest, MinNum) { TEST(APFloatTest, MaxNum) { APFloat f1(1.0); APFloat f2(2.0); - APFloat nan = APFloat::getNaN(APFloat::IEEEdouble); + APFloat nan = APFloat::getNaN(APFloat::IEEEdouble()); EXPECT_EQ(2.0, maxnum(f1, f2).convertToDouble()); EXPECT_EQ(2.0, maxnum(f2, f1).convertToDouble()); @@ -557,11 +559,11 @@ TEST(APFloatTest, Denormal) { // Test single precision { const char *MinNormalStr = "1.17549435082228750797e-38"; - EXPECT_FALSE(APFloat(APFloat::IEEEsingle, MinNormalStr).isDenormal()); - EXPECT_FALSE(APFloat(APFloat::IEEEsingle, 0.0).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), MinNormalStr).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), 0.0).isDenormal()); - APFloat Val2(APFloat::IEEEsingle, 2.0e0); - APFloat T(APFloat::IEEEsingle, MinNormalStr); + APFloat Val2(APFloat::IEEEsingle(), 2.0e0); + APFloat T(APFloat::IEEEsingle(), MinNormalStr); T.divide(Val2, rdmd); EXPECT_TRUE(T.isDenormal()); } @@ -569,11 +571,11 @@ TEST(APFloatTest, Denormal) { // Test double precision { const char *MinNormalStr = "2.22507385850720138309e-308"; - EXPECT_FALSE(APFloat(APFloat::IEEEdouble, MinNormalStr).isDenormal()); - EXPECT_FALSE(APFloat(APFloat::IEEEdouble, 0.0).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::IEEEdouble(), MinNormalStr).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::IEEEdouble(), 0.0).isDenormal()); - APFloat Val2(APFloat::IEEEdouble, 2.0e0); - APFloat T(APFloat::IEEEdouble, MinNormalStr); + APFloat Val2(APFloat::IEEEdouble(), 2.0e0); + APFloat T(APFloat::IEEEdouble(), MinNormalStr); T.divide(Val2, rdmd); EXPECT_TRUE(T.isDenormal()); } @@ -581,11 +583,11 @@ TEST(APFloatTest, Denormal) { // Test Intel double-ext { const char *MinNormalStr = "3.36210314311209350626e-4932"; - EXPECT_FALSE(APFloat(APFloat::x87DoubleExtended, MinNormalStr).isDenormal()); - EXPECT_FALSE(APFloat(APFloat::x87DoubleExtended, 0.0).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::x87DoubleExtended(), MinNormalStr).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::x87DoubleExtended(), 0.0).isDenormal()); - APFloat Val2(APFloat::x87DoubleExtended, 2.0e0); - APFloat T(APFloat::x87DoubleExtended, MinNormalStr); + APFloat Val2(APFloat::x87DoubleExtended(), 2.0e0); + APFloat T(APFloat::x87DoubleExtended(), MinNormalStr); T.divide(Val2, rdmd); EXPECT_TRUE(T.isDenormal()); } @@ -593,11 +595,11 @@ TEST(APFloatTest, Denormal) { // Test quadruple precision { const char *MinNormalStr = "3.36210314311209350626267781732175260e-4932"; - EXPECT_FALSE(APFloat(APFloat::IEEEquad, MinNormalStr).isDenormal()); - EXPECT_FALSE(APFloat(APFloat::IEEEquad, 0.0).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::IEEEquad(), MinNormalStr).isDenormal()); + EXPECT_FALSE(APFloat(APFloat::IEEEquad(), 0.0).isDenormal()); - APFloat Val2(APFloat::IEEEquad, 2.0e0); - APFloat T(APFloat::IEEEquad, MinNormalStr); + APFloat Val2(APFloat::IEEEquad(), 2.0e0); + APFloat T(APFloat::IEEEquad(), MinNormalStr); T.divide(Val2, rdmd); EXPECT_TRUE(T.isDenormal()); } @@ -616,7 +618,7 @@ TEST(APFloatTest, Zero) { TEST(APFloatTest, DecimalStringsWithoutNullTerminators) { // Make sure that we can parse strings without null terminators. // rdar://14323230. - APFloat Val(APFloat::IEEEdouble); + APFloat Val(APFloat::IEEEdouble()); Val.convertFromString(StringRef("0.00", 3), llvm::APFloat::rmNearestTiesToEven); EXPECT_EQ(Val.convertToDouble(), 0.0); @@ -639,292 +641,292 @@ TEST(APFloatTest, DecimalStringsWithoutNullTerminators) { } TEST(APFloatTest, fromZeroDecimalString) { - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, ".0").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+.0").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-.0").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.0").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.0").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.0").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "00000.").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+00000.").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-00000.").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "00000.").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+00000.").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-00000.").convertToDouble()); - EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble, ".00000").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+.00000").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-.00000").convertToDouble()); + EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), ".00000").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.00000").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.00000").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0000.00000").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0000.00000").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0000.00000").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0000.00000").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0000.00000").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0000.00000").convertToDouble()); } TEST(APFloatTest, fromZeroDecimalSingleExponentString) { - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0e1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0e1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0e1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0e+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0e+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0e+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0e-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0e-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0e-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.e1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.e1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.e1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.e1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.e1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.e1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.e+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.e+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.e+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.e+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.e+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.e+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.e-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.e-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.e-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.e-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.e-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.e-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, ".0e1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+.0e1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-.0e1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0e1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0e1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0e1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, ".0e+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+.0e+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-.0e+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0e+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0e+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0e+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, ".0e-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+.0e-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-.0e-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0e-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0e-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0e-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.0e1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.0e1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.0e1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0e1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0e1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0e1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.0e+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.0e+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.0e+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0e+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0e+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0e+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0.0e-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0.0e-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0.0e-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0e-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0e-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0e-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "000.0000e1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+000.0000e+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-000.0000e+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "000.0000e1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+000.0000e+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-000.0000e+1").convertToDouble()); } TEST(APFloatTest, fromZeroDecimalLargeExponentString) { - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0e1234").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0e1234").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0e1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e1234").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e1234").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e1234").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0e+1234").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0e+1234").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0e+1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e+1234").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e+1234").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e+1234").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0e-1234").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0e-1234").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0e-1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e-1234").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e-1234").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e-1234").convertToDouble()); - EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble, "000.0000e1234").convertToDouble()); - EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble, "000.0000e-1234").convertToDouble()); + EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), "000.0000e1234").convertToDouble()); + EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), "000.0000e-1234").convertToDouble()); - EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble, StringRef("0e1234\02", 6)).convertToDouble()); + EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), StringRef("0e1234\02", 6)).convertToDouble()); } TEST(APFloatTest, fromZeroHexadecimalString) { - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0p1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0p1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0p1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0p+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0p+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0p+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0p+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0p-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0p-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0p-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0p-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.p1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0.p1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0.p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.p1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.p+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0.p+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0.p+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.p+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.p+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.p-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0.p-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0.p-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.p-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.p-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x.0p1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x.0p1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x.0p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.0p1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x.0p1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x.0p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x.0p+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x.0p+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x.0p+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.0p+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x.0p+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x.0p+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x.0p-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x.0p-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x.0p-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.0p-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x.0p-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x.0p-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.0p1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0.0p1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0.0p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.0p1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.0p1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.0p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.0p+1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0.0p+1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0.0p+1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.0p+1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.0p+1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.0p+1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.0p-1").convertToDouble()); - EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0x0.0p-1").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0.0p-1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.0p-1").convertToDouble()); + EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.0p-1").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.0p-1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x00000.p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0000.00000p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x.00000p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.p1").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0p1234").convertToDouble()); - EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble, "-0x0p1234").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x00000.p1234").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0000.00000p1234").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x.00000p1234").convertToDouble()); - EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0x0.p1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x00000.p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0000.00000p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.00000p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p1").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p1234").convertToDouble()); + EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x00000.p1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0000.00000p1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.00000p1234").convertToDouble()); + EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p1234").convertToDouble()); } TEST(APFloatTest, fromDecimalString) { - EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble, "1").convertToDouble()); - EXPECT_EQ(2.0, APFloat(APFloat::IEEEdouble, "2.").convertToDouble()); - EXPECT_EQ(0.5, APFloat(APFloat::IEEEdouble, ".5").convertToDouble()); - EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble, "1.0").convertToDouble()); - EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble, "-2").convertToDouble()); - EXPECT_EQ(-4.0, APFloat(APFloat::IEEEdouble, "-4.").convertToDouble()); - EXPECT_EQ(-0.5, APFloat(APFloat::IEEEdouble, "-.5").convertToDouble()); - EXPECT_EQ(-1.5, APFloat(APFloat::IEEEdouble, "-1.5").convertToDouble()); - EXPECT_EQ(1.25e12, APFloat(APFloat::IEEEdouble, "1.25e12").convertToDouble()); - EXPECT_EQ(1.25e+12, APFloat(APFloat::IEEEdouble, "1.25e+12").convertToDouble()); - EXPECT_EQ(1.25e-12, APFloat(APFloat::IEEEdouble, "1.25e-12").convertToDouble()); - EXPECT_EQ(1024.0, APFloat(APFloat::IEEEdouble, "1024.").convertToDouble()); - EXPECT_EQ(1024.05, APFloat(APFloat::IEEEdouble, "1024.05000").convertToDouble()); - EXPECT_EQ(0.05, APFloat(APFloat::IEEEdouble, ".05000").convertToDouble()); - EXPECT_EQ(2.0, APFloat(APFloat::IEEEdouble, "2.").convertToDouble()); - EXPECT_EQ(2.0e2, APFloat(APFloat::IEEEdouble, "2.e2").convertToDouble()); - EXPECT_EQ(2.0e+2, APFloat(APFloat::IEEEdouble, "2.e+2").convertToDouble()); - EXPECT_EQ(2.0e-2, APFloat(APFloat::IEEEdouble, "2.e-2").convertToDouble()); - EXPECT_EQ(2.05e2, APFloat(APFloat::IEEEdouble, "002.05000e2").convertToDouble()); - EXPECT_EQ(2.05e+2, APFloat(APFloat::IEEEdouble, "002.05000e+2").convertToDouble()); - EXPECT_EQ(2.05e-2, APFloat(APFloat::IEEEdouble, "002.05000e-2").convertToDouble()); - EXPECT_EQ(2.05e12, APFloat(APFloat::IEEEdouble, "002.05000e12").convertToDouble()); - EXPECT_EQ(2.05e+12, APFloat(APFloat::IEEEdouble, "002.05000e+12").convertToDouble()); - EXPECT_EQ(2.05e-12, APFloat(APFloat::IEEEdouble, "002.05000e-12").convertToDouble()); + EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1").convertToDouble()); + EXPECT_EQ(2.0, APFloat(APFloat::IEEEdouble(), "2.").convertToDouble()); + EXPECT_EQ(0.5, APFloat(APFloat::IEEEdouble(), ".5").convertToDouble()); + EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1.0").convertToDouble()); + EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble(), "-2").convertToDouble()); + EXPECT_EQ(-4.0, APFloat(APFloat::IEEEdouble(), "-4.").convertToDouble()); + EXPECT_EQ(-0.5, APFloat(APFloat::IEEEdouble(), "-.5").convertToDouble()); + EXPECT_EQ(-1.5, APFloat(APFloat::IEEEdouble(), "-1.5").convertToDouble()); + EXPECT_EQ(1.25e12, APFloat(APFloat::IEEEdouble(), "1.25e12").convertToDouble()); + EXPECT_EQ(1.25e+12, APFloat(APFloat::IEEEdouble(), "1.25e+12").convertToDouble()); + EXPECT_EQ(1.25e-12, APFloat(APFloat::IEEEdouble(), "1.25e-12").convertToDouble()); + EXPECT_EQ(1024.0, APFloat(APFloat::IEEEdouble(), "1024.").convertToDouble()); + EXPECT_EQ(1024.05, APFloat(APFloat::IEEEdouble(), "1024.05000").convertToDouble()); + EXPECT_EQ(0.05, APFloat(APFloat::IEEEdouble(), ".05000").convertToDouble()); + EXPECT_EQ(2.0, APFloat(APFloat::IEEEdouble(), "2.").convertToDouble()); + EXPECT_EQ(2.0e2, APFloat(APFloat::IEEEdouble(), "2.e2").convertToDouble()); + EXPECT_EQ(2.0e+2, APFloat(APFloat::IEEEdouble(), "2.e+2").convertToDouble()); + EXPECT_EQ(2.0e-2, APFloat(APFloat::IEEEdouble(), "2.e-2").convertToDouble()); + EXPECT_EQ(2.05e2, APFloat(APFloat::IEEEdouble(), "002.05000e2").convertToDouble()); + EXPECT_EQ(2.05e+2, APFloat(APFloat::IEEEdouble(), "002.05000e+2").convertToDouble()); + EXPECT_EQ(2.05e-2, APFloat(APFloat::IEEEdouble(), "002.05000e-2").convertToDouble()); + EXPECT_EQ(2.05e12, APFloat(APFloat::IEEEdouble(), "002.05000e12").convertToDouble()); + EXPECT_EQ(2.05e+12, APFloat(APFloat::IEEEdouble(), "002.05000e+12").convertToDouble()); + EXPECT_EQ(2.05e-12, APFloat(APFloat::IEEEdouble(), "002.05000e-12").convertToDouble()); // These are "carefully selected" to overflow the fast log-base // calculations in APFloat.cpp - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "99e99999").isInfinity()); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-99e99999").isInfinity()); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "1e-99999").isPosZero()); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-1e-99999").isNegZero()); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "99e99999").isInfinity()); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-99e99999").isInfinity()); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "1e-99999").isPosZero()); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-1e-99999").isNegZero()); EXPECT_EQ(2.71828, convertToDoubleFromString("2.71828")); } TEST(APFloatTest, fromHexadecimalString) { - EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble, "0x1p0").convertToDouble()); - EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble, "+0x1p0").convertToDouble()); - EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble, "-0x1p0").convertToDouble()); + EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble(), "0x1p0").convertToDouble()); + EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble(), "+0x1p0").convertToDouble()); + EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-0x1p0").convertToDouble()); - EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble, "0x1p+0").convertToDouble()); - EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble, "+0x1p+0").convertToDouble()); - EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble, "-0x1p+0").convertToDouble()); + EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble(), "0x1p+0").convertToDouble()); + EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble(), "+0x1p+0").convertToDouble()); + EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-0x1p+0").convertToDouble()); - EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble, "0x1p-0").convertToDouble()); - EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble, "+0x1p-0").convertToDouble()); - EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble, "-0x1p-0").convertToDouble()); + EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble(), "0x1p-0").convertToDouble()); + EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble(), "+0x1p-0").convertToDouble()); + EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-0x1p-0").convertToDouble()); - EXPECT_EQ( 2.0, APFloat(APFloat::IEEEdouble, "0x1p1").convertToDouble()); - EXPECT_EQ(+2.0, APFloat(APFloat::IEEEdouble, "+0x1p1").convertToDouble()); - EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble, "-0x1p1").convertToDouble()); + EXPECT_EQ( 2.0, APFloat(APFloat::IEEEdouble(), "0x1p1").convertToDouble()); + EXPECT_EQ(+2.0, APFloat(APFloat::IEEEdouble(), "+0x1p1").convertToDouble()); + EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble(), "-0x1p1").convertToDouble()); - EXPECT_EQ( 2.0, APFloat(APFloat::IEEEdouble, "0x1p+1").convertToDouble()); - EXPECT_EQ(+2.0, APFloat(APFloat::IEEEdouble, "+0x1p+1").convertToDouble()); - EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble, "-0x1p+1").convertToDouble()); + EXPECT_EQ( 2.0, APFloat(APFloat::IEEEdouble(), "0x1p+1").convertToDouble()); + EXPECT_EQ(+2.0, APFloat(APFloat::IEEEdouble(), "+0x1p+1").convertToDouble()); + EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble(), "-0x1p+1").convertToDouble()); - EXPECT_EQ( 0.5, APFloat(APFloat::IEEEdouble, "0x1p-1").convertToDouble()); - EXPECT_EQ(+0.5, APFloat(APFloat::IEEEdouble, "+0x1p-1").convertToDouble()); - EXPECT_EQ(-0.5, APFloat(APFloat::IEEEdouble, "-0x1p-1").convertToDouble()); + EXPECT_EQ( 0.5, APFloat(APFloat::IEEEdouble(), "0x1p-1").convertToDouble()); + EXPECT_EQ(+0.5, APFloat(APFloat::IEEEdouble(), "+0x1p-1").convertToDouble()); + EXPECT_EQ(-0.5, APFloat(APFloat::IEEEdouble(), "-0x1p-1").convertToDouble()); - EXPECT_EQ( 3.0, APFloat(APFloat::IEEEdouble, "0x1.8p1").convertToDouble()); - EXPECT_EQ(+3.0, APFloat(APFloat::IEEEdouble, "+0x1.8p1").convertToDouble()); - EXPECT_EQ(-3.0, APFloat(APFloat::IEEEdouble, "-0x1.8p1").convertToDouble()); + EXPECT_EQ( 3.0, APFloat(APFloat::IEEEdouble(), "0x1.8p1").convertToDouble()); + EXPECT_EQ(+3.0, APFloat(APFloat::IEEEdouble(), "+0x1.8p1").convertToDouble()); + EXPECT_EQ(-3.0, APFloat(APFloat::IEEEdouble(), "-0x1.8p1").convertToDouble()); - EXPECT_EQ( 3.0, APFloat(APFloat::IEEEdouble, "0x1.8p+1").convertToDouble()); - EXPECT_EQ(+3.0, APFloat(APFloat::IEEEdouble, "+0x1.8p+1").convertToDouble()); - EXPECT_EQ(-3.0, APFloat(APFloat::IEEEdouble, "-0x1.8p+1").convertToDouble()); + EXPECT_EQ( 3.0, APFloat(APFloat::IEEEdouble(), "0x1.8p+1").convertToDouble()); + EXPECT_EQ(+3.0, APFloat(APFloat::IEEEdouble(), "+0x1.8p+1").convertToDouble()); + EXPECT_EQ(-3.0, APFloat(APFloat::IEEEdouble(), "-0x1.8p+1").convertToDouble()); - EXPECT_EQ( 0.75, APFloat(APFloat::IEEEdouble, "0x1.8p-1").convertToDouble()); - EXPECT_EQ(+0.75, APFloat(APFloat::IEEEdouble, "+0x1.8p-1").convertToDouble()); - EXPECT_EQ(-0.75, APFloat(APFloat::IEEEdouble, "-0x1.8p-1").convertToDouble()); + EXPECT_EQ( 0.75, APFloat(APFloat::IEEEdouble(), "0x1.8p-1").convertToDouble()); + EXPECT_EQ(+0.75, APFloat(APFloat::IEEEdouble(), "+0x1.8p-1").convertToDouble()); + EXPECT_EQ(-0.75, APFloat(APFloat::IEEEdouble(), "-0x1.8p-1").convertToDouble()); - EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble, "0x1000.000p1").convertToDouble()); - EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble, "+0x1000.000p1").convertToDouble()); - EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble, "-0x1000.000p1").convertToDouble()); + EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000.000p1").convertToDouble()); + EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000.000p1").convertToDouble()); + EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000.000p1").convertToDouble()); - EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble, "0x1000.000p+1").convertToDouble()); - EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble, "+0x1000.000p+1").convertToDouble()); - EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble, "-0x1000.000p+1").convertToDouble()); + EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000.000p+1").convertToDouble()); + EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000.000p+1").convertToDouble()); + EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000.000p+1").convertToDouble()); - EXPECT_EQ( 2048.0, APFloat(APFloat::IEEEdouble, "0x1000.000p-1").convertToDouble()); - EXPECT_EQ(+2048.0, APFloat(APFloat::IEEEdouble, "+0x1000.000p-1").convertToDouble()); - EXPECT_EQ(-2048.0, APFloat(APFloat::IEEEdouble, "-0x1000.000p-1").convertToDouble()); + EXPECT_EQ( 2048.0, APFloat(APFloat::IEEEdouble(), "0x1000.000p-1").convertToDouble()); + EXPECT_EQ(+2048.0, APFloat(APFloat::IEEEdouble(), "+0x1000.000p-1").convertToDouble()); + EXPECT_EQ(-2048.0, APFloat(APFloat::IEEEdouble(), "-0x1000.000p-1").convertToDouble()); - EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble, "0x1000p1").convertToDouble()); - EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble, "+0x1000p1").convertToDouble()); - EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble, "-0x1000p1").convertToDouble()); + EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000p1").convertToDouble()); + EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000p1").convertToDouble()); + EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000p1").convertToDouble()); - EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble, "0x1000p+1").convertToDouble()); - EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble, "+0x1000p+1").convertToDouble()); - EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble, "-0x1000p+1").convertToDouble()); + EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000p+1").convertToDouble()); + EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000p+1").convertToDouble()); + EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000p+1").convertToDouble()); - EXPECT_EQ( 2048.0, APFloat(APFloat::IEEEdouble, "0x1000p-1").convertToDouble()); - EXPECT_EQ(+2048.0, APFloat(APFloat::IEEEdouble, "+0x1000p-1").convertToDouble()); - EXPECT_EQ(-2048.0, APFloat(APFloat::IEEEdouble, "-0x1000p-1").convertToDouble()); + EXPECT_EQ( 2048.0, APFloat(APFloat::IEEEdouble(), "0x1000p-1").convertToDouble()); + EXPECT_EQ(+2048.0, APFloat(APFloat::IEEEdouble(), "+0x1000p-1").convertToDouble()); + EXPECT_EQ(-2048.0, APFloat(APFloat::IEEEdouble(), "-0x1000p-1").convertToDouble()); - EXPECT_EQ( 16384.0, APFloat(APFloat::IEEEdouble, "0x10p10").convertToDouble()); - EXPECT_EQ(+16384.0, APFloat(APFloat::IEEEdouble, "+0x10p10").convertToDouble()); - EXPECT_EQ(-16384.0, APFloat(APFloat::IEEEdouble, "-0x10p10").convertToDouble()); + EXPECT_EQ( 16384.0, APFloat(APFloat::IEEEdouble(), "0x10p10").convertToDouble()); + EXPECT_EQ(+16384.0, APFloat(APFloat::IEEEdouble(), "+0x10p10").convertToDouble()); + EXPECT_EQ(-16384.0, APFloat(APFloat::IEEEdouble(), "-0x10p10").convertToDouble()); - EXPECT_EQ( 16384.0, APFloat(APFloat::IEEEdouble, "0x10p+10").convertToDouble()); - EXPECT_EQ(+16384.0, APFloat(APFloat::IEEEdouble, "+0x10p+10").convertToDouble()); - EXPECT_EQ(-16384.0, APFloat(APFloat::IEEEdouble, "-0x10p+10").convertToDouble()); + EXPECT_EQ( 16384.0, APFloat(APFloat::IEEEdouble(), "0x10p+10").convertToDouble()); + EXPECT_EQ(+16384.0, APFloat(APFloat::IEEEdouble(), "+0x10p+10").convertToDouble()); + EXPECT_EQ(-16384.0, APFloat(APFloat::IEEEdouble(), "-0x10p+10").convertToDouble()); - EXPECT_EQ( 0.015625, APFloat(APFloat::IEEEdouble, "0x10p-10").convertToDouble()); - EXPECT_EQ(+0.015625, APFloat(APFloat::IEEEdouble, "+0x10p-10").convertToDouble()); - EXPECT_EQ(-0.015625, APFloat(APFloat::IEEEdouble, "-0x10p-10").convertToDouble()); + EXPECT_EQ( 0.015625, APFloat(APFloat::IEEEdouble(), "0x10p-10").convertToDouble()); + EXPECT_EQ(+0.015625, APFloat(APFloat::IEEEdouble(), "+0x10p-10").convertToDouble()); + EXPECT_EQ(-0.015625, APFloat(APFloat::IEEEdouble(), "-0x10p-10").convertToDouble()); - EXPECT_EQ(1.0625, APFloat(APFloat::IEEEdouble, "0x1.1p0").convertToDouble()); - EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble, "0x1p0").convertToDouble()); + EXPECT_EQ(1.0625, APFloat(APFloat::IEEEdouble(), "0x1.1p0").convertToDouble()); + EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "0x1p0").convertToDouble()); EXPECT_EQ(convertToDoubleFromString("0x1p-150"), convertToDoubleFromString("+0x800000000000000001.p-221")); @@ -953,44 +955,44 @@ TEST(APFloatTest, toInteger) { APSInt result(5, /*isUnsigned=*/true); EXPECT_EQ(APFloat::opOK, - APFloat(APFloat::IEEEdouble, "10") + APFloat(APFloat::IEEEdouble(), "10") .convertToInteger(result, APFloat::rmTowardZero, &isExact)); EXPECT_TRUE(isExact); EXPECT_EQ(APSInt(APInt(5, 10), true), result); EXPECT_EQ(APFloat::opInvalidOp, - APFloat(APFloat::IEEEdouble, "-10") + APFloat(APFloat::IEEEdouble(), "-10") .convertToInteger(result, APFloat::rmTowardZero, &isExact)); EXPECT_FALSE(isExact); EXPECT_EQ(APSInt::getMinValue(5, true), result); EXPECT_EQ(APFloat::opInvalidOp, - APFloat(APFloat::IEEEdouble, "32") + APFloat(APFloat::IEEEdouble(), "32") .convertToInteger(result, APFloat::rmTowardZero, &isExact)); EXPECT_FALSE(isExact); EXPECT_EQ(APSInt::getMaxValue(5, true), result); EXPECT_EQ(APFloat::opInexact, - APFloat(APFloat::IEEEdouble, "7.9") + APFloat(APFloat::IEEEdouble(), "7.9") .convertToInteger(result, APFloat::rmTowardZero, &isExact)); EXPECT_FALSE(isExact); EXPECT_EQ(APSInt(APInt(5, 7), true), result); result.setIsUnsigned(false); EXPECT_EQ(APFloat::opOK, - APFloat(APFloat::IEEEdouble, "-10") + APFloat(APFloat::IEEEdouble(), "-10") .convertToInteger(result, APFloat::rmTowardZero, &isExact)); EXPECT_TRUE(isExact); EXPECT_EQ(APSInt(APInt(5, -10, true), false), result); EXPECT_EQ(APFloat::opInvalidOp, - APFloat(APFloat::IEEEdouble, "-17") + APFloat(APFloat::IEEEdouble(), "-17") .convertToInteger(result, APFloat::rmTowardZero, &isExact)); EXPECT_FALSE(isExact); EXPECT_EQ(APSInt::getMinValue(5, false), result); EXPECT_EQ(APFloat::opInvalidOp, - APFloat(APFloat::IEEEdouble, "16") + APFloat(APFloat::IEEEdouble(), "16") .convertToInteger(result, APFloat::rmTowardZero, &isExact)); EXPECT_FALSE(isExact); EXPECT_EQ(APSInt::getMaxValue(5, false), result); @@ -1006,224 +1008,224 @@ static APInt nanbits(const fltSemantics &Sem, } TEST(APFloatTest, makeNaN) { - ASSERT_EQ(0x7fc00000, nanbits(APFloat::IEEEsingle, false, false, 0)); - ASSERT_EQ(0xffc00000, nanbits(APFloat::IEEEsingle, false, true, 0)); - ASSERT_EQ(0x7fc0ae72, nanbits(APFloat::IEEEsingle, false, false, 0xae72)); - ASSERT_EQ(0x7fffae72, nanbits(APFloat::IEEEsingle, false, false, 0xffffae72)); - ASSERT_EQ(0x7fa00000, nanbits(APFloat::IEEEsingle, true, false, 0)); - ASSERT_EQ(0xffa00000, nanbits(APFloat::IEEEsingle, true, true, 0)); - ASSERT_EQ(0x7f80ae72, nanbits(APFloat::IEEEsingle, true, false, 0xae72)); - ASSERT_EQ(0x7fbfae72, nanbits(APFloat::IEEEsingle, true, false, 0xffffae72)); - - ASSERT_EQ(0x7ff8000000000000ULL, nanbits(APFloat::IEEEdouble, false, false, 0)); - ASSERT_EQ(0xfff8000000000000ULL, nanbits(APFloat::IEEEdouble, false, true, 0)); - ASSERT_EQ(0x7ff800000000ae72ULL, nanbits(APFloat::IEEEdouble, false, false, 0xae72)); - ASSERT_EQ(0x7fffffffffffae72ULL, nanbits(APFloat::IEEEdouble, false, false, 0xffffffffffffae72ULL)); - ASSERT_EQ(0x7ff4000000000000ULL, nanbits(APFloat::IEEEdouble, true, false, 0)); - ASSERT_EQ(0xfff4000000000000ULL, nanbits(APFloat::IEEEdouble, true, true, 0)); - ASSERT_EQ(0x7ff000000000ae72ULL, nanbits(APFloat::IEEEdouble, true, false, 0xae72)); - ASSERT_EQ(0x7ff7ffffffffae72ULL, nanbits(APFloat::IEEEdouble, true, false, 0xffffffffffffae72ULL)); + ASSERT_EQ(0x7fc00000, nanbits(APFloat::IEEEsingle(), false, false, 0)); + ASSERT_EQ(0xffc00000, nanbits(APFloat::IEEEsingle(), false, true, 0)); + ASSERT_EQ(0x7fc0ae72, nanbits(APFloat::IEEEsingle(), false, false, 0xae72)); + ASSERT_EQ(0x7fffae72, nanbits(APFloat::IEEEsingle(), false, false, 0xffffae72)); + ASSERT_EQ(0x7fa00000, nanbits(APFloat::IEEEsingle(), true, false, 0)); + ASSERT_EQ(0xffa00000, nanbits(APFloat::IEEEsingle(), true, true, 0)); + ASSERT_EQ(0x7f80ae72, nanbits(APFloat::IEEEsingle(), true, false, 0xae72)); + ASSERT_EQ(0x7fbfae72, nanbits(APFloat::IEEEsingle(), true, false, 0xffffae72)); + + ASSERT_EQ(0x7ff8000000000000ULL, nanbits(APFloat::IEEEdouble(), false, false, 0)); + ASSERT_EQ(0xfff8000000000000ULL, nanbits(APFloat::IEEEdouble(), false, true, 0)); + ASSERT_EQ(0x7ff800000000ae72ULL, nanbits(APFloat::IEEEdouble(), false, false, 0xae72)); + ASSERT_EQ(0x7fffffffffffae72ULL, nanbits(APFloat::IEEEdouble(), false, false, 0xffffffffffffae72ULL)); + ASSERT_EQ(0x7ff4000000000000ULL, nanbits(APFloat::IEEEdouble(), true, false, 0)); + ASSERT_EQ(0xfff4000000000000ULL, nanbits(APFloat::IEEEdouble(), true, true, 0)); + ASSERT_EQ(0x7ff000000000ae72ULL, nanbits(APFloat::IEEEdouble(), true, false, 0xae72)); + ASSERT_EQ(0x7ff7ffffffffae72ULL, nanbits(APFloat::IEEEdouble(), true, false, 0xffffffffffffae72ULL)); } #ifdef GTEST_HAS_DEATH_TEST #ifndef NDEBUG TEST(APFloatTest, SemanticsDeath) { - EXPECT_DEATH(APFloat(APFloat::IEEEsingle, 0.0f).convertToDouble(), "Float semantics are not IEEEdouble"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, 0.0 ).convertToFloat(), "Float semantics are not IEEEsingle"); + EXPECT_DEATH(APFloat(APFloat::IEEEsingle(), 0.0f).convertToDouble(), "Float semantics are not IEEEdouble"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), 0.0 ).convertToFloat(), "Float semantics are not IEEEsingle"); } TEST(APFloatTest, StringDecimalDeath) { - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ""), "Invalid string length"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+"), "String has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-"), "String has no digits"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("\0", 1)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("1\0", 2)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("1\02", 3)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("1\02e1", 5)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("1e\0", 3)), "Invalid character in exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("1e1\0", 4)), "Invalid character in exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("1e1\02", 5)), "Invalid character in exponent"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1.0f"), "Invalid character in significand"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ".."), "String contains multiple dots"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "..0"), "String contains multiple dots"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1.0.0"), "String contains multiple dots"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ""), "Invalid string length"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+"), "String has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-"), "String has no digits"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("\0", 1)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("1\0", 2)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("1\02", 3)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("1\02e1", 5)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("1e\0", 3)), "Invalid character in exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("1e1\0", 4)), "Invalid character in exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("1e1\02", 5)), "Invalid character in exponent"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1.0f"), "Invalid character in significand"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ".."), "String contains multiple dots"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "..0"), "String contains multiple dots"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1.0.0"), "String contains multiple dots"); } TEST(APFloatTest, StringDecimalSignificandDeath) { - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "."), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+."), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-."), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "."), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+."), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-."), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "e"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+e"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-e"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "e"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+e"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-e"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "e1"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+e1"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-e1"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "e1"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+e1"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-e1"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ".e1"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+.e1"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-.e1"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ".e1"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+.e1"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-.e1"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ".e"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+.e"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-.e"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ".e"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+.e"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-.e"), "Significand has no digits"); } TEST(APFloatTest, StringDecimalExponentDeath) { - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1.e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+1.e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-1.e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1.e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+1.e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-1.e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ".1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+.1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-.1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ".1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+.1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-.1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1.1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+1.1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-1.1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1.1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+1.1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-1.1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1e+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1e-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1e+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1e-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ".1e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ".1e+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, ".1e-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ".1e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ".1e+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), ".1e-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1.0e"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1.0e+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "1.0e-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1.0e"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1.0e+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "1.0e-"), "Exponent has no digits"); } TEST(APFloatTest, StringHexadecimalDeath) { - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x"), "Invalid string"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x"), "Invalid string"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x"), "Invalid string"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x0"), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x0"), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x0"), "Hex strings require an exponent"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x0."), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x0."), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x0."), "Hex strings require an exponent"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x.0"), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x.0"), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x.0"), "Hex strings require an exponent"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x0.0"), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x0.0"), "Hex strings require an exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x0.0"), "Hex strings require an exponent"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("0x\0", 3)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("0x1\0", 4)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("0x1\02", 5)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("0x1\02p1", 7)), "Invalid character in significand"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("0x1p\0", 5)), "Invalid character in exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("0x1p1\0", 6)), "Invalid character in exponent"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, StringRef("0x1p1\02", 7)), "Invalid character in exponent"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1p0f"), "Invalid character in exponent"); - - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x..p1"), "String contains multiple dots"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x..0p1"), "String contains multiple dots"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1.0.0p1"), "String contains multiple dots"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x"), "Invalid string"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x"), "Invalid string"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x"), "Invalid string"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x0"), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x0"), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x0"), "Hex strings require an exponent"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x0."), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x0."), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x0."), "Hex strings require an exponent"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x.0"), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x.0"), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x.0"), "Hex strings require an exponent"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x0.0"), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x0.0"), "Hex strings require an exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x0.0"), "Hex strings require an exponent"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("0x\0", 3)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("0x1\0", 4)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("0x1\02", 5)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("0x1\02p1", 7)), "Invalid character in significand"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("0x1p\0", 5)), "Invalid character in exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("0x1p1\0", 6)), "Invalid character in exponent"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), StringRef("0x1p1\02", 7)), "Invalid character in exponent"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1p0f"), "Invalid character in exponent"); + + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x..p1"), "String contains multiple dots"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x..0p1"), "String contains multiple dots"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1.0.0p1"), "String contains multiple dots"); } TEST(APFloatTest, StringHexadecimalSignificandDeath) { - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x."), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x."), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x."), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x."), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x."), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x."), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0xp"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0xp"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0xp"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0xp"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0xp"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0xp"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0xp+"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0xp+"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0xp+"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0xp+"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0xp+"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0xp+"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0xp-"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0xp-"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0xp-"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0xp-"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0xp-"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0xp-"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x.p"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x.p"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x.p"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x.p"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x.p"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x.p"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x.p+"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x.p+"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x.p+"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x.p+"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x.p+"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x.p+"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x.p-"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x.p-"), "Significand has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x.p-"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x.p-"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x.p-"), "Significand has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x.p-"), "Significand has no digits"); } TEST(APFloatTest, StringHexadecimalExponentDeath) { - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1.p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1.p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1.p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1.p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1.p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1.p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1.p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1.p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1.p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1.p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1.p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1.p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1.p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1.p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1.p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1.p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1.p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1.p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x.1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x.1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x.1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x.1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x.1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x.1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x.1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x.1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x.1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x.1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x.1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x.1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x.1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x.1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x.1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x.1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x.1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x.1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1.1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1.1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1.1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1.1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1.1p"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1.1p"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1.1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1.1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1.1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1.1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1.1p+"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1.1p+"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "0x1.1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "+0x1.1p-"), "Exponent has no digits"); - EXPECT_DEATH(APFloat(APFloat::IEEEdouble, "-0x1.1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "0x1.1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "+0x1.1p-"), "Exponent has no digits"); + EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), "-0x1.1p-"), "Exponent has no digits"); } #endif #endif @@ -1236,12 +1238,12 @@ TEST(APFloatTest, exactInverse) { EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(0.5))); EXPECT_TRUE(APFloat(2.0f).getExactInverse(&inv)); EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(0.5f))); - EXPECT_TRUE(APFloat(APFloat::IEEEquad, "2.0").getExactInverse(&inv)); - EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::IEEEquad, "0.5"))); - EXPECT_TRUE(APFloat(APFloat::PPCDoubleDouble, "2.0").getExactInverse(&inv)); - EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::PPCDoubleDouble, "0.5"))); - EXPECT_TRUE(APFloat(APFloat::x87DoubleExtended, "2.0").getExactInverse(&inv)); - EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::x87DoubleExtended, "0.5"))); + EXPECT_TRUE(APFloat(APFloat::IEEEquad(), "2.0").getExactInverse(&inv)); + EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::IEEEquad(), "0.5"))); + EXPECT_TRUE(APFloat(APFloat::PPCDoubleDouble(), "2.0").getExactInverse(&inv)); + EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::PPCDoubleDouble(), "0.5"))); + EXPECT_TRUE(APFloat(APFloat::x87DoubleExtended(), "2.0").getExactInverse(&inv)); + EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::x87DoubleExtended(), "0.5"))); // FLT_MIN EXPECT_TRUE(APFloat(1.17549435e-38f).getExactInverse(&inv)); @@ -1256,7 +1258,7 @@ TEST(APFloatTest, exactInverse) { } TEST(APFloatTest, roundToIntegral) { - APFloat T(-0.5), S(3.14), R(APFloat::getLargest(APFloat::IEEEdouble)), P(0.0); + APFloat T(-0.5), S(3.14), R(APFloat::getLargest(APFloat::IEEEdouble())), P(0.0); P = T; P.roundToIntegral(APFloat::rmTowardZero); @@ -1297,19 +1299,19 @@ TEST(APFloatTest, roundToIntegral) { P.roundToIntegral(APFloat::rmNearestTiesToEven); EXPECT_EQ(R.convertToDouble(), P.convertToDouble()); - P = APFloat::getZero(APFloat::IEEEdouble); + P = APFloat::getZero(APFloat::IEEEdouble()); P.roundToIntegral(APFloat::rmTowardZero); EXPECT_EQ(0.0, P.convertToDouble()); - P = APFloat::getZero(APFloat::IEEEdouble, true); + P = APFloat::getZero(APFloat::IEEEdouble(), true); P.roundToIntegral(APFloat::rmTowardZero); EXPECT_EQ(-0.0, P.convertToDouble()); - P = APFloat::getNaN(APFloat::IEEEdouble); + P = APFloat::getNaN(APFloat::IEEEdouble()); P.roundToIntegral(APFloat::rmTowardZero); EXPECT_TRUE(std::isnan(P.convertToDouble())); - P = APFloat::getInf(APFloat::IEEEdouble); + P = APFloat::getInf(APFloat::IEEEdouble()); P.roundToIntegral(APFloat::rmTowardZero); EXPECT_TRUE(std::isinf(P.convertToDouble()) && P.convertToDouble() > 0.0); - P = APFloat::getInf(APFloat::IEEEdouble, true); + P = APFloat::getInf(APFloat::IEEEdouble(), true); P.roundToIntegral(APFloat::rmTowardZero); EXPECT_TRUE(std::isinf(P.convertToDouble()) && P.convertToDouble() < 0.0); } @@ -1319,45 +1321,45 @@ TEST(APFloatTest, isInteger) { EXPECT_TRUE(T.isInteger()); T = APFloat(3.14159); EXPECT_FALSE(T.isInteger()); - T = APFloat::getNaN(APFloat::IEEEdouble); + T = APFloat::getNaN(APFloat::IEEEdouble()); EXPECT_FALSE(T.isInteger()); - T = APFloat::getInf(APFloat::IEEEdouble); + T = APFloat::getInf(APFloat::IEEEdouble()); EXPECT_FALSE(T.isInteger()); - T = APFloat::getInf(APFloat::IEEEdouble, true); + T = APFloat::getInf(APFloat::IEEEdouble(), true); EXPECT_FALSE(T.isInteger()); - T = APFloat::getLargest(APFloat::IEEEdouble); + T = APFloat::getLargest(APFloat::IEEEdouble()); EXPECT_TRUE(T.isInteger()); } TEST(APFloatTest, getLargest) { - EXPECT_EQ(3.402823466e+38f, APFloat::getLargest(APFloat::IEEEsingle).convertToFloat()); - EXPECT_EQ(1.7976931348623158e+308, APFloat::getLargest(APFloat::IEEEdouble).convertToDouble()); + EXPECT_EQ(3.402823466e+38f, APFloat::getLargest(APFloat::IEEEsingle()).convertToFloat()); + EXPECT_EQ(1.7976931348623158e+308, APFloat::getLargest(APFloat::IEEEdouble()).convertToDouble()); } TEST(APFloatTest, getSmallest) { - APFloat test = APFloat::getSmallest(APFloat::IEEEsingle, false); - APFloat expected = APFloat(APFloat::IEEEsingle, "0x0.000002p-126"); + APFloat test = APFloat::getSmallest(APFloat::IEEEsingle(), false); + APFloat expected = APFloat(APFloat::IEEEsingle(), "0x0.000002p-126"); EXPECT_FALSE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_TRUE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); - test = APFloat::getSmallest(APFloat::IEEEsingle, true); - expected = APFloat(APFloat::IEEEsingle, "-0x0.000002p-126"); + test = APFloat::getSmallest(APFloat::IEEEsingle(), true); + expected = APFloat(APFloat::IEEEsingle(), "-0x0.000002p-126"); EXPECT_TRUE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_TRUE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); - test = APFloat::getSmallest(APFloat::IEEEquad, false); - expected = APFloat(APFloat::IEEEquad, "0x0.0000000000000000000000000001p-16382"); + test = APFloat::getSmallest(APFloat::IEEEquad(), false); + expected = APFloat(APFloat::IEEEquad(), "0x0.0000000000000000000000000001p-16382"); EXPECT_FALSE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_TRUE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); - test = APFloat::getSmallest(APFloat::IEEEquad, true); - expected = APFloat(APFloat::IEEEquad, "-0x0.0000000000000000000000000001p-16382"); + test = APFloat::getSmallest(APFloat::IEEEquad(), true); + expected = APFloat(APFloat::IEEEquad(), "-0x0.0000000000000000000000000001p-16382"); EXPECT_TRUE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_TRUE(test.isDenormal()); @@ -1365,29 +1367,29 @@ TEST(APFloatTest, getSmallest) { } TEST(APFloatTest, getSmallestNormalized) { - APFloat test = APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); - APFloat expected = APFloat(APFloat::IEEEsingle, "0x1p-126"); + APFloat test = APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false); + APFloat expected = APFloat(APFloat::IEEEsingle(), "0x1p-126"); EXPECT_FALSE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_FALSE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); - test = APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); - expected = APFloat(APFloat::IEEEsingle, "-0x1p-126"); + test = APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true); + expected = APFloat(APFloat::IEEEsingle(), "-0x1p-126"); EXPECT_TRUE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_FALSE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); - test = APFloat::getSmallestNormalized(APFloat::IEEEquad, false); - expected = APFloat(APFloat::IEEEquad, "0x1p-16382"); + test = APFloat::getSmallestNormalized(APFloat::IEEEquad(), false); + expected = APFloat(APFloat::IEEEquad(), "0x1p-16382"); EXPECT_FALSE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_FALSE(test.isDenormal()); EXPECT_TRUE(test.bitwiseIsEqual(expected)); - test = APFloat::getSmallestNormalized(APFloat::IEEEquad, true); - expected = APFloat(APFloat::IEEEquad, "-0x1p-16382"); + test = APFloat::getSmallestNormalized(APFloat::IEEEquad(), true); + expected = APFloat(APFloat::IEEEquad(), "-0x1p-16382"); EXPECT_TRUE(test.isNegative()); EXPECT_TRUE(test.isFiniteNonZero()); EXPECT_FALSE(test.isDenormal()); @@ -1401,18 +1403,18 @@ TEST(APFloatTest, getZero) { const unsigned long long bitPattern[2]; const unsigned bitPatternLength; } const GetZeroTest[] = { - { &APFloat::IEEEhalf, false, {0, 0}, 1}, - { &APFloat::IEEEhalf, true, {0x8000ULL, 0}, 1}, - { &APFloat::IEEEsingle, false, {0, 0}, 1}, - { &APFloat::IEEEsingle, true, {0x80000000ULL, 0}, 1}, - { &APFloat::IEEEdouble, false, {0, 0}, 1}, - { &APFloat::IEEEdouble, true, {0x8000000000000000ULL, 0}, 1}, - { &APFloat::IEEEquad, false, {0, 0}, 2}, - { &APFloat::IEEEquad, true, {0, 0x8000000000000000ULL}, 2}, - { &APFloat::PPCDoubleDouble, false, {0, 0}, 2}, - { &APFloat::PPCDoubleDouble, true, {0x8000000000000000ULL, 0}, 2}, - { &APFloat::x87DoubleExtended, false, {0, 0}, 2}, - { &APFloat::x87DoubleExtended, true, {0, 0x8000ULL}, 2}, + { &APFloat::IEEEhalf(), false, {0, 0}, 1}, + { &APFloat::IEEEhalf(), true, {0x8000ULL, 0}, 1}, + { &APFloat::IEEEsingle(), false, {0, 0}, 1}, + { &APFloat::IEEEsingle(), true, {0x80000000ULL, 0}, 1}, + { &APFloat::IEEEdouble(), false, {0, 0}, 1}, + { &APFloat::IEEEdouble(), true, {0x8000000000000000ULL, 0}, 1}, + { &APFloat::IEEEquad(), false, {0, 0}, 2}, + { &APFloat::IEEEquad(), true, {0, 0x8000000000000000ULL}, 2}, + { &APFloat::PPCDoubleDouble(), false, {0, 0}, 2}, + { &APFloat::PPCDoubleDouble(), true, {0x8000000000000000ULL, 0}, 2}, + { &APFloat::x87DoubleExtended(), false, {0, 0}, 2}, + { &APFloat::x87DoubleExtended(), true, {0, 0x8000ULL}, 2}, }; const unsigned NumGetZeroTests = 12; for (unsigned i = 0; i < NumGetZeroTests; ++i) { @@ -1444,177 +1446,189 @@ TEST(APFloatTest, copySign) { TEST(APFloatTest, convert) { bool losesInfo; - APFloat test(APFloat::IEEEdouble, "1.0"); - test.convert(APFloat::IEEEsingle, APFloat::rmNearestTiesToEven, &losesInfo); + APFloat test(APFloat::IEEEdouble(), "1.0"); + test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_EQ(1.0f, test.convertToFloat()); EXPECT_FALSE(losesInfo); - test = APFloat(APFloat::x87DoubleExtended, "0x1p-53"); - test.add(APFloat(APFloat::x87DoubleExtended, "1.0"), APFloat::rmNearestTiesToEven); - test.convert(APFloat::IEEEdouble, APFloat::rmNearestTiesToEven, &losesInfo); + test = APFloat(APFloat::x87DoubleExtended(), "0x1p-53"); + test.add(APFloat(APFloat::x87DoubleExtended(), "1.0"), APFloat::rmNearestTiesToEven); + test.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_EQ(1.0, test.convertToDouble()); EXPECT_TRUE(losesInfo); - test = APFloat(APFloat::IEEEquad, "0x1p-53"); - test.add(APFloat(APFloat::IEEEquad, "1.0"), APFloat::rmNearestTiesToEven); - test.convert(APFloat::IEEEdouble, APFloat::rmNearestTiesToEven, &losesInfo); + test = APFloat(APFloat::IEEEquad(), "0x1p-53"); + test.add(APFloat(APFloat::IEEEquad(), "1.0"), APFloat::rmNearestTiesToEven); + test.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_EQ(1.0, test.convertToDouble()); EXPECT_TRUE(losesInfo); - test = APFloat(APFloat::x87DoubleExtended, "0xf.fffffffp+28"); - test.convert(APFloat::IEEEdouble, APFloat::rmNearestTiesToEven, &losesInfo); + test = APFloat(APFloat::x87DoubleExtended(), "0xf.fffffffp+28"); + test.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_EQ(4294967295.0, test.convertToDouble()); EXPECT_FALSE(losesInfo); - test = APFloat::getSNaN(APFloat::IEEEsingle); - APFloat X87SNaN = APFloat::getSNaN(APFloat::x87DoubleExtended); - test.convert(APFloat::x87DoubleExtended, APFloat::rmNearestTiesToEven, + test = APFloat::getSNaN(APFloat::IEEEsingle()); + APFloat X87SNaN = APFloat::getSNaN(APFloat::x87DoubleExtended()); + test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_TRUE(test.bitwiseIsEqual(X87SNaN)); EXPECT_FALSE(losesInfo); - test = APFloat::getQNaN(APFloat::IEEEsingle); - APFloat X87QNaN = APFloat::getQNaN(APFloat::x87DoubleExtended); - test.convert(APFloat::x87DoubleExtended, APFloat::rmNearestTiesToEven, + test = APFloat::getQNaN(APFloat::IEEEsingle()); + APFloat X87QNaN = APFloat::getQNaN(APFloat::x87DoubleExtended()); + test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_TRUE(test.bitwiseIsEqual(X87QNaN)); EXPECT_FALSE(losesInfo); - test = APFloat::getSNaN(APFloat::x87DoubleExtended); - test.convert(APFloat::x87DoubleExtended, APFloat::rmNearestTiesToEven, + test = APFloat::getSNaN(APFloat::x87DoubleExtended()); + test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_TRUE(test.bitwiseIsEqual(X87SNaN)); EXPECT_FALSE(losesInfo); - test = APFloat::getQNaN(APFloat::x87DoubleExtended); - test.convert(APFloat::x87DoubleExtended, APFloat::rmNearestTiesToEven, + test = APFloat::getQNaN(APFloat::x87DoubleExtended()); + test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven, &losesInfo); EXPECT_TRUE(test.bitwiseIsEqual(X87QNaN)); EXPECT_FALSE(losesInfo); } TEST(APFloatTest, PPCDoubleDouble) { - APFloat test(APFloat::PPCDoubleDouble, "1.0"); + APFloat test(APFloat::PPCDoubleDouble(), "1.0"); EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); - test.divide(APFloat(APFloat::PPCDoubleDouble, "3.0"), APFloat::rmNearestTiesToEven); + test.divide(APFloat(APFloat::PPCDoubleDouble(), "3.0"), APFloat::rmNearestTiesToEven); EXPECT_EQ(0x3fd5555555555555ull, test.bitcastToAPInt().getRawData()[0]); EXPECT_EQ(0x3c75555555555556ull, test.bitcastToAPInt().getRawData()[1]); // LDBL_MAX - test = APFloat(APFloat::PPCDoubleDouble, "1.79769313486231580793728971405301e+308"); + test = APFloat(APFloat::PPCDoubleDouble(), "1.79769313486231580793728971405301e+308"); EXPECT_EQ(0x7fefffffffffffffull, test.bitcastToAPInt().getRawData()[0]); EXPECT_EQ(0x7c8ffffffffffffeull, test.bitcastToAPInt().getRawData()[1]); // LDBL_MIN - test = APFloat(APFloat::PPCDoubleDouble, "2.00416836000897277799610805135016e-292"); + test = APFloat(APFloat::PPCDoubleDouble(), "2.00416836000897277799610805135016e-292"); EXPECT_EQ(0x0360000000000000ull, test.bitcastToAPInt().getRawData()[0]); EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); - test = APFloat(APFloat::PPCDoubleDouble, "1.0"); - test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-105"), APFloat::rmNearestTiesToEven); - EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); - EXPECT_EQ(0x3960000000000000ull, test.bitcastToAPInt().getRawData()[1]); - - test = APFloat(APFloat::PPCDoubleDouble, "1.0"); - test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-106"), APFloat::rmNearestTiesToEven); - EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); -#if 0 // XFAIL - // This is what we would expect with a true double-double implementation - EXPECT_EQ(0x3950000000000000ull, test.bitcastToAPInt().getRawData()[1]); -#else - // This is what we get with our 106-bit mantissa approximation - EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); -#endif + // PR30869 + { + auto Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") + + APFloat(APFloat::PPCDoubleDouble(), "1.0"); + EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics()); + + Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") - + APFloat(APFloat::PPCDoubleDouble(), "1.0"); + EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics()); + + Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") * + APFloat(APFloat::PPCDoubleDouble(), "1.0"); + EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics()); + + Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") / + APFloat(APFloat::PPCDoubleDouble(), "1.0"); + EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics()); + + int Exp; + Result = frexp(APFloat(APFloat::PPCDoubleDouble(), "1.0"), Exp, + APFloat::rmNearestTiesToEven); + EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics()); + + Result = scalbn(APFloat(APFloat::PPCDoubleDouble(), "1.0"), 1, + APFloat::rmNearestTiesToEven); + EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics()); + } } TEST(APFloatTest, isNegative) { - APFloat t(APFloat::IEEEsingle, "0x1p+0"); + APFloat t(APFloat::IEEEsingle(), "0x1p+0"); EXPECT_FALSE(t.isNegative()); - t = APFloat(APFloat::IEEEsingle, "-0x1p+0"); + t = APFloat(APFloat::IEEEsingle(), "-0x1p+0"); EXPECT_TRUE(t.isNegative()); - EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle, false).isNegative()); - EXPECT_TRUE(APFloat::getInf(APFloat::IEEEsingle, true).isNegative()); + EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isNegative()); + EXPECT_TRUE(APFloat::getInf(APFloat::IEEEsingle(), true).isNegative()); - EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle, false).isNegative()); - EXPECT_TRUE(APFloat::getZero(APFloat::IEEEsingle, true).isNegative()); + EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isNegative()); + EXPECT_TRUE(APFloat::getZero(APFloat::IEEEsingle(), true).isNegative()); - EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle, false).isNegative()); - EXPECT_TRUE(APFloat::getNaN(APFloat::IEEEsingle, true).isNegative()); + EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isNegative()); + EXPECT_TRUE(APFloat::getNaN(APFloat::IEEEsingle(), true).isNegative()); - EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle, false).isNegative()); - EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle, true).isNegative()); + EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isNegative()); + EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), true).isNegative()); } TEST(APFloatTest, isNormal) { - APFloat t(APFloat::IEEEsingle, "0x1p+0"); + APFloat t(APFloat::IEEEsingle(), "0x1p+0"); EXPECT_TRUE(t.isNormal()); - EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle, false).isNormal()); - EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle, false).isNormal()); - EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle, false).isNormal()); - EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle, false).isNormal()); - EXPECT_FALSE(APFloat(APFloat::IEEEsingle, "0x1p-149").isNormal()); + EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isNormal()); + EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isNormal()); + EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isNormal()); + EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isNormal()); + EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isNormal()); } TEST(APFloatTest, isFinite) { - APFloat t(APFloat::IEEEsingle, "0x1p+0"); + APFloat t(APFloat::IEEEsingle(), "0x1p+0"); EXPECT_TRUE(t.isFinite()); - EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle, false).isFinite()); - EXPECT_TRUE(APFloat::getZero(APFloat::IEEEsingle, false).isFinite()); - EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle, false).isFinite()); - EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle, false).isFinite()); - EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "0x1p-149").isFinite()); + EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isFinite()); + EXPECT_TRUE(APFloat::getZero(APFloat::IEEEsingle(), false).isFinite()); + EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isFinite()); + EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isFinite()); + EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isFinite()); } TEST(APFloatTest, isInfinity) { - APFloat t(APFloat::IEEEsingle, "0x1p+0"); + APFloat t(APFloat::IEEEsingle(), "0x1p+0"); EXPECT_FALSE(t.isInfinity()); - EXPECT_TRUE(APFloat::getInf(APFloat::IEEEsingle, false).isInfinity()); - EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle, false).isInfinity()); - EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle, false).isInfinity()); - EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle, false).isInfinity()); - EXPECT_FALSE(APFloat(APFloat::IEEEsingle, "0x1p-149").isInfinity()); + EXPECT_TRUE(APFloat::getInf(APFloat::IEEEsingle(), false).isInfinity()); + EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isInfinity()); + EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isInfinity()); + EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isInfinity()); + EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isInfinity()); } TEST(APFloatTest, isNaN) { - APFloat t(APFloat::IEEEsingle, "0x1p+0"); + APFloat t(APFloat::IEEEsingle(), "0x1p+0"); EXPECT_FALSE(t.isNaN()); - EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle, false).isNaN()); - EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle, false).isNaN()); - EXPECT_TRUE(APFloat::getNaN(APFloat::IEEEsingle, false).isNaN()); - EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle, false).isNaN()); - EXPECT_FALSE(APFloat(APFloat::IEEEsingle, "0x1p-149").isNaN()); + EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isNaN()); + EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isNaN()); + EXPECT_TRUE(APFloat::getNaN(APFloat::IEEEsingle(), false).isNaN()); + EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isNaN()); + EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isNaN()); } TEST(APFloatTest, isFiniteNonZero) { // Test positive/negative normal value. - EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "0x1p+0").isFiniteNonZero()); - EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "-0x1p+0").isFiniteNonZero()); + EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p+0").isFiniteNonZero()); + EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "-0x1p+0").isFiniteNonZero()); // Test positive/negative denormal value. - EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "0x1p-149").isFiniteNonZero()); - EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "-0x1p-149").isFiniteNonZero()); + EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isFiniteNonZero()); + EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "-0x1p-149").isFiniteNonZero()); // Test +/- Infinity. - EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle, false).isFiniteNonZero()); - EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle, true).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), true).isFiniteNonZero()); // Test +/- Zero. - EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle, false).isFiniteNonZero()); - EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle, true).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), true).isFiniteNonZero()); // Test +/- qNaN. +/- dont mean anything with qNaN but paranoia can't hurt in // this instance. - EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle, false).isFiniteNonZero()); - EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle, true).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), true).isFiniteNonZero()); // Test +/- sNaN. +/- dont mean anything with sNaN but paranoia can't hurt in // this instance. - EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle, false).isFiniteNonZero()); - EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle, true).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isFiniteNonZero()); + EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), true).isFiniteNonZero()); } TEST(APFloatTest, add) { @@ -1625,22 +1639,22 @@ TEST(APFloatTest, add) { // signaling NaNs should have a result that is a quiet NaN. Currently they // return sNaN. - APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false); - APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true); - APFloat PZero = APFloat::getZero(APFloat::IEEEsingle, false); - APFloat MZero = APFloat::getZero(APFloat::IEEEsingle, true); - APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle, false); - APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false); - APFloat PNormalValue = APFloat(APFloat::IEEEsingle, "0x1p+0"); - APFloat MNormalValue = APFloat(APFloat::IEEEsingle, "-0x1p+0"); - APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle, false); - APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle, true); - APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, false); - APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, true); + APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false); + APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true); + APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false); + APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true); + APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false); + APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false); + APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0"); + APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0"); + APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false); + APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true); + APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false); + APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true); APFloat PSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false); APFloat MSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true); const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; @@ -1897,7 +1911,7 @@ TEST(APFloatTest, add) { APFloat y(SpecialCaseTests[i].y); APFloat::opStatus status = x.add(y, APFloat::rmNearestTiesToEven); - APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + APFloat result(APFloat::IEEEsingle(), SpecialCaseTests[i].result); EXPECT_TRUE(result.bitwiseIsEqual(x)); EXPECT_TRUE((int)status == SpecialCaseTests[i].status); @@ -1913,22 +1927,22 @@ TEST(APFloatTest, subtract) { // signaling NaNs should have a result that is a quiet NaN. Currently they // return sNaN. - APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false); - APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true); - APFloat PZero = APFloat::getZero(APFloat::IEEEsingle, false); - APFloat MZero = APFloat::getZero(APFloat::IEEEsingle, true); - APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle, false); - APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false); - APFloat PNormalValue = APFloat(APFloat::IEEEsingle, "0x1p+0"); - APFloat MNormalValue = APFloat(APFloat::IEEEsingle, "-0x1p+0"); - APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle, false); - APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle, true); - APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, false); - APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, true); + APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false); + APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true); + APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false); + APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true); + APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false); + APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false); + APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0"); + APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0"); + APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false); + APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true); + APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false); + APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true); APFloat PSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false); APFloat MSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true); const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; @@ -2185,7 +2199,7 @@ TEST(APFloatTest, subtract) { APFloat y(SpecialCaseTests[i].y); APFloat::opStatus status = x.subtract(y, APFloat::rmNearestTiesToEven); - APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + APFloat result(APFloat::IEEEsingle(), SpecialCaseTests[i].result); EXPECT_TRUE(result.bitwiseIsEqual(x)); EXPECT_TRUE((int)status == SpecialCaseTests[i].status); @@ -2201,22 +2215,22 @@ TEST(APFloatTest, multiply) { // signaling NaNs should have a result that is a quiet NaN. Currently they // return sNaN. - APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false); - APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true); - APFloat PZero = APFloat::getZero(APFloat::IEEEsingle, false); - APFloat MZero = APFloat::getZero(APFloat::IEEEsingle, true); - APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle, false); - APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false); - APFloat PNormalValue = APFloat(APFloat::IEEEsingle, "0x1p+0"); - APFloat MNormalValue = APFloat(APFloat::IEEEsingle, "-0x1p+0"); - APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle, false); - APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle, true); - APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, false); - APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, true); + APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false); + APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true); + APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false); + APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true); + APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false); + APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false); + APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0"); + APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0"); + APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false); + APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true); + APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false); + APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true); APFloat PSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false); APFloat MSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true); const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; const int UnderflowStatus = APFloat::opUnderflow | APFloat::opInexact; @@ -2474,7 +2488,7 @@ TEST(APFloatTest, multiply) { APFloat y(SpecialCaseTests[i].y); APFloat::opStatus status = x.multiply(y, APFloat::rmNearestTiesToEven); - APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + APFloat result(APFloat::IEEEsingle(), SpecialCaseTests[i].result); EXPECT_TRUE(result.bitwiseIsEqual(x)); EXPECT_TRUE((int)status == SpecialCaseTests[i].status); @@ -2490,22 +2504,22 @@ TEST(APFloatTest, divide) { // signaling NaNs should have a result that is a quiet NaN. Currently they // return sNaN. - APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false); - APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true); - APFloat PZero = APFloat::getZero(APFloat::IEEEsingle, false); - APFloat MZero = APFloat::getZero(APFloat::IEEEsingle, true); - APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle, false); - APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false); - APFloat PNormalValue = APFloat(APFloat::IEEEsingle, "0x1p+0"); - APFloat MNormalValue = APFloat(APFloat::IEEEsingle, "-0x1p+0"); - APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle, false); - APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle, true); - APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, false); - APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, true); + APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false); + APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true); + APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false); + APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true); + APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false); + APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false); + APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0"); + APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0"); + APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false); + APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true); + APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false); + APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true); APFloat PSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false); APFloat MSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true); const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; const int UnderflowStatus = APFloat::opUnderflow | APFloat::opInexact; @@ -2763,7 +2777,7 @@ TEST(APFloatTest, divide) { APFloat y(SpecialCaseTests[i].y); APFloat::opStatus status = x.divide(y, APFloat::rmNearestTiesToEven); - APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + APFloat result(APFloat::IEEEsingle(), SpecialCaseTests[i].result); EXPECT_TRUE(result.bitwiseIsEqual(x)); EXPECT_TRUE((int)status == SpecialCaseTests[i].status); @@ -2773,8 +2787,8 @@ TEST(APFloatTest, divide) { TEST(APFloatTest, operatorOverloads) { // This is mostly testing that these operator overloads compile. - APFloat One = APFloat(APFloat::IEEEsingle, "0x1p+0"); - APFloat Two = APFloat(APFloat::IEEEsingle, "0x2p+0"); + APFloat One = APFloat(APFloat::IEEEsingle(), "0x1p+0"); + APFloat Two = APFloat(APFloat::IEEEsingle(), "0x2p+0"); EXPECT_TRUE(Two.bitwiseIsEqual(One + One)); EXPECT_TRUE(One.bitwiseIsEqual(Two - One)); EXPECT_TRUE(Two.bitwiseIsEqual(One * Two)); @@ -2782,24 +2796,24 @@ TEST(APFloatTest, operatorOverloads) { } TEST(APFloatTest, abs) { - APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false); - APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true); - APFloat PZero = APFloat::getZero(APFloat::IEEEsingle, false); - APFloat MZero = APFloat::getZero(APFloat::IEEEsingle, true); - APFloat PQNaN = APFloat::getNaN(APFloat::IEEEsingle, false); - APFloat MQNaN = APFloat::getNaN(APFloat::IEEEsingle, true); - APFloat PSNaN = APFloat::getSNaN(APFloat::IEEEsingle, false); - APFloat MSNaN = APFloat::getSNaN(APFloat::IEEEsingle, true); - APFloat PNormalValue = APFloat(APFloat::IEEEsingle, "0x1p+0"); - APFloat MNormalValue = APFloat(APFloat::IEEEsingle, "-0x1p+0"); - APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle, false); - APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle, true); - APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, false); - APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle, true); + APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false); + APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true); + APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false); + APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true); + APFloat PQNaN = APFloat::getNaN(APFloat::IEEEsingle(), false); + APFloat MQNaN = APFloat::getNaN(APFloat::IEEEsingle(), true); + APFloat PSNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false); + APFloat MSNaN = APFloat::getSNaN(APFloat::IEEEsingle(), true); + APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0"); + APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0"); + APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false); + APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true); + APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false); + APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true); APFloat PSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false); APFloat MSmallestNormalized = - APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true); EXPECT_TRUE(PInf.bitwiseIsEqual(abs(PInf))); EXPECT_TRUE(PInf.bitwiseIsEqual(abs(MInf))); @@ -2820,68 +2834,68 @@ 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"))); - EXPECT_EQ(-42, ilogb(APFloat(APFloat::IEEEsingle, "0x1p-42"))); + EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), false))); + EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), true))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1024"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1023"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1023"))); + EXPECT_EQ(-51, ilogb(APFloat(APFloat::IEEEdouble(), "0x1p-51"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1023"))); + EXPECT_EQ(-2, ilogb(APFloat(APFloat::IEEEdouble(), "0x0.ffffp-1"))); + EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.fffep-1023"))); + EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble(), false))); + EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble(), true))); + + + EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+0"))); + EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle(), "-0x1p+0"))); + EXPECT_EQ(42, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+42"))); + EXPECT_EQ(-42, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p-42"))); EXPECT_EQ(APFloat::IEK_Inf, - ilogb(APFloat::getInf(APFloat::IEEEsingle, false))); + ilogb(APFloat::getInf(APFloat::IEEEsingle(), false))); EXPECT_EQ(APFloat::IEK_Inf, - ilogb(APFloat::getInf(APFloat::IEEEsingle, true))); + ilogb(APFloat::getInf(APFloat::IEEEsingle(), true))); EXPECT_EQ(APFloat::IEK_Zero, - ilogb(APFloat::getZero(APFloat::IEEEsingle, false))); + ilogb(APFloat::getZero(APFloat::IEEEsingle(), false))); EXPECT_EQ(APFloat::IEK_Zero, - ilogb(APFloat::getZero(APFloat::IEEEsingle, true))); + ilogb(APFloat::getZero(APFloat::IEEEsingle(), true))); EXPECT_EQ(APFloat::IEK_NaN, - ilogb(APFloat::getNaN(APFloat::IEEEsingle, false))); + ilogb(APFloat::getNaN(APFloat::IEEEsingle(), false))); EXPECT_EQ(APFloat::IEK_NaN, - ilogb(APFloat::getSNaN(APFloat::IEEEsingle, false))); + ilogb(APFloat::getSNaN(APFloat::IEEEsingle(), false))); - EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle, false))); - EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle, true))); + EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle(), false))); + EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle(), true))); - EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, false))); - EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, true))); + EXPECT_EQ(-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))); + ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false))); EXPECT_EQ(-126, - ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle, true))); + ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true))); } TEST(APFloatTest, scalbn) { const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven; EXPECT_TRUE( - APFloat(APFloat::IEEEsingle, "0x1p+0") - .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 0, RM))); + APFloat(APFloat::IEEEsingle(), "0x1p+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, RM))); + APFloat(APFloat::IEEEsingle(), "0x1p+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, RM))); + APFloat(APFloat::IEEEsingle(), "0x1p-42") + .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+0"), -42, RM))); - APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false); - APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true); - APFloat PZero = APFloat::getZero(APFloat::IEEEsingle, false); - APFloat MZero = APFloat::getZero(APFloat::IEEEsingle, true); - APFloat QPNaN = APFloat::getNaN(APFloat::IEEEsingle, false); - APFloat QMNaN = APFloat::getNaN(APFloat::IEEEsingle, true); - APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false); + APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false); + APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true); + APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false); + APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true); + APFloat QPNaN = APFloat::getNaN(APFloat::IEEEsingle(), false); + APFloat QMNaN = APFloat::getNaN(APFloat::IEEEsingle(), true); + APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false); EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0, RM))); EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0, RM))); @@ -2900,57 +2914,57 @@ TEST(APFloatTest, scalbn) { (UINT64_C(1234) << 32) | 1); - APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble, false, + 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))); + scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+0"), 128, RM))); EXPECT_TRUE(MInf.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), 128, RM))); + scalbn(APFloat(APFloat::IEEEsingle(), "-0x1p+0"), 128, RM))); EXPECT_TRUE(PInf.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "0x1p+127"), 1, RM))); + scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+127"), 1, RM))); EXPECT_TRUE(PZero.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "0x1p-127"), -127, RM))); + scalbn(APFloat(APFloat::IEEEsingle(), "0x1p-127"), -127, RM))); EXPECT_TRUE(MZero.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -127, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "-0x1p-149").bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -22, RM))); + 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"), -24, RM))); + scalbn(APFloat(APFloat::IEEEsingle(), "0x1p-126"), -24, RM))); - APFloat SmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, false); - APFloat NegSmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, true); + 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 LargestF64 = APFloat::getLargest(APFloat::IEEEdouble(), false); + APFloat NegLargestF64 = APFloat::getLargest(APFloat::IEEEdouble(), true); APFloat SmallestNormalizedF64 - = APFloat::getSmallestNormalized(APFloat::IEEEdouble, false); + = APFloat::getSmallestNormalized(APFloat::IEEEdouble(), false); APFloat NegSmallestNormalizedF64 - = APFloat::getSmallestNormalized(APFloat::IEEEdouble, true); + = APFloat::getSmallestNormalized(APFloat::IEEEdouble(), true); - APFloat LargestDenormalF64(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023"); - APFloat NegLargestDenormalF64(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023"); + 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))); + scalbn(APFloat(APFloat::IEEEdouble(), "0x1p-1074"), 0, RM))); EXPECT_TRUE(NegSmallestF64.bitwiseIsEqual( - scalbn(APFloat(APFloat::IEEEdouble, "-0x1p-1074"), 0, RM))); + scalbn(APFloat(APFloat::IEEEdouble(), "-0x1p-1074"), 0, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023") + 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") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p+1022") .bitwiseIsEqual(scalbn(SmallestF64, 2096, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023") + 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()); @@ -2964,12 +2978,12 @@ TEST(APFloatTest, scalbn) { EXPECT_TRUE(NegLargestDenormalF64 .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 0, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1022") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1022") .bitwiseIsEqual(scalbn(LargestDenormalF64, 1, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1021") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1021") .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 2, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1") + 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()); @@ -2978,25 +2992,25 @@ TEST(APFloatTest, scalbn) { EXPECT_TRUE(scalbn(LargestDenormalF64, 2098, RM).isInfinity()); EXPECT_TRUE(scalbn(LargestDenormalF64, 2099, RM).isInfinity()); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-2") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-2") .bitwiseIsEqual(scalbn(LargestDenormalF64, 1021, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1") .bitwiseIsEqual(scalbn(LargestDenormalF64, 1022, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+0") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep+0") .bitwiseIsEqual(scalbn(LargestDenormalF64, 1023, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1023") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep+1023") .bitwiseIsEqual(scalbn(LargestDenormalF64, 2046, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+974") + 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") + 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") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1") .bitwiseIsEqual(scalbn(RandomDenormalF64, -52, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-2") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-2") .bitwiseIsEqual(scalbn(RandomDenormalF64, -53, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+0") + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp+0") .bitwiseIsEqual(scalbn(RandomDenormalF64, -51, RM))); EXPECT_TRUE(scalbn(RandomDenormalF64, -2097, RM).isPosZero()); @@ -3004,60 +3018,60 @@ TEST(APFloatTest, scalbn) { EXPECT_TRUE( - APFloat(APFloat::IEEEdouble, "-0x1p-1073") + APFloat(APFloat::IEEEdouble(), "-0x1p-1073") .bitwiseIsEqual(scalbn(NegLargestF64, -2097, RM))); EXPECT_TRUE( - APFloat(APFloat::IEEEdouble, "-0x1p-1024") + APFloat(APFloat::IEEEdouble(), "-0x1p-1024") .bitwiseIsEqual(scalbn(NegLargestF64, -2048, RM))); EXPECT_TRUE( - APFloat(APFloat::IEEEdouble, "0x1p-1073") + APFloat(APFloat::IEEEdouble(), "0x1p-1073") .bitwiseIsEqual(scalbn(LargestF64, -2097, RM))); EXPECT_TRUE( - APFloat(APFloat::IEEEdouble, "0x1p-1074") + APFloat(APFloat::IEEEdouble(), "0x1p-1074") .bitwiseIsEqual(scalbn(LargestF64, -2098, RM))); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1074") + 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))); + 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))); + 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 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 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 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 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 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); + 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) | @@ -3065,16 +3079,16 @@ TEST(APFloatTest, frexp) { (UINT64_C(1234) << 32) | 1); - APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble, false, + APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble(), false, &Payload); APFloat SmallestNormalized - = APFloat::getSmallestNormalized(APFloat::IEEEdouble, false); + = APFloat::getSmallestNormalized(APFloat::IEEEdouble(), false); APFloat NegSmallestNormalized - = APFloat::getSmallestNormalized(APFloat::IEEEdouble, true); + = APFloat::getSmallestNormalized(APFloat::IEEEdouble(), true); int Exp; - APFloat Frac(APFloat::IEEEdouble); + APFloat Frac(APFloat::IEEEdouble()); Frac = frexp(PZero, Exp, RM); @@ -3088,37 +3102,37 @@ TEST(APFloatTest, frexp) { Frac = frexp(One, Exp, RM); EXPECT_EQ(1, Exp); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1.fffffffffffffp-1").bitwiseIsEqual(Frac)); Frac = frexp(PInf, Exp, RM); @@ -3146,16 +3160,150 @@ TEST(APFloatTest, frexp) { EXPECT_TRUE(Frac.isNaN() && !Frac.isSignaling()); EXPECT_EQ(Payload, Frac.bitcastToAPInt().getLoBits(51)); - Frac = frexp(APFloat(APFloat::IEEEdouble, "0x0.ffffp-1"), Exp, RM); + Frac = frexp(APFloat(APFloat::IEEEdouble(), "0x0.ffffp-1"), Exp, RM); EXPECT_EQ(-1, Exp); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.fffep-1").bitwiseIsEqual(Frac)); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.fffep-1").bitwiseIsEqual(Frac)); - Frac = frexp(APFloat(APFloat::IEEEdouble, "0x1p-51"), Exp, RM); + Frac = frexp(APFloat(APFloat::IEEEdouble(), "0x1p-51"), Exp, RM); EXPECT_EQ(-50, Exp); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac)); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p-1").bitwiseIsEqual(Frac)); - Frac = frexp(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+51"), Exp, RM); + Frac = frexp(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp+51"), Exp, RM); EXPECT_EQ(52, Exp); - EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac)); + EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac)); +} + +TEST(APFloatTest, PPCDoubleDoubleAddSpecial) { + using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, + APFloat::fltCategory, APFloat::roundingMode>; + DataType Data[] = { + // (1 + 0) + (-1 + 0) = fcZero + std::make_tuple(0x3ff0000000000000ull, 0, 0xbff0000000000000ull, 0, + APFloat::fcZero, APFloat::rmNearestTiesToEven), + // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = fcInfinity + std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + 0x7948000000000000ull, 0ull, APFloat::fcInfinity, + APFloat::rmNearestTiesToEven), + // TODO: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when + // PPCDoubleDoubleImpl is gone. + // LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 - + // 160))) = fcNormal + std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + 0x7947ffffffffffffull, 0x75effffffffffffeull, + APFloat::fcNormal, APFloat::rmNearestTiesToEven), + // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = fcInfinity + std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + 0x7fefffffffffffffull, 0x7c8ffffffffffffeull, + APFloat::fcInfinity, APFloat::rmNearestTiesToEven), + // NaN + (1 + 0) = fcNaN + std::make_tuple(0x7ff8000000000000ull, 0, 0x3ff0000000000000ull, 0, + APFloat::fcNaN, APFloat::rmNearestTiesToEven), + }; + + for (auto Tp : Data) { + uint64_t Op1[2], Op2[2]; + APFloat::fltCategory Expected; + APFloat::roundingMode RM; + std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected, RM) = Tp; + + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A1.add(A2, RM); + + EXPECT_EQ(Expected, A1.getCategory()) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], + Op2[1]) + .str(); + } +} + +TEST(APFloatTest, PPCDoubleDoubleAdd) { + using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, + uint64_t, APFloat::roundingMode>; + DataType Data[] = { + // (1 + 0) + (1e-105 + 0) = (1 + 1e-105) + std::make_tuple(0x3ff0000000000000ull, 0, 0x3960000000000000ull, 0, + 0x3ff0000000000000ull, 0x3960000000000000ull, + APFloat::rmNearestTiesToEven), + // (1 + 0) + (1e-106 + 0) = (1 + 1e-106) + std::make_tuple(0x3ff0000000000000ull, 0, 0x3950000000000000ull, 0, + 0x3ff0000000000000ull, 0x3950000000000000ull, + APFloat::rmNearestTiesToEven), + // (1 + 1e-106) + (1e-106 + 0) = (1 + 1e-105) + std::make_tuple(0x3ff0000000000000ull, 0x3950000000000000ull, + 0x3950000000000000ull, 0, 0x3ff0000000000000ull, + 0x3960000000000000ull, APFloat::rmNearestTiesToEven), + // (1 + 0) + (epsilon + 0) = (1 + epsilon) + std::make_tuple(0x3ff0000000000000ull, 0, 0x0000000000000001ull, 0, + 0x3ff0000000000000ull, 0x0000000000000001ull, + APFloat::rmNearestTiesToEven), + // TODO: change 0xf950000000000000 to 0xf940000000000000, when + // PPCDoubleDoubleImpl is gone. + // (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX + + // 1.11111... << (1023 - 52) + std::make_tuple(0x7fefffffffffffffull, 0xf950000000000000ull, + 0x7c90000000000000ull, 0, 0x7fefffffffffffffull, + 0x7c8ffffffffffffeull, APFloat::rmNearestTiesToEven), + // TODO: change 0xf950000000000000 to 0xf940000000000000, when + // PPCDoubleDoubleImpl is gone. + // (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX + + // 1.11111... << (1023 - 52) + std::make_tuple(0x7c90000000000000ull, 0, 0x7fefffffffffffffull, + 0xf950000000000000ull, 0x7fefffffffffffffull, + 0x7c8ffffffffffffeull, APFloat::rmNearestTiesToEven), + }; + + for (auto Tp : Data) { + uint64_t Op1[2], Op2[2], Expected[2]; + APFloat::roundingMode RM; + std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp; + + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A1.add(A2, RM); + + EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], + Op2[1]) + .str(); + EXPECT_EQ(Expected[1], A1.getSecondFloat().bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], + Op2[1]) + .str(); + } +} + +TEST(APFloatTest, PPCDoubleDoubleSubtract) { + using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, + uint64_t, APFloat::roundingMode>; + DataType Data[] = { + // (1 + 0) - (-1e-105 + 0) = (1 + 1e-105) + std::make_tuple(0x3ff0000000000000ull, 0, 0xb960000000000000ull, 0, + 0x3ff0000000000000ull, 0x3960000000000000ull, + APFloat::rmNearestTiesToEven), + // (1 + 0) - (-1e-106 + 0) = (1 + 1e-106) + std::make_tuple(0x3ff0000000000000ull, 0, 0xb950000000000000ull, 0, + 0x3ff0000000000000ull, 0x3950000000000000ull, + APFloat::rmNearestTiesToEven), + }; + + for (auto Tp : Data) { + uint64_t Op1[2], Op2[2], Expected[2]; + APFloat::roundingMode RM; + std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp; + + APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1)); + APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2)); + A1.subtract(A2, RM); + + EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) - ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], + Op2[1]) + .str(); + EXPECT_EQ(Expected[1], A1.getSecondFloat().bitcastToAPInt().getRawData()[0]) + << formatv("({0:x} + {1:x}) - ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0], + Op2[1]) + .str(); + } } } diff --git a/unittests/ADT/APIntTest.cpp b/unittests/ADT/APIntTest.cpp index b0d80c3c6819..cbffdc096fbe 100644 --- a/unittests/ADT/APIntTest.cpp +++ b/unittests/ADT/APIntTest.cpp @@ -32,6 +32,11 @@ TEST(APIntTest, ShiftLeftByZero) { EXPECT_FALSE(Shl[1]); } +TEST(APIntTest, i64_ArithmeticRightShiftNegative) { + const APInt neg_one(64, static_cast<uint64_t>(-1), true); + EXPECT_EQ(neg_one, neg_one.ashr(7)); +} + TEST(APIntTest, i128_NegativeCount) { APInt Minus3(128, static_cast<uint64_t>(-3), true); EXPECT_EQ(126u, Minus3.countLeadingOnes()); @@ -47,9 +52,6 @@ TEST(APIntTest, i128_NegativeCount) { EXPECT_EQ(-1, Minus1.getSExtValue()); } -// XFAIL this test on FreeBSD where the system gcc-4.2.1 seems to miscompile it. -#if defined(__llvm__) || !defined(__FreeBSD__) - TEST(APIntTest, i33_Count) { APInt i33minus2(33, static_cast<uint64_t>(-2), true); EXPECT_EQ(0u, i33minus2.countLeadingZeros()); @@ -61,8 +63,6 @@ TEST(APIntTest, i33_Count) { EXPECT_EQ(((uint64_t)-2)&((1ull<<33) -1), i33minus2.getZExtValue()); } -#endif - TEST(APIntTest, i65_Count) { APInt i65(65, 0, true); EXPECT_EQ(65u, i65.countLeadingZeros()); @@ -416,6 +416,175 @@ TEST(APIntTest, compareLargeIntegers) { EXPECT_TRUE(!MinusTwo.slt(MinusTwo)); } +TEST(APIntTest, rvalue_arithmetic) { + // Test all combinations of lvalue/rvalue lhs/rhs of add/sub + + // Lamdba to return an APInt by value, but also provide the raw value of the + // allocated data. + auto getRValue = [](const char *HexString, uint64_t const *&RawData) { + APInt V(129, HexString, 16); + RawData = V.getRawData(); + return V; + }; + + APInt One(129, "1", 16); + APInt Two(129, "2", 16); + APInt Three(129, "3", 16); + APInt MinusOne = -One; + + const uint64_t *RawDataL = nullptr; + const uint64_t *RawDataR = nullptr; + + { + // 1 + 1 = 2 + APInt AddLL = One + One; + EXPECT_EQ(AddLL, Two); + + APInt AddLR = One + getRValue("1", RawDataR); + EXPECT_EQ(AddLR, Two); + EXPECT_EQ(AddLR.getRawData(), RawDataR); + + APInt AddRL = getRValue("1", RawDataL) + One; + EXPECT_EQ(AddRL, Two); + EXPECT_EQ(AddRL.getRawData(), RawDataL); + + APInt AddRR = getRValue("1", RawDataL) + getRValue("1", RawDataR); + EXPECT_EQ(AddRR, Two); + EXPECT_EQ(AddRR.getRawData(), RawDataR); + + // LValue's and constants + APInt AddLK = One + 1; + EXPECT_EQ(AddLK, Two); + + APInt AddKL = 1 + One; + EXPECT_EQ(AddKL, Two); + + // RValue's and constants + APInt AddRK = getRValue("1", RawDataL) + 1; + EXPECT_EQ(AddRK, Two); + EXPECT_EQ(AddRK.getRawData(), RawDataL); + + APInt AddKR = 1 + getRValue("1", RawDataR); + EXPECT_EQ(AddKR, Two); + EXPECT_EQ(AddKR.getRawData(), RawDataR); + } + + { + // 0x0,FFFF...FFFF + 0x2 = 0x100...0001 + APInt AllOnes(129, "0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16); + APInt HighOneLowOne(129, "100000000000000000000000000000001", 16); + + APInt AddLL = AllOnes + Two; + EXPECT_EQ(AddLL, HighOneLowOne); + + APInt AddLR = AllOnes + getRValue("2", RawDataR); + EXPECT_EQ(AddLR, HighOneLowOne); + EXPECT_EQ(AddLR.getRawData(), RawDataR); + + APInt AddRL = getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataL) + Two; + EXPECT_EQ(AddRL, HighOneLowOne); + EXPECT_EQ(AddRL.getRawData(), RawDataL); + + APInt AddRR = getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataL) + + getRValue("2", RawDataR); + EXPECT_EQ(AddRR, HighOneLowOne); + EXPECT_EQ(AddRR.getRawData(), RawDataR); + + // LValue's and constants + APInt AddLK = AllOnes + 2; + EXPECT_EQ(AddLK, HighOneLowOne); + + APInt AddKL = 2 + AllOnes; + EXPECT_EQ(AddKL, HighOneLowOne); + + // RValue's and constants + APInt AddRK = getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataL) + 2; + EXPECT_EQ(AddRK, HighOneLowOne); + EXPECT_EQ(AddRK.getRawData(), RawDataL); + + APInt AddKR = 2 + getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR); + EXPECT_EQ(AddKR, HighOneLowOne); + EXPECT_EQ(AddKR.getRawData(), RawDataR); + } + + { + // 2 - 1 = 1 + APInt SubLL = Two - One; + EXPECT_EQ(SubLL, One); + + APInt SubLR = Two - getRValue("1", RawDataR); + EXPECT_EQ(SubLR, One); + EXPECT_EQ(SubLR.getRawData(), RawDataR); + + APInt SubRL = getRValue("2", RawDataL) - One; + EXPECT_EQ(SubRL, One); + EXPECT_EQ(SubRL.getRawData(), RawDataL); + + APInt SubRR = getRValue("2", RawDataL) - getRValue("1", RawDataR); + EXPECT_EQ(SubRR, One); + EXPECT_EQ(SubRR.getRawData(), RawDataR); + + // LValue's and constants + APInt SubLK = Two - 1; + EXPECT_EQ(SubLK, One); + + APInt SubKL = 2 - One; + EXPECT_EQ(SubKL, One); + + // RValue's and constants + APInt SubRK = getRValue("2", RawDataL) - 1; + EXPECT_EQ(SubRK, One); + EXPECT_EQ(SubRK.getRawData(), RawDataL); + + APInt SubKR = 2 - getRValue("1", RawDataR); + EXPECT_EQ(SubKR, One); + EXPECT_EQ(SubKR.getRawData(), RawDataR); + } + + { + // 0x100...0001 - 0x0,FFFF...FFFF = 0x2 + APInt AllOnes(129, "0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16); + APInt HighOneLowOne(129, "100000000000000000000000000000001", 16); + + APInt SubLL = HighOneLowOne - AllOnes; + EXPECT_EQ(SubLL, Two); + + APInt SubLR = HighOneLowOne - + getRValue("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR); + EXPECT_EQ(SubLR, Two); + EXPECT_EQ(SubLR.getRawData(), RawDataR); + + APInt SubRL = getRValue("100000000000000000000000000000001", RawDataL) - + AllOnes; + EXPECT_EQ(SubRL, Two); + EXPECT_EQ(SubRL.getRawData(), RawDataL); + + APInt SubRR = getRValue("100000000000000000000000000000001", RawDataL) - + getRValue("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR); + EXPECT_EQ(SubRR, Two); + EXPECT_EQ(SubRR.getRawData(), RawDataR); + + // LValue's and constants + // 0x100...0001 - 0x2 = 0x0,FFFF...FFFF + APInt SubLK = HighOneLowOne - 2; + EXPECT_EQ(SubLK, AllOnes); + + // 2 - (-1) = 3 + APInt SubKL = 2 - MinusOne; + EXPECT_EQ(SubKL, Three); + + // RValue's and constants + // 0x100...0001 - 0x2 = 0x0,FFFF...FFFF + APInt SubRK = getRValue("100000000000000000000000000000001", RawDataL) - 2; + EXPECT_EQ(SubRK, AllOnes); + EXPECT_EQ(SubRK.getRawData(), RawDataL); + + APInt SubKR = 2 - getRValue("1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR); + EXPECT_EQ(SubKR, Three); + EXPECT_EQ(SubKR.getRawData(), RawDataR); + } +} + // Tests different div/rem varaints using scheme (a * b + c) / a void testDiv(APInt a, APInt b, APInt c) { diff --git a/unittests/ADT/ArrayRefTest.cpp b/unittests/ADT/ArrayRefTest.cpp index b5b71f06f65b..65b4cbcd6689 100644 --- a/unittests/ADT/ArrayRefTest.cpp +++ b/unittests/ADT/ArrayRefTest.cpp @@ -31,6 +31,26 @@ static_assert( !std::is_convertible<ArrayRef<volatile int *>, ArrayRef<int *>>::value, "Removing volatile"); +// Check that we can't accidentally assign a temporary location to an ArrayRef. +// (Unfortunately we can't make use of the same thing with constructors.) +// +// Disable this check under MSVC; even MSVC 2015 isn't inconsistent between +// std::is_assignable and actually writing such an assignment. +#if !defined(_MSC_VER) +static_assert( + !std::is_assignable<ArrayRef<int *>, int *>::value, + "Assigning from single prvalue element"); +static_assert( + !std::is_assignable<ArrayRef<int *>, int * &&>::value, + "Assigning from single xvalue element"); +static_assert( + std::is_assignable<ArrayRef<int *>, int * &>::value, + "Assigning from single lvalue element"); +static_assert( + !std::is_assignable<ArrayRef<int *>, std::initializer_list<int *>>::value, + "Assigning from an initializer list"); +#endif + namespace { TEST(ArrayRefTest, AllocatorCopy) { @@ -82,6 +102,64 @@ TEST(ArrayRefTest, DropFront) { EXPECT_EQ(1U, AR3.drop_front(AR3.size() - 1).size()); } +TEST(ArrayRefTest, DropWhile) { + static const int TheNumbers[] = {1, 3, 5, 8, 10, 11}; + ArrayRef<int> AR1(TheNumbers); + ArrayRef<int> Expected = AR1.drop_front(3); + EXPECT_EQ(Expected, AR1.drop_while([](const int &N) { return N % 2 == 1; })); + + EXPECT_EQ(AR1, AR1.drop_while([](const int &N) { return N < 0; })); + EXPECT_EQ(ArrayRef<int>(), + AR1.drop_while([](const int &N) { return N > 0; })); +} + +TEST(ArrayRefTest, DropUntil) { + static const int TheNumbers[] = {1, 3, 5, 8, 10, 11}; + ArrayRef<int> AR1(TheNumbers); + ArrayRef<int> Expected = AR1.drop_front(3); + EXPECT_EQ(Expected, AR1.drop_until([](const int &N) { return N % 2 == 0; })); + + EXPECT_EQ(ArrayRef<int>(), + AR1.drop_until([](const int &N) { return N < 0; })); + EXPECT_EQ(AR1, AR1.drop_until([](const int &N) { return N > 0; })); +} + +TEST(ArrayRefTest, TakeBack) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef<int> AR1(TheNumbers); + ArrayRef<int> AR2(AR1.end() - 1, 1); + EXPECT_TRUE(AR1.take_back().equals(AR2)); +} + +TEST(ArrayRefTest, TakeFront) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef<int> AR1(TheNumbers); + ArrayRef<int> AR2(AR1.data(), 2); + EXPECT_TRUE(AR1.take_front(2).equals(AR2)); +} + +TEST(ArrayRefTest, TakeWhile) { + static const int TheNumbers[] = {1, 3, 5, 8, 10, 11}; + ArrayRef<int> AR1(TheNumbers); + ArrayRef<int> Expected = AR1.take_front(3); + EXPECT_EQ(Expected, AR1.take_while([](const int &N) { return N % 2 == 1; })); + + EXPECT_EQ(ArrayRef<int>(), + AR1.take_while([](const int &N) { return N < 0; })); + EXPECT_EQ(AR1, AR1.take_while([](const int &N) { return N > 0; })); +} + +TEST(ArrayRefTest, TakeUntil) { + static const int TheNumbers[] = {1, 3, 5, 8, 10, 11}; + ArrayRef<int> AR1(TheNumbers); + ArrayRef<int> Expected = AR1.take_front(3); + EXPECT_EQ(Expected, AR1.take_until([](const int &N) { return N % 2 == 0; })); + + EXPECT_EQ(AR1, AR1.take_until([](const int &N) { return N < 0; })); + EXPECT_EQ(ArrayRef<int>(), + AR1.take_until([](const int &N) { return N > 0; })); +} + TEST(ArrayRefTest, Equals) { static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8}; ArrayRef<int> AR1(A1); @@ -134,7 +212,8 @@ static void ArgTest12(ArrayRef<int> A) { } TEST(ArrayRefTest, InitializerList) { - ArrayRef<int> A = { 0, 1, 2, 3, 4 }; + std::initializer_list<int> init_list = { 0, 1, 2, 3, 4 }; + ArrayRef<int> A = init_list; for (int i = 0; i < 5; ++i) EXPECT_EQ(i, A[i]); @@ -146,6 +225,14 @@ TEST(ArrayRefTest, InitializerList) { ArgTest12({1, 2}); } +TEST(ArrayRefTest, EmptyInitializerList) { + ArrayRef<int> A = {}; + EXPECT_TRUE(A.empty()); + + A = {}; + EXPECT_TRUE(A.empty()); +} + // Test that makeArrayRef works on ArrayRef (no-op) TEST(ArrayRefTest, makeArrayRef) { static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8}; diff --git a/unittests/ADT/BumpPtrListTest.cpp b/unittests/ADT/BumpPtrListTest.cpp new file mode 100644 index 000000000000..be34a71373ce --- /dev/null +++ b/unittests/ADT/BumpPtrListTest.cpp @@ -0,0 +1,243 @@ +//===- unittests/ADT/BumpPtrListTest.cpp - BumpPtrList 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/AllocatorList.h" +#include "llvm/ADT/STLExtras.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct CountsDestructors { + static unsigned NumCalls; + ~CountsDestructors() { ++NumCalls; } +}; +unsigned CountsDestructors::NumCalls = 0; + +struct MoveOnly { + int V; + explicit MoveOnly(int V) : V(V) {} + MoveOnly() = delete; + MoveOnly(MoveOnly &&X) { V = X.V; } + MoveOnly(const MoveOnly &X) = delete; + MoveOnly &operator=(MoveOnly &&X) = delete; + MoveOnly &operator=(const MoveOnly &X) = delete; +}; + +struct EmplaceOnly { + int V1, V2; + explicit EmplaceOnly(int V1, int V2) : V1(V1), V2(V2) {} + EmplaceOnly() = delete; + EmplaceOnly(EmplaceOnly &&X) = delete; + EmplaceOnly(const EmplaceOnly &X) = delete; + EmplaceOnly &operator=(EmplaceOnly &&X) = delete; + EmplaceOnly &operator=(const EmplaceOnly &X) = delete; +}; + +TEST(BumpPtrListTest, DefaultConstructor) { + BumpPtrList<int> L; + EXPECT_TRUE(L.empty()); +} + +TEST(BumpPtrListTest, pushPopBack) { + // Build a list with push_back. + BumpPtrList<int> L; + int Ns[] = {1, 3, 9, 5, 7}; + for (const int N : Ns) + L.push_back(N); + + // Use iterators to check contents. + auto I = L.begin(); + for (int N : Ns) + EXPECT_EQ(N, *I++); + EXPECT_EQ(I, L.end()); + + // Unbuild the list with pop_back. + for (int N : llvm::reverse(Ns)) { + EXPECT_EQ(N, L.back()); + L.pop_back(); + } + EXPECT_TRUE(L.empty()); +} + +TEST(BumpPtrListTest, pushPopFront) { + // Build a list with push_front. + BumpPtrList<int> L; + int Ns[] = {1, 3, 9, 5, 7}; + for (const int N : Ns) + L.push_front(N); + + // Use reverse iterators to check contents. + auto I = L.rbegin(); + for (int N : Ns) + EXPECT_EQ(N, *I++); + EXPECT_EQ(I, L.rend()); + + // Unbuild the list with pop_front. + for (int N : llvm::reverse(Ns)) { + EXPECT_EQ(N, L.front()); + L.pop_front(); + } + EXPECT_TRUE(L.empty()); +} + +TEST(BumpPtrListTest, pushBackMoveOnly) { + BumpPtrList<MoveOnly> L; + int Ns[] = {1, 3, 9, 5, 7}; + for (const int N : Ns) { + L.push_back(MoveOnly(N)); + EXPECT_EQ(N, L.back().V); + } + // Instantiate with MoveOnly. + while (!L.empty()) + L.pop_back(); +} + +TEST(BumpPtrListTest, pushFrontMoveOnly) { + BumpPtrList<MoveOnly> L; + int Ns[] = {1, 3, 9, 5, 7}; + for (const int N : Ns) { + L.push_front(MoveOnly(N)); + EXPECT_EQ(N, L.front().V); + } + // Instantiate with MoveOnly. + while (!L.empty()) + L.pop_front(); +} + +TEST(BumpPtrListTest, emplaceBack) { + BumpPtrList<EmplaceOnly> L; + int N1s[] = {1, 3, 9, 5, 7}; + int N2s[] = {7, 3, 1, 8, 2}; + for (int I = 0; I != 5; ++I) { + L.emplace_back(N1s[I], N2s[I]); + EXPECT_EQ(N1s[I], L.back().V1); + EXPECT_EQ(N2s[I], L.back().V2); + } + // Instantiate with EmplaceOnly. + while (!L.empty()) + L.pop_back(); +} + +TEST(BumpPtrListTest, emplaceFront) { + BumpPtrList<EmplaceOnly> L; + int N1s[] = {1, 3, 9, 5, 7}; + int N2s[] = {7, 3, 1, 8, 2}; + for (int I = 0; I != 5; ++I) { + L.emplace_front(N1s[I], N2s[I]); + EXPECT_EQ(N1s[I], L.front().V1); + EXPECT_EQ(N2s[I], L.front().V2); + } + // Instantiate with EmplaceOnly. + while (!L.empty()) + L.pop_front(); +} + +TEST(BumpPtrListTest, swap) { + // Build two lists with different lifetimes and swap them. + int N1s[] = {1, 3, 5, 7, 9}; + int N2s[] = {2, 4, 6, 8, 10}; + + BumpPtrList<int> L1; + L1.insert(L1.end(), std::begin(N1s), std::end(N1s)); + { + BumpPtrList<int> L2; + L2.insert(L2.end(), std::begin(N2s), std::end(N2s)); + + // Swap the lists. + L1.swap(L2); + + // Check L2's contents before it goes out of scope. + auto I = L2.begin(); + for (int N : N1s) + EXPECT_EQ(N, *I++); + EXPECT_EQ(I, L2.end()); + } + + // Check L1's contents now that L2 is out of scope (with its allocation + // blocks). + auto I = L1.begin(); + for (int N : N2s) + EXPECT_EQ(N, *I++); + EXPECT_EQ(I, L1.end()); +} + +TEST(BumpPtrListTest, clear) { + CountsDestructors::NumCalls = 0; + CountsDestructors N; + BumpPtrList<CountsDestructors> L; + L.push_back(N); + L.push_back(N); + L.push_back(N); + EXPECT_EQ(3u, L.size()); + EXPECT_EQ(0u, CountsDestructors::NumCalls); + L.pop_back(); + EXPECT_EQ(1u, CountsDestructors::NumCalls); + L.clear(); + EXPECT_EQ(3u, CountsDestructors::NumCalls); +} + +TEST(BumpPtrListTest, move) { + BumpPtrList<int> L1, L2; + L1.push_back(1); + L2.push_back(2); + L1 = std::move(L2); + EXPECT_EQ(1u, L1.size()); + EXPECT_EQ(2, L1.front()); + EXPECT_EQ(0u, L2.size()); +} + +TEST(BumpPtrListTest, moveCallsDestructors) { + CountsDestructors::NumCalls = 0; + BumpPtrList<CountsDestructors> L1, L2; + L1.emplace_back(); + EXPECT_EQ(0u, CountsDestructors::NumCalls); + L1 = std::move(L2); + EXPECT_EQ(1u, CountsDestructors::NumCalls); +} + +TEST(BumpPtrListTest, copy) { + BumpPtrList<int> L1, L2; + L1.push_back(1); + L2.push_back(2); + L1 = L2; + EXPECT_EQ(1u, L1.size()); + EXPECT_EQ(2, L1.front()); + EXPECT_EQ(1u, L2.size()); + EXPECT_EQ(2, L2.front()); +} + +TEST(BumpPtrListTest, copyCallsDestructors) { + CountsDestructors::NumCalls = 0; + BumpPtrList<CountsDestructors> L1, L2; + L1.emplace_back(); + EXPECT_EQ(0u, CountsDestructors::NumCalls); + L1 = L2; + EXPECT_EQ(1u, CountsDestructors::NumCalls); +} + +TEST(BumpPtrListTest, resetAlloc) { + // Resetting an empty list should work. + BumpPtrList<int> L; + + // Resetting an empty list that has allocated should also work. + L.resetAlloc(); + L.push_back(5); + L.erase(L.begin()); + L.resetAlloc(); + + // Resetting a non-empty list should crash. + L.push_back(5); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH(L.resetAlloc(), "Cannot reset allocator if not empty"); +#endif +} + +} // end namespace diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index ca1644b5346e..738f6efe92d6 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -9,19 +9,27 @@ set(ADTSources ArrayRefTest.cpp BitmaskEnumTest.cpp BitVectorTest.cpp + BumpPtrListTest.cpp DAGDeltaAlgorithmTest.cpp DeltaAlgorithmTest.cpp DenseMapTest.cpp DenseSetTest.cpp + DepthFirstIteratorTest.cpp FoldingSet.cpp FunctionRefTest.cpp HashingTest.cpp - ilistTest.cpp + IListBaseTest.cpp + IListIteratorTest.cpp + IListNodeBaseTest.cpp + IListNodeTest.cpp + IListSentinelTest.cpp + IListTest.cpp ImmutableMapTest.cpp ImmutableSetTest.cpp IntEqClassesTest.cpp IntervalMapTest.cpp IntrusiveRefCntPtrTest.cpp + IteratorTest.cpp MakeUniqueTest.cpp MapVectorTest.cpp OptionalTest.cpp @@ -33,17 +41,23 @@ set(ADTSources PostOrderIteratorTest.cpp PriorityWorklistTest.cpp RangeAdapterTest.cpp + ReverseIterationTest.cpp SCCIteratorTest.cpp + STLExtrasTest.cpp + ScopeExitTest.cpp SequenceTest.cpp SetVectorTest.cpp + SimpleIListTest.cpp SmallPtrSetTest.cpp SmallStringTest.cpp SmallVectorTest.cpp SparseBitVectorTest.cpp SparseMultiSetTest.cpp SparseSetTest.cpp + StringExtrasTest.cpp StringMapTest.cpp StringRefTest.cpp + StringSwitchTest.cpp TinyPtrVectorTest.cpp TripleTest.cpp TwineTest.cpp diff --git a/unittests/ADT/DenseMapTest.cpp b/unittests/ADT/DenseMapTest.cpp index db00f8cf8e57..80f0462bc8fb 100644 --- a/unittests/ADT/DenseMapTest.cpp +++ b/unittests/ADT/DenseMapTest.cpp @@ -496,55 +496,6 @@ 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 { @@ -619,4 +570,14 @@ TEST(DenseMapCustomTest, SmallDenseMapGrowTest) { EXPECT_TRUE(map.find(32) == map.end()); } +TEST(DenseMapCustomTest, TryEmplaceTest) { + DenseMap<int, std::unique_ptr<int>> Map; + std::unique_ptr<int> P(new int(2)); + auto Try1 = Map.try_emplace(0, new int(1)); + EXPECT_TRUE(Try1.second); + auto Try2 = Map.try_emplace(0, std::move(P)); + EXPECT_FALSE(Try2.second); + EXPECT_EQ(Try1.first, Try2.first); + EXPECT_NE(nullptr, P); +} } diff --git a/unittests/ADT/DenseSetTest.cpp b/unittests/ADT/DenseSetTest.cpp index 5952353034fd..4d5a82902f0e 100644 --- a/unittests/ADT/DenseSetTest.cpp +++ b/unittests/ADT/DenseSetTest.cpp @@ -7,19 +7,16 @@ // //===----------------------------------------------------------------------===// -#include "gtest/gtest.h" #include "llvm/ADT/DenseSet.h" +#include "gtest/gtest.h" +#include <type_traits> using namespace llvm; namespace { -// Test fixture -class DenseSetTest : public testing::Test { -}; - // Test hashing with a set of only two entries. -TEST_F(DenseSetTest, DoubleEntrySetTest) { +TEST(DenseSetTest, DoubleEntrySetTest) { llvm::DenseSet<unsigned> set(2); set.insert(0); set.insert(1); @@ -42,12 +39,48 @@ struct TestDenseSetInfo { } }; -TEST(DenseSetCustomTest, FindAsTest) { - DenseSet<unsigned, TestDenseSetInfo> set; - set.insert(0); - set.insert(1); - set.insert(2); +// Test fixture +template <typename T> class DenseSetTest : public testing::Test { +protected: + T Set = GetTestSet(); +private: + static T GetTestSet() { + typename std::remove_const<T>::type Set; + Set.insert(0); + Set.insert(1); + Set.insert(2); + return Set; + } +}; + +// Register these types for testing. +typedef ::testing::Types<DenseSet<unsigned, TestDenseSetInfo>, + const DenseSet<unsigned, TestDenseSetInfo>, + SmallDenseSet<unsigned, 1, TestDenseSetInfo>, + SmallDenseSet<unsigned, 4, TestDenseSetInfo>, + const SmallDenseSet<unsigned, 4, TestDenseSetInfo>, + SmallDenseSet<unsigned, 64, TestDenseSetInfo>> + DenseSetTestTypes; +TYPED_TEST_CASE(DenseSetTest, DenseSetTestTypes); + +TYPED_TEST(DenseSetTest, InitializerList) { + TypeParam set({1, 2, 1, 4}); + EXPECT_EQ(3u, set.size()); + EXPECT_EQ(1u, set.count(1)); + EXPECT_EQ(1u, set.count(2)); + EXPECT_EQ(1u, set.count(4)); + EXPECT_EQ(0u, set.count(3)); +} + +TYPED_TEST(DenseSetTest, EmptyInitializerList) { + TypeParam set({}); + EXPECT_EQ(0u, set.size()); + EXPECT_EQ(0u, set.count(0)); +} + +TYPED_TEST(DenseSetTest, FindAsTest) { + auto &set = this->Set; // Size tests EXPECT_EQ(3u, set.size()); @@ -65,4 +98,75 @@ TEST(DenseSetCustomTest, FindAsTest) { EXPECT_TRUE(set.find_as("d") == set.end()); } +// Simple class that counts how many moves and copy happens when growing a map +struct CountCopyAndMove { + static int Move; + static int Copy; + int Value; + CountCopyAndMove(int Value) : Value(Value) {} + + CountCopyAndMove(const CountCopyAndMove &RHS) { + Value = RHS.Value; + Copy++; + } + CountCopyAndMove &operator=(const CountCopyAndMove &RHS) { + Value = RHS.Value; + Copy++; + return *this; + } + CountCopyAndMove(CountCopyAndMove &&RHS) { + Value = RHS.Value; + Move++; + } + CountCopyAndMove &operator=(const CountCopyAndMove &&RHS) { + Value = RHS.Value; + Move++; + return *this; + } +}; +int CountCopyAndMove::Copy = 0; +int CountCopyAndMove::Move = 0; +} // anonymous namespace + +namespace llvm { +// Specialization required to insert a CountCopyAndMove into a DenseSet. +template <> struct DenseMapInfo<CountCopyAndMove> { + static inline CountCopyAndMove getEmptyKey() { return CountCopyAndMove(-1); }; + static inline CountCopyAndMove getTombstoneKey() { + return CountCopyAndMove(-2); + }; + static unsigned getHashValue(const CountCopyAndMove &Val) { + return Val.Value; + } + static bool isEqual(const CountCopyAndMove &LHS, + const CountCopyAndMove &RHS) { + return LHS.Value == RHS.Value; + } +}; +} + +namespace { +// Make sure reserve actually gives us enough buckets to insert N items +// without increasing allocation size. +TEST(DenseSetCustomTest, 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 + // DenseMa. 66 is a value just above the 64 default init. + for (auto Size : {1, 2, 48, 66}) { + DenseSet<CountCopyAndMove> Set; + Set.reserve(Size); + unsigned MemorySize = Set.getMemorySize(); + CountCopyAndMove::Copy = 0; + CountCopyAndMove::Move = 0; + for (int i = 0; i < Size; ++i) + Set.insert(CountCopyAndMove(i)); + // Check that we didn't grow + EXPECT_EQ(MemorySize, Set.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); + } +} } diff --git a/unittests/ADT/DepthFirstIteratorTest.cpp b/unittests/ADT/DepthFirstIteratorTest.cpp new file mode 100644 index 000000000000..463d6928bd5c --- /dev/null +++ b/unittests/ADT/DepthFirstIteratorTest.cpp @@ -0,0 +1,54 @@ +//=== llvm/unittest/ADT/DepthFirstIteratorTest.cpp - DFS iterator tests ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestGraph.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace llvm { + +template <typename T> struct CountedSet { + typedef typename SmallPtrSet<T, 4>::iterator iterator; + + SmallPtrSet<T, 4> S; + int InsertVisited = 0; + + std::pair<iterator, bool> insert(const T &Item) { + InsertVisited++; + return S.insert(Item); + } + + size_t count(const T &Item) const { return S.count(Item); } + + void completed(T) { } +}; + +template <typename T> class df_iterator_storage<CountedSet<T>, true> { +public: + df_iterator_storage(CountedSet<T> &VSet) : Visited(VSet) {} + + CountedSet<T> &Visited; +}; + +TEST(DepthFirstIteratorTest, ActuallyUpdateIterator) { + typedef CountedSet<Graph<3>::NodeType *> StorageT; + typedef df_iterator<Graph<3>, StorageT, true> DFIter; + + Graph<3> G; + G.AddEdge(0, 1); + G.AddEdge(0, 2); + StorageT S; + for (auto N : make_range(DFIter::begin(G, S), DFIter::end(G, S))) + (void)N; + + EXPECT_EQ(3, S.InsertVisited); +} +} diff --git a/unittests/ADT/FoldingSet.cpp b/unittests/ADT/FoldingSet.cpp index 927ef313cb93..696463881195 100644 --- a/unittests/ADT/FoldingSet.cpp +++ b/unittests/ADT/FoldingSet.cpp @@ -35,6 +35,27 @@ TEST(FoldingSetTest, UnalignedStringTest) { EXPECT_EQ(a.ComputeHash(), b.ComputeHash()); } +TEST(FoldingSetTest, LongLongComparison) { + struct LongLongContainer : FoldingSetNode { + unsigned long long A, B; + LongLongContainer(unsigned long long A, unsigned long long B) + : A(A), B(B) {} + void Profile(FoldingSetNodeID &ID) const { + ID.AddInteger(A); + ID.AddInteger(B); + } + }; + + LongLongContainer C1((1ULL << 32) + 1, 1ULL); + LongLongContainer C2(1ULL, (1ULL << 32) + 1); + + FoldingSet<LongLongContainer> Set; + + EXPECT_EQ(&C1, Set.GetOrInsertNode(&C1)); + EXPECT_EQ(&C2, Set.GetOrInsertNode(&C2)); + EXPECT_EQ(2U, Set.size()); +} + struct TrivialPair : public FoldingSetNode { unsigned Key = 0; unsigned Value = 0; diff --git a/unittests/ADT/IListBaseTest.cpp b/unittests/ADT/IListBaseTest.cpp new file mode 100644 index 000000000000..3b8ede801c56 --- /dev/null +++ b/unittests/ADT/IListBaseTest.cpp @@ -0,0 +1,166 @@ +//===- unittests/ADT/IListBaseTest.cpp - ilist_base 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/ilist_base.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +// Test fixture. +template <typename T> class IListBaseTest : public ::testing::Test {}; + +// Test variants with the same test. +typedef ::testing::Types<ilist_base<false>, ilist_base<true>> + IListBaseTestTypes; +TYPED_TEST_CASE(IListBaseTest, IListBaseTestTypes); + +TYPED_TEST(IListBaseTest, insertBeforeImpl) { + typedef TypeParam list_base_type; + typedef typename list_base_type::node_base_type node_base_type; + + node_base_type S, A, B; + + // [S] <-> [S] + S.setPrev(&S); + S.setNext(&S); + + // [S] <-> A <-> [S] + list_base_type::insertBeforeImpl(S, A); + EXPECT_EQ(&A, S.getPrev()); + EXPECT_EQ(&S, A.getPrev()); + EXPECT_EQ(&A, S.getNext()); + EXPECT_EQ(&S, A.getNext()); + + // [S] <-> A <-> B <-> [S] + list_base_type::insertBeforeImpl(S, B); + EXPECT_EQ(&B, S.getPrev()); + EXPECT_EQ(&A, B.getPrev()); + EXPECT_EQ(&S, A.getPrev()); + EXPECT_EQ(&A, S.getNext()); + EXPECT_EQ(&B, A.getNext()); + EXPECT_EQ(&S, B.getNext()); +} + +TYPED_TEST(IListBaseTest, removeImpl) { + typedef TypeParam list_base_type; + typedef typename list_base_type::node_base_type node_base_type; + + node_base_type S, A, B; + + // [S] <-> A <-> B <-> [S] + S.setPrev(&S); + S.setNext(&S); + list_base_type::insertBeforeImpl(S, A); + list_base_type::insertBeforeImpl(S, B); + + // [S] <-> B <-> [S] + list_base_type::removeImpl(A); + EXPECT_EQ(&B, S.getPrev()); + EXPECT_EQ(&S, B.getPrev()); + EXPECT_EQ(&B, S.getNext()); + EXPECT_EQ(&S, B.getNext()); + EXPECT_EQ(nullptr, A.getPrev()); + EXPECT_EQ(nullptr, A.getNext()); + + // [S] <-> [S] + list_base_type::removeImpl(B); + EXPECT_EQ(&S, S.getPrev()); + EXPECT_EQ(&S, S.getNext()); + EXPECT_EQ(nullptr, B.getPrev()); + EXPECT_EQ(nullptr, B.getNext()); +} + +TYPED_TEST(IListBaseTest, removeRangeImpl) { + typedef TypeParam list_base_type; + typedef typename list_base_type::node_base_type node_base_type; + + node_base_type S, A, B, C, D; + + // [S] <-> A <-> B <-> C <-> D <-> [S] + S.setPrev(&S); + S.setNext(&S); + list_base_type::insertBeforeImpl(S, A); + list_base_type::insertBeforeImpl(S, B); + list_base_type::insertBeforeImpl(S, C); + list_base_type::insertBeforeImpl(S, D); + + // [S] <-> A <-> D <-> [S] + list_base_type::removeRangeImpl(B, D); + EXPECT_EQ(&D, S.getPrev()); + EXPECT_EQ(&A, D.getPrev()); + EXPECT_EQ(&S, A.getPrev()); + EXPECT_EQ(&A, S.getNext()); + EXPECT_EQ(&D, A.getNext()); + EXPECT_EQ(&S, D.getNext()); + EXPECT_EQ(nullptr, B.getPrev()); + EXPECT_EQ(nullptr, C.getNext()); +} + +TYPED_TEST(IListBaseTest, removeRangeImplAllButSentinel) { + typedef TypeParam list_base_type; + typedef typename list_base_type::node_base_type node_base_type; + + node_base_type S, A, B; + + // [S] <-> A <-> B <-> [S] + S.setPrev(&S); + S.setNext(&S); + list_base_type::insertBeforeImpl(S, A); + list_base_type::insertBeforeImpl(S, B); + + // [S] <-> [S] + list_base_type::removeRangeImpl(A, S); + EXPECT_EQ(&S, S.getPrev()); + EXPECT_EQ(&S, S.getNext()); + EXPECT_EQ(nullptr, A.getPrev()); + EXPECT_EQ(nullptr, B.getNext()); +} + +TYPED_TEST(IListBaseTest, transferBeforeImpl) { + typedef TypeParam list_base_type; + typedef typename list_base_type::node_base_type node_base_type; + + node_base_type S1, S2, A, B, C, D, E; + + // [S1] <-> A <-> B <-> C <-> [S1] + S1.setPrev(&S1); + S1.setNext(&S1); + list_base_type::insertBeforeImpl(S1, A); + list_base_type::insertBeforeImpl(S1, B); + list_base_type::insertBeforeImpl(S1, C); + + // [S2] <-> D <-> E <-> [S2] + S2.setPrev(&S2); + S2.setNext(&S2); + list_base_type::insertBeforeImpl(S2, D); + list_base_type::insertBeforeImpl(S2, E); + + // [S1] <-> C <-> [S1] + list_base_type::transferBeforeImpl(D, A, C); + EXPECT_EQ(&C, S1.getPrev()); + EXPECT_EQ(&S1, C.getPrev()); + EXPECT_EQ(&C, S1.getNext()); + EXPECT_EQ(&S1, C.getNext()); + + // [S2] <-> A <-> B <-> D <-> E <-> [S2] + EXPECT_EQ(&E, S2.getPrev()); + EXPECT_EQ(&D, E.getPrev()); + EXPECT_EQ(&B, D.getPrev()); + EXPECT_EQ(&A, B.getPrev()); + EXPECT_EQ(&S2, A.getPrev()); + EXPECT_EQ(&A, S2.getNext()); + EXPECT_EQ(&B, A.getNext()); + EXPECT_EQ(&D, B.getNext()); + EXPECT_EQ(&E, D.getNext()); + EXPECT_EQ(&S2, E.getNext()); +} + +} // end namespace diff --git a/unittests/ADT/IListIteratorTest.cpp b/unittests/ADT/IListIteratorTest.cpp new file mode 100644 index 000000000000..ddcab781b9ba --- /dev/null +++ b/unittests/ADT/IListIteratorTest.cpp @@ -0,0 +1,134 @@ +//===- unittests/ADT/IListIteratorTest.cpp - ilist_iterator 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/simple_ilist.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct Node : ilist_node<Node> {}; + +TEST(IListIteratorTest, DefaultConstructor) { + simple_ilist<Node>::iterator I; + simple_ilist<Node>::reverse_iterator RI; + simple_ilist<Node>::const_iterator CI; + simple_ilist<Node>::const_reverse_iterator CRI; + EXPECT_EQ(nullptr, I.getNodePtr()); + EXPECT_EQ(nullptr, CI.getNodePtr()); + EXPECT_EQ(nullptr, RI.getNodePtr()); + EXPECT_EQ(nullptr, CRI.getNodePtr()); + EXPECT_EQ(I, I); + EXPECT_EQ(I, CI); + EXPECT_EQ(CI, I); + EXPECT_EQ(CI, CI); + EXPECT_EQ(RI, RI); + EXPECT_EQ(RI, CRI); + EXPECT_EQ(CRI, RI); + EXPECT_EQ(CRI, CRI); + EXPECT_EQ(I, RI.getReverse()); + EXPECT_EQ(RI, I.getReverse()); +} + +TEST(IListIteratorTest, Empty) { + simple_ilist<Node> L; + + // Check iterators of L. + EXPECT_EQ(L.begin(), L.end()); + EXPECT_EQ(L.rbegin(), L.rend()); + + // Reverse of end should be rend (since the sentinel sits on both sides). + EXPECT_EQ(L.end(), L.rend().getReverse()); + EXPECT_EQ(L.rend(), L.end().getReverse()); + + // Iterators shouldn't match default constructors. + simple_ilist<Node>::iterator I; + simple_ilist<Node>::reverse_iterator RI; + EXPECT_NE(I, L.begin()); + EXPECT_NE(I, L.end()); + EXPECT_NE(RI, L.rbegin()); + EXPECT_NE(RI, L.rend()); +} + +TEST(IListIteratorTest, OneNodeList) { + simple_ilist<Node> L; + Node A; + L.insert(L.end(), A); + + // Check address of reference. + EXPECT_EQ(&A, &*L.begin()); + EXPECT_EQ(&A, &*L.rbegin()); + + // Check that the handle matches. + EXPECT_EQ(L.rbegin().getNodePtr(), L.begin().getNodePtr()); + + // Check iteration. + EXPECT_EQ(L.end(), ++L.begin()); + EXPECT_EQ(L.begin(), --L.end()); + EXPECT_EQ(L.rend(), ++L.rbegin()); + EXPECT_EQ(L.rbegin(), --L.rend()); + + // Check conversions. + EXPECT_EQ(L.rbegin(), L.begin().getReverse()); + EXPECT_EQ(L.begin(), L.rbegin().getReverse()); +} + +TEST(IListIteratorTest, TwoNodeList) { + simple_ilist<Node> L; + Node A, B; + L.insert(L.end(), A); + L.insert(L.end(), B); + + // Check order. + EXPECT_EQ(&A, &*L.begin()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(L.end(), ++++L.begin()); + EXPECT_EQ(&B, &*L.rbegin()); + EXPECT_EQ(&A, &*++L.rbegin()); + EXPECT_EQ(L.rend(), ++++L.rbegin()); + + // Check conversions. + EXPECT_EQ(++L.rbegin(), L.begin().getReverse()); + EXPECT_EQ(L.rbegin(), (++L.begin()).getReverse()); + EXPECT_EQ(++L.begin(), L.rbegin().getReverse()); + EXPECT_EQ(L.begin(), (++L.rbegin()).getReverse()); +} + +TEST(IListIteratorTest, CheckEraseForward) { + simple_ilist<Node> L; + Node A, B; + L.insert(L.end(), A); + L.insert(L.end(), B); + + // Erase nodes. + auto I = L.begin(); + EXPECT_EQ(&A, &*I); + L.remove(*I++); + EXPECT_EQ(&B, &*I); + L.remove(*I++); + EXPECT_EQ(L.end(), I); +} + +TEST(IListIteratorTest, CheckEraseReverse) { + simple_ilist<Node> L; + Node A, B; + L.insert(L.end(), A); + L.insert(L.end(), B); + + // Erase nodes. + auto RI = L.rbegin(); + EXPECT_EQ(&B, &*RI); + L.remove(*RI++); + EXPECT_EQ(&A, &*RI); + L.remove(*RI++); + EXPECT_EQ(L.rend(), RI); +} + +} // end namespace diff --git a/unittests/ADT/IListNodeBaseTest.cpp b/unittests/ADT/IListNodeBaseTest.cpp new file mode 100644 index 000000000000..8819ab118a59 --- /dev/null +++ b/unittests/ADT/IListNodeBaseTest.cpp @@ -0,0 +1,100 @@ +//===- unittests/ADT/IListNodeBaseTest.cpp - ilist_node_base 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/ilist_node_base.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +typedef ilist_node_base<false> RawNode; +typedef ilist_node_base<true> TrackingNode; + +TEST(IListNodeBaseTest, DefaultConstructor) { + RawNode A; + EXPECT_EQ(nullptr, A.getPrev()); + EXPECT_EQ(nullptr, A.getNext()); + EXPECT_FALSE(A.isKnownSentinel()); + + TrackingNode TA; + EXPECT_EQ(nullptr, TA.getPrev()); + EXPECT_EQ(nullptr, TA.getNext()); + EXPECT_FALSE(TA.isKnownSentinel()); + EXPECT_FALSE(TA.isSentinel()); +} + +TEST(IListNodeBaseTest, setPrevAndNext) { + RawNode A, B, C; + A.setPrev(&B); + EXPECT_EQ(&B, A.getPrev()); + EXPECT_EQ(nullptr, A.getNext()); + EXPECT_EQ(nullptr, B.getPrev()); + EXPECT_EQ(nullptr, B.getNext()); + EXPECT_EQ(nullptr, C.getPrev()); + EXPECT_EQ(nullptr, C.getNext()); + + A.setNext(&C); + EXPECT_EQ(&B, A.getPrev()); + EXPECT_EQ(&C, A.getNext()); + EXPECT_EQ(nullptr, B.getPrev()); + EXPECT_EQ(nullptr, B.getNext()); + EXPECT_EQ(nullptr, C.getPrev()); + EXPECT_EQ(nullptr, C.getNext()); + + TrackingNode TA, TB, TC; + TA.setPrev(&TB); + EXPECT_EQ(&TB, TA.getPrev()); + EXPECT_EQ(nullptr, TA.getNext()); + EXPECT_EQ(nullptr, TB.getPrev()); + EXPECT_EQ(nullptr, TB.getNext()); + EXPECT_EQ(nullptr, TC.getPrev()); + EXPECT_EQ(nullptr, TC.getNext()); + + TA.setNext(&TC); + EXPECT_EQ(&TB, TA.getPrev()); + EXPECT_EQ(&TC, TA.getNext()); + EXPECT_EQ(nullptr, TB.getPrev()); + EXPECT_EQ(nullptr, TB.getNext()); + EXPECT_EQ(nullptr, TC.getPrev()); + EXPECT_EQ(nullptr, TC.getNext()); +} + +TEST(IListNodeBaseTest, isKnownSentinel) { + // Without sentinel tracking. + RawNode A, B; + EXPECT_FALSE(A.isKnownSentinel()); + A.setPrev(&B); + A.setNext(&B); + EXPECT_EQ(&B, A.getPrev()); + EXPECT_EQ(&B, A.getNext()); + EXPECT_FALSE(A.isKnownSentinel()); + A.initializeSentinel(); + EXPECT_FALSE(A.isKnownSentinel()); + EXPECT_EQ(&B, A.getPrev()); + EXPECT_EQ(&B, A.getNext()); + + // With sentinel tracking. + TrackingNode TA, TB; + EXPECT_FALSE(TA.isKnownSentinel()); + EXPECT_FALSE(TA.isSentinel()); + TA.setPrev(&TB); + TA.setNext(&TB); + EXPECT_EQ(&TB, TA.getPrev()); + EXPECT_EQ(&TB, TA.getNext()); + EXPECT_FALSE(TA.isKnownSentinel()); + EXPECT_FALSE(TA.isSentinel()); + TA.initializeSentinel(); + EXPECT_TRUE(TA.isKnownSentinel()); + EXPECT_TRUE(TA.isSentinel()); + EXPECT_EQ(&TB, TA.getPrev()); + EXPECT_EQ(&TB, TA.getNext()); +} + +} // end namespace diff --git a/unittests/ADT/IListNodeTest.cpp b/unittests/ADT/IListNodeTest.cpp new file mode 100644 index 000000000000..51bebc21ce65 --- /dev/null +++ b/unittests/ADT/IListNodeTest.cpp @@ -0,0 +1,70 @@ +//===- unittests/ADT/IListNodeTest.cpp - ilist_node 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/ilist_node.h" +#include "gtest/gtest.h" +#include <type_traits> + +using namespace llvm; +using namespace llvm::ilist_detail; + +namespace { + +struct Node; + +struct TagA {}; +struct TagB {}; + +TEST(IListNodeTest, Options) { + static_assert( + std::is_same<compute_node_options<Node>::type, + compute_node_options<Node, ilist_tag<void>>::type>::value, + "default tag is void"); + static_assert( + !std::is_same<compute_node_options<Node, ilist_tag<TagA>>::type, + compute_node_options<Node, ilist_tag<void>>::type>::value, + "default tag is void, different from TagA"); + static_assert( + !std::is_same<compute_node_options<Node, ilist_tag<TagA>>::type, + compute_node_options<Node, ilist_tag<TagB>>::type>::value, + "TagA is not TagB"); + static_assert( + std::is_same< + compute_node_options<Node, ilist_sentinel_tracking<false>>::type, + compute_node_options<Node, ilist_sentinel_tracking<false>, + ilist_tag<void>>::type>::value, + "default tag is void, even with sentinel tracking off"); + static_assert( + std::is_same< + compute_node_options<Node, ilist_sentinel_tracking<false>>::type, + compute_node_options<Node, ilist_tag<void>, + ilist_sentinel_tracking<false>>::type>::value, + "order shouldn't matter"); + static_assert( + std::is_same< + compute_node_options<Node, ilist_sentinel_tracking<true>>::type, + compute_node_options<Node, ilist_sentinel_tracking<true>, + ilist_tag<void>>::type>::value, + "default tag is void, even with sentinel tracking on"); + static_assert( + std::is_same< + compute_node_options<Node, ilist_sentinel_tracking<true>>::type, + compute_node_options<Node, ilist_tag<void>, + ilist_sentinel_tracking<true>>::type>::value, + "order shouldn't matter"); + static_assert( + std::is_same< + compute_node_options<Node, ilist_sentinel_tracking<true>, + ilist_tag<TagA>>::type, + compute_node_options<Node, ilist_tag<TagA>, + ilist_sentinel_tracking<true>>::type>::value, + "order shouldn't matter with real tags"); +} + +} // end namespace diff --git a/unittests/ADT/IListSentinelTest.cpp b/unittests/ADT/IListSentinelTest.cpp new file mode 100644 index 000000000000..bd60c909de78 --- /dev/null +++ b/unittests/ADT/IListSentinelTest.cpp @@ -0,0 +1,63 @@ +//===- unittests/ADT/IListSentinelTest.cpp - ilist_sentinel 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/ilist_node.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +template <class T, class... Options> struct PickSentinel { + typedef ilist_sentinel< + typename ilist_detail::compute_node_options<T, Options...>::type> + type; +}; + +class Node : public ilist_node<Node> {}; +class TrackingNode : public ilist_node<Node, ilist_sentinel_tracking<true>> {}; +typedef PickSentinel<Node>::type Sentinel; +typedef PickSentinel<Node, ilist_sentinel_tracking<true>>::type + TrackingSentinel; +typedef PickSentinel<Node, ilist_sentinel_tracking<false>>::type + NoTrackingSentinel; + +struct LocalAccess : ilist_detail::NodeAccess { + using NodeAccess::getPrev; + using NodeAccess::getNext; +}; + +TEST(IListSentinelTest, DefaultConstructor) { + Sentinel S; + EXPECT_EQ(&S, LocalAccess::getPrev(S)); + EXPECT_EQ(&S, LocalAccess::getNext(S)); +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + EXPECT_TRUE(S.isKnownSentinel()); +#else + EXPECT_FALSE(S.isKnownSentinel()); +#endif + + TrackingSentinel TS; + NoTrackingSentinel NTS; + EXPECT_TRUE(TS.isSentinel()); + EXPECT_TRUE(TS.isKnownSentinel()); + EXPECT_FALSE(NTS.isKnownSentinel()); +} + +TEST(IListSentinelTest, NormalNodeIsNotKnownSentinel) { + Node N; + EXPECT_EQ(nullptr, LocalAccess::getPrev(N)); + EXPECT_EQ(nullptr, LocalAccess::getNext(N)); + EXPECT_FALSE(N.isKnownSentinel()); + + TrackingNode TN; + EXPECT_FALSE(TN.isSentinel()); +} + +} // end namespace diff --git a/unittests/ADT/IListTest.cpp b/unittests/ADT/IListTest.cpp new file mode 100644 index 000000000000..0dee4c10f68f --- /dev/null +++ b/unittests/ADT/IListTest.cpp @@ -0,0 +1,276 @@ +//===- unittests/ADT/IListTest.cpp - ilist 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/ilist.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ilist_node.h" +#include "gtest/gtest.h" +#include <ostream> + +using namespace llvm; + +namespace { + +struct Node : ilist_node<Node> { + int Value; + + Node() {} + Node(int Value) : Value(Value) {} + Node(const Node&) = default; + ~Node() { Value = -1; } +}; + +TEST(IListTest, Basic) { + ilist<Node> List; + List.push_back(new Node(1)); + EXPECT_EQ(1, List.back().Value); + EXPECT_EQ(nullptr, List.getPrevNode(List.back())); + EXPECT_EQ(nullptr, List.getNextNode(List.back())); + + List.push_back(new Node(2)); + EXPECT_EQ(2, List.back().Value); + EXPECT_EQ(2, List.getNextNode(List.front())->Value); + EXPECT_EQ(1, List.getPrevNode(List.back())->Value); + + const ilist<Node> &ConstList = List; + EXPECT_EQ(2, ConstList.back().Value); + EXPECT_EQ(2, ConstList.getNextNode(ConstList.front())->Value); + EXPECT_EQ(1, ConstList.getPrevNode(ConstList.back())->Value); +} + +TEST(IListTest, cloneFrom) { + Node L1Nodes[] = {Node(0), Node(1)}; + Node L2Nodes[] = {Node(0), Node(1)}; + ilist<Node> L1, L2, L3; + + // Build L1 from L1Nodes. + L1.push_back(&L1Nodes[0]); + L1.push_back(&L1Nodes[1]); + + // Build L2 from L2Nodes, based on L1 nodes. + L2.cloneFrom(L1, [&](const Node &N) { return &L2Nodes[N.Value]; }); + + // Add a node to L3 to be deleted, and then rebuild L3 by copying L1. + L3.push_back(new Node(7)); + L3.cloneFrom(L1, [](const Node &N) { return new Node(N); }); + + EXPECT_EQ(2u, L1.size()); + EXPECT_EQ(&L1Nodes[0], &L1.front()); + EXPECT_EQ(&L1Nodes[1], &L1.back()); + EXPECT_EQ(2u, L2.size()); + EXPECT_EQ(&L2Nodes[0], &L2.front()); + EXPECT_EQ(&L2Nodes[1], &L2.back()); + EXPECT_EQ(2u, L3.size()); + EXPECT_EQ(0, L3.front().Value); + EXPECT_EQ(1, L3.back().Value); + + // Don't free nodes on the stack. + L1.clearAndLeakNodesUnsafely(); + L2.clearAndLeakNodesUnsafely(); +} + +TEST(IListTest, SpliceOne) { + ilist<Node> List; + List.push_back(new Node(1)); + + // The single-element splice operation supports noops. + List.splice(List.begin(), List, List.begin()); + EXPECT_EQ(1u, List.size()); + EXPECT_EQ(1, List.front().Value); + EXPECT_TRUE(std::next(List.begin()) == List.end()); + + // Altenative noop. Move the first element behind itself. + List.push_back(new Node(2)); + List.push_back(new Node(3)); + List.splice(std::next(List.begin()), List, List.begin()); + EXPECT_EQ(3u, List.size()); + EXPECT_EQ(1, List.front().Value); + EXPECT_EQ(2, std::next(List.begin())->Value); + EXPECT_EQ(3, List.back().Value); +} + +TEST(IListTest, SpliceSwap) { + ilist<Node> L; + Node N0(0); + Node N1(1); + L.insert(L.end(), &N0); + L.insert(L.end(), &N1); + EXPECT_EQ(0, L.front().Value); + EXPECT_EQ(1, L.back().Value); + + L.splice(L.begin(), L, ++L.begin()); + EXPECT_EQ(1, L.front().Value); + EXPECT_EQ(0, L.back().Value); + + L.clearAndLeakNodesUnsafely(); +} + +TEST(IListTest, SpliceSwapOtherWay) { + ilist<Node> L; + Node N0(0); + Node N1(1); + L.insert(L.end(), &N0); + L.insert(L.end(), &N1); + EXPECT_EQ(0, L.front().Value); + EXPECT_EQ(1, L.back().Value); + + L.splice(L.end(), L, L.begin()); + EXPECT_EQ(1, L.front().Value); + EXPECT_EQ(0, L.back().Value); + + L.clearAndLeakNodesUnsafely(); +} + +TEST(IListTest, UnsafeClear) { + ilist<Node> List; + + // Before even allocating a sentinel. + List.clearAndLeakNodesUnsafely(); + EXPECT_EQ(0u, List.size()); + + // Empty list with sentinel. + ilist<Node>::iterator E = List.end(); + List.clearAndLeakNodesUnsafely(); + EXPECT_EQ(0u, List.size()); + // The sentinel shouldn't change. + EXPECT_TRUE(E == List.end()); + + // List with contents. + List.push_back(new Node(1)); + ASSERT_EQ(1u, List.size()); + Node *N = &*List.begin(); + EXPECT_EQ(1, N->Value); + List.clearAndLeakNodesUnsafely(); + EXPECT_EQ(0u, List.size()); + ASSERT_EQ(1, N->Value); + delete N; + + // List is still functional. + List.push_back(new Node(5)); + List.push_back(new Node(6)); + ASSERT_EQ(2u, List.size()); + EXPECT_EQ(5, List.front().Value); + EXPECT_EQ(6, List.back().Value); +} + +struct Empty {}; +TEST(IListTest, HasObsoleteCustomizationTrait) { + // Negative test for HasObsoleteCustomization. + static_assert(!ilist_detail::HasObsoleteCustomization<Empty, Node>::value, + "Empty has no customizations"); +} + +struct GetNext { + Node *getNext(Node *); +}; +TEST(IListTest, HasGetNextTrait) { + static_assert(ilist_detail::HasGetNext<GetNext, Node>::value, + "GetNext has a getNext(Node*)"); + static_assert(ilist_detail::HasObsoleteCustomization<GetNext, Node>::value, + "Empty should be obsolete because of getNext()"); + + // Negative test for HasGetNext. + static_assert(!ilist_detail::HasGetNext<Empty, Node>::value, + "Empty does not have a getNext(Node*)"); +} + +struct CreateSentinel { + Node *createSentinel(); +}; +TEST(IListTest, HasCreateSentinelTrait) { + static_assert(ilist_detail::HasCreateSentinel<CreateSentinel>::value, + "CreateSentinel has a getNext(Node*)"); + static_assert( + ilist_detail::HasObsoleteCustomization<CreateSentinel, Node>::value, + "Empty should be obsolete because of createSentinel()"); + + // Negative test for HasCreateSentinel. + static_assert(!ilist_detail::HasCreateSentinel<Empty>::value, + "Empty does not have a createSentinel()"); +} + +struct NodeWithCallback : ilist_node<NodeWithCallback> { + int Value = 0; + bool IsInList = false; + bool WasTransferred = false; + + NodeWithCallback() = default; + NodeWithCallback(int Value) : Value(Value) {} + NodeWithCallback(const NodeWithCallback &) = delete; +}; + +} // end namespace + +namespace llvm { +template <> struct ilist_callback_traits<NodeWithCallback> { + void addNodeToList(NodeWithCallback *N) { N->IsInList = true; } + void removeNodeFromList(NodeWithCallback *N) { N->IsInList = false; } + template <class Iterator> + void transferNodesFromList(ilist_callback_traits &Other, Iterator First, + Iterator Last) { + for (; First != Last; ++First) { + First->WasTransferred = true; + Other.removeNodeFromList(&*First); + addNodeToList(&*First); + } + } +}; +} // end namespace llvm + +namespace { + +TEST(IListTest, addNodeToList) { + ilist<NodeWithCallback> L1, L2; + NodeWithCallback N(7); + ASSERT_FALSE(N.IsInList); + ASSERT_FALSE(N.WasTransferred); + + L1.insert(L1.begin(), &N); + ASSERT_EQ(1u, L1.size()); + ASSERT_EQ(&N, &L1.front()); + ASSERT_TRUE(N.IsInList); + ASSERT_FALSE(N.WasTransferred); + + L2.splice(L2.end(), L1); + ASSERT_EQ(&N, &L2.front()); + ASSERT_TRUE(N.IsInList); + ASSERT_TRUE(N.WasTransferred); + + L1.remove(&N); + ASSERT_EQ(0u, L1.size()); + ASSERT_FALSE(N.IsInList); + ASSERT_TRUE(N.WasTransferred); +} + +struct PrivateNode : private ilist_node<PrivateNode> { + friend struct llvm::ilist_detail::NodeAccess; + + int Value = 0; + + PrivateNode() = default; + PrivateNode(int Value) : Value(Value) {} + PrivateNode(const PrivateNode &) = delete; +}; + +TEST(IListTest, privateNode) { + // Instantiate various APIs to be sure they're callable when ilist_node is + // inherited privately. + ilist<PrivateNode> L; + PrivateNode N(7); + L.insert(L.begin(), &N); + ++L.begin(); + (void)*L.begin(); + (void)(L.begin() == L.end()); + + ilist<PrivateNode> L2; + L2.splice(L2.end(), L); + L2.remove(&N); +} + +} // end namespace diff --git a/unittests/ADT/IntervalMapTest.cpp b/unittests/ADT/IntervalMapTest.cpp index b5556d265ae4..11f13752f310 100644 --- a/unittests/ADT/IntervalMapTest.cpp +++ b/unittests/ADT/IntervalMapTest.cpp @@ -15,6 +15,8 @@ using namespace llvm; namespace { typedef IntervalMap<unsigned, unsigned, 4> UUMap; +typedef IntervalMap<unsigned, unsigned, 4, + IntervalMapHalfOpenInfo<unsigned>> UUHalfOpenMap; // Empty map tests TEST(IntervalMapTest, EmptyMap) { @@ -125,18 +127,63 @@ TEST(IntervalMapTest, SingleEntryMap) { EXPECT_EQ(200u, I.stop()); EXPECT_EQ(2u, I.value()); + // Shrink the interval to have a length of 1 + I.setStop(150); + ASSERT_TRUE(I.valid()); + EXPECT_EQ(150u, I.start()); + EXPECT_EQ(150u, I.stop()); + EXPECT_EQ(2u, I.value()); + I.setStop(160); ASSERT_TRUE(I.valid()); EXPECT_EQ(150u, I.start()); EXPECT_EQ(160u, I.stop()); EXPECT_EQ(2u, I.value()); + // Shrink the interval to have a length of 1 + I.setStart(160); + ASSERT_TRUE(I.valid()); + EXPECT_EQ(160u, I.start()); + EXPECT_EQ(160u, I.stop()); + EXPECT_EQ(2u, I.value()); + // Erase last elem. I.erase(); EXPECT_TRUE(map.empty()); EXPECT_EQ(0, std::distance(map.begin(), map.end())); } +// Single entry half-open map tests +TEST(IntervalMapTest, SingleEntryHalfOpenMap) { + UUHalfOpenMap::Allocator allocator; + UUHalfOpenMap map(allocator); + map.insert(100, 150, 1); + EXPECT_FALSE(map.empty()); + + UUHalfOpenMap::iterator I = map.begin(); + ASSERT_TRUE(I.valid()); + + // Shrink the interval to have a length of 1 + I.setStart(149); + ASSERT_TRUE(I.valid()); + EXPECT_EQ(149u, I.start()); + EXPECT_EQ(150u, I.stop()); + EXPECT_EQ(1u, I.value()); + + I.setStop(160); + ASSERT_TRUE(I.valid()); + EXPECT_EQ(149u, I.start()); + EXPECT_EQ(160u, I.stop()); + EXPECT_EQ(1u, I.value()); + + // Shrink the interval to have a length of 1 + I.setStop(150); + ASSERT_TRUE(I.valid()); + EXPECT_EQ(149u, I.start()); + EXPECT_EQ(150u, I.stop()); + EXPECT_EQ(1u, I.value()); +} + // Flat coalescing tests. TEST(IntervalMapTest, RootCoalescing) { UUMap::Allocator allocator; diff --git a/unittests/ADT/IntrusiveRefCntPtrTest.cpp b/unittests/ADT/IntrusiveRefCntPtrTest.cpp index c67ec130912d..143a8cc49107 100644 --- a/unittests/ADT/IntrusiveRefCntPtrTest.cpp +++ b/unittests/ADT/IntrusiveRefCntPtrTest.cpp @@ -10,30 +10,29 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "gtest/gtest.h" -namespace { -struct VirtualRefCounted : public llvm::RefCountedBaseVPTR { - virtual void f() {} -}; -} - namespace llvm { -// Run this test with valgrind to detect memory leaks. -TEST(IntrusiveRefCntPtr, RefCountedBaseVPTRCopyDoesNotLeak) { - VirtualRefCounted *V1 = new VirtualRefCounted; - IntrusiveRefCntPtr<VirtualRefCounted> R1 = V1; - VirtualRefCounted *V2 = new VirtualRefCounted(*V1); - IntrusiveRefCntPtr<VirtualRefCounted> R2 = V2; -} +namespace { +struct SimpleRefCounted : public RefCountedBase<SimpleRefCounted> { + SimpleRefCounted() { ++NumInstances; } + SimpleRefCounted(const SimpleRefCounted &) { ++NumInstances; } + ~SimpleRefCounted() { --NumInstances; } -struct SimpleRefCounted : public RefCountedBase<SimpleRefCounted> {}; + static int NumInstances; +}; +int SimpleRefCounted::NumInstances = 0; +} // anonymous namespace -// Run this test with valgrind to detect memory leaks. TEST(IntrusiveRefCntPtr, RefCountedBaseCopyDoesNotLeak) { - SimpleRefCounted *S1 = new SimpleRefCounted; - IntrusiveRefCntPtr<SimpleRefCounted> R1 = S1; - SimpleRefCounted *S2 = new SimpleRefCounted(*S1); - IntrusiveRefCntPtr<SimpleRefCounted> R2 = S2; + EXPECT_EQ(0, SimpleRefCounted::NumInstances); + { + SimpleRefCounted *S1 = new SimpleRefCounted; + IntrusiveRefCntPtr<SimpleRefCounted> R1 = S1; + SimpleRefCounted *S2 = new SimpleRefCounted(*S1); + IntrusiveRefCntPtr<SimpleRefCounted> R2 = S2; + EXPECT_EQ(2, SimpleRefCounted::NumInstances); + } + EXPECT_EQ(0, SimpleRefCounted::NumInstances); } struct InterceptRefCounted : public RefCountedBase<InterceptRefCounted> { diff --git a/unittests/Support/IteratorTest.cpp b/unittests/ADT/IteratorTest.cpp index 63dfa2a9d011..a8d5b33a0b49 100644 --- a/unittests/Support/IteratorTest.cpp +++ b/unittests/ADT/IteratorTest.cpp @@ -185,4 +185,91 @@ TEST(FilterIteratorTest, InputIterator) { EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual); } +TEST(PointerIterator, Basic) { + int A[] = {1, 2, 3, 4}; + pointer_iterator<int *> Begin(std::begin(A)), End(std::end(A)); + EXPECT_EQ(A, *Begin); + ++Begin; + EXPECT_EQ(A + 1, *Begin); + ++Begin; + EXPECT_EQ(A + 2, *Begin); + ++Begin; + EXPECT_EQ(A + 3, *Begin); + ++Begin; + EXPECT_EQ(Begin, End); +} + +TEST(PointerIterator, Const) { + int A[] = {1, 2, 3, 4}; + const pointer_iterator<int *> Begin(std::begin(A)); + EXPECT_EQ(A, *Begin); + EXPECT_EQ(A + 1, std::next(*Begin, 1)); + EXPECT_EQ(A + 2, std::next(*Begin, 2)); + EXPECT_EQ(A + 3, std::next(*Begin, 3)); + EXPECT_EQ(A + 4, std::next(*Begin, 4)); +} + +TEST(ZipIteratorTest, Basic) { + using namespace std; + const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9}; + SmallVector<bool, 6> odd{1, 1, 0, 1, 1, 1}; + const char message[] = "yynyyy\0"; + + for (auto tup : zip(pi, odd, message)) { + EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); + EXPECT_EQ(get<0>(tup) & 0x01 ? 'y' : 'n', get<2>(tup)); + } + + // note the rvalue + for (auto tup : zip(pi, SmallVector<bool, 0>{1, 1, 0, 1, 1})) { + EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); + } +} + +TEST(ZipIteratorTest, ZipFirstBasic) { + using namespace std; + const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9}; + unsigned iters = 0; + + for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) { + EXPECT_EQ(get<0>(tup), get<1>(tup) & 0x01); + iters += 1; + } + + EXPECT_EQ(iters, 4u); +} + +TEST(ZipIteratorTest, Mutability) { + using namespace std; + const SmallVector<unsigned, 4> pi{3, 1, 4, 1, 5, 9}; + char message[] = "hello zip\0"; + + for (auto tup : zip(pi, message, message)) { + EXPECT_EQ(get<1>(tup), get<2>(tup)); + get<2>(tup) = get<0>(tup) & 0x01 ? 'y' : 'n'; + } + + // note the rvalue + for (auto tup : zip(message, "yynyyyzip\0")) { + EXPECT_EQ(get<0>(tup), get<1>(tup)); + } +} + +TEST(ZipIteratorTest, ZipFirstMutability) { + using namespace std; + vector<unsigned> pi{3, 1, 4, 1, 5, 9}; + unsigned iters = 0; + + for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) { + get<1>(tup) = get<0>(tup); + iters += 1; + } + + EXPECT_EQ(iters, 4u); + + for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) { + EXPECT_EQ(get<0>(tup), get<1>(tup)); + } +} + } // anonymous namespace diff --git a/unittests/ADT/MapVectorTest.cpp b/unittests/ADT/MapVectorTest.cpp index ff8464293c79..f5b094fb627b 100644 --- a/unittests/ADT/MapVectorTest.cpp +++ b/unittests/ADT/MapVectorTest.cpp @@ -148,6 +148,15 @@ TEST(MapVectorTest, iteration_test) { } } +TEST(MapVectorTest, NonCopyable) { + MapVector<int, std::unique_ptr<int>> MV; + MV.insert(std::make_pair(1, llvm::make_unique<int>(1))); + MV.insert(std::make_pair(2, llvm::make_unique<int>(2))); + + ASSERT_EQ(MV.count(1), 1u); + ASSERT_EQ(*MV.find(2)->second, 2); +} + TEST(SmallMapVectorSmallTest, insert_pop) { SmallMapVector<int, int, 32> MV; std::pair<SmallMapVector<int, int, 32>::iterator, bool> R; @@ -257,6 +266,15 @@ TEST(SmallMapVectorSmallTest, iteration_test) { } } +TEST(SmallMapVectorSmallTest, NonCopyable) { + SmallMapVector<int, std::unique_ptr<int>, 8> MV; + MV.insert(std::make_pair(1, llvm::make_unique<int>(1))); + MV.insert(std::make_pair(2, llvm::make_unique<int>(2))); + + ASSERT_EQ(MV.count(1), 1u); + ASSERT_EQ(*MV.find(2)->second, 2); +} + TEST(SmallMapVectorLargeTest, insert_pop) { SmallMapVector<int, int, 1> MV; std::pair<SmallMapVector<int, int, 1>::iterator, bool> R; diff --git a/unittests/ADT/OptionalTest.cpp b/unittests/ADT/OptionalTest.cpp index 18b59e315818..4c0c99393d21 100644 --- a/unittests/ADT/OptionalTest.cpp +++ b/unittests/ADT/OptionalTest.cpp @@ -9,6 +9,7 @@ #include "gtest/gtest.h" #include "llvm/ADT/Optional.h" + using namespace llvm; namespace { @@ -377,17 +378,144 @@ TEST_F(OptionalTest, MoveGetValueOr) { #endif // LLVM_HAS_RVALUE_REFERENCE_THIS -TEST_F(OptionalTest, NoneComparison) { - Optional<int> o; - EXPECT_EQ(o, None); - EXPECT_EQ(None, o); - EXPECT_FALSE(o != None); - EXPECT_FALSE(None != o); - o = 3; - EXPECT_FALSE(o == None); - EXPECT_FALSE(None == o); - EXPECT_TRUE(o != None); - EXPECT_TRUE(None != o); +struct EqualTo { + template <typename T, typename U> static bool apply(const T &X, const U &Y) { + return X == Y; + } +}; + +struct NotEqualTo { + template <typename T, typename U> static bool apply(const T &X, const U &Y) { + return X != Y; + } +}; + +struct Less { + template <typename T, typename U> static bool apply(const T &X, const U &Y) { + return X < Y; + } +}; + +struct Greater { + template <typename T, typename U> static bool apply(const T &X, const U &Y) { + return X > Y; + } +}; + +struct LessEqual { + template <typename T, typename U> static bool apply(const T &X, const U &Y) { + return X <= Y; + } +}; + +struct GreaterEqual { + template <typename T, typename U> static bool apply(const T &X, const U &Y) { + return X >= Y; + } +}; + +template <typename OperatorT, typename T> +void CheckRelation(const Optional<T> &Lhs, const Optional<T> &Rhs, + bool Expected) { + EXPECT_EQ(Expected, OperatorT::apply(Lhs, Rhs)); + + if (Lhs) + EXPECT_EQ(Expected, OperatorT::apply(*Lhs, Rhs)); + else + EXPECT_EQ(Expected, OperatorT::apply(None, Rhs)); + + if (Rhs) + EXPECT_EQ(Expected, OperatorT::apply(Lhs, *Rhs)); + else + EXPECT_EQ(Expected, OperatorT::apply(Lhs, None)); +} + +struct EqualityMock {}; +const Optional<EqualityMock> NoneEq, EqualityLhs((EqualityMock())), + EqualityRhs((EqualityMock())); +bool IsEqual; + +bool operator==(const EqualityMock &Lhs, const EqualityMock &Rhs) { + EXPECT_EQ(&*EqualityLhs, &Lhs); + EXPECT_EQ(&*EqualityRhs, &Rhs); + return IsEqual; +} + +TEST_F(OptionalTest, OperatorEqual) { + CheckRelation<EqualTo>(NoneEq, NoneEq, true); + CheckRelation<EqualTo>(NoneEq, EqualityRhs, false); + CheckRelation<EqualTo>(EqualityLhs, NoneEq, false); + + IsEqual = false; + CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual); + IsEqual = true; + CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual); +} + +TEST_F(OptionalTest, OperatorNotEqual) { + CheckRelation<NotEqualTo>(NoneEq, NoneEq, false); + CheckRelation<NotEqualTo>(NoneEq, EqualityRhs, true); + CheckRelation<NotEqualTo>(EqualityLhs, NoneEq, true); + + IsEqual = false; + CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual); + IsEqual = true; + CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual); +} + +struct InequalityMock {}; +const Optional<InequalityMock> NoneIneq, InequalityLhs((InequalityMock())), + InequalityRhs((InequalityMock())); +bool IsLess; + +bool operator<(const InequalityMock &Lhs, const InequalityMock &Rhs) { + EXPECT_EQ(&*InequalityLhs, &Lhs); + EXPECT_EQ(&*InequalityRhs, &Rhs); + return IsLess; +} + +TEST_F(OptionalTest, OperatorLess) { + CheckRelation<Less>(NoneIneq, NoneIneq, false); + CheckRelation<Less>(NoneIneq, InequalityRhs, true); + CheckRelation<Less>(InequalityLhs, NoneIneq, false); + + IsLess = false; + CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess); + IsLess = true; + CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess); +} + +TEST_F(OptionalTest, OperatorGreater) { + CheckRelation<Greater>(NoneIneq, NoneIneq, false); + CheckRelation<Greater>(NoneIneq, InequalityRhs, false); + CheckRelation<Greater>(InequalityLhs, NoneIneq, true); + + IsLess = false; + CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess); + IsLess = true; + CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess); +} + +TEST_F(OptionalTest, OperatorLessEqual) { + CheckRelation<LessEqual>(NoneIneq, NoneIneq, true); + CheckRelation<LessEqual>(NoneIneq, InequalityRhs, true); + CheckRelation<LessEqual>(InequalityLhs, NoneIneq, false); + + IsLess = false; + CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess); + IsLess = true; + CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess); +} + +TEST_F(OptionalTest, OperatorGreaterEqual) { + CheckRelation<GreaterEqual>(NoneIneq, NoneIneq, true); + CheckRelation<GreaterEqual>(NoneIneq, InequalityRhs, false); + CheckRelation<GreaterEqual>(InequalityLhs, NoneIneq, true); + + IsLess = false; + CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess); + IsLess = true; + CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess); } } // end anonymous namespace diff --git a/unittests/ADT/PostOrderIteratorTest.cpp b/unittests/ADT/PostOrderIteratorTest.cpp index 1da1078c7498..17b8c4d842d3 100644 --- a/unittests/ADT/PostOrderIteratorTest.cpp +++ b/unittests/ADT/PostOrderIteratorTest.cpp @@ -21,17 +21,17 @@ TEST(PostOrderIteratorTest, Compiles) { // Tests that template specializations are kept up to date void *Null = nullptr; po_iterator_storage<std::set<void *>, false> PIS; - PIS.insertEdge(Null, Null); + PIS.insertEdge(Optional<void *>(), Null); ExtSetTy Ext; po_iterator_storage<ExtSetTy, true> PISExt(Ext); - PIS.insertEdge(Null, Null); + PIS.insertEdge(Optional<void *>(), Null); // Test above, but going through po_iterator (which inherits from template // base) BasicBlock *NullBB = nullptr; auto PI = po_end(NullBB); - PI.insertEdge(NullBB, NullBB); + PI.insertEdge(Optional<BasicBlock *>(), NullBB); auto PIExt = po_ext_end(NullBB, Ext); - PIExt.insertEdge(NullBB, NullBB); + PIExt.insertEdge(Optional<BasicBlock *>(), NullBB); } } diff --git a/unittests/ADT/RangeAdapterTest.cpp b/unittests/ADT/RangeAdapterTest.cpp index 634f5bb990d9..4c7bef53235b 100644 --- a/unittests/ADT/RangeAdapterTest.cpp +++ b/unittests/ADT/RangeAdapterTest.cpp @@ -27,26 +27,96 @@ public: ReverseOnlyVector(std::initializer_list<int> list) : Vec(list) {} typedef std::vector<int>::reverse_iterator reverse_iterator; + typedef std::vector<int>::const_reverse_iterator const_reverse_iterator; reverse_iterator rbegin() { return Vec.rbegin(); } reverse_iterator rend() { return Vec.rend(); } + const_reverse_iterator rbegin() const { return Vec.rbegin(); } + const_reverse_iterator rend() const { return Vec.rend(); } }; // A wrapper around vector which exposes begin(), end(), rbegin() and rend(). // begin() and end() don't have implementations as this ensures that we will // get a linker error if reverse() chooses begin()/end() over rbegin(), rend(). class BidirectionalVector { - std::vector<int> Vec; + mutable std::vector<int> Vec; public: BidirectionalVector(std::initializer_list<int> list) : Vec(list) {} typedef std::vector<int>::iterator iterator; + iterator begin() const; + iterator end() const; + + typedef std::vector<int>::reverse_iterator reverse_iterator; + reverse_iterator rbegin() const { return Vec.rbegin(); } + reverse_iterator rend() const { return Vec.rend(); } +}; + +/// This is the same as BidirectionalVector but with the addition of const +/// begin/rbegin methods to ensure that the type traits for has_rbegin works. +class BidirectionalVectorConsts { + std::vector<int> Vec; + +public: + BidirectionalVectorConsts(std::initializer_list<int> list) : Vec(list) {} + + typedef std::vector<int>::iterator iterator; + typedef std::vector<int>::const_iterator const_iterator; iterator begin(); iterator end(); + const_iterator begin() const; + const_iterator end() const; typedef std::vector<int>::reverse_iterator reverse_iterator; + typedef std::vector<int>::const_reverse_iterator const_reverse_iterator; reverse_iterator rbegin() { return Vec.rbegin(); } reverse_iterator rend() { return Vec.rend(); } + const_reverse_iterator rbegin() const { return Vec.rbegin(); } + const_reverse_iterator rend() const { return Vec.rend(); } +}; + +/// Check that types with custom iterators work. +class CustomIteratorVector { + mutable std::vector<int> V; + +public: + CustomIteratorVector(std::initializer_list<int> list) : V(list) {} + + typedef std::vector<int>::iterator iterator; + class reverse_iterator { + std::vector<int>::iterator I; + + public: + reverse_iterator() = default; + reverse_iterator(const reverse_iterator &) = default; + reverse_iterator &operator=(const reverse_iterator &) = default; + + explicit reverse_iterator(std::vector<int>::iterator I) : I(I) {} + + reverse_iterator &operator++() { + --I; + return *this; + } + reverse_iterator &operator--() { + ++I; + return *this; + } + int &operator*() const { return *std::prev(I); } + int *operator->() const { return &*std::prev(I); } + friend bool operator==(const reverse_iterator &L, + const reverse_iterator &R) { + return L.I == R.I; + } + friend bool operator!=(const reverse_iterator &L, + const reverse_iterator &R) { + return !(L == R); + } + }; + + iterator begin() const { return V.begin(); } + iterator end() const { return V.end(); } + reverse_iterator rbegin() const { return reverse_iterator(V.end()); } + reverse_iterator rend() const { return reverse_iterator(V.begin()); } }; template <typename R> void TestRev(const R &r) { @@ -72,12 +142,31 @@ TYPED_TEST(RangeAdapterLValueTest, TrivialOperation) { template <typename T> struct RangeAdapterRValueTest : testing::Test {}; -typedef ::testing::Types<std::vector<int>, std::list<int>, ReverseOnlyVector, - BidirectionalVector> RangeAdapterRValueTestTypes; +typedef ::testing::Types<std::vector<int>, std::list<int>, CustomIteratorVector, + ReverseOnlyVector, BidirectionalVector, + BidirectionalVectorConsts> + RangeAdapterRValueTestTypes; TYPED_TEST_CASE(RangeAdapterRValueTest, RangeAdapterRValueTestTypes); TYPED_TEST(RangeAdapterRValueTest, TrivialOperation) { TestRev(reverse(TypeParam({0, 1, 2, 3}))); } +TYPED_TEST(RangeAdapterRValueTest, HasRbegin) { + static_assert(has_rbegin<TypeParam>::value, "rbegin() should be defined"); +} + +TYPED_TEST(RangeAdapterRValueTest, RangeType) { + static_assert( + std::is_same< + decltype(reverse(*static_cast<TypeParam *>(nullptr)).begin()), + decltype(static_cast<TypeParam *>(nullptr)->rbegin())>::value, + "reverse().begin() should have the same type as rbegin()"); + static_assert( + std::is_same< + decltype(reverse(*static_cast<const TypeParam *>(nullptr)).begin()), + decltype(static_cast<const TypeParam *>(nullptr)->rbegin())>::value, + "reverse().begin() should have the same type as rbegin() [const]"); +} + } // anonymous namespace diff --git a/unittests/ADT/ReverseIterationTest.cpp b/unittests/ADT/ReverseIterationTest.cpp new file mode 100644 index 000000000000..a1fd3b26d4e3 --- /dev/null +++ b/unittests/ADT/ReverseIterationTest.cpp @@ -0,0 +1,52 @@ +//===- llvm/unittest/ADT/ReverseIterationTest.cpp ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ReverseIteration unit tests. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/ADT/SmallPtrSet.h" + +#if LLVM_ENABLE_ABI_BREAKING_CHECKS +using namespace llvm; + +TEST(ReverseIterationTest, SmallPtrSetTest) { + + SmallPtrSet<void*, 4> Set; + void *Ptrs[] = { (void*)0x1, (void*)0x2, (void*)0x3, (void*)0x4 }; + void *ReversePtrs[] = { (void*)0x4, (void*)0x3, (void*)0x2, (void*)0x1 }; + + for (auto *Ptr: Ptrs) + Set.insert(Ptr); + + // Check forward iteration. + ReverseIterate<bool>::value = false; + for (const auto &Tuple : zip(Set, Ptrs)) + ASSERT_EQ(std::get<0>(Tuple), std::get<1>(Tuple)); + + // Check operator++ (post-increment) in forward iteration. + int i = 0; + for (auto begin = Set.begin(), end = Set.end(); + begin != end; i++) + ASSERT_EQ(*begin++, Ptrs[i]); + + // Check reverse iteration. + ReverseIterate<bool>::value = true; + for (const auto &Tuple : zip(Set, ReversePtrs)) + ASSERT_EQ(std::get<0>(Tuple), std::get<1>(Tuple)); + + // Check operator++ (post-increment) in reverse iteration. + i = 0; + for (auto begin = Set.begin(), end = Set.end(); + begin != end; i++) + ASSERT_EQ(*begin++, ReversePtrs[i]); + +} +#endif diff --git a/unittests/ADT/SCCIteratorTest.cpp b/unittests/ADT/SCCIteratorTest.cpp index 597661fd8cc0..f596ea6d6b88 100644 --- a/unittests/ADT/SCCIteratorTest.cpp +++ b/unittests/ADT/SCCIteratorTest.cpp @@ -8,240 +8,14 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/SCCIterator.h" -#include "llvm/ADT/GraphTraits.h" #include "gtest/gtest.h" +#include "TestGraph.h" #include <limits.h> using namespace llvm; namespace llvm { -/// Graph<N> - A graph with N nodes. Note that N can be at most 8. -template <unsigned N> -class Graph { -private: - // Disable copying. - Graph(const Graph&); - Graph& operator=(const Graph&); - - static void ValidateIndex(unsigned Idx) { - assert(Idx < N && "Invalid node index!"); - } -public: - - /// NodeSubset - A subset of the graph's nodes. - class NodeSubset { - typedef unsigned char BitVector; // Where the limitation N <= 8 comes from. - BitVector Elements; - NodeSubset(BitVector e) : Elements(e) {} - public: - /// NodeSubset - Default constructor, creates an empty subset. - NodeSubset() : Elements(0) { - assert(N <= sizeof(BitVector)*CHAR_BIT && "Graph too big!"); - } - - /// Comparison operators. - bool operator==(const NodeSubset &other) const { - return other.Elements == this->Elements; - } - bool operator!=(const NodeSubset &other) const { - return !(*this == other); - } - - /// AddNode - Add the node with the given index to the subset. - void AddNode(unsigned Idx) { - ValidateIndex(Idx); - Elements |= 1U << Idx; - } - - /// DeleteNode - Remove the node with the given index from the subset. - void DeleteNode(unsigned Idx) { - ValidateIndex(Idx); - Elements &= ~(1U << Idx); - } - - /// count - Return true if the node with the given index is in the subset. - bool count(unsigned Idx) { - ValidateIndex(Idx); - return (Elements & (1U << Idx)) != 0; - } - - /// isEmpty - Return true if this is the empty set. - bool isEmpty() const { - return Elements == 0; - } - - /// isSubsetOf - Return true if this set is a subset of the given one. - bool isSubsetOf(const NodeSubset &other) const { - return (this->Elements | other.Elements) == other.Elements; - } - - /// Complement - Return the complement of this subset. - NodeSubset Complement() const { - return ~(unsigned)this->Elements & ((1U << N) - 1); - } - - /// Join - Return the union of this subset and the given one. - NodeSubset Join(const NodeSubset &other) const { - return this->Elements | other.Elements; - } - - /// Meet - Return the intersection of this subset and the given one. - NodeSubset Meet(const NodeSubset &other) const { - return this->Elements & other.Elements; - } - }; - - /// NodeType - Node index and set of children of the node. - typedef std::pair<unsigned, NodeSubset> NodeType; - -private: - /// Nodes - The list of nodes for this graph. - NodeType Nodes[N]; -public: - - /// Graph - Default constructor. Creates an empty graph. - Graph() { - // Let each node know which node it is. This allows us to find the start of - // the Nodes array given a pointer to any element of it. - for (unsigned i = 0; i != N; ++i) - Nodes[i].first = i; - } - - /// AddEdge - Add an edge from the node with index FromIdx to the node with - /// index ToIdx. - void AddEdge(unsigned FromIdx, unsigned ToIdx) { - ValidateIndex(FromIdx); - Nodes[FromIdx].second.AddNode(ToIdx); - } - - /// DeleteEdge - Remove the edge (if any) from the node with index FromIdx to - /// the node with index ToIdx. - void DeleteEdge(unsigned FromIdx, unsigned ToIdx) { - ValidateIndex(FromIdx); - Nodes[FromIdx].second.DeleteNode(ToIdx); - } - - /// AccessNode - Get a pointer to the node with the given index. - NodeType *AccessNode(unsigned Idx) const { - ValidateIndex(Idx); - // The constant cast is needed when working with GraphTraits, which insists - // on taking a constant Graph. - return const_cast<NodeType *>(&Nodes[Idx]); - } - - /// NodesReachableFrom - Return the set of all nodes reachable from the given - /// node. - NodeSubset NodesReachableFrom(unsigned Idx) const { - // This algorithm doesn't scale, but that doesn't matter given the small - // size of our graphs. - NodeSubset Reachable; - - // The initial node is reachable. - Reachable.AddNode(Idx); - do { - NodeSubset Previous(Reachable); - - // Add in all nodes which are children of a reachable node. - for (unsigned i = 0; i != N; ++i) - if (Previous.count(i)) - Reachable = Reachable.Join(Nodes[i].second); - - // If nothing changed then we have found all reachable nodes. - if (Reachable == Previous) - return Reachable; - - // Rinse and repeat. - } while (1); - } - - /// ChildIterator - Visit all children of a node. - class ChildIterator { - friend class Graph; - - /// FirstNode - Pointer to first node in the graph's Nodes array. - NodeType *FirstNode; - /// Children - Set of nodes which are children of this one and that haven't - /// yet been visited. - NodeSubset Children; - - ChildIterator(); // Disable default constructor. - protected: - ChildIterator(NodeType *F, NodeSubset C) : FirstNode(F), Children(C) {} - - public: - /// ChildIterator - Copy constructor. - ChildIterator(const ChildIterator& other) : FirstNode(other.FirstNode), - Children(other.Children) {} - - /// Comparison operators. - bool operator==(const ChildIterator &other) const { - return other.FirstNode == this->FirstNode && - other.Children == this->Children; - } - bool operator!=(const ChildIterator &other) const { - return !(*this == other); - } - - /// Prefix increment operator. - ChildIterator& operator++() { - // Find the next unvisited child node. - for (unsigned i = 0; i != N; ++i) - if (Children.count(i)) { - // Remove that child - it has been visited. This is the increment! - Children.DeleteNode(i); - return *this; - } - assert(false && "Incrementing end iterator!"); - return *this; // Avoid compiler warnings. - } - - /// Postfix increment operator. - ChildIterator operator++(int) { - ChildIterator Result(*this); - ++(*this); - return Result; - } - - /// Dereference operator. - NodeType *operator*() { - // Find the next unvisited child node. - for (unsigned i = 0; i != N; ++i) - if (Children.count(i)) - // Return a pointer to it. - return FirstNode + i; - assert(false && "Dereferencing end iterator!"); - return nullptr; // Avoid compiler warning. - } - }; - - /// child_begin - Return an iterator pointing to the first child of the given - /// node. - static ChildIterator child_begin(NodeType *Parent) { - return ChildIterator(Parent - Parent->first, Parent->second); - } - - /// child_end - Return the end iterator for children of the given node. - static ChildIterator child_end(NodeType *Parent) { - return ChildIterator(Parent - Parent->first, NodeSubset()); - } -}; - -template <unsigned N> -struct GraphTraits<Graph<N> > { - typedef typename Graph<N>::NodeType NodeType; - typedef typename Graph<N>::NodeType *NodeRef; - typedef typename Graph<N>::ChildIterator ChildIteratorType; - - static inline NodeType *getEntryNode(const Graph<N> &G) { return G.AccessNode(0); } - static inline ChildIteratorType child_begin(NodeType *Node) { - return Graph<N>::child_begin(Node); - } - static inline ChildIteratorType child_end(NodeType *Node) { - return Graph<N>::child_end(Node); - } -}; - TEST(SCCIteratorTest, AllSmallGraphs) { // Test SCC computation against every graph with NUM_NODES nodes or less. // Since SCC considers every node to have an implicit self-edge, we only diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp new file mode 100644 index 000000000000..f17d24f36b23 --- /dev/null +++ b/unittests/ADT/STLExtrasTest.cpp @@ -0,0 +1,311 @@ +//===- STLExtrasTest.cpp - Unit tests for STL extras ----------------------===// +// +// 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 "gtest/gtest.h" + +#include <list> +#include <vector> + +using namespace llvm; + +namespace { + +int f(rank<0>) { return 0; } +int f(rank<1>) { return 1; } +int f(rank<2>) { return 2; } +int f(rank<4>) { return 4; } + +TEST(STLExtrasTest, Rank) { + // We shouldn't get ambiguities and should select the overload of the same + // rank as the argument. + EXPECT_EQ(0, f(rank<0>())); + EXPECT_EQ(1, f(rank<1>())); + EXPECT_EQ(2, f(rank<2>())); + + // This overload is missing so we end up back at 2. + EXPECT_EQ(2, f(rank<3>())); + + // But going past 3 should work fine. + EXPECT_EQ(4, f(rank<4>())); + + // And we can even go higher and just fall back to the last overload. + EXPECT_EQ(4, f(rank<5>())); + EXPECT_EQ(4, f(rank<6>())); +} + +TEST(STLExtrasTest, EnumerateLValue) { + // Test that a simple LValue can be enumerated and gives correct results with + // multiple types, including the empty container. + std::vector<char> foo = {'a', 'b', 'c'}; + typedef std::pair<std::size_t, char> CharPairType; + std::vector<CharPairType> CharResults; + + for (auto X : llvm::enumerate(foo)) { + CharResults.emplace_back(X.Index, X.Value); + } + ASSERT_EQ(3u, CharResults.size()); + EXPECT_EQ(CharPairType(0u, 'a'), CharResults[0]); + EXPECT_EQ(CharPairType(1u, 'b'), CharResults[1]); + EXPECT_EQ(CharPairType(2u, 'c'), CharResults[2]); + + // Test a const range of a different type. + typedef std::pair<std::size_t, int> IntPairType; + std::vector<IntPairType> IntResults; + const std::vector<int> bar = {1, 2, 3}; + for (auto X : llvm::enumerate(bar)) { + IntResults.emplace_back(X.Index, X.Value); + } + ASSERT_EQ(3u, IntResults.size()); + EXPECT_EQ(IntPairType(0u, 1), IntResults[0]); + EXPECT_EQ(IntPairType(1u, 2), IntResults[1]); + EXPECT_EQ(IntPairType(2u, 3), IntResults[2]); + + // Test an empty range. + IntResults.clear(); + const std::vector<int> baz; + for (auto X : llvm::enumerate(baz)) { + IntResults.emplace_back(X.Index, X.Value); + } + EXPECT_TRUE(IntResults.empty()); +} + +TEST(STLExtrasTest, EnumerateModifyLValue) { + // Test that you can modify the underlying entries of an lvalue range through + // the enumeration iterator. + std::vector<char> foo = {'a', 'b', 'c'}; + + for (auto X : llvm::enumerate(foo)) { + ++X.Value; + } + EXPECT_EQ('b', foo[0]); + EXPECT_EQ('c', foo[1]); + EXPECT_EQ('d', foo[2]); +} + +TEST(STLExtrasTest, EnumerateRValueRef) { + // Test that an rvalue can be enumerated. + typedef std::pair<std::size_t, int> PairType; + std::vector<PairType> Results; + + auto Enumerator = llvm::enumerate(std::vector<int>{1, 2, 3}); + + for (auto X : llvm::enumerate(std::vector<int>{1, 2, 3})) { + Results.emplace_back(X.Index, X.Value); + } + + ASSERT_EQ(3u, Results.size()); + EXPECT_EQ(PairType(0u, 1), Results[0]); + EXPECT_EQ(PairType(1u, 2), Results[1]); + EXPECT_EQ(PairType(2u, 3), Results[2]); +} + +TEST(STLExtrasTest, EnumerateModifyRValue) { + // Test that when enumerating an rvalue, modification still works (even if + // this isn't terribly useful, it at least shows that we haven't snuck an + // extra const in there somewhere. + typedef std::pair<std::size_t, char> PairType; + std::vector<PairType> Results; + + for (auto X : llvm::enumerate(std::vector<char>{'1', '2', '3'})) { + ++X.Value; + Results.emplace_back(X.Index, X.Value); + } + + ASSERT_EQ(3u, Results.size()); + EXPECT_EQ(PairType(0u, '2'), Results[0]); + EXPECT_EQ(PairType(1u, '3'), Results[1]); + EXPECT_EQ(PairType(2u, '4'), Results[2]); +} + +template <bool B> struct CanMove {}; +template <> struct CanMove<false> { + CanMove(CanMove &&) = delete; + + CanMove() = default; + CanMove(const CanMove &) = default; +}; + +template <bool B> struct CanCopy {}; +template <> struct CanCopy<false> { + CanCopy(const CanCopy &) = delete; + + CanCopy() = default; + CanCopy(CanCopy &&) = default; +}; + +template <bool Moveable, bool Copyable> +struct Range : CanMove<Moveable>, CanCopy<Copyable> { + explicit Range(int &C, int &M, int &D) : C(C), M(M), D(D) {} + Range(const Range &R) : CanCopy<Copyable>(R), C(R.C), M(R.M), D(R.D) { ++C; } + Range(Range &&R) : CanMove<Moveable>(std::move(R)), C(R.C), M(R.M), D(R.D) { + ++M; + } + ~Range() { ++D; } + + int &C; + int &M; + int &D; + + int *begin() { return nullptr; } + int *end() { return nullptr; } +}; + +TEST(STLExtrasTest, EnumerateLifetimeSemantics) { + // Test that when enumerating lvalues and rvalues, there are no surprise + // copies or moves. + + // With an rvalue, it should not be destroyed until the end of the scope. + int Copies = 0; + int Moves = 0; + int Destructors = 0; + { + auto E1 = enumerate(Range<true, false>(Copies, Moves, Destructors)); + // Doesn't compile. rvalue ranges must be moveable. + // auto E2 = enumerate(Range<false, true>(Copies, Moves, Destructors)); + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(1, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(2, Destructors); + + Copies = Moves = Destructors = 0; + // With an lvalue, it should not be destroyed even after the end of the scope. + // lvalue ranges need be neither copyable nor moveable. + Range<false, false> R(Copies, Moves, Destructors); + { + auto Enumerator = enumerate(R); + (void)Enumerator; + EXPECT_EQ(0, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(0, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(0, Destructors); +} + +TEST(STLExtrasTest, ApplyTuple) { + auto T = std::make_tuple(1, 3, 7); + auto U = llvm::apply_tuple( + [](int A, int B, int C) { return std::make_tuple(A - B, B - C, C - A); }, + T); + + EXPECT_EQ(-2, std::get<0>(U)); + EXPECT_EQ(-4, std::get<1>(U)); + EXPECT_EQ(6, std::get<2>(U)); + + auto V = llvm::apply_tuple( + [](int A, int B, int C) { + return std::make_tuple(std::make_pair(A, char('A' + A)), + std::make_pair(B, char('A' + B)), + std::make_pair(C, char('A' + C))); + }, + T); + + EXPECT_EQ(std::make_pair(1, 'B'), std::get<0>(V)); + EXPECT_EQ(std::make_pair(3, 'D'), std::get<1>(V)); + EXPECT_EQ(std::make_pair(7, 'H'), std::get<2>(V)); +} + +class apply_variadic { + static int apply_one(int X) { return X + 1; } + static char apply_one(char C) { return C + 1; } + static StringRef apply_one(StringRef S) { return S.drop_back(); } + +public: + template <typename... Ts> + auto operator()(Ts &&... Items) + -> decltype(std::make_tuple(apply_one(Items)...)) { + return std::make_tuple(apply_one(Items)...); + } +}; + +TEST(STLExtrasTest, ApplyTupleVariadic) { + auto Items = std::make_tuple(1, llvm::StringRef("Test"), 'X'); + auto Values = apply_tuple(apply_variadic(), Items); + + EXPECT_EQ(2, std::get<0>(Values)); + EXPECT_EQ("Tes", std::get<1>(Values)); + EXPECT_EQ('Y', std::get<2>(Values)); +} + +TEST(STLExtrasTest, CountAdaptor) { + std::vector<int> v; + + v.push_back(1); + v.push_back(2); + v.push_back(1); + v.push_back(4); + v.push_back(3); + v.push_back(2); + v.push_back(1); + + EXPECT_EQ(3, count(v, 1)); + EXPECT_EQ(2, count(v, 2)); + EXPECT_EQ(1, count(v, 3)); + EXPECT_EQ(1, count(v, 4)); +} + +TEST(STLExtrasTest, ConcatRange) { + std::vector<int> Expected = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector<int> Test; + + std::vector<int> V1234 = {1, 2, 3, 4}; + std::list<int> L56 = {5, 6}; + SmallVector<int, 2> SV78 = {7, 8}; + + // Use concat across different sized ranges of different types with different + // iterators. + for (int &i : concat<int>(V1234, L56, SV78)) + Test.push_back(i); + EXPECT_EQ(Expected, Test); + + // Use concat between a temporary, an L-value, and an R-value to make sure + // complex lifetimes work well. + Test.clear(); + for (int &i : concat<int>(std::vector<int>(V1234), L56, std::move(SV78))) + Test.push_back(i); + EXPECT_EQ(Expected, Test); +} + +TEST(STLExtrasTest, PartitionAdaptor) { + std::vector<int> V = {1, 2, 3, 4, 5, 6, 7, 8}; + + auto I = partition(V, [](int i) { return i % 2 == 0; }); + ASSERT_EQ(V.begin() + 4, I); + + // Sort the two halves as partition may have messed with the order. + std::sort(V.begin(), I); + std::sort(I, V.end()); + + EXPECT_EQ(2, V[0]); + EXPECT_EQ(4, V[1]); + EXPECT_EQ(6, V[2]); + EXPECT_EQ(8, V[3]); + EXPECT_EQ(1, V[4]); + EXPECT_EQ(3, V[5]); + EXPECT_EQ(5, V[6]); + EXPECT_EQ(7, V[7]); +} + +TEST(STLExtrasTest, EraseIf) { + std::vector<int> V = {1, 2, 3, 4, 5, 6, 7, 8}; + + erase_if(V, [](int i) { return i % 2 == 0; }); + EXPECT_EQ(4u, V.size()); + EXPECT_EQ(1, V[0]); + EXPECT_EQ(3, V[1]); + EXPECT_EQ(5, V[2]); + EXPECT_EQ(7, V[3]); +} + +} diff --git a/unittests/ADT/ScopeExitTest.cpp b/unittests/ADT/ScopeExitTest.cpp new file mode 100644 index 000000000000..301942c30bbc --- /dev/null +++ b/unittests/ADT/ScopeExitTest.cpp @@ -0,0 +1,32 @@ +//===- llvm/unittest/ADT/ScopeExit.cpp - Scope exit unit tests --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ScopeExit.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(ScopeExitTest, Basic) { + struct Callable { + bool &Called; + Callable(bool &Called) : Called(Called) {} + Callable(Callable &&RHS) : Called(RHS.Called) {} + void operator()() { Called = true; } + }; + bool Called = false; + { + auto g = make_scope_exit(Callable(Called)); + EXPECT_FALSE(Called); + } + EXPECT_TRUE(Called); +} + +} // end anonymous namespace diff --git a/unittests/ADT/SimpleIListTest.cpp b/unittests/ADT/SimpleIListTest.cpp new file mode 100644 index 000000000000..a354f332006a --- /dev/null +++ b/unittests/ADT/SimpleIListTest.cpp @@ -0,0 +1,654 @@ +//===- unittests/ADT/SimpleIListTest.cpp - simple_ilist 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/simple_ilist.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct Node : ilist_node<Node> {}; +bool operator<(const Node &L, const Node &R) { return &L < &R; } +bool makeFalse(const Node &, const Node &) { return false; } + +struct deleteNode : std::default_delete<Node> {}; +void doNothing(Node *) {} + +TEST(SimpleIListTest, DefaultConstructor) { + simple_ilist<Node> L; + EXPECT_EQ(L.begin(), L.end()); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, pushPopFront) { + simple_ilist<Node> L; + Node A, B; + L.push_front(B); + L.push_front(A); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); + + // Pop front and check the new front. + L.pop_front(); + EXPECT_EQ(&B, &L.front()); + + // Pop to empty. + L.pop_front(); + EXPECT_TRUE(L.empty()); +} + +TEST(SimpleIListTest, pushPopBack) { + simple_ilist<Node> L; + Node A, B; + L.push_back(A); + L.push_back(B); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); + + // Pop back and check the new front. + L.pop_back(); + EXPECT_EQ(&A, &L.back()); + + // Pop to empty. + L.pop_back(); + EXPECT_TRUE(L.empty()); +} + +TEST(SimpleIListTest, swap) { + simple_ilist<Node> L1, L2; + Node A, B; + L1.push_back(A); + L1.push_back(B); + L1.swap(L2); + EXPECT_TRUE(L1.empty()); + EXPECT_EQ(0u, L1.size()); + EXPECT_EQ(&A, &L2.front()); + EXPECT_EQ(&B, &L2.back()); + EXPECT_FALSE(L2.empty()); + EXPECT_EQ(2u, L2.size()); +} + +TEST(SimpleIListTest, insertEraseAtEnd) { + simple_ilist<Node> L; + Node A, B; + L.insert(L.end(), A); + L.insert(L.end(), B); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, insertAtBegin) { + simple_ilist<Node> L; + Node A, B; + L.insert(L.begin(), B); + L.insert(L.begin(), A); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, remove) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.remove(B); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); + + L.remove(A); + EXPECT_EQ(&C, &L.front()); + EXPECT_EQ(1u, L.size()); + + L.remove(C); + EXPECT_TRUE(L.empty()); +} + +TEST(SimpleIListTest, removeAndDispose) { + simple_ilist<Node> L; + Node A, C; + Node *B = new Node; + L.push_back(A); + L.push_back(*B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.removeAndDispose(*B, deleteNode()); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, removeAndDisposeNullDeleter) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.removeAndDispose(B, doNothing); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, erase) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + EXPECT_EQ(C.getIterator(), L.erase(B.getIterator())); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, reverse_iterator) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + + auto ReverseIter = L.rbegin(); + EXPECT_EQ(C.getReverseIterator(), ReverseIter); + ++ReverseIter; + EXPECT_EQ(B.getReverseIterator(), ReverseIter); + ++ReverseIter; + EXPECT_EQ(A.getReverseIterator(), ReverseIter); + ++ReverseIter; + EXPECT_EQ(L.rend(), ReverseIter); +} + +TEST(SimpleIListTest, eraseAndDispose) { + simple_ilist<Node> L; + Node A, C; + Node *B = new Node; + L.push_back(A); + L.push_back(*B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.eraseAndDispose(B->getIterator(), deleteNode()); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseAndDisposeNullDeleter) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.eraseAndDispose(B.getIterator(), doNothing); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseRange) { + simple_ilist<Node> L; + Node A, B, C, D, E; + L.push_back(A); + L.push_back(B); + L.push_back(C); + L.push_back(D); + L.push_back(E); + auto I = L.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L.end(), I); + EXPECT_EQ(5u, L.size()); + + // Erase a range. + EXPECT_EQ(E.getIterator(), L.erase(B.getIterator(), E.getIterator())); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&E, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseAndDisposeRange) { + simple_ilist<Node> L; + Node A, *B = new Node, *C = new Node, *D = new Node, E; + L.push_back(A); + L.push_back(*B); + L.push_back(*C); + L.push_back(*D); + L.push_back(E); + auto I = L.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(B, &*I++); + EXPECT_EQ(C, &*I++); + EXPECT_EQ(D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L.end(), I); + EXPECT_EQ(5u, L.size()); + + // Erase a range. + EXPECT_EQ(E.getIterator(), + L.eraseAndDispose(B->getIterator(), E.getIterator(), deleteNode())); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&E, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseAndDisposeRangeNullDeleter) { + simple_ilist<Node> L; + Node A, B, C, D, E; + L.push_back(A); + L.push_back(B); + L.push_back(C); + L.push_back(D); + L.push_back(E); + auto I = L.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L.end(), I); + EXPECT_EQ(5u, L.size()); + + // Erase a range. + EXPECT_EQ(E.getIterator(), + L.eraseAndDispose(B.getIterator(), E.getIterator(), doNothing)); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&E, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, clear) { + simple_ilist<Node> L; + Node A, B; + L.push_back(A); + L.push_back(B); + L.clear(); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, clearAndDispose) { + simple_ilist<Node> L; + Node *A = new Node; + Node *B = new Node; + L.push_back(*A); + L.push_back(*B); + L.clearAndDispose(deleteNode()); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, clearAndDisposeNullDeleter) { + simple_ilist<Node> L; + Node A, B; + L.push_back(A); + L.push_back(B); + L.clearAndDispose(doNothing); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, spliceList) { + simple_ilist<Node> L1, L2; + Node A, B, C, D; + + // [A, D]. + L1.push_back(A); + L1.push_back(D); + + // [B, C]. + L2.push_back(B); + L2.push_back(C); + + // Splice in L2, giving [A, B, C, D]. + L1.splice(--L1.end(), L2); + EXPECT_TRUE(L2.empty()); + EXPECT_EQ(4u, L1.size()); + auto I = L1.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(L1.end(), I); +} + +TEST(SimpleIListTest, spliceSingle) { + simple_ilist<Node> L1, L2; + Node A, B, C, D, E; + + // [A, C]. + L1.push_back(A); + L1.push_back(C); + + // [D, B, E]. + L2.push_back(D); + L2.push_back(B); + L2.push_back(E); + + // Splice B from L2 to L1, giving [A, B, C] and [D, E]. + L1.splice(--L1.end(), L2, ++L2.begin()); + auto I = L1.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(L1.end(), I); + + I = L2.begin(); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L2.end(), I); +} + +TEST(SimpleIListTest, spliceRange) { + simple_ilist<Node> L1, L2; + Node A, B, C, D, E, F; + + // [A, D]. + L1.push_back(A); + L1.push_back(D); + + // [E, B, C, F]. + L2.push_back(E); + L2.push_back(B); + L2.push_back(C); + L2.push_back(F); + + // Splice B from L2 to L1, giving [A, B, C, D] and [E, F]. + L1.splice(--L1.end(), L2, ++L2.begin(), --L2.end()); + auto I = L1.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(L1.end(), I); + + I = L2.begin(); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(&F, &*I++); + EXPECT_EQ(L2.end(), I); +} + +TEST(SimpleIListTest, merge) { + for (bool IsL1LHS : {false, true}) { + simple_ilist<Node> L1, L2; + Node Ns[10]; + + // Fill L1. + L1.push_back(Ns[0]); + L1.push_back(Ns[3]); + L1.push_back(Ns[4]); + L1.push_back(Ns[8]); + + // Fill L2. + L2.push_back(Ns[1]); + L2.push_back(Ns[2]); + L2.push_back(Ns[5]); + L2.push_back(Ns[6]); + L2.push_back(Ns[7]); + L2.push_back(Ns[9]); + + // Check setup. + EXPECT_EQ(4u, L1.size()); + EXPECT_EQ(6u, L2.size()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end())); + EXPECT_TRUE(std::is_sorted(L2.begin(), L2.end())); + + // Merge. + auto &LHS = IsL1LHS ? L1 : L2; + auto &RHS = IsL1LHS ? L2 : L1; + LHS.merge(RHS); + EXPECT_TRUE(RHS.empty()); + EXPECT_FALSE(LHS.empty()); + EXPECT_TRUE(std::is_sorted(LHS.begin(), LHS.end())); + auto I = LHS.begin(); + for (Node &N : Ns) + EXPECT_EQ(&N, &*I++); + EXPECT_EQ(LHS.end(), I); + } +} + +TEST(SimpleIListTest, mergeIsStable) { + simple_ilist<Node> L1, L2; + Node Ns[5]; + + auto setup = [&]() { + EXPECT_TRUE(L1.empty()); + EXPECT_TRUE(L2.empty()); + + // Fill L1. + L1.push_back(Ns[0]); + L1.push_back(Ns[3]); + L1.push_back(Ns[4]); + + // Fill L2. + L2.push_back(Ns[1]); + L2.push_back(Ns[2]); + + // Check setup. + EXPECT_EQ(3u, L1.size()); + EXPECT_EQ(2u, L2.size()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end(), makeFalse)); + EXPECT_TRUE(std::is_sorted(L2.begin(), L2.end(), makeFalse)); + }; + + // Merge. Should be stable. + setup(); + L1.merge(L2, makeFalse); + EXPECT_TRUE(L2.empty()); + EXPECT_FALSE(L1.empty()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end(), makeFalse)); + auto I = L1.begin(); + EXPECT_EQ(&Ns[0], &*I++); + EXPECT_EQ(&Ns[3], &*I++); + EXPECT_EQ(&Ns[4], &*I++); + EXPECT_EQ(&Ns[1], &*I++); + EXPECT_EQ(&Ns[2], &*I++); + EXPECT_EQ(L1.end(), I); + + // Merge the other way. Should be stable. + L1.clear(); + setup(); + L2.merge(L1, makeFalse); + EXPECT_TRUE(L1.empty()); + EXPECT_FALSE(L2.empty()); + EXPECT_TRUE(std::is_sorted(L2.begin(), L2.end(), makeFalse)); + I = L2.begin(); + EXPECT_EQ(&Ns[1], &*I++); + EXPECT_EQ(&Ns[2], &*I++); + EXPECT_EQ(&Ns[0], &*I++); + EXPECT_EQ(&Ns[3], &*I++); + EXPECT_EQ(&Ns[4], &*I++); + EXPECT_EQ(L2.end(), I); +} + +TEST(SimpleIListTest, mergeEmpty) { + for (bool IsL1LHS : {false, true}) { + simple_ilist<Node> L1, L2; + Node Ns[4]; + + // Fill L1. + L1.push_back(Ns[0]); + L1.push_back(Ns[1]); + L1.push_back(Ns[2]); + L1.push_back(Ns[3]); + + // Check setup. + EXPECT_EQ(4u, L1.size()); + EXPECT_TRUE(L2.empty()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end())); + + // Merge. + auto &LHS = IsL1LHS ? L1 : L2; + auto &RHS = IsL1LHS ? L2 : L1; + LHS.merge(RHS); + EXPECT_TRUE(RHS.empty()); + EXPECT_FALSE(LHS.empty()); + EXPECT_TRUE(std::is_sorted(LHS.begin(), LHS.end())); + auto I = LHS.begin(); + for (Node &N : Ns) + EXPECT_EQ(&N, &*I++); + EXPECT_EQ(LHS.end(), I); + } +} + +TEST(SimpleIListTest, mergeBothEmpty) { + simple_ilist<Node> L1, L2; + L1.merge(L2); + EXPECT_TRUE(L1.empty()); + EXPECT_TRUE(L2.empty()); +} + +TEST(SimpleIListTest, sort) { + simple_ilist<Node> L; + Node Ns[10]; + + // Fill L. + for (int I : {3, 4, 0, 8, 1, 2, 6, 7, 9, 5}) + L.push_back(Ns[I]); + + // Check setup. + EXPECT_EQ(10u, L.size()); + EXPECT_FALSE(std::is_sorted(L.begin(), L.end())); + + // Sort. + L.sort(); + EXPECT_TRUE(std::is_sorted(L.begin(), L.end())); + auto I = L.begin(); + for (Node &N : Ns) + EXPECT_EQ(&N, &*I++); + EXPECT_EQ(L.end(), I); +} + +TEST(SimpleIListTest, sortIsStable) { + simple_ilist<Node> L; + Node Ns[10]; + + // Compare such that nodes are partitioned but not fully sorted. + auto partition = [&](const Node &N) { return &N >= &Ns[5]; }; + auto compare = [&](const Node &L, const Node &R) { + return partition(L) < partition(R); + }; + + // Fill L. + for (int I : {3, 4, 7, 8, 1, 2, 6, 0, 9, 5}) + L.push_back(Ns[I]); + + // Check setup. + EXPECT_EQ(10u, L.size()); + EXPECT_FALSE(std::is_sorted(L.begin(), L.end(), compare)); + + // Sort. + L.sort(compare); + EXPECT_TRUE(std::is_sorted(L.begin(), L.end(), compare)); + auto I = L.begin(); + for (int O : {3, 4, 1, 2, 0}) + EXPECT_EQ(&Ns[O], &*I++); + for (int O : {7, 8, 6, 9, 5}) + EXPECT_EQ(&Ns[O], &*I++); + EXPECT_EQ(L.end(), I); +} + +TEST(SimpleIListTest, sortEmpty) { + simple_ilist<Node> L; + L.sort(); +} + +struct Tag1 {}; +struct Tag2 {}; + +struct DoubleNode : ilist_node<DoubleNode, ilist_tag<Tag1>>, + ilist_node<DoubleNode, ilist_tag<Tag2>> { + typedef ilist_node<DoubleNode, ilist_tag<Tag1>> Node1Type; + typedef ilist_node<DoubleNode, ilist_tag<Tag2>> Node2Type; + + Node1Type::self_iterator getIterator1() { return Node1Type::getIterator(); } + Node2Type::self_iterator getIterator2() { return Node2Type::getIterator(); } + Node1Type::const_self_iterator getIterator1() const { + return Node1Type::getIterator(); + } + Node2Type::const_self_iterator getIterator2() const { + return Node2Type::getIterator(); + } +}; +typedef simple_ilist<DoubleNode, ilist_tag<Tag1>> TaggedList1Type; +typedef simple_ilist<DoubleNode, ilist_tag<Tag2>> TaggedList2Type; + +TEST(SimpleIListTest, TaggedLists) { + TaggedList1Type L1; + TaggedList2Type L2; + + // Build the two lists, sharing a couple of nodes. + DoubleNode Ns[10]; + int Order1[] = {0, 1, 2, 3, 4, 7, 9}; + int Order2[] = {2, 5, 6, 7, 8, 4, 9, 1}; + for (int I : Order1) + L1.push_back(Ns[I]); + for (int I : Order2) + L2.push_back(Ns[I]); + + // Check that each list is correct. + EXPECT_EQ(sizeof(Order1) / sizeof(int), L1.size()); + auto I1 = L1.begin(); + for (int I : Order1) { + EXPECT_EQ(Ns[I].getIterator1(), I1); + EXPECT_EQ(&Ns[I], &*I1++); + } + EXPECT_EQ(L1.end(), I1); + + EXPECT_EQ(sizeof(Order2) / sizeof(int), L2.size()); + auto I2 = L2.begin(); + for (int I : Order2) { + EXPECT_EQ(Ns[I].getIterator2(), I2); + EXPECT_EQ(&Ns[I], &*I2++); + } + EXPECT_EQ(L2.end(), I2); +} + +} // end namespace diff --git a/unittests/ADT/SmallPtrSetTest.cpp b/unittests/ADT/SmallPtrSetTest.cpp index d8d07b16cfe3..d4d963fdc5bd 100644 --- a/unittests/ADT/SmallPtrSetTest.cpp +++ b/unittests/ADT/SmallPtrSetTest.cpp @@ -21,10 +21,7 @@ TEST(SmallPtrSetTest, Assignment) { for (int i = 0; i < 8; ++i) buf[i] = 0; - SmallPtrSet<int *, 4> s1; - s1.insert(&buf[0]); - s1.insert(&buf[1]); - + SmallPtrSet<int *, 4> s1 = {&buf[0], &buf[1]}; SmallPtrSet<int *, 4> s2; (s2 = s1).insert(&buf[2]); @@ -38,6 +35,15 @@ TEST(SmallPtrSetTest, Assignment) { EXPECT_TRUE(s1.count(&buf[i])); else EXPECT_FALSE(s1.count(&buf[i])); + + // Assign and insert with initializer lists, and ones that contain both + // duplicates and out-of-order elements. + (s2 = {&buf[6], &buf[7], &buf[6]}).insert({&buf[5], &buf[4]}); + for (int i = 0; i < 8; ++i) + if (i < 4) + EXPECT_FALSE(s2.count(&buf[i])); + else + EXPECT_TRUE(s2.count(&buf[i])); } TEST(SmallPtrSetTest, GrowthTest) { diff --git a/unittests/ADT/StringExtrasTest.cpp b/unittests/ADT/StringExtrasTest.cpp new file mode 100644 index 000000000000..afb984e405d8 --- /dev/null +++ b/unittests/ADT/StringExtrasTest.cpp @@ -0,0 +1,52 @@ +//===- StringExtrasTest.cpp - Unit tests for String extras ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(StringExtrasTest, Join) { + std::vector<std::string> Items; + EXPECT_EQ("", join(Items.begin(), Items.end(), " <sep> ")); + + Items = {"foo"}; + EXPECT_EQ("foo", join(Items.begin(), Items.end(), " <sep> ")); + + Items = {"foo", "bar"}; + EXPECT_EQ("foo <sep> bar", join(Items.begin(), Items.end(), " <sep> ")); + + Items = {"foo", "bar", "baz"}; + EXPECT_EQ("foo <sep> bar <sep> baz", + join(Items.begin(), Items.end(), " <sep> ")); +} + +TEST(StringExtrasTest, JoinItems) { + const char *Foo = "foo"; + std::string Bar = "bar"; + llvm::StringRef Baz = "baz"; + char X = 'x'; + + EXPECT_EQ("", join_items(" <sep> ")); + EXPECT_EQ("", join_items('/')); + + EXPECT_EQ("foo", join_items(" <sep> ", Foo)); + EXPECT_EQ("foo", join_items('/', Foo)); + + EXPECT_EQ("foo <sep> bar", join_items(" <sep> ", Foo, Bar)); + EXPECT_EQ("foo/bar", join_items('/', Foo, Bar)); + + EXPECT_EQ("foo <sep> bar <sep> baz", join_items(" <sep> ", Foo, Bar, Baz)); + EXPECT_EQ("foo/bar/baz", join_items('/', Foo, Bar, Baz)); + + EXPECT_EQ("foo <sep> bar <sep> baz <sep> x", + join_items(" <sep> ", Foo, Bar, Baz, X)); + + EXPECT_EQ("foo/bar/baz/x", join_items('/', Foo, Bar, Baz, X)); +} diff --git a/unittests/ADT/StringMapTest.cpp b/unittests/ADT/StringMapTest.cpp index 6ca701bb23a5..911c72d74961 100644 --- a/unittests/ADT/StringMapTest.cpp +++ b/unittests/ADT/StringMapTest.cpp @@ -458,7 +458,7 @@ struct NonMoveableNonCopyableType { // 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); + Map.try_emplace("abcd", 42); EXPECT_EQ(1u, Map.count("abcd")); EXPECT_EQ(42, Map["abcd"].Data); } diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 66e5944b56eb..5b6822ed757d 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -32,6 +32,34 @@ std::ostream &operator<<(std::ostream &OS, } +// Check that we can't accidentally assign a temporary std::string to a +// StringRef. (Unfortunately we can't make use of the same thing with +// constructors.) +// +// Disable this check under MSVC; even MSVC 2015 isn't consistent between +// std::is_assignable and actually writing such an assignment. +#if !defined(_MSC_VER) +static_assert( + !std::is_assignable<StringRef, std::string>::value, + "Assigning from prvalue std::string"); +static_assert( + !std::is_assignable<StringRef, std::string &&>::value, + "Assigning from xvalue std::string"); +static_assert( + std::is_assignable<StringRef, std::string &>::value, + "Assigning from lvalue std::string"); +static_assert( + std::is_assignable<StringRef, const char *>::value, + "Assigning from prvalue C string"); +static_assert( + std::is_assignable<StringRef, const char * &&>::value, + "Assigning from xvalue C string"); +static_assert( + std::is_assignable<StringRef, const char * &>::value, + "Assigning from lvalue C string"); +#endif + + namespace { TEST(StringRefTest, Construction) { EXPECT_EQ("", StringRef()); @@ -40,6 +68,14 @@ TEST(StringRefTest, Construction) { EXPECT_EQ("hello", StringRef(std::string("hello"))); } +TEST(StringRefTest, EmptyInitializerList) { + StringRef S = {}; + EXPECT_TRUE(S.empty()); + + S = {}; + EXPECT_TRUE(S.empty()); +} + TEST(StringRefTest, Iteration) { StringRef S("hello"); const char *p = "hello"; @@ -322,6 +358,22 @@ TEST(StringRefTest, StartsWithLower) { EXPECT_FALSE(Str.startswith_lower("hi")); } +TEST(StringRefTest, ConsumeFront) { + StringRef Str("hello"); + EXPECT_TRUE(Str.consume_front("")); + EXPECT_EQ("hello", Str); + EXPECT_TRUE(Str.consume_front("he")); + EXPECT_EQ("llo", Str); + EXPECT_FALSE(Str.consume_front("lloworld")); + EXPECT_EQ("llo", Str); + EXPECT_FALSE(Str.consume_front("lol")); + EXPECT_EQ("llo", Str); + EXPECT_TRUE(Str.consume_front("llo")); + EXPECT_EQ("", Str); + EXPECT_FALSE(Str.consume_front("o")); + EXPECT_TRUE(Str.consume_front("")); +} + TEST(StringRefTest, EndsWith) { StringRef Str("hello"); EXPECT_TRUE(Str.endswith("")); @@ -341,22 +393,75 @@ TEST(StringRefTest, EndsWithLower) { EXPECT_FALSE(Str.endswith_lower("hi")); } -TEST(StringRefTest, Find) { +TEST(StringRefTest, ConsumeBack) { StringRef Str("hello"); - EXPECT_EQ(2U, Str.find('l')); - EXPECT_EQ(StringRef::npos, Str.find('z')); - EXPECT_EQ(StringRef::npos, Str.find("helloworld")); - EXPECT_EQ(0U, Str.find("hello")); - EXPECT_EQ(1U, Str.find("ello")); - EXPECT_EQ(StringRef::npos, Str.find("zz")); - EXPECT_EQ(2U, Str.find("ll", 2)); - EXPECT_EQ(StringRef::npos, Str.find("ll", 3)); - EXPECT_EQ(0U, Str.find("")); - StringRef LongStr("hellx xello hell ello world foo bar hello"); - EXPECT_EQ(36U, LongStr.find("hello")); - EXPECT_EQ(28U, LongStr.find("foo")); - EXPECT_EQ(12U, LongStr.find("hell", 2)); - EXPECT_EQ(0U, LongStr.find("")); + EXPECT_TRUE(Str.consume_back("")); + EXPECT_EQ("hello", Str); + EXPECT_TRUE(Str.consume_back("lo")); + EXPECT_EQ("hel", Str); + EXPECT_FALSE(Str.consume_back("helhel")); + EXPECT_EQ("hel", Str); + EXPECT_FALSE(Str.consume_back("hle")); + EXPECT_EQ("hel", Str); + EXPECT_TRUE(Str.consume_back("hel")); + EXPECT_EQ("", Str); + EXPECT_FALSE(Str.consume_back("h")); + EXPECT_TRUE(Str.consume_back("")); +} + +TEST(StringRefTest, Find) { + StringRef Str("helloHELLO"); + StringRef LongStr("hellx xello hell ello world foo bar hello HELLO"); + + struct { + StringRef Str; + char C; + std::size_t From; + std::size_t Pos; + std::size_t LowerPos; + } CharExpectations[] = { + {Str, 'h', 0U, 0U, 0U}, + {Str, 'e', 0U, 1U, 1U}, + {Str, 'l', 0U, 2U, 2U}, + {Str, 'l', 3U, 3U, 3U}, + {Str, 'o', 0U, 4U, 4U}, + {Str, 'L', 0U, 7U, 2U}, + {Str, 'z', 0U, StringRef::npos, StringRef::npos}, + }; + + struct { + StringRef Str; + llvm::StringRef S; + std::size_t From; + std::size_t Pos; + std::size_t LowerPos; + } StrExpectations[] = { + {Str, "helloword", 0, StringRef::npos, StringRef::npos}, + {Str, "hello", 0, 0U, 0U}, + {Str, "ello", 0, 1U, 1U}, + {Str, "zz", 0, StringRef::npos, StringRef::npos}, + {Str, "ll", 2U, 2U, 2U}, + {Str, "ll", 3U, StringRef::npos, 7U}, + {Str, "LL", 2U, 7U, 2U}, + {Str, "LL", 3U, 7U, 7U}, + {Str, "", 0U, 0U, 0U}, + {LongStr, "hello", 0U, 36U, 36U}, + {LongStr, "foo", 0U, 28U, 28U}, + {LongStr, "hell", 2U, 12U, 12U}, + {LongStr, "HELL", 2U, 42U, 12U}, + {LongStr, "", 0U, 0U, 0U}}; + + for (auto &E : CharExpectations) { + EXPECT_EQ(E.Pos, E.Str.find(E.C, E.From)); + EXPECT_EQ(E.LowerPos, E.Str.find_lower(E.C, E.From)); + EXPECT_EQ(E.LowerPos, E.Str.find_lower(toupper(E.C), E.From)); + } + + for (auto &E : StrExpectations) { + EXPECT_EQ(E.Pos, E.Str.find(E.S, E.From)); + EXPECT_EQ(E.LowerPos, E.Str.find_lower(E.S, E.From)); + EXPECT_EQ(E.LowerPos, E.Str.find_lower(E.S.upper(), E.From)); + } EXPECT_EQ(3U, Str.rfind('l')); EXPECT_EQ(StringRef::npos, Str.rfind('z')); @@ -365,10 +470,19 @@ TEST(StringRefTest, Find) { EXPECT_EQ(1U, Str.rfind("ello")); EXPECT_EQ(StringRef::npos, Str.rfind("zz")); + EXPECT_EQ(8U, Str.rfind_lower('l')); + EXPECT_EQ(8U, Str.rfind_lower('L')); + EXPECT_EQ(StringRef::npos, Str.rfind_lower('z')); + EXPECT_EQ(StringRef::npos, Str.rfind_lower("HELLOWORLD")); + EXPECT_EQ(5U, Str.rfind("HELLO")); + EXPECT_EQ(6U, Str.rfind("ELLO")); + EXPECT_EQ(StringRef::npos, Str.rfind("ZZ")); + EXPECT_EQ(2U, Str.find_first_of('l')); EXPECT_EQ(1U, Str.find_first_of("el")); EXPECT_EQ(StringRef::npos, Str.find_first_of("xyz")); + Str = "hello"; EXPECT_EQ(1U, Str.find_first_not_of('h')); EXPECT_EQ(4U, Str.find_first_not_of("hel")); EXPECT_EQ(StringRef::npos, Str.find_first_not_of("hello")); @@ -539,7 +653,8 @@ TEST(StringRefTest, getAsInteger) { static const char* BadStrings[] = { - "18446744073709551617" // value just over max + "" // empty string + , "18446744073709551617" // value just over max , "123456789012345678901" // value way too large , "4t23v" // illegal decimal characters , "0x123W56" // illegal hex characters @@ -547,6 +662,8 @@ static const char* BadStrings[] = { , "08" // illegal oct characters , "0o8" // illegal oct characters , "-123" // negative unsigned value + , "0x" + , "0b" }; @@ -558,6 +675,183 @@ TEST(StringRefTest, getAsUnsignedIntegerBadStrings) { } } +struct ConsumeUnsignedPair { + const char *Str; + uint64_t Expected; + const char *Leftover; +} ConsumeUnsigned[] = { + {"0", 0, ""}, + {"255", 255, ""}, + {"256", 256, ""}, + {"65535", 65535, ""}, + {"65536", 65536, ""}, + {"4294967295", 4294967295ULL, ""}, + {"4294967296", 4294967296ULL, ""}, + {"255A376", 255, "A376"}, + {"18446744073709551615", 18446744073709551615ULL, ""}, + {"18446744073709551615ABC", 18446744073709551615ULL, "ABC"}, + {"042", 34, ""}, + {"0x42", 66, ""}, + {"0x42-0x34", 66, "-0x34"}, + {"0b101010", 42, ""}, + {"0429F", 042, "9F"}, // Auto-sensed octal radix, invalid digit + {"0x42G12", 0x42, "G12"}, // Auto-sensed hex radix, invalid digit + {"0b10101020101", 42, "20101"}}; // Auto-sensed binary radix, invalid digit. + +struct ConsumeSignedPair { + const char *Str; + int64_t Expected; + const char *Leftover; +} ConsumeSigned[] = { + {"0", 0, ""}, + {"-0", 0, ""}, + {"0-1", 0, "-1"}, + {"-0-1", 0, "-1"}, + {"127", 127, ""}, + {"128", 128, ""}, + {"127-1", 127, "-1"}, + {"128-1", 128, "-1"}, + {"-128", -128, ""}, + {"-129", -129, ""}, + {"-128-1", -128, "-1"}, + {"-129-1", -129, "-1"}, + {"32767", 32767, ""}, + {"32768", 32768, ""}, + {"32767-1", 32767, "-1"}, + {"32768-1", 32768, "-1"}, + {"-32768", -32768, ""}, + {"-32769", -32769, ""}, + {"-32768-1", -32768, "-1"}, + {"-32769-1", -32769, "-1"}, + {"2147483647", 2147483647LL, ""}, + {"2147483648", 2147483648LL, ""}, + {"2147483647-1", 2147483647LL, "-1"}, + {"2147483648-1", 2147483648LL, "-1"}, + {"-2147483648", -2147483648LL, ""}, + {"-2147483649", -2147483649LL, ""}, + {"-2147483648-1", -2147483648LL, "-1"}, + {"-2147483649-1", -2147483649LL, "-1"}, + {"-9223372036854775808", -(9223372036854775807LL) - 1, ""}, + {"-9223372036854775808-1", -(9223372036854775807LL) - 1, "-1"}, + {"042", 34, ""}, + {"042-1", 34, "-1"}, + {"0x42", 66, ""}, + {"0x42-1", 66, "-1"}, + {"0b101010", 42, ""}, + {"0b101010-1", 42, "-1"}, + {"-042", -34, ""}, + {"-042-1", -34, "-1"}, + {"-0x42", -66, ""}, + {"-0x42-1", -66, "-1"}, + {"-0b101010", -42, ""}, + {"-0b101010-1", -42, "-1"}}; + +TEST(StringRefTest, consumeIntegerUnsigned) { + uint8_t U8; + uint16_t U16; + uint32_t U32; + uint64_t U64; + + for (size_t i = 0; i < array_lengthof(ConsumeUnsigned); ++i) { + StringRef Str = ConsumeUnsigned[i].Str; + bool U8Success = Str.consumeInteger(0, U8); + if (static_cast<uint8_t>(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U8Success); + EXPECT_EQ(U8, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U8Success); + } + + Str = ConsumeUnsigned[i].Str; + bool U16Success = Str.consumeInteger(0, U16); + if (static_cast<uint16_t>(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U16Success); + EXPECT_EQ(U16, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U16Success); + } + + Str = ConsumeUnsigned[i].Str; + bool U32Success = Str.consumeInteger(0, U32); + if (static_cast<uint32_t>(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U32Success); + EXPECT_EQ(U32, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U32Success); + } + + Str = ConsumeUnsigned[i].Str; + bool U64Success = Str.consumeInteger(0, U64); + if (static_cast<uint64_t>(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U64Success); + EXPECT_EQ(U64, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U64Success); + } + } +} + +TEST(StringRefTest, consumeIntegerSigned) { + int8_t S8; + int16_t S16; + int32_t S32; + int64_t S64; + + for (size_t i = 0; i < array_lengthof(ConsumeSigned); ++i) { + StringRef Str = ConsumeSigned[i].Str; + bool S8Success = Str.consumeInteger(0, S8); + if (static_cast<int8_t>(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S8Success); + EXPECT_EQ(S8, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S8Success); + } + + Str = ConsumeSigned[i].Str; + bool S16Success = Str.consumeInteger(0, S16); + if (static_cast<int16_t>(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S16Success); + EXPECT_EQ(S16, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S16Success); + } + + Str = ConsumeSigned[i].Str; + bool S32Success = Str.consumeInteger(0, S32); + if (static_cast<int32_t>(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S32Success); + EXPECT_EQ(S32, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S32Success); + } + + Str = ConsumeSigned[i].Str; + bool S64Success = Str.consumeInteger(0, S64); + if (static_cast<int64_t>(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S64Success); + EXPECT_EQ(S64, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S64Success); + } + } +} + static const char *join_input[] = { "a", "b", "c" }; static const char join_result1[] = "a"; static const char join_result2[] = "a:b:c"; @@ -608,5 +902,110 @@ TEST(StringRefTest, AllocatorCopy) { EXPECT_NE(Str2.data(), Str2c.data()); } +TEST(StringRefTest, Drop) { + StringRef Test("StringRefTest::Drop"); + + StringRef Dropped = Test.drop_front(5); + EXPECT_EQ(Dropped, "gRefTest::Drop"); + + Dropped = Test.drop_back(5); + EXPECT_EQ(Dropped, "StringRefTest:"); + + Dropped = Test.drop_front(0); + EXPECT_EQ(Dropped, Test); + + Dropped = Test.drop_back(0); + EXPECT_EQ(Dropped, Test); + + Dropped = Test.drop_front(Test.size()); + EXPECT_TRUE(Dropped.empty()); + + Dropped = Test.drop_back(Test.size()); + EXPECT_TRUE(Dropped.empty()); +} + +TEST(StringRefTest, Take) { + StringRef Test("StringRefTest::Take"); + + StringRef Taken = Test.take_front(5); + EXPECT_EQ(Taken, "Strin"); + + Taken = Test.take_back(5); + EXPECT_EQ(Taken, ":Take"); + + Taken = Test.take_front(Test.size()); + EXPECT_EQ(Taken, Test); + + Taken = Test.take_back(Test.size()); + EXPECT_EQ(Taken, Test); + + Taken = Test.take_front(0); + EXPECT_TRUE(Taken.empty()); + + Taken = Test.take_back(0); + EXPECT_TRUE(Taken.empty()); +} + +TEST(StringRefTest, FindIf) { + StringRef Punct("Test.String"); + StringRef NoPunct("ABCDEFG"); + StringRef Empty; + + auto IsPunct = [](char c) { return ::ispunct(c); }; + auto IsAlpha = [](char c) { return ::isalpha(c); }; + EXPECT_EQ(4U, Punct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, NoPunct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, Empty.find_if(IsPunct)); + + EXPECT_EQ(4U, Punct.find_if_not(IsAlpha)); + EXPECT_EQ(StringRef::npos, NoPunct.find_if_not(IsAlpha)); + EXPECT_EQ(StringRef::npos, Empty.find_if_not(IsAlpha)); +} + +TEST(StringRefTest, TakeWhileUntil) { + StringRef Test("String With 1 Number"); + + StringRef Taken = Test.take_while([](char c) { return ::isdigit(c); }); + EXPECT_EQ("", Taken); + + Taken = Test.take_until([](char c) { return ::isdigit(c); }); + EXPECT_EQ("String With ", Taken); + + Taken = Test.take_while([](char c) { return true; }); + EXPECT_EQ(Test, Taken); + + Taken = Test.take_until([](char c) { return true; }); + EXPECT_EQ("", Taken); + + Test = ""; + Taken = Test.take_while([](char c) { return true; }); + EXPECT_EQ("", Taken); +} + +TEST(StringRefTest, DropWhileUntil) { + StringRef Test("String With 1 Number"); + + StringRef Taken = Test.drop_while([](char c) { return ::isdigit(c); }); + EXPECT_EQ(Test, Taken); + + Taken = Test.drop_until([](char c) { return ::isdigit(c); }); + EXPECT_EQ("1 Number", Taken); + + Taken = Test.drop_while([](char c) { return true; }); + EXPECT_EQ("", Taken); + + Taken = Test.drop_until([](char c) { return true; }); + EXPECT_EQ(Test, Taken); + + StringRef EmptyString = ""; + Taken = EmptyString.drop_while([](char c) { return true; }); + EXPECT_EQ("", Taken); +} + +TEST(StringRefTest, StringLiteral) { + constexpr StringLiteral Strings[] = {"Foo", "Bar"}; + EXPECT_EQ(StringRef("Foo"), Strings[0]); + EXPECT_EQ(StringRef("Bar"), Strings[1]); +} } // end anonymous namespace diff --git a/unittests/ADT/StringSwitchTest.cpp b/unittests/ADT/StringSwitchTest.cpp new file mode 100644 index 000000000000..1a51629ca574 --- /dev/null +++ b/unittests/ADT/StringSwitchTest.cpp @@ -0,0 +1,206 @@ +//===- llvm/unittest/ADT/StringSwitchTest.cpp - StringSwitch 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/StringSwitch.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(StringSwitchTest, Case) { + auto Translate = [](StringRef S) { + return llvm::StringSwitch<int>(S) + .Case("0", 0) + .Case("1", 1) + .Case("2", 2) + .Case("3", 3) + .Case("4", 4) + .Case("5", 5) + .Case("6", 6) + .Case("7", 7) + .Case("8", 8) + .Case("9", 9) + .Case("A", 10) + .Case("B", 11) + .Case("C", 12) + .Case("D", 13) + .Case("E", 14) + .Case("F", 15) + .Default(-1); + }; + EXPECT_EQ(1, Translate("1")); + EXPECT_EQ(2, Translate("2")); + EXPECT_EQ(11, Translate("B")); + EXPECT_EQ(-1, Translate("b")); + EXPECT_EQ(-1, Translate("")); + EXPECT_EQ(-1, Translate("Test")); +} + +TEST(StringSwitchTest, CaseLower) { + auto Translate = [](StringRef S) { + return llvm::StringSwitch<int>(S) + .Case("0", 0) + .Case("1", 1) + .Case("2", 2) + .Case("3", 3) + .Case("4", 4) + .Case("5", 5) + .Case("6", 6) + .Case("7", 7) + .Case("8", 8) + .Case("9", 9) + .CaseLower("A", 10) + .CaseLower("B", 11) + .CaseLower("C", 12) + .CaseLower("D", 13) + .CaseLower("E", 14) + .CaseLower("F", 15) + .Default(-1); + }; + EXPECT_EQ(1, Translate("1")); + EXPECT_EQ(2, Translate("2")); + EXPECT_EQ(11, Translate("B")); + EXPECT_EQ(11, Translate("b")); + + EXPECT_EQ(-1, Translate("")); + EXPECT_EQ(-1, Translate("Test")); +} + +TEST(StringSwitchTest, StartsWith) { + auto Translate = [](StringRef S) { + return llvm::StringSwitch<std::function<int(int, int)>>(S) + .StartsWith("add", [](int X, int Y) { return X + Y; }) + .StartsWith("sub", [](int X, int Y) { return X - Y; }) + .StartsWith("mul", [](int X, int Y) { return X * Y; }) + .StartsWith("div", [](int X, int Y) { return X / Y; }) + .Default([](int X, int Y) { return 0; }); + }; + + EXPECT_EQ(15, Translate("adder")(10, 5)); + EXPECT_EQ(5, Translate("subtracter")(10, 5)); + EXPECT_EQ(50, Translate("multiplier")(10, 5)); + EXPECT_EQ(2, Translate("divider")(10, 5)); + + EXPECT_EQ(0, Translate("nothing")(10, 5)); + EXPECT_EQ(0, Translate("ADDER")(10, 5)); +} + +TEST(StringSwitchTest, StartsWithLower) { + auto Translate = [](StringRef S) { + return llvm::StringSwitch<std::function<int(int, int)>>(S) + .StartsWithLower("add", [](int X, int Y) { return X + Y; }) + .StartsWithLower("sub", [](int X, int Y) { return X - Y; }) + .StartsWithLower("mul", [](int X, int Y) { return X * Y; }) + .StartsWithLower("div", [](int X, int Y) { return X / Y; }) + .Default([](int X, int Y) { return 0; }); + }; + + EXPECT_EQ(15, Translate("adder")(10, 5)); + EXPECT_EQ(5, Translate("subtracter")(10, 5)); + EXPECT_EQ(50, Translate("multiplier")(10, 5)); + EXPECT_EQ(2, Translate("divider")(10, 5)); + + EXPECT_EQ(15, Translate("AdDeR")(10, 5)); + EXPECT_EQ(5, Translate("SuBtRaCtEr")(10, 5)); + EXPECT_EQ(50, Translate("MuLtIpLiEr")(10, 5)); + EXPECT_EQ(2, Translate("DiViDeR")(10, 5)); + + EXPECT_EQ(0, Translate("nothing")(10, 5)); +} + +TEST(StringSwitchTest, EndsWith) { + enum class Suffix { Possible, PastTense, Process, InProgressAction, Unknown }; + + auto Translate = [](StringRef S) { + return llvm::StringSwitch<Suffix>(S) + .EndsWith("able", Suffix::Possible) + .EndsWith("ed", Suffix::PastTense) + .EndsWith("ation", Suffix::Process) + .EndsWith("ing", Suffix::InProgressAction) + .Default(Suffix::Unknown); + }; + + EXPECT_EQ(Suffix::Possible, Translate("optimizable")); + EXPECT_EQ(Suffix::PastTense, Translate("optimized")); + EXPECT_EQ(Suffix::Process, Translate("optimization")); + EXPECT_EQ(Suffix::InProgressAction, Translate("optimizing")); + EXPECT_EQ(Suffix::Unknown, Translate("optimizer")); + EXPECT_EQ(Suffix::Unknown, Translate("OPTIMIZABLE")); +} + +TEST(StringSwitchTest, EndsWithLower) { + enum class Suffix { Possible, PastTense, Process, InProgressAction, Unknown }; + + auto Translate = [](StringRef S) { + return llvm::StringSwitch<Suffix>(S) + .EndsWithLower("able", Suffix::Possible) + .EndsWithLower("ed", Suffix::PastTense) + .EndsWithLower("ation", Suffix::Process) + .EndsWithLower("ing", Suffix::InProgressAction) + .Default(Suffix::Unknown); + }; + + EXPECT_EQ(Suffix::Possible, Translate("optimizable")); + EXPECT_EQ(Suffix::Possible, Translate("OPTIMIZABLE")); + EXPECT_EQ(Suffix::PastTense, Translate("optimized")); + EXPECT_EQ(Suffix::Process, Translate("optimization")); + EXPECT_EQ(Suffix::InProgressAction, Translate("optimizing")); + EXPECT_EQ(Suffix::Unknown, Translate("optimizer")); +} + +TEST(StringSwitchTest, Cases) { + enum class OSType { Windows, Linux, Unknown }; + + auto Translate = [](StringRef S) { + return llvm::StringSwitch<OSType>(S) + .Cases("wind\0ws", "win32", "winnt", OSType::Windows) + .Cases("linux", "unix", "*nix", "posix", OSType::Linux) + .Default(OSType::Unknown); + }; + + EXPECT_EQ(OSType::Windows, Translate(llvm::StringRef("wind\0ws", 7))); + EXPECT_EQ(OSType::Windows, Translate("win32")); + EXPECT_EQ(OSType::Windows, Translate("winnt")); + + EXPECT_EQ(OSType::Linux, Translate("linux")); + EXPECT_EQ(OSType::Linux, Translate("unix")); + EXPECT_EQ(OSType::Linux, Translate("*nix")); + EXPECT_EQ(OSType::Linux, Translate("posix")); + + // Note that the whole null-terminator embedded string is required for the + // case to match. + EXPECT_EQ(OSType::Unknown, Translate("wind")); + EXPECT_EQ(OSType::Unknown, Translate("Windows")); + EXPECT_EQ(OSType::Unknown, Translate("")); +} + +TEST(StringSwitchTest, CasesLower) { + enum class OSType { Windows, Linux, Unknown }; + + auto Translate = [](StringRef S) { + return llvm::StringSwitch<OSType>(S) + .CasesLower("wind\0ws", "win32", "winnt", OSType::Windows) + .CasesLower("linux", "unix", "*nix", "posix", OSType::Linux) + .Default(OSType::Unknown); + }; + + EXPECT_EQ(OSType::Windows, Translate(llvm::StringRef("WIND\0WS", 7))); + EXPECT_EQ(OSType::Windows, Translate("WIN32")); + EXPECT_EQ(OSType::Windows, Translate("WINNT")); + + EXPECT_EQ(OSType::Linux, Translate("LINUX")); + EXPECT_EQ(OSType::Linux, Translate("UNIX")); + EXPECT_EQ(OSType::Linux, Translate("*NIX")); + EXPECT_EQ(OSType::Linux, Translate("POSIX")); + + EXPECT_EQ(OSType::Windows, Translate(llvm::StringRef("wind\0ws", 7))); + EXPECT_EQ(OSType::Linux, Translate("linux")); + + EXPECT_EQ(OSType::Unknown, Translate("wind")); + EXPECT_EQ(OSType::Unknown, Translate("")); +} diff --git a/unittests/ADT/TestGraph.h b/unittests/ADT/TestGraph.h new file mode 100644 index 000000000000..1c495d24bc0d --- /dev/null +++ b/unittests/ADT/TestGraph.h @@ -0,0 +1,251 @@ +//===- llvm/unittest/ADT/TestGraph.h - Graph for testing ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Common graph data structure for testing. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/GraphTraits.h" +#include <cassert> +#include <climits> +#include <utility> + +#ifndef LLVM_UNITTESTS_ADT_TEST_GRAPH_H +#define LLVM_UNITTESTS_ADT_TEST_GRAPH_H + +namespace llvm { + +/// Graph<N> - A graph with N nodes. Note that N can be at most 8. +template <unsigned N> +class Graph { +private: + // Disable copying. + Graph(const Graph&); + Graph& operator=(const Graph&); + + static void ValidateIndex(unsigned Idx) { + assert(Idx < N && "Invalid node index!"); + } +public: + + /// NodeSubset - A subset of the graph's nodes. + class NodeSubset { + typedef unsigned char BitVector; // Where the limitation N <= 8 comes from. + BitVector Elements; + NodeSubset(BitVector e) : Elements(e) {} + public: + /// NodeSubset - Default constructor, creates an empty subset. + NodeSubset() : Elements(0) { + assert(N <= sizeof(BitVector)*CHAR_BIT && "Graph too big!"); + } + + /// Comparison operators. + bool operator==(const NodeSubset &other) const { + return other.Elements == this->Elements; + } + bool operator!=(const NodeSubset &other) const { + return !(*this == other); + } + + /// AddNode - Add the node with the given index to the subset. + void AddNode(unsigned Idx) { + ValidateIndex(Idx); + Elements |= 1U << Idx; + } + + /// DeleteNode - Remove the node with the given index from the subset. + void DeleteNode(unsigned Idx) { + ValidateIndex(Idx); + Elements &= ~(1U << Idx); + } + + /// count - Return true if the node with the given index is in the subset. + bool count(unsigned Idx) { + ValidateIndex(Idx); + return (Elements & (1U << Idx)) != 0; + } + + /// isEmpty - Return true if this is the empty set. + bool isEmpty() const { + return Elements == 0; + } + + /// isSubsetOf - Return true if this set is a subset of the given one. + bool isSubsetOf(const NodeSubset &other) const { + return (this->Elements | other.Elements) == other.Elements; + } + + /// Complement - Return the complement of this subset. + NodeSubset Complement() const { + return ~(unsigned)this->Elements & ((1U << N) - 1); + } + + /// Join - Return the union of this subset and the given one. + NodeSubset Join(const NodeSubset &other) const { + return this->Elements | other.Elements; + } + + /// Meet - Return the intersection of this subset and the given one. + NodeSubset Meet(const NodeSubset &other) const { + return this->Elements & other.Elements; + } + }; + + /// NodeType - Node index and set of children of the node. + typedef std::pair<unsigned, NodeSubset> NodeType; + +private: + /// Nodes - The list of nodes for this graph. + NodeType Nodes[N]; +public: + + /// Graph - Default constructor. Creates an empty graph. + Graph() { + // Let each node know which node it is. This allows us to find the start of + // the Nodes array given a pointer to any element of it. + for (unsigned i = 0; i != N; ++i) + Nodes[i].first = i; + } + + /// AddEdge - Add an edge from the node with index FromIdx to the node with + /// index ToIdx. + void AddEdge(unsigned FromIdx, unsigned ToIdx) { + ValidateIndex(FromIdx); + Nodes[FromIdx].second.AddNode(ToIdx); + } + + /// DeleteEdge - Remove the edge (if any) from the node with index FromIdx to + /// the node with index ToIdx. + void DeleteEdge(unsigned FromIdx, unsigned ToIdx) { + ValidateIndex(FromIdx); + Nodes[FromIdx].second.DeleteNode(ToIdx); + } + + /// AccessNode - Get a pointer to the node with the given index. + NodeType *AccessNode(unsigned Idx) const { + ValidateIndex(Idx); + // The constant cast is needed when working with GraphTraits, which insists + // on taking a constant Graph. + return const_cast<NodeType *>(&Nodes[Idx]); + } + + /// NodesReachableFrom - Return the set of all nodes reachable from the given + /// node. + NodeSubset NodesReachableFrom(unsigned Idx) const { + // This algorithm doesn't scale, but that doesn't matter given the small + // size of our graphs. + NodeSubset Reachable; + + // The initial node is reachable. + Reachable.AddNode(Idx); + do { + NodeSubset Previous(Reachable); + + // Add in all nodes which are children of a reachable node. + for (unsigned i = 0; i != N; ++i) + if (Previous.count(i)) + Reachable = Reachable.Join(Nodes[i].second); + + // If nothing changed then we have found all reachable nodes. + if (Reachable == Previous) + return Reachable; + + // Rinse and repeat. + } while (1); + } + + /// ChildIterator - Visit all children of a node. + class ChildIterator { + friend class Graph; + + /// FirstNode - Pointer to first node in the graph's Nodes array. + NodeType *FirstNode; + /// Children - Set of nodes which are children of this one and that haven't + /// yet been visited. + NodeSubset Children; + + ChildIterator(); // Disable default constructor. + protected: + ChildIterator(NodeType *F, NodeSubset C) : FirstNode(F), Children(C) {} + + public: + /// ChildIterator - Copy constructor. + ChildIterator(const ChildIterator& other) : FirstNode(other.FirstNode), + Children(other.Children) {} + + /// Comparison operators. + bool operator==(const ChildIterator &other) const { + return other.FirstNode == this->FirstNode && + other.Children == this->Children; + } + bool operator!=(const ChildIterator &other) const { + return !(*this == other); + } + + /// Prefix increment operator. + ChildIterator& operator++() { + // Find the next unvisited child node. + for (unsigned i = 0; i != N; ++i) + if (Children.count(i)) { + // Remove that child - it has been visited. This is the increment! + Children.DeleteNode(i); + return *this; + } + assert(false && "Incrementing end iterator!"); + return *this; // Avoid compiler warnings. + } + + /// Postfix increment operator. + ChildIterator operator++(int) { + ChildIterator Result(*this); + ++(*this); + return Result; + } + + /// Dereference operator. + NodeType *operator*() { + // Find the next unvisited child node. + for (unsigned i = 0; i != N; ++i) + if (Children.count(i)) + // Return a pointer to it. + return FirstNode + i; + assert(false && "Dereferencing end iterator!"); + return nullptr; // Avoid compiler warning. + } + }; + + /// child_begin - Return an iterator pointing to the first child of the given + /// node. + static ChildIterator child_begin(NodeType *Parent) { + return ChildIterator(Parent - Parent->first, Parent->second); + } + + /// child_end - Return the end iterator for children of the given node. + static ChildIterator child_end(NodeType *Parent) { + return ChildIterator(Parent - Parent->first, NodeSubset()); + } +}; + +template <unsigned N> +struct GraphTraits<Graph<N> > { + typedef typename Graph<N>::NodeType *NodeRef; + typedef typename Graph<N>::ChildIterator ChildIteratorType; + + static NodeRef getEntryNode(const Graph<N> &G) { return G.AccessNode(0); } + static ChildIteratorType child_begin(NodeRef Node) { + return Graph<N>::child_begin(Node); + } + static ChildIteratorType child_end(NodeRef Node) { + return Graph<N>::child_end(Node); + } +}; + +} // End namespace llvm + +#endif diff --git a/unittests/ADT/TripleTest.cpp b/unittests/ADT/TripleTest.cpp index 984f4a2a595a..c80477f6ddc9 100644 --- a/unittests/ADT/TripleTest.cpp +++ b/unittests/ADT/TripleTest.cpp @@ -87,6 +87,12 @@ TEST(TripleTest, ParsedIDs) { EXPECT_EQ(Triple::ELFIAMCU, T.getOS()); EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("i386-pc-contiki-unknown"); + EXPECT_EQ(Triple::x86, T.getArch()); + EXPECT_EQ(Triple::PC, T.getVendor()); + EXPECT_EQ(Triple::Contiki, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("x86_64-pc-linux-gnu"); EXPECT_EQ(Triple::x86_64, T.getArch()); EXPECT_EQ(Triple::PC, T.getVendor()); @@ -200,6 +206,12 @@ TEST(TripleTest, ParsedIDs) { EXPECT_EQ(Triple::CloudABI, T.getOS()); EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("x86_64-unknown-fuchsia"); + EXPECT_EQ(Triple::x86_64, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::Fuchsia, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("wasm32-unknown-unknown"); EXPECT_EQ(Triple::wasm32, T.getArch()); EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); @@ -248,6 +260,30 @@ TEST(TripleTest, ParsedIDs) { EXPECT_EQ(Triple::AMDHSA, T.getOS()); EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("amdgcn-amd-amdhsa-opencl"); + EXPECT_EQ(Triple::amdgcn, T.getArch()); + EXPECT_EQ(Triple::AMD, T.getVendor()); + EXPECT_EQ(Triple::AMDHSA, T.getOS()); + EXPECT_EQ(Triple::OpenCL, T.getEnvironment()); + + T = Triple("riscv32-unknown-unknown"); + EXPECT_EQ(Triple::riscv32, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("riscv64-unknown-linux"); + EXPECT_EQ(Triple::riscv64, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::Linux, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("riscv64-unknown-freebsd"); + EXPECT_EQ(Triple::riscv64, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::FreeBSD, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("huh"); EXPECT_EQ(Triple::UnknownArch, T.getArch()); } @@ -302,60 +338,74 @@ TEST(TripleTest, Normalization) { // Check that normalizing a permutated set of valid components returns a // triple with the unpermuted components. - StringRef C[4]; - for (int Arch = 1+Triple::UnknownArch; Arch <= Triple::LastArchType; ++Arch) { + // + // We don't check every possible combination. For the set of architectures A, + // vendors V, operating systems O, and environments E, that would require |A| + // * |V| * |O| * |E| * 4! tests. Instead we check every option for any given + // slot and make sure it gets normalized to the correct position from every + // permutation. This should cover the core logic while being a tractable + // number of tests at (|A| + |V| + |O| + |E|) * 4!. + auto FirstArchType = Triple::ArchType(Triple::UnknownArch + 1); + auto FirstVendorType = Triple::VendorType(Triple::UnknownVendor + 1); + auto FirstOSType = Triple::OSType(Triple::UnknownOS + 1); + auto FirstEnvType = Triple::EnvironmentType(Triple::UnknownEnvironment + 1); + StringRef InitialC[] = {Triple::getArchTypeName(FirstArchType), + Triple::getVendorTypeName(FirstVendorType), + Triple::getOSTypeName(FirstOSType), + Triple::getEnvironmentTypeName(FirstEnvType)}; + for (int Arch = FirstArchType; Arch <= Triple::LastArchType; ++Arch) { + StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]}; C[0] = Triple::getArchTypeName(Triple::ArchType(Arch)); - for (int Vendor = 1+Triple::UnknownVendor; Vendor <= Triple::LastVendorType; - ++Vendor) { - C[1] = Triple::getVendorTypeName(Triple::VendorType(Vendor)); - for (int OS = 1+Triple::UnknownOS; OS <= Triple::LastOSType; ++OS) { - if (OS == Triple::Win32) - continue; - - C[2] = Triple::getOSTypeName(Triple::OSType(OS)); - - std::string E = Join(C[0], C[1], C[2]); - EXPECT_EQ(E, Triple::normalize(Join(C[0], C[1], C[2]))); - - EXPECT_EQ(E, Triple::normalize(Join(C[0], C[2], C[1]))); - EXPECT_EQ(E, Triple::normalize(Join(C[1], C[2], C[0]))); - EXPECT_EQ(E, Triple::normalize(Join(C[1], C[0], C[2]))); - EXPECT_EQ(E, Triple::normalize(Join(C[2], C[0], C[1]))); - EXPECT_EQ(E, Triple::normalize(Join(C[2], C[1], C[0]))); - - for (int Env = 1 + Triple::UnknownEnvironment; Env <= Triple::LastEnvironmentType; - ++Env) { - C[3] = Triple::getEnvironmentTypeName(Triple::EnvironmentType(Env)); - - std::string F = Join(C[0], C[1], C[2], C[3]); - EXPECT_EQ(F, Triple::normalize(Join(C[0], C[1], C[2], C[3]))); - - EXPECT_EQ(F, Triple::normalize(Join(C[0], C[1], C[3], C[2]))); - EXPECT_EQ(F, Triple::normalize(Join(C[0], C[2], C[3], C[1]))); - EXPECT_EQ(F, Triple::normalize(Join(C[0], C[2], C[1], C[3]))); - EXPECT_EQ(F, Triple::normalize(Join(C[0], C[3], C[1], C[2]))); - EXPECT_EQ(F, Triple::normalize(Join(C[0], C[3], C[2], C[1]))); - EXPECT_EQ(F, Triple::normalize(Join(C[1], C[2], C[3], C[0]))); - EXPECT_EQ(F, Triple::normalize(Join(C[1], C[2], C[0], C[3]))); - EXPECT_EQ(F, Triple::normalize(Join(C[1], C[3], C[0], C[2]))); - EXPECT_EQ(F, Triple::normalize(Join(C[1], C[3], C[2], C[0]))); - EXPECT_EQ(F, Triple::normalize(Join(C[1], C[0], C[2], C[3]))); - EXPECT_EQ(F, Triple::normalize(Join(C[1], C[0], C[3], C[2]))); - EXPECT_EQ(F, Triple::normalize(Join(C[2], C[3], C[0], C[1]))); - EXPECT_EQ(F, Triple::normalize(Join(C[2], C[3], C[1], C[0]))); - EXPECT_EQ(F, Triple::normalize(Join(C[2], C[0], C[1], C[3]))); - EXPECT_EQ(F, Triple::normalize(Join(C[2], C[0], C[3], C[1]))); - EXPECT_EQ(F, Triple::normalize(Join(C[2], C[1], C[3], C[0]))); - EXPECT_EQ(F, Triple::normalize(Join(C[2], C[1], C[0], C[3]))); - EXPECT_EQ(F, Triple::normalize(Join(C[3], C[0], C[1], C[2]))); - EXPECT_EQ(F, Triple::normalize(Join(C[3], C[0], C[2], C[1]))); - EXPECT_EQ(F, Triple::normalize(Join(C[3], C[1], C[2], C[0]))); - EXPECT_EQ(F, Triple::normalize(Join(C[3], C[1], C[0], C[2]))); - EXPECT_EQ(F, Triple::normalize(Join(C[3], C[2], C[0], C[1]))); - EXPECT_EQ(F, Triple::normalize(Join(C[3], C[2], C[1], C[0]))); - } - } - } + std::string E = Join(C[0], C[1], C[2]); + int I[] = {0, 1, 2}; + do { + EXPECT_EQ(E, Triple::normalize(Join(C[I[0]], C[I[1]], C[I[2]]))); + } while (std::next_permutation(std::begin(I), std::end(I))); + std::string F = Join(C[0], C[1], C[2], C[3]); + int J[] = {0, 1, 2, 3}; + do { + EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]]))); + } while (std::next_permutation(std::begin(J), std::end(J))); + } + for (int Vendor = FirstVendorType; Vendor <= Triple::LastVendorType; + ++Vendor) { + StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]}; + C[1] = Triple::getVendorTypeName(Triple::VendorType(Vendor)); + std::string E = Join(C[0], C[1], C[2]); + int I[] = {0, 1, 2}; + do { + EXPECT_EQ(E, Triple::normalize(Join(C[I[0]], C[I[1]], C[I[2]]))); + } while (std::next_permutation(std::begin(I), std::end(I))); + std::string F = Join(C[0], C[1], C[2], C[3]); + int J[] = {0, 1, 2, 3}; + do { + EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]]))); + } while (std::next_permutation(std::begin(J), std::end(J))); + } + for (int OS = FirstOSType; OS <= Triple::LastOSType; ++OS) { + if (OS == Triple::Win32) + continue; + StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]}; + C[2] = Triple::getOSTypeName(Triple::OSType(OS)); + std::string E = Join(C[0], C[1], C[2]); + int I[] = {0, 1, 2}; + do { + EXPECT_EQ(E, Triple::normalize(Join(C[I[0]], C[I[1]], C[I[2]]))); + } while (std::next_permutation(std::begin(I), std::end(I))); + std::string F = Join(C[0], C[1], C[2], C[3]); + int J[] = {0, 1, 2, 3}; + do { + EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]]))); + } while (std::next_permutation(std::begin(J), std::end(J))); + } + for (int Env = FirstEnvType; Env <= Triple::LastEnvironmentType; ++Env) { + StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]}; + C[3] = Triple::getEnvironmentTypeName(Triple::EnvironmentType(Env)); + std::string F = Join(C[0], C[1], C[2], C[3]); + int J[] = {0, 1, 2, 3}; + do { + EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]]))); + } while (std::next_permutation(std::begin(J), std::end(J))); } // Various real-world funky triples. The value returned by GCC's config.sub @@ -527,6 +577,16 @@ TEST(TripleTest, BitWidthPredicates) { EXPECT_FALSE(T.isArch16Bit()); EXPECT_TRUE(T.isArch32Bit()); EXPECT_FALSE(T.isArch64Bit()); + + T.setArch(Triple::riscv32); + EXPECT_FALSE(T.isArch16Bit()); + EXPECT_TRUE(T.isArch32Bit()); + EXPECT_FALSE(T.isArch64Bit()); + + T.setArch(Triple::riscv64); + EXPECT_FALSE(T.isArch16Bit()); + EXPECT_FALSE(T.isArch32Bit()); + EXPECT_TRUE(T.isArch64Bit()); } TEST(TripleTest, BitWidthArchVariants) { @@ -617,6 +677,14 @@ TEST(TripleTest, BitWidthArchVariants) { T.setArch(Triple::wasm64); EXPECT_EQ(Triple::wasm32, T.get32BitArchVariant().getArch()); EXPECT_EQ(Triple::wasm64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::riscv32); + EXPECT_EQ(Triple::riscv32, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::riscv64, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::riscv64); + EXPECT_EQ(Triple::riscv32, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::riscv64, T.get64BitArchVariant().getArch()); } TEST(TripleTest, EndianArchVariants) { @@ -874,112 +942,6 @@ TEST(TripleTest, NormalizeWindows) { } TEST(TripleTest, getARMCPUForArch) { - // Standard ARM Architectures. - { - llvm::Triple Triple("armv4-unknown-eabi"); - EXPECT_EQ("strongarm", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv4t-unknown-eabi"); - EXPECT_EQ("arm7tdmi", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv5-unknown-eabi"); - EXPECT_EQ("arm10tdmi", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv5t-unknown-eabi"); - EXPECT_EQ("arm10tdmi", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv5e-unknown-eabi"); - EXPECT_EQ("arm1022e", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv5tej-unknown-eabi"); - EXPECT_EQ("arm926ej-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6-unknown-eabi"); - EXPECT_EQ("arm1136jf-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6j-unknown-eabi"); - EXPECT_EQ("arm1136jf-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6k-unknown-eabi"); - EXPECT_EQ("arm1176j-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6kz-unknown-eabi"); - EXPECT_EQ("arm1176jzf-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6zk-unknown-eabi"); - EXPECT_EQ("arm1176jzf-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6t2-unknown-eabi"); - EXPECT_EQ("arm1156t2-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6m-unknown-eabi"); - EXPECT_EQ("cortex-m0", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7-unknown-eabi"); - EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7a-unknown-eabi"); - EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7m-unknown-eabi"); - EXPECT_EQ("cortex-m3", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7r-unknown-eabi"); - EXPECT_EQ("cortex-r4", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7r-unknown-eabi"); - EXPECT_EQ("cortex-r4", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7r-unknown-eabi"); - EXPECT_EQ("cortex-r4", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7r-unknown-eabi"); - EXPECT_EQ("cortex-r4", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv8a-unknown-eabi"); - EXPECT_EQ("cortex-a53", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv8.1a-unknown-eabi"); - EXPECT_EQ("generic", Triple.getARMCPUForArch()); - } - // Non-synonym names, using -march style, not default arch. - { - llvm::Triple Triple("arm"); - EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch("armv7-a")); - } - { - llvm::Triple Triple("arm"); - EXPECT_EQ("cortex-m3", Triple.getARMCPUForArch("armv7-m")); - } - { - llvm::Triple Triple("arm"); - EXPECT_EQ("cortex-a53", Triple.getARMCPUForArch("armv8")); - } - { - llvm::Triple Triple("arm"); - EXPECT_EQ("cortex-a53", Triple.getARMCPUForArch("armv8-a")); - } // Platform specific defaults. { llvm::Triple Triple("arm--nacl"); @@ -1003,18 +965,6 @@ TEST(TripleTest, getARMCPUForArch) { } // Some alternative architectures { - llvm::Triple Triple("xscale-unknown-eabi"); - EXPECT_EQ("xscale", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("iwmmxt-unknown-eabi"); - EXPECT_EQ("iwmmxt", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv7s-apple-ios7"); - EXPECT_EQ("swift", Triple.getARMCPUForArch()); - } - { llvm::Triple Triple("armv7k-apple-ios9"); EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch()); } @@ -1026,18 +976,6 @@ TEST(TripleTest, 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()); - } - { - llvm::Triple Triple("armv7l-linux-gnueabihf"); - EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6sm-apple-ios7"); - EXPECT_EQ("cortex-m0", Triple.getARMCPUForArch()); - } // armeb is permitted, but armebeb is not { llvm::Triple Triple("armeb-none-eabi"); @@ -1051,15 +989,6 @@ TEST(TripleTest, getARMCPUForArch) { llvm::Triple Triple("armebv6eb-none-eabi"); EXPECT_EQ("", Triple.getARMCPUForArch()); } - // armebv6 and armv6eb are permitted, but armebv6eb is not - { - llvm::Triple Triple("armebv6-non-eabi"); - EXPECT_EQ("arm1136jf-s", Triple.getARMCPUForArch()); - } - { - llvm::Triple Triple("armv6eb-none-eabi"); - EXPECT_EQ("arm1136jf-s", Triple.getARMCPUForArch()); - } // xscaleeb is permitted, but armebxscale is not { llvm::Triple Triple("xscaleeb-none-eabi"); @@ -1095,62 +1024,18 @@ TEST(TripleTest, ParseARMArch) { EXPECT_EQ(Triple::arm, T.getArch()); } { - Triple T = Triple("armv6t2"); - EXPECT_EQ(Triple::arm, T.getArch()); - } - { - Triple T = Triple("armv8"); - EXPECT_EQ(Triple::arm, T.getArch()); - } - { Triple T = Triple("armeb"); EXPECT_EQ(Triple::armeb, T.getArch()); } - { - Triple T = Triple("armv5eb"); - EXPECT_EQ(Triple::armeb, T.getArch()); - } - { - Triple T = Triple("armebv7m"); - EXPECT_EQ(Triple::armeb, T.getArch()); - } - { - Triple T = Triple("armv7eb"); - EXPECT_EQ(Triple::armeb, T.getArch()); - } // THUMB { Triple T = Triple("thumb"); EXPECT_EQ(Triple::thumb, T.getArch()); } { - Triple T = Triple("thumbv7a"); - EXPECT_EQ(Triple::thumb, T.getArch()); - } - { Triple T = Triple("thumbeb"); EXPECT_EQ(Triple::thumbeb, T.getArch()); } - { - Triple T = Triple("thumbv4teb"); - EXPECT_EQ(Triple::thumbeb, T.getArch()); - } - { - Triple T = Triple("thumbebv7"); - EXPECT_EQ(Triple::thumbeb, T.getArch()); - } - { - Triple T = Triple("armv6m"); - EXPECT_EQ(Triple::thumb, T.getArch()); - } - { - Triple T = Triple("thumbv2"); - EXPECT_EQ(Triple::UnknownArch, T.getArch()); - } - { - Triple T = Triple("thumbebv6eb"); - EXPECT_EQ(Triple::UnknownArch, T.getArch()); - } // AARCH64 { Triple T = Triple("arm64"); @@ -1164,13 +1049,5 @@ TEST(TripleTest, ParseARMArch) { Triple T = Triple("aarch64_be"); EXPECT_EQ(Triple::aarch64_be, T.getArch()); } - { - Triple T = Triple("aarch64be"); - EXPECT_EQ(Triple::UnknownArch, T.getArch()); - } - { - Triple T = Triple("arm64be"); - EXPECT_EQ(Triple::UnknownArch, T.getArch()); - } } } // end anonymous namespace diff --git a/unittests/ADT/TwineTest.cpp b/unittests/ADT/TwineTest.cpp index 9683e97511b6..0b7e88dee500 100644 --- a/unittests/ADT/TwineTest.cpp +++ b/unittests/ADT/TwineTest.cpp @@ -7,8 +7,10 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/Twine.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" using namespace llvm; @@ -30,6 +32,7 @@ TEST(TwineTest, Construction) { EXPECT_EQ("hi", Twine(StringRef(std::string("hi"))).str()); EXPECT_EQ("hi", Twine(StringRef("hithere", 2)).str()); EXPECT_EQ("hi", Twine(SmallString<4>("hi")).str()); + EXPECT_EQ("hi", Twine(formatv("{0}", "hi")).str()); } TEST(TwineTest, Numbers) { @@ -65,6 +68,10 @@ TEST(TwineTest, Concat) { repr(Twine().concat(Twine("hi")))); EXPECT_EQ("(Twine smallstring:\"hi\" empty)", repr(Twine().concat(Twine(SmallString<5>("hi"))))); + EXPECT_EQ("(Twine formatv:\"howdy\" empty)", + repr(Twine(formatv("howdy")).concat(Twine()))); + EXPECT_EQ("(Twine formatv:\"howdy\" empty)", + repr(Twine().concat(Twine(formatv("howdy"))))); EXPECT_EQ("(Twine smallstring:\"hey\" cstring:\"there\")", repr(Twine(SmallString<7>("hey")).concat(Twine("there")))); @@ -89,6 +96,25 @@ TEST(TwineTest, toNullTerminatedStringRef) { EXPECT_EQ(0, *Twine(SmallString<11>("hello")) .toNullTerminatedStringRef(storage) .end()); + EXPECT_EQ(0, *Twine(formatv("{0}{1}", "how", "dy")) + .toNullTerminatedStringRef(storage) + .end()); +} + +TEST(TwineTest, LazyEvaluation) { + struct formatter : FormatAdapter<int> { + explicit formatter(int &Count) : FormatAdapter(0), Count(Count) {} + int &Count; + + void format(raw_ostream &OS, StringRef Style) { ++Count; } + }; + + int Count = 0; + formatter Formatter(Count); + (void)Twine(formatv("{0}", Formatter)); + EXPECT_EQ(0, Count); + (void)Twine(formatv("{0}", Formatter)).str(); + EXPECT_EQ(1, Count); } // I suppose linking in the entire code generator to add a unit test to check diff --git a/unittests/ADT/ilistTest.cpp b/unittests/ADT/ilistTest.cpp deleted file mode 100644 index 377dcc044ddb..000000000000 --- a/unittests/ADT/ilistTest.cpp +++ /dev/null @@ -1,99 +0,0 @@ -//===- llvm/unittest/ADT/APInt.cpp - APInt 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/ilist.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/ilist_node.h" -#include "gtest/gtest.h" -#include <ostream> - -using namespace llvm; - -namespace { - -struct Node : ilist_node<Node> { - int Value; - - Node() {} - Node(int Value) : Value(Value) {} - Node(const Node&) = default; - ~Node() { Value = -1; } -}; - -TEST(ilistTest, Basic) { - ilist<Node> List; - List.push_back(Node(1)); - EXPECT_EQ(1, List.back().Value); - EXPECT_EQ(nullptr, List.getPrevNode(List.back())); - EXPECT_EQ(nullptr, List.getNextNode(List.back())); - - List.push_back(Node(2)); - EXPECT_EQ(2, List.back().Value); - EXPECT_EQ(2, List.getNextNode(List.front())->Value); - EXPECT_EQ(1, List.getPrevNode(List.back())->Value); - - const ilist<Node> &ConstList = List; - EXPECT_EQ(2, ConstList.back().Value); - EXPECT_EQ(2, ConstList.getNextNode(ConstList.front())->Value); - EXPECT_EQ(1, ConstList.getPrevNode(ConstList.back())->Value); -} - -TEST(ilistTest, SpliceOne) { - ilist<Node> List; - List.push_back(1); - - // The single-element splice operation supports noops. - List.splice(List.begin(), List, List.begin()); - EXPECT_EQ(1u, List.size()); - EXPECT_EQ(1, List.front().Value); - EXPECT_TRUE(std::next(List.begin()) == List.end()); - - // Altenative noop. Move the first element behind itself. - List.push_back(2); - List.push_back(3); - List.splice(std::next(List.begin()), List, List.begin()); - EXPECT_EQ(3u, List.size()); - EXPECT_EQ(1, List.front().Value); - EXPECT_EQ(2, std::next(List.begin())->Value); - EXPECT_EQ(3, List.back().Value); -} - -TEST(ilistTest, UnsafeClear) { - ilist<Node> List; - - // Before even allocating a sentinel. - List.clearAndLeakNodesUnsafely(); - EXPECT_EQ(0u, List.size()); - - // Empty list with sentinel. - ilist<Node>::iterator E = List.end(); - List.clearAndLeakNodesUnsafely(); - EXPECT_EQ(0u, List.size()); - // The sentinel shouldn't change. - EXPECT_TRUE(E == List.end()); - - // List with contents. - List.push_back(1); - ASSERT_EQ(1u, List.size()); - Node *N = &*List.begin(); - EXPECT_EQ(1, N->Value); - List.clearAndLeakNodesUnsafely(); - EXPECT_EQ(0u, List.size()); - ASSERT_EQ(1, N->Value); - delete N; - - // List is still functional. - List.push_back(5); - List.push_back(6); - ASSERT_EQ(2u, List.size()); - EXPECT_EQ(5, List.front().Value); - EXPECT_EQ(6, List.back().Value); -} - -} diff --git a/unittests/Analysis/BranchProbabilityInfoTest.cpp b/unittests/Analysis/BranchProbabilityInfoTest.cpp new file mode 100644 index 000000000000..cbf8b50c7623 --- /dev/null +++ b/unittests/Analysis/BranchProbabilityInfoTest.cpp @@ -0,0 +1,88 @@ +//===- BranchProbabilityInfoTest.cpp - BranchProbabilityInfo 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/BranchProbabilityInfo.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Instructions.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 { + +struct BranchProbabilityInfoTest : public testing::Test { + std::unique_ptr<BranchProbabilityInfo> BPI; + std::unique_ptr<DominatorTree> DT; + std::unique_ptr<LoopInfo> LI; + LLVMContext C; + + BranchProbabilityInfo &buildBPI(Function &F) { + DT.reset(new DominatorTree(F)); + LI.reset(new LoopInfo(*DT)); + BPI.reset(new BranchProbabilityInfo(F, *LI)); + return *BPI; + } + + std::unique_ptr<Module> makeLLVMModule() { + const char *ModuleString = "define void @f() { exit: ret void }\n"; + SMDiagnostic Err; + return parseAssemblyString(ModuleString, Err, C); + } +}; + +TEST_F(BranchProbabilityInfoTest, StressUnreachableHeuristic) { + auto M = makeLLVMModule(); + Function *F = M->getFunction("f"); + + // define void @f() { + // entry: + // switch i32 undef, label %exit, [ + // i32 0, label %preexit + // ... ;;< Add lots of cases to stress the heuristic. + // ] + // preexit: + // unreachable + // exit: + // ret void + // } + + auto *ExitBB = &F->back(); + auto *EntryBB = BasicBlock::Create(C, "entry", F, /*insertBefore=*/ExitBB); + + auto *PreExitBB = + BasicBlock::Create(C, "preexit", F, /*insertBefore=*/ExitBB); + new UnreachableInst(C, PreExitBB); + + unsigned NumCases = 4096; + auto *I32 = IntegerType::get(C, 32); + auto *Undef = UndefValue::get(I32); + auto *Switch = SwitchInst::Create(Undef, ExitBB, NumCases, EntryBB); + for (unsigned I = 0; I < NumCases; ++I) + Switch->addCase(ConstantInt::get(I32, I), PreExitBB); + + BranchProbabilityInfo &BPI = buildBPI(*F); + + // FIXME: This doesn't seem optimal. Since all of the cases handled by the + // switch have the *same* destination block ("preexit"), shouldn't it be the + // hot one? I'd expect the results to be reversed here... + EXPECT_FALSE(BPI.isEdgeHot(EntryBB, PreExitBB)); + EXPECT_TRUE(BPI.isEdgeHot(EntryBB, ExitBB)); +} + +} // end anonymous namespace +} // end namespace llvm diff --git a/unittests/Analysis/CGSCCPassManagerTest.cpp b/unittests/Analysis/CGSCCPassManagerTest.cpp index 224f2df13181..ab5d1862c23e 100644 --- a/unittests/Analysis/CGSCCPassManagerTest.cpp +++ b/unittests/Analysis/CGSCCPassManagerTest.cpp @@ -22,16 +22,13 @@ using namespace llvm; namespace { -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) { @@ -40,48 +37,44 @@ public: } private: - static char PassID; + friend AnalysisInfoMixin<TestModuleAnalysis>; + static AnalysisKey Key; int &Runs; }; -char TestModuleAnalysis::PassID; +AnalysisKey TestModuleAnalysis::Key; -class TestSCCAnalysis { +class TestSCCAnalysis : public AnalysisInfoMixin<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) { + Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &) { ++Runs; return Result(C.size()); } private: - static char PassID; + friend AnalysisInfoMixin<TestSCCAnalysis>; + static AnalysisKey Key; int &Runs; }; -char TestSCCAnalysis::PassID; +AnalysisKey TestSCCAnalysis::Key; -class TestFunctionAnalysis { +class TestFunctionAnalysis : public AnalysisInfoMixin<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) { @@ -95,22 +88,24 @@ public: } private: - static char PassID; + friend AnalysisInfoMixin<TestFunctionAnalysis>; + static AnalysisKey Key; int &Runs; }; -char TestFunctionAnalysis::PassID; +AnalysisKey TestFunctionAnalysis::Key; -class TestImmutableFunctionAnalysis { +class TestImmutableFunctionAnalysis + : public AnalysisInfoMixin<TestImmutableFunctionAnalysis> { public: struct Result { - bool invalidate(Function &, const PreservedAnalyses &) { return false; } + bool invalidate(Function &, const PreservedAnalyses &, + FunctionAnalysisManager::Invalidator &) { + return false; + } }; - static void *ID() { return (void *)&PassID; } - static StringRef name() { return "TestImmutableFunctionAnalysis"; } - TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {} Result run(Function &F, FunctionAnalysisManager &AM) { @@ -119,93 +114,47 @@ public: } private: - static char PassID; + friend AnalysisInfoMixin<TestImmutableFunctionAnalysis>; + static AnalysisKey Key; int &Runs; }; -char TestImmutableFunctionAnalysis::PassID; +AnalysisKey TestImmutableFunctionAnalysis::Key; -struct TestModulePass { - TestModulePass(int &RunCount) : RunCount(RunCount) {} +struct LambdaModulePass : public PassInfoMixin<LambdaModulePass> { + template <typename T> + LambdaModulePass(T &&Arg) : Func(std::forward<T>(Arg)) {} - PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { - ++RunCount; - (void)AM.getResult<TestModuleAnalysis>(M); - return PreservedAnalyses::all(); + PreservedAnalyses run(Module &F, ModuleAnalysisManager &AM) { + return Func(F, AM); } - static StringRef name() { return "TestModulePass"; } - - int &RunCount; + std::function<PreservedAnalyses(Module &, ModuleAnalysisManager &)> Func; }; -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()); - } - } +struct LambdaSCCPass : public PassInfoMixin<LambdaSCCPass> { + template <typename T> LambdaSCCPass(T &&Arg) : Func(std::forward<T>(Arg)) {} - return PreservedAnalyses::all(); + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR) { + return Func(C, AM, CG, UR); } - static StringRef name() { return "TestSCCPass"; } - - int &RunCount; - int &AnalyzedInstrCount; - int &AnalyzedSCCFunctionCount; - int &AnalyzedModuleFunctionCount; - bool OnlyUseCachedResults; + std::function<PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &, + LazyCallGraph &, CGSCCUpdateResult &)> + Func; }; -struct TestFunctionPass { - TestFunctionPass(int &RunCount) : RunCount(RunCount) {} +struct LambdaFunctionPass : public PassInfoMixin<LambdaFunctionPass> { + template <typename T> + LambdaFunctionPass(T &&Arg) : Func(std::forward<T>(Arg)) {} - PreservedAnalyses run(Function &F, AnalysisManager<Function> &) { - ++RunCount; - return PreservedAnalyses::none(); + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { + return Func(F, AM); } - static StringRef name() { return "TestFunctionPass"; } - - int &RunCount; + std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func; }; std::unique_ptr<Module> parseIR(const char *IR) { @@ -217,40 +166,78 @@ std::unique_ptr<Module> parseIR(const char *IR) { 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); +class CGSCCPassManagerTest : public ::testing::Test { +protected: + LLVMContext Context; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + + std::unique_ptr<Module> M; + +public: + CGSCCPassManagerTest() + : FAM(/*DebugLogging*/ true), CGAM(/*DebugLogging*/ true), + MAM(/*DebugLogging*/ true), + M(parseIR( + // Define a module with the following call graph, where calls go + // out the bottom of nodes and enter the top: + // + // f + // |\ _ + // | \ / | + // g h1 | + // | | | + // | h2 | + // | | | + // | h3 | + // | / \_/ + // |/ + // x + // + "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")) { + MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); + MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); + MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); + CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(); }); + CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); }); + FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); + } +}; + +TEST_F(CGSCCPassManagerTest, Basic) { int FunctionAnalysisRuns = 0; FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); int ImmutableFunctionAnalysisRuns = 0; @@ -258,54 +245,842 @@ TEST(CGSCCPassManagerTest, Basic) { 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)); + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); CGSCCPassManager CGPM1(/*DebugLogging*/ true); + FunctionPassManager FPM1(/*DebugLogging*/ true); + int FunctionPassRunCount1 = 0; + FPM1.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { + ++FunctionPassRunCount1; + return PreservedAnalyses::none(); + })); + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + int SCCPassRunCount1 = 0; int AnalyzedInstrCount1 = 0; int AnalyzedSCCFunctionCount1 = 0; int AnalyzedModuleFunctionCount1 = 0; - CGPM1.addPass(TestSCCPass(SCCPassRunCount1, AnalyzedInstrCount1, - AnalyzedSCCFunctionCount1, - AnalyzedModuleFunctionCount1)); + CGPM1.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR) { + ++SCCPassRunCount1; + + const ModuleAnalysisManager &MAM = + AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); + FunctionAnalysisManager &FAM = + AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); + if (TestModuleAnalysis::Result *TMA = + MAM.getCachedResult<TestModuleAnalysis>( + *C.begin()->getFunction().getParent())) + AnalyzedModuleFunctionCount1 += TMA->FunctionCount; + + TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C, CG); + AnalyzedSCCFunctionCount1 += AR.FunctionCount; + for (LazyCallGraph::Node &N : C) { + TestFunctionAnalysis::Result &FAR = + FAM.getResult<TestFunctionAnalysis>(N.getFunction()); + AnalyzedInstrCount1 += FAR.InstructionCount; + + // Just ensure we get the immutable results. + (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction()); + } + + return PreservedAnalyses::all(); + })); + + FunctionPassManager FPM2(/*DebugLogging*/ true); + int FunctionPassRunCount2 = 0; + FPM2.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { + ++FunctionPassRunCount2; + return PreservedAnalyses::none(); + })); + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); - FunctionPassManager FPM1(/*DebugLogging*/ true); - int FunctionPassRunCount1 = 0; - FPM1.addPass(TestFunctionPass(FunctionPassRunCount1)); - CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + FunctionPassManager FPM3(/*DebugLogging*/ true); + int FunctionPassRunCount3 = 0; + FPM3.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { + ++FunctionPassRunCount3; + return PreservedAnalyses::none(); + })); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM3))); + MPM.run(*M, MAM); - EXPECT_EQ(1, ModulePassRunCount1); + EXPECT_EQ(4, SCCPassRunCount1); + EXPECT_EQ(6, FunctionPassRunCount1); + EXPECT_EQ(6, FunctionPassRunCount2); + EXPECT_EQ(6, FunctionPassRunCount3); 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); } +// Test that an SCC pass which fails to preserve a module analysis does in fact +// invalidate that module analysis. +TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesModuleAnalysis) { + int ModuleAnalysisRuns = 0; + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + + // The first CGSCC run we preserve everything and make sure that works and + // the module analysis is available in the second CGSCC run from the one + // required module pass above. + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + int CountFoundModuleAnalysis1 = 0; + CGPM1.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR) { + const auto &MAM = + AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); + auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( + *C.begin()->getFunction().getParent()); + + if (TMA) + ++CountFoundModuleAnalysis1; + + return PreservedAnalyses::all(); + })); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // The second CGSCC run checks that the module analysis got preserved the + // previous time and in one SCC fails to preserve it. + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + int CountFoundModuleAnalysis2 = 0; + CGPM2.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR) { + const auto &MAM = + AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); + auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( + *C.begin()->getFunction().getParent()); + + if (TMA) + ++CountFoundModuleAnalysis2; + + // Only fail to preserve analyses on one SCC and make sure that gets + // propagated. + return C.getName() == "(g)" ? PreservedAnalyses::none() + : PreservedAnalyses::all(); + })); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + // The third CGSCC run should fail to find a cached module analysis as it + // should have been invalidated by the above CGSCC run. + CGSCCPassManager CGPM3(/*DebugLogging*/ true); + int CountFoundModuleAnalysis3 = 0; + CGPM3.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR) { + const auto &MAM = + AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); + auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( + *C.begin()->getFunction().getParent()); + + if (TMA) + ++CountFoundModuleAnalysis3; + + return PreservedAnalyses::none(); + })); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); + + MPM.run(*M, MAM); + + EXPECT_EQ(1, ModuleAnalysisRuns); + EXPECT_EQ(4, CountFoundModuleAnalysis1); + EXPECT_EQ(4, CountFoundModuleAnalysis2); + EXPECT_EQ(0, CountFoundModuleAnalysis3); +} + +// Similar to the above, but test that this works for function passes embedded +// *within* a CGSCC layer. +TEST_F(CGSCCPassManagerTest, TestFunctionPassInsideCGSCCInvalidatesModuleAnalysis) { + int ModuleAnalysisRuns = 0; + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + + // The first run we preserve everything and make sure that works and the + // module analysis is available in the second run from the one required + // module pass above. + FunctionPassManager FPM1(/*DebugLogging*/ true); + // Start true and mark false if we ever failed to find a module analysis + // because we expect this to succeed for each SCC. + bool FoundModuleAnalysis1 = true; + FPM1.addPass( + LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { + const auto &MAM = + AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); + auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); + + if (!TMA) + FoundModuleAnalysis1 = false; + + return PreservedAnalyses::all(); + })); + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // The second run checks that the module analysis got preserved the previous + // time and in one function fails to preserve it. + FunctionPassManager FPM2(/*DebugLogging*/ true); + // Again, start true and mark false if we ever failed to find a module analysis + // because we expect this to succeed for each SCC. + bool FoundModuleAnalysis2 = true; + FPM2.addPass( + LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { + const auto &MAM = + AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); + auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); + + if (!TMA) + FoundModuleAnalysis2 = false; + + // Only fail to preserve analyses on one SCC and make sure that gets + // propagated. + return F.getName() == "h2" ? PreservedAnalyses::none() + : PreservedAnalyses::all(); + })); + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + // The third run should fail to find a cached module analysis as it should + // have been invalidated by the above run. + FunctionPassManager FPM3(/*DebugLogging*/ true); + // Start false and mark true if we ever *succeeded* to find a module + // analysis, as we expect this to fail for every function. + bool FoundModuleAnalysis3 = false; + FPM3.addPass( + LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { + const auto &MAM = + AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); + auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); + + if (TMA) + FoundModuleAnalysis3 = true; + + return PreservedAnalyses::none(); + })); + CGSCCPassManager CGPM3(/*DebugLogging*/ true); + CGPM3.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM3))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); + + MPM.run(*M, MAM); + + EXPECT_EQ(1, ModuleAnalysisRuns); + EXPECT_TRUE(FoundModuleAnalysis1); + EXPECT_TRUE(FoundModuleAnalysis2); + EXPECT_FALSE(FoundModuleAnalysis3); +} + +// Test that a Module pass which fails to preserve an SCC analysis in fact +// invalidates that analysis. +TEST_F(CGSCCPassManagerTest, TestModulePassInvalidatesSCCAnalysis) { + int SCCAnalysisRuns = 0; + CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, + CGSCCAnalysisManager, LazyCallGraph &, + CGSCCUpdateResult &>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // Now run a module pass that preserves the LazyCallGraph and the proxy but + // not the SCC analysis. + MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { + PreservedAnalyses PA; + PA.preserve<LazyCallGraphAnalysis>(); + PA.preserve<CGSCCAnalysisManagerModuleProxy>(); + PA.preserve<FunctionAnalysisManagerModuleProxy>(); + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again. This + // will trigger re-running it. + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, + CGSCCAnalysisManager, LazyCallGraph &, + CGSCCUpdateResult &>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + MPM.run(*M, MAM); + // Two runs and four SCCs. + EXPECT_EQ(2 * 4, SCCAnalysisRuns); +} + +// Check that marking the SCC analysis preserved is sufficient to avoid +// invaliadtion. This should only run the analysis once for each SCC. +TEST_F(CGSCCPassManagerTest, TestModulePassCanPreserveSCCAnalysis) { + int SCCAnalysisRuns = 0; + CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, + CGSCCAnalysisManager, LazyCallGraph &, + CGSCCUpdateResult &>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // Now run a module pass that preserves each of the necessary components + // (but not everything). + MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { + PreservedAnalyses PA; + PA.preserve<LazyCallGraphAnalysis>(); + PA.preserve<CGSCCAnalysisManagerModuleProxy>(); + PA.preserve<FunctionAnalysisManagerModuleProxy>(); + PA.preserve<TestSCCAnalysis>(); + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again but find + // it in the cache. + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, + CGSCCAnalysisManager, LazyCallGraph &, + CGSCCUpdateResult &>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + MPM.run(*M, MAM); + // Four SCCs + EXPECT_EQ(4, SCCAnalysisRuns); +} + +// Check that even when the analysis is preserved, if the SCC information isn't +// we still nuke things because the SCC keys could change. +TEST_F(CGSCCPassManagerTest, TestModulePassInvalidatesSCCAnalysisOnCGChange) { + int SCCAnalysisRuns = 0; + CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, + CGSCCAnalysisManager, LazyCallGraph &, + CGSCCUpdateResult &>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // Now run a module pass that preserves the analysis but not the call + // graph or proxy. + MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { + PreservedAnalyses PA; + PA.preserve<TestSCCAnalysis>(); + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again. + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, + CGSCCAnalysisManager, LazyCallGraph &, + CGSCCUpdateResult &>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + MPM.run(*M, MAM); + // Two runs and four SCCs. + EXPECT_EQ(2 * 4, SCCAnalysisRuns); +} + +// Test that an SCC pass which fails to preserve a Function analysis in fact +// invalidates that analysis. +TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesFunctionAnalysis) { + int FunctionAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + + // Create a very simple module with a single function and SCC to make testing + // these issues much easier. + std::unique_ptr<Module> M = parseIR("declare void @g()\n" + "declare void @h()\n" + "define void @f() {\n" + "entry:\n" + " call void @g()\n" + " call void @h()\n" + " ret void\n" + "}\n"); + + CGSCCPassManager CGPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + FunctionPassManager FPM1(/*DebugLogging*/ true); + FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + + // Now run a module pass that preserves the LazyCallGraph and proxy but not + // the SCC analysis. + CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &, + LazyCallGraph &, CGSCCUpdateResult &) { + PreservedAnalyses PA; + PA.preserve<LazyCallGraphAnalysis>(); + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again. This + // will trigger re-running it. + FunctionPassManager FPM2(/*DebugLogging*/ true); + FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); + + ModulePassManager MPM(/*DebugLogging*/ true); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); + MPM.run(*M, MAM); + EXPECT_EQ(2, FunctionAnalysisRuns); +} + +// Check that marking the SCC analysis preserved is sufficient. This should +// only run the analysis once the SCC. +TEST_F(CGSCCPassManagerTest, TestSCCPassCanPreserveFunctionAnalysis) { + int FunctionAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + + // Create a very simple module with a single function and SCC to make testing + // these issues much easier. + std::unique_ptr<Module> M = parseIR("declare void @g()\n" + "declare void @h()\n" + "define void @f() {\n" + "entry:\n" + " call void @g()\n" + " call void @h()\n" + " ret void\n" + "}\n"); + + CGSCCPassManager CGPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + FunctionPassManager FPM1(/*DebugLogging*/ true); + FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + + // Now run a module pass that preserves each of the necessary components + // (but + // not everything). + CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &, + LazyCallGraph &, CGSCCUpdateResult &) { + PreservedAnalyses PA; + PA.preserve<LazyCallGraphAnalysis>(); + PA.preserve<TestFunctionAnalysis>(); + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again but find + // it in the cache. + FunctionPassManager FPM2(/*DebugLogging*/ true); + FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); + + ModulePassManager MPM(/*DebugLogging*/ true); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); + MPM.run(*M, MAM); + EXPECT_EQ(1, FunctionAnalysisRuns); +} + +// Note that there is no test for invalidating the call graph or other +// structure with an SCC pass because there is no mechanism to do that from +// withinsuch a pass. Instead, such a pass has to directly update the call +// graph structure. + +// Test that a madule pass invalidates function analyses when the CGSCC proxies +// and pass manager. +TEST_F(CGSCCPassManagerTest, + TestModulePassInvalidatesFunctionAnalysisNestedInCGSCC) { + MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); + + int FunctionAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + FunctionPassManager FPM1(/*DebugLogging*/ true); + FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // Now run a module pass that preserves the LazyCallGraph and proxy but not + // the Function analysis. + MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { + PreservedAnalyses PA; + PA.preserve<LazyCallGraphAnalysis>(); + PA.preserve<CGSCCAnalysisManagerModuleProxy>(); + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again. This + // will trigger re-running it. + FunctionPassManager FPM2(/*DebugLogging*/ true); + FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + MPM.run(*M, MAM); + // Two runs and 6 functions. + EXPECT_EQ(2 * 6, FunctionAnalysisRuns); +} + +// Check that by marking the function pass and FAM proxy as preserved, this +// propagates all the way through. +TEST_F(CGSCCPassManagerTest, + TestModulePassCanPreserveFunctionAnalysisNestedInCGSCC) { + MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); + + int FunctionAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + FunctionPassManager FPM1(/*DebugLogging*/ true); + FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // Now run a module pass that preserves the LazyCallGraph, the proxy, and + // the Function analysis. + MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { + PreservedAnalyses PA; + PA.preserve<LazyCallGraphAnalysis>(); + PA.preserve<CGSCCAnalysisManagerModuleProxy>(); + PA.preserve<FunctionAnalysisManagerModuleProxy>(); + PA.preserve<TestFunctionAnalysis>(); + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again. This + // will trigger re-running it. + FunctionPassManager FPM2(/*DebugLogging*/ true); + FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + MPM.run(*M, MAM); + // One run and 6 functions. + EXPECT_EQ(6, FunctionAnalysisRuns); +} + +// Check that if the lazy call graph itself isn't preserved we still manage to +// invalidate everything. +TEST_F(CGSCCPassManagerTest, + TestModulePassInvalidatesFunctionAnalysisNestedInCGSCCOnCGChange) { + MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); + + int FunctionAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + + ModulePassManager MPM(/*DebugLogging*/ true); + + // First force the analysis to be run. + FunctionPassManager FPM1(/*DebugLogging*/ true); + FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGSCCPassManager CGPM1(/*DebugLogging*/ true); + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); + + // Now run a module pass that preserves the LazyCallGraph but not the + // Function analysis. + MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { + PreservedAnalyses PA; + return PA; + })); + + // And now a second CGSCC run which requires the SCC analysis again. This + // will trigger re-running it. + FunctionPassManager FPM2(/*DebugLogging*/ true); + FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + + MPM.run(*M, MAM); + // Two runs and 6 functions. + EXPECT_EQ(2 * 6, FunctionAnalysisRuns); +} + +/// A test CGSCC-level analysis pass which caches in its result another +/// analysis pass and uses it to serve queries. This requires the result to +/// invalidate itself when its dependency is invalidated. +/// +/// FIXME: Currently this doesn't also depend on a function analysis, and if it +/// did we would fail to invalidate it correctly. +struct TestIndirectSCCAnalysis + : public AnalysisInfoMixin<TestIndirectSCCAnalysis> { + struct Result { + Result(TestSCCAnalysis::Result &SCCDep, TestModuleAnalysis::Result &MDep) + : SCCDep(SCCDep), MDep(MDep) {} + TestSCCAnalysis::Result &SCCDep; + TestModuleAnalysis::Result &MDep; + + bool invalidate(LazyCallGraph::SCC &C, const PreservedAnalyses &PA, + CGSCCAnalysisManager::Invalidator &Inv) { + auto PAC = PA.getChecker<TestIndirectSCCAnalysis>(); + return !(PAC.preserved() || + PAC.preservedSet<AllAnalysesOn<LazyCallGraph::SCC>>()) || + Inv.invalidate<TestSCCAnalysis>(C, PA); + } + }; + + TestIndirectSCCAnalysis(int &Runs) : Runs(Runs) {} + + /// Run the analysis pass over the function and return a result. + Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG) { + ++Runs; + auto &SCCDep = AM.getResult<TestSCCAnalysis>(C, CG); + + auto &ModuleProxy = AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG); + const ModuleAnalysisManager &MAM = ModuleProxy.getManager(); + // For the test, we insist that the module analysis starts off in the + // cache. + auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>( + *C.begin()->getFunction().getParent()); + // Register the dependency as module analysis dependencies have to be + // pre-registered on the proxy. + ModuleProxy.registerOuterAnalysisInvalidation<TestModuleAnalysis, + TestIndirectSCCAnalysis>(); + + return Result(SCCDep, MDep); + } + +private: + friend AnalysisInfoMixin<TestIndirectSCCAnalysis>; + static AnalysisKey Key; + + int &Runs; +}; + +AnalysisKey TestIndirectSCCAnalysis::Key; + +/// A test analysis pass which caches in its result the result from the above +/// indirect analysis pass. +/// +/// This allows us to ensure that whenever an analysis pass is invalidated due +/// to dependencies (especially dependencies across IR units that trigger +/// asynchronous invalidation) we correctly detect that this may in turn cause +/// other analysis to be invalidated. +struct TestDoublyIndirectSCCAnalysis + : public AnalysisInfoMixin<TestDoublyIndirectSCCAnalysis> { + struct Result { + Result(TestIndirectSCCAnalysis::Result &IDep) : IDep(IDep) {} + TestIndirectSCCAnalysis::Result &IDep; + + bool invalidate(LazyCallGraph::SCC &C, const PreservedAnalyses &PA, + CGSCCAnalysisManager::Invalidator &Inv) { + auto PAC = PA.getChecker<TestDoublyIndirectSCCAnalysis>(); + return !(PAC.preserved() || + PAC.preservedSet<AllAnalysesOn<LazyCallGraph::SCC>>()) || + Inv.invalidate<TestIndirectSCCAnalysis>(C, PA); + } + }; + + TestDoublyIndirectSCCAnalysis(int &Runs) : Runs(Runs) {} + + /// Run the analysis pass over the function and return a result. + Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG) { + ++Runs; + auto &IDep = AM.getResult<TestIndirectSCCAnalysis>(C, CG); + return Result(IDep); + } + +private: + friend AnalysisInfoMixin<TestDoublyIndirectSCCAnalysis>; + static AnalysisKey Key; + + int &Runs; +}; + +AnalysisKey TestDoublyIndirectSCCAnalysis::Key; + +/// A test analysis pass which caches results from three different IR unit +/// layers and requires intermediate layers to correctly propagate the entire +/// distance. +struct TestIndirectFunctionAnalysis + : public AnalysisInfoMixin<TestIndirectFunctionAnalysis> { + struct Result { + Result(TestFunctionAnalysis::Result &FDep, TestModuleAnalysis::Result &MDep, + TestSCCAnalysis::Result &SCCDep) + : FDep(FDep), MDep(MDep), SCCDep(SCCDep) {} + TestFunctionAnalysis::Result &FDep; + TestModuleAnalysis::Result &MDep; + TestSCCAnalysis::Result &SCCDep; + + bool invalidate(Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + auto PAC = PA.getChecker<TestIndirectFunctionAnalysis>(); + return !(PAC.preserved() || + PAC.preservedSet<AllAnalysesOn<Function>>()) || + Inv.invalidate<TestFunctionAnalysis>(F, PA); + } + }; + + TestIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {} + + /// Run the analysis pass over the function and return a result. + Result run(Function &F, FunctionAnalysisManager &AM) { + ++Runs; + auto &FDep = AM.getResult<TestFunctionAnalysis>(F); + + auto &ModuleProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F); + const ModuleAnalysisManager &MAM = ModuleProxy.getManager(); + // For the test, we insist that the module analysis starts off in the + // cache. + auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); + // Register the dependency as module analysis dependencies have to be + // pre-registered on the proxy. + ModuleProxy.registerOuterAnalysisInvalidation< + TestModuleAnalysis, TestIndirectFunctionAnalysis>(); + + // For thet test we assume this is run inside a CGSCC pass manager. + const LazyCallGraph &CG = + *MAM.getCachedResult<LazyCallGraphAnalysis>(*F.getParent()); + auto &CGSCCProxy = AM.getResult<CGSCCAnalysisManagerFunctionProxy>(F); + const CGSCCAnalysisManager &CGAM = CGSCCProxy.getManager(); + // For the test, we insist that the CGSCC analysis starts off in the cache. + auto &SCCDep = + *CGAM.getCachedResult<TestSCCAnalysis>(*CG.lookupSCC(*CG.lookup(F))); + // Register the dependency as CGSCC analysis dependencies have to be + // pre-registered on the proxy. + CGSCCProxy.registerOuterAnalysisInvalidation< + TestSCCAnalysis, TestIndirectFunctionAnalysis>(); + + return Result(FDep, MDep, SCCDep); + } + +private: + friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>; + static AnalysisKey Key; + + int &Runs; +}; + +AnalysisKey TestIndirectFunctionAnalysis::Key; + +TEST_F(CGSCCPassManagerTest, TestIndirectAnalysisInvalidation) { + int ModuleAnalysisRuns = 0; + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); + + int SCCAnalysisRuns = 0, IndirectSCCAnalysisRuns = 0, + DoublyIndirectSCCAnalysisRuns = 0; + CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); + CGAM.registerPass( + [&] { return TestIndirectSCCAnalysis(IndirectSCCAnalysisRuns); }); + CGAM.registerPass([&] { + return TestDoublyIndirectSCCAnalysis(DoublyIndirectSCCAnalysisRuns); + }); + + int FunctionAnalysisRuns = 0, IndirectFunctionAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + FAM.registerPass([&] { + return TestIndirectFunctionAnalysis(IndirectFunctionAnalysisRuns); + }); + + ModulePassManager MPM(/*DebugLogging*/ true); + + int FunctionCount = 0; + CGSCCPassManager CGPM(/*DebugLogging*/ true); + // First just use the analysis to get the function count and preserve + // everything. + CGPM.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); + auto &IndirectResult = DoublyIndirectResult.IDep; + FunctionCount += IndirectResult.SCCDep.FunctionCount; + return PreservedAnalyses::all(); + })); + // Next, invalidate + // - both analyses for the (f) and (x) SCCs, + // - just the underlying (indirect) analysis for (g) SCC, and + // - just the direct analysis for (h1,h2,h3) SCC. + CGPM.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); + auto &IndirectResult = DoublyIndirectResult.IDep; + FunctionCount += IndirectResult.SCCDep.FunctionCount; + auto PA = PreservedAnalyses::none(); + if (C.getName() == "(g)") + PA.preserve<TestSCCAnalysis>(); + else if (C.getName() == "(h3, h1, h2)") + PA.preserve<TestIndirectSCCAnalysis>(); + return PA; + })); + // Finally, use the analysis again on each function, forcing re-computation + // for all of them. + CGPM.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); + auto &IndirectResult = DoublyIndirectResult.IDep; + FunctionCount += IndirectResult.SCCDep.FunctionCount; + return PreservedAnalyses::all(); + })); + + // Create a second CGSCC pass manager. This will cause the module-level + // invalidation to occur, which will force yet another invalidation of the + // indirect SCC-level analysis as the module analysis it depends on gets + // invalidated. + CGSCCPassManager CGPM2(/*DebugLogging*/ true); + CGPM2.addPass( + LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); + auto &IndirectResult = DoublyIndirectResult.IDep; + FunctionCount += IndirectResult.SCCDep.FunctionCount; + return PreservedAnalyses::all(); + })); + + // Add a requires pass to populate the module analysis and then our function + // pass pipeline. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); + // Now require the module analysis again (it will have been invalidated once) + // and then use it again from a function pass manager. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); + MPM.run(*M, MAM); + + // There are generally two possible runs for each of the four SCCs. But + // for one SCC, we only invalidate the indirect analysis so the base one + // only gets run seven times. + EXPECT_EQ(7, SCCAnalysisRuns); + // The module analysis pass should be run twice here. + EXPECT_EQ(2, ModuleAnalysisRuns); + // The indirect analysis is invalidated (either directly or indirectly) three + // times for each of four SCCs. + EXPECT_EQ(3 * 4, IndirectSCCAnalysisRuns); + EXPECT_EQ(3 * 4, DoublyIndirectSCCAnalysisRuns); + + // Four passes count each of six functions once (via SCCs). + EXPECT_EQ(4 * 6, FunctionCount); +} } diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt index 2292a454c27a..65a2ac094cff 100644 --- a/unittests/Analysis/CMakeLists.txt +++ b/unittests/Analysis/CMakeLists.txt @@ -8,13 +8,15 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(AnalysisTests AliasAnalysisTest.cpp BlockFrequencyInfoTest.cpp + BranchProbabilityInfoTest.cpp CallGraphTest.cpp CFGTest.cpp CGSCCPassManagerTest.cpp LazyCallGraphTest.cpp LoopPassManagerTest.cpp + MemoryBuiltinsTest.cpp ScalarEvolutionTest.cpp - MixedTBAATest.cpp + TBAATest.cpp ValueTrackingTest.cpp UnrollAnalyzer.cpp ) diff --git a/unittests/Analysis/CallGraphTest.cpp b/unittests/Analysis/CallGraphTest.cpp index af46291074c2..2d4e63facf3b 100644 --- a/unittests/Analysis/CallGraphTest.cpp +++ b/unittests/Analysis/CallGraphTest.cpp @@ -17,29 +17,29 @@ using namespace llvm; namespace { template <typename Ty> void canSpecializeGraphTraitsIterators(Ty *G) { - typedef typename GraphTraits<Ty *>::NodeType NodeTy; + typedef typename GraphTraits<Ty *>::NodeRef NodeRef; auto I = GraphTraits<Ty *>::nodes_begin(G); auto E = GraphTraits<Ty *>::nodes_end(G); auto X = ++I; // Should be able to iterate over all nodes of the graph. - static_assert(std::is_same<decltype(*I), NodeTy &>::value, + static_assert(std::is_same<decltype(*I), NodeRef>::value, "Node type does not match"); - static_assert(std::is_same<decltype(*X), NodeTy &>::value, + static_assert(std::is_same<decltype(*X), NodeRef>::value, "Node type does not match"); - static_assert(std::is_same<decltype(*E), NodeTy &>::value, + static_assert(std::is_same<decltype(*E), NodeRef>::value, "Node type does not match"); - NodeTy *N = GraphTraits<Ty *>::getEntryNode(G); + NodeRef N = GraphTraits<Ty *>::getEntryNode(G); - auto S = GraphTraits<NodeTy *>::child_begin(N); - auto F = GraphTraits<NodeTy *>::child_end(N); + auto S = GraphTraits<NodeRef>::child_begin(N); + auto F = GraphTraits<NodeRef>::child_end(N); // Should be able to iterate over immediate successors of a node. - static_assert(std::is_same<decltype(*S), NodeTy *>::value, + static_assert(std::is_same<decltype(*S), NodeRef>::value, "Node type does not match"); - static_assert(std::is_same<decltype(*F), NodeTy *>::value, + static_assert(std::is_same<decltype(*F), NodeRef>::value, "Node type does not match"); } diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp index 224a9458cc88..5bb9dec3449f 100644 --- a/unittests/Analysis/LazyCallGraphTest.cpp +++ b/unittests/Analysis/LazyCallGraphTest.cpp @@ -9,6 +9,7 @@ #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/AsmParser/Parser.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" @@ -120,6 +121,101 @@ static const char DiamondOfTriangles[] = " ret void\n" "}\n"; +/* + IR forming a reference graph with a diamond of triangle-shaped RefSCCs + + d1 + / \ + d3--d2 + / \ + b1 c1 + / \ / \ + b3--b2 c3--c2 + \ / + a1 + / \ + a3--a2 + + All call edges go up between RefSCCs, and clockwise around the RefSCC. + */ +static const char DiamondOfTrianglesRefGraph[] = + "define void @a1() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @a2, void ()** %a\n" + " store void ()* @b2, void ()** %a\n" + " store void ()* @c3, void ()** %a\n" + " ret void\n" + "}\n" + "define void @a2() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @a3, void ()** %a\n" + " ret void\n" + "}\n" + "define void @a3() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @a1, void ()** %a\n" + " ret void\n" + "}\n" + "define void @b1() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @b2, void ()** %a\n" + " store void ()* @d3, void ()** %a\n" + " ret void\n" + "}\n" + "define void @b2() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @b3, void ()** %a\n" + " ret void\n" + "}\n" + "define void @b3() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @b1, void ()** %a\n" + " ret void\n" + "}\n" + "define void @c1() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @c2, void ()** %a\n" + " store void ()* @d2, void ()** %a\n" + " ret void\n" + "}\n" + "define void @c2() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @c3, void ()** %a\n" + " ret void\n" + "}\n" + "define void @c3() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @c1, void ()** %a\n" + " ret void\n" + "}\n" + "define void @d1() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @d2, void ()** %a\n" + " ret void\n" + "}\n" + "define void @d2() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @d3, void ()** %a\n" + " ret void\n" + "}\n" + "define void @d3() {\n" + "entry:\n" + " %a = alloca void ()*\n" + " store void ()* @d1, void ()** %a\n" + " ret void\n" + "}\n"; + TEST(LazyCallGraphTest, BasicGraphFormation) { LLVMContext Context; std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); @@ -220,6 +316,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_FALSE(D.isChildOf(D)); EXPECT_FALSE(D.isAncestorOf(D)); EXPECT_FALSE(D.isDescendantOf(D)); + EXPECT_EQ(&D, &*CG.postorder_ref_scc_begin()); LazyCallGraph::RefSCC &C = *J++; ASSERT_EQ(1, C.size()); @@ -235,6 +332,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_FALSE(C.isChildOf(D)); EXPECT_TRUE(C.isAncestorOf(D)); EXPECT_FALSE(C.isDescendantOf(D)); + EXPECT_EQ(&C, &*std::next(CG.postorder_ref_scc_begin())); LazyCallGraph::RefSCC &B = *J++; ASSERT_EQ(1, B.size()); @@ -252,6 +350,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_FALSE(B.isDescendantOf(D)); EXPECT_FALSE(B.isAncestorOf(C)); EXPECT_FALSE(C.isAncestorOf(B)); + EXPECT_EQ(&B, &*std::next(CG.postorder_ref_scc_begin(), 2)); LazyCallGraph::RefSCC &A = *J++; ASSERT_EQ(1, A.size()); @@ -269,8 +368,10 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_TRUE(A.isAncestorOf(B)); EXPECT_TRUE(A.isAncestorOf(C)); EXPECT_TRUE(A.isAncestorOf(D)); + EXPECT_EQ(&A, &*std::next(CG.postorder_ref_scc_begin(), 3)); EXPECT_EQ(CG.postorder_ref_scc_end(), J); + EXPECT_EQ(J, std::next(CG.postorder_ref_scc_begin(), 4)); } static Function &lookupFunction(Module &M, StringRef Name) { @@ -478,7 +579,7 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { // Force the graph to be fully expanded. for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) - (void)RC; + dbgs() << "Formed RefSCC: " << RC << "\n"; LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); @@ -493,13 +594,21 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C); LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D); EXPECT_TRUE(ARC.isParentOf(BRC)); + EXPECT_TRUE(AC.isParentOf(BC)); EXPECT_TRUE(ARC.isParentOf(CRC)); + EXPECT_TRUE(AC.isParentOf(CC)); EXPECT_FALSE(ARC.isParentOf(DRC)); + EXPECT_FALSE(AC.isParentOf(DC)); EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(AC.isAncestorOf(DC)); EXPECT_FALSE(DRC.isChildOf(ARC)); + EXPECT_FALSE(DC.isChildOf(AC)); EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_TRUE(DC.isDescendantOf(AC)); EXPECT_TRUE(DRC.isChildOf(BRC)); + EXPECT_TRUE(DC.isChildOf(BC)); EXPECT_TRUE(DRC.isChildOf(CRC)); + EXPECT_TRUE(DC.isChildOf(CC)); EXPECT_EQ(2, std::distance(A.begin(), A.end())); ARC.insertOutgoingEdge(A, D, LazyCallGraph::Edge::Call); @@ -512,9 +621,13 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { // Only the parent and child tests sholud have changed. The rest of the graph // remains the same. EXPECT_TRUE(ARC.isParentOf(DRC)); + EXPECT_TRUE(AC.isParentOf(DC)); EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(AC.isAncestorOf(DC)); EXPECT_TRUE(DRC.isChildOf(ARC)); + EXPECT_TRUE(DC.isChildOf(AC)); EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_TRUE(DC.isDescendantOf(AC)); EXPECT_EQ(&AC, CG.lookupSCC(A)); EXPECT_EQ(&BC, CG.lookupSCC(B)); EXPECT_EQ(&CC, CG.lookupSCC(C)); @@ -527,11 +640,15 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { ARC.switchOutgoingEdgeToRef(A, D); EXPECT_FALSE(NewE.isCall()); - // Verify the graph remains the same. + // Verify the reference graph remains the same but the SCC graph is updated. EXPECT_TRUE(ARC.isParentOf(DRC)); + EXPECT_FALSE(AC.isParentOf(DC)); EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(AC.isAncestorOf(DC)); EXPECT_TRUE(DRC.isChildOf(ARC)); + EXPECT_FALSE(DC.isChildOf(AC)); EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_TRUE(DC.isDescendantOf(AC)); EXPECT_EQ(&AC, CG.lookupSCC(A)); EXPECT_EQ(&BC, CG.lookupSCC(B)); EXPECT_EQ(&CC, CG.lookupSCC(C)); @@ -544,11 +661,15 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { ARC.switchOutgoingEdgeToCall(A, D); EXPECT_TRUE(NewE.isCall()); - // Verify the graph remains the same. + // Verify the reference graph remains the same but the SCC graph is updated. EXPECT_TRUE(ARC.isParentOf(DRC)); + EXPECT_TRUE(AC.isParentOf(DC)); EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(AC.isAncestorOf(DC)); EXPECT_TRUE(DRC.isChildOf(ARC)); + EXPECT_TRUE(DC.isChildOf(AC)); EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_TRUE(DC.isDescendantOf(AC)); EXPECT_EQ(&AC, CG.lookupSCC(A)); EXPECT_EQ(&BC, CG.lookupSCC(B)); EXPECT_EQ(&CC, CG.lookupSCC(C)); @@ -563,9 +684,13 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { // Now the parent and child tests fail again but the rest remains the same. EXPECT_FALSE(ARC.isParentOf(DRC)); + EXPECT_FALSE(AC.isParentOf(DC)); EXPECT_TRUE(ARC.isAncestorOf(DRC)); + EXPECT_TRUE(AC.isAncestorOf(DC)); EXPECT_FALSE(DRC.isChildOf(ARC)); + EXPECT_FALSE(DC.isChildOf(AC)); EXPECT_TRUE(DRC.isDescendantOf(ARC)); + EXPECT_TRUE(DC.isDescendantOf(AC)); EXPECT_EQ(&AC, CG.lookupSCC(A)); EXPECT_EQ(&BC, CG.lookupSCC(B)); EXPECT_EQ(&CC, CG.lookupSCC(C)); @@ -599,7 +724,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertion) { // Force the graph to be fully expanded. for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) - (void)RC; + dbgs() << "Formed RefSCC: " << RC << "\n"; LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1")); LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2")); @@ -668,6 +793,16 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertion) { // And that ancestry tests have been updated. EXPECT_TRUE(ARC.isParentOf(CRC)); EXPECT_TRUE(BRC.isParentOf(CRC)); + + // And verify the post-order walk reflects the updated structure. + auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); + ASSERT_NE(I, E); + EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I; + ASSERT_NE(++I, E); + EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I; + ASSERT_NE(++I, E); + EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I; + EXPECT_EQ(++I, E); } TEST(LazyCallGraphTest, IncomingEdgeInsertionMidTraversal) { @@ -726,15 +861,574 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionMidTraversal) { 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. + // Verify that the post-order walk reflects the updated but still incomplete + // structure. + auto J = CG.postorder_ref_scc_begin(); + EXPECT_NE(J, E); + EXPECT_EQ(&CRC, &*J) << "Actual RefSCC: " << *J; + EXPECT_EQ(I, J); + + // Check that we can form the last two RefSCCs now, and even that we can do + // it with alternating iterators. + ++J; + EXPECT_NE(J, E); + LazyCallGraph::RefSCC &BRC = *J; + EXPECT_NE(&BRC, nullptr); + EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b1")))); + EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b2")))); + EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b3")))); + EXPECT_TRUE(BRC.isParentOf(CRC)); + ++I; + EXPECT_EQ(J, I); + EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I; + + // Increment I this time to form the new RefSCC, flopping back to the first + // iterator. ++I; EXPECT_NE(I, E); - LazyCallGraph::RefSCC &BRC = *I; + LazyCallGraph::RefSCC &ARC = *I; + EXPECT_NE(&ARC, nullptr); + EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a1")))); + EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a2")))); + EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a3")))); + EXPECT_TRUE(ARC.isParentOf(CRC)); + ++J; + EXPECT_EQ(I, J); + EXPECT_EQ(&ARC, &*J) << "Actual RefSCC: " << *J; + ++I; + EXPECT_EQ(E, I); + ++J; + EXPECT_EQ(E, J); +} + +TEST(LazyCallGraphTest, IncomingEdgeInsertionRefGraph) { + LLVMContext Context; + // Another variation of the above test but with all the edges switched to + // references rather than calls. + std::unique_ptr<Module> M = + parseAssembly(Context, DiamondOfTrianglesRefGraph); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) + dbgs() << "Formed RefSCC: " << RC << "\n"; + + LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1")); + LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2")); + LazyCallGraph::Node &A3 = *CG.lookup(lookupFunction(*M, "a3")); + 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 &D1 = *CG.lookup(lookupFunction(*M, "d1")); + LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2")); + LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "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: + // + // d1 | + // / \ | + // d3--d2---. | + // / \ | | + // b1 c1 | | + // / \ / \ / | + // b3--b2 c3--c2 | + // \ / | + // a1 | + // / \ | + // a3--a2 | + 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 SCC sets. + 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(ARC.isParentOf(CRC)); + EXPECT_TRUE(BRC.isParentOf(CRC)); + + // And verify the post-order walk reflects the updated structure. + auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); + ASSERT_NE(I, E); + EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I; + ASSERT_NE(++I, E); + EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I; + ASSERT_NE(++I, E); + EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I; + EXPECT_EQ(++I, E); +} + +TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeCallCycle) { + 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 @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::RefSCC &RC : CG.postorder_ref_sccs()) + dbgs() << "Formed RefSCC: " << RC << "\n"; + + 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); + LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A); + LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B); + LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C); + LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D); + + // Connect the top to the bottom forming a large RefSCC made up mostly of calls. + auto MergedRCs = ARC.insertIncomingRefEdge(D, A); + // Make sure we connected the nodes. + EXPECT_NE(D.begin(), D.end()); + EXPECT_EQ(&A, D.begin()->getNode()); + + // Check that we have the dead RCs, but ignore the order. + EXPECT_EQ(3u, MergedRCs.size()); + EXPECT_NE(find(MergedRCs, &BRC), MergedRCs.end()); + EXPECT_NE(find(MergedRCs, &CRC), MergedRCs.end()); + EXPECT_NE(find(MergedRCs, &DRC), MergedRCs.end()); + + // Make sure the nodes point to the right place now. + EXPECT_EQ(&ARC, CG.lookupRefSCC(A)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(B)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(C)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(D)); + + // Check that the SCCs are in postorder. + EXPECT_EQ(4, ARC.size()); + EXPECT_EQ(&DC, &ARC[0]); + EXPECT_EQ(&CC, &ARC[1]); + EXPECT_EQ(&BC, &ARC[2]); + EXPECT_EQ(&AC, &ARC[3]); + + // And verify the post-order walk reflects the updated structure. + auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); + ASSERT_NE(I, E); + EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I; + EXPECT_EQ(++I, E); +} + +TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeRefCycle) { + LLVMContext Context; + std::unique_ptr<Module> M = + parseAssembly(Context, "define void @a() {\n" + "entry:\n" + " %p = alloca void ()*\n" + " store void ()* @b, void ()** %p\n" + " ret void\n" + "}\n" + "define void @b() {\n" + "entry:\n" + " %p = alloca void ()*\n" + " store void ()* @c, void ()** %p\n" + " ret void\n" + "}\n" + "define void @c() {\n" + "entry:\n" + " %p = alloca void ()*\n" + " store void ()* @d, void ()** %p\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::RefSCC &RC : CG.postorder_ref_sccs()) + dbgs() << "Formed RefSCC: " << RC << "\n"; + + 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::RefSCC &ARC = *CG.lookupRefSCC(A); + LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B); + LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C); + LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D); + + // Connect the top to the bottom forming a large RefSCC made up just of + // references. + auto MergedRCs = ARC.insertIncomingRefEdge(D, A); + // Make sure we connected the nodes. + EXPECT_NE(D.begin(), D.end()); + EXPECT_EQ(&A, D.begin()->getNode()); + + // Check that we have the dead RCs, but ignore the order. + EXPECT_EQ(3u, MergedRCs.size()); + EXPECT_NE(find(MergedRCs, &BRC), MergedRCs.end()); + EXPECT_NE(find(MergedRCs, &CRC), MergedRCs.end()); + EXPECT_NE(find(MergedRCs, &DRC), MergedRCs.end()); + + // Make sure the nodes point to the right place now. + EXPECT_EQ(&ARC, CG.lookupRefSCC(A)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(B)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(C)); + EXPECT_EQ(&ARC, CG.lookupRefSCC(D)); + + // And verify the post-order walk reflects the updated structure. + auto I = CG.postorder_ref_scc_begin(), End = CG.postorder_ref_scc_end(); + ASSERT_NE(I, End); + EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I; + EXPECT_EQ(++I, End); +} + +TEST(LazyCallGraphTest, InlineAndDeleteFunction) { + LLVMContext Context; + // We want to ensure we can delete nodes from relatively complex graphs and + // so use the diamond of triangles graph defined above. + // + // The ascii diagram is repeated here for easy reference. + // + // d1 | + // / \ | + // d3--d2 | + // / \ | + // b1 c1 | + // / \ / \ | + // b3--b2 c3--c2 | + // \ / | + // a1 | + // / \ | + // a3--a2 | + // + std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) + dbgs() << "Formed RefSCC: " << RC << "\n"; + + LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1")); + LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2")); + LazyCallGraph::Node &A3 = *CG.lookup(lookupFunction(*M, "a3")); + 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 &D1 = *CG.lookup(lookupFunction(*M, "d1")); + LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2")); + LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "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())); + + // Delete d2 from the graph, as if it had been inlined. + // + // d1 | + // / / | + // d3--. | + // / \ | + // b1 c1 | + // / \ / \ | + // b3--b2 c3--c2 | + // \ / | + // a1 | + // / \ | + // a3--a2 | + + Function &D2F = D2.getFunction(); + CallInst *C1Call = nullptr, *D1Call = nullptr; + for (User *U : D2F.users()) { + CallInst *CI = dyn_cast<CallInst>(U); + ASSERT_TRUE(CI) << "Expected a call: " << *U; + if (CI->getParent()->getParent() == &C1.getFunction()) { + ASSERT_EQ(nullptr, C1Call) << "Found too many C1 calls: " << *CI; + C1Call = CI; + } else if (CI->getParent()->getParent() == &D1.getFunction()) { + ASSERT_EQ(nullptr, D1Call) << "Found too many D1 calls: " << *CI; + D1Call = CI; + } else { + FAIL() << "Found an unexpected call instruction: " << *CI; + } + } + ASSERT_NE(C1Call, nullptr); + ASSERT_NE(D1Call, nullptr); + ASSERT_EQ(&D2F, C1Call->getCalledFunction()); + ASSERT_EQ(&D2F, D1Call->getCalledFunction()); + C1Call->setCalledFunction(&D3.getFunction()); + D1Call->setCalledFunction(&D3.getFunction()); + ASSERT_EQ(0u, D2F.getNumUses()); + + // Insert new edges first. + CRC.insertTrivialCallEdge(C1, D3); + DRC.insertTrivialCallEdge(D1, D3); + + // Then remove the old ones. + LazyCallGraph::SCC &DC = *CG.lookupSCC(D2); + auto NewCs = DRC.switchInternalEdgeToRef(D1, D2); + EXPECT_EQ(&DC, CG.lookupSCC(D2)); + EXPECT_EQ(NewCs.end(), std::next(NewCs.begin())); + LazyCallGraph::SCC &NewDC = *NewCs.begin(); + EXPECT_EQ(&NewDC, CG.lookupSCC(D1)); + EXPECT_EQ(&NewDC, CG.lookupSCC(D3)); + auto NewRCs = DRC.removeInternalRefEdge(D1, D2); + EXPECT_EQ(&DRC, CG.lookupRefSCC(D2)); + EXPECT_EQ(NewRCs.end(), std::next(NewRCs.begin())); + LazyCallGraph::RefSCC &NewDRC = **NewRCs.begin(); + EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D1)); + EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3)); + EXPECT_FALSE(NewDRC.isParentOf(DRC)); + EXPECT_TRUE(CRC.isParentOf(DRC)); + EXPECT_TRUE(CRC.isParentOf(NewDRC)); + EXPECT_TRUE(DRC.isParentOf(NewDRC)); + CRC.removeOutgoingEdge(C1, D2); + EXPECT_FALSE(CRC.isParentOf(DRC)); + EXPECT_TRUE(CRC.isParentOf(NewDRC)); + EXPECT_TRUE(DRC.isParentOf(NewDRC)); + + // Now that we've updated the call graph, D2 is dead, so remove it. + CG.removeDeadFunction(D2F); + + // Check that the graph still looks the same. + EXPECT_EQ(&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(&NewDRC, CG.lookupRefSCC(D1)); + EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3)); + EXPECT_TRUE(CRC.isParentOf(NewDRC)); + + // Verify the post-order walk hasn't changed. + auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); + ASSERT_NE(I, E); + EXPECT_EQ(&NewDRC, &*I) << "Actual RefSCC: " << *I; + ASSERT_NE(++I, E); + EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I; + ASSERT_NE(++I, E); + EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I; + ASSERT_NE(++I, E); + EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I; + EXPECT_EQ(++I, E); +} + +TEST(LazyCallGraphTest, InlineAndDeleteFunctionMidTraversal) { + LLVMContext Context; + // This is the same fundamental test as the previous, but we perform it + // having only partially walked the RefSCCs of the graph. + // + // The ascii diagram is repeated here for easy reference. + // + // d1 | + // / \ | + // d3--d2 | + // / \ | + // b1 c1 | + // / \ / \ | + // b3--b2 c3--c2 | + // \ / | + // a1 | + // / \ | + // a3--a2 | + // + std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles); + LazyCallGraph CG(*M); + + // Walk the RefSCCs until we find the one containing 'c1'. + auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); + ASSERT_NE(I, E); + LazyCallGraph::RefSCC &DRC = *I; + ASSERT_NE(&DRC, nullptr); + ++I; + ASSERT_NE(I, E); + LazyCallGraph::RefSCC &CRC = *I; + ASSERT_NE(&CRC, nullptr); + + ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a1"))); + ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a2"))); + ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a3"))); + ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b1"))); + ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b2"))); + ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "b3"))); + LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1")); + LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2")); + LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3")); + LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1")); + LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2")); + LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3")); + ASSERT_EQ(&CRC, CG.lookupRefSCC(C1)); + ASSERT_EQ(&CRC, CG.lookupRefSCC(C2)); + ASSERT_EQ(&CRC, CG.lookupRefSCC(C3)); + ASSERT_EQ(&DRC, CG.lookupRefSCC(D1)); + ASSERT_EQ(&DRC, CG.lookupRefSCC(D2)); + ASSERT_EQ(&DRC, CG.lookupRefSCC(D3)); + ASSERT_EQ(1, std::distance(D2.begin(), D2.end())); + + // Delete d2 from the graph, as if it had been inlined. + // + // d1 | + // / / | + // d3--. | + // / \ | + // b1 c1 | + // / \ / \ | + // b3--b2 c3--c2 | + // \ / | + // a1 | + // / \ | + // a3--a2 | + + Function &D2F = D2.getFunction(); + CallInst *C1Call = nullptr, *D1Call = nullptr; + for (User *U : D2F.users()) { + CallInst *CI = dyn_cast<CallInst>(U); + ASSERT_TRUE(CI) << "Expected a call: " << *U; + if (CI->getParent()->getParent() == &C1.getFunction()) { + ASSERT_EQ(nullptr, C1Call) << "Found too many C1 calls: " << *CI; + C1Call = CI; + } else if (CI->getParent()->getParent() == &D1.getFunction()) { + ASSERT_EQ(nullptr, D1Call) << "Found too many D1 calls: " << *CI; + D1Call = CI; + } else { + FAIL() << "Found an unexpected call instruction: " << *CI; + } + } + ASSERT_NE(C1Call, nullptr); + ASSERT_NE(D1Call, nullptr); + ASSERT_EQ(&D2F, C1Call->getCalledFunction()); + ASSERT_EQ(&D2F, D1Call->getCalledFunction()); + C1Call->setCalledFunction(&D3.getFunction()); + D1Call->setCalledFunction(&D3.getFunction()); + ASSERT_EQ(0u, D2F.getNumUses()); + + // Insert new edges first. + CRC.insertTrivialCallEdge(C1, D3); + DRC.insertTrivialCallEdge(D1, D3); + + // Then remove the old ones. + LazyCallGraph::SCC &DC = *CG.lookupSCC(D2); + auto NewCs = DRC.switchInternalEdgeToRef(D1, D2); + EXPECT_EQ(&DC, CG.lookupSCC(D2)); + EXPECT_EQ(NewCs.end(), std::next(NewCs.begin())); + LazyCallGraph::SCC &NewDC = *NewCs.begin(); + EXPECT_EQ(&NewDC, CG.lookupSCC(D1)); + EXPECT_EQ(&NewDC, CG.lookupSCC(D3)); + auto NewRCs = DRC.removeInternalRefEdge(D1, D2); + EXPECT_EQ(&DRC, CG.lookupRefSCC(D2)); + EXPECT_EQ(NewRCs.end(), std::next(NewRCs.begin())); + LazyCallGraph::RefSCC &NewDRC = **NewRCs.begin(); + EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D1)); + EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3)); + EXPECT_FALSE(NewDRC.isParentOf(DRC)); + EXPECT_TRUE(CRC.isParentOf(DRC)); + EXPECT_TRUE(CRC.isParentOf(NewDRC)); + EXPECT_TRUE(DRC.isParentOf(NewDRC)); + CRC.removeOutgoingEdge(C1, D2); + EXPECT_FALSE(CRC.isParentOf(DRC)); + EXPECT_TRUE(CRC.isParentOf(NewDRC)); + EXPECT_TRUE(DRC.isParentOf(NewDRC)); + + // Now that we've updated the call graph, D2 is dead, so remove it. + CG.removeDeadFunction(D2F); + + // Check that the graph still looks the same. + EXPECT_EQ(&CRC, CG.lookupRefSCC(C1)); + EXPECT_EQ(&CRC, CG.lookupRefSCC(C2)); + EXPECT_EQ(&CRC, CG.lookupRefSCC(C3)); + EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D1)); + EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3)); + EXPECT_TRUE(CRC.isParentOf(NewDRC)); + + // Verify that the post-order walk reflects the updated but still incomplete + // structure. + auto J = CG.postorder_ref_scc_begin(); + EXPECT_NE(J, E); + EXPECT_EQ(&NewDRC, &*J) << "Actual RefSCC: " << *J; + ++J; + EXPECT_NE(J, E); + EXPECT_EQ(&CRC, &*J) << "Actual RefSCC: " << *J; + EXPECT_EQ(I, J); + + // Check that we can form the last two RefSCCs now, and even that we can do + // it with alternating iterators. + ++J; + EXPECT_NE(J, E); + LazyCallGraph::RefSCC &BRC = *J; EXPECT_NE(&BRC, nullptr); EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b1")))); EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b2")))); EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b3")))); - EXPECT_TRUE(BRC.isParentOf(CRC)); + EXPECT_TRUE(BRC.isParentOf(NewDRC)); + ++I; + EXPECT_EQ(J, I); + EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I; + + // Increment I this time to form the new RefSCC, flopping back to the first + // iterator. ++I; EXPECT_NE(I, E); LazyCallGraph::RefSCC &ARC = *I; @@ -742,9 +1436,15 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionMidTraversal) { EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a1")))); EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a2")))); EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a3")))); + EXPECT_TRUE(ARC.isParentOf(BRC)); EXPECT_TRUE(ARC.isParentOf(CRC)); + ++J; + EXPECT_EQ(I, J); + EXPECT_EQ(&ARC, &*J) << "Actual RefSCC: " << *J; ++I; EXPECT_EQ(E, I); + ++J; + EXPECT_EQ(E, J); } TEST(LazyCallGraphTest, InternalEdgeMutation) { @@ -796,7 +1496,7 @@ TEST(LazyCallGraphTest, InternalEdgeMutation) { // 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); + auto NewCs = RC.switchInternalEdgeToRef(B, C); EXPECT_EQ(&RC, CG.lookupRefSCC(A)); EXPECT_EQ(&RC, CG.lookupRefSCC(B)); EXPECT_EQ(&RC, CG.lookupRefSCC(C)); @@ -808,6 +1508,10 @@ TEST(LazyCallGraphTest, InternalEdgeMutation) { EXPECT_EQ(&*J++, CG.lookupSCC(A)); EXPECT_EQ(&*J++, CG.lookupSCC(C)); EXPECT_EQ(RC.end(), J); + // And the returned range must be the slice of this sequence containing new + // SCCs. + EXPECT_EQ(RC.begin(), NewCs.begin()); + EXPECT_EQ(std::prev(RC.end()), NewCs.end()); // 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 @@ -855,9 +1559,9 @@ TEST(LazyCallGraphTest, InternalEdgeRemoval) { 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); + auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); + LazyCallGraph::RefSCC &RC = *I; + EXPECT_EQ(E, std::next(I)); LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a")); LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b")); @@ -874,6 +1578,10 @@ TEST(LazyCallGraphTest, InternalEdgeRemoval) { EXPECT_EQ(&RC, CG.lookupRefSCC(A)); EXPECT_EQ(&RC, CG.lookupRefSCC(B)); EXPECT_EQ(&RC, CG.lookupRefSCC(C)); + auto J = CG.postorder_ref_scc_begin(); + EXPECT_EQ(I, J); + EXPECT_EQ(&RC, &*J); + EXPECT_EQ(E, std::next(J)); // Remove the edge from c -> a, which should leave 'a' in the original RefSCC // and form a new RefSCC for 'b' and 'c'. @@ -881,9 +1589,97 @@ TEST(LazyCallGraphTest, InternalEdgeRemoval) { 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]); + LazyCallGraph::RefSCC &RC2 = *CG.lookupRefSCC(B); + EXPECT_EQ(&RC2, CG.lookupRefSCC(C)); + EXPECT_EQ(&RC2, NewRCs[0]); + J = CG.postorder_ref_scc_begin(); + EXPECT_NE(I, J); + EXPECT_EQ(&RC2, &*J); + ++J; + EXPECT_EQ(I, J); + EXPECT_EQ(&RC, &*J); + ++I; + EXPECT_EQ(E, I); + ++J; + EXPECT_EQ(E, J); +} + +TEST(LazyCallGraphTest, InternalNoOpEdgeRemoval) { + LLVMContext Context; + // A graph with a single cycle formed both from call and reference edges + // which makes the reference edges trivial to delete. The graph looks like: + // + // Reference edges: a -> b -> c -> a + // Call edges: a -> c -> b -> a + std::unique_ptr<Module> M = parseAssembly( + Context, "define void @a(i8** %ptr) {\n" + "entry:\n" + " call void @b(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" + " call void @c(i8** %ptr)\n" + " ret void\n" + "}\n" + "define void @c(i8** %ptr) {\n" + "entry:\n" + " call void @a(i8** %ptr)\n" + " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + // Force the graph to be fully expanded. + auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end(); + LazyCallGraph::RefSCC &RC = *I; + EXPECT_EQ(E, std::next(I)); + + LazyCallGraph::SCC &C = *RC.begin(); + EXPECT_EQ(RC.end(), std::next(RC.begin())); + + LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c")); + EXPECT_EQ(&RC, CG.lookupRefSCC(AN)); + EXPECT_EQ(&RC, CG.lookupRefSCC(BN)); + EXPECT_EQ(&RC, CG.lookupRefSCC(CN)); + EXPECT_EQ(&C, CG.lookupSCC(AN)); + EXPECT_EQ(&C, CG.lookupSCC(BN)); + EXPECT_EQ(&C, CG.lookupSCC(CN)); + + // Remove the edge from a -> c which doesn't change anything. + SmallVector<LazyCallGraph::RefSCC *, 1> NewRCs = + RC.removeInternalRefEdge(AN, CN); + EXPECT_EQ(0u, NewRCs.size()); + EXPECT_EQ(&RC, CG.lookupRefSCC(AN)); + EXPECT_EQ(&RC, CG.lookupRefSCC(BN)); + EXPECT_EQ(&RC, CG.lookupRefSCC(CN)); + EXPECT_EQ(&C, CG.lookupSCC(AN)); + EXPECT_EQ(&C, CG.lookupSCC(BN)); + EXPECT_EQ(&C, CG.lookupSCC(CN)); + auto J = CG.postorder_ref_scc_begin(); + EXPECT_EQ(I, J); + EXPECT_EQ(&RC, &*J); + EXPECT_EQ(E, std::next(J)); + + // Remove the edge from b -> a and c -> b; again this doesn't change + // anything. + NewRCs = RC.removeInternalRefEdge(BN, AN); + NewRCs = RC.removeInternalRefEdge(CN, BN); + EXPECT_EQ(0u, NewRCs.size()); + EXPECT_EQ(&RC, CG.lookupRefSCC(AN)); + EXPECT_EQ(&RC, CG.lookupRefSCC(BN)); + EXPECT_EQ(&RC, CG.lookupRefSCC(CN)); + EXPECT_EQ(&C, CG.lookupSCC(AN)); + EXPECT_EQ(&C, CG.lookupSCC(BN)); + EXPECT_EQ(&C, CG.lookupSCC(CN)); + J = CG.postorder_ref_scc_begin(); + EXPECT_EQ(I, J); + EXPECT_EQ(&RC, &*J); + EXPECT_EQ(E, std::next(J)); } TEST(LazyCallGraphTest, InternalCallEdgeToRef) { @@ -918,54 +1714,59 @@ TEST(LazyCallGraphTest, InternalCallEdgeToRef) { EXPECT_EQ(CG.postorder_ref_scc_end(), I); EXPECT_EQ(1, RC.size()); - LazyCallGraph::SCC &CallC = *RC.begin(); + LazyCallGraph::SCC &AC = *RC.begin(); - 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)); + LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a")); + LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b")); + LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c")); + EXPECT_EQ(&AC, CG.lookupSCC(AN)); + EXPECT_EQ(&AC, CG.lookupSCC(BN)); + EXPECT_EQ(&AC, CG.lookupSCC(CN)); // 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); + auto NewCs = RC.switchInternalEdgeToRef(BN, AN); + EXPECT_EQ(NewCs.begin(), NewCs.end()); 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(&AC, CG.lookupSCC(AN)); + EXPECT_EQ(&AC, CG.lookupSCC(BN)); + EXPECT_EQ(&AC, CG.lookupSCC(CN)); // 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); + NewCs = RC.switchInternalEdgeToRef(CN, AN); + EXPECT_EQ(1, std::distance(NewCs.begin(), NewCs.end())); 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); + EXPECT_EQ(&AC, CG.lookupSCC(AN)); + LazyCallGraph::SCC &BC = *CG.lookupSCC(BN); + EXPECT_NE(&BC, &AC); + EXPECT_EQ(&BC, CG.lookupSCC(CN)); + auto J = RC.find(AC); + EXPECT_EQ(&AC, &*J); --J; - EXPECT_EQ(&BCallC, &*J); + EXPECT_EQ(&BC, &*J); EXPECT_EQ(RC.begin(), J); + EXPECT_EQ(J, NewCs.begin()); // 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); + NewCs = RC.switchInternalEdgeToRef(CN, BN); + EXPECT_EQ(1, std::distance(NewCs.begin(), NewCs.end())); 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); + EXPECT_EQ(&AC, CG.lookupSCC(AN)); + EXPECT_EQ(&BC, CG.lookupSCC(BN)); + LazyCallGraph::SCC &CC = *CG.lookupSCC(CN); + EXPECT_NE(&CC, &AC); + EXPECT_NE(&CC, &BC); + J = RC.find(AC); + EXPECT_EQ(&AC, &*J); --J; - EXPECT_EQ(&BCallC, &*J); + EXPECT_EQ(&BC, &*J); --J; - EXPECT_EQ(&CCallC, &*J); + EXPECT_EQ(&CC, &*J); EXPECT_EQ(RC.begin(), J); + EXPECT_EQ(J, NewCs.begin()); } TEST(LazyCallGraphTest, InternalRefEdgeToCall) { @@ -1135,11 +1936,11 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCallNoCycleInterleaved) { // 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); + RC.switchTrivialInternalEdgeToRef(B3, C3); + RC.switchTrivialInternalEdgeToRef(C2, B3); + RC.switchTrivialInternalEdgeToRef(B2, C2); + RC.switchTrivialInternalEdgeToRef(C1, B2); + RC.switchTrivialInternalEdgeToRef(B1, C1); // Check the initial post-order. We ensure this order with the extra edges // that are nuked above. @@ -1262,8 +2063,8 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCallBothPartitionAndMerge) { 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); + RC.switchTrivialInternalEdgeToRef(C, D); + RC.switchTrivialInternalEdgeToRef(D, E); // Check the initial post-order. We ensure this order with the extra edges // that are nuked above. @@ -1302,4 +2103,35 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCallBothPartitionAndMerge) { EXPECT_EQ(&AC, &RC[4]); } +// Test for IR containing constants using blockaddress constant expressions. +// These are truly unique constructs: constant expressions with non-constant +// operands. +TEST(LazyCallGraphTest, HandleBlockAddress) { + LLVMContext Context; + std::unique_ptr<Module> M = + parseAssembly(Context, "define void @f() {\n" + "entry:\n" + " ret void\n" + "bb:\n" + " unreachable\n" + "}\n" + "define void @g(i8** %ptr) {\n" + "entry:\n" + " store i8* blockaddress(@f, %bb), i8** %ptr\n" + " ret void\n" + "}\n"); + LazyCallGraph CG(*M); + + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC &FRC = *I++; + LazyCallGraph::RefSCC &GRC = *I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + LazyCallGraph::Node &F = *CG.lookup(lookupFunction(*M, "f")); + LazyCallGraph::Node &G = *CG.lookup(lookupFunction(*M, "g")); + EXPECT_EQ(&FRC, CG.lookupRefSCC(F)); + EXPECT_EQ(&GRC, CG.lookupRefSCC(G)); + EXPECT_TRUE(GRC.isParentOf(FRC)); +} + } diff --git a/unittests/Analysis/LoopPassManagerTest.cpp b/unittests/Analysis/LoopPassManagerTest.cpp index 5858e174aabb..092e4bf91133 100644 --- a/unittests/Analysis/LoopPassManagerTest.cpp +++ b/unittests/Analysis/LoopPassManagerTest.cpp @@ -7,8 +7,11 @@ // //===----------------------------------------------------------------------===// -#include "gtest/gtest.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/LoopPassManager.h" +#include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" @@ -16,14 +19,15 @@ #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/Support/SourceMgr.h" +#include "gtest/gtest.h" using namespace llvm; namespace { -class TestLoopAnalysis { - /// \brief Private static data to provide unique ID. - static char PassID; +class TestLoopAnalysis : public AnalysisInfoMixin<TestLoopAnalysis> { + friend AnalysisInfoMixin<TestLoopAnalysis>; + static AnalysisKey Key; int &Runs; @@ -33,16 +37,10 @@ public: 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) { + Result run(Loop &L, LoopAnalysisManager &AM) { ++Runs; int Count = 0; @@ -52,7 +50,7 @@ public: } }; -char TestLoopAnalysis::PassID; +AnalysisKey TestLoopAnalysis::Key; class TestLoopPass { std::vector<StringRef> &VisitedLoops; @@ -65,7 +63,7 @@ public: : VisitedLoops(VisitedLoops), AnalyzedBlockCount(AnalyzedBlockCount), OnlyUseCachedResults(OnlyUseCachedResults) {} - PreservedAnalyses run(Loop &L, AnalysisManager<Loop> &AM) { + PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM) { VisitedLoops.push_back(L.getName()); if (OnlyUseCachedResults) { @@ -91,7 +89,7 @@ class TestLoopInvalidatingPass { public: TestLoopInvalidatingPass(StringRef LoopName) : Name(LoopName) {} - PreservedAnalyses run(Loop &L, AnalysisManager<Loop> &AM) { + PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM) { return L.getName() == Name ? getLoopPassPreservedAnalyses() : PreservedAnalyses::all(); } @@ -152,6 +150,12 @@ TEST_F(LoopPassManagerTest, Basic) { // We need DominatorTreeAnalysis for LoopAnalysis. FAM.registerPass([&] { return DominatorTreeAnalysis(); }); FAM.registerPass([&] { return LoopAnalysis(); }); + // We also allow loop passes to assume a set of other analyses and so need + // those. + FAM.registerPass([&] { return AAManager(); }); + FAM.registerPass([&] { return TargetLibraryAnalysis(); }); + FAM.registerPass([&] { return ScalarEvolutionAnalysis(); }); + FAM.registerPass([&] { return AssumptionAnalysis(); }); FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); }); LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); }); diff --git a/unittests/Analysis/MemoryBuiltinsTest.cpp b/unittests/Analysis/MemoryBuiltinsTest.cpp new file mode 100644 index 000000000000..898ebeece4fb --- /dev/null +++ b/unittests/Analysis/MemoryBuiltinsTest.cpp @@ -0,0 +1,50 @@ +//===- MemoryBuiltinsTest.cpp - Tests for utilities in MemoryBuiltins.h ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { +// allocsize should not imply that a function is a traditional allocation +// function (e.g. that can be optimized out/...); it just tells us how many +// bytes exist at the pointer handed back by the function. +TEST(AllocSize, AllocationBuiltinsTest) { + LLVMContext Context; + Module M("", Context); + IntegerType *ArgTy = Type::getInt32Ty(Context); + + Function *AllocSizeFn = Function::Create( + FunctionType::get(Type::getInt8PtrTy(Context), {ArgTy}, false), + GlobalValue::ExternalLinkage, "F", &M); + + AllocSizeFn->addFnAttr(Attribute::getWithAllocSizeArgs(Context, 1, None)); + + // 100 is arbitrary. + std::unique_ptr<CallInst> Caller( + CallInst::Create(AllocSizeFn, {ConstantInt::get(ArgTy, 100)})); + + const TargetLibraryInfo *TLI = nullptr; + EXPECT_FALSE(isNoAliasFn(Caller.get(), TLI)); + EXPECT_FALSE(isMallocLikeFn(Caller.get(), TLI)); + EXPECT_FALSE(isCallocLikeFn(Caller.get(), TLI)); + EXPECT_FALSE(isAllocLikeFn(Caller.get(), TLI)); + + // FIXME: We might be able to treat allocsize functions as general allocation + // functions. For the moment, being conservative seems better (and we'd have + // to plumb stuff around `isNoAliasFn`). + EXPECT_FALSE(isAllocationFn(Caller.get(), TLI)); +} +} diff --git a/unittests/Analysis/MixedTBAATest.cpp b/unittests/Analysis/MixedTBAATest.cpp deleted file mode 100644 index d70324f2c6ab..000000000000 --- a/unittests/Analysis/MixedTBAATest.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//===--- MixedTBAATest.cpp - Mixed TBAA 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/TypeBasedAliasAnalysis.h" -#include "llvm/Analysis/AliasAnalysisEvaluator.h" -#include "llvm/Analysis/Passes.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/MDBuilder.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/Support/CommandLine.h" -#include "gtest/gtest.h" - -namespace llvm { -namespace { - -class MixedTBAATest : public testing::Test { -protected: - MixedTBAATest() : M("MixedTBAATest", C), MD(C) {} - - LLVMContext C; - Module M; - MDBuilder MD; - legacy::PassManager PM; -}; - -TEST_F(MixedTBAATest, MixedTBAA) { - // Setup function. - FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), - std::vector<Type *>(), false); - auto *F = cast<Function>(M.getOrInsertFunction("f", FTy)); - auto *BB = BasicBlock::Create(C, "entry", F); - auto IntType = Type::getInt32Ty(C); - auto PtrType = Type::getInt32PtrTy(C); - auto *Value = ConstantInt::get(IntType, 42); - auto *Addr = ConstantPointerNull::get(PtrType); - - auto *Store1 = new StoreInst(Value, Addr, BB); - auto *Store2 = new StoreInst(Value, Addr, BB); - ReturnInst::Create(C, nullptr, BB); - - // New TBAA metadata - { - auto RootMD = MD.createTBAARoot("Simple C/C++ TBAA"); - auto MD1 = MD.createTBAAScalarTypeNode("omnipotent char", RootMD); - auto MD2 = MD.createTBAAScalarTypeNode("int", MD1); - auto MD3 = MD.createTBAAStructTagNode(MD2, MD2, 0); - Store2->setMetadata(LLVMContext::MD_tbaa, MD3); - } - - // Old TBAA metadata - { - auto RootMD = MD.createTBAARoot("Simple C/C++ TBAA"); - auto MD1 = MD.createTBAANode("omnipotent char", RootMD); - auto MD2 = MD.createTBAANode("int", MD1); - Store1->setMetadata(LLVMContext::MD_tbaa, MD2); - } - - // Run the TBAA eval pass on a mixture of path-aware and non-path-aware TBAA. - // The order of the metadata (path-aware vs non-path-aware) is important, - // because the AA eval pass only runs one test per store-pair. - const char* args[] = { "MixedTBAATest", "-evaluate-aa-metadata" }; - cl::ParseCommandLineOptions(sizeof(args) / sizeof(const char*), args); - PM.add(createTypeBasedAAWrapperPass()); - PM.add(createAAEvalPass()); - PM.run(M); -} - -} // end anonymous namspace -} // end llvm namespace - diff --git a/unittests/Analysis/ScalarEvolutionTest.cpp b/unittests/Analysis/ScalarEvolutionTest.cpp index c4cd74f01c72..752cc8128248 100644 --- a/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/unittests/Analysis/ScalarEvolutionTest.cpp @@ -7,18 +7,23 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/AsmParser/Parser.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" namespace llvm { @@ -45,6 +50,15 @@ protected: LI.reset(new LoopInfo(*DT)); return ScalarEvolution(F, TLI, *AC, *DT, *LI); } + + void runWithFunctionAndSE( + Module &M, StringRef FuncName, + function_ref<void(Function &F, ScalarEvolution &SE)> Test) { + auto *F = M.getFunction(FuncName); + ASSERT_NE(F, nullptr) << "Could not find " << FuncName; + ScalarEvolution SE = buildSE(*F); + Test(*F, SE); + } }; TEST_F(ScalarEvolutionsTest, SCEVUnknownRAUW) { @@ -264,5 +278,259 @@ TEST_F(ScalarEvolutionsTest, SimplifiedPHI) { EXPECT_EQ(S1, S2); } +TEST_F(ScalarEvolutionsTest, ExpandPtrTypeSCEV) { + // It is to test the fix for PR30213. It exercises the branch in scev + // expansion when the value in ValueOffsetPair is a ptr and the offset + // is not divisible by the elem type size of value. + auto *I8Ty = Type::getInt8Ty(Context); + auto *I8PtrTy = Type::getInt8PtrTy(Context); + auto *I32Ty = Type::getInt32Ty(Context); + auto *I32PtrTy = Type::getInt32PtrTy(Context); + 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); + ReturnInst::Create(Context, nullptr, ExitBB); + + // loop: ; preds = %loop, %entry + // %alloca = alloca i32 + // %gep0 = getelementptr i32, i32* %alloca, i32 1 + // %bitcast1 = bitcast i32* %gep0 to i8* + // %gep1 = getelementptr i8, i8* %bitcast1, i32 1 + // %gep2 = getelementptr i8, i8* undef, i32 1 + // %cmp = icmp ult i8* undef, %bitcast1 + // %select = select i1 %cmp, i8* %gep1, i8* %gep2 + // %bitcast2 = bitcast i8* %select to i32* + // br i1 undef, label %loop, label %exit + + BranchInst *Br = BranchInst::Create( + LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB); + AllocaInst *Alloca = new AllocaInst(I32Ty, "alloca", Br); + ConstantInt *Ci32 = ConstantInt::get(Context, APInt(32, 1)); + GetElementPtrInst *Gep0 = + GetElementPtrInst::Create(I32Ty, Alloca, Ci32, "gep0", Br); + CastInst *CastA = + CastInst::CreateBitOrPointerCast(Gep0, I8PtrTy, "bitcast1", Br); + GetElementPtrInst *Gep1 = + GetElementPtrInst::Create(I8Ty, CastA, Ci32, "gep1", Br); + GetElementPtrInst *Gep2 = GetElementPtrInst::Create( + I8Ty, UndefValue::get(I8PtrTy), Ci32, "gep2", Br); + CmpInst *Cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, + UndefValue::get(I8PtrTy), CastA, "cmp", Br); + SelectInst *Sel = SelectInst::Create(Cmp, Gep1, Gep2, "select", Br); + CastInst *CastB = + CastInst::CreateBitOrPointerCast(Sel, I32PtrTy, "bitcast2", Br); + + ScalarEvolution SE = buildSE(*F); + auto *S = SE.getSCEV(CastB); + SCEVExpander Exp(SE, M.getDataLayout(), "expander"); + Value *V = + Exp.expandCodeFor(cast<SCEVAddExpr>(S)->getOperand(1), nullptr, Br); + + // Expect the expansion code contains: + // %0 = bitcast i32* %bitcast2 to i8* + // %uglygep = getelementptr i8, i8* %0, i64 -1 + // %1 = bitcast i8* %uglygep to i32* + EXPECT_TRUE(isa<BitCastInst>(V)); + Instruction *Gep = cast<Instruction>(V)->getPrevNode(); + EXPECT_TRUE(isa<GetElementPtrInst>(Gep)); + EXPECT_TRUE(isa<ConstantInt>(Gep->getOperand(1))); + EXPECT_EQ(cast<ConstantInt>(Gep->getOperand(1))->getSExtValue(), -1); + EXPECT_TRUE(isa<BitCastInst>(Gep->getPrevNode())); +} + +static Instruction *getInstructionByName(Function &F, StringRef Name) { + for (auto &I : instructions(F)) + if (I.getName() == Name) + return &I; + llvm_unreachable("Expected to find instruction!"); +} + +TEST_F(ScalarEvolutionsTest, CommutativeExprOperandOrder) { + LLVMContext C; + SMDiagnostic Err; + std::unique_ptr<Module> M = parseAssemblyString( + "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" " + " " + "@var_0 = external global i32, align 4" + "@var_1 = external global i32, align 4" + "@var_2 = external global i32, align 4" + " " + "declare i32 @unknown(i32, i32, i32)" + " " + "define void @f_1(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) " + " local_unnamed_addr { " + "entry: " + " %entrycond = icmp sgt i32 %n, 0 " + " br i1 %entrycond, label %loop.ph, label %for.end " + " " + "loop.ph: " + " %a = load i32, i32* %A, align 4 " + " %b = load i32, i32* %B, align 4 " + " %mul = mul nsw i32 %b, %a " + " %iv0.init = getelementptr inbounds i8, i8* %arr, i32 %mul " + " br label %loop " + " " + "loop: " + " %iv0 = phi i8* [ %iv0.inc, %loop ], [ %iv0.init, %loop.ph ] " + " %iv1 = phi i32 [ %iv1.inc, %loop ], [ 0, %loop.ph ] " + " %conv = trunc i32 %iv1 to i8 " + " store i8 %conv, i8* %iv0, align 1 " + " %iv0.inc = getelementptr inbounds i8, i8* %iv0, i32 %b " + " %iv1.inc = add nuw nsw i32 %iv1, 1 " + " %exitcond = icmp eq i32 %iv1.inc, %n " + " br i1 %exitcond, label %for.end.loopexit, label %loop " + " " + "for.end.loopexit: " + " br label %for.end " + " " + "for.end: " + " ret void " + "} " + " " + "define void @f_2(i32* %X, i32* %Y, i32* %Z) { " + " %x = load i32, i32* %X " + " %y = load i32, i32* %Y " + " %z = load i32, i32* %Z " + " ret void " + "} " + " " + "define void @f_3() { " + " %x = load i32, i32* @var_0" + " %y = load i32, i32* @var_1" + " %z = load i32, i32* @var_2" + " ret void" + "} " + " " + "define void @f_4(i32 %a, i32 %b, i32 %c) { " + " %x = call i32 @unknown(i32 %a, i32 %b, i32 %c)" + " %y = call i32 @unknown(i32 %b, i32 %c, i32 %a)" + " %z = call i32 @unknown(i32 %c, i32 %a, i32 %b)" + " ret void" + "} " + , + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithFunctionAndSE(*M, "f_1", [&](Function &F, ScalarEvolution &SE) { + auto *IV0 = getInstructionByName(F, "iv0"); + auto *IV0Inc = getInstructionByName(F, "iv0.inc"); + + auto *FirstExprForIV0 = SE.getSCEV(IV0); + auto *FirstExprForIV0Inc = SE.getSCEV(IV0Inc); + auto *SecondExprForIV0 = SE.getSCEV(IV0); + + EXPECT_TRUE(isa<SCEVAddRecExpr>(FirstExprForIV0)); + EXPECT_TRUE(isa<SCEVAddRecExpr>(FirstExprForIV0Inc)); + EXPECT_TRUE(isa<SCEVAddRecExpr>(SecondExprForIV0)); + }); + + auto CheckCommutativeMulExprs = [&](ScalarEvolution &SE, const SCEV *A, + const SCEV *B, const SCEV *C) { + EXPECT_EQ(SE.getMulExpr(A, B), SE.getMulExpr(B, A)); + EXPECT_EQ(SE.getMulExpr(B, C), SE.getMulExpr(C, B)); + EXPECT_EQ(SE.getMulExpr(A, C), SE.getMulExpr(C, A)); + + SmallVector<const SCEV *, 3> Ops0 = {A, B, C}; + SmallVector<const SCEV *, 3> Ops1 = {A, C, B}; + SmallVector<const SCEV *, 3> Ops2 = {B, A, C}; + SmallVector<const SCEV *, 3> Ops3 = {B, C, A}; + SmallVector<const SCEV *, 3> Ops4 = {C, B, A}; + SmallVector<const SCEV *, 3> Ops5 = {C, A, B}; + + auto *Mul0 = SE.getMulExpr(Ops0); + auto *Mul1 = SE.getMulExpr(Ops1); + auto *Mul2 = SE.getMulExpr(Ops2); + auto *Mul3 = SE.getMulExpr(Ops3); + auto *Mul4 = SE.getMulExpr(Ops4); + auto *Mul5 = SE.getMulExpr(Ops5); + + EXPECT_EQ(Mul0, Mul1) << "Expected " << *Mul0 << " == " << *Mul1; + EXPECT_EQ(Mul1, Mul2) << "Expected " << *Mul1 << " == " << *Mul2; + EXPECT_EQ(Mul2, Mul3) << "Expected " << *Mul2 << " == " << *Mul3; + EXPECT_EQ(Mul3, Mul4) << "Expected " << *Mul3 << " == " << *Mul4; + EXPECT_EQ(Mul4, Mul5) << "Expected " << *Mul4 << " == " << *Mul5; + }; + + for (StringRef FuncName : {"f_2", "f_3", "f_4"}) + runWithFunctionAndSE(*M, FuncName, [&](Function &F, ScalarEvolution &SE) { + CheckCommutativeMulExprs(SE, SE.getSCEV(getInstructionByName(F, "x")), + SE.getSCEV(getInstructionByName(F, "y")), + SE.getSCEV(getInstructionByName(F, "z"))); + }); +} + +TEST_F(ScalarEvolutionsTest, SCEVCompareComplexity) { + 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, "bb1", F); + BranchInst::Create(LoopBB, EntryBB); + + auto *Ty = Type::getInt32Ty(Context); + SmallVector<Instruction*, 8> Muls(8), Acc(8), NextAcc(8); + + Acc[0] = PHINode::Create(Ty, 2, "", LoopBB); + Acc[1] = PHINode::Create(Ty, 2, "", LoopBB); + Acc[2] = PHINode::Create(Ty, 2, "", LoopBB); + Acc[3] = PHINode::Create(Ty, 2, "", LoopBB); + Acc[4] = PHINode::Create(Ty, 2, "", LoopBB); + Acc[5] = PHINode::Create(Ty, 2, "", LoopBB); + Acc[6] = PHINode::Create(Ty, 2, "", LoopBB); + Acc[7] = PHINode::Create(Ty, 2, "", LoopBB); + + for (int i = 0; i < 20; i++) { + Muls[0] = BinaryOperator::CreateMul(Acc[0], Acc[0], "", LoopBB); + NextAcc[0] = BinaryOperator::CreateAdd(Muls[0], Acc[4], "", LoopBB); + Muls[1] = BinaryOperator::CreateMul(Acc[1], Acc[1], "", LoopBB); + NextAcc[1] = BinaryOperator::CreateAdd(Muls[1], Acc[5], "", LoopBB); + Muls[2] = BinaryOperator::CreateMul(Acc[2], Acc[2], "", LoopBB); + NextAcc[2] = BinaryOperator::CreateAdd(Muls[2], Acc[6], "", LoopBB); + Muls[3] = BinaryOperator::CreateMul(Acc[3], Acc[3], "", LoopBB); + NextAcc[3] = BinaryOperator::CreateAdd(Muls[3], Acc[7], "", LoopBB); + + Muls[4] = BinaryOperator::CreateMul(Acc[4], Acc[4], "", LoopBB); + NextAcc[4] = BinaryOperator::CreateAdd(Muls[4], Acc[0], "", LoopBB); + Muls[5] = BinaryOperator::CreateMul(Acc[5], Acc[5], "", LoopBB); + NextAcc[5] = BinaryOperator::CreateAdd(Muls[5], Acc[1], "", LoopBB); + Muls[6] = BinaryOperator::CreateMul(Acc[6], Acc[6], "", LoopBB); + NextAcc[6] = BinaryOperator::CreateAdd(Muls[6], Acc[2], "", LoopBB); + Muls[7] = BinaryOperator::CreateMul(Acc[7], Acc[7], "", LoopBB); + NextAcc[7] = BinaryOperator::CreateAdd(Muls[7], Acc[3], "", LoopBB); + Acc = NextAcc; + } + + auto II = LoopBB->begin(); + for (int i = 0; i < 8; i++) { + PHINode *Phi = cast<PHINode>(&*II++); + Phi->addIncoming(Acc[i], LoopBB); + Phi->addIncoming(UndefValue::get(Ty), EntryBB); + } + + BasicBlock *ExitBB = BasicBlock::Create(Context, "bb2", F); + BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), + LoopBB); + + Acc[0] = BinaryOperator::CreateAdd(Acc[0], Acc[1], "", ExitBB); + Acc[1] = BinaryOperator::CreateAdd(Acc[2], Acc[3], "", ExitBB); + Acc[2] = BinaryOperator::CreateAdd(Acc[4], Acc[5], "", ExitBB); + Acc[3] = BinaryOperator::CreateAdd(Acc[6], Acc[7], "", ExitBB); + Acc[0] = BinaryOperator::CreateAdd(Acc[0], Acc[1], "", ExitBB); + Acc[1] = BinaryOperator::CreateAdd(Acc[2], Acc[3], "", ExitBB); + Acc[0] = BinaryOperator::CreateAdd(Acc[0], Acc[1], "", ExitBB); + + ReturnInst::Create(Context, nullptr, ExitBB); + + ScalarEvolution SE = buildSE(*F); + + EXPECT_NE(nullptr, SE.getSCEV(Acc[0])); +} + } // end anonymous namespace } // end namespace llvm diff --git a/unittests/Analysis/TBAATest.cpp b/unittests/Analysis/TBAATest.cpp new file mode 100644 index 000000000000..3a1d2f43563e --- /dev/null +++ b/unittests/Analysis/TBAATest.cpp @@ -0,0 +1,91 @@ +//===--- TBAATest.cpp - Mixed TBAA 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/AliasAnalysisEvaluator.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/CommandLine.h" +#include "gtest/gtest.h" + +namespace llvm { +namespace { + +class TBAATest : public testing::Test { +protected: + TBAATest() : M("TBAATest", C), MD(C) {} + + LLVMContext C; + Module M; + MDBuilder MD; +}; + +static StoreInst *getFunctionWithSingleStore(Module *M, StringRef Name) { + auto &C = M->getContext(); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), {}); + auto *F = cast<Function>(M->getOrInsertFunction(Name, FTy)); + auto *BB = BasicBlock::Create(C, "entry", F); + auto *IntType = Type::getInt32Ty(C); + auto *PtrType = Type::getInt32PtrTy(C); + auto *SI = new StoreInst(ConstantInt::get(IntType, 42), + ConstantPointerNull::get(PtrType), BB); + ReturnInst::Create(C, nullptr, BB); + + return SI; +} + +TEST_F(TBAATest, checkVerifierBehaviorForOldTBAA) { + auto *SI = getFunctionWithSingleStore(&M, "f1"); + auto *F = SI->getFunction(); + + // C++ unit test case to avoid going through the auto upgrade logic. + auto *RootMD = MD.createTBAARoot("Simple C/C++ TBAA"); + auto *MD1 = MD.createTBAANode("omnipotent char", RootMD); + auto *MD2 = MD.createTBAANode("int", MD1); + SI->setMetadata(LLVMContext::MD_tbaa, MD2); + + SmallVector<char, 0> ErrorMsg; + raw_svector_ostream Outs(ErrorMsg); + + StringRef ExpectedFailureMsg( + "Old-style TBAA is no longer allowed, use struct-path TBAA instead"); + + EXPECT_TRUE(verifyFunction(*F, &Outs)); + EXPECT_TRUE(StringRef(ErrorMsg.begin(), ErrorMsg.size()) + .startswith(ExpectedFailureMsg)); +} + +TEST_F(TBAATest, checkTBAAMerging) { + auto *SI = getFunctionWithSingleStore(&M, "f2"); + auto *F = SI->getFunction(); + + auto *RootMD = MD.createTBAARoot("tbaa-root"); + auto *MD1 = MD.createTBAANode("scalar-a", RootMD); + auto *StructTag1 = MD.createTBAAStructTagNode(MD1, MD1, 0); + auto *MD2 = MD.createTBAANode("scalar-b", RootMD); + auto *StructTag2 = MD.createTBAAStructTagNode(MD2, MD2, 0); + + auto *GenericMD = MDNode::getMostGenericTBAA(StructTag1, StructTag2); + + EXPECT_EQ(GenericMD, nullptr); + + // Despite GenericMD being nullptr, we expect the setMetadata call to be well + // defined and produce a well-formed function. + SI->setMetadata(LLVMContext::MD_tbaa, GenericMD); + + EXPECT_TRUE(!verifyFunction(*F)); +} + +} // end anonymous namspace +} // end llvm namespace diff --git a/unittests/Analysis/ValueTrackingTest.cpp b/unittests/Analysis/ValueTrackingTest.cpp index f429c3b99149..ba0d30d59b66 100644 --- a/unittests/Analysis/ValueTrackingTest.cpp +++ b/unittests/Analysis/ValueTrackingTest.cpp @@ -188,3 +188,54 @@ TEST_F(MatchSelectPatternTest, DoubleCastBad) { // The cast types here aren't the same, so we cannot match an UMIN. expectPattern({SPF_UNKNOWN, SPNB_NA, false}); } + +TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) { + StringRef Assembly = + "declare void @nounwind_readonly(i32*) nounwind readonly " + "declare void @nounwind_argmemonly(i32*) nounwind argmemonly " + "declare void @throws_but_readonly(i32*) readonly " + "declare void @throws_but_argmemonly(i32*) argmemonly " + " " + "declare void @unknown(i32*) " + " " + "define void @f(i32* %p) { " + " call void @nounwind_readonly(i32* %p) " + " call void @nounwind_argmemonly(i32* %p) " + " call void @throws_but_readonly(i32* %p) " + " call void @throws_but_argmemonly(i32* %p) " + " call void @unknown(i32* %p) nounwind readonly " + " call void @unknown(i32* %p) nounwind argmemonly " + " call void @unknown(i32* %p) readonly " + " call void @unknown(i32* %p) argmemonly " + " ret void " + "} "; + + LLVMContext Context; + SMDiagnostic Error; + auto M = parseAssemblyString(Assembly, Error, Context); + assert(M && "Bad assembly?"); + + auto *F = M->getFunction("f"); + assert(F && "Bad assembly?"); + + auto &BB = F->getEntryBlock(); + ArrayRef<bool> ExpectedAnswers = { + true, // call void @nounwind_readonly(i32* %p) + true, // call void @nounwind_argmemonly(i32* %p) + false, // call void @throws_but_readonly(i32* %p) + false, // call void @throws_but_argmemonly(i32* %p) + true, // call void @unknown(i32* %p) nounwind readonly + true, // call void @unknown(i32* %p) nounwind argmemonly + false, // call void @unknown(i32* %p) readonly + false, // call void @unknown(i32* %p) argmemonly + false, // ret void + }; + + int Index = 0; + for (auto &I : BB) { + EXPECT_EQ(isGuaranteedToTransferExecutionToSuccessor(&I), + ExpectedAnswers[Index]) + << "Incorrect answer at instruction " << Index << " = " << I; + Index++; + } +} diff --git a/unittests/Bitcode/BitReaderTest.cpp b/unittests/Bitcode/BitReaderTest.cpp index d0f33d12d5bc..0003a2b6fb8f 100644 --- a/unittests/Bitcode/BitReaderTest.cpp +++ b/unittests/Bitcode/BitReaderTest.cpp @@ -7,22 +7,22 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/AsmParser/Parser.h" +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/Bitcode/BitstreamReader.h" #include "llvm/Bitcode/BitstreamWriter.h" -#include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" -#include "llvm/Support/DataStream.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" -#include "llvm/Support/StreamingMemoryObject.h" #include "gtest/gtest.h" using namespace llvm; @@ -55,91 +55,13 @@ static std::unique_ptr<Module> getLazyModuleFromAssembly(LLVMContext &Context, SmallString<1024> &Mem, const char *Assembly) { writeModuleToBuffer(parseAssembly(Context, Assembly), Mem); - std::unique_ptr<MemoryBuffer> Buffer = - MemoryBuffer::getMemBuffer(Mem.str(), "test", false); - ErrorOr<std::unique_ptr<Module>> ModuleOrErr = - getLazyBitcodeModule(std::move(Buffer), Context); + Expected<std::unique_ptr<Module>> ModuleOrErr = + getLazyBitcodeModule(MemoryBufferRef(Mem.str(), "test"), Context); + if (!ModuleOrErr) + report_fatal_error("Could not parse bitcode module"); return std::move(ModuleOrErr.get()); } -class BufferDataStreamer : public DataStreamer { - std::unique_ptr<MemoryBuffer> Buffer; - unsigned Pos = 0; - size_t GetBytes(unsigned char *Out, size_t Len) override { - StringRef Buf = Buffer->getBuffer(); - size_t Left = Buf.size() - Pos; - Len = std::min(Left, Len); - memcpy(Out, Buffer->getBuffer().substr(Pos).data(), Len); - Pos += Len; - return Len; - } - -public: - BufferDataStreamer(std::unique_ptr<MemoryBuffer> Buffer) - : Buffer(std::move(Buffer)) {} -}; - -static std::unique_ptr<Module> -getStreamedModuleFromAssembly(LLVMContext &Context, SmallString<1024> &Mem, - const char *Assembly) { - 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)); - ErrorOr<std::unique_ptr<Module>> ModuleOrErr = - getStreamedBitcodeModule("test", std::move(Streamer), Context); - return std::move(ModuleOrErr.get()); -} - -// Checks if we correctly detect eof if we try to read N bits when there are not -// enough bits left on the input stream to read N bits, and we are using a data -// streamer. In particular, it checks if we properly set the object size when -// the eof is reached under such conditions. -TEST(BitReaderTest, TestForEofAfterReadFailureOnDataStreamer) { - // Note: Because StreamingMemoryObject does a call to method GetBytes in it's - // constructor, using internal constant kChunkSize, we must fill the input - // with more characters than that amount. - static size_t InputSize = StreamingMemoryObject::kChunkSize + 5; - char *Text = new char[InputSize]; - std::memset(Text, 'a', InputSize); - Text[InputSize - 1] = '\0'; - StringRef Input(Text); - - // Build bitsteam reader using data streamer. - auto MemoryBuf = MemoryBuffer::getMemBuffer(Input); - std::unique_ptr<DataStreamer> Streamer( - new BufferDataStreamer(std::move(MemoryBuf))); - auto OwnedBytes = - llvm::make_unique<StreamingMemoryObject>(std::move(Streamer)); - auto Reader = llvm::make_unique<BitstreamReader>(std::move(OwnedBytes)); - BitstreamCursor Cursor; - Cursor.init(Reader.get()); - - // Jump to two bytes before end of stream. - Cursor.JumpToBit((InputSize - 4) * CHAR_BIT); - // Try to read 4 bytes when only 2 are present, resulting in error value 0. - const size_t ReadErrorValue = 0; - EXPECT_EQ(ReadErrorValue, Cursor.Read(32)); - // Should be at eof now. - EXPECT_TRUE(Cursor.AtEndOfStream()); - - delete[] Text; -} - -TEST(BitReaderTest, MateralizeForwardRefWithStream) { - SmallString<1024> Mem; - - LLVMContext Context; - std::unique_ptr<Module> M = getStreamedModuleFromAssembly( - Context, Mem, "@table = constant i8* blockaddress(@func, %bb)\n" - "define void @func() {\n" - " unreachable\n" - "bb:\n" - " unreachable\n" - "}\n"); - EXPECT_FALSE(M->getFunction("func")->empty()); -} - // Tests that lazy evaluation can parse functions out of order. TEST(BitReaderTest, MaterializeFunctionsOutOfOrder) { SmallString<1024> Mem; @@ -172,7 +94,7 @@ TEST(BitReaderTest, MaterializeFunctionsOutOfOrder) { EXPECT_FALSE(verifyModule(*M, &dbgs())); // Materialize h. - H->materialize(); + ASSERT_FALSE(H->materialize()); EXPECT_TRUE(F->empty()); EXPECT_TRUE(G->empty()); EXPECT_FALSE(H->empty()); @@ -180,7 +102,7 @@ TEST(BitReaderTest, MaterializeFunctionsOutOfOrder) { EXPECT_FALSE(verifyModule(*M, &dbgs())); // Materialize g. - G->materialize(); + ASSERT_FALSE(G->materialize()); EXPECT_TRUE(F->empty()); EXPECT_FALSE(G->empty()); EXPECT_FALSE(H->empty()); @@ -188,7 +110,7 @@ TEST(BitReaderTest, MaterializeFunctionsOutOfOrder) { EXPECT_FALSE(verifyModule(*M, &dbgs())); // Materialize j. - J->materialize(); + ASSERT_FALSE(J->materialize()); EXPECT_TRUE(F->empty()); EXPECT_FALSE(G->empty()); EXPECT_FALSE(H->empty()); @@ -196,7 +118,7 @@ TEST(BitReaderTest, MaterializeFunctionsOutOfOrder) { EXPECT_FALSE(verifyModule(*M, &dbgs())); // Materialize f. - F->materialize(); + ASSERT_FALSE(F->materialize()); EXPECT_FALSE(F->empty()); EXPECT_FALSE(G->empty()); EXPECT_FALSE(H->empty()); @@ -216,6 +138,7 @@ TEST(BitReaderTest, MaterializeFunctionsForBlockAddr) { // PR11677 " unreachable\n" "}\n"); EXPECT_FALSE(verifyModule(*M, &dbgs())); + EXPECT_FALSE(M->getFunction("func")->empty()); } TEST(BitReaderTest, MaterializeFunctionsForBlockAddrInFunctionBefore) { diff --git a/unittests/Bitcode/BitstreamReaderTest.cpp b/unittests/Bitcode/BitstreamReaderTest.cpp index 2be774cc5394..704eb803f9c7 100644 --- a/unittests/Bitcode/BitstreamReaderTest.cpp +++ b/unittests/Bitcode/BitstreamReaderTest.cpp @@ -10,34 +10,17 @@ #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 }; - BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); - BitstreamCursor Cursor(Reader); + BitstreamCursor Cursor(Bytes); EXPECT_FALSE(Cursor.AtEndOfStream()); (void)Cursor.Read(8); @@ -56,27 +39,23 @@ TEST(BitstreamReaderTest, AtEndOfStreamJump) { uint8_t Bytes[4] = { 0x00, 0x01, 0x02, 0x03 }; - BitstreamReader Reader(std::begin(Bytes), std::end(Bytes)); - BitstreamCursor Cursor(Reader); + BitstreamCursor Cursor(Bytes); Cursor.JumpToBit(32); EXPECT_TRUE(Cursor.AtEndOfStream()); } TEST(BitstreamReaderTest, AtEndOfStreamEmpty) { - uint8_t Dummy = 0xFF; - BitstreamReader Reader(&Dummy, &Dummy); - BitstreamCursor Cursor(Reader); + BitstreamCursor Cursor(ArrayRef<uint8_t>{}); 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); + SimpleBitstreamCursor Cursor(Bytes); - for (unsigned I = 0, E = 33; I != E; ++I) { + for (unsigned I = 0, E = 32; I != E; ++I) { EXPECT_EQ(I / 8, Cursor.getCurrentByteNo()); (void)Cursor.Read(1); } @@ -85,8 +64,7 @@ TEST(BitstreamReaderTest, 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); + SimpleBitstreamCursor Cursor(Bytes); for (unsigned I = 0, E = 8; I != E; ++I) { EXPECT_EQ(Bytes + I, Cursor.getPointerToByte(I, 1)); @@ -95,87 +73,13 @@ TEST(BitstreamReaderTest, getPointerToByte) { 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); + SimpleBitstreamCursor Cursor(Bytes); 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) @@ -208,10 +112,8 @@ TEST(BitstreamReaderTest, readRecordWithBlobWhileStreaming) { } // Stream the buffer into the reader. - BitstreamReader R(llvm::make_unique<StreamingMemoryObject>( - llvm::make_unique<BufferStreamer>( - StringRef(Buffer.begin(), Buffer.size())))); - BitstreamCursor Stream(R); + BitstreamCursor Stream( + ArrayRef<uint8_t>((const uint8_t *)Buffer.begin(), Buffer.size())); // Header. Included in test so that we can run llvm-bcanalyzer to debug // when there are problems. @@ -238,4 +140,12 @@ TEST(BitstreamReaderTest, readRecordWithBlobWhileStreaming) { } } +TEST(BitstreamReaderTest, shortRead) { + uint8_t Bytes[] = {8, 7, 6, 5, 4, 3, 2, 1}; + for (unsigned I = 1; I != 8; ++I) { + SimpleBitstreamCursor Cursor(ArrayRef<uint8_t>(Bytes, I)); + EXPECT_EQ(8ull, Cursor.Read(8)); + } +} + } // end anonymous namespace diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 49a9b31b60d9..8dbca211d026 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -1,12 +1,6 @@ add_custom_target(UnitTests) set_target_properties(UnitTests PROPERTIES FOLDER "Tests") -if (APPLE) - set(CMAKE_INSTALL_RPATH "@executable_path/../../lib") -else(UNIX) - set(CMAKE_INSTALL_RPATH "\$ORIGIN/../../lib${LLVM_LIBDIR_SUFFIX}") -endif() - function(add_llvm_unittest test_dirname) add_unittest(UnitTests ${test_dirname} ${ARGN}) endfunction() @@ -23,8 +17,10 @@ add_subdirectory(LineEditor) add_subdirectory(Linker) add_subdirectory(MC) add_subdirectory(MI) +add_subdirectory(Object) add_subdirectory(ObjectYAML) add_subdirectory(Option) add_subdirectory(ProfileData) add_subdirectory(Support) +add_subdirectory(Target) add_subdirectory(Transforms) diff --git a/unittests/CodeGen/CMakeLists.txt b/unittests/CodeGen/CMakeLists.txt index 65c0ac3f20e4..240734dc6b18 100644 --- a/unittests/CodeGen/CMakeLists.txt +++ b/unittests/CodeGen/CMakeLists.txt @@ -1,12 +1,18 @@ set(LLVM_LINK_COMPONENTS AsmPrinter + CodeGen + Core Support ) set(CodeGenSources DIEHashTest.cpp + LowLevelTypeTest.cpp + MachineInstrBundleIteratorTest.cpp ) add_llvm_unittest(CodeGenTests ${CodeGenSources} ) + +add_subdirectory(GlobalISel) diff --git a/unittests/CodeGen/GlobalISel/CMakeLists.txt b/unittests/CodeGen/GlobalISel/CMakeLists.txt new file mode 100644 index 000000000000..94e31159c6bb --- /dev/null +++ b/unittests/CodeGen/GlobalISel/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + GlobalISel + CodeGen + ) + +if(LLVM_BUILD_GLOBAL_ISEL) + add_llvm_unittest(GlobalISelTests + LegalizerInfoTest.cpp + ) +endif() diff --git a/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp b/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp new file mode 100644 index 000000000000..882df5f25216 --- /dev/null +++ b/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp @@ -0,0 +1,120 @@ +//===- llvm/unittest/CodeGen/GlobalISel/LegalizerInfoTest.cpp -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/Target/TargetOpcodes.h" +#include "gtest/gtest.h" + +using namespace llvm; + +// Define a couple of pretty printers to help debugging when things go wrong. +namespace llvm { +std::ostream & +operator<<(std::ostream &OS, const llvm::LegalizerInfo::LegalizeAction Act) { + switch (Act) { + case LegalizerInfo::Lower: OS << "Lower"; break; + case LegalizerInfo::Legal: OS << "Legal"; break; + case LegalizerInfo::NarrowScalar: OS << "NarrowScalar"; break; + case LegalizerInfo::WidenScalar: OS << "WidenScalar"; break; + case LegalizerInfo::FewerElements: OS << "FewerElements"; break; + case LegalizerInfo::MoreElements: OS << "MoreElements"; break; + case LegalizerInfo::Libcall: OS << "Libcall"; break; + case LegalizerInfo::Custom: OS << "Custom"; break; + case LegalizerInfo::Unsupported: OS << "Unsupported"; break; + case LegalizerInfo::NotFound: OS << "NotFound"; + } + return OS; +} + +std::ostream & +operator<<(std::ostream &OS, const llvm::LLT Ty) { + std::string Repr; + raw_string_ostream SS{Repr}; + Ty.print(SS); + OS << SS.str(); + return OS; +} +} + +namespace { + + +TEST(LegalizerInfoTest, ScalarRISC) { + using namespace TargetOpcode; + LegalizerInfo L; + // Typical RISCy set of operations based on AArch64. + L.setAction({G_ADD, LLT::scalar(8)}, LegalizerInfo::WidenScalar); + L.setAction({G_ADD, LLT::scalar(16)}, LegalizerInfo::WidenScalar); + L.setAction({G_ADD, LLT::scalar(32)}, LegalizerInfo::Legal); + L.setAction({G_ADD, LLT::scalar(64)}, LegalizerInfo::Legal); + L.computeTables(); + + // Check we infer the correct types and actually do what we're told. + ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(8)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(16)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(32)}), + std::make_pair(LegalizerInfo::Legal, LLT::scalar(32))); + ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(64)}), + std::make_pair(LegalizerInfo::Legal, LLT::scalar(64))); + + // Make sure the default for over-sized types applies. + ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(128)}), + std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64))); +} + +TEST(LegalizerInfoTest, VectorRISC) { + using namespace TargetOpcode; + LegalizerInfo L; + // Typical RISCy set of operations based on ARM. + L.setScalarInVectorAction(G_ADD, LLT::scalar(8), LegalizerInfo::Legal); + L.setScalarInVectorAction(G_ADD, LLT::scalar(16), LegalizerInfo::Legal); + L.setScalarInVectorAction(G_ADD, LLT::scalar(32), LegalizerInfo::Legal); + + L.setAction({G_ADD, LLT::vector(8, 8)}, LegalizerInfo::Legal); + L.setAction({G_ADD, LLT::vector(16, 8)}, LegalizerInfo::Legal); + L.setAction({G_ADD, LLT::vector(4, 16)}, LegalizerInfo::Legal); + L.setAction({G_ADD, LLT::vector(8, 16)}, LegalizerInfo::Legal); + L.setAction({G_ADD, LLT::vector(2, 32)}, LegalizerInfo::Legal); + L.setAction({G_ADD, LLT::vector(4, 32)}, LegalizerInfo::Legal); + L.computeTables(); + + // Check we infer the correct types and actually do what we're told for some + // simple cases. + ASSERT_EQ(L.getAction({G_ADD, LLT::vector(2, 8)}), + std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8))); + ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 8)}), + std::make_pair(LegalizerInfo::Legal, LLT::vector(8, 8))); + ASSERT_EQ( + L.getAction({G_ADD, LLT::vector(8, 32)}), + std::make_pair(LegalizerInfo::FewerElements, LLT::vector(4, 32))); +} + +TEST(LegalizerInfoTest, MultipleTypes) { + using namespace TargetOpcode; + LegalizerInfo L; + LLT p0 = LLT::pointer(0, 64); + LLT s32 = LLT::scalar(32); + LLT s64 = LLT::scalar(64); + + // Typical RISCy set of operations based on AArch64. + L.setAction({G_PTRTOINT, 0, s64}, LegalizerInfo::Legal); + L.setAction({G_PTRTOINT, 1, p0}, LegalizerInfo::Legal); + + L.setAction({G_PTRTOINT, 0, s32}, LegalizerInfo::WidenScalar); + L.computeTables(); + + // Check we infer the correct types and actually do what we're told. + ASSERT_EQ(L.getAction({G_PTRTOINT, 0, s64}), + std::make_pair(LegalizerInfo::Legal, s64)); + ASSERT_EQ(L.getAction({G_PTRTOINT, 1, p0}), + std::make_pair(LegalizerInfo::Legal, p0)); +} +} diff --git a/unittests/CodeGen/LowLevelTypeTest.cpp b/unittests/CodeGen/LowLevelTypeTest.cpp new file mode 100644 index 000000000000..4ea181c1c9d3 --- /dev/null +++ b/unittests/CodeGen/LowLevelTypeTest.cpp @@ -0,0 +1,204 @@ +//===- llvm/unittest/CodeGen/GlobalISel/LowLevelTypeTest.cpp --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/LowLevelType.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Type.h" +#include "gtest/gtest.h" + +using namespace llvm; + +// Define a pretty printer to help debugging when things go wrong. +namespace llvm { +std::ostream & +operator<<(std::ostream &OS, const llvm::LLT Ty) { + std::string Repr; + raw_string_ostream SS{Repr}; + Ty.print(SS); + OS << SS.str(); + return OS; +} +} + +namespace { + +TEST(LowLevelTypeTest, Scalar) { + LLVMContext C; + DataLayout DL(""); + + for (unsigned S : {1U, 17U, 32U, 64U, 0xfffffU}) { + const LLT Ty = LLT::scalar(S); + const LLT HalfTy = (S % 2) == 0 ? Ty.halfScalarSize() : Ty; + const LLT DoubleTy = Ty.doubleScalarSize(); + + // Test kind. + for (const LLT TestTy : {Ty, HalfTy, DoubleTy}) { + ASSERT_TRUE(TestTy.isValid()); + ASSERT_TRUE(TestTy.isScalar()); + + ASSERT_FALSE(TestTy.isPointer()); + ASSERT_FALSE(TestTy.isVector()); + } + + // Test sizes. + EXPECT_EQ(S, Ty.getSizeInBits()); + EXPECT_EQ(S, Ty.getScalarSizeInBits()); + + EXPECT_EQ(S*2, DoubleTy.getSizeInBits()); + EXPECT_EQ(S*2, DoubleTy.getScalarSizeInBits()); + + if ((S % 2) == 0) { + EXPECT_EQ(S/2, HalfTy.getSizeInBits()); + EXPECT_EQ(S/2, HalfTy.getScalarSizeInBits()); + } + + // Test equality operators. + EXPECT_TRUE(Ty == Ty); + EXPECT_FALSE(Ty != Ty); + + EXPECT_NE(Ty, DoubleTy); + + // Test Type->LLT conversion. + Type *IRTy = IntegerType::get(C, S); + EXPECT_EQ(Ty, LLT(*IRTy, DL)); + } +} + +TEST(LowLevelTypeTest, Vector) { + LLVMContext C; + DataLayout DL(""); + + for (unsigned S : {1U, 17U, 32U, 64U, 0xfffU}) { + for (uint16_t Elts : {2U, 3U, 4U, 32U, 0xffU}) { + const LLT STy = LLT::scalar(S); + const LLT VTy = LLT::vector(Elts, S); + + // Test the alternative vector(). + { + const LLT VSTy = LLT::vector(Elts, STy); + EXPECT_EQ(VTy, VSTy); + } + + // Test getElementType(). + EXPECT_EQ(STy, VTy.getElementType()); + + const LLT HalfSzTy = ((S % 2) == 0) ? VTy.halfScalarSize() : VTy; + const LLT DoubleSzTy = VTy.doubleScalarSize(); + + // halfElements requires an even number of elements. + const LLT HalfEltIfEvenTy = ((Elts % 2) == 0) ? VTy.halfElements() : VTy; + const LLT DoubleEltTy = VTy.doubleElements(); + + // Test kind. + for (const LLT TestTy : {VTy, HalfSzTy, DoubleSzTy, DoubleEltTy}) { + ASSERT_TRUE(TestTy.isValid()); + ASSERT_TRUE(TestTy.isVector()); + + ASSERT_FALSE(TestTy.isScalar()); + ASSERT_FALSE(TestTy.isPointer()); + } + + // Test halving elements to a scalar. + { + ASSERT_TRUE(HalfEltIfEvenTy.isValid()); + ASSERT_FALSE(HalfEltIfEvenTy.isPointer()); + if (Elts > 2) { + ASSERT_TRUE(HalfEltIfEvenTy.isVector()); + } else { + ASSERT_FALSE(HalfEltIfEvenTy.isVector()); + EXPECT_EQ(STy, HalfEltIfEvenTy); + } + } + + + // Test sizes. + EXPECT_EQ(S * Elts, VTy.getSizeInBits()); + EXPECT_EQ(S, VTy.getScalarSizeInBits()); + EXPECT_EQ(Elts, VTy.getNumElements()); + + if ((S % 2) == 0) { + EXPECT_EQ((S / 2) * Elts, HalfSzTy.getSizeInBits()); + EXPECT_EQ(S / 2, HalfSzTy.getScalarSizeInBits()); + EXPECT_EQ(Elts, HalfSzTy.getNumElements()); + } + + EXPECT_EQ((S * 2) * Elts, DoubleSzTy.getSizeInBits()); + EXPECT_EQ(S * 2, DoubleSzTy.getScalarSizeInBits()); + EXPECT_EQ(Elts, DoubleSzTy.getNumElements()); + + if ((Elts % 2) == 0) { + EXPECT_EQ(S * (Elts / 2), HalfEltIfEvenTy.getSizeInBits()); + EXPECT_EQ(S, HalfEltIfEvenTy.getScalarSizeInBits()); + if (Elts > 2) + EXPECT_EQ(Elts / 2, HalfEltIfEvenTy.getNumElements()); + } + + EXPECT_EQ(S * (Elts * 2), DoubleEltTy.getSizeInBits()); + EXPECT_EQ(S, DoubleEltTy.getScalarSizeInBits()); + EXPECT_EQ(Elts * 2, DoubleEltTy.getNumElements()); + + // Test equality operators. + EXPECT_TRUE(VTy == VTy); + EXPECT_FALSE(VTy != VTy); + + // Test inequality operators on.. + // ..different kind. + EXPECT_NE(VTy, STy); + // ..different #elts. + EXPECT_NE(VTy, DoubleEltTy); + // ..different scalar size. + EXPECT_NE(VTy, DoubleSzTy); + + // Test Type->LLT conversion. + Type *IRSTy = IntegerType::get(C, S); + Type *IRTy = VectorType::get(IRSTy, Elts); + EXPECT_EQ(VTy, LLT(*IRTy, DL)); + } + } +} + +TEST(LowLevelTypeTest, Pointer) { + LLVMContext C; + DataLayout DL(""); + + for (unsigned AS : {0U, 1U, 127U, 0xffffU}) { + const LLT Ty = LLT::pointer(AS, DL.getPointerSizeInBits(AS)); + + // Test kind. + ASSERT_TRUE(Ty.isValid()); + ASSERT_TRUE(Ty.isPointer()); + + ASSERT_FALSE(Ty.isScalar()); + ASSERT_FALSE(Ty.isVector()); + + // Test addressspace. + EXPECT_EQ(AS, Ty.getAddressSpace()); + + // Test equality operators. + EXPECT_TRUE(Ty == Ty); + EXPECT_FALSE(Ty != Ty); + + // Test Type->LLT conversion. + Type *IRTy = PointerType::get(IntegerType::get(C, 8), AS); + EXPECT_EQ(Ty, LLT(*IRTy, DL)); + } +} + +TEST(LowLevelTypeTest, Invalid) { + const LLT Ty; + + ASSERT_FALSE(Ty.isValid()); + ASSERT_FALSE(Ty.isScalar()); + ASSERT_FALSE(Ty.isPointer()); + ASSERT_FALSE(Ty.isVector()); +} + +} diff --git a/unittests/CodeGen/MachineInstrBundleIteratorTest.cpp b/unittests/CodeGen/MachineInstrBundleIteratorTest.cpp new file mode 100644 index 000000000000..416f5774f4c6 --- /dev/null +++ b/unittests/CodeGen/MachineInstrBundleIteratorTest.cpp @@ -0,0 +1,133 @@ +//===- MachineInstrBundleIteratorTest.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/ilist_node.h" +#include "llvm/CodeGen/MachineInstrBundleIterator.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct MyBundledInstr + : public ilist_node<MyBundledInstr, ilist_sentinel_tracking<true>> { + bool isBundledWithPred() const { return true; } + bool isBundledWithSucc() const { return true; } +}; +typedef MachineInstrBundleIterator<MyBundledInstr> bundled_iterator; +typedef MachineInstrBundleIterator<const MyBundledInstr> const_bundled_iterator; +typedef MachineInstrBundleIterator<MyBundledInstr, true> + reverse_bundled_iterator; +typedef MachineInstrBundleIterator<const MyBundledInstr, true> + const_reverse_bundled_iterator; + +#ifdef GTEST_HAS_DEATH_TEST +#ifndef NDEBUG +TEST(MachineInstrBundleIteratorTest, CheckForBundles) { + MyBundledInstr MBI; + auto I = MBI.getIterator(); + auto RI = I.getReverse(); + + // Confirm that MBI is always considered bundled. + EXPECT_TRUE(MBI.isBundledWithPred()); + EXPECT_TRUE(MBI.isBundledWithSucc()); + + // Confirm that iterators check in their constructor for bundled iterators. + EXPECT_DEATH((void)static_cast<bundled_iterator>(I), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<bundled_iterator>(MBI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<bundled_iterator>(&MBI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<const_bundled_iterator>(I), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<const_bundled_iterator>(MBI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<const_bundled_iterator>(&MBI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<reverse_bundled_iterator>(RI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<reverse_bundled_iterator>(MBI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<reverse_bundled_iterator>(&MBI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<const_reverse_bundled_iterator>(RI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<const_reverse_bundled_iterator>(MBI), + "not legal to initialize"); + EXPECT_DEATH((void)static_cast<const_reverse_bundled_iterator>(&MBI), + "not legal to initialize"); +} +#endif +#endif + +TEST(MachineInstrBundleIteratorTest, CompareToBundledMI) { + MyBundledInstr MBI; + const MyBundledInstr &CMBI = MBI; + bundled_iterator I; + const_bundled_iterator CI; + + // Confirm that MBI is always considered bundled. + EXPECT_TRUE(MBI.isBundledWithPred()); + EXPECT_TRUE(MBI.isBundledWithSucc()); + + // These invocations will crash when !NDEBUG if a conversion is taking place. + // These checks confirm that comparison operators don't use any conversion + // operators. + ASSERT_FALSE(MBI == I); + ASSERT_FALSE(&MBI == I); + ASSERT_FALSE(CMBI == I); + ASSERT_FALSE(&CMBI == I); + ASSERT_FALSE(I == MBI); + ASSERT_FALSE(I == &MBI); + ASSERT_FALSE(I == CMBI); + ASSERT_FALSE(I == &CMBI); + ASSERT_FALSE(MBI == CI); + ASSERT_FALSE(&MBI == CI); + ASSERT_FALSE(CMBI == CI); + ASSERT_FALSE(&CMBI == CI); + ASSERT_FALSE(CI == MBI); + ASSERT_FALSE(CI == &MBI); + ASSERT_FALSE(CI == CMBI); + ASSERT_FALSE(CI == &CMBI); + ASSERT_FALSE(MBI.getIterator() == I); + ASSERT_FALSE(CMBI.getIterator() == I); + ASSERT_FALSE(I == MBI.getIterator()); + ASSERT_FALSE(I == CMBI.getIterator()); + ASSERT_FALSE(MBI.getIterator() == CI); + ASSERT_FALSE(CMBI.getIterator() == CI); + ASSERT_FALSE(CI == MBI.getIterator()); + ASSERT_FALSE(CI == CMBI.getIterator()); + ASSERT_TRUE(MBI != I); + ASSERT_TRUE(&MBI != I); + ASSERT_TRUE(CMBI != I); + ASSERT_TRUE(&CMBI != I); + ASSERT_TRUE(I != MBI); + ASSERT_TRUE(I != &MBI); + ASSERT_TRUE(I != CMBI); + ASSERT_TRUE(I != &CMBI); + ASSERT_TRUE(MBI != CI); + ASSERT_TRUE(&MBI != CI); + ASSERT_TRUE(CMBI != CI); + ASSERT_TRUE(&CMBI != CI); + ASSERT_TRUE(CI != MBI); + ASSERT_TRUE(CI != &MBI); + ASSERT_TRUE(CI != CMBI); + ASSERT_TRUE(CI != &CMBI); + ASSERT_TRUE(MBI.getIterator() != I); + ASSERT_TRUE(CMBI.getIterator() != I); + ASSERT_TRUE(I != MBI.getIterator()); + ASSERT_TRUE(I != CMBI.getIterator()); + ASSERT_TRUE(MBI.getIterator() != CI); + ASSERT_TRUE(CMBI.getIterator() != CI); + ASSERT_TRUE(CI != MBI.getIterator()); + ASSERT_TRUE(CI != CMBI.getIterator()); +} + +} // end namespace diff --git a/unittests/DebugInfo/DWARF/CMakeLists.txt b/unittests/DebugInfo/DWARF/CMakeLists.txt index 4bec17cbb524..eafca4a2fc06 100644 --- a/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -1,8 +1,15 @@ set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + AsmPrinter DebugInfoDWARF + MC + Object + Support ) set(DebugInfoSources + DwarfGenerator.cpp + DWARFDebugInfoTest.cpp DWARFFormValueTest.cpp ) diff --git a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp new file mode 100644 index 000000000000..e2f4bb788dd0 --- /dev/null +++ b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -0,0 +1,1087 @@ +//===- llvm/unittest/DebugInfo/DWARFFormValueTest.cpp ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DwarfGenerator.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" +#include <climits> + +using namespace llvm; +using namespace dwarf; + +namespace { + +void initLLVMIfNeeded() { + static bool gInitialized = false; + if (!gInitialized) { + gInitialized = true; + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + } +} + +Triple getHostTripleForAddrSize(uint8_t AddrSize) { + Triple PT(Triple::normalize(LLVM_HOST_TRIPLE)); + + if (AddrSize == 8 && PT.isArch32Bit()) + return PT.get64BitArchVariant(); + if (AddrSize == 4 && PT.isArch64Bit()) + return PT.get32BitArchVariant(); + return PT; +} + +/// Take any llvm::Expected and check and handle any errors. +/// +/// \param Expected a llvm::Excepted instance to check. +/// \returns true if there were errors, false otherwise. +template <typename T> +static bool HandleExpectedError(T &Expected) { + std::string ErrorMsg; + handleAllErrors(Expected.takeError(), [&](const llvm::ErrorInfoBase &EI) { + ErrorMsg = EI.message(); + }); + if (!ErrorMsg.empty()) { + ::testing::AssertionFailure() << "error: " << ErrorMsg; + return true; + } + return false; +} + +template <uint16_t Version, class AddrType, class RefAddrType> +void TestAllForms() { + // Test that we can decode all DW_FORM values correctly. + + const uint8_t AddrSize = sizeof(AddrType); + const AddrType AddrValue = (AddrType)0x0123456789abcdefULL; + const uint8_t BlockData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; + const uint32_t BlockSize = sizeof(BlockData); + const RefAddrType RefAddr = 0x12345678; + const uint8_t Data1 = 0x01U; + const uint16_t Data2 = 0x2345U; + const uint32_t Data4 = 0x6789abcdU; + const uint64_t Data8 = 0x0011223344556677ULL; + const uint64_t Data8_2 = 0xAABBCCDDEEFF0011ULL; + const int64_t SData = INT64_MIN; + const uint64_t UData[] = {UINT64_MAX - 1, UINT64_MAX - 2, UINT64_MAX - 3, + UINT64_MAX - 4, UINT64_MAX - 5, UINT64_MAX - 6, + UINT64_MAX - 7, UINT64_MAX - 8, UINT64_MAX - 9}; +#define UDATA_1 18446744073709551614ULL + const uint32_t Dwarf32Values[] = {1, 2, 3, 4, 5, 6, 7, 8}; + const char *StringValue = "Hello"; + const char *StrpValue = "World"; + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + dwarfgen::DIE CUDie = CU.getUnitDIE(); + uint16_t Attr = DW_AT_lo_user; + + //---------------------------------------------------------------------- + // Test address forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_addr = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_addr, DW_FORM_addr, AddrValue); + + //---------------------------------------------------------------------- + // Test block forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_block = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_block, DW_FORM_block, BlockData, BlockSize); + + const auto Attr_DW_FORM_block1 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_block1, DW_FORM_block1, BlockData, BlockSize); + + const auto Attr_DW_FORM_block2 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_block2, DW_FORM_block2, BlockData, BlockSize); + + const auto Attr_DW_FORM_block4 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_block4, DW_FORM_block4, BlockData, BlockSize); + + //---------------------------------------------------------------------- + // Test data forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_data1 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_data1, DW_FORM_data1, Data1); + + const auto Attr_DW_FORM_data2 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_data2, DW_FORM_data2, Data2); + + const auto Attr_DW_FORM_data4 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_data4, DW_FORM_data4, Data4); + + const auto Attr_DW_FORM_data8 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_data8, DW_FORM_data8, Data8); + + //---------------------------------------------------------------------- + // Test string forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_string = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_string, DW_FORM_string, StringValue); + + const auto Attr_DW_FORM_strp = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_strp, DW_FORM_strp, StrpValue); + + //---------------------------------------------------------------------- + // Test reference forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_ref_addr = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_ref_addr, DW_FORM_ref_addr, RefAddr); + + const auto Attr_DW_FORM_ref1 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_ref1, DW_FORM_ref1, Data1); + + const auto Attr_DW_FORM_ref2 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_ref2, DW_FORM_ref2, Data2); + + const auto Attr_DW_FORM_ref4 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_ref4, DW_FORM_ref4, Data4); + + const auto Attr_DW_FORM_ref8 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_ref8, DW_FORM_ref8, Data8); + + const auto Attr_DW_FORM_ref_sig8 = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_ref_sig8, DW_FORM_ref_sig8, Data8_2); + + const auto Attr_DW_FORM_ref_udata = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_ref_udata, DW_FORM_ref_udata, UData[0]); + + //---------------------------------------------------------------------- + // Test flag forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_flag_true = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_flag_true, DW_FORM_flag, true); + + const auto Attr_DW_FORM_flag_false = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_flag_false, DW_FORM_flag, false); + + const auto Attr_DW_FORM_flag_present = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_flag_present, DW_FORM_flag_present); + + //---------------------------------------------------------------------- + // Test SLEB128 based forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_sdata = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_sdata, DW_FORM_sdata, SData); + + //---------------------------------------------------------------------- + // Test ULEB128 based forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_udata = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_udata, DW_FORM_udata, UData[0]); + + //---------------------------------------------------------------------- + // Test DWARF32/DWARF64 forms + //---------------------------------------------------------------------- + const auto Attr_DW_FORM_GNU_ref_alt = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_GNU_ref_alt, DW_FORM_GNU_ref_alt, + Dwarf32Values[0]); + + const auto Attr_DW_FORM_sec_offset = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_DW_FORM_sec_offset, DW_FORM_sec_offset, + Dwarf32Values[1]); + + //---------------------------------------------------------------------- + // Add an address at the end to make sure we can decode this value + //---------------------------------------------------------------------- + const auto Attr_Last = static_cast<dwarf::Attribute>(Attr++); + CUDie.addAttribute(Attr_Last, DW_FORM_addr, AddrValue); + + //---------------------------------------------------------------------- + // Generate the DWARF + //---------------------------------------------------------------------- + StringRef FileBytes = DG->generate(); + MemoryBufferRef FileBuffer(FileBytes, "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + auto DieDG = U->getUnitDIE(false); + EXPECT_TRUE(DieDG.isValid()); + + //---------------------------------------------------------------------- + // Test address forms + //---------------------------------------------------------------------- + EXPECT_EQ(DieDG.getAttributeValueAsAddress(Attr_DW_FORM_addr, 0), + AddrValue); + + //---------------------------------------------------------------------- + // Test block forms + //---------------------------------------------------------------------- + Optional<DWARFFormValue> FormValue; + ArrayRef<uint8_t> ExtractedBlockData; + Optional<ArrayRef<uint8_t>> BlockDataOpt; + + FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block); + EXPECT_TRUE((bool)FormValue); + BlockDataOpt = FormValue->getAsBlock(); + EXPECT_TRUE(BlockDataOpt.hasValue()); + ExtractedBlockData = BlockDataOpt.getValue(); + EXPECT_EQ(ExtractedBlockData.size(), BlockSize); + EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); + + FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block1); + EXPECT_TRUE((bool)FormValue); + BlockDataOpt = FormValue->getAsBlock(); + EXPECT_TRUE(BlockDataOpt.hasValue()); + ExtractedBlockData = BlockDataOpt.getValue(); + EXPECT_EQ(ExtractedBlockData.size(), BlockSize); + EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); + + FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block2); + EXPECT_TRUE((bool)FormValue); + BlockDataOpt = FormValue->getAsBlock(); + EXPECT_TRUE(BlockDataOpt.hasValue()); + ExtractedBlockData = BlockDataOpt.getValue(); + EXPECT_EQ(ExtractedBlockData.size(), BlockSize); + EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); + + FormValue = DieDG.getAttributeValue(Attr_DW_FORM_block4); + EXPECT_TRUE((bool)FormValue); + BlockDataOpt = FormValue->getAsBlock(); + EXPECT_TRUE(BlockDataOpt.hasValue()); + ExtractedBlockData = BlockDataOpt.getValue(); + EXPECT_EQ(ExtractedBlockData.size(), BlockSize); + EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); + + //---------------------------------------------------------------------- + // Test data forms + //---------------------------------------------------------------------- + EXPECT_EQ( + DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data1, 0), + Data1); + EXPECT_EQ( + DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data2, 0), + Data2); + EXPECT_EQ( + DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data4, 0), + Data4); + EXPECT_EQ( + DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_data8, 0), + Data8); + + //---------------------------------------------------------------------- + // Test string forms + //---------------------------------------------------------------------- + const char *ExtractedStringValue = + DieDG.getAttributeValueAsString(Attr_DW_FORM_string, nullptr); + EXPECT_TRUE(ExtractedStringValue != nullptr); + EXPECT_TRUE(strcmp(StringValue, ExtractedStringValue) == 0); + + const char *ExtractedStrpValue = + DieDG.getAttributeValueAsString(Attr_DW_FORM_strp, nullptr); + EXPECT_TRUE(ExtractedStrpValue != nullptr); + EXPECT_TRUE(strcmp(StrpValue, ExtractedStrpValue) == 0); + + //---------------------------------------------------------------------- + // Test reference forms + //---------------------------------------------------------------------- + EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref_addr, 0), + RefAddr); + EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref1, 0), + Data1); + EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref2, 0), + Data2); + EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref4, 0), + Data4); + EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref8, 0), + Data8); + EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref_sig8, 0), + Data8_2); + EXPECT_EQ(DieDG.getAttributeValueAsReference(Attr_DW_FORM_ref_udata, 0), + UData[0]); + + //---------------------------------------------------------------------- + // Test flag forms + //---------------------------------------------------------------------- + EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant( + Attr_DW_FORM_flag_true, 0ULL), + 1ULL); + EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant( + Attr_DW_FORM_flag_false, 1ULL), + 0ULL); + EXPECT_EQ(DieDG.getAttributeValueAsUnsignedConstant( + Attr_DW_FORM_flag_present, 0ULL), + 1ULL); + + // TODO: test Attr_DW_FORM_implicit_const extraction + + //---------------------------------------------------------------------- + // Test SLEB128 based forms + //---------------------------------------------------------------------- + EXPECT_EQ(DieDG.getAttributeValueAsSignedConstant(Attr_DW_FORM_sdata, 0), + SData); + + //---------------------------------------------------------------------- + // Test ULEB128 based forms + //---------------------------------------------------------------------- + EXPECT_EQ( + DieDG.getAttributeValueAsUnsignedConstant(Attr_DW_FORM_udata, 0), + UData[0]); + + //---------------------------------------------------------------------- + // Test DWARF32/DWARF64 forms + //---------------------------------------------------------------------- + EXPECT_EQ( + DieDG.getAttributeValueAsReference(Attr_DW_FORM_GNU_ref_alt, 0), + Dwarf32Values[0]); + EXPECT_EQ( + DieDG.getAttributeValueAsSectionOffset(Attr_DW_FORM_sec_offset, 0), + Dwarf32Values[1]); + + //---------------------------------------------------------------------- + // Add an address at the end to make sure we can decode this value + //---------------------------------------------------------------------- + EXPECT_EQ(DieDG.getAttributeValueAsAddress(Attr_Last, 0), AddrValue); +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr4AllForms) { + // Test that we can decode all forms for DWARF32, version 2, with 4 byte + // addresses. + typedef uint32_t AddrType; + // DW_FORM_ref_addr are the same as the address type in DWARF32 version 2. + typedef AddrType RefAddrType; + TestAllForms<2, AddrType, RefAddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr8AllForms) { + // Test that we can decode all forms for DWARF32, version 2, with 4 byte + // addresses. + typedef uint64_t AddrType; + // DW_FORM_ref_addr are the same as the address type in DWARF32 version 2. + typedef AddrType RefAddrType; + TestAllForms<2, AddrType, RefAddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr4AllForms) { + // Test that we can decode all forms for DWARF32, version 3, with 4 byte + // addresses. + typedef uint32_t AddrType; + // DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and later. + typedef uint32_t RefAddrType; + TestAllForms<3, AddrType, RefAddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr8AllForms) { + // Test that we can decode all forms for DWARF32, version 3, with 8 byte + // addresses. + typedef uint64_t AddrType; + // DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and later + typedef uint32_t RefAddrType; + TestAllForms<3, AddrType, RefAddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr4AllForms) { + // Test that we can decode all forms for DWARF32, version 4, with 4 byte + // addresses. + typedef uint32_t AddrType; + // DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and later + typedef uint32_t RefAddrType; + TestAllForms<4, AddrType, RefAddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr8AllForms) { + // Test that we can decode all forms for DWARF32, version 4, with 8 byte + // addresses. + typedef uint64_t AddrType; + // DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and later + typedef uint32_t RefAddrType; + TestAllForms<4, AddrType, RefAddrType>(); +} + +template <uint16_t Version, class AddrType> void TestChildren() { + // Test that we can decode DW_FORM_ref_addr values correctly in DWARF 2 with + // 4 byte addresses. DW_FORM_ref_addr values should be 4 bytes when using + // 8 byte addresses. + + const uint8_t AddrSize = sizeof(AddrType); + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + dwarfgen::DIE CUDie = CU.getUnitDIE(); + + CUDie.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c"); + CUDie.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C); + + dwarfgen::DIE SubprogramDie = CUDie.addChild(DW_TAG_subprogram); + SubprogramDie.addAttribute(DW_AT_name, DW_FORM_strp, "main"); + SubprogramDie.addAttribute(DW_AT_low_pc, DW_FORM_addr, 0x1000U); + SubprogramDie.addAttribute(DW_AT_high_pc, DW_FORM_addr, 0x2000U); + + dwarfgen::DIE IntDie = CUDie.addChild(DW_TAG_base_type); + IntDie.addAttribute(DW_AT_name, DW_FORM_strp, "int"); + IntDie.addAttribute(DW_AT_encoding, DW_FORM_data1, DW_ATE_signed); + IntDie.addAttribute(DW_AT_byte_size, DW_FORM_data1, 4); + + dwarfgen::DIE ArgcDie = SubprogramDie.addChild(DW_TAG_formal_parameter); + ArgcDie.addAttribute(DW_AT_name, DW_FORM_strp, "argc"); + // ArgcDie.addAttribute(DW_AT_type, DW_FORM_ref4, IntDie); + ArgcDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, IntDie); + + StringRef FileBytes = DG->generate(); + MemoryBufferRef FileBuffer(FileBytes, "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + + // Get the compile unit DIE is valid. + auto DieDG = U->getUnitDIE(false); + EXPECT_TRUE(DieDG.isValid()); + // DieDG.dump(llvm::outs(), U, UINT32_MAX); + + // Verify the first child of the compile unit DIE is our subprogram. + auto SubprogramDieDG = DieDG.getFirstChild(); + EXPECT_TRUE(SubprogramDieDG.isValid()); + EXPECT_EQ(SubprogramDieDG.getTag(), DW_TAG_subprogram); + + // Verify the first child of the subprogram is our formal parameter. + auto ArgcDieDG = SubprogramDieDG.getFirstChild(); + EXPECT_TRUE(ArgcDieDG.isValid()); + EXPECT_EQ(ArgcDieDG.getTag(), DW_TAG_formal_parameter); + + // Verify our formal parameter has a NULL tag sibling. + auto NullDieDG = ArgcDieDG.getSibling(); + EXPECT_TRUE(NullDieDG.isValid()); + if (NullDieDG) { + EXPECT_EQ(NullDieDG.getTag(), DW_TAG_null); + EXPECT_TRUE(!NullDieDG.getSibling().isValid()); + EXPECT_TRUE(!NullDieDG.getFirstChild().isValid()); + } + + // Verify the sibling of our subprogram is our integer base type. + auto IntDieDG = SubprogramDieDG.getSibling(); + EXPECT_TRUE(IntDieDG.isValid()); + EXPECT_EQ(IntDieDG.getTag(), DW_TAG_base_type); + + // Verify the sibling of our subprogram is our integer base is a NULL tag. + NullDieDG = IntDieDG.getSibling(); + EXPECT_TRUE(NullDieDG.isValid()); + if (NullDieDG) { + EXPECT_EQ(NullDieDG.getTag(), DW_TAG_null); + EXPECT_TRUE(!NullDieDG.getSibling().isValid()); + EXPECT_TRUE(!NullDieDG.getFirstChild().isValid()); + } +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Children) { + // Test that we can decode all forms for DWARF32, version 2, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestChildren<2, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr8Children) { + // Test that we can decode all forms for DWARF32, version 2, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestChildren<2, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Children) { + // Test that we can decode all forms for DWARF32, version 3, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestChildren<3, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr8Children) { + // Test that we can decode all forms for DWARF32, version 3, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestChildren<3, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Children) { + // Test that we can decode all forms for DWARF32, version 4, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestChildren<4, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr8Children) { + // Test that we can decode all forms for DWARF32, version 4, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestChildren<4, AddrType>(); +} + +template <uint16_t Version, class AddrType> void TestReferences() { + // Test that we can decode DW_FORM_refXXX values correctly in DWARF. + + const uint8_t AddrSize = sizeof(AddrType); + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU1 = DG->addCompileUnit(); + dwarfgen::CompileUnit &CU2 = DG->addCompileUnit(); + + dwarfgen::DIE CU1Die = CU1.getUnitDIE(); + CU1Die.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c"); + CU1Die.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C); + + dwarfgen::DIE CU1TypeDie = CU1Die.addChild(DW_TAG_base_type); + CU1TypeDie.addAttribute(DW_AT_name, DW_FORM_strp, "int"); + CU1TypeDie.addAttribute(DW_AT_encoding, DW_FORM_data1, DW_ATE_signed); + CU1TypeDie.addAttribute(DW_AT_byte_size, DW_FORM_data1, 4); + + dwarfgen::DIE CU1Ref1Die = CU1Die.addChild(DW_TAG_variable); + CU1Ref1Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref1"); + CU1Ref1Die.addAttribute(DW_AT_type, DW_FORM_ref1, CU1TypeDie); + + dwarfgen::DIE CU1Ref2Die = CU1Die.addChild(DW_TAG_variable); + CU1Ref2Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref2"); + CU1Ref2Die.addAttribute(DW_AT_type, DW_FORM_ref2, CU1TypeDie); + + dwarfgen::DIE CU1Ref4Die = CU1Die.addChild(DW_TAG_variable); + CU1Ref4Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref4"); + CU1Ref4Die.addAttribute(DW_AT_type, DW_FORM_ref4, CU1TypeDie); + + dwarfgen::DIE CU1Ref8Die = CU1Die.addChild(DW_TAG_variable); + CU1Ref8Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref8"); + CU1Ref8Die.addAttribute(DW_AT_type, DW_FORM_ref8, CU1TypeDie); + + dwarfgen::DIE CU1RefAddrDie = CU1Die.addChild(DW_TAG_variable); + CU1RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU1RefAddr"); + CU1RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU1TypeDie); + + dwarfgen::DIE CU2Die = CU2.getUnitDIE(); + CU2Die.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/foo.c"); + CU2Die.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C); + + dwarfgen::DIE CU2TypeDie = CU2Die.addChild(DW_TAG_base_type); + CU2TypeDie.addAttribute(DW_AT_name, DW_FORM_strp, "float"); + CU2TypeDie.addAttribute(DW_AT_encoding, DW_FORM_data1, DW_ATE_float); + CU2TypeDie.addAttribute(DW_AT_byte_size, DW_FORM_data1, 4); + + dwarfgen::DIE CU2Ref1Die = CU2Die.addChild(DW_TAG_variable); + CU2Ref1Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref1"); + CU2Ref1Die.addAttribute(DW_AT_type, DW_FORM_ref1, CU2TypeDie); + + dwarfgen::DIE CU2Ref2Die = CU2Die.addChild(DW_TAG_variable); + CU2Ref2Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref2"); + CU2Ref2Die.addAttribute(DW_AT_type, DW_FORM_ref2, CU2TypeDie); + + dwarfgen::DIE CU2Ref4Die = CU2Die.addChild(DW_TAG_variable); + CU2Ref4Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref4"); + CU2Ref4Die.addAttribute(DW_AT_type, DW_FORM_ref4, CU2TypeDie); + + dwarfgen::DIE CU2Ref8Die = CU2Die.addChild(DW_TAG_variable); + CU2Ref8Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref8"); + CU2Ref8Die.addAttribute(DW_AT_type, DW_FORM_ref8, CU2TypeDie); + + dwarfgen::DIE CU2RefAddrDie = CU2Die.addChild(DW_TAG_variable); + CU2RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU2RefAddr"); + CU2RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU2TypeDie); + + // Refer to a type in CU1 from CU2 + dwarfgen::DIE CU2ToCU1RefAddrDie = CU2Die.addChild(DW_TAG_variable); + CU2ToCU1RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU2ToCU1RefAddr"); + CU2ToCU1RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU1TypeDie); + + // Refer to a type in CU2 from CU1 + dwarfgen::DIE CU1ToCU2RefAddrDie = CU1Die.addChild(DW_TAG_variable); + CU1ToCU2RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU1ToCU2RefAddr"); + CU1ToCU2RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU2TypeDie); + + StringRef FileBytes = DG->generate(); + MemoryBufferRef FileBuffer(FileBytes, "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 2u); + DWARFCompileUnit *U1 = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U2 = DwarfContext.getCompileUnitAtIndex(1); + + // Get the compile unit DIE is valid. + auto Unit1DieDG = U1->getUnitDIE(false); + EXPECT_TRUE(Unit1DieDG.isValid()); + // Unit1DieDG.dump(llvm::outs(), UINT32_MAX); + + auto Unit2DieDG = U2->getUnitDIE(false); + EXPECT_TRUE(Unit2DieDG.isValid()); + // Unit2DieDG.dump(llvm::outs(), UINT32_MAX); + + // Verify the first child of the compile unit 1 DIE is our int base type. + auto CU1TypeDieDG = Unit1DieDG.getFirstChild(); + EXPECT_TRUE(CU1TypeDieDG.isValid()); + EXPECT_EQ(CU1TypeDieDG.getTag(), DW_TAG_base_type); + EXPECT_EQ( + CU1TypeDieDG.getAttributeValueAsUnsignedConstant(DW_AT_encoding, 0), + DW_ATE_signed); + + // Verify the first child of the compile unit 2 DIE is our float base type. + auto CU2TypeDieDG = Unit2DieDG.getFirstChild(); + EXPECT_TRUE(CU2TypeDieDG.isValid()); + EXPECT_EQ(CU2TypeDieDG.getTag(), DW_TAG_base_type); + EXPECT_EQ( + CU2TypeDieDG.getAttributeValueAsUnsignedConstant(DW_AT_encoding, 0), + DW_ATE_float); + + // Verify the sibling of the base type DIE is our Ref1 DIE and that its + // DW_AT_type points to our base type DIE. + auto CU1Ref1DieDG = CU1TypeDieDG.getSibling(); + EXPECT_TRUE(CU1Ref1DieDG.isValid()); + EXPECT_EQ(CU1Ref1DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU1Ref1DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU1TypeDieDG.getOffset()); + // Verify the sibling is our Ref2 DIE and that its DW_AT_type points to our + // base type DIE in CU1. + auto CU1Ref2DieDG = CU1Ref1DieDG.getSibling(); + EXPECT_TRUE(CU1Ref2DieDG.isValid()); + EXPECT_EQ(CU1Ref2DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU1Ref2DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU1TypeDieDG.getOffset()); + + // Verify the sibling is our Ref4 DIE and that its DW_AT_type points to our + // base type DIE in CU1. + auto CU1Ref4DieDG = CU1Ref2DieDG.getSibling(); + EXPECT_TRUE(CU1Ref4DieDG.isValid()); + EXPECT_EQ(CU1Ref4DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU1Ref4DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU1TypeDieDG.getOffset()); + + // Verify the sibling is our Ref8 DIE and that its DW_AT_type points to our + // base type DIE in CU1. + auto CU1Ref8DieDG = CU1Ref4DieDG.getSibling(); + EXPECT_TRUE(CU1Ref8DieDG.isValid()); + EXPECT_EQ(CU1Ref8DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU1Ref8DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU1TypeDieDG.getOffset()); + + // Verify the sibling is our RefAddr DIE and that its DW_AT_type points to our + // base type DIE in CU1. + auto CU1RefAddrDieDG = CU1Ref8DieDG.getSibling(); + EXPECT_TRUE(CU1RefAddrDieDG.isValid()); + EXPECT_EQ(CU1RefAddrDieDG.getTag(), DW_TAG_variable); + EXPECT_EQ( + CU1RefAddrDieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU1TypeDieDG.getOffset()); + + // Verify the sibling of the Ref4 DIE is our RefAddr DIE and that its + // DW_AT_type points to our base type DIE. + auto CU1ToCU2RefAddrDieDG = CU1RefAddrDieDG.getSibling(); + EXPECT_TRUE(CU1ToCU2RefAddrDieDG.isValid()); + EXPECT_EQ(CU1ToCU2RefAddrDieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU1ToCU2RefAddrDieDG.getAttributeValueAsReference(DW_AT_type, + -1ULL), + CU2TypeDieDG.getOffset()); + + // Verify the sibling of the base type DIE is our Ref1 DIE and that its + // DW_AT_type points to our base type DIE. + auto CU2Ref1DieDG = CU2TypeDieDG.getSibling(); + EXPECT_TRUE(CU2Ref1DieDG.isValid()); + EXPECT_EQ(CU2Ref1DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU2Ref1DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU2TypeDieDG.getOffset()); + // Verify the sibling is our Ref2 DIE and that its DW_AT_type points to our + // base type DIE in CU2. + auto CU2Ref2DieDG = CU2Ref1DieDG.getSibling(); + EXPECT_TRUE(CU2Ref2DieDG.isValid()); + EXPECT_EQ(CU2Ref2DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU2Ref2DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU2TypeDieDG.getOffset()); + + // Verify the sibling is our Ref4 DIE and that its DW_AT_type points to our + // base type DIE in CU2. + auto CU2Ref4DieDG = CU2Ref2DieDG.getSibling(); + EXPECT_TRUE(CU2Ref4DieDG.isValid()); + EXPECT_EQ(CU2Ref4DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU2Ref4DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU2TypeDieDG.getOffset()); + + // Verify the sibling is our Ref8 DIE and that its DW_AT_type points to our + // base type DIE in CU2. + auto CU2Ref8DieDG = CU2Ref4DieDG.getSibling(); + EXPECT_TRUE(CU2Ref8DieDG.isValid()); + EXPECT_EQ(CU2Ref8DieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU2Ref8DieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU2TypeDieDG.getOffset()); + + // Verify the sibling is our RefAddr DIE and that its DW_AT_type points to our + // base type DIE in CU2. + auto CU2RefAddrDieDG = CU2Ref8DieDG.getSibling(); + EXPECT_TRUE(CU2RefAddrDieDG.isValid()); + EXPECT_EQ(CU2RefAddrDieDG.getTag(), DW_TAG_variable); + EXPECT_EQ( + CU2RefAddrDieDG.getAttributeValueAsReference(DW_AT_type, -1ULL), + CU2TypeDieDG.getOffset()); + + // Verify the sibling of the Ref4 DIE is our RefAddr DIE and that its + // DW_AT_type points to our base type DIE. + auto CU2ToCU1RefAddrDieDG = CU2RefAddrDieDG.getSibling(); + EXPECT_TRUE(CU2ToCU1RefAddrDieDG.isValid()); + EXPECT_EQ(CU2ToCU1RefAddrDieDG.getTag(), DW_TAG_variable); + EXPECT_EQ(CU2ToCU1RefAddrDieDG.getAttributeValueAsReference(DW_AT_type, + -1ULL), + CU1TypeDieDG.getOffset()); +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr4References) { + // Test that we can decode all forms for DWARF32, version 2, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestReferences<2, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr8References) { + // Test that we can decode all forms for DWARF32, version 2, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestReferences<2, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr4References) { + // Test that we can decode all forms for DWARF32, version 3, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestReferences<3, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr8References) { + // Test that we can decode all forms for DWARF32, version 3, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestReferences<3, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr4References) { + // Test that we can decode all forms for DWARF32, version 4, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestReferences<4, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr8References) { + // Test that we can decode all forms for DWARF32, version 4, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestReferences<4, AddrType>(); +} + +template <uint16_t Version, class AddrType> void TestAddresses() { + // Test the DWARF APIs related to accessing the DW_AT_low_pc and + // DW_AT_high_pc. + const uint8_t AddrSize = sizeof(AddrType); + const bool SupportsHighPCAsOffset = Version >= 4; + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + dwarfgen::DIE CUDie = CU.getUnitDIE(); + + CUDie.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c"); + CUDie.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C); + + // Create a subprogram DIE with no low or high PC. + dwarfgen::DIE SubprogramNoPC = CUDie.addChild(DW_TAG_subprogram); + SubprogramNoPC.addAttribute(DW_AT_name, DW_FORM_strp, "no_pc"); + + // Create a subprogram DIE with a low PC only. + dwarfgen::DIE SubprogramLowPC = CUDie.addChild(DW_TAG_subprogram); + SubprogramLowPC.addAttribute(DW_AT_name, DW_FORM_strp, "low_pc"); + const uint64_t ActualLowPC = 0x1000; + const uint64_t ActualHighPC = 0x2000; + const uint64_t ActualHighPCOffset = ActualHighPC - ActualLowPC; + SubprogramLowPC.addAttribute(DW_AT_low_pc, DW_FORM_addr, ActualLowPC); + + // Create a subprogram DIE with a low and high PC. + dwarfgen::DIE SubprogramLowHighPC = CUDie.addChild(DW_TAG_subprogram); + SubprogramLowHighPC.addAttribute(DW_AT_name, DW_FORM_strp, "low_high_pc"); + SubprogramLowHighPC.addAttribute(DW_AT_low_pc, DW_FORM_addr, ActualLowPC); + // Encode the high PC as an offset from the low PC if supported. + if (SupportsHighPCAsOffset) + SubprogramLowHighPC.addAttribute(DW_AT_high_pc, DW_FORM_data4, + ActualHighPCOffset); + else + SubprogramLowHighPC.addAttribute(DW_AT_high_pc, DW_FORM_addr, ActualHighPC); + + StringRef FileBytes = DG->generate(); + MemoryBufferRef FileBuffer(FileBytes, "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + + // Get the compile unit DIE is valid. + auto DieDG = U->getUnitDIE(false); + EXPECT_TRUE(DieDG.isValid()); + // DieDG.dump(llvm::outs(), U, UINT32_MAX); + + uint64_t LowPC, HighPC; + Optional<uint64_t> OptU64; + // Verify the that our subprogram with no PC value fails appropriately when + // asked for any PC values. + auto SubprogramDieNoPC = DieDG.getFirstChild(); + EXPECT_TRUE(SubprogramDieNoPC.isValid()); + EXPECT_EQ(SubprogramDieNoPC.getTag(), DW_TAG_subprogram); + OptU64 = SubprogramDieNoPC.getAttributeValueAsAddress(DW_AT_low_pc); + EXPECT_FALSE((bool)OptU64); + OptU64 = SubprogramDieNoPC.getAttributeValueAsAddress(DW_AT_high_pc); + EXPECT_FALSE((bool)OptU64); + EXPECT_FALSE(SubprogramDieNoPC.getLowAndHighPC(LowPC, HighPC)); + OptU64 = SubprogramDieNoPC.getAttributeValueAsAddress(DW_AT_high_pc); + EXPECT_FALSE((bool)OptU64); + OptU64 = SubprogramDieNoPC.getAttributeValueAsUnsignedConstant(DW_AT_high_pc); + EXPECT_FALSE((bool)OptU64); + OptU64 = SubprogramDieNoPC.getHighPC(ActualLowPC); + EXPECT_FALSE((bool)OptU64); + EXPECT_FALSE(SubprogramDieNoPC.getLowAndHighPC(LowPC, HighPC)); + + + // Verify the that our subprogram with only a low PC value succeeds when + // we ask for the Low PC, but fails appropriately when asked for the high PC + // or both low and high PC values. + auto SubprogramDieLowPC = SubprogramDieNoPC.getSibling(); + EXPECT_TRUE(SubprogramDieLowPC.isValid()); + EXPECT_EQ(SubprogramDieLowPC.getTag(), DW_TAG_subprogram); + OptU64 = SubprogramDieLowPC.getAttributeValueAsAddress(DW_AT_low_pc); + EXPECT_TRUE((bool)OptU64); + EXPECT_EQ(OptU64.getValue(), ActualLowPC); + OptU64 = SubprogramDieLowPC.getAttributeValueAsAddress(DW_AT_high_pc); + EXPECT_FALSE((bool)OptU64); + OptU64 = SubprogramDieLowPC.getAttributeValueAsUnsignedConstant(DW_AT_high_pc); + EXPECT_FALSE((bool)OptU64); + OptU64 = SubprogramDieLowPC.getHighPC(ActualLowPC); + EXPECT_FALSE((bool)OptU64); + EXPECT_FALSE(SubprogramDieLowPC.getLowAndHighPC(LowPC, HighPC)); + + + // Verify the that our subprogram with only a low PC value succeeds when + // we ask for the Low PC, but fails appropriately when asked for the high PC + // or both low and high PC values. + auto SubprogramDieLowHighPC = SubprogramDieLowPC.getSibling(); + EXPECT_TRUE(SubprogramDieLowHighPC.isValid()); + EXPECT_EQ(SubprogramDieLowHighPC.getTag(), DW_TAG_subprogram); + OptU64 = SubprogramDieLowHighPC.getAttributeValueAsAddress(DW_AT_low_pc); + EXPECT_TRUE((bool)OptU64); + EXPECT_EQ(OptU64.getValue(), ActualLowPC); + // Get the high PC as an address. This should succeed if the high PC was + // encoded as an address and fail if the high PC was encoded as an offset. + OptU64 = SubprogramDieLowHighPC.getAttributeValueAsAddress(DW_AT_high_pc); + if (SupportsHighPCAsOffset) { + EXPECT_FALSE((bool)OptU64); + } else { + EXPECT_TRUE((bool)OptU64); + EXPECT_EQ(OptU64.getValue(), ActualHighPC); + } + // Get the high PC as an unsigned constant. This should succeed if the high PC + // was encoded as an offset and fail if the high PC was encoded as an address. + OptU64 = SubprogramDieLowHighPC.getAttributeValueAsUnsignedConstant( + DW_AT_high_pc); + if (SupportsHighPCAsOffset) { + EXPECT_TRUE((bool)OptU64); + EXPECT_EQ(OptU64.getValue(), ActualHighPCOffset); + } else { + EXPECT_FALSE((bool)OptU64); + } + + OptU64 = SubprogramDieLowHighPC.getHighPC(ActualLowPC); + EXPECT_TRUE((bool)OptU64); + EXPECT_EQ(OptU64.getValue(), ActualHighPC); + + EXPECT_TRUE(SubprogramDieLowHighPC.getLowAndHighPC(LowPC, HighPC)); + EXPECT_EQ(LowPC, ActualLowPC); + EXPECT_EQ(HighPC, ActualHighPC); +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Addresses) { + // Test that we can decode address values in DWARF32, version 2, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestAddresses<2, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version2Addr8Addresses) { + // Test that we can decode address values in DWARF32, version 2, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestAddresses<2, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Addresses) { + // Test that we can decode address values in DWARF32, version 3, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestAddresses<3, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version3Addr8Addresses) { + // Test that we can decode address values in DWARF32, version 3, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestAddresses<3, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Addresses) { + // Test that we can decode address values in DWARF32, version 4, with 4 byte + // addresses. + typedef uint32_t AddrType; + TestAddresses<4, AddrType>(); +} + +TEST(DWARFDebugInfo, TestDWARF32Version4Addr8Addresses) { + // Test that we can decode address values in DWARF32, version 4, with 8 byte + // addresses. + typedef uint64_t AddrType; + TestAddresses<4, AddrType>(); +} + +TEST(DWARFDebugInfo, TestRelations) { + // Test the DWARF APIs related to accessing the DW_AT_low_pc and + // DW_AT_high_pc. + uint16_t Version = 4; + + const uint8_t AddrSize = sizeof(void *); + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + + enum class Tag: uint16_t { + A = dwarf::DW_TAG_lo_user, + B, + B1, + B2, + C, + C1 + }; + + // Scope to allow us to re-use the same DIE names + { + // Create DWARF tree that looks like: + // + // CU + // A + // B + // B1 + // B2 + // C + // C1 + dwarfgen::DIE CUDie = CU.getUnitDIE(); + CUDie.addChild((dwarf::Tag)Tag::A); + dwarfgen::DIE B = CUDie.addChild((dwarf::Tag)Tag::B); + dwarfgen::DIE C = CUDie.addChild((dwarf::Tag)Tag::C); + B.addChild((dwarf::Tag)Tag::B1); + B.addChild((dwarf::Tag)Tag::B2); + C.addChild((dwarf::Tag)Tag::C1); + } + + MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + + // Get the compile unit DIE is valid. + auto CUDie = U->getUnitDIE(false); + EXPECT_TRUE(CUDie.isValid()); + // DieDG.dump(llvm::outs(), U, UINT32_MAX); + + // The compile unit doesn't have a parent or a sibling. + auto ParentDie = CUDie.getParent(); + EXPECT_FALSE(ParentDie.isValid()); + auto SiblingDie = CUDie.getSibling(); + EXPECT_FALSE(SiblingDie.isValid()); + + // Get the children of the compile unit + auto A = CUDie.getFirstChild(); + auto B = A.getSibling(); + auto C = B.getSibling(); + auto Null = C.getSibling(); + + // Verify NULL Die is NULL and has no children or siblings + EXPECT_TRUE(Null.isNULL()); + EXPECT_FALSE(Null.getSibling().isValid()); + EXPECT_FALSE(Null.getFirstChild().isValid()); + + // Verify all children of the compile unit DIE are correct. + EXPECT_EQ(A.getTag(), (dwarf::Tag)Tag::A); + EXPECT_EQ(B.getTag(), (dwarf::Tag)Tag::B); + EXPECT_EQ(C.getTag(), (dwarf::Tag)Tag::C); + + // Verify who has children + EXPECT_FALSE(A.hasChildren()); + EXPECT_TRUE(B.hasChildren()); + + // Make sure the parent of all the children of the compile unit are the + // compile unit. + EXPECT_EQ(A.getParent(), CUDie); + EXPECT_EQ(B.getParent(), CUDie); + EXPECT_EQ(Null.getParent(), CUDie); + + EXPECT_FALSE(A.getFirstChild().isValid()); + + // Verify the children of the B DIE + auto B1 = B.getFirstChild(); + auto B2 = B1.getSibling(); + EXPECT_TRUE(B2.getSibling().isNULL()); + + // Verify all children of the B DIE correctly valid or invalid. + EXPECT_EQ(B1.getTag(), (dwarf::Tag)Tag::B1); + EXPECT_EQ(B2.getTag(), (dwarf::Tag)Tag::B2); + + // Make sure the parent of all the children of the B are the B. + EXPECT_EQ(B1.getParent(), B); + EXPECT_EQ(B2.getParent(), B); +} + +TEST(DWARFDebugInfo, TestDWARFDie) { + + // Make sure a default constructed DWARFDie doesn't have any parent, sibling + // or child; + DWARFDie DefaultDie; + EXPECT_FALSE(DefaultDie.getParent().isValid()); + EXPECT_FALSE(DefaultDie.getFirstChild().isValid()); + EXPECT_FALSE(DefaultDie.getSibling().isValid()); +} + + +} // end anonymous namespace diff --git a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp index eb9607ade16d..028a03595de6 100644 --- a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp @@ -21,19 +21,50 @@ using namespace dwarf; namespace { TEST(DWARFFormValue, FixedFormSizes) { - // Size of DW_FORM_addr and DW_FORM_ref_addr are equal in DWARF2, - // DW_FORM_ref_addr is always 4 bytes in DWARF32 starting from DWARF3. - ArrayRef<uint8_t> sizes = DWARFFormValue::getFixedFormSizes(4, 2); - EXPECT_EQ(sizes[DW_FORM_addr], sizes[DW_FORM_ref_addr]); - sizes = DWARFFormValue::getFixedFormSizes(8, 2); - EXPECT_EQ(sizes[DW_FORM_addr], sizes[DW_FORM_ref_addr]); - sizes = DWARFFormValue::getFixedFormSizes(8, 3); - EXPECT_EQ(4, sizes[DW_FORM_ref_addr]); - // Check that we don't have fixed form sizes for weird address sizes. - EXPECT_EQ(0U, DWARFFormValue::getFixedFormSizes(16, 2).size()); + Optional<uint8_t> RefSize; + Optional<uint8_t> AddrSize; + // Test 32 bit DWARF version 2 with 4 byte addresses. + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 2, 4, DWARF32); + AddrSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 2, 4, DWARF32); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_TRUE(AddrSize.hasValue()); + EXPECT_EQ(*RefSize, *AddrSize); + + // Test 32 bit DWARF version 2 with 8 byte addresses. + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 2, 8, DWARF32); + AddrSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 2, 8, DWARF32); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_TRUE(AddrSize.hasValue()); + EXPECT_EQ(*RefSize, *AddrSize); + + // DW_FORM_ref_addr is 4 bytes in DWARF 32 in DWARF version 3 and beyond. + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 3, 4, DWARF32); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_EQ(*RefSize, 4); + + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 4, 4, DWARF32); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_EQ(*RefSize, 4); + + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 5, 4, DWARF32); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_EQ(*RefSize, 4); + + // DW_FORM_ref_addr is 8 bytes in DWARF 64 in DWARF version 3 and beyond. + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 3, 8, DWARF64); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_EQ(*RefSize, 8); + + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 4, 8, DWARF64); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_EQ(*RefSize, 8); + + RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, 5, 8, DWARF64); + EXPECT_TRUE(RefSize.hasValue()); + EXPECT_EQ(*RefSize, 8); } -bool isFormClass(uint16_t Form, DWARFFormValue::FormClass FC) { +bool isFormClass(dwarf::Form Form, DWARFFormValue::FormClass FC) { return DWARFFormValue(Form).isFormClass(FC); } @@ -52,7 +83,7 @@ TEST(DWARFFormValue, FormClass) { } template<typename RawTypeT> -DWARFFormValue createDataXFormValue(uint16_t Form, RawTypeT Value) { +DWARFFormValue createDataXFormValue(dwarf::Form Form, RawTypeT Value) { char Raw[sizeof(RawTypeT)]; memcpy(Raw, &Value, sizeof(RawTypeT)); uint32_t Offset = 0; diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp new file mode 100644 index 000000000000..9a72f70a0f21 --- /dev/null +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp @@ -0,0 +1,266 @@ +//===--- unittests/DebugInfo/DWARF/DwarfGenerator.cpp -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../lib/CodeGen/AsmPrinter/DwarfStringPool.h" +#include "DwarfGenerator.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/IR/LegacyPassManagers.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/PassAnalysisSupport.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +using namespace llvm; +using namespace dwarf; + +namespace {} // end anonymous namespace + +//===----------------------------------------------------------------------===// +/// dwarfgen::DIE implementation. +//===----------------------------------------------------------------------===// +unsigned dwarfgen::DIE::computeSizeAndOffsets(unsigned Offset) { + auto &DG = CU->getGenerator(); + return Die->computeOffsetsAndAbbrevs(DG.getAsmPrinter(), DG.getAbbrevSet(), + Offset); +} + +void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, uint64_t U) { + auto &DG = CU->getGenerator(); + Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form, + DIEInteger(U)); +} + +void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, + StringRef String) { + auto &DG = CU->getGenerator(); + if (Form == DW_FORM_string) { + Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form, + new (DG.getAllocator()) + DIEInlineString(String, DG.getAllocator())); + } else { + Die->addValue( + DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form, + DIEString(DG.getStringPool().getEntry(*DG.getAsmPrinter(), String))); + } +} + +void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, + dwarfgen::DIE &RefDie) { + auto &DG = CU->getGenerator(); + Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form, + DIEEntry(*RefDie.Die)); +} + +void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, const void *P, + size_t S) { + auto &DG = CU->getGenerator(); + DIEBlock *Block = new (DG.getAllocator()) DIEBlock; + for (size_t I = 0; I < S; ++I) + Block->addValue( + DG.getAllocator(), (dwarf::Attribute)0, dwarf::DW_FORM_data1, + DIEInteger( + (const_cast<uint8_t *>(static_cast<const uint8_t *>(P)))[I])); + + Block->ComputeSize(DG.getAsmPrinter()); + Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form, + Block); +} + +void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form) { + auto &DG = CU->getGenerator(); + assert(Form == DW_FORM_flag_present); + Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form, + DIEInteger(1)); +} + +dwarfgen::DIE dwarfgen::DIE::addChild(dwarf::Tag Tag) { + auto &DG = CU->getGenerator(); + return dwarfgen::DIE(CU, + &Die->addChild(llvm::DIE::get(DG.getAllocator(), Tag))); +} + +dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() { + return dwarfgen::DIE(this, &DU.getUnitDie()); +} + +//===----------------------------------------------------------------------===// +/// dwarfgen::Generator implementation. +//===----------------------------------------------------------------------===// + +dwarfgen::Generator::Generator() + : MAB(nullptr), MCE(nullptr), MS(nullptr), StringPool(nullptr), + Abbreviations(Allocator) {} +dwarfgen::Generator::~Generator() = default; + +llvm::Expected<std::unique_ptr<dwarfgen::Generator>> +dwarfgen::Generator::create(Triple TheTriple, uint16_t DwarfVersion) { + std::unique_ptr<dwarfgen::Generator> GenUP(new dwarfgen::Generator()); + llvm::Error error = GenUP->init(TheTriple, DwarfVersion); + if (error) + return Expected<std::unique_ptr<dwarfgen::Generator>>(std::move(error)); + return Expected<std::unique_ptr<dwarfgen::Generator>>(std::move(GenUP)); +} + +llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) { + Version = V; + std::string ErrorStr; + std::string TripleName; + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); + if (!TheTarget) + return make_error<StringError>(ErrorStr, inconvertibleErrorCode()); + + TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + return make_error<StringError>(Twine("no register info for target ") + + TripleName, + inconvertibleErrorCode()); + + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!MAI) + return make_error<StringError>("no asm info for target " + TripleName, + inconvertibleErrorCode()); + + MOFI.reset(new MCObjectFileInfo); + MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); + MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, CodeModel::Default, *MC); + + MCTargetOptions Options; + MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "", Options); + if (!MAB) + return make_error<StringError>("no asm backend for target " + TripleName, + inconvertibleErrorCode()); + + MII.reset(TheTarget->createMCInstrInfo()); + if (!MII) + return make_error<StringError>("no instr info info for target " + + TripleName, + inconvertibleErrorCode()); + + MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return make_error<StringError>("no subtarget info for target " + TripleName, + inconvertibleErrorCode()); + + MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); + if (!MCE) + return make_error<StringError>("no code emitter for target " + TripleName, + inconvertibleErrorCode()); + + Stream = make_unique<raw_svector_ostream>(FileBytes); + + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + MS = TheTarget->createMCObjectStreamer( + TheTriple, *MC, *MAB, *Stream, MCE, *MSTI, MCOptions.MCRelaxAll, + MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false); + if (!MS) + return make_error<StringError>("no object streamer for target " + + TripleName, + inconvertibleErrorCode()); + + // Finally create the AsmPrinter we'll use to emit the DIEs. + TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(), + None)); + if (!TM) + return make_error<StringError>("no target machine for target " + TripleName, + inconvertibleErrorCode()); + + Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS))); + if (!Asm) + return make_error<StringError>("no asm printer for target " + TripleName, + inconvertibleErrorCode()); + + // Set the DWARF version correctly on all classes that we use. + MC->setDwarfVersion(Version); + Asm->setDwarfVersion(Version); + + StringPool = llvm::make_unique<DwarfStringPool>(Allocator, *Asm, StringRef()); + + return Error::success(); +} + +StringRef dwarfgen::Generator::generate() { + // Offset from the first CU in the debug info section is 0 initially. + unsigned SecOffset = 0; + + // Iterate over each compile unit and set the size and offsets for each + // DIE within each compile unit. All offsets are CU relative. + for (auto &CU : CompileUnits) { + // Set the absolute .debug_info offset for this compile unit. + CU->setOffset(SecOffset); + // The DIEs contain compile unit relative offsets. + unsigned CUOffset = 11; + CUOffset = CU->getUnitDIE().computeSizeAndOffsets(CUOffset); + // Update our absolute .debug_info offset. + SecOffset += CUOffset; + CU->setLength(CUOffset - 4); + } + Abbreviations.Emit(Asm.get(), MOFI->getDwarfAbbrevSection()); + StringPool->emit(*Asm, MOFI->getDwarfStrSection()); + MS->SwitchSection(MOFI->getDwarfInfoSection()); + for (auto &CU : CompileUnits) { + uint16_t Version = CU->getVersion(); + auto Length = CU->getLength(); + MC->setDwarfVersion(Version); + assert(Length != -1U); + Asm->EmitInt32(Length); + Asm->EmitInt16(Version); + Asm->EmitInt32(0); + Asm->EmitInt8(CU->getAddressSize()); + Asm->emitDwarfDIE(*CU->getUnitDIE().Die); + } + + MS->Finish(); + if (FileBytes.empty()) + return StringRef(); + return StringRef(FileBytes.data(), FileBytes.size()); +} + +bool dwarfgen::Generator::saveFile(StringRef Path) { + if (FileBytes.empty()) + return false; + std::error_code EC; + raw_fd_ostream Strm(Path, EC, sys::fs::F_None); + if (EC) + return false; + Strm.write(FileBytes.data(), FileBytes.size()); + Strm.close(); + return true; +} + +dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() { + CompileUnits.push_back(std::unique_ptr<CompileUnit>( + new CompileUnit(*this, Version, Asm->getPointerSize()))); + return *CompileUnits.back(); +} diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.h b/unittests/DebugInfo/DWARF/DwarfGenerator.h new file mode 100644 index 000000000000..966725b4fa4e --- /dev/null +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.h @@ -0,0 +1,231 @@ +//===--- unittests/DebugInfo/DWARF/DwarfGenerator.h -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A file that can generate DWARF debug info for unit tests. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H +#define LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/Support/Error.h" + +#include <memory> +#include <string> +#include <tuple> +#include <vector> + +namespace llvm { + +class AsmPrinter; +class DIE; +class DIEAbbrev; +class DwarfStringPool; +class MCAsmBackend; +class MCAsmInfo; +class MCCodeEmitter; +class MCContext; +struct MCDwarfLineTableParams; +class MCInstrInfo; +class MCObjectFileInfo; +class MCRegisterInfo; +class MCStreamer; +class MCSubtargetInfo; +class raw_fd_ostream; +class TargetMachine; +class Triple; + +namespace dwarfgen { + +class Generator; +class CompileUnit; + +/// A DWARF debug information entry class used to generate DWARF DIEs. +/// +/// This class is used to quickly generate DWARF debug information by creating +/// child DIEs or adding attributes to the current DIE. Instances of this class +/// are created from the compile unit (dwarfgen::CompileUnit::getUnitDIE()) or +/// by calling dwarfgen::DIE::addChild(...) and using the returned DIE object. +class DIE { + dwarfgen::CompileUnit *CU; + llvm::DIE *Die; + +protected: + friend class Generator; + friend class CompileUnit; + + DIE(CompileUnit *U = nullptr, llvm::DIE *D = nullptr) : CU(U), Die(D) {} + + /// Called with a compile/type unit relative offset prior to generating the + /// DWARF debug info. + /// + /// \param CUOffset the compile/type unit relative offset where the + /// abbreviation code for this DIE will be encoded. + unsigned computeSizeAndOffsets(unsigned CUOffset); + +public: + /// Add an attribute value that has no value. + /// + /// \param Attr a dwarf::Attribute enumeration value or any uint16_t that + /// represents a user defined DWARF attribute. + /// \param Form the dwarf::Form to use when encoding the attribute. This is + /// only used with the DW_FORM_flag_present form encoding. + void addAttribute(uint16_t Attr, dwarf::Form Form); + + /// Add an attribute value to be encoded as a DIEInteger + /// + /// \param Attr a dwarf::Attribute enumeration value or any uint16_t that + /// represents a user defined DWARF attribute. + /// \param Form the dwarf::Form to use when encoding the attribute. + /// \param U the unsigned integer to encode. + void addAttribute(uint16_t Attr, dwarf::Form Form, uint64_t U); + + /// Add an attribute value to be encoded as a DIEString or DIEInlinedString. + /// + /// \param Attr a dwarf::Attribute enumeration value or any uint16_t that + /// represents a user defined DWARF attribute. + /// \param Form the dwarf::Form to use when encoding the attribute. The form + /// must be one of DW_FORM_strp or DW_FORM_string. + /// \param String the string to encode. + void addAttribute(uint16_t Attr, dwarf::Form Form, StringRef String); + + /// Add an attribute value to be encoded as a DIEEntry. + /// + /// DIEEntry attributes refer to other llvm::DIE objects that have been + /// created. + /// + /// \param Attr a dwarf::Attribute enumeration value or any uint16_t that + /// represents a user defined DWARF attribute. + /// \param Form the dwarf::Form to use when encoding the attribute. The form + /// must be one of DW_FORM_strp or DW_FORM_string. + /// \param RefDie the DIE that this attriute refers to. + void addAttribute(uint16_t Attr, dwarf::Form Form, dwarfgen::DIE &RefDie); + + /// Add an attribute value to be encoded as a DIEBlock. + /// + /// DIEBlock attributes refers to binary data that is stored as the + /// attribute's value. + /// + /// \param Attr a dwarf::Attribute enumeration value or any uint16_t that + /// represents a user defined DWARF attribute. + /// \param Form the dwarf::Form to use when encoding the attribute. The form + /// must be one of DW_FORM_strp or DW_FORM_string. + /// \param P a pointer to the data to store as the attribute value. + /// \param S the size in bytes of the data pointed to by P . + void addAttribute(uint16_t Attr, dwarf::Form Form, const void *P, size_t S); + + /// Add a new child to this DIE object. + /// + /// \param Tag the dwarf::Tag to assing to the llvm::DIE object. + /// \returns the newly created DIE object that is now a child owned by this + /// object. + dwarfgen::DIE addChild(dwarf::Tag Tag); +}; + +/// A DWARF compile unit used to generate DWARF compile/type units. +/// +/// Instances of these classes are created by instances of the Generator +/// class. All information required to generate a DWARF compile unit is +/// contained inside this class. +class CompileUnit { + Generator &DG; + DIEUnit DU; + +public: + CompileUnit(Generator &D, uint16_t V, uint8_t A) + : DG(D), DU(V, A, dwarf::DW_TAG_compile_unit) {} + DIE getUnitDIE(); + Generator &getGenerator() { return DG; } + uint64_t getOffset() const { return DU.getDebugSectionOffset(); } + uint64_t getLength() const { return DU.getLength(); } + uint16_t getVersion() const { return DU.getDwarfVersion(); } + uint16_t getAddressSize() const { return DU.getAddressSize(); } + void setOffset(uint64_t Offset) { DU.setDebugSectionOffset(Offset); } + void setLength(uint64_t Length) { DU.setLength(Length); } +}; + +/// A DWARF generator. +/// +/// Generate DWARF for unit tests by creating any instance of this class and +/// calling Generator::addCompileUnit(), and then getting the dwarfgen::DIE from +/// the returned compile unit and adding attributes and children to each DIE. +class Generator { + std::unique_ptr<MCRegisterInfo> MRI; + std::unique_ptr<MCAsmInfo> MAI; + std::unique_ptr<MCObjectFileInfo> MOFI; + std::unique_ptr<MCContext> MC; + MCAsmBackend *MAB; // Owned by MCStreamer + std::unique_ptr<MCInstrInfo> MII; + std::unique_ptr<MCSubtargetInfo> MSTI; + MCCodeEmitter *MCE; // Owned by MCStreamer + MCStreamer *MS; // Owned by AsmPrinter + std::unique_ptr<TargetMachine> TM; + std::unique_ptr<AsmPrinter> Asm; + BumpPtrAllocator Allocator; + std::unique_ptr<DwarfStringPool> StringPool; // Entries owned by Allocator. + std::vector<std::unique_ptr<CompileUnit>> CompileUnits; + DIEAbbrevSet Abbreviations; + + SmallString<4096> FileBytes; + /// The stream we use to generate the DWARF into as an ELF file. + std::unique_ptr<raw_svector_ostream> Stream; + /// The DWARF version to generate. + uint16_t Version; + + /// Private constructor, call Generator::Create(...) to get a DWARF generator + /// expected. + Generator(); + + /// Create the streamer and setup the output buffer. + llvm::Error init(Triple TheTriple, uint16_t DwarfVersion); + +public: + /// Create a DWARF generator or get an appropriate error. + /// + /// \param TheTriple the triple to use when creating any required support + /// classes needed to emit the DWARF. + /// \param DwarfVersion the version of DWARF to emit. + /// + /// \returns a llvm::Expected that either contains a unique_ptr to a Generator + /// or a llvm::Error. + static llvm::Expected<std::unique_ptr<Generator>> + create(Triple TheTriple, uint16_t DwarfVersion); + + ~Generator(); + + /// Generate all DWARF sections and return a memory buffer that + /// contains an ELF file that contains the DWARF. + StringRef generate(); + + /// Add a compile unit to be generated. + /// + /// \returns a dwarfgen::CompileUnit that can be used to retrieve the compile + /// unit dwarfgen::DIE that can be used to add attributes and add child DIE + /// objedts to. + dwarfgen::CompileUnit &addCompileUnit(); + + BumpPtrAllocator &getAllocator() { return Allocator; } + AsmPrinter *getAsmPrinter() const { return Asm.get(); } + DIEAbbrevSet &getAbbrevSet() { return Abbreviations; } + DwarfStringPool &getStringPool() { return *StringPool; } + + /// Save the generated DWARF file to disk. + /// + /// \param Path the path to save the ELF file to. + bool saveFile(StringRef Path); +}; + +} // end namespace dwarfgen + +} // end namespace llvm + +#endif // LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H diff --git a/unittests/DebugInfo/PDB/CMakeLists.txt b/unittests/DebugInfo/PDB/CMakeLists.txt index 405b7bb9b1a6..406b487e7a18 100644 --- a/unittests/DebugInfo/PDB/CMakeLists.txt +++ b/unittests/DebugInfo/PDB/CMakeLists.txt @@ -1,11 +1,12 @@ set(LLVM_LINK_COMPONENTS DebugInfoCodeView + DebugInfoMSF DebugInfoPDB ) set(DebugInfoPDBSources MappedBlockStreamTest.cpp - MsfBuilderTest.cpp + MSFBuilderTest.cpp PDBApiTest.cpp ) diff --git a/unittests/DebugInfo/PDB/ErrorChecking.h b/unittests/DebugInfo/PDB/ErrorChecking.h index da734a9b1b5b..6d4a7de7834a 100644 --- a/unittests/DebugInfo/PDB/ErrorChecking.h +++ b/unittests/DebugInfo/PDB/ErrorChecking.h @@ -36,6 +36,14 @@ } \ } -#define EXPECT_UNEXPECTED(Exp) EXPECT_ERROR(Err) +#define EXPECT_UNEXPECTED(Exp) \ + { \ + auto E = Exp.takeError(); \ + EXPECT_TRUE(static_cast<bool>(E)); \ + if (E) { \ + consumeError(std::move(E)); \ + return; \ + } \ + } #endif diff --git a/unittests/DebugInfo/PDB/MsfBuilderTest.cpp b/unittests/DebugInfo/PDB/MSFBuilderTest.cpp index ac292a73a8fb..5f2f0c271e97 100644 --- a/unittests/DebugInfo/PDB/MsfBuilderTest.cpp +++ b/unittests/DebugInfo/PDB/MSFBuilderTest.cpp @@ -1,4 +1,4 @@ -//===- MsfBuilderTest.cpp Tests manipulation of MSF stream metadata ------===// +//===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===// // // The LLVM Compiler Infrastructure // @@ -9,17 +9,16 @@ #include "ErrorChecking.h" -#include "llvm/DebugInfo/PDB/Raw/MsfBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/MsfCommon.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" #include "gtest/gtest.h" using namespace llvm; -using namespace llvm::pdb; -using namespace llvm::pdb::msf; +using namespace llvm::msf; namespace { -class MsfBuilderTest : public testing::Test { +class MSFBuilderTest : public testing::Test { protected: void initializeSimpleSuperBlock(msf::SuperBlock &SB) { initializeSuperBlock(SB); @@ -31,6 +30,7 @@ protected: ::memset(&SB, 0, sizeof(SB)); ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic)); + SB.FreeBlockMapBlock = 1; SB.BlockMapAddr = 1; SB.BlockSize = 4096; SB.NumDirectoryBytes = 0; @@ -41,7 +41,7 @@ protected: }; } -TEST_F(MsfBuilderTest, ValidateSuperBlockAccept) { +TEST_F(MSFBuilderTest, ValidateSuperBlockAccept) { // Test that a known good super block passes validation. SuperBlock SB; initializeSuperBlock(SB); @@ -49,7 +49,7 @@ TEST_F(MsfBuilderTest, ValidateSuperBlockAccept) { EXPECT_NO_ERROR(msf::validateSuperBlock(SB)); } -TEST_F(MsfBuilderTest, ValidateSuperBlockReject) { +TEST_F(MSFBuilderTest, ValidateSuperBlockReject) { // Test that various known problems cause a super block to be rejected. SuperBlock SB; initializeSimpleSuperBlock(SB); @@ -76,7 +76,7 @@ TEST_F(MsfBuilderTest, ValidateSuperBlockReject) { EXPECT_ERROR(msf::validateSuperBlock(SB)); } -TEST_F(MsfBuilderTest, TestUsedBlocksMarkedAsUsed) { +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. @@ -85,11 +85,11 @@ TEST_F(MsfBuilderTest, TestUsedBlocksMarkedAsUsed) { // 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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096, NumBlocks); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; - EXPECT_NO_ERROR(Msf.addStream(Blocks.size() * 4096, Blocks)); + EXPECT_EXPECTED(Msf.addStream(Blocks.size() * 4096, Blocks)); for (auto B : Blocks) { EXPECT_FALSE(Msf.isBlockFree(B)); @@ -101,26 +101,26 @@ TEST_F(MsfBuilderTest, TestUsedBlocksMarkedAsUsed) { } } -TEST_F(MsfBuilderTest, TestAddStreamNoDirectoryBlockIncrease) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; auto ExpectedL1 = Msf.build(); EXPECT_EXPECTED(ExpectedL1); - Layout &L1 = *ExpectedL1; + MSFLayout &L1 = *ExpectedL1; auto OldDirBlocks = L1.DirectoryBlocks; EXPECT_EQ(1U, OldDirBlocks.size()); - auto ExpectedMsf2 = MsfBuilder::create(Allocator, 4096); + auto ExpectedMsf2 = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf2); auto &Msf2 = *ExpectedMsf2; - EXPECT_NO_ERROR(Msf2.addStream(4000)); + EXPECT_EXPECTED(Msf2.addStream(4000)); EXPECT_EQ(1U, Msf2.getNumStreams()); EXPECT_EQ(4000U, Msf2.getStreamSize(0)); auto Blocks = Msf2.getStreamBlocks(0); @@ -128,38 +128,38 @@ TEST_F(MsfBuilderTest, TestAddStreamNoDirectoryBlockIncrease) { auto ExpectedL2 = Msf2.build(); EXPECT_EXPECTED(ExpectedL2); - Layout &L2 = *ExpectedL2; + MSFLayout &L2 = *ExpectedL2; auto NewDirBlocks = L2.DirectoryBlocks; EXPECT_EQ(1U, NewDirBlocks.size()); } -TEST_F(MsfBuilderTest, TestAddStreamWithDirectoryBlockIncrease) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; - EXPECT_NO_ERROR(Msf.addStream(4096 * 4096 / sizeof(uint32_t))); + EXPECT_EXPECTED(Msf.addStream(4096 * 4096 / sizeof(uint32_t))); auto ExpectedL1 = Msf.build(); EXPECT_EXPECTED(ExpectedL1); - Layout &L1 = *ExpectedL1; + MSFLayout &L1 = *ExpectedL1; auto DirBlocks = L1.DirectoryBlocks; EXPECT_EQ(2U, DirBlocks.size()); } -TEST_F(MsfBuilderTest, TestGrowStreamNoBlockIncrease) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; - EXPECT_NO_ERROR(Msf.addStream(1024)); + EXPECT_EXPECTED(Msf.addStream(1024)); EXPECT_EQ(1024U, Msf.getStreamSize(0)); auto OldStreamBlocks = Msf.getStreamBlocks(0); EXPECT_EQ(1U, OldStreamBlocks.size()); @@ -172,16 +172,16 @@ TEST_F(MsfBuilderTest, TestGrowStreamNoBlockIncrease) { EXPECT_EQ(OldStreamBlocks, NewStreamBlocks); } -TEST_F(MsfBuilderTest, TestGrowStreamWithBlockIncrease) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; - EXPECT_NO_ERROR(Msf.addStream(2048)); + EXPECT_EXPECTED(Msf.addStream(2048)); EXPECT_EQ(2048U, Msf.getStreamSize(0)); std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0); EXPECT_EQ(1U, OldStreamBlocks.size()); @@ -195,14 +195,14 @@ TEST_F(MsfBuilderTest, TestGrowStreamWithBlockIncrease) { EXPECT_NE(NewStreamBlocks[0], NewStreamBlocks[1]); } -TEST_F(MsfBuilderTest, TestShrinkStreamNoBlockDecrease) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; - EXPECT_NO_ERROR(Msf.addStream(2048)); + EXPECT_EXPECTED(Msf.addStream(2048)); EXPECT_EQ(2048U, Msf.getStreamSize(0)); std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0); EXPECT_EQ(1U, OldStreamBlocks.size()); @@ -215,15 +215,15 @@ TEST_F(MsfBuilderTest, TestShrinkStreamNoBlockDecrease) { EXPECT_EQ(OldStreamBlocks, NewStreamBlocks); } -TEST_F(MsfBuilderTest, TestShrinkStreamWithBlockDecrease) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; - EXPECT_NO_ERROR(Msf.addStream(6144)); + EXPECT_EXPECTED(Msf.addStream(6144)); EXPECT_EQ(6144U, Msf.getStreamSize(0)); std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0); EXPECT_EQ(2U, OldStreamBlocks.size()); @@ -236,23 +236,23 @@ TEST_F(MsfBuilderTest, TestShrinkStreamWithBlockDecrease) { EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]); } -TEST_F(MsfBuilderTest, TestRejectReusedStreamBlock) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; - EXPECT_NO_ERROR(Msf.addStream(6144)); + EXPECT_EXPECTED(Msf.addStream(6144)); std::vector<uint32_t> Blocks = {2, 3}; - EXPECT_ERROR(Msf.addStream(6144, Blocks)); + EXPECT_UNEXPECTED(Msf.addStream(6144, Blocks)); } -TEST_F(MsfBuilderTest, TestBlockCountsWhenAddingStreams) { +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); + auto ExpectedMsf = MSFBuilder::create(Allocator, 4096); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; @@ -263,31 +263,31 @@ TEST_F(MsfBuilderTest, TestBlockCountsWhenAddingStreams) { const uint32_t StreamSizes[] = {4000, 6193, 189723}; for (int I = 0; I < 3; ++I) { - EXPECT_NO_ERROR(Msf.addStream(StreamSizes[I])); + EXPECT_EXPECTED(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 +TEST_F(MSFBuilderTest, BuildMsfLayout) { + // Test that we can generate an MSFLayout structure from a valid layout // specification. - auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); + 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])); + EXPECT_EXPECTED(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; + MSFLayout &L = *ExpectedLayout; EXPECT_EQ(4096U, L.SB->BlockSize); EXPECT_EQ(ExpectedNumBlocks, L.SB->NumBlocks); @@ -302,19 +302,19 @@ TEST_F(MsfBuilderTest, BuildMsfLayout) { } } -TEST_F(MsfBuilderTest, UseDirectoryBlockHint) { - Expected<MsfBuilder> ExpectedMsf = MsfBuilder::create( +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})); + EXPECT_EXPECTED(Msf.addStream(2048, {B + 2})); auto ExpectedLayout = Msf.build(); EXPECT_EXPECTED(ExpectedLayout); - Layout &L = *ExpectedLayout; + MSFLayout &L = *ExpectedLayout; EXPECT_EQ(msf::getMinimumBlockCount() + 2, L.SB->NumBlocks); EXPECT_EQ(1U, L.DirectoryBlocks.size()); EXPECT_EQ(1U, L.StreamMap[0].size()); @@ -323,38 +323,38 @@ TEST_F(MsfBuilderTest, UseDirectoryBlockHint) { EXPECT_EQ(B + 2, L.StreamMap[0].front()); } -TEST_F(MsfBuilderTest, DirectoryBlockHintInsufficient) { - Expected<MsfBuilder> ExpectedMsf = - MsfBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2); +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)); + EXPECT_EXPECTED(Msf.addStream(Size)); auto ExpectedLayout = Msf.build(); EXPECT_EXPECTED(ExpectedLayout); - Layout &L = *ExpectedLayout; + MSFLayout &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); +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)); + EXPECT_EXPECTED(Msf.addStream(2048)); auto ExpectedLayout = Msf.build(); EXPECT_EXPECTED(ExpectedLayout); - Layout &L = *ExpectedLayout; + MSFLayout &L = *ExpectedLayout; EXPECT_EQ(1U, L.DirectoryBlocks.size()); EXPECT_EQ(B + 1, L.DirectoryBlocks[0]); } diff --git a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp index 6f9e86c4f262..07591ca69d30 100644 --- a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp +++ b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp @@ -9,78 +9,77 @@ #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 "llvm/DebugInfo/MSF/ByteStream.h" +#include "llvm/DebugInfo/MSF/IMSFFile.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/MSFStreamLayout.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" #include "gtest/gtest.h" #include <unordered_map> using namespace llvm; -using namespace llvm::codeview; -using namespace llvm::pdb; +using namespace llvm::msf; 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 { +class DiscontiguousStream : public WritableStream { public: - DiscontiguousFile(ArrayRef<uint32_t> Blocks, MutableArrayRef<uint8_t> Data) + DiscontiguousStream(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; + uint32_t block_size() const { return 1; } + uint32_t block_count() const { return Blocks.size(); } + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const override { + if (Offset + Size > Data.size()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + Buffer = Data.slice(Offset, Size); + return Error::success(); } - Expected<ArrayRef<uint8_t>> getBlockData(uint32_t BlockIndex, - uint32_t NumBytes) const override { - return ArrayRef<uint8_t>(&Data[BlockIndex], NumBytes); + + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const override { + if (Offset >= Data.size()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + Buffer = Data.drop_front(Offset); + return Error::success(); } - 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()); + uint32_t getLength() const override { return Data.size(); } + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> SrcData) const override { + if (Offset + SrcData.size() > Data.size()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + ::memcpy(&Data[Offset], SrcData.data(), SrcData.size()); return Error::success(); } + Error commit() const override { return Error::success(); } + + MSFStreamLayout layout() const { + return MSFStreamLayout{static_cast<uint32_t>(Data.size()), Blocks}; + } 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; + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + + StreamReader R(*S); + ReadableStreamRef SR; EXPECT_NO_ERROR(R.readStreamRef(SR, 0U)); ArrayRef<uint8_t> Buffer; EXPECT_ERROR(SR.readBytes(0U, 1U, Buffer)); @@ -91,133 +90,143 @@ TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) { // 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + + StreamReader R(*S); StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA"; EXPECT_NO_ERROR(R.readFixedString(Str, 1)); EXPECT_EQ(Str, StringRef("A")); - EXPECT_EQ(0U, S.getNumBytesCopied()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + StreamReader R(*S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 2)); EXPECT_EQ(Str, StringRef("AB")); - EXPECT_EQ(0U, S.getNumBytesCopied()); + 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()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + StreamReader R(*S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 10)); EXPECT_EQ(Str, StringRef("ABCDEFGHIJ")); - EXPECT_EQ(10U, S.getNumBytesCopied()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + StreamReader R(*S); StringRef Str; R.setOffset(10); EXPECT_ERROR(R.readFixedString(Str, 1)); - EXPECT_EQ(0U, S.getNumBytesCopied()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + StreamReader R(*S); StringRef Str; R.setOffset(6); EXPECT_ERROR(R.readFixedString(Str, 5)); - EXPECT_EQ(0U, S.getNumBytesCopied()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + StreamReader R(*S); StringRef Str; EXPECT_ERROR(R.readFixedString(Str, 11)); - EXPECT_EQ(0U, S.getNumBytesCopied()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), F); + StreamReader R(*S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 1)); EXPECT_EQ(Str, StringRef("A")); - EXPECT_EQ(0U, S.getNumBytesCopied()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), 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()); + 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()); + 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); + DiscontiguousStream F(BlocksAry, DataAry); + auto S = MappedBlockStream::createStream(F.block_size(), F.block_count(), + F.layout(), 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()); + 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()); + EXPECT_EQ(10U, S->getNumBytesCopied()); } TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) { @@ -228,41 +237,43 @@ TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) { static_assert(sizeof(LargeBuffer) > sizeof(Data), "LargeBuffer is not big enough"); - DiscontiguousFile F(BlocksAry, Data); - MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F); + DiscontiguousStream F(BlocksAry, Data); + auto S = WritableMappedBlockStream::createStream( + F.block_size(), F.block_count(), F.layout(), 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))); + 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); + DiscontiguousStream F(BlocksAry, Data); + auto S = WritableMappedBlockStream::createStream( + F.block_size(), F.block_count(), F.layout(), F); ArrayRef<uint8_t> Buffer; - EXPECT_NO_ERROR(S.readBytes(0, 1, 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_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->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_NO_ERROR(S->readBytes(0, 1, Buffer)); EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J')); - EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer)); + 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->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_NO_ERROR(S->readBytes(0, 1, Buffer)); EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A')); - EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer)); + EXPECT_NO_ERROR(S->readBytes(9, 1, Buffer)); EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J')); } @@ -272,16 +283,17 @@ TEST(MappedBlockStreamTest, TestWriteBytesBreakBoundary) { 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); + DiscontiguousStream F(BlocksAry, Data); + auto S = WritableMappedBlockStream::createStream( + F.block_size(), F.block_count(), F.layout(), F); ArrayRef<uint8_t> Buffer; - EXPECT_NO_ERROR(S.writeBytes(0, TestData)); + 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_NO_ERROR(S->readBytes(0, 8, Buffer)); EXPECT_EQ(Buffer, ArrayRef<uint8_t>(TestData)); } @@ -290,8 +302,9 @@ TEST(MappedBlockStreamTest, TestWriteThenRead) { 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); + DiscontiguousStream F(Blocks, Data); + auto S = WritableMappedBlockStream::createStream( + F.block_size(), F.block_count(), F.layout(), F); enum class MyEnum : uint32_t { Val1 = 2908234, Val2 = 120891234 }; using support::ulittle32_t; @@ -306,10 +319,12 @@ TEST(MappedBlockStreamTest, TestWriteThenRead) { ArrayRef<uint8_t> byteArrayRef0(byteArray0); ArrayRef<uint8_t> byteArrayRef1(byteArray1); ArrayRef<uint8_t> byteArray[] = { byteArrayRef0, byteArrayRef1 }; - ArrayRef<uint32_t> intArray[] = {{890723408, 29082234}, {0, 0}}; + uint32_t intArr0[] = {890723408, 29082234}; + uint32_t intArr1[] = {890723408, 29082234}; + ArrayRef<uint32_t> intArray[] = {intArr0, intArr1}; - StreamReader Reader(S); - StreamWriter Writer(S); + 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]); @@ -379,26 +394,26 @@ TEST(MappedBlockStreamTest, TestWriteContiguousStreamRef) { std::vector<uint8_t> SrcDataBytes(10); MutableArrayRef<uint8_t> SrcData(SrcDataBytes); - DiscontiguousFile F(DestBlocks, DestData); - MappedBlockStreamImpl DestStream(llvm::make_unique<IndexedStreamData>(0, F), - F); + DiscontiguousStream F(DestBlocks, DestData); + auto DestStream = WritableMappedBlockStream::createStream( + F.block_size(), F.block_count(), F.layout(), F); // First write "Test Str" into the source stream. - ByteStream<true> SourceStream(SrcData); + MutableByteStream 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); + 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); + StreamReader DestReader(*DestStream); EXPECT_NO_ERROR(DestReader.readZeroString(Result)); EXPECT_EQ(Result, "Test Str"); } @@ -412,29 +427,29 @@ TEST(MappedBlockStreamTest, TestWriteDiscontiguousStreamRef) { 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); + DiscontiguousStream DestF(DestBlocks, DestData); + DiscontiguousStream SrcF(SrcBlocks, SrcData); - MappedBlockStreamImpl DestStream( - llvm::make_unique<IndexedStreamData>(0, DestFile), DestFile); - MappedBlockStreamImpl SrcStream( - llvm::make_unique<IndexedStreamData>(0, SrcFile), SrcFile); + auto Dest = WritableMappedBlockStream::createStream( + DestF.block_size(), DestF.block_count(), DestF.layout(), DestF); + auto Src = WritableMappedBlockStream::createStream( + SrcF.block_size(), SrcF.block_count(), SrcF.layout(), SrcF); // First write "Test Str" into the source stream. - StreamWriter SourceWriter(SrcStream); + StreamWriter SourceWriter(*Src); 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)); + StreamWriter DestWriter(*Dest); + EXPECT_NO_ERROR(DestWriter.writeStreamRef(*Src)); EXPECT_EQ(DestDataBytes, std::vector<uint8_t>( {'s', 'e', 'T', ' ', 'S', 't', 't', 'r', 0, 0})); // Then read the string back out of the dest stream. StringRef Result; - StreamReader DestReader(DestStream); + StreamReader DestReader(*Dest); EXPECT_NO_ERROR(DestReader.readZeroString(Result)); EXPECT_EQ(Result, "Test Str"); } diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h index 21def6e9eb1b..a3d3d1fffdbe 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h +++ b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h @@ -15,6 +15,7 @@ #ifndef LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTAPICOMMON_H #define LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTAPICOMMON_H +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/IR/LegacyPassManager.h" @@ -56,19 +57,17 @@ protected: bool ArchSupportsMCJIT() { Triple Host(HostTriple); // If ARCH is not supported, bail - if (std::find(SupportedArchs.begin(), SupportedArchs.end(), Host.getArch()) - == SupportedArchs.end()) + if (!is_contained(SupportedArchs, Host.getArch())) return false; // If ARCH is supported and has no specific sub-arch support - if (std::find(HasSubArchs.begin(), HasSubArchs.end(), Host.getArch()) - == HasSubArchs.end()) + if (!is_contained(HasSubArchs, Host.getArch())) return true; // If ARCH has sub-arch support, find it SmallVectorImpl<std::string>::const_iterator I = SupportedSubArchs.begin(); for(; I != SupportedSubArchs.end(); ++I) - if (Host.getArchName().startswith(I->c_str())) + if (Host.getArchName().startswith(*I)) return true; return false; @@ -78,12 +77,11 @@ protected: bool OSSupportsMCJIT() { Triple Host(HostTriple); - if (std::find(UnsupportedEnvironments.begin(), UnsupportedEnvironments.end(), - Host.getEnvironment()) != UnsupportedEnvironments.end()) + if (find(UnsupportedEnvironments, Host.getEnvironment()) != + UnsupportedEnvironments.end()) return false; - if (std::find(UnsupportedOSs.begin(), UnsupportedOSs.end(), Host.getOS()) - == UnsupportedOSs.end()) + if (!is_contained(UnsupportedOSs, Host.getOS())) return true; return false; diff --git a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp index 8140a1ff2493..ab04c14b0957 100644 --- a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp @@ -26,7 +26,7 @@ public: class DummyStubsManager : public orc::IndirectStubsManager { public: - Error createStub(StringRef StubName, TargetAddress InitAddr, + Error createStub(StringRef StubName, JITTargetAddress InitAddr, JITSymbolFlags Flags) override { llvm_unreachable("Not implemented"); } @@ -43,7 +43,7 @@ public: llvm_unreachable("Not implemented"); } - Error updatePointer(StringRef Name, TargetAddress NewAddr) override { + Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override { llvm_unreachable("Not implemented"); } }; diff --git a/unittests/ExecutionEngine/Orc/GlobalMappingLayerTest.cpp b/unittests/ExecutionEngine/Orc/GlobalMappingLayerTest.cpp index 054fc16cabd4..25b6c891c622 100644 --- a/unittests/ExecutionEngine/Orc/GlobalMappingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/GlobalMappingLayerTest.cpp @@ -21,7 +21,7 @@ struct MockBaseLayer { JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { if (Name == "bar") - return llvm::orc::JITSymbol(0x4567, JITSymbolFlags::Exported); + return llvm::JITSymbol(0x4567, JITSymbolFlags::Exported); return nullptr; } @@ -37,13 +37,13 @@ TEST(GlobalMappingLayerTest, Empty) { // Test fall-through for symbol in base layer. auto BarSym = L.findSymbol("bar", true); - EXPECT_EQ(BarSym.getAddress(), static_cast<TargetAddress>(0x4567)) + EXPECT_EQ(BarSym.getAddress(), static_cast<JITTargetAddress>(0x4567)) << "Symbol lookup fall-through failed."; // Test setup of a global mapping. L.setGlobalMapping("foo", 0x0123); auto FooSym2 = L.findSymbol("foo", true); - EXPECT_EQ(FooSym2.getAddress(), static_cast<TargetAddress>(0x0123)) + EXPECT_EQ(FooSym2.getAddress(), static_cast<JITTargetAddress>(0x0123)) << "Symbol mapping setup failed."; // Test removal of a global mapping. diff --git a/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp b/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp index a495766db91a..3362f490a38a 100644 --- a/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ExecutionEngine/RuntimeDyld.h" #include "llvm/ExecutionEngine/Orc/LazyEmittingLayer.h" #include "gtest/gtest.h" @@ -17,7 +18,7 @@ struct MockBaseLayer { ModuleSetHandleT addModuleSet( std::list<std::unique_ptr<llvm::Module>>, std::unique_ptr<llvm::RuntimeDyld::MemoryManager> MemMgr, - std::unique_ptr<llvm::RuntimeDyld::SymbolResolver> Resolver) { + std::unique_ptr<llvm::JITSymbolResolver> Resolver) { EXPECT_FALSE(MemMgr); return 42; } diff --git a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp index 87928347d88e..44b44f604159 100644 --- a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp @@ -90,10 +90,10 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { auto Resolver = createLambdaResolver( [](const std::string &Name) { - return RuntimeDyld::SymbolInfo(nullptr); + return JITSymbol(nullptr); }, [](const std::string &Name) { - return RuntimeDyld::SymbolInfo(nullptr); + return JITSymbol(nullptr); }); { @@ -165,11 +165,11 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { createLambdaResolver( [&](const std::string &Name) { if (auto Sym = ObjLayer.findSymbol(Name, true)) - return Sym.toRuntimeDyldSymbol(); - return RuntimeDyld::SymbolInfo(nullptr); + return Sym; + return JITSymbol(nullptr); }, [](const std::string &Name) { - return RuntimeDyld::SymbolInfo(nullptr); + return JITSymbol(nullptr); }); SectionMemoryManagerWrapper SMMW; diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp index e1b1f2f92781..63b85dc82ca8 100644 --- a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp @@ -95,31 +95,32 @@ public: resetExpectations(); } - JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { + llvm::JITSymbol findSymbol(const std::string &Name, + bool ExportedSymbolsOnly) { EXPECT_EQ(MockName, Name) << "Name should pass through"; EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through"; LastCalled = "findSymbol"; - MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None); + MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None); return MockSymbol; } void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) { MockName = Name; MockBool = ExportedSymbolsOnly; } - void verifyFindSymbol(llvm::orc::JITSymbol Returned) { + void verifyFindSymbol(llvm::JITSymbol Returned) { EXPECT_EQ("findSymbol", LastCalled); EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress()) << "Return should pass through"; resetExpectations(); } - JITSymbol findSymbolIn(ObjSetHandleT H, const std::string &Name, - bool ExportedSymbolsOnly) { + llvm::JITSymbol findSymbolIn(ObjSetHandleT H, const std::string &Name, + bool ExportedSymbolsOnly) { EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through"; EXPECT_EQ(MockName, Name) << "Name should pass through"; EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through"; LastCalled = "findSymbolIn"; - MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None); + MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None); return MockSymbol; } void expectFindSymbolIn(ObjSetHandleT H, const std::string &Name, @@ -128,7 +129,7 @@ public: MockName = Name; MockBool = ExportedSymbolsOnly; } - void verifyFindSymbolIn(llvm::orc::JITSymbol Returned) { + void verifyFindSymbolIn(llvm::JITSymbol Returned) { EXPECT_EQ("findSymbolIn", LastCalled); EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress()) << "Return should pass through"; @@ -146,14 +147,14 @@ public: } void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress, - TargetAddress TargetAddr) { + llvm::JITTargetAddress TargetAddr) { EXPECT_EQ(MockObjSetHandle, H); EXPECT_EQ(MockLocalAddress, LocalAddress); EXPECT_EQ(MockTargetAddress, TargetAddr); LastCalled = "mapSectionAddress"; } void expectMapSectionAddress(ObjSetHandleT H, const void *LocalAddress, - TargetAddress TargetAddr) { + llvm::JITTargetAddress TargetAddr) { MockObjSetHandle = H; MockLocalAddress = LocalAddress; MockTargetAddress = TargetAddr; @@ -172,9 +173,9 @@ private: ObjSetHandleT MockObjSetHandle; std::string MockName; bool MockBool; - JITSymbol MockSymbol; + llvm::JITSymbol MockSymbol; const void *MockLocalAddress; - TargetAddress MockTargetAddress; + llvm::JITTargetAddress MockTargetAddress; MockMemoryBufferSet MockBufferSet; // Clear remembered parameters between calls @@ -185,7 +186,7 @@ private: MockObjects.clear(); MockObjSetHandle = 0; MockName = "bogus"; - MockSymbol = JITSymbol(nullptr); + MockSymbol = llvm::JITSymbol(nullptr); MockLocalAddress = nullptr; MockTargetAddress = 0; MockBufferSet = 0; @@ -245,7 +246,7 @@ TEST(ObjectTransformLayerTest, Main) { std::string Name = "foo"; bool ExportedOnly = true; M.expectFindSymbol(Name, ExportedOnly); - JITSymbol Symbol = T2.findSymbol(Name, ExportedOnly); + llvm::JITSymbol Symbol = T2.findSymbol(Name, ExportedOnly); M.verifyFindSymbol(Symbol); // Test findSymbolIn @@ -262,7 +263,7 @@ TEST(ObjectTransformLayerTest, Main) { // Test mapSectionAddress char Buffer[24]; - TargetAddress MockAddress = 255; + llvm::JITTargetAddress MockAddress = 255; M.expectMapSectionAddress(H, Buffer, MockAddress); T1.mapSectionAddress(H, Buffer, MockAddress); M.verifyMapSectionAddress(); diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/unittests/ExecutionEngine/Orc/OrcTestCommon.h index fe3da88dc9d1..f3972a3084e5 100644 --- a/unittests/ExecutionEngine/Orc/OrcTestCommon.h +++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.h @@ -22,7 +22,7 @@ #include "llvm/IR/TypeBuilder.h" #include "llvm/Object/ObjectFile.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" -#include "llvm/ExecutionEngine/Orc/JITSymbol.h" +#include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/Support/TargetSelect.h" #include <memory> @@ -124,11 +124,11 @@ public: RemoveModuleSet(H); } - orc::JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { + JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { return FindSymbol(Name, ExportedSymbolsOnly); } - orc::JITSymbol findSymbolIn(ModuleSetHandleT H, const std::string &Name, + JITSymbol findSymbolIn(ModuleSetHandleT H, const std::string &Name, bool ExportedSymbolsOnly) { return FindSymbolIn(H, Name, ExportedSymbolsOnly); } diff --git a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp index 7d55641e4ce2..381fd1030422 100644 --- a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ExecutionEngine/Orc/RPCChannel.h" +#include "llvm/ExecutionEngine/Orc/RawByteChannel.h" #include "llvm/ExecutionEngine/Orc/RPCUtils.h" #include "gtest/gtest.h" @@ -15,42 +15,39 @@ using namespace llvm; using namespace llvm::orc; -using namespace llvm::orc::remote; +using namespace llvm::orc::rpc; class Queue : public std::queue<char> { public: - std::mutex &getLock() { return Lock; } - + std::mutex &getMutex() { return M; } + std::condition_variable &getCondVar() { return CV; } private: - std::mutex Lock; + std::mutex M; + std::condition_variable CV; }; -class QueueChannel : public RPCChannel { +class QueueChannel : public RawByteChannel { 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. + std::unique_lock<std::mutex> Lock(InQueue.getMutex()); + while (Size) { 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(); - } + InQueue.getCondVar().wait(Lock); + *Dst++ = InQueue.front(); + --Size; + InQueue.pop(); } return Error::success(); } Error appendBytes(const char *Src, unsigned Size) override { - std::lock_guard<std::mutex> Lock(OutQueue.getLock()); + std::unique_lock<std::mutex> Lock(OutQueue.getMutex()); while (Size--) OutQueue.push(*Src++); + OutQueue.getCondVar().notify_one(); return Error::success(); } @@ -61,148 +58,402 @@ private: Queue &OutQueue; }; -class DummyRPC : public testing::Test, public RPC<QueueChannel> { +class RPCFoo {}; + +namespace llvm { +namespace orc { +namespace rpc { + + template <> + class RPCTypeName<RPCFoo> { + public: + static const char* getName() { return "RPCFoo"; } + }; + + template <> + class SerializationTraits<QueueChannel, RPCFoo, RPCFoo> { + public: + static Error serialize(QueueChannel&, const RPCFoo&) { + return Error::success(); + } + + static Error deserialize(QueueChannel&, RPCFoo&) { + return Error::success(); + } + }; + +} // end namespace rpc +} // end namespace orc +} // end namespace llvm + +class RPCBar {}; + +namespace llvm { +namespace orc { +namespace rpc { + + template <> + class SerializationTraits<QueueChannel, RPCFoo, RPCBar> { + public: + static Error serialize(QueueChannel&, const RPCBar&) { + return Error::success(); + } + + static Error deserialize(QueueChannel&, RPCBar&) { + return Error::success(); + } +}; + +} // end namespace rpc +} // end namespace orc +} // end namespace llvm + +class DummyRPCAPI { public: - enum FuncId : uint32_t { - VoidBoolId = RPCFunctionIdTraits<FuncId>::FirstValidId, - IntIntId, - AllTheTypesId + + class VoidBool : public Function<VoidBool, void(bool)> { + public: + static const char* getName() { return "VoidBool"; } + }; + + class IntInt : public Function<IntInt, int32_t(int32_t)> { + public: + static const char* getName() { return "IntInt"; } + }; + + class AllTheTypes + : public Function<AllTheTypes, + void(int8_t, uint8_t, int16_t, uint16_t, int32_t, + uint32_t, int64_t, uint64_t, bool, std::string, + std::vector<int>)> { + public: + static const char* getName() { return "AllTheTypes"; } }; - 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; + class CustomType : public Function<CustomType, RPCFoo(RPCFoo)> { + public: + static const char* getName() { return "CustomType"; } + }; + +}; + +class DummyRPCEndpoint : public DummyRPCAPI, + public SingleThreadedRPC<QueueChannel> { +public: + DummyRPCEndpoint(Queue &Q1, Queue &Q2) + : SingleThreadedRPC(C, true), C(Q1, Q2) {} +private: + QueueChannel C; }; -TEST_F(DummyRPC, TestAsyncVoidBool) { + +TEST(DummyRPC, TestAsyncVoidBool) { Queue Q1, Q2; - QueueChannel C1(Q1, Q2); - QueueChannel C2(Q2, Q1); + DummyRPCEndpoint Client(Q1, Q2); + DummyRPCEndpoint Server(Q2, Q1); - // Make an async call. - auto ResOrErr = callAsyncWithSeq<VoidBool>(C1, true); - EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed"; + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::VoidBool>( + [](bool B) { + EXPECT_EQ(B, true) + << "Server void(bool) received unexpected result"; + }); + + { + // Poke the server to handle the negotiate call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; + } + + { + // Poke the server to handle the VoidBool call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)"; + } + }); { - // 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 an async call. + auto Err = Client.callAsync<DummyRPCAPI::VoidBool>( + [](Error Err) { + EXPECT_FALSE(!!Err) << "Async void(bool) response handler failed"; + return Error::success(); + }, true); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(bool)"; } { - // Wait for the result. - auto EC = waitForResult(C1, ResOrErr->second, handleNone); - EXPECT_FALSE(EC) << "Could not read result."; + // Poke the client to process the result of the void(bool) call. + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; } - // Verify that the function returned ok. - auto Val = ResOrErr->first.get(); - EXPECT_TRUE(Val) << "Remote void function failed to execute."; + ServerThread.join(); } -TEST_F(DummyRPC, TestAsyncIntInt) { +TEST(DummyRPC, TestAsyncIntInt) { Queue Q1, Q2; - QueueChannel C1(Q1, Q2); - QueueChannel C2(Q2, Q1); + DummyRPCEndpoint Client(Q1, Q2); + DummyRPCEndpoint Server(Q2, Q1); - // Make an async call. - auto ResOrErr = callAsyncWithSeq<IntInt>(C1, 21); - EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed"; + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::IntInt>( + [](int X) -> int { + EXPECT_EQ(X, 21) << "Server int(int) receieved unexpected result"; + return 2 * X; + }); + + { + // Poke the server to handle the negotiate call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; + } + + { + // Poke the server to handle the int(int) call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to int(int)"; + } + }); + + { + auto Err = Client.callAsync<DummyRPCAPI::IntInt>( + [](Expected<int> Result) { + EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; + EXPECT_EQ(*Result, 42) + << "Async int(int) response handler received incorrect result"; + return Error::success(); + }, 21); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for int(int)"; + } { - // Expect a call to Proc1. - auto EC = expect<IntInt>(C2, [&](int32_t I) -> Expected<int32_t> { - EXPECT_EQ(I, 21) << "Bool serialization broken"; - return 2 * I; + // Poke the client to process the result. + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; + } + + ServerThread.join(); +} + +TEST(DummyRPC, TestSerialization) { + Queue Q1, Q2; + DummyRPCEndpoint Client(Q1, Q2); + DummyRPCEndpoint Server(Q2, Q1); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::AllTheTypes>( + [&](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(); + }); + + { + // Poke the server to handle the negotiate call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; + } + + { + // Poke the server to handle the AllTheTypes call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)"; + } }); - EXPECT_FALSE(EC) << "Simple expect over queue failed"; + + + { + // Make an async call. + std::vector<int> v({42, 7}); + auto Err = Client.callAsync<DummyRPCAPI::AllTheTypes>( + [](Error Err) { + EXPECT_FALSE(!!Err) << "Async AllTheTypes response handler failed"; + return Error::success(); + }, + static_cast<int8_t>(-101), static_cast<uint8_t>(250), + static_cast<int16_t>(-10000), static_cast<uint16_t>(10000), + static_cast<int32_t>(-1000000000), static_cast<uint32_t>(1000000000), + static_cast<int64_t>(-10000000000), static_cast<uint64_t>(10000000000), + true, std::string("foo"), v); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for AllTheTypes"; } { - // Wait for the result. - auto EC = waitForResult(C1, ResOrErr->second, handleNone); - EXPECT_FALSE(EC) << "Could not read result."; + // Poke the client to process the result of the AllTheTypes call. + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) << "Client failed to handle response from AllTheTypes"; } - // 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."; + ServerThread.join(); } -TEST_F(DummyRPC, TestSerialization) { +TEST(DummyRPC, TestCustomType) { Queue Q1, Q2; - QueueChannel C1(Q1, Q2); - QueueChannel C2(Q2, Q1); + DummyRPCEndpoint Client(Q1, Q2); + DummyRPCEndpoint Server(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"; + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::CustomType>( + [](RPCFoo F) {}); + + { + // Poke the server to handle the negotiate call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; + } + + { + // Poke the server to handle the CustomType call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to RPCFoo(RPCFoo)"; + } + }); { - // 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"; + // Make an async call. + auto Err = Client.callAsync<DummyRPCAPI::CustomType>( + [](Expected<RPCFoo> FOrErr) { + EXPECT_TRUE(!!FOrErr) + << "Async RPCFoo(RPCFoo) response handler failed"; return Error::success(); - }); - EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed"; + }, RPCFoo()); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for RPCFoo(RPCFoo)"; } { - // Wait for the result. - auto EC = waitForResult(C1, ResOrErr->second, handleNone); - EXPECT_FALSE(EC) << "Could not read result."; + // Poke the client to process the result of the RPCFoo() call. + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) + << "Client failed to handle response from RPCFoo(RPCFoo)"; } - // Verify that the function returned ok. - auto Val = ResOrErr->first.get(); - EXPECT_TRUE(Val) << "Remote void function failed to execute."; + ServerThread.join(); } -// 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"; -// } +TEST(DummyRPC, TestWithAltCustomType) { + Queue Q1, Q2; + DummyRPCEndpoint Client(Q1, Q2); + DummyRPCEndpoint Server(Q2, Q1); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::CustomType>( + [](RPCBar F) {}); + + { + // Poke the server to handle the negotiate call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; + } + + { + // Poke the server to handle the CustomType call. + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to RPCFoo(RPCFoo)"; + } + }); + + { + // Make an async call. + auto Err = Client.callAsync<DummyRPCAPI::CustomType>( + [](Expected<RPCBar> FOrErr) { + EXPECT_TRUE(!!FOrErr) + << "Async RPCFoo(RPCFoo) response handler failed"; + return Error::success(); + }, RPCBar()); + EXPECT_FALSE(!!Err) << "Client.callAsync failed for RPCFoo(RPCFoo)"; + } + + { + // Poke the client to process the result of the RPCFoo() call. + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) + << "Client failed to handle response from RPCFoo(RPCFoo)"; + } + + ServerThread.join(); +} + +TEST(DummyRPC, TestParallelCallGroup) { + Queue Q1, Q2; + DummyRPCEndpoint Client(Q1, Q2); + DummyRPCEndpoint Server(Q2, Q1); + + std::thread ServerThread([&]() { + Server.addHandler<DummyRPCAPI::IntInt>( + [](int X) -> int { + return 2 * X; + }); + + // Handle the negotiate, plus three calls. + for (unsigned I = 0; I != 4; ++I) { + auto Err = Server.handleOne(); + EXPECT_FALSE(!!Err) << "Server failed to handle call to int(int)"; + } + }); + + { + int A, B, C; + ParallelCallGroup<DummyRPCEndpoint> PCG(Client); + + { + auto Err = PCG.appendCall<DummyRPCAPI::IntInt>( + [&A](Expected<int> Result) { + EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; + A = *Result; + return Error::success(); + }, 1); + EXPECT_FALSE(!!Err) << "First parallel call failed for int(int)"; + } + + { + auto Err = PCG.appendCall<DummyRPCAPI::IntInt>( + [&B](Expected<int> Result) { + EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; + B = *Result; + return Error::success(); + }, 2); + EXPECT_FALSE(!!Err) << "Second parallel call failed for int(int)"; + } + + { + auto Err = PCG.appendCall<DummyRPCAPI::IntInt>( + [&C](Expected<int> Result) { + EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; + C = *Result; + return Error::success(); + }, 3); + EXPECT_FALSE(!!Err) << "Third parallel call failed for int(int)"; + } + + // Handle the three int(int) results. + for (unsigned I = 0; I != 3; ++I) { + auto Err = Client.handleOne(); + EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; + } + + { + auto Err = PCG.wait(); + EXPECT_FALSE(!!Err) << "Third parallel call failed for int(int)"; + } + + EXPECT_EQ(A, 2) << "First parallel call returned bogus result"; + EXPECT_EQ(B, 4) << "Second parallel call returned bogus result"; + EXPECT_EQ(C, 6) << "Third parallel call returned bogus result"; + } + + ServerThread.join(); +} diff --git a/unittests/IR/CMakeLists.txt b/unittests/IR/CMakeLists.txt index 2baa4370c70e..750f638c7a42 100644 --- a/unittests/IR/CMakeLists.txt +++ b/unittests/IR/CMakeLists.txt @@ -20,6 +20,7 @@ set(IRSources LegacyPassManagerTest.cpp MDBuilderTest.cpp MetadataTest.cpp + ModuleTest.cpp PassManagerTest.cpp PatternMatch.cpp TypeBuilderTest.cpp diff --git a/unittests/IR/ConstantRangeTest.cpp b/unittests/IR/ConstantRangeTest.cpp index f7a8a82043b9..58fd04448e2e 100644 --- a/unittests/IR/ConstantRangeTest.cpp +++ b/unittests/IR/ConstantRangeTest.cpp @@ -102,10 +102,19 @@ TEST_F(ConstantRangeTest, Equality) { TEST_F(ConstantRangeTest, SingleElement) { EXPECT_EQ(Full.getSingleElement(), static_cast<APInt *>(nullptr)); EXPECT_EQ(Empty.getSingleElement(), static_cast<APInt *>(nullptr)); + EXPECT_EQ(Full.getSingleMissingElement(), static_cast<APInt *>(nullptr)); + EXPECT_EQ(Empty.getSingleMissingElement(), static_cast<APInt *>(nullptr)); + EXPECT_EQ(*One.getSingleElement(), APInt(16, 0xa)); EXPECT_EQ(Some.getSingleElement(), static_cast<APInt *>(nullptr)); EXPECT_EQ(Wrap.getSingleElement(), static_cast<APInt *>(nullptr)); + EXPECT_EQ(One.getSingleMissingElement(), static_cast<APInt *>(nullptr)); + EXPECT_EQ(Some.getSingleMissingElement(), static_cast<APInt *>(nullptr)); + + ConstantRange OneInverse = One.inverse(); + EXPECT_EQ(*OneInverse.getSingleMissingElement(), *One.getSingleElement()); + EXPECT_FALSE(Full.isSingleElement()); EXPECT_FALSE(Empty.isSingleElement()); EXPECT_TRUE(One.isSingleElement()); @@ -348,6 +357,32 @@ TEST_F(ConstantRangeTest, Add) { ConstantRange(APInt(16, 0xe))); } +TEST_F(ConstantRangeTest, AddWithNoSignedWrap) { + EXPECT_EQ(Empty.addWithNoSignedWrap(APInt(16, 1)), Empty); + EXPECT_EQ(Full.addWithNoSignedWrap(APInt(16, 1)), + ConstantRange(APInt(16, INT16_MIN+1), APInt(16, INT16_MIN))); + EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50)).addWithNoSignedWrap(APInt(8, 10)), + ConstantRange(APInt(8, -40), APInt(8, 60))); + EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 120)).addWithNoSignedWrap(APInt(8, 10)), + ConstantRange(APInt(8, -40), APInt(8, INT8_MIN))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10)).addWithNoSignedWrap(APInt(8, 5)), + ConstantRange(APInt(8, 125), APInt(8, -5))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -120)).addWithNoSignedWrap(APInt(8, 10)), + ConstantRange(APInt(8, INT8_MIN+10), APInt(8, -110))); + + EXPECT_EQ(Empty.addWithNoSignedWrap(APInt(16, -1)), Empty); + EXPECT_EQ(Full.addWithNoSignedWrap(APInt(16, -1)), + ConstantRange(APInt(16, INT16_MIN), APInt(16, INT16_MAX))); + EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50)).addWithNoSignedWrap(APInt(8, -10)), + ConstantRange(APInt(8, -60), APInt(8, 40))); + EXPECT_EQ(ConstantRange(APInt(8, -120), APInt(8, 50)).addWithNoSignedWrap(APInt(8, -10)), + ConstantRange(APInt(8, INT8_MIN), APInt(8, 40))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -120)).addWithNoSignedWrap(APInt(8, -5)), + ConstantRange(APInt(8, 115), APInt(8, -125))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -120)).addWithNoSignedWrap(APInt(8, -10)), + ConstantRange(APInt(8, 110), APInt(8, INT8_MIN-10))); +} + TEST_F(ConstantRangeTest, Sub) { EXPECT_EQ(Full.sub(APInt(16, 4)), Full); EXPECT_EQ(Full.sub(Full), Full); @@ -760,6 +795,42 @@ TEST(ConstantRange, GetEquivalentICmp) { EXPECT_FALSE(ConstantRange(APInt::getMinValue(32) - APInt(32, 100), APInt::getMinValue(32) + APInt(32, 100)) .getEquivalentICmp(Pred, RHS)); + + EXPECT_TRUE(ConstantRange(APInt(32, 100)).getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_EQ); + EXPECT_EQ(RHS, APInt(32, 100)); + + EXPECT_TRUE( + ConstantRange(APInt(32, 100)).inverse().getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_NE); + EXPECT_EQ(RHS, APInt(32, 100)); + + EXPECT_TRUE( + ConstantRange(APInt(512, 100)).inverse().getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_NE); + EXPECT_EQ(RHS, APInt(512, 100)); + + // NB! It would be correct for the following four calls to getEquivalentICmp + // to return ordered predicates like CmpInst::ICMP_ULT or CmpInst::ICMP_UGT. + // However, that's not the case today. + + EXPECT_TRUE(ConstantRange(APInt(32, 0)).getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_EQ); + EXPECT_EQ(RHS, APInt(32, 0)); + + EXPECT_TRUE( + ConstantRange(APInt(32, 0)).inverse().getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_NE); + EXPECT_EQ(RHS, APInt(32, 0)); + + EXPECT_TRUE(ConstantRange(APInt(32, -1)).getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_EQ); + EXPECT_EQ(RHS, APInt(32, -1)); + + EXPECT_TRUE( + ConstantRange(APInt(32, -1)).inverse().getEquivalentICmp(Pred, RHS)); + EXPECT_EQ(Pred, CmpInst::ICMP_NE); + EXPECT_EQ(RHS, APInt(32, -1)); } } // anonymous namespace diff --git a/unittests/IR/DebugInfoTest.cpp b/unittests/IR/DebugInfoTest.cpp index f633782b3799..a80df3c54fd4 100644 --- a/unittests/IR/DebugInfoTest.cpp +++ b/unittests/IR/DebugInfoTest.cpp @@ -55,26 +55,28 @@ TEST(DINodeTest, getFlagString) { DINode::getFlagString(DINode::FlagPublic | DINode::FlagVector)); EXPECT_EQ(StringRef(), DINode::getFlagString(DINode::FlagFwdDecl | DINode::FlagArtificial)); - EXPECT_EQ(StringRef(), DINode::getFlagString(0xffff)); + EXPECT_EQ(StringRef(), + DINode::getFlagString(static_cast<DINode::DIFlags>(0xffff))); } TEST(DINodeTest, splitFlags) { // Some valid flags. #define CHECK_SPLIT(FLAGS, VECTOR, REMAINDER) \ { \ - SmallVector<unsigned, 8> V; \ + SmallVector<DINode::DIFlags, 8> V; \ EXPECT_EQ(REMAINDER, DINode::splitFlags(FLAGS, V)); \ EXPECT_TRUE(makeArrayRef(V).equals(VECTOR)); \ } - CHECK_SPLIT(DINode::FlagPublic, {DINode::FlagPublic}, 0u); - CHECK_SPLIT(DINode::FlagProtected, {DINode::FlagProtected}, 0u); - CHECK_SPLIT(DINode::FlagPrivate, {DINode::FlagPrivate}, 0u); - CHECK_SPLIT(DINode::FlagVector, {DINode::FlagVector}, 0u); - CHECK_SPLIT(DINode::FlagRValueReference, {DINode::FlagRValueReference}, 0u); - unsigned Flags[] = {DINode::FlagFwdDecl, DINode::FlagVector}; - CHECK_SPLIT(DINode::FlagFwdDecl | DINode::FlagVector, Flags, 0u); - CHECK_SPLIT(0x100000u, {}, 0x100000u); - CHECK_SPLIT(0x100000u | DINode::FlagVector, {DINode::FlagVector}, 0x100000u); + CHECK_SPLIT(DINode::FlagPublic, {DINode::FlagPublic}, DINode::FlagZero); + CHECK_SPLIT(DINode::FlagProtected, {DINode::FlagProtected}, DINode::FlagZero); + CHECK_SPLIT(DINode::FlagPrivate, {DINode::FlagPrivate}, DINode::FlagZero); + CHECK_SPLIT(DINode::FlagVector, {DINode::FlagVector}, DINode::FlagZero); + CHECK_SPLIT(DINode::FlagRValueReference, {DINode::FlagRValueReference}, + DINode::FlagZero); + DINode::DIFlags Flags[] = {DINode::FlagFwdDecl, DINode::FlagVector}; + CHECK_SPLIT(DINode::FlagFwdDecl | DINode::FlagVector, Flags, + DINode::FlagZero); + CHECK_SPLIT(DINode::FlagZero, {}, DINode::FlagZero); #undef CHECK_SPLIT } diff --git a/unittests/IR/DebugTypeODRUniquingTest.cpp b/unittests/IR/DebugTypeODRUniquingTest.cpp index 2c899d85d1f3..7cf1cd22a2fb 100644 --- a/unittests/IR/DebugTypeODRUniquingTest.cpp +++ b/unittests/IR/DebugTypeODRUniquingTest.cpp @@ -30,7 +30,7 @@ TEST(DebugTypeODRUniquingTest, getODRType) { // 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)); + nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr)); // Enable the mapping. There still shouldn't be a type. Context.enableDebugTypeODRUniquing(); @@ -39,19 +39,20 @@ TEST(DebugTypeODRUniquingTest, getODRType) { // 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); + nullptr, 0, 0, 0, DINode::FlagZero, 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)); + Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, + 0, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, 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, DINode::FlagZero, nullptr, 0, nullptr, nullptr)); // Check that it's discarded with the type map. Context.disableDebugTypeODRUniquing(); @@ -82,8 +83,9 @@ TEST(DebugTypeODRUniquingTest, buildODRType) { // 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)); + Context, UUID, dwarf::DW_TAG_structure_type, nullptr, + nullptr, 0, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, + nullptr, 0, nullptr, nullptr)); EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag()); // Further updates should be ignored. @@ -92,8 +94,9 @@ TEST(DebugTypeODRUniquingTest, buildODRType) { 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)); + Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, + 0, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, + nullptr, nullptr)); EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag()); } diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp index 58fd71b8a357..579384c5a5f4 100644 --- a/unittests/IR/IRBuilderTest.cpp +++ b/unittests/IR/IRBuilderTest.cpp @@ -340,15 +340,16 @@ TEST_F(IRBuilderTest, DIBuilder) { IRBuilder<> Builder(BB); DIBuilder DIB(*M); auto File = DIB.createFile("F.CBL", "/"); - auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74, "F.CBL", "/", - "llvm-cobol74", true, "", 0); + auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74, + DIB.createFile("F.CBL", "/"), "llvm-cobol74", + true, "", 0); auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); - auto SP = - DIB.createFunction(CU, "foo", "", File, 1, Type, false, true, 1, 0, true); + auto SP = DIB.createFunction(CU, "foo", "", File, 1, Type, false, true, 1, + DINode::FlagZero, true); F->setSubprogram(SP); AllocaInst *I = Builder.CreateAlloca(Builder.getInt8Ty()); - auto BarSP = - DIB.createFunction(CU, "bar", "", File, 1, Type, false, true, 1, 0, true); + auto BarSP = DIB.createFunction(CU, "bar", "", File, 1, Type, false, true, 1, + DINode::FlagZero, true); auto BadScope = DIB.createLexicalBlockFile(BarSP, File, 0); I->setDebugLoc(DebugLoc::get(2, 0, BadScope)); DIB.finalize(); @@ -392,8 +393,9 @@ TEST_F(IRBuilderTest, DebugLoc) { DIBuilder DIB(*M); auto File = DIB.createFile("tmp.cpp", "/"); - auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C_plus_plus_11, "tmp.cpp", "/", - "", true, "", 0); + auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C_plus_plus_11, + DIB.createFile("tmp.cpp", "/"), "", true, "", + 0); auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); auto SP = DIB.createFunction(CU, "foo", "foo", File, 1, SPType, false, true, 1); @@ -422,8 +424,9 @@ TEST_F(IRBuilderTest, DebugLoc) { TEST_F(IRBuilderTest, DIImportedEntity) { IRBuilder<> Builder(BB); DIBuilder DIB(*M); - auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74, "F.CBL", "/", - "llvm-cobol74", true, "", 0); + auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74, + DIB.createFile("F.CBL", "/"), "llvm-cobol74", + true, "", 0); DIB.createImportedDeclaration(CU, nullptr, 1); DIB.createImportedDeclaration(CU, nullptr, 1); DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, 2); diff --git a/unittests/IR/MetadataTest.cpp b/unittests/IR/MetadataTest.cpp index 15b03b3d5720..7bb8d4010d38 100644 --- a/unittests/IR/MetadataTest.cpp +++ b/unittests/IR/MetadataTest.cpp @@ -80,12 +80,13 @@ protected: MDTuple *getTuple() { return MDTuple::getDistinct(Context, None); } DISubroutineType *getSubroutineType() { - return DISubroutineType::getDistinct(Context, 0, 0, getNode(nullptr)); + return DISubroutineType::getDistinct(Context, DINode::FlagZero, 0, + getNode(nullptr)); } DISubprogram *getSubprogram() { return DISubprogram::getDistinct(Context, nullptr, "", "", nullptr, 0, - nullptr, false, false, 0, nullptr, - 0, 0, 0, 0, false, nullptr); + nullptr, false, false, 0, nullptr, 0, 0, 0, + DINode::FlagZero, false, nullptr); } DIFile *getFile() { return DIFile::getDistinct(Context, "file.c", "/path/to/dir"); @@ -94,15 +95,15 @@ protected: return DICompileUnit::getDistinct(Context, 1, getFile(), "clang", false, "-g", 2, "", DICompileUnit::FullDebug, getTuple(), getTuple(), getTuple(), - getTuple(), getTuple(), 0); + getTuple(), getTuple(), 0, true); } 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); + return DIDerivedType::getDistinct( + Context, dwarf::DW_TAG_pointer_type, "", nullptr, 0, nullptr, + getBasicType("basictype"), 1, 2, 0, DINode::FlagZero); } Constant *getConstant() { return ConstantInt::get(Type::getInt32Ty(Context), Counter++); @@ -113,7 +114,7 @@ protected: DIType *getCompositeType() { return DICompositeType::getDistinct( Context, dwarf::DW_TAG_structure_type, "", nullptr, 0, nullptr, nullptr, - 32, 32, 0, 0, nullptr, 0, nullptr, nullptr, ""); + 32, 32, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, ""); } Function *getFunction(StringRef Name) { return cast<Function>(M.getOrInsertFunction( @@ -996,9 +997,9 @@ TEST_F(DIBasicTypeTest, get) { TEST_F(DIBasicTypeTest, getWithLargeValues) { auto *N = DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", - UINT64_MAX, UINT64_MAX - 1, 7); + UINT64_MAX, UINT32_MAX - 1, 7); EXPECT_EQ(UINT64_MAX, N->getSizeInBits()); - EXPECT_EQ(UINT64_MAX - 1, N->getAlignInBits()); + EXPECT_EQ(UINT32_MAX - 1, N->getAlignInBits()); } TEST_F(DIBasicTypeTest, getUnspecified) { @@ -1028,19 +1029,21 @@ TEST_F(DITypeTest, setFlags) { Metadata *TypesOps[] = {nullptr}; Metadata *Types = MDTuple::get(Context, TypesOps); - DIType *D = DISubroutineType::getDistinct(Context, 0u, 0, Types); - EXPECT_EQ(0u, D->getFlags()); + DIType *D = + DISubroutineType::getDistinct(Context, DINode::FlagZero, 0, Types); + EXPECT_EQ(DINode::FlagZero, D->getFlags()); D->setFlags(DINode::FlagRValueReference); EXPECT_EQ(DINode::FlagRValueReference, D->getFlags()); - D->setFlags(0u); - EXPECT_EQ(0u, D->getFlags()); + D->setFlags(DINode::FlagZero); + EXPECT_EQ(DINode::FlagZero, D->getFlags()); - TempDIType T = DISubroutineType::getTemporary(Context, 0u, 0, Types); - EXPECT_EQ(0u, T->getFlags()); + TempDIType T = + DISubroutineType::getTemporary(Context, DINode::FlagZero, 0, Types); + EXPECT_EQ(DINode::FlagZero, T->getFlags()); T->setFlags(DINode::FlagRValueReference); EXPECT_EQ(DINode::FlagRValueReference, T->getFlags()); - T->setFlags(0u); - EXPECT_EQ(0u, T->getFlags()); + T->setFlags(DINode::FlagZero); + EXPECT_EQ(DINode::FlagZero, T->getFlags()); } typedef MetadataTest DIDerivedTypeTest; @@ -1050,9 +1053,12 @@ TEST_F(DIDerivedTypeTest, get) { DIScope *Scope = getSubprogram(); DIType *BaseType = getBasicType("basic"); MDTuple *ExtraData = getTuple(); + DINode::DIFlags Flags5 = static_cast<DINode::DIFlags>(5); + DINode::DIFlags Flags4 = static_cast<DINode::DIFlags>(4); - auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", - File, 1, Scope, BaseType, 2, 3, 4, 5, ExtraData); + auto *N = + DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, + 1, Scope, BaseType, 2, 3, 4, Flags5, ExtraData); EXPECT_EQ(dwarf::DW_TAG_pointer_type, N->getTag()); EXPECT_EQ("something", N->getName()); EXPECT_EQ(File, N->getFile()); @@ -1066,41 +1072,41 @@ TEST_F(DIDerivedTypeTest, get) { EXPECT_EQ(ExtraData, N->getExtraData()); EXPECT_EQ(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, 5, ExtraData)); + 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_reference_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, 5, ExtraData)); + 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "else", - File, 1, Scope, BaseType, 2, 3, 4, 5, + File, 1, Scope, BaseType, 2, 3, 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", getFile(), 1, Scope, BaseType, 2, - 3, 4, 5, ExtraData)); + 3, 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 2, Scope, BaseType, 2, 3, - 4, 5, ExtraData)); + 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, getSubprogram(), - BaseType, 2, 3, 4, 5, ExtraData)); + BaseType, 2, 3, 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get( Context, dwarf::DW_TAG_pointer_type, "something", File, 1, - Scope, getBasicType("basic2"), 2, 3, 4, 5, ExtraData)); + Scope, getBasicType("basic2"), 2, 3, 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 3, 3, - 4, 5, ExtraData)); + 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 2, - 4, 5, ExtraData)); + 4, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 5, 5, ExtraData)); + 5, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, 4, ExtraData)); + 4, Flags4, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, 5, getTuple())); + 4, Flags5, getTuple())); TempDIDerivedType Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1111,12 +1117,13 @@ TEST_F(DIDerivedTypeTest, getWithLargeValues) { DIScope *Scope = getSubprogram(); DIType *BaseType = getBasicType("basic"); MDTuple *ExtraData = getTuple(); + DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5); - auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", - File, 1, Scope, BaseType, UINT64_MAX, - UINT64_MAX - 1, UINT64_MAX - 2, 5, ExtraData); + auto *N = DIDerivedType::get( + Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, + BaseType, UINT64_MAX, UINT32_MAX - 1, UINT64_MAX - 2, Flags, ExtraData); EXPECT_EQ(UINT64_MAX, N->getSizeInBits()); - EXPECT_EQ(UINT64_MAX - 1, N->getAlignInBits()); + EXPECT_EQ(UINT32_MAX - 1, N->getAlignInBits()); EXPECT_EQ(UINT64_MAX - 2, N->getOffsetInBits()); } @@ -1130,9 +1137,9 @@ TEST_F(DICompositeTypeTest, get) { DIScope *Scope = getSubprogram(); DIType *BaseType = getCompositeType(); uint64_t SizeInBits = 2; - uint64_t AlignInBits = 3; + uint32_t AlignInBits = 3; uint64_t OffsetInBits = 4; - unsigned Flags = 5; + DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5); MDTuple *Elements = getTuple(); unsigned RuntimeLang = 6; DIType *VTableHolder = getCompositeType(); @@ -1200,9 +1207,10 @@ TEST_F(DICompositeTypeTest, get) { Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits, OffsetInBits + 1, Flags, Elements, RuntimeLang, VTableHolder, TemplateParams, Identifier)); + DINode::DIFlags FlagsPOne = static_cast<DINode::DIFlags>(Flags + 1); EXPECT_NE(N, DICompositeType::get( Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, - AlignInBits, OffsetInBits, Flags + 1, Elements, RuntimeLang, + AlignInBits, OffsetInBits, FlagsPOne, Elements, RuntimeLang, VTableHolder, TemplateParams, Identifier)); EXPECT_NE(N, DICompositeType::get( Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, @@ -1249,9 +1257,9 @@ TEST_F(DICompositeTypeTest, getWithLargeValues) { DIScope *Scope = getSubprogram(); DIType *BaseType = getCompositeType(); uint64_t SizeInBits = UINT64_MAX; - uint64_t AlignInBits = UINT64_MAX - 1; + uint32_t AlignInBits = UINT32_MAX - 1; uint64_t OffsetInBits = UINT64_MAX - 2; - unsigned Flags = 5; + DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5); MDTuple *Elements = getTuple(); unsigned RuntimeLang = 6; DIType *VTableHolder = getCompositeType(); @@ -1275,9 +1283,9 @@ TEST_F(DICompositeTypeTest, replaceOperands) { DIScope *Scope = getSubprogram(); DIType *BaseType = getCompositeType(); uint64_t SizeInBits = 2; - uint64_t AlignInBits = 3; + uint32_t AlignInBits = 3; uint64_t OffsetInBits = 4; - unsigned Flags = 5; + DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5); unsigned RuntimeLang = 6; StringRef Identifier = "some id"; @@ -1310,7 +1318,8 @@ TEST_F(DICompositeTypeTest, replaceOperands) { typedef MetadataTest DISubroutineTypeTest; TEST_F(DISubroutineTypeTest, get) { - unsigned Flags = 1; + DINode::DIFlags Flags = static_cast<DINode::DIFlags>(1); + DINode::DIFlags FlagsPOne = static_cast<DINode::DIFlags>(Flags + 1); MDTuple *TypeArray = getTuple(); auto *N = DISubroutineType::get(Context, Flags, 0, TypeArray); @@ -1319,7 +1328,7 @@ TEST_F(DISubroutineTypeTest, get) { EXPECT_EQ(TypeArray, N->getTypeArray().get()); 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, FlagsPOne, 0, TypeArray)); EXPECT_NE(N, DISubroutineType::get(Context, Flags, 0, getTuple())); // Test the hashing of calling conventions. @@ -1351,15 +1360,21 @@ typedef MetadataTest DIFileTest; TEST_F(DIFileTest, get) { StringRef Filename = "file"; StringRef Directory = "dir"; - auto *N = DIFile::get(Context, Filename, Directory); + DIFile::ChecksumKind CSKind = DIFile::CSK_MD5; + StringRef Checksum = "000102030405060708090a0b0c0d0e0f"; + auto *N = DIFile::get(Context, Filename, Directory, CSKind, Checksum); EXPECT_EQ(dwarf::DW_TAG_file_type, N->getTag()); EXPECT_EQ(Filename, N->getFilename()); EXPECT_EQ(Directory, N->getDirectory()); - EXPECT_EQ(N, DIFile::get(Context, Filename, Directory)); + EXPECT_EQ(CSKind, N->getChecksumKind()); + EXPECT_EQ(Checksum, N->getChecksum()); + EXPECT_EQ(N, DIFile::get(Context, Filename, Directory, CSKind, Checksum)); - EXPECT_NE(N, DIFile::get(Context, "other", Directory)); - EXPECT_NE(N, DIFile::get(Context, Filename, "other")); + EXPECT_NE(N, DIFile::get(Context, "other", Directory, CSKind, Checksum)); + EXPECT_NE(N, DIFile::get(Context, Filename, "other", CSKind, Checksum)); + EXPECT_NE(N, DIFile::get(Context, Filename, Directory, DIFile::CSK_SHA1, Checksum)); + EXPECT_NE(N, DIFile::get(Context, Filename, Directory)); TempDIFile Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1391,8 +1406,7 @@ TEST_F(DICompileUnitTest, get) { auto *N = DICompileUnit::getDistinct( Context, SourceLanguage, File, Producer, IsOptimized, Flags, RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes, - RetainedTypes, GlobalVariables, ImportedEntities, Macros, - DWOId); + RetainedTypes, GlobalVariables, ImportedEntities, Macros, DWOId, true); EXPECT_EQ(dwarf::DW_TAG_compile_unit, N->getTag()); EXPECT_EQ(SourceLanguage, N->getSourceLanguage()); @@ -1449,7 +1463,7 @@ TEST_F(DICompileUnitTest, replaceArrays) { auto *N = DICompileUnit::getDistinct( Context, SourceLanguage, File, Producer, IsOptimized, Flags, RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes, - RetainedTypes, nullptr, ImportedEntities, nullptr, DWOId); + RetainedTypes, nullptr, ImportedEntities, nullptr, DWOId, true); auto *GlobalVariables = MDTuple::getDistinct(Context, None); EXPECT_EQ(nullptr, N->getGlobalVariables().get()); @@ -1482,8 +1496,7 @@ TEST_F(DISubprogramTest, get) { unsigned Virtuality = 2; unsigned VirtualIndex = 5; int ThisAdjustment = -3; - unsigned Flags = 6; - unsigned NotFlags = (~Flags) & ((1 << 27) - 1); + DINode::DIFlags Flags = static_cast<DINode::DIFlags>(6); bool IsOptimized = false; MDTuple *TemplateParams = getTuple(); DISubprogram *Declaration = getSubprogram(); @@ -1586,11 +1599,6 @@ TEST_F(DISubprogramTest, get) { EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, - 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, ThisAdjustment, Flags, !IsOptimized, Unit, TemplateParams, Declaration, Variables)); EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, @@ -1704,20 +1712,28 @@ TEST_F(DINamespaceTest, get) { DIFile *File = getFile(); StringRef Name = "namespace"; unsigned Line = 5; + bool ExportSymbols = true; - auto *N = DINamespace::get(Context, Scope, File, Name, Line); + auto *N = DINamespace::get(Context, Scope, File, Name, Line, ExportSymbols); EXPECT_EQ(dwarf::DW_TAG_namespace, N->getTag()); EXPECT_EQ(Scope, N->getScope()); EXPECT_EQ(File, N->getFile()); EXPECT_EQ(Name, N->getName()); EXPECT_EQ(Line, N->getLine()); - EXPECT_EQ(N, DINamespace::get(Context, Scope, File, Name, Line)); + EXPECT_EQ(N, + DINamespace::get(Context, Scope, File, Name, Line, ExportSymbols)); - EXPECT_NE(N, DINamespace::get(Context, getFile(), File, Name, Line)); - EXPECT_NE(N, DINamespace::get(Context, Scope, getFile(), Name, Line)); - EXPECT_NE(N, DINamespace::get(Context, Scope, File, "other", Line)); - EXPECT_NE(N, DINamespace::get(Context, Scope, File, Name, Line + 1)); + EXPECT_NE(N, + DINamespace::get(Context, getFile(), File, Name, Line, ExportSymbols)); + EXPECT_NE(N, + DINamespace::get(Context, Scope, getFile(), Name, Line, ExportSymbols)); + EXPECT_NE(N, + DINamespace::get(Context, Scope, File, "other", Line, ExportSymbols)); + EXPECT_NE(N, + DINamespace::get(Context, Scope, File, Name, Line + 1, ExportSymbols)); + EXPECT_NE(N, + DINamespace::get(Context, Scope, File, Name, Line, !ExportSymbols)); TempDINamespace Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1818,13 +1834,13 @@ TEST_F(DIGlobalVariableTest, get) { DIType *Type = getDerivedType(); bool IsLocalToUnit = false; bool IsDefinition = true; - Constant *Variable = getConstant(); DIDerivedType *StaticDataMemberDeclaration = cast<DIDerivedType>(getDerivedType()); + uint32_t AlignInBits = 8; auto *N = DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, - Type, IsLocalToUnit, IsDefinition, Variable, - StaticDataMemberDeclaration); + Type, IsLocalToUnit, IsDefinition, + StaticDataMemberDeclaration, AlignInBits); EXPECT_EQ(dwarf::DW_TAG_variable, N->getTag()); EXPECT_EQ(Scope, N->getScope()); EXPECT_EQ(Name, N->getName()); @@ -1834,53 +1850,88 @@ TEST_F(DIGlobalVariableTest, get) { EXPECT_EQ(Type, N->getType()); EXPECT_EQ(IsLocalToUnit, N->isLocalToUnit()); EXPECT_EQ(IsDefinition, N->isDefinition()); - EXPECT_EQ(Variable, N->getVariable()); EXPECT_EQ(StaticDataMemberDeclaration, N->getStaticDataMemberDeclaration()); + EXPECT_EQ(AlignInBits, N->getAlignInBits()); EXPECT_EQ(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, getSubprogram(), Name, LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, "other", LinkageName, File, Line, Type, IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, "other", File, Line, Type, IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, getFile(), Line, Type, IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line + 1, Type, IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, getDerivedType(), IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, Type, !IsLocalToUnit, IsDefinition, - Variable, StaticDataMemberDeclaration)); + StaticDataMemberDeclaration, AlignInBits)); EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit, !IsDefinition, - Variable, StaticDataMemberDeclaration)); - EXPECT_NE(N, - DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, - Type, IsLocalToUnit, IsDefinition, - getConstant(), StaticDataMemberDeclaration)); - EXPECT_NE(N, - DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, - Type, IsLocalToUnit, IsDefinition, Variable, - cast<DIDerivedType>(getDerivedType()))); + StaticDataMemberDeclaration, AlignInBits)); + EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, + Line, Type, IsLocalToUnit, IsDefinition, + cast<DIDerivedType>(getDerivedType()), + AlignInBits)); + EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, + Line, Type, IsLocalToUnit, IsDefinition, + StaticDataMemberDeclaration, + (AlignInBits << 1))); TempDIGlobalVariable Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); } +typedef MetadataTest DIGlobalVariableExpressionTest; + +TEST_F(DIGlobalVariableExpressionTest, get) { + DIScope *Scope = getSubprogram(); + StringRef Name = "name"; + StringRef LinkageName = "linkage"; + DIFile *File = getFile(); + unsigned Line = 5; + DIType *Type = getDerivedType(); + bool IsLocalToUnit = false; + bool IsDefinition = true; + auto *Expr = DIExpression::get(Context, {1, 2}); + auto *Expr2 = DIExpression::get(Context, {1, 2, 3}); + DIDerivedType *StaticDataMemberDeclaration = + cast<DIDerivedType>(getDerivedType()); + uint32_t AlignInBits = 8; + + auto *Var = DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, + Line, Type, IsLocalToUnit, IsDefinition, + StaticDataMemberDeclaration, AlignInBits); + auto *Var2 = DIGlobalVariable::get(Context, Scope, "other", LinkageName, File, + Line, Type, IsLocalToUnit, IsDefinition, + StaticDataMemberDeclaration, AlignInBits); + auto *N = DIGlobalVariableExpression::get(Context, Var, Expr); + + EXPECT_EQ(Var, N->getVariable()); + EXPECT_EQ(Expr, N->getExpression()); + EXPECT_EQ(N, DIGlobalVariableExpression::get(Context, Var, Expr)); + EXPECT_NE(N, DIGlobalVariableExpression::get(Context, Var2, Expr)); + EXPECT_NE(N, DIGlobalVariableExpression::get(Context, Var, Expr2)); + + TempDIGlobalVariableExpression Temp = N->clone(); + EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); +} + typedef MetadataTest DILocalVariableTest; TEST_F(DILocalVariableTest, get) { @@ -1890,11 +1941,12 @@ TEST_F(DILocalVariableTest, get) { unsigned Line = 5; DIType *Type = getDerivedType(); unsigned Arg = 6; - unsigned Flags = 7; - unsigned NotFlags = (~Flags) & ((1 << 16) - 1); + DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7); + uint32_t AlignInBits = 8; auto *N = - DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg, Flags); + DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg, Flags, + AlignInBits); EXPECT_TRUE(N->isParameter()); EXPECT_EQ(Scope, N->getScope()); EXPECT_EQ(Name, N->getName()); @@ -1903,26 +1955,27 @@ TEST_F(DILocalVariableTest, get) { EXPECT_EQ(Type, N->getType()); EXPECT_EQ(Arg, N->getArg()); EXPECT_EQ(Flags, N->getFlags()); + EXPECT_EQ(AlignInBits, N->getAlignInBits()); EXPECT_EQ(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg, - Flags)); + Flags, AlignInBits)); EXPECT_FALSE( - DILocalVariable::get(Context, Scope, Name, File, Line, Type, 0, Flags) - ->isParameter()); + DILocalVariable::get(Context, Scope, Name, File, Line, Type, 0, Flags, + AlignInBits)->isParameter()); EXPECT_NE(N, DILocalVariable::get(Context, getSubprogram(), Name, File, Line, - Type, Arg, Flags)); + Type, Arg, Flags, AlignInBits)); EXPECT_NE(N, DILocalVariable::get(Context, Scope, "other", File, Line, Type, - Arg, Flags)); + Arg, Flags, AlignInBits)); EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, getFile(), Line, Type, - Arg, Flags)); + Arg, Flags, AlignInBits)); EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line + 1, Type, - Arg, Flags)); + Arg, Flags, AlignInBits)); EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, - getDerivedType(), Arg, Flags)); + getDerivedType(), Arg, Flags, AlignInBits)); + EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type, + Arg + 1, Flags, AlignInBits)); 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, - NotFlags)); + Arg, Flags, (AlignInBits << 1))); TempDILocalVariable Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); @@ -1930,17 +1983,17 @@ TEST_F(DILocalVariableTest, get) { TEST_F(DILocalVariableTest, getArg256) { EXPECT_EQ(255u, DILocalVariable::get(Context, getSubprogram(), "", getFile(), - 0, nullptr, 255, 0) + 0, nullptr, 255, DINode::FlagZero, 0) ->getArg()); EXPECT_EQ(256u, DILocalVariable::get(Context, getSubprogram(), "", getFile(), - 0, nullptr, 256, 0) + 0, nullptr, 256, DINode::FlagZero, 0) ->getArg()); EXPECT_EQ(257u, DILocalVariable::get(Context, getSubprogram(), "", getFile(), - 0, nullptr, 257, 0) + 0, nullptr, 257, DINode::FlagZero, 0) ->getArg()); unsigned Max = UINT16_MAX; EXPECT_EQ(Max, DILocalVariable::get(Context, getSubprogram(), "", getFile(), - 0, nullptr, Max, 0) + 0, nullptr, Max, DINode::FlagZero, 0) ->getArg()); } @@ -1981,19 +2034,20 @@ TEST_F(DIExpressionTest, isValid) { // Valid constructions. EXPECT_VALID(dwarf::DW_OP_plus, 6); EXPECT_VALID(dwarf::DW_OP_deref); - EXPECT_VALID(dwarf::DW_OP_bit_piece, 3, 7); + EXPECT_VALID(dwarf::DW_OP_LLVM_fragment, 3, 7); EXPECT_VALID(dwarf::DW_OP_plus, 6, dwarf::DW_OP_deref); EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_plus, 6); - EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_bit_piece, 3, 7); - EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_plus, 6, dwarf::DW_OP_bit_piece, 3, 7); + EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_LLVM_fragment, 3, 7); + EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_plus, 6, + dwarf::DW_OP_LLVM_fragment, 3, 7); // Invalid constructions. EXPECT_INVALID(~0u); EXPECT_INVALID(dwarf::DW_OP_plus); - EXPECT_INVALID(dwarf::DW_OP_bit_piece); - EXPECT_INVALID(dwarf::DW_OP_bit_piece, 3); - EXPECT_INVALID(dwarf::DW_OP_bit_piece, 3, 7, dwarf::DW_OP_plus, 3); - EXPECT_INVALID(dwarf::DW_OP_bit_piece, 3, 7, dwarf::DW_OP_deref); + EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment); + EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment, 3); + EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment, 3, 7, dwarf::DW_OP_plus, 3); + EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment, 3, 7, dwarf::DW_OP_deref); #undef EXPECT_VALID #undef EXPECT_INVALID diff --git a/unittests/IR/ModuleTest.cpp b/unittests/IR/ModuleTest.cpp new file mode 100644 index 000000000000..9f52fedc4559 --- /dev/null +++ b/unittests/IR/ModuleTest.cpp @@ -0,0 +1,75 @@ +//===- unittests/IR/ModuleTest.cpp - Module 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/GlobalVariable.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "gtest/gtest.h" + +#include <random> + +using namespace llvm; + +namespace { + +bool sortByName(const GlobalVariable &L, const GlobalVariable &R) { + return L.getName() < R.getName(); +} + +bool sortByNameReverse(const GlobalVariable &L, const GlobalVariable &R) { + return sortByName(R, L); +} + +TEST(ModuleTest, sortGlobalsByName) { + LLVMContext Context; + for (auto compare : {&sortByName, &sortByNameReverse}) { + Module M("M", Context); + Type *T = Type::getInt8Ty(Context); + GlobalValue::LinkageTypes L = GlobalValue::ExternalLinkage; + (void)new GlobalVariable(M, T, false, L, nullptr, "A"); + (void)new GlobalVariable(M, T, false, L, nullptr, "F"); + (void)new GlobalVariable(M, T, false, L, nullptr, "G"); + (void)new GlobalVariable(M, T, false, L, nullptr, "E"); + (void)new GlobalVariable(M, T, false, L, nullptr, "B"); + (void)new GlobalVariable(M, T, false, L, nullptr, "H"); + (void)new GlobalVariable(M, T, false, L, nullptr, "C"); + (void)new GlobalVariable(M, T, false, L, nullptr, "D"); + + // Sort the globals by name. + EXPECT_FALSE(std::is_sorted(M.global_begin(), M.global_end(), compare)); + M.getGlobalList().sort(compare); + EXPECT_TRUE(std::is_sorted(M.global_begin(), M.global_end(), compare)); + } +} + +TEST(ModuleTest, randomNumberGenerator) { + LLVMContext Context; + static char ID; + struct DummyPass : ModulePass { + DummyPass() : ModulePass(ID) {} + bool runOnModule(Module &) { return true; } + } DP; + + Module M("R", Context); + + std::uniform_int_distribution<int> dist; + const size_t NBCheck = 10; + + std::array<int, NBCheck> RandomStreams[2]; + for (auto &RandomStream : RandomStreams) { + std::unique_ptr<RandomNumberGenerator> RNG{M.createRNG(&DP)}; + std::generate(RandomStream.begin(), RandomStream.end(), + [&]() { return dist(*RNG); }); + } + + EXPECT_TRUE(std::equal(RandomStreams[0].begin(), RandomStreams[0].end(), + RandomStreams[1].begin())); +} + +} // end namespace diff --git a/unittests/IR/PassManagerTest.cpp b/unittests/IR/PassManagerTest.cpp index c2ac863260ae..b3a039a364fc 100644 --- a/unittests/IR/PassManagerTest.cpp +++ b/unittests/IR/PassManagerTest.cpp @@ -41,12 +41,12 @@ public: private: friend AnalysisInfoMixin<TestFunctionAnalysis>; - static char PassID; + static AnalysisKey Key; int &Runs; }; -char TestFunctionAnalysis::PassID; +AnalysisKey TestFunctionAnalysis::Key; class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> { public: @@ -67,12 +67,12 @@ public: private: friend AnalysisInfoMixin<TestModuleAnalysis>; - static char PassID; + static AnalysisKey Key; int &Runs; }; -char TestModuleAnalysis::PassID; +AnalysisKey TestModuleAnalysis::Key; struct TestModulePass : PassInfoMixin<TestModulePass> { TestModulePass(int &RunCount) : RunCount(RunCount) {} @@ -91,19 +91,6 @@ struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> { } }; -struct TestMinPreservingModulePass - : PassInfoMixin<TestMinPreservingModulePass> { - PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { - PreservedAnalyses PA; - - // Force running an analysis. - (void)AM.getResult<TestModuleAnalysis>(M); - - PA.preserve<FunctionAnalysisManagerModuleProxy>(); - return PA; - } -}; - struct TestFunctionPass : PassInfoMixin<TestFunctionPass> { TestFunctionPass(int &RunCount, int &AnalyzedInstrCount, int &AnalyzedFunctionCount, @@ -181,45 +168,232 @@ public: "}\n")) {} }; -TEST_F(PassManagerTest, BasicPreservedAnalyses) { +TEST(PreservedAnalysesTest, Basic) { PreservedAnalyses PA1 = PreservedAnalyses(); - EXPECT_FALSE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA1.preserved<TestModuleAnalysis>()); - PreservedAnalyses PA2 = PreservedAnalyses::none(); - EXPECT_FALSE(PA2.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA2.preserved<TestModuleAnalysis>()); - PreservedAnalyses PA3 = PreservedAnalyses::all(); - EXPECT_TRUE(PA3.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA3.preserved<TestModuleAnalysis>()); + { + auto PAC = PA1.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } + { + auto PAC = PA1.getChecker<TestModuleAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>()); + } + auto PA2 = PreservedAnalyses::none(); + { + auto PAC = PA2.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } + auto PA3 = PreservedAnalyses::all(); + { + auto PAC = PA3.getChecker<TestFunctionAnalysis>(); + EXPECT_TRUE(PAC.preserved()); + EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } PreservedAnalyses PA4 = PA1; - EXPECT_FALSE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>()); + { + auto PAC = PA4.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } PA4 = PA3; - EXPECT_TRUE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA4.preserved<TestModuleAnalysis>()); + { + auto PAC = PA4.getChecker<TestFunctionAnalysis>(); + EXPECT_TRUE(PAC.preserved()); + EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } PA4 = std::move(PA2); - EXPECT_FALSE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>()); - PA4.preserve<TestFunctionAnalysis>(); - EXPECT_TRUE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>()); - PA1.preserve<TestModuleAnalysis>(); - EXPECT_FALSE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA1.preserved<TestModuleAnalysis>()); + { + auto PAC = PA4.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } +} + +TEST(PreservedAnalysesTest, Preserve) { + auto PA = PreservedAnalyses::none(); + PA.preserve<TestFunctionAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved()); + PA.preserve<TestModuleAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved()); + + // Redundant calls are fine. + PA.preserve<TestFunctionAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved()); +} + +TEST(PreservedAnalysesTest, PreserveSets) { + auto PA = PreservedAnalyses::none(); + PA.preserveSet<AllAnalysesOn<Function>>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + PA.preserveSet<AllAnalysesOn<Module>>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Mixing is fine. + PA.preserve<TestFunctionAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Redundant calls are fine. + PA.preserveSet<AllAnalysesOn<Module>>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); +} + +TEST(PreservedAnalysisTest, Intersect) { + // Setup the initial sets. + auto PA1 = PreservedAnalyses::none(); PA1.preserve<TestFunctionAnalysis>(); - EXPECT_TRUE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA1.preserved<TestModuleAnalysis>()); - PA1.intersect(PA4); - EXPECT_TRUE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA1.preserved<TestModuleAnalysis>()); + PA1.preserveSet<AllAnalysesOn<Module>>(); + auto PA2 = PreservedAnalyses::none(); + PA2.preserve<TestFunctionAnalysis>(); + PA2.preserveSet<AllAnalysesOn<Function>>(); + PA2.preserve<TestModuleAnalysis>(); + PA2.preserveSet<AllAnalysesOn<Module>>(); + auto PA3 = PreservedAnalyses::none(); + PA3.preserve<TestModuleAnalysis>(); + PA3.preserveSet<AllAnalysesOn<Function>>(); + + // Self intersection is a no-op. + auto Intersected = PA1; + Intersected.intersect(PA1); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with all is a no-op. + Intersected.intersect(PreservedAnalyses::all()); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting a narrow set with a more broad set is the narrow set. + Intersected.intersect(PA2); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting a broad set with a more narrow set is the narrow set. + Intersected = PA2; + Intersected.intersect(PA1); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with empty clears. + Intersected.intersect(PreservedAnalyses::none()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting non-overlapping clears. + Intersected = PA1; + Intersected.intersect(PA3); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with moves works in when there is storage on both sides. + Intersected = PA1; + auto Tmp = PA2; + Intersected.intersect(std::move(Tmp)); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with move works for incoming all and existing all. + auto Tmp2 = PreservedAnalyses::all(); + Intersected.intersect(std::move(Tmp2)); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + Intersected = PreservedAnalyses::all(); + auto Tmp3 = PA1; + Intersected.intersect(std::move(Tmp3)); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); +} + +TEST(PreservedAnalysisTest, Abandon) { + auto PA = PreservedAnalyses::none(); + + // We can abandon things after they are preserved. + PA.preserve<TestFunctionAnalysis>(); + PA.abandon<TestFunctionAnalysis>(); + EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved()); + + // Repeated is fine, and abandoning if they were never preserved is fine. + PA.abandon<TestFunctionAnalysis>(); + EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved()); + PA.abandon<TestModuleAnalysis>(); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved()); + + // Even if the sets are preserved, the abandoned analyses' checker won't + // return true for those sets. + PA.preserveSet<AllAnalysesOn<Function>>(); + PA.preserveSet<AllAnalysesOn<Module>>(); + EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // But an arbitrary (opaque) analysis will still observe the sets as + // preserved. This also checks that we can use an explicit ID rather than + // a type. + AnalysisKey FakeKey, *FakeID = &FakeKey; + EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Module>>()); } TEST_F(PassManagerTest, Basic) { - FunctionAnalysisManager FAM; + FunctionAnalysisManager FAM(/*DebugLogging*/ true); int FunctionAnalysisRuns = 0; FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); - ModuleAnalysisManager MAM; + ModuleAnalysisManager MAM(/*DebugLogging*/ true); int ModuleAnalysisRuns = 0; MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); @@ -233,11 +407,11 @@ TEST_F(PassManagerTest, Basic) { int AnalyzedFunctionCount1 = 0; { // Pointless scoped copy to test move assignment. - ModulePassManager NestedMPM; + ModulePassManager NestedMPM(/*DebugLogging*/ true); FunctionPassManager FPM; { // Pointless scope to test move assignment. - FunctionPassManager NestedFPM; + FunctionPassManager NestedFPM(/*DebugLogging*/ true); NestedFPM.addPass(TestFunctionPass( FunctionPassRunCount1, AnalyzedInstrCount1, AnalyzedFunctionCount1)); FPM = std::move(NestedFPM); @@ -255,7 +429,7 @@ TEST_F(PassManagerTest, Basic) { int AnalyzedInstrCount2 = 0; int AnalyzedFunctionCount2 = 0; { - FunctionPassManager FPM; + FunctionPassManager FPM(/*DebugLogging*/ true); FPM.addPass(TestFunctionPass(FunctionPassRunCount2, AnalyzedInstrCount2, AnalyzedFunctionCount2)); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); @@ -268,15 +442,16 @@ TEST_F(PassManagerTest, Basic) { int AnalyzedInstrCount3 = 0; int AnalyzedFunctionCount3 = 0; { - FunctionPassManager FPM; + FunctionPassManager FPM(/*DebugLogging*/ true); FPM.addPass(TestFunctionPass(FunctionPassRunCount3, AnalyzedInstrCount3, AnalyzedFunctionCount3)); FPM.addPass(TestInvalidationFunctionPass("f")); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } - // A fourth function pass manager but with a minimal intervening passes. - MPM.addPass(TestMinPreservingModulePass()); + // A fourth function pass manager but with only preserving intervening + // passes but triggering the module analysis. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); int FunctionPassRunCount4 = 0; int AnalyzedInstrCount4 = 0; int AnalyzedFunctionCount4 = 0; @@ -287,12 +462,13 @@ TEST_F(PassManagerTest, Basic) { MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } - // A fifth function pass manager but which uses only cached results. + // A fifth function pass manager which invalidates one function first but + // uses only cached results. int FunctionPassRunCount5 = 0; int AnalyzedInstrCount5 = 0; int AnalyzedFunctionCount5 = 0; { - FunctionPassManager FPM; + FunctionPassManager FPM(/*DebugLogging*/ true); FPM.addPass(TestInvalidationFunctionPass("f")); FPM.addPass(TestFunctionPass(FunctionPassRunCount5, AnalyzedInstrCount5, AnalyzedFunctionCount5, @@ -317,18 +493,276 @@ TEST_F(PassManagerTest, Basic) { EXPECT_EQ(0, AnalyzedFunctionCount3); EXPECT_EQ(3, FunctionPassRunCount4); EXPECT_EQ(5, AnalyzedInstrCount4); - EXPECT_EQ(0, AnalyzedFunctionCount4); + EXPECT_EQ(9, AnalyzedFunctionCount4); EXPECT_EQ(3, FunctionPassRunCount5); EXPECT_EQ(2, AnalyzedInstrCount5); // Only 'g' and 'h' were cached. - EXPECT_EQ(0, AnalyzedFunctionCount5); + EXPECT_EQ(9, AnalyzedFunctionCount5); // Validate the analysis counters: // first run over 3 functions, then module pass invalidates // second run over 3 functions, nothing invalidates // third run over 0 functions, but 1 function invalidated // fourth run over 1 function + // fifth run invalidates 1 function first, but runs over 0 functions EXPECT_EQ(7, FunctionAnalysisRuns); EXPECT_EQ(1, ModuleAnalysisRuns); } + +// A customized pass manager that passes extra arguments through the +// infrastructure. +typedef AnalysisManager<Function, int> CustomizedAnalysisManager; +typedef PassManager<Function, CustomizedAnalysisManager, int, int &> + CustomizedPassManager; + +class CustomizedAnalysis : public AnalysisInfoMixin<CustomizedAnalysis> { +public: + struct Result { + Result(int I) : I(I) {} + int I; + }; + + Result run(Function &F, CustomizedAnalysisManager &AM, int I) { + return Result(I); + } + +private: + friend AnalysisInfoMixin<CustomizedAnalysis>; + static AnalysisKey Key; +}; + +AnalysisKey CustomizedAnalysis::Key; + +struct CustomizedPass : PassInfoMixin<CustomizedPass> { + std::function<void(CustomizedAnalysis::Result &, int &)> Callback; + + template <typename CallbackT> + CustomizedPass(CallbackT Callback) : Callback(Callback) {} + + PreservedAnalyses run(Function &F, CustomizedAnalysisManager &AM, int I, + int &O) { + Callback(AM.getResult<CustomizedAnalysis>(F, I), O); + return PreservedAnalyses::none(); + } +}; + +TEST_F(PassManagerTest, CustomizedPassManagerArgs) { + CustomizedAnalysisManager AM; + AM.registerPass([&] { return CustomizedAnalysis(); }); + + CustomizedPassManager PM; + + // Add an instance of the customized pass that just accumulates the input + // after it is round-tripped through the analysis. + int Result = 0; + PM.addPass( + CustomizedPass([](CustomizedAnalysis::Result &R, int &O) { O += R.I; })); + + // Run this over every function with the input of 42. + for (Function &F : *M) + PM.run(F, AM, 42, Result); + + // And ensure that we accumulated the correct result. + EXPECT_EQ(42 * (int)M->size(), Result); +} + +/// A test analysis pass which caches in its result another analysis pass and +/// uses it to serve queries. This requires the result to invalidate itself +/// when its dependency is invalidated. +struct TestIndirectFunctionAnalysis + : public AnalysisInfoMixin<TestIndirectFunctionAnalysis> { + struct Result { + Result(TestFunctionAnalysis::Result &FDep, TestModuleAnalysis::Result &MDep) + : FDep(FDep), MDep(MDep) {} + TestFunctionAnalysis::Result &FDep; + TestModuleAnalysis::Result &MDep; + + bool invalidate(Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + auto PAC = PA.getChecker<TestIndirectFunctionAnalysis>(); + return !(PAC.preserved() || + PAC.preservedSet<AllAnalysesOn<Function>>()) || + Inv.invalidate<TestFunctionAnalysis>(F, PA); + } + }; + + TestIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {} + + /// Run the analysis pass over the function and return a result. + Result run(Function &F, FunctionAnalysisManager &AM) { + ++Runs; + auto &FDep = AM.getResult<TestFunctionAnalysis>(F); + auto &Proxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F); + const ModuleAnalysisManager &MAM = Proxy.getManager(); + // For the test, we insist that the module analysis starts off in the + // cache. + auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); + // And register the dependency as module analysis dependencies have to be + // pre-registered on the proxy. + Proxy.registerOuterAnalysisInvalidation<TestModuleAnalysis, + TestIndirectFunctionAnalysis>(); + return Result(FDep, MDep); + } + +private: + friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>; + static AnalysisKey Key; + + int &Runs; +}; + +AnalysisKey TestIndirectFunctionAnalysis::Key; + +/// A test analysis pass which chaches in its result the result from the above +/// indirect analysis pass. +/// +/// This allows us to ensure that whenever an analysis pass is invalidated due +/// to dependencies (especially dependencies across IR units that trigger +/// asynchronous invalidation) we correctly detect that this may in turn cause +/// other analysis to be invalidated. +struct TestDoublyIndirectFunctionAnalysis + : public AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis> { + struct Result { + Result(TestIndirectFunctionAnalysis::Result &IDep) : IDep(IDep) {} + TestIndirectFunctionAnalysis::Result &IDep; + + bool invalidate(Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + auto PAC = PA.getChecker<TestDoublyIndirectFunctionAnalysis>(); + return !(PAC.preserved() || + PAC.preservedSet<AllAnalysesOn<Function>>()) || + Inv.invalidate<TestIndirectFunctionAnalysis>(F, PA); + } + }; + + TestDoublyIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {} + + /// Run the analysis pass over the function and return a result. + Result run(Function &F, FunctionAnalysisManager &AM) { + ++Runs; + auto &IDep = AM.getResult<TestIndirectFunctionAnalysis>(F); + return Result(IDep); + } + +private: + friend AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis>; + static AnalysisKey Key; + + int &Runs; +}; + +AnalysisKey TestDoublyIndirectFunctionAnalysis::Key; + +struct LambdaPass : public PassInfoMixin<LambdaPass> { + using FuncT = std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)>; + + LambdaPass(FuncT Func) : Func(std::move(Func)) {} + + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { + return Func(F, AM); + } + + FuncT Func; +}; + +TEST_F(PassManagerTest, IndirectAnalysisInvalidation) { + FunctionAnalysisManager FAM(/*DebugLogging*/ true); + int FunctionAnalysisRuns = 0, ModuleAnalysisRuns = 0, + IndirectAnalysisRuns = 0, DoublyIndirectAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + FAM.registerPass( + [&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns); }); + FAM.registerPass([&] { + return TestDoublyIndirectFunctionAnalysis(DoublyIndirectAnalysisRuns); + }); + + ModuleAnalysisManager MAM(/*DebugLogging*/ true); + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); + MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); + + int InstrCount = 0, FunctionCount = 0; + ModulePassManager MPM(/*DebugLogging*/ true); + FunctionPassManager FPM(/*DebugLogging*/ true); + // First just use the analysis to get the instruction count, and preserve + // everything. + FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + return PreservedAnalyses::all(); + })); + // Next, invalidate + // - both analyses for "f", + // - just the underlying (indirect) analysis for "g", and + // - just the direct analysis for "h". + FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + auto PA = PreservedAnalyses::none(); + if (F.getName() == "g") + PA.preserve<TestFunctionAnalysis>(); + else if (F.getName() == "h") + PA.preserve<TestIndirectFunctionAnalysis>(); + return PA; + })); + // Finally, use the analysis again on each function, forcing re-computation + // for all of them. + FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + return PreservedAnalyses::all(); + })); + + // Create a second function pass manager. This will cause the module-level + // invalidation to occur, which will force yet another invalidation of the + // indirect function-level analysis as the module analysis it depends on gets + // invalidated. + FunctionPassManager FPM2(/*DebugLogging*/ true); + FPM2.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + return PreservedAnalyses::all(); + })); + + // Add a requires pass to populate the module analysis and then our function + // pass pipeline. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + // Now require the module analysis again (it will have been invalidated once) + // and then use it again from a function pass manager. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM2))); + MPM.run(*M, MAM); + + // There are generally two possible runs for each of the three functions. But + // for one function, we only invalidate the indirect analysis so the base one + // only gets run five times. + EXPECT_EQ(5, FunctionAnalysisRuns); + // The module analysis pass should be run twice here. + EXPECT_EQ(2, ModuleAnalysisRuns); + // The indirect analysis is invalidated for each function (either directly or + // indirectly) and run twice for each. + EXPECT_EQ(9, IndirectAnalysisRuns); + EXPECT_EQ(9, DoublyIndirectAnalysisRuns); + + // There are five instructions in the module and we add the count four + // times. + EXPECT_EQ(5 * 4, InstrCount); + + // There are three functions and we count them four times for each of the + // three functions. + EXPECT_EQ(3 * 4 * 3, FunctionCount); +} } diff --git a/unittests/IR/PatternMatch.cpp b/unittests/IR/PatternMatch.cpp index 1121d6554db7..2d1321def7e3 100644 --- a/unittests/IR/PatternMatch.cpp +++ b/unittests/IR/PatternMatch.cpp @@ -295,4 +295,31 @@ TEST_F(PatternMatchTest, OverflowingBinOps) { EXPECT_FALSE(m_NUWShl(m_Value(), m_Value()).match(IRB.CreateNUWAdd(L, R))); } +template <typename T> struct MutableConstTest : PatternMatchTest { }; + +typedef ::testing::Types<std::tuple<Value*, Instruction*>, + std::tuple<const Value*, const Instruction *>> + MutableConstTestTypes; +TYPED_TEST_CASE(MutableConstTest, MutableConstTestTypes); + +TYPED_TEST(MutableConstTest, ICmp) { + auto &IRB = PatternMatchTest::IRB; + + typedef typename std::tuple_element<0, TypeParam>::type ValueType; + typedef typename std::tuple_element<1, TypeParam>::type InstructionType; + + Value *L = IRB.getInt32(1); + Value *R = IRB.getInt32(2); + ICmpInst::Predicate Pred = ICmpInst::ICMP_UGT; + + ValueType MatchL; + ValueType MatchR; + ICmpInst::Predicate MatchPred; + + EXPECT_TRUE(m_ICmp(MatchPred, m_Value(MatchL), m_Value(MatchR)) + .match((InstructionType)IRB.CreateICmp(Pred, L, R))); + EXPECT_EQ(L, MatchL); + EXPECT_EQ(R, MatchR); +} + } // anonymous namespace. diff --git a/unittests/IR/VerifierTest.cpp b/unittests/IR/VerifierTest.cpp index c33c92a6f7c5..ad6940afd05e 100644 --- a/unittests/IR/VerifierTest.cpp +++ b/unittests/IR/VerifierTest.cpp @@ -14,6 +14,7 @@ #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" @@ -121,31 +122,6 @@ 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); @@ -173,37 +149,67 @@ TEST(VerifierTest, InvalidFunctionLinkage) { "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)); + { + LLVMContext C; + Module M("M", C); + DIBuilder DIB(M); + DIB.createCompileUnit(dwarf::DW_LANG_C89, DIB.createFile("broken.c", "/"), + "unittest", false, "", 0); + DIB.finalize(); + EXPECT_FALSE(verifyModule(M)); + + // Now break it by inserting non-CU node to the list of CUs. + 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)); + } + { + LLVMContext C; + Module M("M", C); + DIBuilder DIB(M); + auto *CU = DIB.createCompileUnit(dwarf::DW_LANG_C89, + DIB.createFile("broken.c", "/"), + "unittest", false, "", 0); + new GlobalVariable(M, Type::getInt8Ty(C), false, + GlobalValue::ExternalLinkage, nullptr, "g"); + + auto *F = cast<Function>(M.getOrInsertFunction( + "f", FunctionType::get(Type::getVoidTy(C), false))); + IRBuilder<> Builder(BasicBlock::Create(C, "", F)); + Builder.CreateUnreachable(); + F->setSubprogram(DIB.createFunction(CU, "f", "f", + DIB.createFile("broken.c", "/"), 1, + nullptr, true, true, 1)); + DIB.finalize(); + EXPECT_FALSE(verifyModule(M)); + + // Now break it by not listing the CU at all. + M.eraseNamedMetadata(M.getOrInsertNamedMetadata("llvm.dbg.cu")); + 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", "/", + DIB.createCompileUnit(dwarf::DW_LANG_C89, DIB.createFile("broken.c", "/"), "unittest", false, "", 0); DIB.finalize(); EXPECT_FALSE(verifyModule(M)); diff --git a/unittests/MC/DwarfLineTables.cpp b/unittests/MC/DwarfLineTables.cpp index 4bfb5acea039..d66c832df0ba 100644 --- a/unittests/MC/DwarfLineTables.cpp +++ b/unittests/MC/DwarfLineTables.cpp @@ -46,21 +46,25 @@ struct Context { operator MCContext &() { return *Ctx; }; }; -Context Ctxt; +Context &getContext() { + static Context Ctxt; + return 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); + MCDwarfLineAddr::Encode(getContext(), 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) + if (!getContext()) return; MCDwarfLineTableParams Params; @@ -110,7 +114,7 @@ TEST(DwarfLineTables, TestDefaultParams) { } TEST(DwarfLineTables, TestCustomParams) { - if (!Ctxt) + if (!getContext()) return; // Some tests against the example values given in the standard. @@ -164,7 +168,7 @@ TEST(DwarfLineTables, TestCustomParams) { } TEST(DwarfLineTables, TestCustomParams2) { - if (!Ctxt) + if (!getContext()) return; // Corner case param values. diff --git a/unittests/MC/StringTableBuilderTest.cpp b/unittests/MC/StringTableBuilderTest.cpp index f78d3588ffff..f68350d90ba5 100644 --- a/unittests/MC/StringTableBuilderTest.cpp +++ b/unittests/MC/StringTableBuilderTest.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/SmallString.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Endian.h" #include "gtest/gtest.h" @@ -32,7 +33,11 @@ TEST(StringTableBuilderTest, BasicELF) { Expected += "foo"; Expected += '\x00'; - EXPECT_EQ(Expected, B.data()); + SmallString<64> Data; + raw_svector_ostream OS(Data); + B.write(OS); + + EXPECT_EQ(Expected, Data); EXPECT_EQ(1U, B.getOffset("foobar")); EXPECT_EQ(4U, B.getOffset("bar")); EXPECT_EQ(8U, B.getOffset("foo")); @@ -50,7 +55,7 @@ TEST(StringTableBuilderTest, BasicWinCOFF) { // size_field + "pygmy hippopotamus\0" + "river horse\0" uint32_t ExpectedSize = 4 + 19 + 12; - EXPECT_EQ(ExpectedSize, B.data().size()); + EXPECT_EQ(ExpectedSize, B.getSize()); std::string Expected; @@ -62,7 +67,11 @@ TEST(StringTableBuilderTest, BasicWinCOFF) { Expected += "river horse"; Expected += '\x00'; - EXPECT_EQ(Expected, B.data()); + SmallString<64> Data; + raw_svector_ostream OS(Data); + B.write(OS); + + EXPECT_EQ(Expected, Data); EXPECT_EQ(4U, B.getOffset("pygmy hippopotamus")); EXPECT_EQ(10U, B.getOffset("hippopotamus")); EXPECT_EQ(23U, B.getOffset("river horse")); @@ -85,7 +94,11 @@ TEST(StringTableBuilderTest, ELFInOrder) { Expected += "foobar"; Expected += '\x00'; - EXPECT_EQ(Expected, B.data()); + SmallString<64> Data; + raw_svector_ostream OS(Data); + B.write(OS); + + EXPECT_EQ(Expected, Data); EXPECT_EQ(1U, B.getOffset("foo")); EXPECT_EQ(5U, B.getOffset("bar")); EXPECT_EQ(9U, B.getOffset("foobar")); diff --git a/unittests/MI/LiveIntervalTest.cpp b/unittests/MI/LiveIntervalTest.cpp index e0b3d5529afb..1d6df97a3200 100644 --- a/unittests/MI/LiveIntervalTest.cpp +++ b/unittests/MI/LiveIntervalTest.cpp @@ -35,9 +35,10 @@ void initLLVM() { } /// 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. +/// unittests, we go for "AMDGPU" to be able to test normal and subregister +/// liveranges. std::unique_ptr<TargetMachine> createTargetMachine() { - Triple TargetTriple("x86_64--"); + Triple TargetTriple("amdgcn--"); std::string Error; const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error); if (!T) @@ -45,7 +46,7 @@ std::unique_ptr<TargetMachine> createTargetMachine() { TargetOptions Options; return std::unique_ptr<TargetMachine>( - T->createTargetMachine("x86_64", "", "", Options, None, + T->createTargetMachine("AMDGPU", "", "", Options, None, CodeModel::Default, CodeGenOpt::Aggressive)); } @@ -68,9 +69,9 @@ std::unique_ptr<Module> parseMIR(LLVMContext &Context, if (!F) return nullptr; - const LLVMTargetMachine &LLVMTM = static_cast<const LLVMTargetMachine&>(TM); - LLVMTM.addMachineModuleInfo(PM); - LLVMTM.addMachineFunctionAnalysis(PM, MIR.get()); + MachineModuleInfo *MMI = new MachineModuleInfo(&TM); + MMI->setMachineFunctionInitializer(MIR.get()); + PM.add(MMI); return M; } @@ -104,28 +105,31 @@ 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) { +static MachineInstr &getMI(MachineFunction &MF, unsigned At, + unsigned BlockNum) { 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; + if (I == At) + return MI; ++I; } - assert(FromInstr != nullptr && ToInstr != nullptr); + llvm_unreachable("Instruction not found"); +} - MBB.splice(ToInstr->getIterator(), &MBB, FromInstr->getIterator()); - LIS.handleMove(*FromInstr, true); +/** + * 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) { + MachineInstr &FromInstr = getMI(MF, From, BlockNum); + MachineInstr &ToInstr = getMI(MF, To, BlockNum); + + MachineBasicBlock &MBB = *FromInstr.getParent(); + MBB.splice(ToInstr.getIterator(), &MBB, FromInstr.getIterator()); + LIS.handleMove(FromInstr, true); } static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) { @@ -143,7 +147,7 @@ static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) { "...\n" "name: func\n" "registers:\n" -" - { id: 0, class: gr64 }\n" +" - { id: 0, class: sreg_64 }\n" "body: |\n" " bb.0:\n" ) + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S); @@ -164,10 +168,10 @@ INITIALIZE_PASS(TestPass, "testpass", "testpass", false, false) TEST(LiveIntervalTest, MoveUpDef) { // Value defined. liveIntervalTest( -" NOOP\n" -" NOOP\n" +" S_NOP 0\n" +" S_NOP 0\n" " early-clobber %0 = IMPLICIT_DEF\n" -" RETQ %0\n", +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); @@ -176,9 +180,9 @@ TEST(LiveIntervalTest, MoveUpDef) { TEST(LiveIntervalTest, MoveUpRedef) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP\n" +" S_NOP 0\n" " %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" RETQ %0\n", +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); @@ -186,10 +190,10 @@ TEST(LiveIntervalTest, MoveUpRedef) { TEST(LiveIntervalTest, MoveUpEarlyDef) { liveIntervalTest( -" NOOP\n" -" NOOP\n" +" S_NOP 0\n" +" S_NOP 0\n" " early-clobber %0 = IMPLICIT_DEF\n" -" RETQ %0\n", +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); @@ -198,9 +202,9 @@ TEST(LiveIntervalTest, MoveUpEarlyDef) { TEST(LiveIntervalTest, MoveUpEarlyRedef) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP\n" +" S_NOP 0\n" " early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" RETQ %0\n", +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); @@ -209,8 +213,8 @@ TEST(LiveIntervalTest, MoveUpEarlyRedef) { TEST(LiveIntervalTest, MoveUpKill) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP\n" -" NOOP implicit %0\n", +" S_NOP 0\n" +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); @@ -219,9 +223,9 @@ TEST(LiveIntervalTest, MoveUpKill) { TEST(LiveIntervalTest, MoveUpKillFollowing) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP\n" -" NOOP implicit %0\n" -" RETQ %0\n", +" S_NOP 0\n" +" S_NOP 0, implicit %0\n" +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 1); }); @@ -233,10 +237,10 @@ TEST(LiveIntervalTest, MoveUpKillFollowing) { TEST(LiveIntervalTest, MoveDownDef) { // Value defined. liveIntervalTest( -" NOOP\n" +" S_NOP 0\n" " early-clobber %0 = IMPLICIT_DEF\n" -" NOOP\n" -" RETQ %0\n", +" S_NOP 0\n" +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); @@ -246,8 +250,8 @@ TEST(LiveIntervalTest, MoveDownRedef) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" " %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" NOOP\n" -" RETQ %0\n", +" S_NOP 0\n" +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); @@ -255,10 +259,10 @@ TEST(LiveIntervalTest, MoveDownRedef) { TEST(LiveIntervalTest, MoveDownEarlyDef) { liveIntervalTest( -" NOOP\n" +" S_NOP 0\n" " early-clobber %0 = IMPLICIT_DEF\n" -" NOOP\n" -" RETQ %0\n", +" S_NOP 0\n" +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); @@ -268,8 +272,8 @@ TEST(LiveIntervalTest, MoveDownEarlyRedef) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" " early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" NOOP\n" -" RETQ %0\n", +" S_NOP 0\n" +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); @@ -278,8 +282,8 @@ TEST(LiveIntervalTest, MoveDownEarlyRedef) { TEST(LiveIntervalTest, MoveDownKill) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP implicit %0\n" -" NOOP\n", +" S_NOP 0, implicit %0\n" +" S_NOP 0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); @@ -288,9 +292,9 @@ TEST(LiveIntervalTest, MoveDownKill) { TEST(LiveIntervalTest, MoveDownKillFollowing) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP\n" -" NOOP implicit %0\n" -" RETQ %0\n", +" S_NOP 0\n" +" S_NOP 0, implicit %0\n" +" S_NOP 0, implicit %0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 2); }); @@ -299,9 +303,9 @@ TEST(LiveIntervalTest, MoveDownKillFollowing) { TEST(LiveIntervalTest, MoveUndefUse) { liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP implicit undef %0\n" -" NOOP implicit %0\n" -" NOOP\n", +" S_NOP 0, implicit undef %0\n" +" S_NOP 0, implicit %0\n" +" S_NOP 0\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 1, 3); }); @@ -314,16 +318,16 @@ TEST(LiveIntervalTest, MoveUpValNos) { liveIntervalTest( " successors: %bb.1, %bb.2\n" " %0 = IMPLICIT_DEF\n" -" JG_1 %bb.2, implicit %eflags\n" -" JMP_1 %bb.1\n" +" S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc\n" +" S_BRANCH %bb.1\n" " bb.2:\n" -" NOOP implicit %0\n" +" S_NOP 0, implicit %0\n" " bb.1:\n" " successors: %bb.2\n" " %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" " %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" " %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n" -" JMP_1 %bb.2\n", +" S_BRANCH %bb.2\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 2, 0, 2); }); @@ -333,8 +337,8 @@ TEST(LiveIntervalTest, MoveOverUndefUse0) { // findLastUseBefore() used by handleMoveUp() must ignore undef operands. liveIntervalTest( " %0 = IMPLICIT_DEF\n" -" NOOP\n" -" NOOP implicit undef %0\n" +" S_NOP 0\n" +" S_NOP 0, implicit undef %0\n" " %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 3, 1); @@ -344,15 +348,40 @@ TEST(LiveIntervalTest, MoveOverUndefUse0) { 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", +" %sgpr0 = IMPLICIT_DEF\n" +" S_NOP 0\n" +" S_NOP 0, implicit undef %sgpr0\n" +" %sgpr0 = IMPLICIT_DEF implicit %sgpr0(tied-def 0)\n", [](MachineFunction &MF, LiveIntervals &LIS) { testHandleMove(MF, LIS, 3, 1); }); } +TEST(LiveIntervalTest, SubRegMoveDown) { + // Subregister ranges can have holes inside a basic block. Check for a + // movement of the form 32->150 in a liverange [16, 32) [100,200). + liveIntervalTest( +" successors: %bb.1, %bb.2\n" +" %0 = IMPLICIT_DEF\n" +" S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc\n" +" S_BRANCH %bb.1\n" +" bb.2:\n" +" successors: %bb.1\n" +" S_NOP 0, implicit %0.sub0\n" +" S_NOP 0, implicit %0.sub1\n" +" S_NOP 0\n" +" undef %0.sub0 = IMPLICIT_DEF\n" +" %0.sub1 = IMPLICIT_DEF\n" +" bb.1:\n" +" S_NOP 0, implicit %0\n", + [](MachineFunction &MF, LiveIntervals &LIS) { + // Scheduler behaviour: Clear def,read-undef flag and move. + MachineInstr &MI = getMI(MF, 3, /*BlockNum=*/1); + MI.getOperand(0).setIsUndef(false); + testHandleMove(MF, LIS, 1, 4, /*BlockNum=*/1); + }); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); initLLVM(); diff --git a/unittests/Object/CMakeLists.txt b/unittests/Object/CMakeLists.txt new file mode 100644 index 000000000000..7a63c167a30b --- /dev/null +++ b/unittests/Object/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Object + ) + +add_llvm_unittest(ObjectTests + SymbolSizeTest.cpp + ) + diff --git a/unittests/Object/SymbolSizeTest.cpp b/unittests/Object/SymbolSizeTest.cpp new file mode 100644 index 000000000000..ad9c40b986db --- /dev/null +++ b/unittests/Object/SymbolSizeTest.cpp @@ -0,0 +1,33 @@ +//===- SymbolSizeTest.cpp - Tests for SymbolSize.cpp ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/SymbolSize.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::object; + +TEST(Object, SymbolSizeSort) { + auto it = symbol_iterator(SymbolRef()); + std::vector<SymEntry> Syms{ + SymEntry{it, 0xffffffff00000000ull, 1, 0}, + SymEntry{it, 0x00ffffff00000000ull, 2, 0}, + SymEntry{it, 0x00ffffff000000ffull, 3, 0}, + SymEntry{it, 0x0000000100000000ull, 4, 0}, + SymEntry{it, 0x00000000000000ffull, 5, 0}, + SymEntry{it, 0x00000001000000ffull, 6, 0}, + SymEntry{it, 0x000000010000ffffull, 7, 0}, + }; + + array_pod_sort(Syms.begin(), Syms.end(), compareAddress); + + for (unsigned I = 0, N = Syms.size(); I < N - 1; ++I) { + EXPECT_LE(Syms[I].Address, Syms[I + 1].Address); + } +} diff --git a/unittests/ProfileData/CoverageMappingTest.cpp b/unittests/ProfileData/CoverageMappingTest.cpp index 53b40ebae85a..49eab4ad7887 100644 --- a/unittests/ProfileData/CoverageMappingTest.cpp +++ b/unittests/ProfileData/CoverageMappingTest.cpp @@ -16,6 +16,7 @@ #include "gtest/gtest.h" #include <ostream> +#include <utility> using namespace llvm; using namespace coverage; @@ -55,6 +56,17 @@ struct OutputFunctionCoverageData { std::vector<StringRef> Filenames; std::vector<CounterMappingRegion> Regions; + OutputFunctionCoverageData() : Hash(0) {} + + OutputFunctionCoverageData(OutputFunctionCoverageData &&OFCD) + : Name(OFCD.Name), Hash(OFCD.Hash), Filenames(std::move(OFCD.Filenames)), + Regions(std::move(OFCD.Regions)) {} + + OutputFunctionCoverageData(const OutputFunctionCoverageData &) = delete; + OutputFunctionCoverageData & + operator=(const OutputFunctionCoverageData &) = delete; + OutputFunctionCoverageData &operator=(OutputFunctionCoverageData &&) = delete; + void fillCoverageMappingRecord(CoverageMappingRecord &Record) const { Record.FunctionName = Name; Record.FunctionHash = Hash; @@ -95,9 +107,20 @@ struct InputFunctionCoverageData { InputFunctionCoverageData(std::string Name, uint64_t Hash) : Name(std::move(Name)), Hash(Hash) {} + + InputFunctionCoverageData(InputFunctionCoverageData &&IFCD) + : ReverseVirtualFileMapping(std::move(IFCD.ReverseVirtualFileMapping)), + Name(std::move(IFCD.Name)), Hash(IFCD.Hash), + Regions(std::move(IFCD.Regions)) {} + + InputFunctionCoverageData(const InputFunctionCoverageData &) = delete; + InputFunctionCoverageData & + operator=(const InputFunctionCoverageData &) = delete; + InputFunctionCoverageData &operator=(InputFunctionCoverageData &&) = delete; }; -struct CoverageMappingTest : ::testing::Test { +struct CoverageMappingTest : ::testing::TestWithParam<std::pair<bool, bool>> { + bool UseMultipleReaders; StringMap<unsigned> Files; std::vector<InputFunctionCoverageData> InputFunctions; std::vector<OutputFunctionCoverageData> OutputFunctions; @@ -108,7 +131,8 @@ struct CoverageMappingTest : ::testing::Test { std::unique_ptr<CoverageMapping> LoadedCoverage; void SetUp() override { - ProfileWriter.setOutputSparse(false); + ProfileWriter.setOutputSparse(GetParam().first); + UseMultipleReaders = GetParam().second; } unsigned getGlobalFileIndex(StringRef Name) { @@ -116,12 +140,12 @@ struct CoverageMappingTest : ::testing::Test { if (R != Files.end()) return R->second; unsigned Index = Files.size(); - Files.emplace_second(Name, Index); + Files.try_emplace(Name, Index); return Index; } // Return the file index of file 'Name' for the current function. - // Add the file into the global map if necesary. + // Add the file into the global map if necessary. // See also InputFunctionCoverageData::ReverseVirtualFileMapping // for additional comments. unsigned getFileIndexForFunction(StringRef Name) { @@ -164,7 +188,7 @@ struct CoverageMappingTest : ::testing::Test { return OS.str(); } - void readCoverageRegions(std::string Coverage, + void readCoverageRegions(const std::string &Coverage, OutputFunctionCoverageData &Data) { SmallVector<StringRef, 8> Filenames(Files.size()); for (const auto &E : Files) @@ -194,27 +218,30 @@ struct CoverageMappingTest : ::testing::Test { ProfileReader = std::move(ReaderOrErr.get()); } + Expected<std::unique_ptr<CoverageMapping>> readOutputFunctions() { + if (!UseMultipleReaders) { + CoverageMappingReaderMock CovReader(OutputFunctions); + return CoverageMapping::load(CovReader, *ProfileReader); + } + + std::vector<std::unique_ptr<CoverageMappingReader>> CoverageReaders; + for (const auto &OF : OutputFunctions) { + ArrayRef<OutputFunctionCoverageData> Funcs(OF); + CoverageReaders.push_back(make_unique<CoverageMappingReaderMock>(Funcs)); + } + return CoverageMapping::load(CoverageReaders, *ProfileReader); + } + void loadCoverageMapping(bool EmitFilenames = true) { readProfCounts(); writeAndReadCoverageRegions(EmitFilenames); - - CoverageMappingReaderMock CovReader(OutputFunctions); - auto CoverageOrErr = CoverageMapping::load(CovReader, *ProfileReader); + auto CoverageOrErr = readOutputFunctions(); ASSERT_TRUE(NoError(CoverageOrErr.takeError())); LoadedCoverage = std::move(CoverageOrErr.get()); } }; -struct MaybeSparseCoverageMappingTest - : public CoverageMappingTest, - public ::testing::WithParamInterface<bool> { - void SetUp() { - CoverageMappingTest::SetUp(); - ProfileWriter.setOutputSparse(GetParam()); - } -}; - -TEST_P(MaybeSparseCoverageMappingTest, basic_write_read) { +TEST_P(CoverageMappingTest, basic_write_read) { startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1); addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2); @@ -239,8 +266,7 @@ TEST_P(MaybeSparseCoverageMappingTest, basic_write_read) { } } -TEST_P(MaybeSparseCoverageMappingTest, - correct_deserialize_for_more_than_two_files) { +TEST_P(CoverageMappingTest, correct_deserialize_for_more_than_two_files) { const char *FileNames[] = {"bar", "baz", "foo"}; static const unsigned N = array_lengthof(FileNames); @@ -265,7 +291,7 @@ TEST_P(MaybeSparseCoverageMappingTest, } } -TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_more_than_two_files) { +TEST_P(CoverageMappingTest, load_coverage_for_more_than_two_files) { InstrProfRecord Record("func", 0x1234, {0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -287,7 +313,7 @@ TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_more_than_two_files) { } } -TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_several_functions) { +TEST_P(CoverageMappingTest, load_coverage_for_several_functions) { InstrProfRecord RecordFunc1("func1", 0x1234, {10}); NoError(ProfileWriter.addRecord(std::move(RecordFunc1))); InstrProfRecord RecordFunc2("func2", 0x2345, {20}); @@ -318,7 +344,7 @@ TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_several_functions) { } } -TEST_P(MaybeSparseCoverageMappingTest, expansion_gets_first_counter) { +TEST_P(CoverageMappingTest, 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. @@ -334,7 +360,7 @@ TEST_P(MaybeSparseCoverageMappingTest, expansion_gets_first_counter) { ASSERT_EQ(3U, Output.Regions[2].LineStart); } -TEST_P(MaybeSparseCoverageMappingTest, basic_coverage_iteration) { +TEST_P(CoverageMappingTest, basic_coverage_iteration) { InstrProfRecord Record("func", 0x1234, {30, 20, 10, 0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -357,7 +383,7 @@ TEST_P(MaybeSparseCoverageMappingTest, basic_coverage_iteration) { ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]); } -TEST_P(MaybeSparseCoverageMappingTest, uncovered_function) { +TEST_P(CoverageMappingTest, uncovered_function) { startFunction("func", 0x1234); addCMR(Counter::getZero(), "file1", 1, 2, 3, 4); loadCoverageMapping(); @@ -369,7 +395,7 @@ TEST_P(MaybeSparseCoverageMappingTest, uncovered_function) { ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]); } -TEST_P(MaybeSparseCoverageMappingTest, uncovered_function_with_mapping) { +TEST_P(CoverageMappingTest, 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); @@ -383,7 +409,7 @@ TEST_P(MaybeSparseCoverageMappingTest, uncovered_function_with_mapping) { ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]); } -TEST_P(MaybeSparseCoverageMappingTest, combine_regions) { +TEST_P(CoverageMappingTest, combine_regions) { InstrProfRecord Record("func", 0x1234, {10, 20, 30}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -402,8 +428,7 @@ TEST_P(MaybeSparseCoverageMappingTest, combine_regions) { ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]); } -TEST_P(MaybeSparseCoverageMappingTest, - restore_combined_counter_after_nested_region) { +TEST_P(CoverageMappingTest, restore_combined_counter_after_nested_region) { InstrProfRecord Record("func", 0x1234, {10, 20, 40}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -424,7 +449,7 @@ TEST_P(MaybeSparseCoverageMappingTest, // If CodeRegions and ExpansionRegions cover the same area, // only counts of CodeRegions should be used. -TEST_P(MaybeSparseCoverageMappingTest, dont_combine_expansions) { +TEST_P(CoverageMappingTest, dont_combine_expansions) { InstrProfRecord Record1("func", 0x1234, {10, 20}); InstrProfRecord Record2("func", 0x1234, {0, 0}); NoError(ProfileWriter.addRecord(std::move(Record1))); @@ -447,7 +472,7 @@ TEST_P(MaybeSparseCoverageMappingTest, dont_combine_expansions) { } // If an area is covered only by ExpansionRegions, they should be combinated. -TEST_P(MaybeSparseCoverageMappingTest, combine_expansions) { +TEST_P(CoverageMappingTest, combine_expansions) { InstrProfRecord Record("func", 0x1234, {2, 3, 7}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -469,7 +494,7 @@ TEST_P(MaybeSparseCoverageMappingTest, combine_expansions) { EXPECT_EQ(CoverageSegment(5, 5, false), Segments[3]); } -TEST_P(MaybeSparseCoverageMappingTest, strip_filename_prefix) { +TEST_P(CoverageMappingTest, strip_filename_prefix) { InstrProfRecord Record("file1:func", 0x1234, {0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -484,7 +509,7 @@ TEST_P(MaybeSparseCoverageMappingTest, strip_filename_prefix) { ASSERT_EQ("func", Names[0]); } -TEST_P(MaybeSparseCoverageMappingTest, strip_unknown_filename_prefix) { +TEST_P(CoverageMappingTest, strip_unknown_filename_prefix) { InstrProfRecord Record("<unknown>:func", 0x1234, {0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -499,7 +524,7 @@ TEST_P(MaybeSparseCoverageMappingTest, strip_unknown_filename_prefix) { ASSERT_EQ("func", Names[0]); } -TEST_P(MaybeSparseCoverageMappingTest, dont_detect_false_instantiations) { +TEST_P(CoverageMappingTest, dont_detect_false_instantiations) { InstrProfRecord Record1("foo", 0x1234, {10}); InstrProfRecord Record2("bar", 0x2345, {20}); NoError(ProfileWriter.addRecord(std::move(Record1))); @@ -520,7 +545,7 @@ TEST_P(MaybeSparseCoverageMappingTest, dont_detect_false_instantiations) { ASSERT_TRUE(Instantiations.empty()); } -TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_expanded_file) { +TEST_P(CoverageMappingTest, load_coverage_for_expanded_file) { InstrProfRecord Record("func", 0x1234, {10}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -537,7 +562,28 @@ TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_expanded_file) { EXPECT_EQ(CoverageSegment(1, 10, false), Segments[1]); } -INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseCoverageMappingTest, - ::testing::Bool()); +TEST_P(CoverageMappingTest, skip_duplicate_function_record) { + InstrProfRecord Record("func", 0x1234, {1}); + NoError(ProfileWriter.addRecord(std::move(Record))); + + startFunction("func", 0x1234); + addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); + + startFunction("func", 0x1234); + addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); + + loadCoverageMapping(); + + auto Funcs = LoadedCoverage->getCoveredFunctions(); + unsigned NumFuncs = std::distance(Funcs.begin(), Funcs.end()); + ASSERT_EQ(1U, NumFuncs); +} + +// FIXME: Use ::testing::Combine() when llvm updates its copy of googletest. +INSTANTIATE_TEST_CASE_P(ParameterizedCovMapTest, CoverageMappingTest, + ::testing::Values(std::pair<bool, bool>({false, false}), + std::pair<bool, bool>({false, true}), + std::pair<bool, bool>({true, false}), + std::pair<bool, bool>({true, true}))); } // end anonymous namespace diff --git a/unittests/ProfileData/InstrProfTest.cpp b/unittests/ProfileData/InstrProfTest.cpp index c13f31251de4..1b44463cd650 100644 --- a/unittests/ProfileData/InstrProfTest.cpp +++ b/unittests/ProfileData/InstrProfTest.cpp @@ -167,15 +167,13 @@ TEST_F(InstrProfTest, get_profile_summary) { auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) { return PE.Cutoff == Cutoff; }; - auto EightyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + auto EightyPerc = find_if(Details, Predicate); Cutoff = 900000; - auto NinetyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + auto NinetyPerc = find_if(Details, Predicate); Cutoff = 950000; - auto NinetyFivePerc = - std::find_if(Details.begin(), Details.end(), Predicate); + auto NinetyFivePerc = find_if(Details, Predicate); Cutoff = 990000; - auto NinetyNinePerc = - std::find_if(Details.begin(), Details.end(), Predicate); + auto NinetyNinePerc = find_if(Details, Predicate); ASSERT_EQ(576460752303423488U, EightyPerc->MinCount); ASSERT_EQ(288230376151711744U, NinetyPerc->MinCount); ASSERT_EQ(288230376151711744U, NinetyFivePerc->MinCount); @@ -204,6 +202,31 @@ TEST_F(InstrProfTest, get_profile_summary) { delete PSFromMD; } +TEST_F(InstrProfTest, test_writer_merge) { + InstrProfRecord Record1("func1", 0x1234, {42}); + NoError(Writer.addRecord(std::move(Record1))); + + InstrProfWriter Writer2; + InstrProfRecord Record2("func2", 0x1234, {0, 0}); + NoError(Writer2.addRecord(std::move(Record2))); + + NoError(Writer.mergeRecordsFromWriter(std::move(Writer2))); + + auto Profile = Writer.writeBuffer(); + readProfile(std::move(Profile)); + + Expected<InstrProfRecord> R = Reader->getInstrProfRecord("func1", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(1U, R->Counts.size()); + ASSERT_EQ(42U, R->Counts[0]); + + R = Reader->getInstrProfRecord("func2", 0x1234); + ASSERT_TRUE(NoError(R.takeError())); + ASSERT_EQ(2U, R->Counts.size()); + ASSERT_EQ(0U, R->Counts[0]); + ASSERT_EQ(0U, R->Counts[1]); +} + static const char callee1[] = "callee1"; static const char callee2[] = "callee2"; static const char callee3[] = "callee3"; diff --git a/unittests/ProfileData/SampleProfTest.cpp b/unittests/ProfileData/SampleProfTest.cpp index 175852b93fb2..96b2a01c7bd7 100644 --- a/unittests/ProfileData/SampleProfTest.cpp +++ b/unittests/ProfileData/SampleProfTest.cpp @@ -124,15 +124,13 @@ struct SampleProfTest : ::testing::Test { return PE.Cutoff == Cutoff; }; std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary(); - auto EightyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + auto EightyPerc = find_if(Details, Predicate); Cutoff = 900000; - auto NinetyPerc = std::find_if(Details.begin(), Details.end(), Predicate); + auto NinetyPerc = find_if(Details, Predicate); Cutoff = 950000; - auto NinetyFivePerc = - std::find_if(Details.begin(), Details.end(), Predicate); + auto NinetyFivePerc = find_if(Details, Predicate); Cutoff = 990000; - auto NinetyNinePerc = - std::find_if(Details.begin(), Details.end(), Predicate); + auto NinetyNinePerc = find_if(Details, Predicate); ASSERT_EQ(60000u, EightyPerc->MinCount); ASSERT_EQ(60000u, NinetyPerc->MinCount); ASSERT_EQ(60000u, NinetyFivePerc->MinCount); diff --git a/unittests/Support/AlignOfTest.cpp b/unittests/Support/AlignOfTest.cpp index 74b03f0e7dfd..388ca11b7769 100644 --- a/unittests/Support/AlignOfTest.cpp +++ b/unittests/Support/AlignOfTest.cpp @@ -89,140 +89,33 @@ V6::~V6() {} V7::~V7() {} V8::~V8() {} -struct Abstract1 { - virtual ~Abstract1() {} - virtual void method() = 0; - - char c; -}; - -struct Abstract2 : Abstract1 { - ~Abstract2() override = default; - double d; -}; - -struct Final final : Abstract2 { - void method() override {} -}; - -// Ensure alignment is a compile-time constant. -char LLVM_ATTRIBUTE_UNUSED test_arr1 - [AlignOf<char>::Alignment > 0] - [AlignOf<short>::Alignment > 0] - [AlignOf<int>::Alignment > 0] - [AlignOf<long>::Alignment > 0] - [AlignOf<long long>::Alignment > 0] - [AlignOf<float>::Alignment > 0] - [AlignOf<double>::Alignment > 0] - [AlignOf<long double>::Alignment > 0] - [AlignOf<void *>::Alignment > 0] - [AlignOf<int *>::Alignment > 0] - [AlignOf<double (*)(double)>::Alignment > 0] - [AlignOf<double (S6::*)()>::Alignment > 0]; -char LLVM_ATTRIBUTE_UNUSED test_arr2 - [AlignOf<A1>::Alignment > 0] - [AlignOf<A2>::Alignment > 0] - [AlignOf<A4>::Alignment > 0] - [AlignOf<A8>::Alignment > 0]; -char LLVM_ATTRIBUTE_UNUSED test_arr3 - [AlignOf<S1>::Alignment > 0] - [AlignOf<S2>::Alignment > 0] - [AlignOf<S3>::Alignment > 0] - [AlignOf<S4>::Alignment > 0] - [AlignOf<S5>::Alignment > 0] - [AlignOf<S6>::Alignment > 0]; -char LLVM_ATTRIBUTE_UNUSED test_arr4 - [AlignOf<D1>::Alignment > 0] - [AlignOf<D2>::Alignment > 0] - [AlignOf<D3>::Alignment > 0] - [AlignOf<D4>::Alignment > 0] - [AlignOf<D5>::Alignment > 0] - [AlignOf<D6>::Alignment > 0] - [AlignOf<D7>::Alignment > 0] - [AlignOf<D8>::Alignment > 0] - [AlignOf<D9>::Alignment > 0]; -char LLVM_ATTRIBUTE_UNUSED test_arr5 - [AlignOf<V1>::Alignment > 0] - [AlignOf<V2>::Alignment > 0] - [AlignOf<V3>::Alignment > 0] - [AlignOf<V4>::Alignment > 0] - [AlignOf<V5>::Alignment > 0] - [AlignOf<V6>::Alignment > 0] - [AlignOf<V7>::Alignment > 0] - [AlignOf<V8>::Alignment > 0]; - -TEST(AlignOfTest, BasicAlignmentInvariants) { - EXPECT_LE(1u, alignOf<A1>()); - EXPECT_LE(2u, alignOf<A2>()); - EXPECT_LE(4u, alignOf<A4>()); - EXPECT_LE(8u, alignOf<A8>()); - - EXPECT_EQ(1u, alignOf<char>()); - EXPECT_LE(alignOf<char>(), alignOf<short>()); - EXPECT_LE(alignOf<short>(), alignOf<int>()); - EXPECT_LE(alignOf<int>(), alignOf<long>()); - EXPECT_LE(alignOf<long>(), alignOf<long long>()); - EXPECT_LE(alignOf<char>(), alignOf<float>()); - EXPECT_LE(alignOf<float>(), alignOf<double>()); - EXPECT_LE(alignOf<char>(), alignOf<long double>()); - EXPECT_LE(alignOf<char>(), alignOf<void *>()); - EXPECT_EQ(alignOf<void *>(), alignOf<int *>()); - EXPECT_LE(alignOf<char>(), alignOf<S1>()); - EXPECT_LE(alignOf<S1>(), alignOf<S2>()); - EXPECT_LE(alignOf<S1>(), alignOf<S3>()); - EXPECT_LE(alignOf<S1>(), alignOf<S4>()); - EXPECT_LE(alignOf<S1>(), alignOf<S5>()); - EXPECT_LE(alignOf<S1>(), alignOf<S6>()); - EXPECT_LE(alignOf<S1>(), alignOf<D1>()); - EXPECT_LE(alignOf<S1>(), alignOf<D2>()); - EXPECT_LE(alignOf<S1>(), alignOf<D3>()); - EXPECT_LE(alignOf<S1>(), alignOf<D4>()); - EXPECT_LE(alignOf<S1>(), alignOf<D5>()); - EXPECT_LE(alignOf<S1>(), alignOf<D6>()); - EXPECT_LE(alignOf<S1>(), alignOf<D7>()); - EXPECT_LE(alignOf<S1>(), alignOf<D8>()); - EXPECT_LE(alignOf<S1>(), alignOf<D9>()); - EXPECT_LE(alignOf<S1>(), alignOf<V1>()); - EXPECT_LE(alignOf<V1>(), alignOf<V2>()); - EXPECT_LE(alignOf<V1>(), alignOf<V3>()); - EXPECT_LE(alignOf<V1>(), alignOf<V4>()); - EXPECT_LE(alignOf<V1>(), alignOf<V5>()); - EXPECT_LE(alignOf<V1>(), alignOf<V6>()); - EXPECT_LE(alignOf<V1>(), alignOf<V7>()); - EXPECT_LE(alignOf<V1>(), alignOf<V8>()); - - EXPECT_LE(alignOf<char>(), alignOf<Abstract1>()); - EXPECT_LE(alignOf<double>(), alignOf<Abstract2>()); - EXPECT_LE(alignOf<Abstract2>(), alignOf<Final>()); -} +template <typename M> struct T { M m; }; TEST(AlignOfTest, BasicAlignedArray) { - EXPECT_LE(1u, alignOf<AlignedCharArrayUnion<A1> >()); - EXPECT_LE(2u, alignOf<AlignedCharArrayUnion<A2> >()); - EXPECT_LE(4u, alignOf<AlignedCharArrayUnion<A4> >()); - EXPECT_LE(8u, alignOf<AlignedCharArrayUnion<A8> >()); + EXPECT_LE(1u, alignof(AlignedCharArrayUnion<A1>)); + EXPECT_LE(2u, alignof(AlignedCharArrayUnion<A2>)); + EXPECT_LE(4u, alignof(AlignedCharArrayUnion<A4>)); + EXPECT_LE(8u, alignof(AlignedCharArrayUnion<A8>)); EXPECT_LE(1u, sizeof(AlignedCharArrayUnion<A1>)); EXPECT_LE(2u, sizeof(AlignedCharArrayUnion<A2>)); EXPECT_LE(4u, sizeof(AlignedCharArrayUnion<A4>)); EXPECT_LE(8u, sizeof(AlignedCharArrayUnion<A8>)); - EXPECT_EQ(1u, (alignOf<AlignedCharArrayUnion<A1> >())); - EXPECT_EQ(2u, (alignOf<AlignedCharArrayUnion<A1, A2> >())); - EXPECT_EQ(4u, (alignOf<AlignedCharArrayUnion<A1, A2, A4> >())); - EXPECT_EQ(8u, (alignOf<AlignedCharArrayUnion<A1, A2, A4, A8> >())); + EXPECT_EQ(1u, (alignof(AlignedCharArrayUnion<A1>))); + EXPECT_EQ(2u, (alignof(AlignedCharArrayUnion<A1, A2>))); + EXPECT_EQ(4u, (alignof(AlignedCharArrayUnion<A1, A2, A4>))); + EXPECT_EQ(8u, (alignof(AlignedCharArrayUnion<A1, A2, A4, A8>))); EXPECT_EQ(1u, sizeof(AlignedCharArrayUnion<A1>)); EXPECT_EQ(2u, sizeof(AlignedCharArrayUnion<A1, A2>)); EXPECT_EQ(4u, sizeof(AlignedCharArrayUnion<A1, A2, A4>)); EXPECT_EQ(8u, sizeof(AlignedCharArrayUnion<A1, A2, A4, A8>)); - EXPECT_EQ(1u, (alignOf<AlignedCharArrayUnion<A1[1]> >())); - EXPECT_EQ(2u, (alignOf<AlignedCharArrayUnion<A1[2], A2[1]> >())); - EXPECT_EQ(4u, (alignOf<AlignedCharArrayUnion<A1[42], A2[55], - A4[13]> >())); - EXPECT_EQ(8u, (alignOf<AlignedCharArrayUnion<A1[2], A2[1], - A4, A8> >())); + EXPECT_EQ(1u, (alignof(AlignedCharArrayUnion<A1[1]>))); + EXPECT_EQ(2u, (alignof(AlignedCharArrayUnion<A1[2], A2[1]>))); + EXPECT_EQ(4u, (alignof(AlignedCharArrayUnion<A1[42], A2[55], A4[13]>))); + EXPECT_EQ(8u, (alignof(AlignedCharArrayUnion<A1[2], A2[1], A4, A8>))); EXPECT_EQ(1u, sizeof(AlignedCharArrayUnion<A1[1]>)); EXPECT_EQ(2u, sizeof(AlignedCharArrayUnion<A1[2], A2[1]>)); @@ -233,49 +126,48 @@ TEST(AlignOfTest, BasicAlignedArray) { // For other tests we simply assert that the alignment of the union mathes // that of the fundamental type and hope that we have any weird type // productions that would trigger bugs. - EXPECT_EQ(alignOf<char>(), alignOf<AlignedCharArrayUnion<char> >()); - EXPECT_EQ(alignOf<short>(), alignOf<AlignedCharArrayUnion<short> >()); - EXPECT_EQ(alignOf<int>(), alignOf<AlignedCharArrayUnion<int> >()); - EXPECT_EQ(alignOf<long>(), alignOf<AlignedCharArrayUnion<long> >()); - EXPECT_EQ(alignOf<long long>(), - alignOf<AlignedCharArrayUnion<long long> >()); - EXPECT_EQ(alignOf<float>(), alignOf<AlignedCharArrayUnion<float> >()); - EXPECT_EQ(alignOf<double>(), alignOf<AlignedCharArrayUnion<double> >()); - EXPECT_EQ(alignOf<long double>(), - alignOf<AlignedCharArrayUnion<long double> >()); - EXPECT_EQ(alignOf<void *>(), alignOf<AlignedCharArrayUnion<void *> >()); - EXPECT_EQ(alignOf<int *>(), alignOf<AlignedCharArrayUnion<int *> >()); - EXPECT_EQ(alignOf<double (*)(double)>(), - alignOf<AlignedCharArrayUnion<double (*)(double)> >()); - EXPECT_EQ(alignOf<double (S6::*)()>(), - alignOf<AlignedCharArrayUnion<double (S6::*)()> >()); - EXPECT_EQ(alignOf<S1>(), alignOf<AlignedCharArrayUnion<S1> >()); - EXPECT_EQ(alignOf<S2>(), alignOf<AlignedCharArrayUnion<S2> >()); - EXPECT_EQ(alignOf<S3>(), alignOf<AlignedCharArrayUnion<S3> >()); - EXPECT_EQ(alignOf<S4>(), alignOf<AlignedCharArrayUnion<S4> >()); - EXPECT_EQ(alignOf<S5>(), alignOf<AlignedCharArrayUnion<S5> >()); - EXPECT_EQ(alignOf<S6>(), alignOf<AlignedCharArrayUnion<S6> >()); - EXPECT_EQ(alignOf<D1>(), alignOf<AlignedCharArrayUnion<D1> >()); - EXPECT_EQ(alignOf<D2>(), alignOf<AlignedCharArrayUnion<D2> >()); - EXPECT_EQ(alignOf<D3>(), alignOf<AlignedCharArrayUnion<D3> >()); - EXPECT_EQ(alignOf<D4>(), alignOf<AlignedCharArrayUnion<D4> >()); - EXPECT_EQ(alignOf<D5>(), alignOf<AlignedCharArrayUnion<D5> >()); - EXPECT_EQ(alignOf<D6>(), alignOf<AlignedCharArrayUnion<D6> >()); - EXPECT_EQ(alignOf<D7>(), alignOf<AlignedCharArrayUnion<D7> >()); - EXPECT_EQ(alignOf<D8>(), alignOf<AlignedCharArrayUnion<D8> >()); - EXPECT_EQ(alignOf<D9>(), alignOf<AlignedCharArrayUnion<D9> >()); - EXPECT_EQ(alignOf<V1>(), alignOf<AlignedCharArrayUnion<V1> >()); - EXPECT_EQ(alignOf<V2>(), alignOf<AlignedCharArrayUnion<V2> >()); - EXPECT_EQ(alignOf<V3>(), alignOf<AlignedCharArrayUnion<V3> >()); - EXPECT_EQ(alignOf<V4>(), alignOf<AlignedCharArrayUnion<V4> >()); - EXPECT_EQ(alignOf<V5>(), alignOf<AlignedCharArrayUnion<V5> >()); - EXPECT_EQ(alignOf<V6>(), alignOf<AlignedCharArrayUnion<V6> >()); - EXPECT_EQ(alignOf<V7>(), alignOf<AlignedCharArrayUnion<V7> >()); + EXPECT_EQ(alignof(T<char>), alignof(AlignedCharArrayUnion<char>)); + EXPECT_EQ(alignof(T<short>), alignof(AlignedCharArrayUnion<short>)); + EXPECT_EQ(alignof(T<int>), alignof(AlignedCharArrayUnion<int>)); + EXPECT_EQ(alignof(T<long>), alignof(AlignedCharArrayUnion<long>)); + EXPECT_EQ(alignof(T<long long>), alignof(AlignedCharArrayUnion<long long>)); + EXPECT_EQ(alignof(T<float>), alignof(AlignedCharArrayUnion<float>)); + EXPECT_EQ(alignof(T<double>), alignof(AlignedCharArrayUnion<double>)); + EXPECT_EQ(alignof(T<long double>), + alignof(AlignedCharArrayUnion<long double>)); + EXPECT_EQ(alignof(T<void *>), alignof(AlignedCharArrayUnion<void *>)); + EXPECT_EQ(alignof(T<int *>), alignof(AlignedCharArrayUnion<int *>)); + EXPECT_EQ(alignof(T<double (*)(double)>), + alignof(AlignedCharArrayUnion<double (*)(double)>)); + EXPECT_EQ(alignof(T<double (S6::*)()>), + alignof(AlignedCharArrayUnion<double (S6::*)()>)); + EXPECT_EQ(alignof(S1), alignof(AlignedCharArrayUnion<S1>)); + EXPECT_EQ(alignof(S2), alignof(AlignedCharArrayUnion<S2>)); + EXPECT_EQ(alignof(S3), alignof(AlignedCharArrayUnion<S3>)); + EXPECT_EQ(alignof(S4), alignof(AlignedCharArrayUnion<S4>)); + EXPECT_EQ(alignof(S5), alignof(AlignedCharArrayUnion<S5>)); + EXPECT_EQ(alignof(S6), alignof(AlignedCharArrayUnion<S6>)); + EXPECT_EQ(alignof(D1), alignof(AlignedCharArrayUnion<D1>)); + EXPECT_EQ(alignof(D2), alignof(AlignedCharArrayUnion<D2>)); + EXPECT_EQ(alignof(D3), alignof(AlignedCharArrayUnion<D3>)); + EXPECT_EQ(alignof(D4), alignof(AlignedCharArrayUnion<D4>)); + EXPECT_EQ(alignof(D5), alignof(AlignedCharArrayUnion<D5>)); + EXPECT_EQ(alignof(D6), alignof(AlignedCharArrayUnion<D6>)); + EXPECT_EQ(alignof(D7), alignof(AlignedCharArrayUnion<D7>)); + EXPECT_EQ(alignof(D8), alignof(AlignedCharArrayUnion<D8>)); + EXPECT_EQ(alignof(D9), alignof(AlignedCharArrayUnion<D9>)); + EXPECT_EQ(alignof(V1), alignof(AlignedCharArrayUnion<V1>)); + EXPECT_EQ(alignof(V2), alignof(AlignedCharArrayUnion<V2>)); + EXPECT_EQ(alignof(V3), alignof(AlignedCharArrayUnion<V3>)); + EXPECT_EQ(alignof(V4), alignof(AlignedCharArrayUnion<V4>)); + EXPECT_EQ(alignof(V5), alignof(AlignedCharArrayUnion<V5>)); + EXPECT_EQ(alignof(V6), alignof(AlignedCharArrayUnion<V6>)); + EXPECT_EQ(alignof(V7), alignof(AlignedCharArrayUnion<V7>)); // Some versions of MSVC get this wrong somewhat disturbingly. The failure - // appears to be benign: alignOf<V8>() produces a preposterous value: 12 + // appears to be benign: alignof(V8) produces a preposterous value: 12 #ifndef _MSC_VER - EXPECT_EQ(alignOf<V8>(), alignOf<AlignedCharArrayUnion<V8> >()); + EXPECT_EQ(alignof(V8), alignof(AlignedCharArrayUnion<V8>)); #endif EXPECT_EQ(sizeof(char), sizeof(AlignedCharArrayUnion<char>)); @@ -343,11 +235,11 @@ TEST(AlignOfTest, BasicAlignedArray) { EXPECT_EQ(sizeof(V8), sizeof(AlignedCharArrayUnion<V8>)); #endif - EXPECT_EQ(1u, (alignOf<AlignedCharArray<1, 1> >())); - EXPECT_EQ(2u, (alignOf<AlignedCharArray<2, 1> >())); - EXPECT_EQ(4u, (alignOf<AlignedCharArray<4, 1> >())); - EXPECT_EQ(8u, (alignOf<AlignedCharArray<8, 1> >())); - EXPECT_EQ(16u, (alignOf<AlignedCharArray<16, 1> >())); + EXPECT_EQ(1u, (alignof(AlignedCharArray<1, 1>))); + EXPECT_EQ(2u, (alignof(AlignedCharArray<2, 1>))); + EXPECT_EQ(4u, (alignof(AlignedCharArray<4, 1>))); + EXPECT_EQ(8u, (alignof(AlignedCharArray<8, 1>))); + EXPECT_EQ(16u, (alignof(AlignedCharArray<16, 1>))); EXPECT_EQ(1u, sizeof(AlignedCharArray<1, 1>)); EXPECT_EQ(7u, sizeof(AlignedCharArray<1, 7>)); diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt index 9a4a14450911..2ffedab82acb 100644 --- a/unittests/Support/CMakeLists.txt +++ b/unittests/Support/CMakeLists.txt @@ -9,17 +9,21 @@ add_llvm_unittest(SupportTests BlockFrequencyTest.cpp BranchProbabilityTest.cpp Casting.cpp + Chrono.cpp CommandLineTest.cpp CompressionTest.cpp ConvertUTFTest.cpp DataExtractorTest.cpp + DebugTest.cpp DwarfTest.cpp EndianStreamTest.cpp EndianTest.cpp - ErrorTest.cpp ErrorOrTest.cpp + ErrorTest.cpp FileOutputBufferTest.cpp - IteratorTest.cpp + FormatVariadicTest.cpp + GlobPatternTest.cpp + Host.cpp LEB128Test.cpp LineIteratorTest.cpp LockFileManagerTest.cpp @@ -28,6 +32,7 @@ add_llvm_unittest(SupportTests MathExtrasTest.cpp MemoryBufferTest.cpp MemoryTest.cpp + NativeFormatTests.cpp Path.cpp ProcessTest.cpp ProgramTest.cpp @@ -36,16 +41,16 @@ add_llvm_unittest(SupportTests ScaledNumberTest.cpp SourceMgrTest.cpp SpecialCaseListTest.cpp - StreamingMemoryObjectTest.cpp StringPool.cpp SwapByteOrderTest.cpp TargetParserTest.cpp + Threading.cpp ThreadLocalTest.cpp ThreadPool.cpp TimerTest.cpp - TimeValueTest.cpp TypeNameTest.cpp TrailingObjectsTest.cpp + TrigramIndexTest.cpp UnicodeTest.cpp YAMLIOTest.cpp YAMLParserTest.cpp @@ -53,6 +58,7 @@ add_llvm_unittest(SupportTests raw_ostream_test.cpp raw_pwrite_stream_test.cpp raw_sha1_ostream_test.cpp + xxhashTest.cpp ) # ManagedStatic.cpp uses <pthread>. diff --git a/unittests/Support/Chrono.cpp b/unittests/Support/Chrono.cpp new file mode 100644 index 000000000000..3d5787807563 --- /dev/null +++ b/unittests/Support/Chrono.cpp @@ -0,0 +1,79 @@ +//===- llvm/unittest/Support/Chrono.cpp - Time utilities 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/Chrono.h" +#include "llvm/ADT/SmallVector.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::sys; +using namespace std::chrono; + +namespace { + +TEST(Chrono, TimeTConversion) { + EXPECT_EQ(time_t(0), toTimeT(toTimePoint(time_t(0)))); + EXPECT_EQ(time_t(1), toTimeT(toTimePoint(time_t(1)))); + EXPECT_EQ(time_t(47), toTimeT(toTimePoint(time_t(47)))); + + TimePoint<> TP; + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); + TP += seconds(1); + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); + TP += hours(47); + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); +} + +TEST(Chrono, StringConversion) { + std::string S; + raw_string_ostream OS(S); + OS << system_clock::now(); + + // Do a basic sanity check on the output. + // The format we expect is YYYY-MM-DD HH:MM:SS.MMMUUUNNN + StringRef Date, Time; + std::tie(Date, Time) = StringRef(OS.str()).split(' '); + + SmallVector<StringRef, 3> Components; + Date.split(Components, '-'); + ASSERT_EQ(3u, Components.size()); + EXPECT_EQ(4u, Components[0].size()); + EXPECT_EQ(2u, Components[1].size()); + EXPECT_EQ(2u, Components[2].size()); + + StringRef Sec, Nano; + std::tie(Sec, Nano) = Time.split('.'); + + Components.clear(); + Sec.split(Components, ':'); + ASSERT_EQ(3u, Components.size()); + EXPECT_EQ(2u, Components[0].size()); + EXPECT_EQ(2u, Components[1].size()); + EXPECT_EQ(2u, Components[2].size()); + EXPECT_EQ(9u, Nano.size()); +} + +// Test that toTimePoint and toTimeT can be called with a arguments with varying +// precisions. +TEST(Chrono, ImplicitConversions) { + std::time_t TimeT = 47; + TimePoint<seconds> Sec = toTimePoint(TimeT); + TimePoint<milliseconds> Milli = toTimePoint(TimeT); + TimePoint<microseconds> Micro = toTimePoint(TimeT); + TimePoint<nanoseconds> Nano = toTimePoint(TimeT); + EXPECT_EQ(Sec, Milli); + EXPECT_EQ(Sec, Micro); + EXPECT_EQ(Sec, Nano); + EXPECT_EQ(TimeT, toTimeT(Sec)); + EXPECT_EQ(TimeT, toTimeT(Milli)); + EXPECT_EQ(TimeT, toTimeT(Micro)); + EXPECT_EQ(TimeT, toTimeT(Nano)); +} + +} // anonymous namespace diff --git a/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp index 9b24c1e40bca..945eb1d4e1cf 100644 --- a/unittests/Support/CommandLineTest.cpp +++ b/unittests/Support/CommandLineTest.cpp @@ -7,11 +7,15 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/StringSaver.h" #include "gtest/gtest.h" +#include <fstream> #include <stdlib.h> #include <string> @@ -76,8 +80,8 @@ public: class StackSubCommand : public cl::SubCommand { public: - StackSubCommand(const char *const Name, - const char *const Description = nullptr) + StackSubCommand(StringRef Name, + StringRef Description = StringRef()) : SubCommand(Name, Description) {} StackSubCommand() : SubCommand() {} @@ -168,7 +172,7 @@ typedef void ParserFunction(StringRef Source, StringSaver &Saver, SmallVectorImpl<const char *> &NewArgv, bool MarkEOLs); -void testCommandLineTokenizer(ParserFunction *parse, const char *Input, +void testCommandLineTokenizer(ParserFunction *parse, StringRef Input, const char *const Output[], size_t OutputSize) { SmallVector<const char *, 0> Actual; BumpPtrAllocator A; @@ -182,7 +186,7 @@ void testCommandLineTokenizer(ParserFunction *parse, const char *Input, } TEST(CommandLineTest, TokenizeGNUCommandLine) { - const char *Input = + 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[] = { @@ -193,7 +197,7 @@ TEST(CommandLineTest, TokenizeGNUCommandLine) { } TEST(CommandLineTest, TokenizeWindowsCommandLine) { - const char *Input = "a\\b c\\\\d e\\\\\"f g\" h\\\"i j\\\\\\\"k \"lmn\" o pqr " + const char Input[] = "a\\b c\\\\d e\\\\\"f g\" h\\\"i j\\\\\\\"k \"lmn\" o pqr " "\"st \\\"u\" \\v"; const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k", "lmn", "o", "pqr", "st \"u", "\\v" }; @@ -299,7 +303,7 @@ TEST(CommandLineTest, SetValueInSubcategories) { EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args[] = {"prog", "-top-level"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); EXPECT_TRUE(TopLevelOpt); EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); @@ -311,7 +315,7 @@ TEST(CommandLineTest, SetValueInSubcategories) { EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args2[] = {"prog", "sc1", "-sc1"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); EXPECT_FALSE(TopLevelOpt); EXPECT_TRUE(SC1Opt); EXPECT_FALSE(SC2Opt); @@ -323,7 +327,7 @@ TEST(CommandLineTest, SetValueInSubcategories) { EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args3[] = {"prog", "sc2", "-sc2"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), true)); EXPECT_FALSE(TopLevelOpt); EXPECT_FALSE(SC1Opt); EXPECT_TRUE(SC2Opt); @@ -339,7 +343,7 @@ TEST(CommandLineTest, LookupFailsInWrongSubCommand) { StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); const char *args[] = {"prog", "sc1", "-sc2"}; - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); } TEST(CommandLineTest, AddToAllSubCommands) { @@ -355,21 +359,21 @@ TEST(CommandLineTest, AddToAllSubCommands) { const char *args3[] = {"prog", "sc2", "-everywhere"}; EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); EXPECT_TRUE(AllOpt); AllOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); EXPECT_TRUE(AllOpt); AllOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), true)); EXPECT_TRUE(AllOpt); } @@ -382,14 +386,14 @@ TEST(CommandLineTest, ReparseCommandLineOptions) { const char *args[] = {"prog", "-top-level"}; EXPECT_FALSE(TopLevelOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); EXPECT_TRUE(TopLevelOpt); TopLevelOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(TopLevelOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); EXPECT_TRUE(TopLevelOpt); } @@ -403,13 +407,13 @@ TEST(CommandLineTest, RemoveFromRegularSubCommand) { const char *args[] = {"prog", "sc", "-remove-option"}; EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); EXPECT_TRUE(RemoveOption); RemoveOption.removeArgument(); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); } TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { @@ -423,13 +427,13 @@ TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { const char *args[] = {"prog", "-top-level-remove"}; EXPECT_FALSE(TopLevelRemove); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); EXPECT_TRUE(TopLevelRemove); TopLevelRemove.removeArgument(); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, nullptr, true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); } TEST(CommandLineTest, RemoveFromAllSubCommands) { @@ -448,32 +452,122 @@ TEST(CommandLineTest, RemoveFromAllSubCommands) { // It should work for all subcommands including the top-level. EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); EXPECT_TRUE(RemoveOption); RemoveOption = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, StringRef(), true)); EXPECT_TRUE(RemoveOption); RemoveOption = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), 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)); + EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, nullptr, true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, StringRef(), true)); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); +} + +TEST(CommandLineTest, GetRegisteredSubcommands) { + cl::ResetCommandLineParser(); + + StackSubCommand SC1("sc1", "First Subcommand"); + StackOption<bool> Opt1("opt1", cl::sub(SC1), cl::init(false)); + StackSubCommand SC2("sc2", "Second subcommand"); + StackOption<bool> Opt2("opt2", cl::sub(SC2), cl::init(false)); + + const char *args0[] = {"prog", "sc1"}; + const char *args1[] = {"prog", "sc2"}; + + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); + EXPECT_FALSE(Opt1); + EXPECT_FALSE(Opt2); + for (auto *S : cl::getRegisteredSubcommands()) { + if (*S) + EXPECT_EQ("sc1", S->getName()); + } + + cl::ResetAllOptionOccurrences(); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1, StringRef(), true)); + EXPECT_FALSE(Opt1); + EXPECT_FALSE(Opt2); + for (auto *S : cl::getRegisteredSubcommands()) { + if (*S) + EXPECT_EQ("sc2", S->getName()); + } +} + +TEST(CommandLineTest, ResponseFiles) { + llvm::SmallString<128> TestDir; + std::error_code EC = + llvm::sys::fs::createUniqueDirectory("unittest", TestDir); + EXPECT_TRUE(!EC); + + // Create included response file of first level. + llvm::SmallString<128> IncludedFileName; + llvm::sys::path::append(IncludedFileName, TestDir, "resp1"); + std::ofstream IncludedFile(IncludedFileName.c_str()); + EXPECT_TRUE(IncludedFile.is_open()); + IncludedFile << "-option_1 -option_2\n" + "@incdir/resp2\n" + "-option_3=abcd\n"; + IncludedFile.close(); + + // Directory for included file. + llvm::SmallString<128> IncDir; + llvm::sys::path::append(IncDir, TestDir, "incdir"); + EC = llvm::sys::fs::create_directory(IncDir); + EXPECT_TRUE(!EC); + + // Create included response file of second level. + llvm::SmallString<128> IncludedFileName2; + llvm::sys::path::append(IncludedFileName2, IncDir, "resp2"); + std::ofstream IncludedFile2(IncludedFileName2.c_str()); + EXPECT_TRUE(IncludedFile2.is_open()); + IncludedFile2 << "-option_21 -option_22\n"; + IncludedFile2 << "-option_23=abcd\n"; + IncludedFile2.close(); + + // Prepare 'file' with reference to response file. + SmallString<128> IncRef; + IncRef.append(1, '@'); + IncRef.append(IncludedFileName.c_str()); + llvm::SmallVector<const char *, 4> Argv = + { "test/test", "-flag_1", IncRef.c_str(), "-flag_2" }; + + // Expand response files. + llvm::BumpPtrAllocator A; + llvm::StringSaver Saver(A); + bool Res = llvm::cl::ExpandResponseFiles( + Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, true); + EXPECT_TRUE(Res); + EXPECT_EQ(Argv.size(), 9U); + EXPECT_STREQ(Argv[0], "test/test"); + EXPECT_STREQ(Argv[1], "-flag_1"); + EXPECT_STREQ(Argv[2], "-option_1"); + EXPECT_STREQ(Argv[3], "-option_2"); + EXPECT_STREQ(Argv[4], "-option_21"); + EXPECT_STREQ(Argv[5], "-option_22"); + EXPECT_STREQ(Argv[6], "-option_23=abcd"); + EXPECT_STREQ(Argv[7], "-option_3=abcd"); + EXPECT_STREQ(Argv[8], "-flag_2"); + + llvm::sys::fs::remove(IncludedFileName2); + llvm::sys::fs::remove(IncDir); + llvm::sys::fs::remove(IncludedFileName); + llvm::sys::fs::remove(TestDir); } } // anonymous namespace diff --git a/unittests/Support/DebugTest.cpp b/unittests/Support/DebugTest.cpp new file mode 100644 index 000000000000..7db91ff0d854 --- /dev/null +++ b/unittests/Support/DebugTest.cpp @@ -0,0 +1,34 @@ +//===- llvm/unittest/Support/DebugTest.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/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +#include <string> +using namespace llvm; + +#ifndef NDEBUG +TEST(DebugTest, Basic) { + std::string s1, s2; + raw_string_ostream os1(s1), os2(s2); + static const char *DT[] = {"A", "B"}; + + llvm::DebugFlag = true; + setCurrentDebugTypes(DT, 2); + DEBUG_WITH_TYPE("A", os1 << "A"); + DEBUG_WITH_TYPE("B", os1 << "B"); + EXPECT_EQ("AB", os1.str()); + + setCurrentDebugType("A"); + DEBUG_WITH_TYPE("A", os2 << "A"); + DEBUG_WITH_TYPE("B", os2 << "B"); + EXPECT_EQ("A", os2.str()); +} +#endif diff --git a/unittests/Support/DwarfTest.cpp b/unittests/Support/DwarfTest.cpp index 74fcc989b45a..148ea2736e15 100644 --- a/unittests/Support/DwarfTest.cpp +++ b/unittests/Support/DwarfTest.cpp @@ -17,13 +17,13 @@ namespace { TEST(DwarfTest, TagStringOnInvalid) { // This is invalid, so it shouldn't be stringified. - EXPECT_EQ(nullptr, TagString(DW_TAG_invalid)); + EXPECT_EQ(StringRef(), TagString(DW_TAG_invalid)); // These aren't really tags: they describe ranges within tags. They // shouldn't be stringified either. - EXPECT_EQ(nullptr, TagString(DW_TAG_lo_user)); - EXPECT_EQ(nullptr, TagString(DW_TAG_hi_user)); - EXPECT_EQ(nullptr, TagString(DW_TAG_user_base)); + EXPECT_EQ(StringRef(), TagString(DW_TAG_lo_user)); + EXPECT_EQ(StringRef(), TagString(DW_TAG_hi_user)); + EXPECT_EQ(StringRef(), TagString(DW_TAG_user_base)); } TEST(DwarfTest, getTag) { @@ -58,12 +58,12 @@ TEST(DwarfTest, getOperationEncoding) { TEST(DwarfTest, LanguageStringOnInvalid) { // This is invalid, so it shouldn't be stringified. - EXPECT_EQ(nullptr, LanguageString(0)); + EXPECT_EQ(StringRef(), LanguageString(0)); // These aren't really tags: they describe ranges within tags. They // shouldn't be stringified either. - EXPECT_EQ(nullptr, LanguageString(DW_LANG_lo_user)); - EXPECT_EQ(nullptr, LanguageString(DW_LANG_hi_user)); + EXPECT_EQ(StringRef(), LanguageString(DW_LANG_lo_user)); + EXPECT_EQ(StringRef(), LanguageString(DW_LANG_hi_user)); } TEST(DwarfTest, getLanguage) { @@ -85,12 +85,12 @@ TEST(DwarfTest, getLanguage) { TEST(DwarfTest, AttributeEncodingStringOnInvalid) { // This is invalid, so it shouldn't be stringified. - EXPECT_EQ(nullptr, AttributeEncodingString(0)); + EXPECT_EQ(StringRef(), AttributeEncodingString(0)); // These aren't really tags: they describe ranges within tags. They // shouldn't be stringified either. - EXPECT_EQ(nullptr, AttributeEncodingString(DW_ATE_lo_user)); - EXPECT_EQ(nullptr, AttributeEncodingString(DW_ATE_hi_user)); + EXPECT_EQ(StringRef(), AttributeEncodingString(DW_ATE_lo_user)); + EXPECT_EQ(StringRef(), AttributeEncodingString(DW_ATE_hi_user)); } TEST(DwarfTest, getAttributeEncoding) { @@ -122,8 +122,8 @@ TEST(DwarfTest, VirtualityString) { VirtualityString(DW_VIRTUALITY_max)); // Invalid numbers shouldn't be stringified. - EXPECT_EQ(nullptr, VirtualityString(DW_VIRTUALITY_max + 1)); - EXPECT_EQ(nullptr, VirtualityString(DW_VIRTUALITY_max + 77)); + EXPECT_EQ(StringRef(), VirtualityString(DW_VIRTUALITY_max + 1)); + EXPECT_EQ(StringRef(), VirtualityString(DW_VIRTUALITY_max + 77)); } TEST(DwarfTest, getVirtuality) { diff --git a/unittests/Support/ErrorTest.cpp b/unittests/Support/ErrorTest.cpp index 205092683744..29a173a058b6 100644 --- a/unittests/Support/ErrorTest.cpp +++ b/unittests/Support/ErrorTest.cpp @@ -82,12 +82,14 @@ protected: char CustomSubError::ID = 0; -static Error handleCustomError(const CustomError &CE) { return Error(); } +static Error handleCustomError(const CustomError &CE) { + return Error::success(); +} static void handleCustomErrorVoid(const CustomError &CE) {} static Error handleCustomErrorUP(std::unique_ptr<CustomError> CE) { - return Error(); + return Error::success(); } static void handleCustomErrorUPVoid(std::unique_ptr<CustomError> CE) {} @@ -95,21 +97,22 @@ 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; + Error E = Error::success(); EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'"; } // Test that unchecked succes values cause an abort. -#ifndef NDEBUG +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, UncheckedSuccess) { - EXPECT_DEATH({ Error E; }, "Program aborted due to an unhandled Error:") + EXPECT_DEATH({ Error E = Error::success(); }, + "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); + 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 @@ -119,15 +122,15 @@ void errAsOutParamHelper(Error &Err) { // Test that ErrorAsOutParameter sets the checked flag on construction. TEST(Error, ErrorAsOutParameterChecked) { - Error E; + Error E = Error::success(); errAsOutParamHelper(E); (void)!!E; } // Test that ErrorAsOutParameter clears the checked flag on destruction. -#ifndef NDEBUG +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, ErrorAsOutParameterUnchecked) { - EXPECT_DEATH({ Error E; errAsOutParamHelper(E); }, + EXPECT_DEATH({ Error E = Error::success(); errAsOutParamHelper(E); }, "Program aborted due to an unhandled Error:") << "ErrorAsOutParameter did not clear the checked flag on destruction."; } @@ -136,7 +139,7 @@ TEST(Error, ErrorAsOutParameterUnchecked) { // 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 +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, UncheckedError) { auto DropUnhandledError = []() { Error E = make_error<CustomError>(42); @@ -195,31 +198,31 @@ TEST(Error, HandlerTypeDeduction) { handleAllErrors( make_error<CustomError>(42), - [](const CustomError &CE) mutable { return Error::success(); }); + [](const CustomError &CE) mutable -> Error { return Error::success(); }); handleAllErrors(make_error<CustomError>(42), [](const CustomError &CE) mutable {}); handleAllErrors(make_error<CustomError>(42), - [](CustomError &CE) { return Error::success(); }); + [](CustomError &CE) -> Error { return Error::success(); }); handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) {}); handleAllErrors(make_error<CustomError>(42), - [](CustomError &CE) mutable { return Error::success(); }); + [](CustomError &CE) mutable -> Error { return Error::success(); }); handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) mutable {}); handleAllErrors( make_error<CustomError>(42), - [](std::unique_ptr<CustomError> CE) { return Error::success(); }); + [](std::unique_ptr<CustomError> CE) -> Error { 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(); }); + [](std::unique_ptr<CustomError> CE) mutable -> Error { return Error::success(); }); handleAllErrors(make_error<CustomError>(42), [](std::unique_ptr<CustomError> CE) mutable {}); @@ -363,7 +366,7 @@ TEST(Error, CheckJoinErrors) { // Test that we can consume success values. TEST(Error, ConsumeSuccess) { - Error E; + Error E = Error::success(); consumeError(std::move(E)); } @@ -374,7 +377,7 @@ TEST(Error, ConsumeError) { // Test that handleAllUnhandledErrors crashes if an error is not caught. // Test runs in debug mode only. -#ifndef NDEBUG +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, FailureToHandle) { auto FailToHandle = []() { handleAllErrors(make_error<CustomError>(7), [&](const CustomSubError &SE) { @@ -392,7 +395,7 @@ TEST(Error, FailureToHandle) { // Test that handleAllUnhandledErrors crashes if an error is returned from a // handler. // Test runs in debug mode only. -#ifndef NDEBUG +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, FailureFromHandler) { auto ReturnErrorFromHandler = []() { handleAllErrors(make_error<CustomError>(7), @@ -487,7 +490,7 @@ TEST(Error, ExpectedWithReferenceType) { // 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 +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, UncheckedExpectedInSuccessModeDestruction) { EXPECT_DEATH({ Expected<int> A = 7; }, "Expected<T> must be checked before access or destruction.") @@ -498,7 +501,7 @@ TEST(Error, UncheckedExpectedInSuccessModeDestruction) { // 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 +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, UncheckedExpectedInSuccessModeAccess) { EXPECT_DEATH({ Expected<int> A = 7; *A; }, "Expected<T> must be checked before access or destruction.") @@ -509,7 +512,7 @@ TEST(Error, UncheckedExpectedInSuccessModeAccess) { // 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 +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, UncheckedExpectedInSuccessModeAssignment) { EXPECT_DEATH({ Expected<int> A = 7; A = 7; }, "Expected<T> must be checked before access or destruction.") @@ -529,7 +532,7 @@ TEST(Error, ExpectedInFailureMode) { // Check that an Expected instance with an error value doesn't allow access to // operator*. // Test runs in debug mode only. -#ifndef NDEBUG +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, AccessExpectedInFailureMode) { Expected<int> A = make_error<CustomError>(42); EXPECT_DEATH(*A, "Expected<T> must be checked before access or destruction.") @@ -541,7 +544,7 @@ TEST(Error, AccessExpectedInFailureMode) { // Check that an Expected instance with an error triggers an abort if // unhandled. // Test runs in debug mode only. -#ifndef NDEBUG +#if LLVM_ENABLE_ABI_BREAKING_CHECKS TEST(Error, UnhandledExpectedInFailureMode) { EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); }, "Expected<T> must be checked before access or destruction.") diff --git a/unittests/Support/FileOutputBufferTest.cpp b/unittests/Support/FileOutputBufferTest.cpp index 090c476e35cf..53a2ae0aadde 100644 --- a/unittests/Support/FileOutputBufferTest.cpp +++ b/unittests/Support/FileOutputBufferTest.cpp @@ -20,9 +20,12 @@ using namespace llvm::sys; #define ASSERT_NO_ERROR(x) \ if (std::error_code ASSERT_NO_ERROR_ec = x) { \ - errs() << #x ": did not return errc::success.\n" \ - << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \ - << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \ + SmallString<128> MessageStorage; \ + raw_svector_ostream Message(MessageStorage); \ + Message << #x ": did not return errc::success.\n" \ + << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \ + << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \ + GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \ } else { \ } @@ -57,9 +60,9 @@ TEST(FileOutputBuffer, Test) { ASSERT_EQ(File1Size, 8192ULL); ASSERT_NO_ERROR(fs::remove(File1.str())); - // TEST 2: Verify abort case. + // TEST 2: Verify abort case. SmallString<128> File2(TestDirectory); - File2.append("/file2"); + File2.append("/file2"); { ErrorOr<std::unique_ptr<FileOutputBuffer>> Buffer2OrErr = FileOutputBuffer::create(File2, 8192); diff --git a/unittests/Support/FormatVariadicTest.cpp b/unittests/Support/FormatVariadicTest.cpp new file mode 100644 index 000000000000..9307c6d8e09b --- /dev/null +++ b/unittests/Support/FormatVariadicTest.cpp @@ -0,0 +1,570 @@ +//===- FormatVariadicTest.cpp - Unit tests for string formatting ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include "gtest/gtest.h" + +using namespace llvm; + +// Compile-time tests templates in the detail namespace. +namespace { +struct Format : public FormatAdapter<int> { + Format(int N) : FormatAdapter<int>(std::move(N)) {} + void format(raw_ostream &OS, StringRef Opt) override { OS << "Format"; } +}; + +using detail::uses_format_member; +using detail::uses_missing_provider; + +static_assert(uses_format_member<Format>::value, ""); +static_assert(uses_format_member<Format &>::value, ""); +static_assert(uses_format_member<Format &&>::value, ""); +static_assert(uses_format_member<const Format>::value, ""); +static_assert(uses_format_member<const Format &>::value, ""); +static_assert(uses_format_member<const volatile Format>::value, ""); +static_assert(uses_format_member<const volatile Format &>::value, ""); + +struct NoFormat {}; +static_assert(uses_missing_provider<NoFormat>::value, ""); +} + +TEST(FormatVariadicTest, EmptyFormatString) { + auto Replacements = formatv_object_base::parseFormatString(""); + EXPECT_EQ(0U, Replacements.size()); +} + +TEST(FormatVariadicTest, NoReplacements) { + const StringRef kFormatString = "This is a test"; + auto Replacements = formatv_object_base::parseFormatString(kFormatString); + ASSERT_EQ(1U, Replacements.size()); + EXPECT_EQ(kFormatString, Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); +} + +TEST(FormatVariadicTest, EscapedBrace) { + // {{ should be replaced with { + auto Replacements = formatv_object_base::parseFormatString("{{"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // An even number N of braces should be replaced with N/2 braces. + Replacements = formatv_object_base::parseFormatString("{{{{{{"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{{{", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); +} + +TEST(FormatVariadicTest, ValidReplacementSequence) { + // 1. Simple replacement - parameter index only + auto Replacements = formatv_object_base::parseFormatString("{0}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0u, Replacements[0].Align); + EXPECT_EQ("", Replacements[0].Options); + + Replacements = formatv_object_base::parseFormatString("{1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(1u, Replacements[0].Index); + EXPECT_EQ(0u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 2. Parameter index with right alignment + Replacements = formatv_object_base::parseFormatString("{0,3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 3. And left alignment + Replacements = formatv_object_base::parseFormatString("{0,-3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 4. And center alignment + Replacements = formatv_object_base::parseFormatString("{0,=3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Center, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 4. Parameter index with option string + Replacements = formatv_object_base::parseFormatString("{0:foo}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("foo", Replacements[0].Options); + + // 5. Parameter index with alignment before option string + Replacements = formatv_object_base::parseFormatString("{0,-3:foo}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[0].Where); + EXPECT_EQ("foo", Replacements[0].Options); + + // 7. Parameter indices, options, and alignment can all have whitespace. + Replacements = formatv_object_base::parseFormatString("{ 0, -3 : foo }"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[0].Where); + EXPECT_EQ("foo", Replacements[0].Options); + + // 8. Everything after the first option specifier is part of the style, even + // if it contains another option specifier. + Replacements = formatv_object_base::parseFormatString("{0:0:1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("0:0:1", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("0:1", Replacements[0].Options); +} + +TEST(FormatVariadicTest, DefaultReplacementValues) { + // 2. If options string is missing, it defaults to empty. + auto Replacements = formatv_object_base::parseFormatString("{0,3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3u, Replacements[0].Align); + EXPECT_EQ("", Replacements[0].Options); + + // Including if the colon is present but contains no text. + Replacements = formatv_object_base::parseFormatString("{0,3:}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3u, Replacements[0].Align); + EXPECT_EQ("", Replacements[0].Options); + + // 3. If alignment is missing, it defaults to 0, right, space + Replacements = formatv_object_base::parseFormatString("{0:foo}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ(' ', Replacements[0].Pad); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0u, Replacements[0].Align); + EXPECT_EQ("foo", Replacements[0].Options); +} + +TEST(FormatVariadicTest, MultipleReplacements) { + auto Replacements = + formatv_object_base::parseFormatString("{0} {1:foo}-{2,-3:bar}"); + ASSERT_EQ(5u, Replacements.size()); + // {0} + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0u, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // " " + EXPECT_EQ(ReplacementType::Literal, Replacements[1].Type); + EXPECT_EQ(" ", Replacements[1].Spec); + + // {1:foo} - Options=foo + EXPECT_EQ(ReplacementType::Format, Replacements[2].Type); + EXPECT_EQ(1u, Replacements[2].Index); + EXPECT_EQ(0u, Replacements[2].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[2].Where); + EXPECT_EQ("foo", Replacements[2].Options); + + // "-" + EXPECT_EQ(ReplacementType::Literal, Replacements[3].Type); + EXPECT_EQ("-", Replacements[3].Spec); + + // {2:bar,-3} - Options=bar, Align=-3 + EXPECT_EQ(ReplacementType::Format, Replacements[4].Type); + EXPECT_EQ(2u, Replacements[4].Index); + EXPECT_EQ(3u, Replacements[4].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[4].Where); + EXPECT_EQ("bar", Replacements[4].Options); +} + +TEST(FormatVariadicTest, FormatNoReplacements) { + EXPECT_EQ("", formatv("").str()); + EXPECT_EQ("Test", formatv("Test").str()); +} + +TEST(FormatVariadicTest, FormatBasicTypesOneReplacement) { + EXPECT_EQ("1", formatv("{0}", 1).str()); + EXPECT_EQ("c", formatv("{0}", 'c').str()); + EXPECT_EQ("-3", formatv("{0}", -3).str()); + EXPECT_EQ("Test", formatv("{0}", "Test").str()); + EXPECT_EQ("Test2", formatv("{0}", StringRef("Test2")).str()); + EXPECT_EQ("Test3", formatv("{0}", std::string("Test3")).str()); +} + +TEST(FormatVariadicTest, IntegralHexFormatting) { + // 1. Trivial cases. Make sure hex is not the default. + EXPECT_EQ("0", formatv("{0}", 0).str()); + EXPECT_EQ("2748", formatv("{0}", 0xABC).str()); + EXPECT_EQ("-2748", formatv("{0}", -0xABC).str()); + + // 3. various hex prefixes. + EXPECT_EQ("0xFF", formatv("{0:X}", 255).str()); + EXPECT_EQ("0xFF", formatv("{0:X+}", 255).str()); + EXPECT_EQ("0xff", formatv("{0:x}", 255).str()); + EXPECT_EQ("0xff", formatv("{0:x+}", 255).str()); + EXPECT_EQ("FF", formatv("{0:X-}", 255).str()); + EXPECT_EQ("ff", formatv("{0:x-}", 255).str()); + + // 5. Precision pads left of the most significant digit but right of the + // prefix (if one exists). + EXPECT_EQ("0xFF", formatv("{0:X2}", 255).str()); + EXPECT_EQ("0xFF", formatv("{0:X+2}", 255).str()); + EXPECT_EQ("0x0ff", formatv("{0:x3}", 255).str()); + EXPECT_EQ("0x0ff", formatv("{0:x+3}", 255).str()); + EXPECT_EQ("00FF", formatv("{0:X-4}", 255).str()); + EXPECT_EQ("00ff", formatv("{0:x-4}", 255).str()); + + // 6. Try some larger types. + EXPECT_EQ("0xDEADBEEFDEADBEEF", + formatv("{0:X16}", -2401053088876216593LL).str()); + EXPECT_EQ("0xFEEBDAEDFEEBDAED", + formatv("{0:X16}", 0xFEEBDAEDFEEBDAEDULL).str()); + EXPECT_EQ("0x00000000DEADBEEF", formatv("{0:X16}", 0xDEADBEEF).str()); + + // 7. Padding should take into account the prefix + EXPECT_EQ("0xff", formatv("{0,4:x}", 255).str()); + EXPECT_EQ(" 0xff", formatv("{0,5:x+}", 255).str()); + EXPECT_EQ(" FF", formatv("{0,4:X-}", 255).str()); + EXPECT_EQ(" ff", formatv("{0,5:x-}", 255).str()); + + // 8. Including when it's been zero-padded + EXPECT_EQ(" 0x0ff", formatv("{0,7:x3}", 255).str()); + EXPECT_EQ(" 0x00ff", formatv("{0,7:x+4}", 255).str()); + EXPECT_EQ(" 000FF", formatv("{0,7:X-5}", 255).str()); + EXPECT_EQ(" 0000ff", formatv("{0,7:x-6}", 255).str()); + + // 9. Precision with default format specifier should work too + EXPECT_EQ(" 255", formatv("{0,7:3}", 255).str()); + EXPECT_EQ(" 0255", formatv("{0,7:4}", 255).str()); + EXPECT_EQ(" 00255", formatv("{0,7:5}", 255).str()); + EXPECT_EQ(" 000255", formatv("{0,7:6}", 255).str()); +} + +TEST(FormatVariadicTest, PointerFormatting) { + // 1. Trivial cases. Hex is default. Default Precision is pointer width. + if (sizeof(void *) == 4) { + EXPECT_EQ("0x00000000", formatv("{0}", (void *)0).str()); + EXPECT_EQ("0x00000ABC", formatv("{0}", (void *)0xABC).str()); + } else { + EXPECT_EQ("0x0000000000000000", formatv("{0}", (void *)0).str()); + EXPECT_EQ("0x0000000000000ABC", formatv("{0}", (void *)0xABC).str()); + } + + // 2. But we can reduce the precision explicitly. + EXPECT_EQ("0x0", formatv("{0:0}", (void *)0).str()); + EXPECT_EQ("0xABC", formatv("{0:0}", (void *)0xABC).str()); + EXPECT_EQ("0x0000", formatv("{0:4}", (void *)0).str()); + EXPECT_EQ("0x0ABC", formatv("{0:4}", (void *)0xABC).str()); + + // 3. various hex prefixes. + EXPECT_EQ("0x0ABC", formatv("{0:X4}", (void *)0xABC).str()); + EXPECT_EQ("0x0abc", formatv("{0:x4}", (void *)0xABC).str()); + EXPECT_EQ("0ABC", formatv("{0:X-4}", (void *)0xABC).str()); + EXPECT_EQ("0abc", formatv("{0:x-4}", (void *)0xABC).str()); +} + +TEST(FormatVariadicTest, IntegralNumberFormatting) { + // 1. Test comma grouping with default widths and precisions. + EXPECT_EQ("0", formatv("{0:N}", 0).str()); + EXPECT_EQ("10", formatv("{0:N}", 10).str()); + EXPECT_EQ("100", formatv("{0:N}", 100).str()); + EXPECT_EQ("1,000", formatv("{0:N}", 1000).str()); + EXPECT_EQ("1,234,567,890", formatv("{0:N}", 1234567890).str()); + EXPECT_EQ("-10", formatv("{0:N}", -10).str()); + EXPECT_EQ("-100", formatv("{0:N}", -100).str()); + EXPECT_EQ("-1,000", formatv("{0:N}", -1000).str()); + EXPECT_EQ("-1,234,567,890", formatv("{0:N}", -1234567890).str()); + + // 2. If there is no comma, width and precision pad to the same absolute + // size. + EXPECT_EQ(" 1", formatv("{0,2:N}", 1).str()); + + // 3. But if there is a comma or negative sign, width factors them in but + // precision doesn't. + EXPECT_EQ(" 1,000", formatv("{0,6:N}", 1000).str()); + EXPECT_EQ(" -1,000", formatv("{0,7:N}", -1000).str()); + + // 4. Large widths all line up. + EXPECT_EQ(" 1,000", formatv("{0,11:N}", 1000).str()); + EXPECT_EQ(" -1,000", formatv("{0,11:N}", -1000).str()); + EXPECT_EQ(" -100,000", formatv("{0,11:N}", -100000).str()); +} + +TEST(FormatVariadicTest, StringFormatting) { + const char FooArray[] = "FooArray"; + const char *FooPtr = "FooPtr"; + llvm::StringRef FooRef("FooRef"); + std::string FooString("FooString"); + // 1. Test that we can print various types of strings. + EXPECT_EQ(FooArray, formatv("{0}", FooArray).str()); + EXPECT_EQ(FooPtr, formatv("{0}", FooPtr).str()); + EXPECT_EQ(FooRef, formatv("{0}", FooRef).str()); + EXPECT_EQ(FooString, formatv("{0}", FooString).str()); + + // 2. Test that the precision specifier prints the correct number of + // characters. + EXPECT_EQ("FooA", formatv("{0:4}", FooArray).str()); + EXPECT_EQ("FooP", formatv("{0:4}", FooPtr).str()); + EXPECT_EQ("FooR", formatv("{0:4}", FooRef).str()); + EXPECT_EQ("FooS", formatv("{0:4}", FooString).str()); + + // 3. And that padding works. + EXPECT_EQ(" FooA", formatv("{0,6:4}", FooArray).str()); + EXPECT_EQ(" FooP", formatv("{0,6:4}", FooPtr).str()); + EXPECT_EQ(" FooR", formatv("{0,6:4}", FooRef).str()); + EXPECT_EQ(" FooS", formatv("{0,6:4}", FooString).str()); +} + +TEST(FormatVariadicTest, CharFormatting) { + // 1. Not much to see here. Just print a char with and without padding. + EXPECT_EQ("C", formatv("{0}", 'C').str()); + EXPECT_EQ(" C", formatv("{0,3}", 'C').str()); + + // 2. char is really an integral type though, where the only difference is + // that the "default" is to print the ASCII. So if a non-default presentation + // specifier exists, it should print as an integer. + EXPECT_EQ("37", formatv("{0:D}", (char)37).str()); + EXPECT_EQ(" 037", formatv("{0,5:D3}", (char)37).str()); +} + +TEST(FormatVariadicTest, BoolTest) { + // 1. Default style is lowercase text (same as 't') + EXPECT_EQ("true", formatv("{0}", true).str()); + EXPECT_EQ("false", formatv("{0}", false).str()); + EXPECT_EQ("true", formatv("{0:t}", true).str()); + EXPECT_EQ("false", formatv("{0:t}", false).str()); + + // 2. T - uppercase text + EXPECT_EQ("TRUE", formatv("{0:T}", true).str()); + EXPECT_EQ("FALSE", formatv("{0:T}", false).str()); + + // 3. D / d - integral + EXPECT_EQ("1", formatv("{0:D}", true).str()); + EXPECT_EQ("0", formatv("{0:D}", false).str()); + EXPECT_EQ("1", formatv("{0:d}", true).str()); + EXPECT_EQ("0", formatv("{0:d}", false).str()); + + // 4. Y - uppercase yes/no + EXPECT_EQ("YES", formatv("{0:Y}", true).str()); + EXPECT_EQ("NO", formatv("{0:Y}", false).str()); + + // 5. y - lowercase yes/no + EXPECT_EQ("yes", formatv("{0:y}", true).str()); + EXPECT_EQ("no", formatv("{0:y}", false).str()); +} + +TEST(FormatVariadicTest, DoubleFormatting) { + // Test exponents, fixed point, and percent formatting. + + // 1. Signed, unsigned, and zero exponent format. + EXPECT_EQ("0.000000E+00", formatv("{0:E}", 0.0).str()); + EXPECT_EQ("-0.000000E+00", formatv("{0:E}", -0.0).str()); + EXPECT_EQ("1.100000E+00", formatv("{0:E}", 1.1).str()); + EXPECT_EQ("-1.100000E+00", formatv("{0:E}", -1.1).str()); + EXPECT_EQ("1.234568E+03", formatv("{0:E}", 1234.5678).str()); + EXPECT_EQ("-1.234568E+03", formatv("{0:E}", -1234.5678).str()); + EXPECT_EQ("1.234568E-03", formatv("{0:E}", .0012345678).str()); + EXPECT_EQ("-1.234568E-03", formatv("{0:E}", -.0012345678).str()); + + // 2. With padding and precision. + EXPECT_EQ(" 0.000E+00", formatv("{0,11:E3}", 0.0).str()); + EXPECT_EQ(" -1.100E+00", formatv("{0,11:E3}", -1.1).str()); + EXPECT_EQ(" 1.235E+03", formatv("{0,11:E3}", 1234.5678).str()); + EXPECT_EQ(" -1.235E-03", formatv("{0,11:E3}", -.0012345678).str()); + + // 3. Signed, unsigned, and zero fixed point format. + EXPECT_EQ("0.00", formatv("{0:F}", 0.0).str()); + EXPECT_EQ("-0.00", formatv("{0:F}", -0.0).str()); + EXPECT_EQ("1.10", formatv("{0:F}", 1.1).str()); + EXPECT_EQ("-1.10", formatv("{0:F}", -1.1).str()); + EXPECT_EQ("1234.57", formatv("{0:F}", 1234.5678).str()); + EXPECT_EQ("-1234.57", formatv("{0:F}", -1234.5678).str()); + EXPECT_EQ("0.00", formatv("{0:F}", .0012345678).str()); + EXPECT_EQ("-0.00", formatv("{0:F}", -.0012345678).str()); + + // 2. With padding and precision. + EXPECT_EQ(" 0.000", formatv("{0,8:F3}", 0.0).str()); + EXPECT_EQ(" -1.100", formatv("{0,8:F3}", -1.1).str()); + EXPECT_EQ("1234.568", formatv("{0,8:F3}", 1234.5678).str()); + EXPECT_EQ(" -0.001", formatv("{0,8:F3}", -.0012345678).str()); +} + +struct format_tuple { + const char *Fmt; + explicit format_tuple(const char *Fmt) : Fmt(Fmt) {} + + template <typename... Ts> + auto operator()(Ts &&... Values) const + -> decltype(formatv(Fmt, std::forward<Ts>(Values)...)) { + return formatv(Fmt, std::forward<Ts>(Values)...); + } +}; + +TEST(FormatVariadicTest, BigTest) { + using Tuple = + std::tuple<char, int, const char *, StringRef, std::string, double, float, + void *, int, double, int64_t, uint64_t, double, uint8_t>; + Tuple Ts[] = { + Tuple('a', 1, "Str", StringRef(), std::string(), 3.14159, -.17532f, + (void *)nullptr, 123456, 6.02E23, -908234908423, 908234908422234, + std::numeric_limits<double>::quiet_NaN(), 0xAB), + Tuple('x', 0xDDB5B, "LongerStr", "StringRef", "std::string", -2.7, + .08215f, (void *)nullptr, 0, 6.62E-34, -908234908423, + 908234908422234, std::numeric_limits<double>::infinity(), 0x0)}; + // Test long string formatting with many edge cases combined. + const char *Intro = + "There are {{{0}} items in the tuple, and {{{1}} tuple(s) in the array."; + const char *Header = + "{0,6}|{1,8}|{2,=10}|{3,=10}|{4,=13}|{5,7}|{6,7}|{7,10}|{8," + "-7}|{9,10}|{10,16}|{11,17}|{12,6}|{13,4}"; + const char *Line = + "{0,6}|{1,8:X}|{2,=10}|{3,=10:5}|{4,=13}|{5,7:3}|{6,7:P2}|{7," + "10:X8}|{8,-7:N}|{9,10:E4}|{10,16:N}|{11,17:D}|{12,6}|{13," + "4:X}"; + + std::string S; + llvm::raw_string_ostream Stream(S); + Stream << formatv(Intro, std::tuple_size<Tuple>::value, + llvm::array_lengthof(Ts)) + << "\n"; + Stream << formatv(Header, "Char", "HexInt", "Str", "Ref", "std::str", + "double", "float", "pointer", "comma", "exp", "bigint", + "bigint2", "limit", "byte") + << "\n"; + for (auto &Item : Ts) { + Stream << llvm::apply_tuple(format_tuple(Line), Item) << "\n"; + } + Stream.flush(); + const char *Expected = + R"foo(There are {14} items in the tuple, and {2} tuple(s) in the array. + Char| HexInt| Str | Ref | std::str | double| float| pointer|comma | exp| bigint| bigint2| limit|byte + a| 0x1| Str | | | 3.142|-17.53%|0x00000000|123,456|6.0200E+23|-908,234,908,423| 908234908422234| nan|0xAB + x| 0xDDB5B|LongerStr | Strin | std::string | -2.700| 8.21%|0x00000000|0 |6.6200E-34|-908,234,908,423| 908234908422234| INF| 0x0 +)foo"; + + EXPECT_EQ(Expected, S); +} + +TEST(FormatVariadicTest, Range) { + std::vector<int> IntRange = {1, 1, 2, 3, 5, 8, 13}; + + // 1. Simple range with default separator and element style. + EXPECT_EQ("1, 1, 2, 3, 5, 8, 13", + formatv("{0}", make_range(IntRange.begin(), IntRange.end())).str()); + EXPECT_EQ("1, 2, 3, 5, 8", + formatv("{0}", make_range(IntRange.begin() + 1, IntRange.end() - 1)) + .str()); + + // 2. Non-default separator + EXPECT_EQ( + "1/1/2/3/5/8/13", + formatv("{0:$[/]}", make_range(IntRange.begin(), IntRange.end())).str()); + + // 3. Default separator, non-default element style. + EXPECT_EQ( + "0x1, 0x1, 0x2, 0x3, 0x5, 0x8, 0xd", + formatv("{0:@[x]}", make_range(IntRange.begin(), IntRange.end())).str()); + + // 4. Non-default separator and element style. + EXPECT_EQ( + "0x1 + 0x1 + 0x2 + 0x3 + 0x5 + 0x8 + 0xd", + formatv("{0:$[ + ]@[x]}", make_range(IntRange.begin(), IntRange.end())) + .str()); + + // 5. Element style and/or separator using alternate delimeters to allow using + // delimeter characters as part of the separator. + EXPECT_EQ( + "<0x1><0x1><0x2><0x3><0x5><0x8><0xd>", + formatv("<{0:$[><]@(x)}>", make_range(IntRange.begin(), IntRange.end())) + .str()); + EXPECT_EQ( + "[0x1][0x1][0x2][0x3][0x5][0x8][0xd]", + formatv("[{0:$(][)@[x]}]", make_range(IntRange.begin(), IntRange.end())) + .str()); + EXPECT_EQ( + "(0x1)(0x1)(0x2)(0x3)(0x5)(0x8)(0xd)", + formatv("({0:$<)(>@<x>})", make_range(IntRange.begin(), IntRange.end())) + .str()); + + // 5. Empty range. + EXPECT_EQ("", formatv("{0:$[+]@[x]}", + make_range(IntRange.begin(), IntRange.begin())) + .str()); + + // 6. Empty separator and style. + EXPECT_EQ("11235813", + formatv("{0:$[]@<>}", make_range(IntRange.begin(), IntRange.end())) + .str()); +} + +TEST(FormatVariadicTest, Adapter) { + class Negative : public FormatAdapter<int> { + public: + explicit Negative(int N) : FormatAdapter<int>(std::move(N)) {} + void format(raw_ostream &S, StringRef Options) override { S << -Item; } + }; + + EXPECT_EQ("-7", formatv("{0}", Negative(7)).str()); + + int N = 171; + + EXPECT_EQ(" 171 ", + formatv("{0}", fmt_align(N, AlignStyle::Center, 7)).str()); + EXPECT_EQ(" 171 ", formatv("{0}", fmt_pad(N, 1, 3)).str()); + EXPECT_EQ("171171171171171", formatv("{0}", fmt_repeat(N, 5)).str()); + + EXPECT_EQ(" ABABABABAB ", + formatv("{0:X-}", fmt_pad(fmt_repeat(N, 5), 1, 3)).str()); + EXPECT_EQ(" AB AB AB AB AB ", + formatv("{0,=34:X-}", fmt_repeat(fmt_pad(N, 1, 3), 5)).str()); +} + +TEST(FormatVariadicTest, ImplicitConversions) { + std::string S = formatv("{0} {1}", 1, 2); + EXPECT_EQ("1 2", S); + + SmallString<4> S2 = formatv("{0} {1}", 1, 2); + EXPECT_EQ("1 2", S2); +} + +TEST(FormatVariadicTest, FormatAdapter) { + EXPECT_EQ("Format", formatv("{0}", Format(1)).str()); + + Format var(1); + EXPECT_EQ("Format", formatv("{0}", var).str()); + EXPECT_EQ("Format", formatv("{0}", std::move(var)).str()); + + // Not supposed to compile + // const Format cvar(1); + // EXPECT_EQ("Format", formatv("{0}", cvar).str()); +} diff --git a/unittests/Support/GlobPatternTest.cpp b/unittests/Support/GlobPatternTest.cpp new file mode 100644 index 000000000000..44d77266db1f --- /dev/null +++ b/unittests/Support/GlobPatternTest.cpp @@ -0,0 +1,70 @@ +//===- llvm/unittest/Support/GlobPatternTest.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/GlobPattern.h" +#include "gtest/gtest.h" + +using namespace llvm; +namespace { + +class GlobPatternTest : public ::testing::Test {}; + +TEST_F(GlobPatternTest, Basics) { + Expected<GlobPattern> Pat1 = GlobPattern::create(""); + EXPECT_TRUE((bool)Pat1); + EXPECT_TRUE(Pat1->match("")); + EXPECT_FALSE(Pat1->match("a")); + + Expected<GlobPattern> Pat2 = GlobPattern::create("ab*c*def"); + EXPECT_TRUE((bool)Pat2); + EXPECT_TRUE(Pat2->match("abcdef")); + EXPECT_TRUE(Pat2->match("abxcxdef")); + EXPECT_FALSE(Pat2->match("")); + EXPECT_FALSE(Pat2->match("xabcdef")); + EXPECT_FALSE(Pat2->match("abcdefx")); + + Expected<GlobPattern> Pat3 = GlobPattern::create("a??c"); + EXPECT_TRUE((bool)Pat3); + EXPECT_TRUE(Pat3->match("axxc")); + EXPECT_FALSE(Pat3->match("axxx")); + EXPECT_FALSE(Pat3->match("")); + + Expected<GlobPattern> Pat4 = GlobPattern::create("[abc-fy-z]"); + EXPECT_TRUE((bool)Pat4); + EXPECT_TRUE(Pat4->match("a")); + EXPECT_TRUE(Pat4->match("b")); + EXPECT_TRUE(Pat4->match("c")); + EXPECT_TRUE(Pat4->match("d")); + EXPECT_TRUE(Pat4->match("e")); + EXPECT_TRUE(Pat4->match("f")); + EXPECT_TRUE(Pat4->match("y")); + EXPECT_TRUE(Pat4->match("z")); + EXPECT_FALSE(Pat4->match("g")); + EXPECT_FALSE(Pat4->match("")); + + Expected<GlobPattern> Pat5 = GlobPattern::create("[^abc-fy-z]"); + EXPECT_TRUE((bool)Pat5); + EXPECT_TRUE(Pat5->match("g")); + EXPECT_FALSE(Pat5->match("a")); + EXPECT_FALSE(Pat5->match("b")); + EXPECT_FALSE(Pat5->match("c")); + EXPECT_FALSE(Pat5->match("d")); + EXPECT_FALSE(Pat5->match("e")); + EXPECT_FALSE(Pat5->match("f")); + EXPECT_FALSE(Pat5->match("y")); + EXPECT_FALSE(Pat5->match("z")); + EXPECT_FALSE(Pat5->match("")); +} + +TEST_F(GlobPatternTest, Invalid) { + Expected<GlobPattern> Pat1 = GlobPattern::create("["); + EXPECT_FALSE((bool)Pat1); + handleAllErrors(Pat1.takeError(), [&](ErrorInfoBase &EIB) {}); +} +} diff --git a/unittests/Support/Host.cpp b/unittests/Support/Host.cpp new file mode 100644 index 000000000000..934a60495427 --- /dev/null +++ b/unittests/Support/Host.cpp @@ -0,0 +1,48 @@ +//========- unittests/Support/Host.cpp - Host.cpp 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/Host.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +class HostTest : public testing::Test { + Triple Host; + SmallVector<std::pair<Triple::ArchType, Triple::OSType>, 4> SupportedArchAndOSs; + +protected: + bool isSupportedArchAndOS() { + if (is_contained(SupportedArchAndOSs, std::make_pair(Host.getArch(), Host.getOS()))) + return true; + + return false; + } + + HostTest() { + Host.setTriple(Triple::normalize(sys::getProcessTriple())); + + // Initially this is only testing detection of the number of + // physical cores, which is currently only supported/tested for + // x86_64 Linux and Darwin. + SupportedArchAndOSs.push_back(std::make_pair(Triple::x86_64, Triple::Linux)); + SupportedArchAndOSs.push_back(std::make_pair(Triple::x86_64, Triple::Darwin)); + } +}; + +TEST_F(HostTest, NumPhysicalCores) { + int Num = sys::getHostNumPhysicalCores(); + + if (isSupportedArchAndOS()) + ASSERT_GT(Num, 0); + else + ASSERT_EQ(Num, -1); +} diff --git a/unittests/Support/MD5Test.cpp b/unittests/Support/MD5Test.cpp index c4fa5cd92c10..4d790254503e 100644 --- a/unittests/Support/MD5Test.cpp +++ b/unittests/Support/MD5Test.cpp @@ -57,4 +57,14 @@ TEST(MD5Test, MD5) { "81948d1f1554f58cd1a56ebb01f808cb"); TestMD5Sum("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"); } + +TEST(MD5HashTest, MD5) { + ArrayRef<uint8_t> Input((const uint8_t *)"abcdefghijklmnopqrstuvwxyz", 26); + std::array<uint8_t, 16> Vec = MD5::hash(Input); + MD5::MD5Result MD5Res; + SmallString<32> Res; + memcpy(MD5Res, Vec.data(), Vec.size()); + MD5::stringifyResult(MD5Res, Res); + EXPECT_EQ(Res, "c3fcd3d76192e4007dfb496cca67e13b"); +} } diff --git a/unittests/Support/MathExtrasTest.cpp b/unittests/Support/MathExtrasTest.cpp index d373030881ec..b2c377978874 100644 --- a/unittests/Support/MathExtrasTest.cpp +++ b/unittests/Support/MathExtrasTest.cpp @@ -165,6 +165,18 @@ TEST(MathExtras, isPowerOf2_64) { EXPECT_FALSE(isPowerOf2_64(0xABCDEF0ABCDEF0LL)); } +TEST(MathExtras, PowerOf2Ceil) { + EXPECT_EQ(0U, PowerOf2Ceil(0U)); + EXPECT_EQ(8U, PowerOf2Ceil(8U)); + EXPECT_EQ(8U, PowerOf2Ceil(7U)); +} + +TEST(MathExtras, PowerOf2Floor) { + EXPECT_EQ(0U, PowerOf2Floor(0U)); + EXPECT_EQ(8U, PowerOf2Floor(8U)); + EXPECT_EQ(4U, PowerOf2Floor(7U)); +} + TEST(MathExtras, ByteSwap_32) { EXPECT_EQ(0x44332211u, ByteSwap_32(0x11223344)); EXPECT_EQ(0xDDCCBBAAu, ByteSwap_32(0xAABBCCDD)); diff --git a/unittests/Support/MemoryBufferTest.cpp b/unittests/Support/MemoryBufferTest.cpp index 963dcd91c8b6..0efa22c157d9 100644 --- a/unittests/Support/MemoryBufferTest.cpp +++ b/unittests/Support/MemoryBufferTest.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" @@ -71,6 +72,7 @@ TEST_F(MemoryBufferTest, NullTerminator4K) { SmallString<64> TestPath; sys::fs::createTemporaryFile("MemoryBufferTest_NullTerminator4K", "temp", TestFD, TestPath); + FileRemover Cleanup(TestPath); raw_fd_ostream OF(TestFD, true, /*unbuffered=*/true); for (unsigned i = 0; i < 4096 / 16; ++i) { OF << "0123456789abcdef"; @@ -133,6 +135,7 @@ void MemoryBufferTest::testGetOpenFileSlice(bool Reopen) { SmallString<64> TestPath; // Create a temporary file and write data into it. sys::fs::createTemporaryFile("prefix", "temp", TestFD, TestPath); + FileRemover Cleanup(TestPath); // OF is responsible for closing the file; If the file is not // reopened, it will be unbuffered so that the results are // immediately visible through the fd. @@ -182,6 +185,7 @@ TEST_F(MemoryBufferTest, slice) { int FD; SmallString<64> TestPath; sys::fs::createTemporaryFile("MemoryBufferTest_Slice", "temp", FD, TestPath); + FileRemover Cleanup(TestPath); raw_fd_ostream OF(FD, true, /*unbuffered=*/true); for (unsigned i = 0; i < 0x2000 / 8; ++i) { OF << "12345678"; diff --git a/unittests/Support/NativeFormatTests.cpp b/unittests/Support/NativeFormatTests.cpp new file mode 100644 index 000000000000..52acb6a99337 --- /dev/null +++ b/unittests/Support/NativeFormatTests.cpp @@ -0,0 +1,176 @@ +//===- llvm/unittest/Support/NativeFormatTests.cpp - formatting 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/SmallString.h" +#include "llvm/Support/NativeFormatting.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +#include <type_traits> + +using namespace llvm; + +namespace { + +template <typename T> std::string format_number(T N, IntegerStyle Style) { + std::string S; + llvm::raw_string_ostream Str(S); + write_integer(Str, N, 0, Style); + Str.flush(); + return S; +} + +std::string format_number(uint64_t N, HexPrintStyle Style, + Optional<size_t> Width = None) { + std::string S; + llvm::raw_string_ostream Str(S); + write_hex(Str, N, Style, Width); + Str.flush(); + return S; +} + +std::string format_number(double D, FloatStyle Style, + Optional<size_t> Precision = None) { + std::string S; + llvm::raw_string_ostream Str(S); + write_double(Str, D, Style, Precision); + Str.flush(); + return S; +} + +// Test basic number formatting with various styles and default width and +// precision. +TEST(NativeFormatTest, BasicIntegerTests) { + // Simple integers with no decimal. + EXPECT_EQ("0", format_number(0, IntegerStyle::Integer)); + EXPECT_EQ("2425", format_number(2425, IntegerStyle::Integer)); + EXPECT_EQ("-2425", format_number(-2425, IntegerStyle::Integer)); + + EXPECT_EQ("0", format_number(0LL, IntegerStyle::Integer)); + EXPECT_EQ("257257257235709", + format_number(257257257235709LL, IntegerStyle::Integer)); + EXPECT_EQ("-257257257235709", + format_number(-257257257235709LL, IntegerStyle::Integer)); + + // Number formatting. + EXPECT_EQ("0", format_number(0, IntegerStyle::Number)); + EXPECT_EQ("2,425", format_number(2425, IntegerStyle::Number)); + EXPECT_EQ("-2,425", format_number(-2425, IntegerStyle::Number)); + EXPECT_EQ("257,257,257,235,709", + format_number(257257257235709LL, IntegerStyle::Number)); + EXPECT_EQ("-257,257,257,235,709", + format_number(-257257257235709LL, IntegerStyle::Number)); + + // Hex formatting. + // lower case, prefix. + EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixLower)); + EXPECT_EQ("0xbeef", format_number(0xbeefLL, HexPrintStyle::PrefixLower)); + EXPECT_EQ("0xdeadbeef", + format_number(0xdeadbeefLL, HexPrintStyle::PrefixLower)); + + // upper-case, prefix. + EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixUpper)); + EXPECT_EQ("0xBEEF", format_number(0xbeefLL, HexPrintStyle::PrefixUpper)); + EXPECT_EQ("0xDEADBEEF", + format_number(0xdeadbeefLL, HexPrintStyle::PrefixUpper)); + + // lower-case, no prefix + EXPECT_EQ("0", format_number(0, HexPrintStyle::Lower)); + EXPECT_EQ("beef", format_number(0xbeefLL, HexPrintStyle::Lower)); + EXPECT_EQ("deadbeef", format_number(0xdeadbeefLL, HexPrintStyle::Lower)); + + // upper-case, no prefix. + EXPECT_EQ("0", format_number(0, HexPrintStyle::Upper)); + EXPECT_EQ("BEEF", format_number(0xbeef, HexPrintStyle::Upper)); + EXPECT_EQ("DEADBEEF", format_number(0xdeadbeef, HexPrintStyle::Upper)); +} + +// Test basic floating point formatting with various styles and default width +// and precision. +TEST(NativeFormatTest, BasicFloatingPointTests) { + // Double + EXPECT_EQ("0.000000e+00", format_number(0.0, FloatStyle::Exponent)); + EXPECT_EQ("-0.000000e+00", format_number(-0.0, FloatStyle::Exponent)); + EXPECT_EQ("1.100000e+00", format_number(1.1, FloatStyle::Exponent)); + EXPECT_EQ("1.100000E+00", format_number(1.1, FloatStyle::ExponentUpper)); + + // Default precision is 2 for floating points. + EXPECT_EQ("1.10", format_number(1.1, FloatStyle::Fixed)); + EXPECT_EQ("1.34", format_number(1.34, FloatStyle::Fixed)); + EXPECT_EQ("1.34", format_number(1.344, FloatStyle::Fixed)); + EXPECT_EQ("1.35", format_number(1.346, FloatStyle::Fixed)); +} + +// Test common boundary cases and min/max conditions. +TEST(NativeFormatTest, BoundaryTests) { + // Min and max. + EXPECT_EQ("18446744073709551615", + format_number(UINT64_MAX, IntegerStyle::Integer)); + + EXPECT_EQ("9223372036854775807", + format_number(INT64_MAX, IntegerStyle::Integer)); + EXPECT_EQ("-9223372036854775808", + format_number(INT64_MIN, IntegerStyle::Integer)); + + EXPECT_EQ("4294967295", format_number(UINT32_MAX, IntegerStyle::Integer)); + EXPECT_EQ("2147483647", format_number(INT32_MAX, IntegerStyle::Integer)); + EXPECT_EQ("-2147483648", format_number(INT32_MIN, IntegerStyle::Integer)); + + EXPECT_EQ("nan", format_number(std::numeric_limits<double>::quiet_NaN(), + FloatStyle::Fixed)); + EXPECT_EQ("INF", format_number(std::numeric_limits<double>::infinity(), + FloatStyle::Fixed)); +} + +TEST(NativeFormatTest, HexTests) { + // Test hex formatting with different widths and precisions. + + // Width less than the value should print the full value anyway. + EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixLower, 0)); + EXPECT_EQ("0xabcde", format_number(0xABCDE, HexPrintStyle::PrefixLower, 3)); + + // Precision greater than the value should pad with 0s. + // TODO: The prefix should not be counted in the precision. But unfortunately + // it is and we have to live with it unless we fix all existing users of + // prefixed hex formatting. + EXPECT_EQ("0x000", format_number(0, HexPrintStyle::PrefixLower, 5)); + EXPECT_EQ("0x0abcde", format_number(0xABCDE, HexPrintStyle::PrefixLower, 8)); + + EXPECT_EQ("00000", format_number(0, HexPrintStyle::Lower, 5)); + EXPECT_EQ("000abcde", format_number(0xABCDE, HexPrintStyle::Lower, 8)); + + // Try printing more digits than can fit in a uint64. + EXPECT_EQ("0x00000000000000abcde", + format_number(0xABCDE, HexPrintStyle::PrefixLower, 21)); +} + +TEST(NativeFormatTest, IntegerTests) { + EXPECT_EQ("-10", format_number(-10, IntegerStyle::Integer)); + EXPECT_EQ("-100", format_number(-100, IntegerStyle::Integer)); + EXPECT_EQ("-1000", format_number(-1000, IntegerStyle::Integer)); + EXPECT_EQ("-1234567890", format_number(-1234567890, IntegerStyle::Integer)); + EXPECT_EQ("10", format_number(10, IntegerStyle::Integer)); + EXPECT_EQ("100", format_number(100, IntegerStyle::Integer)); + EXPECT_EQ("1000", format_number(1000, IntegerStyle::Integer)); + EXPECT_EQ("1234567890", format_number(1234567890, IntegerStyle::Integer)); +} + +TEST(NativeFormatTest, CommaTests) { + EXPECT_EQ("0", format_number(0, IntegerStyle::Number)); + EXPECT_EQ("10", format_number(10, IntegerStyle::Number)); + EXPECT_EQ("100", format_number(100, IntegerStyle::Number)); + EXPECT_EQ("1,000", format_number(1000, IntegerStyle::Number)); + EXPECT_EQ("1,234,567,890", format_number(1234567890, IntegerStyle::Number)); + + EXPECT_EQ("-10", format_number(-10, IntegerStyle::Number)); + EXPECT_EQ("-100", format_number(-100, IntegerStyle::Number)); + EXPECT_EQ("-1,000", format_number(-1000, IntegerStyle::Number)); + EXPECT_EQ("-1,234,567,890", format_number(-1234567890, IntegerStyle::Number)); +} +} diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index 1a6ffa50e983..30eaa8b278ab 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -8,10 +8,12 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Path.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" @@ -120,22 +122,24 @@ TEST(Support, Path) { } ASSERT_TRUE(ComponentStack.empty()); - path::has_root_path(*i); - path::root_path(*i); - path::has_root_name(*i); - path::root_name(*i); - path::has_root_directory(*i); - path::root_directory(*i); - path::has_parent_path(*i); - path::parent_path(*i); - path::has_filename(*i); - path::filename(*i); - path::has_stem(*i); - path::stem(*i); - path::has_extension(*i); - path::extension(*i); - path::is_absolute(*i); - path::is_relative(*i); + // Crash test most of the API - since we're iterating over all of our paths + // here there isn't really anything reasonable to assert on in the results. + (void)path::has_root_path(*i); + (void)path::root_path(*i); + (void)path::has_root_name(*i); + (void)path::root_name(*i); + (void)path::has_root_directory(*i); + (void)path::root_directory(*i); + (void)path::has_parent_path(*i); + (void)path::parent_path(*i); + (void)path::has_filename(*i); + (void)path::filename(*i); + (void)path::has_stem(*i); + (void)path::stem(*i); + (void)path::has_extension(*i); + (void)path::extension(*i); + (void)path::is_absolute(*i); + (void)path::is_relative(*i); SmallString<128> temp_store; temp_store = *i; @@ -486,6 +490,10 @@ TEST_F(FileSystemTest, Unique) { fs::createUniqueDirectory("dir2", Dir2)); ASSERT_NO_ERROR(fs::getUniqueID(Dir2.c_str(), F2)); ASSERT_NE(F1, F2); + ASSERT_NO_ERROR(fs::remove(Dir1)); + ASSERT_NO_ERROR(fs::remove(Dir2)); + ASSERT_NO_ERROR(fs::remove(TempPath2)); + ASSERT_NO_ERROR(fs::remove(TempPath)); } TEST_F(FileSystemTest, TempFiles) { @@ -529,6 +537,7 @@ TEST_F(FileSystemTest, TempFiles) { SmallString<64> TempPath3; ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "", TempPath3)); ASSERT_FALSE(TempPath3.endswith(".")); + FileRemover Cleanup3(TempPath3); // Create a hard link to Temp1. ASSERT_NO_ERROR(fs::create_link(Twine(TempPath), Twine(TempPath2))); @@ -677,16 +686,15 @@ TEST_F(FileSystemTest, DirectoryIteration) { i.no_push(); visited.push_back(path::filename(i->path())); } - v_t::const_iterator a0 = std::find(visited.begin(), visited.end(), "a0"); - v_t::const_iterator aa1 = std::find(visited.begin(), visited.end(), "aa1"); - v_t::const_iterator ab1 = std::find(visited.begin(), visited.end(), "ab1"); - v_t::const_iterator dontlookhere = std::find(visited.begin(), visited.end(), - "dontlookhere"); - v_t::const_iterator da1 = std::find(visited.begin(), visited.end(), "da1"); - v_t::const_iterator z0 = std::find(visited.begin(), visited.end(), "z0"); - v_t::const_iterator za1 = std::find(visited.begin(), visited.end(), "za1"); - v_t::const_iterator pop = std::find(visited.begin(), visited.end(), "pop"); - v_t::const_iterator p1 = std::find(visited.begin(), visited.end(), "p1"); + v_t::const_iterator a0 = find(visited, "a0"); + v_t::const_iterator aa1 = find(visited, "aa1"); + v_t::const_iterator ab1 = find(visited, "ab1"); + v_t::const_iterator dontlookhere = find(visited, "dontlookhere"); + v_t::const_iterator da1 = find(visited, "da1"); + v_t::const_iterator z0 = find(visited, "z0"); + v_t::const_iterator za1 = find(visited, "za1"); + v_t::const_iterator pop = find(visited, "pop"); + v_t::const_iterator p1 = find(visited, "p1"); // Make sure that each path was visited correctly. ASSERT_NE(a0, visited.end()); @@ -851,6 +859,8 @@ TEST_F(FileSystemTest, Resize) { fs::file_status Status; ASSERT_NO_ERROR(fs::status(FD, Status)); ASSERT_EQ(Status.getSize(), 123U); + ::close(FD); + ASSERT_NO_ERROR(fs::remove(TempPath)); } TEST_F(FileSystemTest, FileMapping) { @@ -874,21 +884,25 @@ TEST_F(FileSystemTest, FileMapping) { mfr.data()[Val.size()] = 0; // Unmap temp file } + ASSERT_EQ(close(FileDescriptor), 0); // Map it back in read-only - int FD; - EC = fs::openFileForRead(Twine(TempPath), FD); - ASSERT_NO_ERROR(EC); - fs::mapped_file_region mfr(FD, fs::mapped_file_region::readonly, Size, 0, EC); - ASSERT_NO_ERROR(EC); - - // Verify content - EXPECT_EQ(StringRef(mfr.const_data()), Val); - - // Unmap temp file - fs::mapped_file_region m(FD, fs::mapped_file_region::readonly, Size, 0, EC); - ASSERT_NO_ERROR(EC); - ASSERT_EQ(close(FD), 0); + { + int FD; + EC = fs::openFileForRead(Twine(TempPath), FD); + ASSERT_NO_ERROR(EC); + fs::mapped_file_region mfr(FD, fs::mapped_file_region::readonly, Size, 0, EC); + ASSERT_NO_ERROR(EC); + + // Verify content + EXPECT_EQ(StringRef(mfr.const_data()), Val); + + // Unmap temp file + fs::mapped_file_region m(FD, fs::mapped_file_region::readonly, Size, 0, EC); + ASSERT_NO_ERROR(EC); + ASSERT_EQ(close(FD), 0); + } + ASSERT_NO_ERROR(fs::remove(TempPath)); } TEST(Support, NormalizePath) { @@ -953,6 +967,8 @@ TEST(Support, RemoveDots) { EXPECT_EQ("a\\..\\b\\c", remove_dots(".\\a\\..\\b\\c", false)); EXPECT_EQ("b\\c", remove_dots(".\\a\\..\\b\\c", true)); EXPECT_EQ("c", remove_dots(".\\.\\c", true)); + EXPECT_EQ("..\\a\\c", remove_dots("..\\a\\b\\..\\c", true)); + EXPECT_EQ("..\\..\\a\\c", remove_dots("..\\..\\a\\b\\..\\c", true)); SmallString<64> Path1(".\\.\\c"); EXPECT_TRUE(path::remove_dots(Path1, true)); @@ -964,6 +980,10 @@ TEST(Support, RemoveDots) { EXPECT_EQ("a/../b/c", remove_dots("./a/../b/c", false)); EXPECT_EQ("b/c", remove_dots("./a/../b/c", true)); EXPECT_EQ("c", remove_dots("././c", true)); + EXPECT_EQ("../a/c", remove_dots("../a/b/../c", true)); + EXPECT_EQ("../../a/c", remove_dots("../../a/b/../c", true)); + EXPECT_EQ("/a/c", remove_dots("/../../a/c", true)); + EXPECT_EQ("/a/c", remove_dots("/../a/b//../././/c", true)); SmallString<64> Path1("././c"); EXPECT_TRUE(path::remove_dots(Path1, true)); @@ -1002,6 +1022,7 @@ TEST_F(FileSystemTest, PathFromFD) { SmallString<64> TempPath; ASSERT_NO_ERROR( fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + FileRemover Cleanup(TempPath); // Make sure it exists. ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); @@ -1030,6 +1051,7 @@ TEST_F(FileSystemTest, PathFromFDWin32) { SmallString<64> TempPath; ASSERT_NO_ERROR( fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + FileRemover Cleanup(TempPath); // Make sure it exists. ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); @@ -1066,6 +1088,7 @@ TEST_F(FileSystemTest, PathFromFDUnicode) { ASSERT_NO_ERROR( fs::createTemporaryFile("\xCF\x80r\xC2\xB2", "\xE2\x84\xB5.0", FileDescriptor, TempPath)); + FileRemover Cleanup(TempPath); // Make sure it exists. ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); @@ -1089,6 +1112,7 @@ TEST_F(FileSystemTest, OpenFileForRead) { SmallString<64> TempPath; ASSERT_NO_ERROR( fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + FileRemover Cleanup(TempPath); // Make sure it exists. ASSERT_TRUE(sys::fs::exists(Twine(TempPath))); diff --git a/unittests/Support/RegexTest.cpp b/unittests/Support/RegexTest.cpp index c045c49bc3d7..5e3ce39f0057 100644 --- a/unittests/Support/RegexTest.cpp +++ b/unittests/Support/RegexTest.cpp @@ -151,6 +151,24 @@ TEST_F(RegexTest, MoveAssign) { Regex r2("abc"); r2 = std::move(r1); EXPECT_TRUE(r2.match("916")); + std::string Error; + EXPECT_FALSE(r1.isValid(Error)); +} + +TEST_F(RegexTest, NoArgConstructor) { + std::string Error; + Regex r1; + EXPECT_FALSE(r1.isValid(Error)); + EXPECT_EQ("invalid regular expression", Error); + r1 = Regex("abc"); + EXPECT_TRUE(r1.isValid(Error)); +} + +TEST_F(RegexTest, MatchInvalid) { + Regex r1; + std::string Error; + EXPECT_FALSE(r1.isValid(Error)); + EXPECT_FALSE(r1.match("X")); } } diff --git a/unittests/Support/SpecialCaseListTest.cpp b/unittests/Support/SpecialCaseListTest.cpp index 0657f8003e8b..e86eecb527bb 100644 --- a/unittests/Support/SpecialCaseListTest.cpp +++ b/unittests/Support/SpecialCaseListTest.cpp @@ -130,6 +130,63 @@ TEST_F(SpecialCaseListTest, MultipleBlacklists) { EXPECT_TRUE(SCL->inSection("src", "ban", "init")); EXPECT_TRUE(SCL->inSection("src", "tomfoolery")); EXPECT_TRUE(SCL->inSection("src", "tomfoglery")); + for (auto &Path : Files) + sys::fs::remove(Path); +} + +TEST_F(SpecialCaseListTest, NoTrigramsInRules) { + std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:b.r\n" + "fun:za*az\n"); + EXPECT_TRUE(SCL->inSection("fun", "bar")); + EXPECT_FALSE(SCL->inSection("fun", "baz")); + EXPECT_TRUE(SCL->inSection("fun", "zakaz")); + EXPECT_FALSE(SCL->inSection("fun", "zaraza")); +} + +TEST_F(SpecialCaseListTest, NoTrigramsInARule) { + std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:*bar*\n" + "fun:za*az\n"); + EXPECT_TRUE(SCL->inSection("fun", "abara")); + EXPECT_FALSE(SCL->inSection("fun", "bor")); + EXPECT_TRUE(SCL->inSection("fun", "zakaz")); + EXPECT_FALSE(SCL->inSection("fun", "zaraza")); +} + +TEST_F(SpecialCaseListTest, RepetitiveRule) { + std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:*bar*bar*bar*bar*\n" + "fun:bar*\n"); + EXPECT_TRUE(SCL->inSection("fun", "bara")); + EXPECT_FALSE(SCL->inSection("fun", "abara")); + EXPECT_TRUE(SCL->inSection("fun", "barbarbarbar")); + EXPECT_TRUE(SCL->inSection("fun", "abarbarbarbar")); + EXPECT_FALSE(SCL->inSection("fun", "abarbarbar")); +} + +TEST_F(SpecialCaseListTest, SpecialSymbolRule) { + std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("src:*c\\+\\+abi*\n"); + EXPECT_TRUE(SCL->inSection("src", "c++abi")); + EXPECT_FALSE(SCL->inSection("src", "c\\+\\+abi")); +} + +TEST_F(SpecialCaseListTest, PopularTrigram) { + std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:*aaaaaa*\n" + "fun:*aaaaa*\n" + "fun:*aaaa*\n" + "fun:*aaa*\n"); + EXPECT_TRUE(SCL->inSection("fun", "aaa")); + EXPECT_TRUE(SCL->inSection("fun", "aaaa")); + EXPECT_TRUE(SCL->inSection("fun", "aaaabbbaaa")); +} + +TEST_F(SpecialCaseListTest, EscapedSymbols) { + std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("src:*c\\+\\+abi*\n" + "src:*hello\\\\world*\n"); + EXPECT_TRUE(SCL->inSection("src", "dir/c++abi")); + EXPECT_FALSE(SCL->inSection("src", "dir/c\\+\\+abi")); + EXPECT_FALSE(SCL->inSection("src", "c\\+\\+abi")); + EXPECT_TRUE(SCL->inSection("src", "C:\\hello\\world")); + EXPECT_TRUE(SCL->inSection("src", "hello\\world")); + EXPECT_FALSE(SCL->inSection("src", "hello\\\\world")); } } diff --git a/unittests/Support/StreamingMemoryObjectTest.cpp b/unittests/Support/StreamingMemoryObjectTest.cpp deleted file mode 100644 index 836dfa9084f5..000000000000 --- a/unittests/Support/StreamingMemoryObjectTest.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//===- 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 index 21994f27b73c..a3d806f76fb5 100644 --- a/unittests/Support/TargetParserTest.cpp +++ b/unittests/Support/TargetParserTest.cpp @@ -7,86 +7,718 @@ // //===----------------------------------------------------------------------===// -#include "gtest/gtest.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/TargetParser.h" +#include "gtest/gtest.h" +#include <string> 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) { +const char *ARMArch[] = { + "armv2", "armv2a", "armv3", "armv3m", "armv4", + "armv4t", "armv5", "armv5t", "armv5e", "armv5te", + "armv5tej", "armv6", "armv6j", "armv6k", "armv6hl", + "armv6t2", "armv6kz", "armv6z", "armv6zk", "armv6-m", + "armv6m", "armv6sm", "armv6s-m", "armv7-a", "armv7", + "armv7a", "armv7hl", "armv7l", "armv7-r", "armv7r", + "armv7-m", "armv7m", "armv7k", "armv7s", "armv7e-m", + "armv7em", "armv8-a", "armv8", "armv8a", "armv8.1-a", + "armv8.1a", "armv8.2-a", "armv8.2a", "armv8-r", "armv8r", + "armv8-m.base", "armv8m.base", "armv8-m.main", "armv8m.main", "iwmmxt", + "iwmmxt2", "xscale"}; + +bool testARMCPU(StringRef CPUName, StringRef ExpectedArch, + StringRef ExpectedFPU, unsigned ExpectedFlags, + StringRef CPUAttr) { + unsigned ArchKind = ARM::parseCPUArch(CPUName); + bool pass = ARM::getArchName(ArchKind).equals(ExpectedArch); + unsigned FPUKind = ARM::getDefaultFPU(CPUName, ArchKind); + pass &= ARM::getFPUName(FPUKind).equals(ExpectedFPU); + + unsigned ExtKind = ARM::getDefaultExtensions(CPUName, ArchKind); + if (ExtKind > 1 && (ExtKind & ARM::AEK_NONE)) + pass &= ((ExtKind ^ ARM::AEK_NONE) == ExpectedFlags); + else + pass &= (ExtKind == ExpectedFlags); + + pass &= ARM::getCPUAttr(ArchKind).equals(CPUAttr); + + return pass; +} + +TEST(TargetParserTest, testARMCPU) { + EXPECT_TRUE(testARMCPU("invalid", "invalid", "invalid", + ARM::AEK_NONE, "")); + EXPECT_TRUE(testARMCPU("generic", "invalid", "none", + ARM::AEK_NONE, "")); + + EXPECT_TRUE(testARMCPU("arm2", "armv2", "none", + ARM::AEK_NONE, "2")); + EXPECT_TRUE(testARMCPU("arm3", "armv2a", "none", + ARM::AEK_NONE, "2A")); + EXPECT_TRUE(testARMCPU("arm6", "armv3", "none", + ARM::AEK_NONE, "3")); + EXPECT_TRUE(testARMCPU("arm7m", "armv3m", "none", + ARM::AEK_NONE, "3M")); + EXPECT_TRUE(testARMCPU("arm8", "armv4", "none", + ARM::AEK_NONE, "4")); + EXPECT_TRUE(testARMCPU("arm810", "armv4", "none", + ARM::AEK_NONE, "4")); + EXPECT_TRUE(testARMCPU("strongarm", "armv4", "none", + ARM::AEK_NONE, "4")); + EXPECT_TRUE(testARMCPU("strongarm110", "armv4", "none", + ARM::AEK_NONE, "4")); + EXPECT_TRUE(testARMCPU("strongarm1100", "armv4", "none", + ARM::AEK_NONE, "4")); + EXPECT_TRUE(testARMCPU("strongarm1110", "armv4", "none", + ARM::AEK_NONE, "4")); + EXPECT_TRUE(testARMCPU("arm7tdmi", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm7tdmi-s", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm710t", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm720t", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm9", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm9tdmi", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm920", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm920t", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm922t", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm9312", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm940t", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("ep9312", "armv4t", "none", + ARM::AEK_NONE, "4T")); + EXPECT_TRUE(testARMCPU("arm10tdmi", "armv5t", "none", + ARM::AEK_NONE, "5T")); + EXPECT_TRUE(testARMCPU("arm1020t", "armv5t", "none", + ARM::AEK_NONE, "5T")); + EXPECT_TRUE(testARMCPU("arm9e", "armv5te", "none", + ARM::AEK_DSP, "5TE")); + EXPECT_TRUE(testARMCPU("arm946e-s", "armv5te", "none", + ARM::AEK_DSP, "5TE")); + EXPECT_TRUE(testARMCPU("arm966e-s", "armv5te", "none", + ARM::AEK_DSP, "5TE")); + EXPECT_TRUE(testARMCPU("arm968e-s", "armv5te", "none", + ARM::AEK_DSP, "5TE")); + EXPECT_TRUE(testARMCPU("arm10e", "armv5te", "none", + ARM::AEK_DSP, "5TE")); + EXPECT_TRUE(testARMCPU("arm1020e", "armv5te", "none", + ARM::AEK_DSP, "5TE")); + EXPECT_TRUE(testARMCPU("arm1022e", "armv5te", "none", + ARM::AEK_DSP, "5TE")); + EXPECT_TRUE(testARMCPU("arm926ej-s", "armv5tej", "none", + ARM::AEK_DSP, "5TEJ")); + EXPECT_TRUE(testARMCPU("arm1136j-s", "armv6", "none", + ARM::AEK_DSP, "6")); + EXPECT_TRUE(testARMCPU("arm1136jf-s", "armv6", "vfpv2", + ARM::AEK_DSP, "6")); + EXPECT_TRUE(testARMCPU("arm1136jz-s", "armv6", "none", + ARM::AEK_DSP, "6")); + EXPECT_TRUE(testARMCPU("arm1176j-s", "armv6k", "none", + ARM::AEK_DSP, "6K")); + EXPECT_TRUE(testARMCPU("arm1176jz-s", "armv6kz", "none", + ARM::AEK_SEC | ARM::AEK_DSP, "6KZ")); + EXPECT_TRUE(testARMCPU("mpcore", "armv6k", "vfpv2", + ARM::AEK_DSP, "6K")); + EXPECT_TRUE(testARMCPU("mpcorenovfp", "armv6k", "none", + ARM::AEK_DSP, "6K")); + EXPECT_TRUE(testARMCPU("arm1176jzf-s", "armv6kz", "vfpv2", + ARM::AEK_SEC | ARM::AEK_DSP, "6KZ")); + EXPECT_TRUE(testARMCPU("arm1156t2-s", "armv6t2", "none", + ARM::AEK_DSP, "6T2")); + EXPECT_TRUE(testARMCPU("arm1156t2f-s", "armv6t2", "vfpv2", + ARM::AEK_DSP, "6T2")); + EXPECT_TRUE(testARMCPU("cortex-m0", "armv6-m", "none", + ARM::AEK_NONE, "6-M")); + EXPECT_TRUE(testARMCPU("cortex-m0plus", "armv6-m", "none", + ARM::AEK_NONE, "6-M")); + EXPECT_TRUE(testARMCPU("cortex-m1", "armv6-m", "none", + ARM::AEK_NONE, "6-M")); + EXPECT_TRUE(testARMCPU("sc000", "armv6-m", "none", + ARM::AEK_NONE, "6-M")); + EXPECT_TRUE(testARMCPU("cortex-a5", "armv7-a", "neon-vfpv4", + ARM::AEK_MP | ARM::AEK_SEC | ARM::AEK_DSP, "7-A")); + EXPECT_TRUE(testARMCPU("cortex-a7", "armv7-a", "neon-vfpv4", + ARM::AEK_HWDIV | ARM::AEK_HWDIVARM | ARM::AEK_MP | + ARM::AEK_SEC | ARM::AEK_VIRT | ARM::AEK_DSP, + "7-A")); + EXPECT_TRUE(testARMCPU("cortex-a8", "armv7-a", "neon", + ARM::AEK_SEC | ARM::AEK_DSP, "7-A")); + EXPECT_TRUE(testARMCPU("cortex-a9", "armv7-a", "neon-fp16", + ARM::AEK_MP | ARM::AEK_SEC | ARM::AEK_DSP, "7-A")); + EXPECT_TRUE(testARMCPU("cortex-a12", "armv7-a", "neon-vfpv4", + ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | + ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_DSP, + "7-A")); + EXPECT_TRUE(testARMCPU("cortex-a15", "armv7-a", "neon-vfpv4", + ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | + ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_DSP, + "7-A")); + EXPECT_TRUE(testARMCPU("cortex-a17", "armv7-a", "neon-vfpv4", + ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | + ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_DSP, + "7-A")); + EXPECT_TRUE(testARMCPU("krait", "armv7-a", "neon-vfpv4", + ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_DSP, + "7-A")); + EXPECT_TRUE(testARMCPU("cortex-r4", "armv7-r", "none", + ARM::AEK_HWDIV | ARM::AEK_DSP, "7-R")); + EXPECT_TRUE(testARMCPU("cortex-r4f", "armv7-r", "vfpv3-d16", + ARM::AEK_HWDIV | ARM::AEK_DSP, "7-R")); + EXPECT_TRUE(testARMCPU("cortex-r5", "armv7-r", "vfpv3-d16", + ARM::AEK_MP | ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | + ARM::AEK_DSP, "7-R")); + EXPECT_TRUE(testARMCPU("cortex-r7", "armv7-r", "vfpv3-d16-fp16", + ARM::AEK_MP | ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | + ARM::AEK_DSP, "7-R")); + EXPECT_TRUE(testARMCPU("cortex-r8", "armv7-r", "vfpv3-d16-fp16", + ARM::AEK_MP | ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | + ARM::AEK_DSP, "7-R")); + EXPECT_TRUE(testARMCPU("cortex-r52", "armv8-r", "neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_MP | ARM::AEK_VIRT | + ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-R")); + EXPECT_TRUE(testARMCPU("sc300", "armv7-m", "none", + ARM::AEK_HWDIV, "7-M")); + EXPECT_TRUE(testARMCPU("cortex-m3", "armv7-m", "none", + ARM::AEK_HWDIV, "7-M")); + EXPECT_TRUE(testARMCPU("cortex-m4", "armv7e-m", "fpv4-sp-d16", + ARM::AEK_HWDIV | ARM::AEK_DSP, "7E-M")); + EXPECT_TRUE(testARMCPU("cortex-m7", "armv7e-m", "fpv5-d16", + ARM::AEK_HWDIV | ARM::AEK_DSP, "7E-M")); + EXPECT_TRUE(testARMCPU("cortex-a32", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("cortex-a35", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("cortex-a53", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("cortex-a57", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("cortex-a72", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("cortex-a73", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("cyclone", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("exynos-m1", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("exynos-m2", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("exynos-m3", "armv8-a", "crypto-neon-fp-armv8", + ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP | + ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIV | ARM::AEK_DSP, + "8-A")); + EXPECT_TRUE(testARMCPU("iwmmxt", "iwmmxt", "none", + ARM::AEK_NONE, "iwmmxt")); + EXPECT_TRUE(testARMCPU("xscale", "xscale", "none", + ARM::AEK_NONE, "xscale")); + EXPECT_TRUE(testARMCPU("swift", "armv7s", "neon-vfpv4", + ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_DSP, + "7-S")); +} + +bool testARMArch(StringRef Arch, StringRef DefaultCPU, StringRef SubArch, + unsigned ArchAttr) { + unsigned ArchKind = ARM::parseArch(Arch); + return (ArchKind != ARM::AK_INVALID) & + ARM::getDefaultCPU(Arch).equals(DefaultCPU) & + ARM::getSubArch(ArchKind).equals(SubArch) & + (ARM::getArchAttr(ArchKind) == ArchAttr); +} + +TEST(TargetParserTest, testARMArch) { + EXPECT_TRUE( + testARMArch("armv2", "arm2", "v2", + ARMBuildAttrs::CPUArch::Pre_v4)); + EXPECT_TRUE( + testARMArch("armv2a", "arm3", "v2a", + ARMBuildAttrs::CPUArch::Pre_v4)); + EXPECT_TRUE( + testARMArch("armv3", "arm6", "v3", + ARMBuildAttrs::CPUArch::Pre_v4)); + EXPECT_TRUE( + testARMArch("armv3m", "arm7m", "v3m", + ARMBuildAttrs::CPUArch::Pre_v4)); + EXPECT_TRUE( + testARMArch("armv4", "strongarm", "v4", + ARMBuildAttrs::CPUArch::v4)); + EXPECT_TRUE( + testARMArch("armv4t", "arm7tdmi", "v4t", + ARMBuildAttrs::CPUArch::v4T)); + EXPECT_TRUE( + testARMArch("armv5t", "arm10tdmi", "v5", + ARMBuildAttrs::CPUArch::v5T)); + EXPECT_TRUE( + testARMArch("armv5te", "arm1022e", "v5e", + ARMBuildAttrs::CPUArch::v5TE)); + EXPECT_TRUE( + testARMArch("armv5tej", "arm926ej-s", "v5e", + ARMBuildAttrs::CPUArch::v5TEJ)); + EXPECT_TRUE( + testARMArch("armv6", "arm1136jf-s", "v6", + ARMBuildAttrs::CPUArch::v6)); + EXPECT_TRUE( + testARMArch("armv6k", "arm1176j-s", "v6k", + ARMBuildAttrs::CPUArch::v6K)); + EXPECT_TRUE( + testARMArch("armv6t2", "arm1156t2-s", "v6t2", + ARMBuildAttrs::CPUArch::v6T2)); + EXPECT_TRUE( + testARMArch("armv6kz", "arm1176jzf-s", "v6kz", + ARMBuildAttrs::CPUArch::v6KZ)); + EXPECT_TRUE( + testARMArch("armv6-m", "cortex-m0", "v6m", + ARMBuildAttrs::CPUArch::v6_M)); + EXPECT_TRUE( + testARMArch("armv7-a", "cortex-a8", "v7", + ARMBuildAttrs::CPUArch::v7)); + EXPECT_TRUE( + testARMArch("armv7-r", "cortex-r4", "v7r", + ARMBuildAttrs::CPUArch::v7)); + EXPECT_TRUE( + testARMArch("armv7-m", "cortex-m3", "v7m", + ARMBuildAttrs::CPUArch::v7)); + EXPECT_TRUE( + testARMArch("armv7e-m", "cortex-m4", "v7em", + ARMBuildAttrs::CPUArch::v7E_M)); + EXPECT_TRUE( + testARMArch("armv8-a", "cortex-a53", "v8", + ARMBuildAttrs::CPUArch::v8_A)); + EXPECT_TRUE( + testARMArch("armv8.1-a", "generic", "v8.1a", + ARMBuildAttrs::CPUArch::v8_A)); + EXPECT_TRUE( + testARMArch("armv8.2-a", "generic", "v8.2a", + ARMBuildAttrs::CPUArch::v8_A)); + EXPECT_TRUE( + testARMArch("armv8-r", "cortex-r52", "v8r", + ARMBuildAttrs::CPUArch::v8_R)); + EXPECT_TRUE( + testARMArch("armv8-m.base", "generic", "v8m.base", + ARMBuildAttrs::CPUArch::v8_M_Base)); + EXPECT_TRUE( + testARMArch("armv8-m.main", "generic", "v8m.main", + ARMBuildAttrs::CPUArch::v8_M_Main)); + EXPECT_TRUE( + testARMArch("iwmmxt", "iwmmxt", "", + ARMBuildAttrs::CPUArch::v5TE)); + EXPECT_TRUE( + testARMArch("iwmmxt2", "generic", "", + ARMBuildAttrs::CPUArch::v5TE)); + EXPECT_TRUE( + testARMArch("xscale", "xscale", "v5e", + ARMBuildAttrs::CPUArch::v5TE)); + EXPECT_TRUE( + testARMArch("armv7s", "swift", "v7s", + ARMBuildAttrs::CPUArch::v7)); + EXPECT_TRUE( + testARMArch("armv7k", "generic", "v7k", + ARMBuildAttrs::CPUArch::v7)); +} + +bool testARMExtension(StringRef CPUName, unsigned ArchKind, StringRef ArchExt) { + return ARM::getDefaultExtensions(CPUName, ArchKind) & + ARM::parseArchExt(ArchExt); +} + +TEST(TargetParserTest, testARMExtension) { + EXPECT_FALSE(testARMExtension("arm2", 0, "thumb")); + EXPECT_FALSE(testARMExtension("arm3", 0, "thumb")); + EXPECT_FALSE(testARMExtension("arm6", 0, "thumb")); + EXPECT_FALSE(testARMExtension("arm7m", 0, "thumb")); + EXPECT_FALSE(testARMExtension("strongarm", 0, "dsp")); + EXPECT_FALSE(testARMExtension("arm7tdmi", 0, "dsp")); + EXPECT_FALSE(testARMExtension("arm10tdmi", 0, "simd")); + EXPECT_FALSE(testARMExtension("arm1022e", 0, "simd")); + EXPECT_FALSE(testARMExtension("arm926ej-s", 0, "simd")); + EXPECT_FALSE(testARMExtension("arm1136jf-s", 0, "crypto")); + EXPECT_FALSE(testARMExtension("arm1176j-s", 0, "crypto")); + EXPECT_FALSE(testARMExtension("arm1156t2-s", 0, "crypto")); + EXPECT_FALSE(testARMExtension("arm1176jzf-s", 0, "crypto")); + EXPECT_FALSE(testARMExtension("cortex-m0", 0, "crypto")); + EXPECT_FALSE(testARMExtension("cortex-a8", 0, "crypto")); + EXPECT_FALSE(testARMExtension("cortex-r4", 0, "crypto")); + EXPECT_FALSE(testARMExtension("cortex-m3", 0, "crypto")); + EXPECT_FALSE(testARMExtension("cortex-a53", 0, "ras")); + EXPECT_FALSE(testARMExtension("cortex-r52", 0, "ras")); + EXPECT_FALSE(testARMExtension("iwmmxt", 0, "crc")); + EXPECT_FALSE(testARMExtension("xscale", 0, "crc")); + EXPECT_FALSE(testARMExtension("swift", 0, "crc")); + + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV2, "thumb")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV2A, "thumb")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV3, "thumb")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV3M, "thumb")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV4, "dsp")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV4T, "dsp")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV5T, "simd")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV5TE, "simd")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV5TEJ, "simd")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV6, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV6K, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV6T2, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV6KZ, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV6M, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV7A, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV7R, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV7M, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV7EM, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV8A, "ras")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV8_1A, "ras")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV8_2A, "spe")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV8R, "ras")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV8MBaseline, "crc")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV8MMainline, "crc")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_IWMMXT, "crc")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_IWMMXT2, "crc")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_XSCALE, "crc")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV7S, "crypto")); + EXPECT_FALSE(testARMExtension("generic", ARM::AK_ARMV7K, "crypto")); +} + +TEST(TargetParserTest, ARMFPUVersion) { 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()); + if (FK == ARM::FK_LAST) + EXPECT_EQ(0U, ARM::getFPUVersion(FK)); + else + EXPECT_LE(0U, ARM::getFPUVersion(FK)); } -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, ARMFPUNeonSupportLevel) { + for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0); + FK <= ARM::FPUKind::FK_LAST; + FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1)) + if (FK == ARM::FK_LAST) + EXPECT_EQ(0U, ARM::getFPUNeonSupportLevel(FK)); + else + EXPECT_LE(0U, ARM::getFPUNeonSupportLevel(FK)); } -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, ARMFPURestriction) { + for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0); + FK <= ARM::FPUKind::FK_LAST; + FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1)) + if (FK == ARM::FK_LAST) + EXPECT_EQ(0U, ARM::getFPURestriction(FK)); + else + EXPECT_LE(0U, ARM::getFPURestriction(FK)); } -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()); +TEST(TargetParserTest, ARMExtensionFeatures) { + std::vector<StringRef> Features; + unsigned Extensions = ARM::AEK_CRC | ARM::AEK_CRYPTO | ARM::AEK_DSP | + ARM::AEK_HWDIVARM | ARM::AEK_HWDIV | ARM::AEK_MP | + ARM::AEK_SEC | ARM::AEK_VIRT | ARM::AEK_RAS; + + for (unsigned i = 0; i <= Extensions; i++) + EXPECT_TRUE(i == 0 ? !ARM::getExtensionFeatures(i, Features) + : ARM::getExtensionFeatures(i, Features)); } + +TEST(TargetParserTest, ARMFPUFeatures) { + std::vector<StringRef> Features; + 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_INVALID || FK >= ARM::FK_LAST) + ? !ARM::getFPUFeatures(FK, Features) + : ARM::getFPUFeatures(FK, Features)); +} + +TEST(TargetParserTest, ARMArchExtFeature) { + const char *ArchExt[][4] = {{"crc", "nocrc", "+crc", "-crc"}, + {"crypto", "nocrypto", "+crypto", "-crypto"}, + {"dsp", "nodsp", "+dsp", "-dsp"}, + {"fp", "nofp", nullptr, nullptr}, + {"idiv", "noidiv", nullptr, nullptr}, + {"mp", "nomp", nullptr, nullptr}, + {"simd", "nosimd", nullptr, nullptr}, + {"sec", "nosec", nullptr, nullptr}, + {"virt", "novirt", nullptr, nullptr}, + {"fp16", "nofp16", "+fullfp16", "-fullfp16"}, + {"ras", "noras", "+ras", "-ras"}, + {"os", "noos", nullptr, nullptr}, + {"iwmmxt", "noiwmmxt", nullptr, nullptr}, + {"iwmmxt2", "noiwmmxt2", nullptr, nullptr}, + {"maverick", "maverick", nullptr, nullptr}, + {"xscale", "noxscale", nullptr, nullptr}}; + + for (unsigned i = 0; i < array_lengthof(ArchExt); i++) { + EXPECT_EQ(StringRef(ArchExt[i][2]), ARM::getArchExtFeature(ArchExt[i][0])); + EXPECT_EQ(StringRef(ArchExt[i][3]), ARM::getArchExtFeature(ArchExt[i][1])); + } } +TEST(TargetParserTest, ARMparseHWDiv) { + const char *hwdiv[] = {"thumb", "arm", "arm,thumb", "thumb,arm"}; + + for (unsigned i = 0; i < array_lengthof(hwdiv); i++) + EXPECT_NE(ARM::AEK_INVALID, ARM::parseHWDiv((StringRef)hwdiv[i])); +} + +TEST(TargetParserTest, ARMparseArchEndianAndISA) { + const char *Arch[] = { + "v2", "v2a", "v3", "v3m", "v4", "v4t", "v5", "v5t", + "v5e", "v5te", "v5tej", "v6", "v6j", "v6k", "v6hl", "v6t2", + "v6kz", "v6z", "v6zk", "v6-m", "v6m", "v6sm", "v6s-m", "v7-a", + "v7", "v7a", "v7hl", "v7l", "v7-r", "v7r", "v7-m", "v7m", + "v7k", "v7s", "v7e-m", "v7em", "v8-a", "v8", "v8a", "v8.1-a", + "v8.1a", "v8.2-a", "v8.2a", "v8-r"}; + + for (unsigned i = 0; i < array_lengthof(Arch); i++) { + std::string arm_1 = "armeb" + (std::string)(Arch[i]); + std::string arm_2 = "arm" + (std::string)(Arch[i]) + "eb"; + std::string arm_3 = "arm" + (std::string)(Arch[i]); + std::string thumb_1 = "thumbeb" + (std::string)(Arch[i]); + std::string thumb_2 = "thumb" + (std::string)(Arch[i]) + "eb"; + std::string thumb_3 = "thumb" + (std::string)(Arch[i]); + + EXPECT_EQ(ARM::EK_BIG, ARM::parseArchEndian(arm_1)); + EXPECT_EQ(ARM::EK_BIG, ARM::parseArchEndian(arm_2)); + EXPECT_EQ(ARM::EK_LITTLE, ARM::parseArchEndian(arm_3)); + + EXPECT_EQ(ARM::IK_ARM, ARM::parseArchISA(arm_1)); + EXPECT_EQ(ARM::IK_ARM, ARM::parseArchISA(arm_2)); + EXPECT_EQ(ARM::IK_ARM, ARM::parseArchISA(arm_3)); + if (i >= 4) { + EXPECT_EQ(ARM::EK_BIG, ARM::parseArchEndian(thumb_1)); + EXPECT_EQ(ARM::EK_BIG, ARM::parseArchEndian(thumb_2)); + EXPECT_EQ(ARM::EK_LITTLE, ARM::parseArchEndian(thumb_3)); + + EXPECT_EQ(ARM::IK_THUMB, ARM::parseArchISA(thumb_1)); + EXPECT_EQ(ARM::IK_THUMB, ARM::parseArchISA(thumb_2)); + EXPECT_EQ(ARM::IK_THUMB, ARM::parseArchISA(thumb_3)); + } + } + + EXPECT_EQ(ARM::EK_LITTLE, ARM::parseArchEndian("aarch64")); + EXPECT_EQ(ARM::EK_BIG, ARM::parseArchEndian("aarch64_be")); + + EXPECT_EQ(ARM::IK_AARCH64, ARM::parseArchISA("aarch64")); + EXPECT_EQ(ARM::IK_AARCH64, ARM::parseArchISA("aarch64_be")); + EXPECT_EQ(ARM::IK_AARCH64, ARM::parseArchISA("arm64")); + EXPECT_EQ(ARM::IK_AARCH64, ARM::parseArchISA("arm64_be")); +} + +TEST(TargetParserTest, ARMparseArchProfile) { + for (unsigned i = 0; i < array_lengthof(ARMArch); i++) { + switch (ARM::parseArch(ARMArch[i])) { + case ARM::AK_ARMV6M: + case ARM::AK_ARMV7M: + case ARM::AK_ARMV7EM: + case ARM::AK_ARMV8MMainline: + case ARM::AK_ARMV8MBaseline: + EXPECT_EQ(ARM::PK_M, ARM::parseArchProfile(ARMArch[i])); + continue; + case ARM::AK_ARMV7R: + case ARM::AK_ARMV8R: + EXPECT_EQ(ARM::PK_R, ARM::parseArchProfile(ARMArch[i])); + continue; + case ARM::AK_ARMV7A: + case ARM::AK_ARMV7K: + case ARM::AK_ARMV8A: + case ARM::AK_ARMV8_1A: + case ARM::AK_ARMV8_2A: + EXPECT_EQ(ARM::PK_A, ARM::parseArchProfile(ARMArch[i])); + continue; + } + EXPECT_EQ(ARM::PK_INVALID, ARM::parseArchProfile(ARMArch[i])); + } +} + +TEST(TargetParserTest, ARMparseArchVersion) { + for (unsigned i = 0; i < array_lengthof(ARMArch); i++) + if (((std::string)ARMArch[i]).substr(0, 4) == "armv") + EXPECT_EQ((ARMArch[i][4] - 48u), ARM::parseArchVersion(ARMArch[i])); + else + EXPECT_EQ(5u, ARM::parseArchVersion(ARMArch[i])); +} + +bool testAArch64CPU(StringRef CPUName, StringRef ExpectedArch, + StringRef ExpectedFPU, unsigned ExpectedFlags, + StringRef CPUAttr) { + unsigned ArchKind = AArch64::parseCPUArch(CPUName); + bool pass = AArch64::getArchName(ArchKind).equals(ExpectedArch); + unsigned FPUKind = AArch64::getDefaultFPU(CPUName, ArchKind); + pass &= AArch64::getFPUName(FPUKind).equals(ExpectedFPU); + + unsigned ExtKind = AArch64::getDefaultExtensions(CPUName, ArchKind); + if (ExtKind > 1 && (ExtKind & AArch64::AEK_NONE)) + pass &= ((ExtKind ^ AArch64::AEK_NONE) == ExpectedFlags); + else + pass &= (ExtKind == ExpectedFlags); + + pass &= AArch64::getCPUAttr(ArchKind).equals(CPUAttr); + + return pass; +} + +TEST(TargetParserTest, testAArch64CPU) { + EXPECT_TRUE(testAArch64CPU( + "invalid", "invalid", "invalid", + AArch64::AEK_INVALID, "")); + EXPECT_TRUE(testAArch64CPU( + "generic", "invalid", "none", + AArch64::AEK_NONE, "")); + + EXPECT_TRUE(testAArch64CPU( + "cortex-a35", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "cortex-a53", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "cortex-a57", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "cortex-a72", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "cortex-a73", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "cyclone", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "exynos-m1", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "exynos-m2", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "exynos-m3", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "falkor", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "kryo", "armv8-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "vulcan", "armv8.1-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_SIMD, "8.1-A")); +} + +bool testAArch64Arch(StringRef Arch, StringRef DefaultCPU, StringRef SubArch, + unsigned ArchAttr) { + unsigned ArchKind = AArch64::parseArch(Arch); + return (ArchKind != static_cast<unsigned>(AArch64::ArchKind::AK_INVALID)) & + AArch64::getDefaultCPU(Arch).equals(DefaultCPU) & + AArch64::getSubArch(ArchKind).equals(SubArch) & + (AArch64::getArchAttr(ArchKind) == ArchAttr); +} + +TEST(TargetParserTest, testAArch64Arch) { + EXPECT_TRUE(testAArch64Arch("armv8-a", "cortex-a53", "v8", + ARMBuildAttrs::CPUArch::v8_A)); + EXPECT_TRUE(testAArch64Arch("armv8.1-a", "generic", "v8.1a", + ARMBuildAttrs::CPUArch::v8_A)); + EXPECT_TRUE(testAArch64Arch("armv8.2-a", "generic", "v8.2a", + ARMBuildAttrs::CPUArch::v8_A)); +} + +bool testAArch64Extension(StringRef CPUName, unsigned ArchKind, + StringRef ArchExt) { + return AArch64::getDefaultExtensions(CPUName, ArchKind) & + AArch64::parseArchExt(ArchExt); +} + +TEST(TargetParserTest, testAArch64Extension) { + EXPECT_FALSE(testAArch64Extension("cortex-a35", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("cortex-a53", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("cortex-a57", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("cortex-a72", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("cortex-a73", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("cyclone", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("exynos-m1", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("kryo", 0, "ras")); + EXPECT_FALSE(testAArch64Extension("vulcan", 0, "ras")); + + EXPECT_FALSE(testAArch64Extension( + "generic", static_cast<unsigned>(AArch64::ArchKind::AK_ARMV8A), "ras")); + EXPECT_FALSE(testAArch64Extension( + "generic", static_cast<unsigned>(AArch64::ArchKind::AK_ARMV8_1A), "ras")); + EXPECT_FALSE(testAArch64Extension( + "generic", static_cast<unsigned>(AArch64::ArchKind::AK_ARMV8_2A), "spe")); +} + +TEST(TargetParserTest, AArch64ExtensionFeatures) { + std::vector<StringRef> Features; + unsigned Extensions = AArch64::AEK_CRC | AArch64::AEK_CRYPTO | + AArch64::AEK_FP | AArch64::AEK_SIMD | + AArch64::AEK_FP16 | AArch64::AEK_PROFILE | + AArch64::AEK_RAS; + + for (unsigned i = 0; i <= Extensions; i++) + EXPECT_TRUE(i == 0 ? !AArch64::getExtensionFeatures(i, Features) + : AArch64::getExtensionFeatures(i, Features)); +} + +TEST(TargetParserTest, AArch64ArchFeatures) { + std::vector<StringRef> Features; + + for (unsigned AK = 0; AK < static_cast<unsigned>(AArch64::ArchKind::AK_LAST); + AK++) + EXPECT_TRUE((AK == static_cast<unsigned>(AArch64::ArchKind::AK_INVALID) || + AK == static_cast<unsigned>(AArch64::ArchKind::AK_LAST)) + ? !AArch64::getArchFeatures(AK, Features) + : AArch64::getArchFeatures(AK, Features)); +} + +TEST(TargetParserTest, AArch64ArchExtFeature) { + const char *ArchExt[][4] = {{"crc", "nocrc", "+crc", "-crc"}, + {"crypto", "nocrypto", "+crypto", "-crypto"}, + {"fp", "nofp", "+fp-armv8", "-fp-armv8"}, + {"simd", "nosimd", "+neon", "-neon"}, + {"fp16", "nofp16", "+fullfp16", "-fullfp16"}, + {"profile", "noprofile", "+spe", "-spe"}, + {"ras", "noras", "+ras", "-ras"}}; + + for (unsigned i = 0; i < array_lengthof(ArchExt); i++) { + EXPECT_EQ(StringRef(ArchExt[i][2]), + AArch64::getArchExtFeature(ArchExt[i][0])); + EXPECT_EQ(StringRef(ArchExt[i][3]), + AArch64::getArchExtFeature(ArchExt[i][1])); + } +} +} diff --git a/unittests/Support/ThreadPool.cpp b/unittests/Support/ThreadPool.cpp index 69a24bc5444c..8e03aacfb1e0 100644 --- a/unittests/Support/ThreadPool.cpp +++ b/unittests/Support/ThreadPool.cpp @@ -31,16 +31,14 @@ protected: bool isUnsupportedOSOrEnvironment() { Triple Host(Triple::normalize(sys::getProcessTriple())); - if (std::find(UnsupportedEnvironments.begin(), UnsupportedEnvironments.end(), - Host.getEnvironment()) != UnsupportedEnvironments.end()) + if (find(UnsupportedEnvironments, Host.getEnvironment()) != + UnsupportedEnvironments.end()) return true; - if (std::find(UnsupportedOSs.begin(), UnsupportedOSs.end(), Host.getOS()) - != UnsupportedOSs.end()) + if (is_contained(UnsupportedOSs, Host.getOS())) return true; - if (std::find(UnsupportedArchs.begin(), UnsupportedArchs.end(), Host.getArch()) - != UnsupportedArchs.end()) + if (is_contained(UnsupportedArchs, Host.getArch())) return true; return false; diff --git a/unittests/Support/Threading.cpp b/unittests/Support/Threading.cpp new file mode 100644 index 000000000000..be53026415d7 --- /dev/null +++ b/unittests/Support/Threading.cpp @@ -0,0 +1,25 @@ +//===- unittests/Threading.cpp - Thread 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/Threading.h" +#include "llvm/Support/thread.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(Threading, PhysicalConcurrency) { + auto Num = heavyweight_hardware_concurrency(); + // Since Num is unsigned this will also catch us trying to + // return -1. + ASSERT_LE(Num, thread::hardware_concurrency()); +} + +} // end anon namespace diff --git a/unittests/Support/TimeValueTest.cpp b/unittests/Support/TimeValueTest.cpp deleted file mode 100644 index 3d2b9780c069..000000000000 --- a/unittests/Support/TimeValueTest.cpp +++ /dev/null @@ -1,40 +0,0 @@ -//===- llvm/unittest/Support/TimeValueTest.cpp - Time Value 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/TimeValue.h" -#include <time.h> - -using namespace llvm; -namespace { - -TEST(TimeValue, time_t) { - sys::TimeValue now = sys::TimeValue::now(); - time_t now_t = time(nullptr); - EXPECT_TRUE(std::abs(static_cast<long>(now_t - now.toEpochTime())) < 2); -} - -TEST(TimeValue, Win32FILETIME) { - uint64_t epoch_as_filetime = 0x19DB1DED53E8000ULL; - uint32_t ns = 765432100; - sys::TimeValue epoch; - - // FILETIME has 100ns of intervals. - uint64_t ft1970 = epoch_as_filetime + ns / 100; - epoch.fromWin32Time(ft1970); - - // The "seconds" part in Posix time may be expected as zero. - EXPECT_EQ(0u, epoch.toEpochTime()); - EXPECT_EQ(ns, static_cast<uint32_t>(epoch.nanoseconds())); - - // Confirm it reversible. - EXPECT_EQ(ft1970, epoch.toWin32Time()); -} - -} diff --git a/unittests/Support/TimerTest.cpp b/unittests/Support/TimerTest.cpp index f556a3f72c62..082a7e35db52 100644 --- a/unittests/Support/TimerTest.cpp +++ b/unittests/Support/TimerTest.cpp @@ -33,7 +33,7 @@ void SleepMS() { } TEST(Timer, Additivity) { - Timer T1("T1"); + Timer T1("T1", "T1"); EXPECT_TRUE(T1.isInitialized()); @@ -50,7 +50,7 @@ TEST(Timer, Additivity) { } TEST(Timer, CheckIfTriggered) { - Timer T1("T1"); + Timer T1("T1", "T1"); EXPECT_FALSE(T1.hasTriggered()); T1.startTimer(); diff --git a/unittests/Support/TrailingObjectsTest.cpp b/unittests/Support/TrailingObjectsTest.cpp index a1d3e7b3c864..cb5c47d1b25b 100644 --- a/unittests/Support/TrailingObjectsTest.cpp +++ b/unittests/Support/TrailingObjectsTest.cpp @@ -41,6 +41,9 @@ public: unsigned numShorts() const { return NumShorts; } // Pull some protected members in as public, for testability. + template <typename... Ty> + using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>; + using TrailingObjects::totalSizeToAlloc; using TrailingObjects::additionalSizeToAlloc; using TrailingObjects::getTrailingObjects; @@ -94,6 +97,9 @@ public: } // Pull some protected members in as public, for testability. + template <typename... Ty> + using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>; + using TrailingObjects::totalSizeToAlloc; using TrailingObjects::additionalSizeToAlloc; using TrailingObjects::getTrailingObjects; @@ -106,7 +112,16 @@ TEST(TrailingObjects, OneArg) { EXPECT_EQ(Class1::additionalSizeToAlloc<short>(1), sizeof(short)); EXPECT_EQ(Class1::additionalSizeToAlloc<short>(3), sizeof(short) * 3); + EXPECT_EQ(alignof(Class1), + alignof(Class1::FixedSizeStorage<short>::with_counts<1>::type)); + EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<1>::type), + llvm::alignTo(Class1::totalSizeToAlloc<short>(1), alignof(Class1))); EXPECT_EQ(Class1::totalSizeToAlloc<short>(1), sizeof(Class1) + sizeof(short)); + + EXPECT_EQ(alignof(Class1), + alignof(Class1::FixedSizeStorage<short>::with_counts<3>::type)); + EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<3>::type), + llvm::alignTo(Class1::totalSizeToAlloc<short>(3), alignof(Class1))); EXPECT_EQ(Class1::totalSizeToAlloc<short>(3), sizeof(Class1) + sizeof(short) * 3); @@ -120,9 +135,8 @@ TEST(TrailingObjects, TwoArg) { Class2 *C1 = Class2::create(4); Class2 *C2 = Class2::create(0, 4.2); - EXPECT_EQ(sizeof(Class2), - llvm::alignTo(sizeof(bool) * 2, llvm::alignOf<double>())); - EXPECT_EQ(llvm::alignOf<Class2>(), llvm::alignOf<double>()); + EXPECT_EQ(sizeof(Class2), llvm::alignTo(sizeof(bool) * 2, alignof(double))); + EXPECT_EQ(alignof(Class2), alignof(double)); EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(1, 0)), sizeof(double)); @@ -131,6 +145,14 @@ TEST(TrailingObjects, TwoArg) { EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(3, 1)), sizeof(double) * 3 + sizeof(short)); + EXPECT_EQ( + alignof(Class2), + (alignof( + Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type))); + EXPECT_EQ( + sizeof(Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type), + llvm::alignTo(Class2::totalSizeToAlloc<double, short>(1, 1), + alignof(Class2))); EXPECT_EQ((Class2::totalSizeToAlloc<double, short>(1, 1)), sizeof(Class2) + sizeof(double) + sizeof(short)); @@ -164,7 +186,18 @@ 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::alignTo(1, llvm::alignOf<double>())); + EXPECT_EQ(sizeof(Class3), llvm::alignTo(1, alignof(double))); + + EXPECT_EQ( + alignof(Class3), + (alignof(Class3::FixedSizeStorage<double, short, + bool>::with_counts<1, 1, 3>::type))); + EXPECT_EQ( + sizeof(Class3::FixedSizeStorage<double, short, + bool>::with_counts<1, 1, 3>::type), + llvm::alignTo(Class3::totalSizeToAlloc<double, short, bool>(1, 1, 3), + alignof(Class3))); + 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)); @@ -184,13 +217,22 @@ class Class4 final : public TrailingObjects<Class4, char, long> { TEST(TrailingObjects, Realignment) { EXPECT_EQ((Class4::additionalSizeToAlloc<char, long>(1, 1)), - llvm::alignTo(sizeof(long) + 1, llvm::alignOf<long>())); - EXPECT_EQ(sizeof(Class4), llvm::alignTo(1, llvm::alignOf<long>())); + llvm::alignTo(sizeof(long) + 1, alignof(long))); + EXPECT_EQ(sizeof(Class4), llvm::alignTo(1, alignof(long))); + + EXPECT_EQ( + alignof(Class4), + (alignof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type))); + EXPECT_EQ( + sizeof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type), + llvm::alignTo(Class4::totalSizeToAlloc<char, long>(1, 1), + alignof(Class4))); + 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)); EXPECT_EQ(C->getTrailingObjects<long>(), reinterpret_cast<long *>(llvm::alignAddr( - reinterpret_cast<char *>(C + 1) + 1, llvm::alignOf<long>()))); + reinterpret_cast<char *>(C + 1) + 1, alignof(long)))); } } diff --git a/unittests/Support/TrigramIndexTest.cpp b/unittests/Support/TrigramIndexTest.cpp new file mode 100644 index 000000000000..fb0ad1749bbd --- /dev/null +++ b/unittests/Support/TrigramIndexTest.cpp @@ -0,0 +1,132 @@ +//===- TrigramIndexTest.cpp - Unit tests for TrigramIndex -----------------===// +// +// 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/TrigramIndex.h" +#include "gtest/gtest.h" + +#include <string> +#include <vector> + +using namespace llvm; + +namespace { + +class TrigramIndexTest : public ::testing::Test { +protected: + std::unique_ptr<TrigramIndex> makeTrigramIndex( + std::vector<std::string> Rules) { + std::unique_ptr<TrigramIndex> TI = + make_unique<TrigramIndex>(); + for (auto &Rule : Rules) + TI->insert(Rule); + return TI; + } +}; + +TEST_F(TrigramIndexTest, Empty) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({}); + EXPECT_FALSE(TI->isDefeated()); + EXPECT_TRUE(TI->isDefinitelyOut("foo")); +} + +TEST_F(TrigramIndexTest, Basic) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"*hello*", "*wor.d*"}); + EXPECT_FALSE(TI->isDefeated()); + EXPECT_TRUE(TI->isDefinitelyOut("foo")); +} + +TEST_F(TrigramIndexTest, NoTrigramsInRules) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"b.r", "za*az"}); + EXPECT_TRUE(TI->isDefeated()); + EXPECT_FALSE(TI->isDefinitelyOut("foo")); + EXPECT_FALSE(TI->isDefinitelyOut("bar")); + EXPECT_FALSE(TI->isDefinitelyOut("zakaz")); +} + +TEST_F(TrigramIndexTest, NoTrigramsInARule) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"*hello*", "*wo.ld*"}); + EXPECT_TRUE(TI->isDefeated()); + EXPECT_FALSE(TI->isDefinitelyOut("foo")); +} + +TEST_F(TrigramIndexTest, RepetitiveRule) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"*bar*bar*bar*bar*bar", "bar*bar"}); + EXPECT_FALSE(TI->isDefeated()); + EXPECT_TRUE(TI->isDefinitelyOut("foo")); + EXPECT_TRUE(TI->isDefinitelyOut("bar")); + EXPECT_FALSE(TI->isDefinitelyOut("barbara")); + EXPECT_FALSE(TI->isDefinitelyOut("bar+bar")); +} + +TEST_F(TrigramIndexTest, PopularTrigram) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"*aaa*", "*aaaa*", "*aaaaa*", "*aaaaa*", "*aaaaaa*"}); + EXPECT_TRUE(TI->isDefeated()); +} + +TEST_F(TrigramIndexTest, PopularTrigram2) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"class1.h", "class2.h", "class3.h", "class4.h", "class.h"}); + EXPECT_TRUE(TI->isDefeated()); +} + +TEST_F(TrigramIndexTest, TooComplicatedRegex) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"[0-9]+"}); + EXPECT_TRUE(TI->isDefeated()); +} + +TEST_F(TrigramIndexTest, TooComplicatedRegex2) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"foo|bar"}); + EXPECT_TRUE(TI->isDefeated()); +} + +TEST_F(TrigramIndexTest, EscapedSymbols) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"*c\\+\\+*", "*hello\\\\world*", "a\\tb", "a\\0b"}); + EXPECT_FALSE(TI->isDefeated()); + EXPECT_FALSE(TI->isDefinitelyOut("c++")); + EXPECT_TRUE(TI->isDefinitelyOut("c\\+\\+")); + EXPECT_FALSE(TI->isDefinitelyOut("hello\\world")); + EXPECT_TRUE(TI->isDefinitelyOut("hello\\\\world")); + EXPECT_FALSE(TI->isDefinitelyOut("atb")); + EXPECT_TRUE(TI->isDefinitelyOut("a\\tb")); + EXPECT_TRUE(TI->isDefinitelyOut("a\tb")); + EXPECT_FALSE(TI->isDefinitelyOut("a0b")); +} + +TEST_F(TrigramIndexTest, Backreference1) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"*foo\\1*"}); + EXPECT_TRUE(TI->isDefeated()); +} + +TEST_F(TrigramIndexTest, Backreference2) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"*foo\\2*"}); + EXPECT_TRUE(TI->isDefeated()); +} + +TEST_F(TrigramIndexTest, Sequence) { + std::unique_ptr<TrigramIndex> TI = + makeTrigramIndex({"class1.h", "class2.h", "class3.h", "class4.h"}); + EXPECT_FALSE(TI->isDefeated()); + EXPECT_FALSE(TI->isDefinitelyOut("class1")); + EXPECT_TRUE(TI->isDefinitelyOut("class.h")); + EXPECT_TRUE(TI->isDefinitelyOut("class")); +} + +} // namespace diff --git a/unittests/Support/YAMLIOTest.cpp b/unittests/Support/YAMLIOTest.cpp index 5f35a802caa5..c3e18d332356 100644 --- a/unittests/Support/YAMLIOTest.cpp +++ b/unittests/Support/YAMLIOTest.cpp @@ -2299,3 +2299,80 @@ TEST(YAMLIO, TestWrapFlow) { out.clear(); } } + +struct MappingContext { + int A = 0; +}; +struct SimpleMap { + int B = 0; + int C = 0; +}; + +struct NestedMap { + NestedMap(MappingContext &Context) : Context(Context) {} + SimpleMap Simple; + MappingContext &Context; +}; + +namespace llvm { +namespace yaml { +template <> struct MappingContextTraits<SimpleMap, MappingContext> { + static void mapping(IO &io, SimpleMap &sm, MappingContext &Context) { + io.mapRequired("B", sm.B); + io.mapRequired("C", sm.C); + ++Context.A; + io.mapRequired("Context", Context.A); + } +}; + +template <> struct MappingTraits<NestedMap> { + static void mapping(IO &io, NestedMap &nm) { + io.mapRequired("Simple", nm.Simple, nm.Context); + } +}; +} +} + +TEST(YAMLIO, TestMapWithContext) { + MappingContext Context; + NestedMap Nested(Context); + std::string out; + llvm::raw_string_ostream ostr(out); + + Output yout(ostr, nullptr, 15); + + yout << Nested; + ostr.flush(); + EXPECT_EQ(1, Context.A); + EXPECT_EQ("---\n" + "Simple: \n" + " B: 0\n" + " C: 0\n" + " Context: 1\n" + "...\n", + out); + + out.clear(); + + Nested.Simple.B = 2; + Nested.Simple.C = 3; + yout << Nested; + ostr.flush(); + EXPECT_EQ(2, Context.A); + EXPECT_EQ("---\n" + "Simple: \n" + " B: 2\n" + " C: 3\n" + " Context: 2\n" + "...\n", + out); + out.clear(); +} + +TEST(YAMLIO, InvalidInput) { + // polluting 1 value in the sequence + Input yin("---\n- foo: 3\n bar: 5\n1\n- foo: 3\n bar: 5\n...\n"); + std::vector<FooBar> Data; + yin >> Data; + EXPECT_TRUE((bool)yin.error()); +} diff --git a/unittests/Support/raw_ostream_test.cpp b/unittests/Support/raw_ostream_test.cpp index ed6ddabe4634..f87d2f60d169 100644 --- a/unittests/Support/raw_ostream_test.cpp +++ b/unittests/Support/raw_ostream_test.cpp @@ -181,5 +181,153 @@ TEST(raw_ostreamTest, FormatDecimal) { printToString(format_decimal(INT64_MIN, 21), 21)); } +static std::string formatted_bytes_str(ArrayRef<uint8_t> Bytes, + llvm::Optional<uint64_t> Offset = None, + uint32_t NumPerLine = 16, + uint8_t ByteGroupSize = 4) { + std::string S; + raw_string_ostream Str(S); + Str << format_bytes(Bytes, Offset, NumPerLine, ByteGroupSize); + Str.flush(); + return S; +} + +static std::string format_bytes_with_ascii_str(ArrayRef<uint8_t> Bytes, + Optional<uint64_t> Offset = None, + uint32_t NumPerLine = 16, + uint8_t ByteGroupSize = 4) { + std::string S; + raw_string_ostream Str(S); + Str << format_bytes_with_ascii(Bytes, Offset, NumPerLine, ByteGroupSize); + Str.flush(); + return S; +} + +TEST(raw_ostreamTest, FormattedHexBytes) { + std::vector<uint8_t> Buf = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', + '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + ArrayRef<uint8_t> B(Buf); + + // Test invalid input. + EXPECT_EQ("", formatted_bytes_str(ArrayRef<uint8_t>())); + EXPECT_EQ("", format_bytes_with_ascii_str(ArrayRef<uint8_t>())); + //---------------------------------------------------------------------- + // Test hex byte output with the default 4 byte groups + //---------------------------------------------------------------------- + EXPECT_EQ("61", formatted_bytes_str(B.take_front())); + EXPECT_EQ("61626364 65", formatted_bytes_str(B.take_front(5))); + // Test that 16 bytes get written to a line correctly. + EXPECT_EQ("61626364 65666768 696a6b6c 6d6e6f70", + formatted_bytes_str(B.take_front(16))); + // Test raw bytes with default 16 bytes per line wrapping. + EXPECT_EQ("61626364 65666768 696a6b6c 6d6e6f70\n71", + formatted_bytes_str(B.take_front(17))); + // Test raw bytes with 1 bytes per line wrapping. + EXPECT_EQ("61\n62\n63\n64\n65\n66", + formatted_bytes_str(B.take_front(6), None, 1)); + // Test raw bytes with 7 bytes per line wrapping. + EXPECT_EQ("61626364 656667\n68696a6b 6c6d6e\n6f7071", + formatted_bytes_str(B.take_front(17), None, 7)); + // Test raw bytes with 8 bytes per line wrapping. + EXPECT_EQ("61626364 65666768\n696a6b6c 6d6e6f70\n71", + formatted_bytes_str(B.take_front(17), None, 8)); + //---------------------------------------------------------------------- + // Test hex byte output with the 1 byte groups + //---------------------------------------------------------------------- + EXPECT_EQ("61 62 63 64 65", + formatted_bytes_str(B.take_front(5), None, 16, 1)); + // Test that 16 bytes get written to a line correctly. + EXPECT_EQ("61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70", + formatted_bytes_str(B.take_front(16), None, 16, 1)); + // Test raw bytes with default 16 bytes per line wrapping. + EXPECT_EQ("61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70\n71", + formatted_bytes_str(B.take_front(17), None, 16, 1)); + // Test raw bytes with 7 bytes per line wrapping. + EXPECT_EQ("61 62 63 64 65 66 67\n68 69 6a 6b 6c 6d 6e\n6f 70 71", + formatted_bytes_str(B.take_front(17), None, 7, 1)); + // Test raw bytes with 8 bytes per line wrapping. + EXPECT_EQ("61 62 63 64 65 66 67 68\n69 6a 6b 6c 6d 6e 6f 70\n71", + formatted_bytes_str(B.take_front(17), None, 8, 1)); + //---------------------------------------------------------------------- + // Test hex byte output with the 2 byte groups + //---------------------------------------------------------------------- + EXPECT_EQ("6162 6364 65", formatted_bytes_str(B.take_front(5), None, 16, 2)); + // Test that 16 bytes get written to a line correctly. + EXPECT_EQ("6162 6364 6566 6768 696a 6b6c 6d6e 6f70", + formatted_bytes_str(B.take_front(16), None, 16, 2)); + // Test raw bytes with default 16 bytes per line wrapping. + EXPECT_EQ("6162 6364 6566 6768 696a 6b6c 6d6e 6f70\n71", + formatted_bytes_str(B.take_front(17), None, 16, 2)); + // Test raw bytes with 7 bytes per line wrapping. + EXPECT_EQ("6162 6364 6566 67\n6869 6a6b 6c6d 6e\n6f70 71", + formatted_bytes_str(B.take_front(17), None, 7, 2)); + // Test raw bytes with 8 bytes per line wrapping. + EXPECT_EQ("6162 6364 6566 6768\n696a 6b6c 6d6e 6f70\n71", + formatted_bytes_str(B.take_front(17), None, 8, 2)); + + //---------------------------------------------------------------------- + // Test hex bytes with offset with the default 4 byte groups. + //---------------------------------------------------------------------- + EXPECT_EQ("0000: 61", formatted_bytes_str(B.take_front(), 0x0)); + EXPECT_EQ("1000: 61", formatted_bytes_str(B.take_front(), 0x1000)); + EXPECT_EQ("1000: 61\n1001: 62", + formatted_bytes_str(B.take_front(2), 0x1000, 1)); + //---------------------------------------------------------------------- + // Test hex bytes with ASCII with the default 4 byte groups. + //---------------------------------------------------------------------- + EXPECT_EQ("61626364 65666768 696a6b6c 6d6e6f70 |abcdefghijklmnop|", + format_bytes_with_ascii_str(B.take_front(16))); + EXPECT_EQ("61626364 65666768 |abcdefgh|\n" + "696a6b6c 6d6e6f70 |ijklmnop|", + format_bytes_with_ascii_str(B.take_front(16), None, 8)); + EXPECT_EQ("61626364 65666768 |abcdefgh|\n696a6b6c |ijkl|", + format_bytes_with_ascii_str(B.take_front(12), None, 8)); + std::vector<uint8_t> Unprintable = {'a', '\x1e', 'b', '\x1f'}; + // Make sure the ASCII is still lined up correctly when fewer bytes than 16 + // bytes per line are available. The ASCII should still be aligned as if 16 + // bytes of hex might be displayed. + EXPECT_EQ("611e621f |a.b.|", + format_bytes_with_ascii_str(Unprintable)); + //---------------------------------------------------------------------- + // Test hex bytes with ASCII with offsets with the default 4 byte groups. + //---------------------------------------------------------------------- + EXPECT_EQ("0000: 61626364 65666768 " + "696a6b6c 6d6e6f70 |abcdefghijklmnop|", + format_bytes_with_ascii_str(B.take_front(16), 0)); + EXPECT_EQ("0000: 61626364 65666768 |abcdefgh|\n" + "0008: 696a6b6c 6d6e6f70 |ijklmnop|", + format_bytes_with_ascii_str(B.take_front(16), 0, 8)); + EXPECT_EQ("0000: 61626364 656667 |abcdefg|\n" + "0007: 68696a6b 6c |hijkl|", + format_bytes_with_ascii_str(B.take_front(12), 0, 7)); + + //---------------------------------------------------------------------- + // Test hex bytes with ASCII with offsets with the default 2 byte groups. + //---------------------------------------------------------------------- + EXPECT_EQ("0000: 6162 6364 6566 6768 " + "696a 6b6c 6d6e 6f70 |abcdefghijklmnop|", + format_bytes_with_ascii_str(B.take_front(16), 0, 16, 2)); + EXPECT_EQ("0000: 6162 6364 6566 6768 |abcdefgh|\n" + "0008: 696a 6b6c 6d6e 6f70 |ijklmnop|", + format_bytes_with_ascii_str(B.take_front(16), 0, 8, 2)); + EXPECT_EQ("0000: 6162 6364 6566 67 |abcdefg|\n" + "0007: 6869 6a6b 6c |hijkl|", + format_bytes_with_ascii_str(B.take_front(12), 0, 7, 2)); + + //---------------------------------------------------------------------- + // Test hex bytes with ASCII with offsets with the default 1 byte groups. + //---------------------------------------------------------------------- + EXPECT_EQ("0000: 61 62 63 64 65 66 67 68 " + "69 6a 6b 6c 6d 6e 6f 70 |abcdefghijklmnop|", + format_bytes_with_ascii_str(B.take_front(16), 0, 16, 1)); + EXPECT_EQ("0000: 61 62 63 64 65 66 67 68 |abcdefgh|\n" + "0008: 69 6a 6b 6c 6d 6e 6f 70 |ijklmnop|", + format_bytes_with_ascii_str(B.take_front(16), 0, 8, 1)); + EXPECT_EQ("0000: 61 62 63 64 65 66 67 |abcdefg|\n" + "0007: 68 69 6a 6b 6c |hijkl|", + format_bytes_with_ascii_str(B.take_front(12), 0, 7, 1)); +} } diff --git a/unittests/Support/raw_pwrite_stream_test.cpp b/unittests/Support/raw_pwrite_stream_test.cpp index a62f6bacb070..08b2f90d6054 100644 --- a/unittests/Support/raw_pwrite_stream_test.cpp +++ b/unittests/Support/raw_pwrite_stream_test.cpp @@ -10,10 +10,22 @@ #include "gtest/gtest.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; +#define ASSERT_NO_ERROR(x) \ + if (std::error_code ASSERT_NO_ERROR_ec = x) { \ + SmallString<128> MessageStorage; \ + raw_svector_ostream Message(MessageStorage); \ + Message << #x ": did not return errc::success.\n" \ + << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \ + << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \ + GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \ + } else { \ + } + namespace { TEST(raw_pwrite_ostreamTest, TestSVector) { @@ -32,10 +44,28 @@ TEST(raw_pwrite_ostreamTest, TestSVector) { #endif } +#ifdef _WIN32 +#define setenv(name, var, ignore) _putenv_s(name, var) +#endif + TEST(raw_pwrite_ostreamTest, TestFD) { SmallString<64> Path; int FD; - sys::fs::createTemporaryFile("foo", "bar", FD, Path); + + // If we want to clean up from a death test, we have to remove the file from + // the parent process. Have the parent create the file, pass it via + // environment variable to the child, let the child crash, and then remove it + // in the parent. + const char *ParentPath = getenv("RAW_PWRITE_TEST_FILE"); + if (ParentPath) { + Path = ParentPath; + ASSERT_NO_ERROR(sys::fs::openFileForRead(Path, FD)); + } else { + ASSERT_NO_ERROR(sys::fs::createTemporaryFile("foo", "bar", FD, Path)); + setenv("RAW_PWRITE_TEST_FILE", Path.c_str(), true); + } + FileRemover Cleanup(Path); + raw_fd_ostream OS(FD, true); OS << "abcd"; StringRef Test = "test"; diff --git a/unittests/Support/raw_sha1_ostream_test.cpp b/unittests/Support/raw_sha1_ostream_test.cpp index db2a3e9ab643..1bb4e2eb1d58 100644 --- a/unittests/Support/raw_sha1_ostream_test.cpp +++ b/unittests/Support/raw_sha1_ostream_test.cpp @@ -37,6 +37,13 @@ TEST(raw_sha1_ostreamTest, Basic) { ASSERT_EQ("2EF7BDE608CE5404E97D5F042F95F89F1C232871", Hash); } +TEST(sha1_hash_test, Basic) { + ArrayRef<uint8_t> Input((const uint8_t *)"Hello World!", 12); + std::array<uint8_t, 20> Vec = SHA1::hash(Input); + std::string Hash = toHex({(const char *)Vec.data(), 20}); + 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) { diff --git a/unittests/Support/xxhashTest.cpp b/unittests/Support/xxhashTest.cpp new file mode 100644 index 000000000000..6a88a2d3a5c5 --- /dev/null +++ b/unittests/Support/xxhashTest.cpp @@ -0,0 +1,20 @@ +//===- llvm/unittest/Support/xxhashTest.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/xxhash.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(xxhashTest, Basic) { + EXPECT_EQ(0x33bf00a859c4ba3fU, xxHash64("foo")); + EXPECT_EQ(0x48a37c90ad27a659U, xxHash64("bar")); + EXPECT_EQ(0x69196c1b3af0bff9U, + xxHash64("0123456789abcdefghijklmnopqrstuvwxyz")); +} diff --git a/unittests/Target/AArch64/CMakeLists.txt b/unittests/Target/AArch64/CMakeLists.txt new file mode 100644 index 000000000000..94addf4fc121 --- /dev/null +++ b/unittests/Target/AArch64/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/lib/Target/AArch64 + ${CMAKE_BINARY_DIR}/lib/Target/AArch64 + ) + +set(LLVM_LINK_COMPONENTS + AArch64CodeGen + AArch64Desc + AArch64Info + CodeGen + Core + MC + MIRParser + SelectionDAG + Support + Target + ) + +add_llvm_unittest(AArch64Tests + InstSizes.cpp + ) diff --git a/unittests/Target/AArch64/InstSizes.cpp b/unittests/Target/AArch64/InstSizes.cpp new file mode 100644 index 000000000000..22b47c6852ab --- /dev/null +++ b/unittests/Target/AArch64/InstSizes.cpp @@ -0,0 +1,122 @@ +#include "AArch64Subtarget.h" +#include "AArch64TargetMachine.h" +#include "llvm/CodeGen/MIRParser/MIRParser.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { +std::unique_ptr<TargetMachine> createTargetMachine() { + auto TT(Triple::normalize("aarch64--")); + std::string CPU("generic"); + std::string FS(""); + + LLVMInitializeAArch64TargetInfo(); + LLVMInitializeAArch64Target(); + LLVMInitializeAArch64TargetMC(); + + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error); + assert(TheTarget && "Target not registered"); + + return std::unique_ptr<TargetMachine>( + TheTarget->createTargetMachine(TT, CPU, FS, TargetOptions(), None, + CodeModel::Default, CodeGenOpt::Default)); +} + +std::unique_ptr<AArch64InstrInfo> createInstrInfo(TargetMachine *TM) { + AArch64Subtarget ST(TM->getTargetTriple(), TM->getTargetCPU(), + TM->getTargetFeatureString(), *TM, /* isLittle */ false); + return llvm::make_unique<AArch64InstrInfo>(ST); +} + +/// The \p InputIRSnippet is only needed for things that can't be expressed in +/// the \p InputMIRSnippet (global variables etc) +/// TODO: Some of this might be useful for other architectures as well - extract +/// the platform-independent parts somewhere they can be reused. +void runChecks( + TargetMachine *TM, AArch64InstrInfo *II, const StringRef InputIRSnippet, + const StringRef InputMIRSnippet, + std::function<void(AArch64InstrInfo &, MachineFunction &)> Checks) { + LLVMContext Context; + + auto MIRString = + "--- |\n" + " declare void @sizes()\n" + + InputIRSnippet.str() + + "...\n" + "---\n" + "name: sizes\n" + "body: |\n" + " bb.0:\n" + + InputMIRSnippet.str(); + + std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRString); + std::unique_ptr<MIRParser> MParser = + createMIRParser(std::move(MBuffer), Context); + assert(MParser && "Couldn't create MIR parser"); + + std::unique_ptr<Module> M = MParser->parseLLVMModule(); + assert(M && "Couldn't parse module"); + + M->setTargetTriple(TM->getTargetTriple().getTriple()); + M->setDataLayout(TM->createDataLayout()); + + auto F = M->getFunction("sizes"); + assert(F && "Couldn't find intended function"); + + MachineModuleInfo MMI(TM); + MMI.setMachineFunctionInitializer(MParser.get()); + auto &MF = MMI.getMachineFunction(*F); + + Checks(*II, MF); +} + +} // anonymous namespace + +TEST(InstSizes, STACKMAP) { + std::unique_ptr<TargetMachine> TM = createTargetMachine(); + std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get()); + + runChecks(TM.get(), II.get(), "", " STACKMAP 0, 16\n" + " STACKMAP 1, 32\n", + [](AArch64InstrInfo &II, MachineFunction &MF) { + auto I = MF.begin()->begin(); + EXPECT_EQ(16u, II.getInstSizeInBytes(*I)); + ++I; + EXPECT_EQ(32u, II.getInstSizeInBytes(*I)); + }); +} + +TEST(InstSizes, PATCHPOINT) { + std::unique_ptr<TargetMachine> TM = createTargetMachine(); + std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get()); + + runChecks(TM.get(), II.get(), "", + " PATCHPOINT 0, 16, 0, 0, 0, csr_aarch64_aapcs\n" + " PATCHPOINT 1, 32, 0, 0, 0, csr_aarch64_aapcs\n", + [](AArch64InstrInfo &II, MachineFunction &MF) { + auto I = MF.begin()->begin(); + EXPECT_EQ(16u, II.getInstSizeInBytes(*I)); + ++I; + EXPECT_EQ(32u, II.getInstSizeInBytes(*I)); + }); +} + +TEST(InstSizes, TLSDESC_CALLSEQ) { + std::unique_ptr<TargetMachine> TM = createTargetMachine(); + std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get()); + + runChecks( + TM.get(), II.get(), + " @ThreadLocalGlobal = external thread_local global i32, align 8\n", + " TLSDESC_CALLSEQ target-flags(aarch64-tls) @ThreadLocalGlobal\n", + [](AArch64InstrInfo &II, MachineFunction &MF) { + auto I = MF.begin()->begin(); + EXPECT_EQ(16u, II.getInstSizeInBytes(*I)); + }); +} diff --git a/unittests/Target/CMakeLists.txt b/unittests/Target/CMakeLists.txt new file mode 100644 index 000000000000..9015029b0970 --- /dev/null +++ b/unittests/Target/CMakeLists.txt @@ -0,0 +1,5 @@ +foreach(t ${LLVM_TARGETS_TO_BUILD}) + if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${t}) + add_subdirectory(${t}) + endif() +endforeach() diff --git a/unittests/Transforms/Utils/ASanStackFrameLayoutTest.cpp b/unittests/Transforms/Utils/ASanStackFrameLayoutTest.cpp index 0d3a239da347..2337ec04776a 100644 --- a/unittests/Transforms/Utils/ASanStackFrameLayoutTest.cpp +++ b/unittests/Transforms/Utils/ASanStackFrameLayoutTest.cpp @@ -21,82 +21,88 @@ ShadowBytesToString(ArrayRef<uint8_t> ShadowBytes) { case kAsanStackLeftRedzoneMagic: os << "L"; break; case kAsanStackRightRedzoneMagic: os << "R"; break; case kAsanStackMidRedzoneMagic: os << "M"; break; + case kAsanStackUseAfterScopeMagic: + os << "S"; + break; default: os << (unsigned)ShadowBytes[i]; } } return os.str(); } -static void TestLayout(SmallVector<ASanStackVariableDescription, 10> Vars, - size_t Granularity, size_t MinHeaderSize, - const std::string &ExpectedDescr, - const std::string &ExpectedShadow) { - ASanStackFrameLayout L; - ComputeASanStackFrameLayout(Vars, Granularity, MinHeaderSize, &L); - EXPECT_EQ(ExpectedDescr, L.DescriptionString); - EXPECT_EQ(ExpectedShadow, ShadowBytesToString(L.ShadowBytes)); -} +// Use macro to preserve line information in EXPECT_EQ output. +#define TEST_LAYOUT(V, Granularity, MinHeaderSize, ExpectedDescr, \ + ExpectedShadow, ExpectedShadowAfterScope) \ + { \ + SmallVector<ASanStackVariableDescription, 10> Vars = V; \ + ASanStackFrameLayout L = \ + ComputeASanStackFrameLayout(Vars, Granularity, MinHeaderSize); \ + EXPECT_STREQ(ExpectedDescr, \ + ComputeASanStackFrameDescription(Vars).c_str()); \ + EXPECT_EQ(ExpectedShadow, ShadowBytesToString(GetShadowBytes(Vars, L))); \ + EXPECT_EQ(ExpectedShadowAfterScope, \ + ShadowBytesToString(GetShadowBytesAfterScope(Vars, L))); \ + } TEST(ASanStackFrameLayout, Test) { -#define VEC1(a) SmallVector<ASanStackVariableDescription, 10>(1, a) -#define VEC(a) \ - SmallVector<ASanStackVariableDescription, 10>(a, a + sizeof(a) / sizeof(a[0])) - -#define VAR(name, size, alignment) \ +#define VAR(name, size, lifetime, alignment, line) \ ASanStackVariableDescription name##size##_##alignment = { \ #name #size "_" #alignment, \ size, \ + lifetime, \ alignment, \ 0, \ - 0 \ + 0, \ + line, \ } - VAR(a, 1, 1); - VAR(p, 1, 32); - VAR(p, 1, 256); - VAR(a, 2, 1); - VAR(a, 3, 1); - VAR(a, 4, 1); - VAR(a, 7, 1); - VAR(a, 8, 1); - VAR(a, 9, 1); - VAR(a, 16, 1); - VAR(a, 41, 1); - VAR(a, 105, 1); + VAR(a, 1, 0, 1, 0); + VAR(p, 1, 0, 32, 15); + VAR(p, 1, 0, 256, 2700); + VAR(a, 2, 0, 1, 0); + VAR(a, 3, 0, 1, 0); + VAR(a, 4, 0, 1, 0); + VAR(a, 7, 0, 1, 0); + VAR(a, 8, 8, 1, 0); + VAR(a, 9, 0, 1, 0); + VAR(a, 16, 16, 1, 0); + VAR(a, 41, 9, 1, 7); + VAR(a, 105, 103, 1, 0); - TestLayout(VEC1(a1_1), 8, 16, "1 16 1 4 a1_1", "LL1R"); - TestLayout(VEC1(a1_1), 64, 64, "1 64 1 4 a1_1", "L1"); - TestLayout(VEC1(p1_32), 8, 32, "1 32 1 5 p1_32", "LLLL1RRR"); - TestLayout(VEC1(p1_32), 8, 64, "1 64 1 5 p1_32", "LLLLLLLL1RRRRRRR"); + TEST_LAYOUT({a1_1}, 8, 16, "1 16 1 4 a1_1", "LL1R", "LL1R"); + TEST_LAYOUT({a1_1}, 64, 64, "1 64 1 4 a1_1", "L1", "L1"); + TEST_LAYOUT({p1_32}, 8, 32, "1 32 1 8 p1_32:15", "LLLL1RRR", "LLLL1RRR"); + TEST_LAYOUT({p1_32}, 8, 64, "1 64 1 8 p1_32:15", "LLLLLLLL1RRRRRRR", + "LLLLLLLL1RRRRRRR"); - TestLayout(VEC1(a1_1), 8, 32, "1 32 1 4 a1_1", "LLLL1RRR"); - TestLayout(VEC1(a2_1), 8, 32, "1 32 2 4 a2_1", "LLLL2RRR"); - TestLayout(VEC1(a3_1), 8, 32, "1 32 3 4 a3_1", "LLLL3RRR"); - TestLayout(VEC1(a4_1), 8, 32, "1 32 4 4 a4_1", "LLLL4RRR"); - TestLayout(VEC1(a7_1), 8, 32, "1 32 7 4 a7_1", "LLLL7RRR"); - TestLayout(VEC1(a8_1), 8, 32, "1 32 8 4 a8_1", "LLLL0RRR"); - TestLayout(VEC1(a9_1), 8, 32, "1 32 9 4 a9_1", "LLLL01RR"); - TestLayout(VEC1(a16_1), 8, 32, "1 32 16 5 a16_1", "LLLL00RR"); - TestLayout(VEC1(p1_256), 8, 32, "1 256 1 6 p1_256", - "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1RRR"); - TestLayout(VEC1(a41_1), 8, 32, "1 32 41 5 a41_1", "LLLL000001RRRRRR"); - TestLayout(VEC1(a105_1), 8, 32, "1 32 105 6 a105_1", - "LLLL00000000000001RRRRRR"); + TEST_LAYOUT({a1_1}, 8, 32, "1 32 1 4 a1_1", "LLLL1RRR", "LLLL1RRR"); + TEST_LAYOUT({a2_1}, 8, 32, "1 32 2 4 a2_1", "LLLL2RRR", "LLLL2RRR"); + TEST_LAYOUT({a3_1}, 8, 32, "1 32 3 4 a3_1", "LLLL3RRR", "LLLL3RRR"); + TEST_LAYOUT({a4_1}, 8, 32, "1 32 4 4 a4_1", "LLLL4RRR", "LLLL4RRR"); + TEST_LAYOUT({a7_1}, 8, 32, "1 32 7 4 a7_1", "LLLL7RRR", "LLLL7RRR"); + TEST_LAYOUT({a8_1}, 8, 32, "1 32 8 4 a8_1", "LLLL0RRR", "LLLLSRRR"); + TEST_LAYOUT({a9_1}, 8, 32, "1 32 9 4 a9_1", "LLLL01RR", "LLLL01RR"); + TEST_LAYOUT({a16_1}, 8, 32, "1 32 16 5 a16_1", "LLLL00RR", "LLLLSSRR"); + TEST_LAYOUT({p1_256}, 8, 32, "1 256 1 11 p1_256:2700", + "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1RRR", + "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1RRR"); + TEST_LAYOUT({a41_1}, 8, 32, "1 32 41 7 a41_1:7", "LLLL000001RRRRRR", + "LLLLSS0001RRRRRR"); + TEST_LAYOUT({a105_1}, 8, 32, "1 32 105 6 a105_1", "LLLL00000000000001RRRRRR", + "LLLLSSSSSSSSSSSSS1RRRRRR"); { - ASanStackVariableDescription t[] = {a1_1, p1_256}; - TestLayout(VEC(t), 8, 32, - "2 256 1 6 p1_256 272 1 4 a1_1", - "LLLLLLLL" "LLLLLLLL" "LLLLLLLL" "LLLLLLLL" "1M1R"); + SmallVector<ASanStackVariableDescription, 10> t = {a1_1, p1_256}; + TEST_LAYOUT(t, 8, 32, "2 256 1 11 p1_256:2700 272 1 4 a1_1", + "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1M1R", + "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1M1R"); } { - ASanStackVariableDescription t[] = {a1_1, a16_1, a41_1}; - TestLayout(VEC(t), 8, 32, - "3 32 1 4 a1_1 48 16 5 a16_1 80 41 5 a41_1", - "LLLL" "1M00" "MM00" "0001" "RRRR"); + SmallVector<ASanStackVariableDescription, 10> t = {a1_1, a16_1, a41_1}; + TEST_LAYOUT(t, 8, 32, "3 32 1 4 a1_1 48 16 5 a16_1 80 41 7 a41_1:7", + "LLLL1M00MM000001RRRR", "LLLL1MSSMMSS0001RRRR"); } -#undef VEC1 -#undef VEC #undef VAR +#undef TEST_LAYOUT } diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt index 657d151048e8..c0f37418e492 100644 --- a/unittests/Transforms/Utils/CMakeLists.txt +++ b/unittests/Transforms/Utils/CMakeLists.txt @@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(UtilsTests ASanStackFrameLayoutTest.cpp Cloning.cpp + FunctionComparator.cpp IntegerDivision.cpp Local.cpp MemorySSA.cpp diff --git a/unittests/Transforms/Utils/Cloning.cpp b/unittests/Transforms/Utils/Cloning.cpp index f53e0a95e94d..216bd32c50d2 100644 --- a/unittests/Transforms/Utils/Cloning.cpp +++ b/unittests/Transforms/Utils/Cloning.cpp @@ -231,12 +231,14 @@ protected: DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None); DISubroutineType *FuncType = DBuilder.createSubroutineType(ParamTypes); - auto *CU = - DBuilder.createCompileUnit(dwarf::DW_LANG_C99, "filename.c", - "/file/dir", "CloneFunc", false, "", 0); - - auto *Subprogram = DBuilder.createFunction( - CU, "f", "f", File, 4, FuncType, true, true, 3, 0, false); + auto *CU = DBuilder.createCompileUnit(dwarf::DW_LANG_C99, + DBuilder.createFile("filename.c", + "/file/dir"), + "CloneFunc", false, "", 0); + + auto *Subprogram = + DBuilder.createFunction(CU, "f", "f", File, 4, FuncType, true, true, 3, + DINode::FlagZero, false); OldFunc->setSubprogram(Subprogram); // Function body @@ -252,8 +254,7 @@ protected: Instruction* Terminator = IBuilder.CreateRetVoid(); // Create a local variable around the alloca - auto *IntType = - DBuilder.createBasicType("int", 32, 0, dwarf::DW_ATE_signed); + auto *IntType = DBuilder.createBasicType("int", 32, dwarf::DW_ATE_signed); auto *E = DBuilder.createExpression(); auto *Variable = DBuilder.createAutoVariable(Subprogram, "x", File, 5, IntType, true); @@ -268,7 +269,8 @@ protected: // Create another, empty, compile unit DIBuilder DBuilder2(*M); DBuilder2.createCompileUnit(dwarf::DW_LANG_C99, - "extra.c", "/file/dir", "CloneFunc", false, "", 0); + DBuilder.createFile("extra.c", "/file/dir"), + "CloneFunc", false, "", 0); DBuilder2.finalize(); } @@ -403,6 +405,11 @@ protected: void SetupModule() { OldM = new Module("", C); } void CreateOldModule() { + auto GV = new GlobalVariable( + *OldM, Type::getInt32Ty(C), false, GlobalValue::ExternalLinkage, + ConstantInt::get(Type::getInt32Ty(C), 1), "gv"); + GV->addMetadata(LLVMContext::MD_type, *MDNode::get(C, {})); + DIBuilder DBuilder(*OldM); IRBuilder<> IBuilder(C); @@ -417,12 +424,14 @@ protected: 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); + auto *CU = DBuilder.createCompileUnit(dwarf::DW_LANG_C99, + DBuilder.createFile("filename.c", + "/file/dir"), + "CloneModule", false, "", 0); // Function DI - auto *Subprogram = DBuilder.createFunction(CU, "f", "f", File, 4, DFuncType, - true, true, 3, 0, false); + auto *Subprogram = + DBuilder.createFunction(CU, "f", "f", File, 4, DFuncType, true, true, 3, + DINode::FlagZero, false); F->setSubprogram(Subprogram); auto *Entry = BasicBlock::Create(C, "", F); @@ -458,4 +467,9 @@ TEST_F(CloneModule, Subprogram) { EXPECT_EQ(SP->getFile()->getFilename(), "filename.c"); EXPECT_EQ(SP->getLine(), (unsigned)4); } + +TEST_F(CloneModule, GlobalMetadata) { + GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); + EXPECT_NE(nullptr, NewGV->getMetadata(LLVMContext::MD_type)); +} } diff --git a/unittests/Transforms/Utils/FunctionComparator.cpp b/unittests/Transforms/Utils/FunctionComparator.cpp new file mode 100644 index 000000000000..ff68cd6224d7 --- /dev/null +++ b/unittests/Transforms/Utils/FunctionComparator.cpp @@ -0,0 +1,130 @@ +//===- FunctionComparator.cpp - Unit tests for FunctionComparator ---------===// +// +// 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/FunctionComparator.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/LLVMContext.h" +#include "gtest/gtest.h" + +using namespace llvm; + +/// Generates a simple test function. +struct TestFunction { + Function *F; + BasicBlock *BB; + Constant *C; + Instruction *I; + Type *T; + + TestFunction(LLVMContext &Ctx, Module &M, int addVal) { + IRBuilder<> B(Ctx); + T = B.getInt8Ty(); + F = Function::Create(FunctionType::get(T, {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + BB = BasicBlock::Create(Ctx, "", F); + B.SetInsertPoint(BB); + Argument *PointerArg = &*F->arg_begin(); + LoadInst *LoadInst = B.CreateLoad(PointerArg); + C = B.getInt8(addVal); + I = cast<Instruction>(B.CreateAdd(LoadInst, C)); + B.CreateRet(I); + } +}; + +/// A class for testing the FunctionComparator API. +/// +/// The main purpose is to test if the required protected functions are +/// accessible from a derived class of FunctionComparator. +class TestComparator : public FunctionComparator { +public: + TestComparator(const Function *F1, const Function *F2, + GlobalNumberState *GN) + : FunctionComparator(F1, F2, GN) { + } + + bool testFunctionAccess(const Function *F1, const Function *F2) { + // Test if FnL and FnR are accessible. + return F1 == FnL && F2 == FnR; + } + + int testCompare() { + return compare(); + } + + int testCompareSignature() { + beginCompare(); + return compareSignature(); + } + + int testCmpBasicBlocks(BasicBlock *BBL, BasicBlock *BBR) { + beginCompare(); + return cmpBasicBlocks(BBL, BBR); + } + + int testCmpConstants(const Constant *L, const Constant *R) { + beginCompare(); + return cmpConstants(L, R); + } + + int testCmpGlobalValues(GlobalValue *L, GlobalValue *R) { + beginCompare(); + return cmpGlobalValues(L, R); + } + + int testCmpValues(const Value *L, const Value *R) { + beginCompare(); + return cmpValues(L, R); + } + + int testCmpOperations(const Instruction *L, const Instruction *R, + bool &needToCmpOperands) { + beginCompare(); + return cmpOperations(L, R, needToCmpOperands); + } + + int testCmpTypes(Type *TyL, Type *TyR) { + beginCompare(); + return cmpTypes(TyL, TyR); + } + + int testCmpPrimitives() { + beginCompare(); + return + cmpNumbers(2, 3) + + cmpAPInts(APInt(32, 2), APInt(32, 3)) + + cmpAPFloats(APFloat(2.0), APFloat(3.0)) + + cmpMem("2", "3"); + } +}; + +/// A sanity check for the FunctionComparator API. +TEST(FunctionComparatorTest, TestAPI) { + LLVMContext C; + Module M("test", C); + TestFunction F1(C, M, 27); + TestFunction F2(C, M, 28); + + GlobalNumberState GN; + TestComparator Cmp(F1.F, F2.F, &GN); + + EXPECT_TRUE(Cmp.testFunctionAccess(F1.F, F2.F)); + EXPECT_EQ(Cmp.testCompare(), -1); + EXPECT_EQ(Cmp.testCompareSignature(), 0); + EXPECT_EQ(Cmp.testCmpBasicBlocks(F1.BB, F2.BB), -1); + EXPECT_EQ(Cmp.testCmpConstants(F1.C, F2.C), -1); + EXPECT_EQ(Cmp.testCmpGlobalValues(F1.F, F2.F), -1); + EXPECT_EQ(Cmp.testCmpValues(F1.I, F2.I), 0); + bool needToCmpOperands = false; + EXPECT_EQ(Cmp.testCmpOperations(F1.I, F2.I, needToCmpOperands), 0); + EXPECT_TRUE(needToCmpOperands); + EXPECT_EQ(Cmp.testCmpTypes(F1.T, F2.T), 0); + EXPECT_EQ(Cmp.testCmpPrimitives(), -4); +} diff --git a/unittests/Transforms/Utils/MemorySSA.cpp b/unittests/Transforms/Utils/MemorySSA.cpp index c21121f78705..945fe32c316c 100644 --- a/unittests/Transforms/Utils/MemorySSA.cpp +++ b/unittests/Transforms/Utils/MemorySSA.cpp @@ -42,14 +42,17 @@ protected: AssumptionCache AC; AAResults AA; BasicAAResult BAA; - MemorySSA MSSA; + // We need to defer MSSA construction until AA is *entirely* set up, which + // requires calling addAAResult. Hence, we just use a pointer here. + std::unique_ptr<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) { + BAA(Test.DL, Test.TLI, AC, &DT) { AA.addAAResult(BAA); - Walker = MSSA.getWalker(); + MSSA = make_unique<MemorySSA>(*Test.F, &AA, &DT); + Walker = MSSA->getWalker(); } }; @@ -65,10 +68,10 @@ public: : M("MemorySSATest", C), B(C), DL(DLString), TLI(TLII), F(nullptr) {} }; -TEST_F(MemorySSATest, CreateALoadAndPhi) { +TEST_F(MemorySSATest, CreateALoad) { // 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. + // building MemorySSA, create a load after the merge point, and use it to test + // updating by creating an access for the load. F = Function::Create( FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), GlobalValue::ExternalLinkage, "F", &M); @@ -80,23 +83,19 @@ TEST_F(MemorySSATest, CreateALoadAndPhi) { B.CreateCondBr(B.getTrue(), Left, Right); B.SetInsertPoint(Left); Argument *PointerArg = &*F->arg_begin(); - StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); + B.CreateStore(B.getInt8(16), PointerArg); BranchInst::Create(Merge, Left); BranchInst::Create(Merge, Right); setupAnalyses(); - MemorySSA &MSSA = Analyses->MSSA; + 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); + // MemoryPHI should already exist. + MemoryPhi *MP = MSSA.getMemoryAccess(Merge); + EXPECT_NE(MP, nullptr); // Create the load memory acccess MemoryUse *LoadAccess = cast<MemoryUse>( @@ -106,6 +105,41 @@ TEST_F(MemorySSATest, CreateALoadAndPhi) { MSSA.verifyMemorySSA(); } +TEST_F(MemorySSATest, MoveAStore) { + // We create a diamond where there is a in the entry, a store on one side, and + // a load at the end. After building MemorySSA, we test updating by moving + // the store from the side block to the entry block. + F = Function::Create( + FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), + GlobalValue::ExternalLinkage, "F", &M); + BasicBlock *Entry(BasicBlock::Create(C, "", F)); + BasicBlock *Left(BasicBlock::Create(C, "", F)); + BasicBlock *Right(BasicBlock::Create(C, "", F)); + BasicBlock *Merge(BasicBlock::Create(C, "", F)); + B.SetInsertPoint(Entry); + Argument *PointerArg = &*F->arg_begin(); + StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg); + B.CreateCondBr(B.getTrue(), Left, Right); + B.SetInsertPoint(Left); + StoreInst *SideStore = B.CreateStore(B.getInt8(16), PointerArg); + BranchInst::Create(Merge, Left); + BranchInst::Create(Merge, Right); + B.SetInsertPoint(Merge); + B.CreateLoad(PointerArg); + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + + // Move the store + SideStore->moveBefore(Entry->getTerminator()); + MemoryAccess *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore); + MemoryAccess *SideStoreAccess = MSSA.getMemoryAccess(SideStore); + MemoryAccess *NewStoreAccess = MSSA.createMemoryAccessAfter( + SideStore, EntryStoreAccess, EntryStoreAccess); + EntryStoreAccess->replaceAllUsesWith(NewStoreAccess); + MSSA.removeMemoryAccess(SideStoreAccess); + 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 @@ -128,7 +162,7 @@ TEST_F(MemorySSATest, RemoveAPhi) { LoadInst *LoadInst = B.CreateLoad(PointerArg); setupAnalyses(); - MemorySSA &MSSA = Analyses->MSSA; + 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)); @@ -171,7 +205,7 @@ TEST_F(MemorySSATest, RemoveMemoryAccess) { LoadInst *LoadInst = B.CreateLoad(PointerArg); setupAnalyses(); - MemorySSA &MSSA = Analyses->MSSA; + MemorySSA &MSSA = *Analyses->MSSA; MemorySSAWalker *Walker = Analyses->Walker; // Before, the load will be a use of a phi<store, liveonentry>. It should be @@ -191,9 +225,15 @@ TEST_F(MemorySSATest, RemoveMemoryAccess) { // 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. + // XXX: This currently requires either removing the phi or resetting optimized + // on the load + + EXPECT_FALSE( + MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst))); + // If we reset optimized, we get live on entry. + LoadAccess->resetOptimized(); 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); @@ -230,14 +270,13 @@ TEST_F(MemorySSATest, TestTripleStore) { StoreInst *S3 = B.CreateStore(ConstantInt::get(Int8, 2), Alloca); setupAnalyses(); - MemorySSA &MSSA = Analyses->MSSA; + 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 *DefiningAccess = MSSA.getMemoryAccess(V)->getDefiningAccess(); MemoryAccess *WalkerClobber = Walker->getClobberingMemoryAccess(V); EXPECT_EQ(DefiningAccess, WalkerClobber) << "Store " << I << " doesn't have the correct clobbering access"; @@ -261,7 +300,7 @@ TEST_F(MemorySSATest, TestStoreAndLoad) { Instruction *LI = B.CreateLoad(Alloca); setupAnalyses(); - MemorySSA &MSSA = Analyses->MSSA; + MemorySSA &MSSA = *Analyses->MSSA; MemorySSAWalker *Walker = Analyses->Walker; MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LI); @@ -290,7 +329,7 @@ TEST_F(MemorySSATest, TestStoreDoubleQuery) { StoreInst *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca); setupAnalyses(); - MemorySSA &MSSA = Analyses->MSSA; + MemorySSA &MSSA = *Analyses->MSSA; MemorySSAWalker *Walker = Analyses->Walker; MemoryAccess *StoreAccess = MSSA.getMemoryAccess(SI); @@ -308,3 +347,188 @@ TEST_F(MemorySSATest, TestStoreDoubleQuery) { EXPECT_EQ(Clobber, StoreAccess); EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry)); } + +// Bug: During phi optimization, the walker wouldn't cache to the proper result +// in the farthest-walked BB. +// +// Specifically, it would assume that whatever we walked to was a clobber. +// "Whatever we walked to" isn't a clobber if we hit a cache entry. +// +// ...So, we need a test case that looks like: +// A +// / \ +// B | +// \ / +// C +// +// Where, when we try to optimize a thing in 'C', a blocker is found in 'B'. +// The walk must determine that the blocker exists by using cache entries *while +// walking* 'B'. +TEST_F(MemorySSATest, PartialWalkerCacheWithPhis) { + F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), + GlobalValue::ExternalLinkage, "F", &M); + B.SetInsertPoint(BasicBlock::Create(C, "A", F)); + Type *Int8 = Type::getInt8Ty(C); + Constant *One = ConstantInt::get(Int8, 1); + Constant *Zero = ConstantInt::get(Int8, 0); + Value *AllocA = B.CreateAlloca(Int8, One, "a"); + Value *AllocB = B.CreateAlloca(Int8, One, "b"); + BasicBlock *IfThen = BasicBlock::Create(C, "B", F); + BasicBlock *IfEnd = BasicBlock::Create(C, "C", F); + + B.CreateCondBr(UndefValue::get(Type::getInt1Ty(C)), IfThen, IfEnd); + + B.SetInsertPoint(IfThen); + Instruction *FirstStore = B.CreateStore(Zero, AllocA); + B.CreateStore(Zero, AllocB); + Instruction *ALoad0 = B.CreateLoad(AllocA, ""); + Instruction *BStore = B.CreateStore(Zero, AllocB); + // Due to use optimization/etc. we make a store to A, which is removed after + // we build MSSA. This helps keep the test case simple-ish. + Instruction *KillStore = B.CreateStore(Zero, AllocA); + Instruction *ALoad = B.CreateLoad(AllocA, ""); + B.CreateBr(IfEnd); + + B.SetInsertPoint(IfEnd); + Instruction *BelowPhi = B.CreateStore(Zero, AllocA); + + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAWalker *Walker = Analyses->Walker; + + // Kill `KillStore`; it exists solely so that the load after it won't be + // optimized to FirstStore. + MSSA.removeMemoryAccess(MSSA.getMemoryAccess(KillStore)); + KillStore->eraseFromParent(); + auto *ALoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(ALoad)); + EXPECT_EQ(ALoadMA->getDefiningAccess(), MSSA.getMemoryAccess(BStore)); + + // Populate the cache for the store to AllocB directly after FirstStore. It + // should point to something in block B (so something in D can't be optimized + // to it). + MemoryAccess *Load0Clobber = Walker->getClobberingMemoryAccess(ALoad0); + EXPECT_EQ(MSSA.getMemoryAccess(FirstStore), Load0Clobber); + + // If the bug exists, this will introduce a bad cache entry for %a on BStore. + // It will point to the store to %b after FirstStore. This only happens during + // phi optimization. + MemoryAccess *BottomClobber = Walker->getClobberingMemoryAccess(BelowPhi); + MemoryAccess *Phi = MSSA.getMemoryAccess(IfEnd); + EXPECT_EQ(BottomClobber, Phi); + + // This query will first check the cache for {%a, BStore}. It should point to + // FirstStore, not to the store after FirstStore. + MemoryAccess *UseClobber = Walker->getClobberingMemoryAccess(ALoad); + EXPECT_EQ(UseClobber, MSSA.getMemoryAccess(FirstStore)); +} + +// Test that our walker properly handles loads with the invariant group +// attribute. It's a bit hacky, since we add the invariant attribute *after* +// building MSSA. Otherwise, the use optimizer will optimize it for us, which +// isn't what we want. +// FIXME: It may be easier/cleaner to just add an 'optimize uses?' flag to MSSA. +TEST_F(MemorySSATest, WalkerInvariantLoadOpt) { + F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), + GlobalValue::ExternalLinkage, "F", &M); + B.SetInsertPoint(BasicBlock::Create(C, "", F)); + Type *Int8 = Type::getInt8Ty(C); + Constant *One = ConstantInt::get(Int8, 1); + Value *AllocA = B.CreateAlloca(Int8, One, ""); + + Instruction *Store = B.CreateStore(One, AllocA); + Instruction *Load = B.CreateLoad(AllocA); + + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAWalker *Walker = Analyses->Walker; + + auto *LoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(Load)); + auto *StoreMA = cast<MemoryDef>(MSSA.getMemoryAccess(Store)); + EXPECT_EQ(LoadMA->getDefiningAccess(), StoreMA); + + // ...At the time of writing, no cache should exist for LoadMA. Be a bit + // flexible to future changes. + Walker->invalidateInfo(LoadMA); + Load->setMetadata(LLVMContext::MD_invariant_load, MDNode::get(C, {})); + + MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LoadMA); + EXPECT_EQ(LoadClobber, MSSA.getLiveOnEntryDef()); +} + +// Test loads get reoptimized properly by the walker. +TEST_F(MemorySSATest, WalkerReopt) { + F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), + GlobalValue::ExternalLinkage, "F", &M); + B.SetInsertPoint(BasicBlock::Create(C, "", F)); + Type *Int8 = Type::getInt8Ty(C); + Value *AllocaA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); + Instruction *SIA = B.CreateStore(ConstantInt::get(Int8, 0), AllocaA); + Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B"); + Instruction *SIB = B.CreateStore(ConstantInt::get(Int8, 0), AllocaB); + Instruction *LIA = B.CreateLoad(AllocaA); + + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAWalker *Walker = Analyses->Walker; + + MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LIA); + MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LIA)); + EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SIA)); + EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SIA))); + MSSA.removeMemoryAccess(LoadAccess); + + // Create the load memory access pointing to an unoptimized place. + MemoryUse *NewLoadAccess = cast<MemoryUse>(MSSA.createMemoryAccessInBB( + LIA, MSSA.getMemoryAccess(SIB), LIA->getParent(), MemorySSA::End)); + // This should it cause it to be optimized + EXPECT_EQ(Walker->getClobberingMemoryAccess(NewLoadAccess), LoadClobber); + EXPECT_EQ(NewLoadAccess->getDefiningAccess(), LoadClobber); +} + +// Test out MemorySSA::spliceMemoryAccessAbove. +TEST_F(MemorySSATest, SpliceAboveMemoryDef) { + F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), + GlobalValue::ExternalLinkage, "F", &M); + B.SetInsertPoint(BasicBlock::Create(C, "", F)); + + Type *Int8 = Type::getInt8Ty(C); + Value *A = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); + Value *B_ = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B"); + Value *C = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "C"); + + StoreInst *StoreA0 = B.CreateStore(ConstantInt::get(Int8, 0), A); + StoreInst *StoreB = B.CreateStore(ConstantInt::get(Int8, 0), B_); + LoadInst *LoadB = B.CreateLoad(B_); + StoreInst *StoreA1 = B.CreateStore(ConstantInt::get(Int8, 4), A); + // splice this above StoreB + StoreInst *StoreC = B.CreateStore(ConstantInt::get(Int8, 4), C); + StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 4), A); + LoadInst *LoadC = B.CreateLoad(C); + + setupAnalyses(); + MemorySSA &MSSA = *Analyses->MSSA; + MemorySSAWalker &Walker = *Analyses->Walker; + + StoreC->moveBefore(StoreB); + MSSA.spliceMemoryAccessAbove(cast<MemoryDef>(MSSA.getMemoryAccess(StoreB)), + MSSA.getMemoryAccess(StoreC)); + + MSSA.verifyMemorySSA(); + + EXPECT_EQ(MSSA.getMemoryAccess(StoreB)->getDefiningAccess(), + MSSA.getMemoryAccess(StoreC)); + EXPECT_EQ(MSSA.getMemoryAccess(StoreC)->getDefiningAccess(), + MSSA.getMemoryAccess(StoreA0)); + EXPECT_EQ(MSSA.getMemoryAccess(StoreA2)->getDefiningAccess(), + MSSA.getMemoryAccess(StoreA1)); + EXPECT_EQ(Walker.getClobberingMemoryAccess(LoadB), + MSSA.getMemoryAccess(StoreB)); + EXPECT_EQ(Walker.getClobberingMemoryAccess(LoadC), + MSSA.getMemoryAccess(StoreC)); + + // exercise block numbering + EXPECT_TRUE(MSSA.locallyDominates(MSSA.getMemoryAccess(StoreC), + MSSA.getMemoryAccess(StoreB))); + EXPECT_TRUE(MSSA.locallyDominates(MSSA.getMemoryAccess(StoreA1), + MSSA.getMemoryAccess(StoreA2))); +} |