diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2013-12-22 00:04:03 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2013-12-22 00:04:03 +0000 |
commit | f8af5cf600354830d4ccf59732403f0f073eccb9 (patch) | |
tree | 2ba0398b4c42ad4f55561327538044fd2c925a8b /unittests | |
parent | 59d6cff90eecf31cb3dd860c4e786674cfdd42eb (diff) |
Notes
Diffstat (limited to 'unittests')
79 files changed, 7610 insertions, 1586 deletions
diff --git a/unittests/ADT/APFloatTest.cpp b/unittests/ADT/APFloatTest.cpp index 278983565ced7..e57c8d4b931f0 100644 --- a/unittests/ADT/APFloatTest.cpp +++ b/unittests/ADT/APFloatTest.cpp @@ -33,6 +33,449 @@ static std::string convertToString(double d, unsigned Prec, unsigned Pad) { namespace { +TEST(APFloatTest, isSignaling) { + // We test qNaN, -qNaN, +sNaN, -sNaN with and without payloads. *NOTE* The + // 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()); +} + +TEST(APFloatTest, next) { + + APFloat test(APFloat::IEEEquad, APFloat::uninitialized); + APFloat expected(APFloat::IEEEquad, APFloat::uninitialized); + + // 1. Test Special Cases Values. + // + // Test all special values for nextUp and nextDown perscribed by IEEE-754R + // 2008. These are: + // 1. +inf + // 2. -inf + // 3. getLargest() + // 4. -getLargest() + // 5. getSmallest() + // 6. -getSmallest() + // 7. qNaN + // 8. sNaN + // 9. +0 + // 10. -0 + + // nextUp(+inf) = +inf. + 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); + 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); + 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); + 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); + EXPECT_EQ(test.next(false), APFloat::opOK); + EXPECT_TRUE(test.isInfinity() && !test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(getLargest()) = -nextUp(-getLargest()) + // = -(-getLargest() + inc) + // = getLargest() - inc. + 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, + "-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); + 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, + "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); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(test.isZero() && !test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextUp(-getSmallest()) = -0. + test = APFloat(APFloat::IEEEquad, "-0x0.0000000000000000000000000001p-16382"); + expected = APFloat::getZero(APFloat::IEEEquad, true); + EXPECT_EQ(test.next(false), APFloat::opOK); + EXPECT_TRUE(test.isZero() && test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(-getSmallest()) = -nextUp(getSmallest()) = -getSmallest() - inc. + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // 2. Binade Boundary Tests. + + // 2a. Test denormal <-> normal binade boundaries. + // * nextUp(+Largest Denormal) -> +Smallest Normal. + // * nextDown(-Largest Denormal) -> -Smallest Normal. + // * nextUp(-Smallest Normal) -> -Largest Denormal. + // * nextDown(+Smallest Normal) -> +Largest Denormal. + + // nextUp(+Largest Denormal) -> +Smallest Normal. + 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, + "-0x0.ffffffffffffffffffffffffffffp-16382"); + 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, + "-0x1.0000000000000000000000000000p-16382"); + 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, + "+0x1.0000000000000000000000000000p-16382"); + expected = APFloat(APFloat::IEEEquad, + "+0x0.ffffffffffffffffffffffffffffp-16382"); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(test.isDenormal()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // 2b. Test normal <-> normal binade boundaries. + // * nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. + // * nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. + // * nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. + // * 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, + "-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"); + 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"); + 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"); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // 2c. Test using next at binade boundaries with a direction away from the + // binade boundary. Away from denormal <-> normal boundaries. + // + // This is to make sure that even though we are at a binade boundary, since + // we are rounding away, we do not trigger the binade boundary code. Thus we + // test: + // * nextUp(-Largest Denormal) -> -Largest Denormal + inc. + // * nextDown(+Largest Denormal) -> +Largest Denormal - inc. + // * nextUp(+Smallest Normal) -> +Smallest Normal + inc. + // * nextDown(-Smallest Normal) -> -Smallest Normal - inc. + + // nextUp(-Largest Denormal) -> -Largest Denormal + inc. + 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()); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(+Largest Denormal) -> +Largest Denormal - inc. + 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()); + EXPECT_TRUE(!test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextUp(+Smallest Normal) -> +Smallest Normal + inc. + 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()); + EXPECT_TRUE(!test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(-Smallest Normal) -> -Smallest Normal - inc. + 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()); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // 2d. Test values which cause our exponent to go to min exponent. This + // is to ensure that guards in the code to check for min exponent + // trigger properly. + // * nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 + // * nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> + // -0x1p-16381 + // * nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16382 + // * nextDown(0x1p-16382) -> 0x1.ffffffffffffffffffffffffffffp-16382 + + // nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 + 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"); + 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"); + 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, + "0x1.ffffffffffffffffffffffffffffp-16382"); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // 3. Now we test both denormal/normal computation which will not cause us + // to go across binade boundaries. Specifically we test: + // * nextUp(+Denormal) -> +Denormal. + // * nextDown(+Denormal) -> +Denormal. + // * nextUp(-Denormal) -> -Denormal. + // * nextDown(-Denormal) -> -Denormal. + // * nextUp(+Normal) -> +Normal. + // * nextDown(+Normal) -> +Normal. + // * nextUp(-Normal) -> -Normal. + // * nextDown(-Normal) -> -Normal. + + // nextUp(+Denormal) -> +Denormal. + test = APFloat(APFloat::IEEEquad, + "0x0.ffffffffffffffffffffffff000cp-16382"); + expected = APFloat(APFloat::IEEEquad, + "0x0.ffffffffffffffffffffffff000dp-16382"); + EXPECT_EQ(test.next(false), APFloat::opOK); + EXPECT_TRUE(test.isDenormal()); + EXPECT_TRUE(!test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(+Denormal) -> +Denormal. + test = APFloat(APFloat::IEEEquad, + "0x0.ffffffffffffffffffffffff000cp-16382"); + expected = APFloat(APFloat::IEEEquad, + "0x0.ffffffffffffffffffffffff000bp-16382"); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(test.isDenormal()); + EXPECT_TRUE(!test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextUp(-Denormal) -> -Denormal. + test = APFloat(APFloat::IEEEquad, + "-0x0.ffffffffffffffffffffffff000cp-16382"); + expected = APFloat(APFloat::IEEEquad, + "-0x0.ffffffffffffffffffffffff000bp-16382"); + EXPECT_EQ(test.next(false), APFloat::opOK); + EXPECT_TRUE(test.isDenormal()); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(-Denormal) -> -Denormal + test = APFloat(APFloat::IEEEquad, + "-0x0.ffffffffffffffffffffffff000cp-16382"); + expected = APFloat(APFloat::IEEEquad, + "-0x0.ffffffffffffffffffffffff000dp-16382"); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(test.isDenormal()); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextUp(+Normal) -> +Normal. + test = APFloat(APFloat::IEEEquad, + "0x1.ffffffffffffffffffffffff000cp-16000"); + expected = APFloat(APFloat::IEEEquad, + "0x1.ffffffffffffffffffffffff000dp-16000"); + EXPECT_EQ(test.next(false), APFloat::opOK); + EXPECT_TRUE(!test.isDenormal()); + EXPECT_TRUE(!test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(+Normal) -> +Normal. + test = APFloat(APFloat::IEEEquad, + "0x1.ffffffffffffffffffffffff000cp-16000"); + expected = APFloat(APFloat::IEEEquad, + "0x1.ffffffffffffffffffffffff000bp-16000"); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(!test.isDenormal()); + EXPECT_TRUE(!test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextUp(-Normal) -> -Normal. + test = APFloat(APFloat::IEEEquad, + "-0x1.ffffffffffffffffffffffff000cp-16000"); + expected = APFloat(APFloat::IEEEquad, + "-0x1.ffffffffffffffffffffffff000bp-16000"); + EXPECT_EQ(test.next(false), APFloat::opOK); + EXPECT_TRUE(!test.isDenormal()); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + + // nextDown(-Normal) -> -Normal. + test = APFloat(APFloat::IEEEquad, + "-0x1.ffffffffffffffffffffffff000cp-16000"); + expected = APFloat(APFloat::IEEEquad, + "-0x1.ffffffffffffffffffffffff000dp-16000"); + EXPECT_EQ(test.next(true), APFloat::opOK); + EXPECT_TRUE(!test.isDenormal()); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); +} + +TEST(APFloatTest, FMA) { + APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven; + + { + APFloat f1(14.5f); + APFloat f2(-14.5f); + APFloat f3(225.0f); + f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven); + EXPECT_EQ(14.75f, f1.convertToFloat()); + } + + { + APFloat Val2(2.0f); + APFloat f1((float)1.17549435e-38F); + APFloat f2((float)1.17549435e-38F); + f1.divide(Val2, rdmd); + f2.divide(Val2, rdmd); + APFloat f3(12.0f); + f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven); + EXPECT_EQ(12.0f, f1.convertToFloat()); + } +} + TEST(APFloatTest, Denormal) { APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven; @@ -95,6 +538,31 @@ TEST(APFloatTest, Zero) { EXPECT_TRUE(APFloat(-0.0).isNegative()); } +TEST(APFloatTest, DecimalStringsWithoutNullTerminators) { + // Make sure that we can parse strings without null terminators. + // rdar://14323230. + APFloat Val(APFloat::IEEEdouble); + Val.convertFromString(StringRef("0.00", 3), + llvm::APFloat::rmNearestTiesToEven); + EXPECT_EQ(Val.convertToDouble(), 0.0); + Val.convertFromString(StringRef("0.01", 3), + llvm::APFloat::rmNearestTiesToEven); + EXPECT_EQ(Val.convertToDouble(), 0.0); + Val.convertFromString(StringRef("0.09", 3), + llvm::APFloat::rmNearestTiesToEven); + EXPECT_EQ(Val.convertToDouble(), 0.0); + Val.convertFromString(StringRef("0.095", 4), + llvm::APFloat::rmNearestTiesToEven); + EXPECT_EQ(Val.convertToDouble(), 0.09); + Val.convertFromString(StringRef("0.00e+3", 7), + llvm::APFloat::rmNearestTiesToEven); + EXPECT_EQ(Val.convertToDouble(), 0.00); + Val.convertFromString(StringRef("0e+3", 4), + llvm::APFloat::rmNearestTiesToEven); + EXPECT_EQ(Val.convertToDouble(), 0.00); + +} + TEST(APFloatTest, fromZeroDecimalString) { EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble, "0").convertToDouble()); EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble, "+0").convertToDouble()); @@ -298,6 +766,8 @@ TEST(APFloatTest, fromDecimalString) { 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) { @@ -381,7 +851,10 @@ TEST(APFloatTest, fromHexadecimalString) { EXPECT_EQ(1.0625, APFloat(APFloat::IEEEdouble, "0x1.1p0").convertToDouble()); EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble, "0x1p0").convertToDouble()); - EXPECT_EQ(2.71828, convertToDoubleFromString("2.71828")); + EXPECT_EQ(convertToDoubleFromString("0x1p-150"), + convertToDoubleFromString("+0x800000000000000001.p-221")); + EXPECT_EQ(2251799813685248.5, + convertToDoubleFromString("0x80000000000004000000.010p-28")); } TEST(APFloatTest, toString) { @@ -393,10 +866,11 @@ TEST(APFloatTest, toString) { ASSERT_EQ("0.0101", convertToString(1.01E-2, 5, 2)); ASSERT_EQ("0.0101", convertToString(1.01E-2, 4, 2)); ASSERT_EQ("1.01E-2", convertToString(1.01E-2, 5, 1)); - ASSERT_EQ("0.7853981633974483", convertToString(0.78539816339744830961, 0, 3)); - ASSERT_EQ("4.940656458412465E-324", convertToString(4.9406564584124654e-324, 0, 3)); - ASSERT_EQ("873.1834", convertToString(873.1834, 0, 1)); - ASSERT_EQ("8.731834E+2", convertToString(873.1834, 0, 0)); + ASSERT_EQ("0.78539816339744828", convertToString(0.78539816339744830961, 0, 3)); + ASSERT_EQ("4.9406564584124654E-324", convertToString(4.9406564584124654e-324, 0, 3)); + ASSERT_EQ("873.18340000000001", convertToString(873.1834, 0, 1)); + ASSERT_EQ("8.7318340000000001E+2", convertToString(873.1834, 0, 0)); + ASSERT_EQ("1.7976931348623157E+308", convertToString(1.7976931348623157E+308, 0, 0)); } TEST(APFloatTest, toInteger) { @@ -771,6 +1245,103 @@ TEST(APFloatTest, getLargest) { 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"); + 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"); + 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"); + 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"); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.isFiniteNonZero()); + EXPECT_TRUE(test.isDenormal()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); +} + +TEST(APFloatTest, getSmallestNormalized) { + 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"); + 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"); + 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"); + EXPECT_TRUE(test.isNegative()); + EXPECT_TRUE(test.isFiniteNonZero()); + EXPECT_FALSE(test.isDenormal()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); +} + +TEST(APFloatTest, getZero) { + struct { + const fltSemantics *semantics; + const bool sign; + 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}, + }; + const unsigned NumGetZeroTests = 12; + for (unsigned i = 0; i < NumGetZeroTests; ++i) { + APFloat test = APFloat::getZero(*GetZeroTest[i].semantics, + GetZeroTest[i].sign); + const char *pattern = GetZeroTest[i].sign? "-0x0p+0" : "0x0p+0"; + APFloat expected = APFloat(*GetZeroTest[i].semantics, + pattern); + EXPECT_TRUE(test.isZero()); + EXPECT_TRUE(GetZeroTest[i].sign? test.isNegative() : !test.isNegative()); + EXPECT_TRUE(test.bitwiseIsEqual(expected)); + for (unsigned j = 0, je = GetZeroTest[i].bitPatternLength; j < je; ++j) { + EXPECT_EQ(GetZeroTest[i].bitPattern[j], + test.bitcastToAPInt().getRawData()[j]); + } + } +} + TEST(APFloatTest, convert) { bool losesInfo; APFloat test(APFloat::IEEEdouble, "1.0"); @@ -857,4 +1428,1247 @@ TEST(APFloatTest, PPCDoubleDouble) { EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); #endif } + +TEST(APFloatTest, isNegative) { + APFloat t(APFloat::IEEEsingle, "0x1p+0"); + EXPECT_FALSE(t.isNegative()); + 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::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::getSNaN(APFloat::IEEEsingle, false).isNegative()); + EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle, true).isNegative()); +} + +TEST(APFloatTest, isNormal) { + 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()); +} + +TEST(APFloatTest, isFinite) { + 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()); +} + +TEST(APFloatTest, isInfinity) { + 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()); +} + +TEST(APFloatTest, isNaN) { + 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()); +} + +TEST(APFloatTest, isFiniteNonZero) { + // Test positive/negative normal value. + 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()); + + // Test +/- Infinity. + 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()); + + // 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()); + + // 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()); +} + +TEST(APFloatTest, add) { + // Test Special Cases against each other and normal values. + + // TODOS/NOTES: + // 1. Since we perform only default exception handling all operations with + // 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 PSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat MSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + + const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; + + const unsigned NumTests = 169; + struct { + APFloat x; + APFloat y; + const char *result; + int status; + int category; + } SpecialCaseTests[NumTests] = { + { PInf, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PInf, PZero, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MZero, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { PInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MInf, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PZero, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MZero, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { MInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { PZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PZero, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PZero, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PZero, PLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PZero, MLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PZero, PSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PZero, MSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PZero, PSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PZero, MSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MZero, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MZero, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { MZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MZero, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MZero, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MZero, PLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MZero, MLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MZero, PSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MZero, MSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MZero, PSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MZero, MSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { SNaN, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, QNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PNormalValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PNormalValue, PZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { PNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PNormalValue, "0x1p+1", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PNormalValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, PSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, MSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, PSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, MSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MNormalValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MNormalValue, PZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { MNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MNormalValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MNormalValue, MNormalValue, "-0x1p+1", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, PSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, MSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, PSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, MSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PLargestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PLargestValue, PZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, MZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { PLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, PLargestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { PLargestValue, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PLargestValue, PSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, MSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, PSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, MSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MLargestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MLargestValue, PZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, MZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { MLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MLargestValue, MLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, PSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, MSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, PSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, MSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestValue, PZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { PSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestValue, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, PSmallestValue, "0x1p-148", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestValue, PSmallestNormalized, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MSmallestNormalized, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestValue, PZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { MSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestValue, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestValue, MSmallestValue, "-0x1p-148", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PSmallestNormalized, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MSmallestNormalized, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestNormalized, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestNormalized, PZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestNormalized, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, PSmallestValue, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MSmallestValue, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PSmallestNormalized, "0x1p-125", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestNormalized, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestNormalized, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestNormalized, PZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 + // See Note 1. + { MSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestNormalized, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, PSmallestValue, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MSmallestValue, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestNormalized, MSmallestNormalized, "-0x1p-125", APFloat::opOK, APFloat::fcNormal } + }; + + for (size_t i = 0; i < NumTests; ++i) { + APFloat x(SpecialCaseTests[i].x); + APFloat y(SpecialCaseTests[i].y); + APFloat::opStatus status = x.add(y, APFloat::rmNearestTiesToEven); + + APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + + EXPECT_TRUE(result.bitwiseIsEqual(x)); + EXPECT_TRUE((int)status == SpecialCaseTests[i].status); + EXPECT_TRUE((int)x.getCategory() == SpecialCaseTests[i].category); + } +} + +TEST(APFloatTest, subtract) { + // Test Special Cases against each other and normal values. + + // TODOS/NOTES: + // 1. Since we perform only default exception handling all operations with + // 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 PSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat MSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + + const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; + + const unsigned NumTests = 169; + struct { + APFloat x; + APFloat y; + const char *result; + int status; + int category; + } SpecialCaseTests[NumTests] = { + { PInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PInf, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PZero, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MZero, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MInf, PZero, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MZero, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PZero, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PZero, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PZero, PLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PZero, MLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PZero, PSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PZero, MSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PZero, PSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PZero, MSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MZero, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MZero, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MZero, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MZero, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MZero, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MZero, PLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MZero, MLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MZero, PSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MZero, MSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MZero, PSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MZero, MSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { SNaN, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, QNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PNormalValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PNormalValue, PZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PNormalValue, MNormalValue, "0x1p+1", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, PSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, MSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, PSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PNormalValue, MSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MNormalValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MNormalValue, PZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MNormalValue, PNormalValue, "-0x1p+1", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MNormalValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, PSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, MSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, PSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MNormalValue, MSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PLargestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PLargestValue, PZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, MZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PLargestValue, MLargestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { PLargestValue, PSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, MSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, PSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PLargestValue, MSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MLargestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MLargestValue, PZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, MZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, PLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MLargestValue, PSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, MSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, PSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MLargestValue, MSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestValue, PZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestValue, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestValue, MSmallestValue, "0x1p-148", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, PSmallestNormalized, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MSmallestNormalized, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestValue, PZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestValue, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestValue, PSmallestValue, "-0x1p-148", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestValue, PSmallestNormalized, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MSmallestNormalized, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestNormalized, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestNormalized, PZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestNormalized, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { PSmallestNormalized, PSmallestValue, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MSmallestValue, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestNormalized, MSmallestNormalized, "0x1p-125", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestNormalized, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestNormalized, PZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestNormalized, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal }, + { MSmallestNormalized, PSmallestValue, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MSmallestValue, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PSmallestNormalized, "-0x1p-125", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero } + }; + + for (size_t i = 0; i < NumTests; ++i) { + APFloat x(SpecialCaseTests[i].x); + APFloat y(SpecialCaseTests[i].y); + APFloat::opStatus status = x.subtract(y, APFloat::rmNearestTiesToEven); + + APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + + EXPECT_TRUE(result.bitwiseIsEqual(x)); + EXPECT_TRUE((int)status == SpecialCaseTests[i].status); + EXPECT_TRUE((int)x.getCategory() == SpecialCaseTests[i].category); + } +} + +TEST(APFloatTest, multiply) { + // Test Special Cases against each other and normal values. + + // TODOS/NOTES: + // 1. Since we perform only default exception handling all operations with + // 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 PSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat MSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + + const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; + const int UnderflowStatus = APFloat::opUnderflow | APFloat::opInexact; + + const unsigned NumTests = 169; + struct { + APFloat x; + APFloat y; + const char *result; + int status; + int category; + } SpecialCaseTests[NumTests] = { + { PInf, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PZero, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PZero, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MZero, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MZero, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MZero, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { SNaN, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, QNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PNormalValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PNormalValue, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PNormalValue, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, PLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, PSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, PSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MNormalValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MNormalValue, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MNormalValue, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MNormalValue, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, PLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, PSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, PSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PLargestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PLargestValue, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PLargestValue, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, PLargestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { PLargestValue, MLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { PLargestValue, PSmallestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, MSmallestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, PSmallestNormalized, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, MSmallestNormalized, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MLargestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MLargestValue, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MLargestValue, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, PLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, MLargestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, PSmallestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, MSmallestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, PSmallestNormalized, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, MSmallestNormalized, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestValue, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestValue, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestValue, PNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, PLargestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MLargestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, PSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestValue, MSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestValue, PSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestValue, MSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestValue, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestValue, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestValue, PNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PLargestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MLargestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestValue, MSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestValue, PSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestValue, MSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestNormalized, PInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestNormalized, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PSmallestNormalized, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestNormalized, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestNormalized, PNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PLargestValue, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MLargestValue, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestNormalized, MSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestNormalized, PSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestNormalized, MSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestNormalized, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestNormalized, MInf, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MSmallestNormalized, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestNormalized, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestNormalized, PNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PLargestValue, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MLargestValue, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestNormalized, MSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestNormalized, PSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestNormalized, MSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero } + }; + + for (size_t i = 0; i < NumTests; ++i) { + APFloat x(SpecialCaseTests[i].x); + APFloat y(SpecialCaseTests[i].y); + APFloat::opStatus status = x.multiply(y, APFloat::rmNearestTiesToEven); + + APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + + EXPECT_TRUE(result.bitwiseIsEqual(x)); + EXPECT_TRUE((int)status == SpecialCaseTests[i].status); + EXPECT_TRUE((int)x.getCategory() == SpecialCaseTests[i].category); + } +} + +TEST(APFloatTest, divide) { + // Test Special Cases against each other and normal values. + + // TODOS/NOTES: + // 1. Since we perform only default exception handling all operations with + // 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 PSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, false); + APFloat MSmallestNormalized = + APFloat::getSmallestNormalized(APFloat::IEEEsingle, true); + + const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact; + const int UnderflowStatus = APFloat::opUnderflow | APFloat::opInexact; + + const unsigned NumTests = 169; + struct { + APFloat x; + APFloat y; + const char *result; + int status; + int category; + } SpecialCaseTests[NumTests] = { + { PInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PInf, PZero, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MZero, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MInf, PZero, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MZero, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MInf, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity }, + { MInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity }, + { PZero, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PZero, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PZero, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MZero, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MZero, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MZero, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, + { QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { SNaN, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, QNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, + { SNaN, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PNormalValue, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PNormalValue, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PNormalValue, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PNormalValue, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, PLargestValue, "0x1p-128", UnderflowStatus, APFloat::fcNormal }, + { PNormalValue, MLargestValue, "-0x1p-128", UnderflowStatus, APFloat::fcNormal }, + { PNormalValue, PSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { PNormalValue, MSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { PNormalValue, PSmallestNormalized, "0x1p+126", APFloat::opOK, APFloat::fcNormal }, + { PNormalValue, MSmallestNormalized, "-0x1p+126", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MNormalValue, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MNormalValue, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MNormalValue, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MNormalValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MNormalValue, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, PLargestValue, "-0x1p-128", UnderflowStatus, APFloat::fcNormal }, + { MNormalValue, MLargestValue, "0x1p-128", UnderflowStatus, APFloat::fcNormal }, + { MNormalValue, PSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { MNormalValue, MSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { MNormalValue, PSmallestNormalized, "-0x1p+126", APFloat::opOK, APFloat::fcNormal }, + { MNormalValue, MSmallestNormalized, "0x1p+126", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PLargestValue, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PLargestValue, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PLargestValue, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, PLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, MLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PLargestValue, PSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { PLargestValue, MSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { PLargestValue, PSmallestNormalized, "inf", OverflowStatus, APFloat::fcInfinity }, + { PLargestValue, MSmallestNormalized, "-inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MLargestValue, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MLargestValue, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MLargestValue, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MLargestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, PLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, MLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MLargestValue, PSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, MSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, PSmallestNormalized, "-inf", OverflowStatus, APFloat::fcInfinity }, + { MLargestValue, MSmallestNormalized, "inf", OverflowStatus, APFloat::fcInfinity }, + { PSmallestValue, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestValue, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestValue, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PSmallestValue, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestValue, PNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, PLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestValue, MLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestValue, PSmallestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MSmallestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, PSmallestNormalized, "0x1p-23", APFloat::opOK, APFloat::fcNormal }, + { PSmallestValue, MSmallestNormalized, "-0x1p-23", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestValue, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestValue, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MSmallestValue, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MSmallestValue, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestValue, PNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestValue, MLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestValue, PSmallestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MSmallestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, PSmallestNormalized, "-0x1p-23", APFloat::opOK, APFloat::fcNormal }, + { MSmallestValue, MSmallestNormalized, "0x1p-23", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestNormalized, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { PSmallestNormalized, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PSmallestNormalized, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { PSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { PSmallestNormalized, PNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestNormalized, MLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { PSmallestNormalized, PSmallestValue, "0x1p+23", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MSmallestValue, "-0x1p+23", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, PSmallestNormalized, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { PSmallestNormalized, MSmallestNormalized, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestNormalized, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero }, + { MSmallestNormalized, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MSmallestNormalized, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity }, + { MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN }, +#if 0 +// See Note 1. + { MSmallestNormalized, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN }, +#endif + { MSmallestNormalized, PNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestNormalized, MLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero }, + { MSmallestNormalized, PSmallestValue, "-0x1p+23", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MSmallestValue, "0x1p+23", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, PSmallestNormalized, "-0x1p+0", APFloat::opOK, APFloat::fcNormal }, + { MSmallestNormalized, MSmallestNormalized, "0x1p+0", APFloat::opOK, APFloat::fcNormal }, + }; + + for (size_t i = 0; i < NumTests; ++i) { + APFloat x(SpecialCaseTests[i].x); + APFloat y(SpecialCaseTests[i].y); + APFloat::opStatus status = x.divide(y, APFloat::rmNearestTiesToEven); + + APFloat result(APFloat::IEEEsingle, SpecialCaseTests[i].result); + + EXPECT_TRUE(result.bitwiseIsEqual(x)); + EXPECT_TRUE((int)status == SpecialCaseTests[i].status); + EXPECT_TRUE((int)x.getCategory() == SpecialCaseTests[i].category); + } +} + } diff --git a/unittests/ADT/APIntTest.cpp b/unittests/ADT/APIntTest.cpp index f129fa71c8e06..3c0dfe1440447 100644 --- a/unittests/ADT/APIntTest.cpp +++ b/unittests/ADT/APIntTest.cpp @@ -532,4 +532,69 @@ TEST(APIntTest, Splat) { EXPECT_EQ(APInt(15, 0xDB6D), APInt::getSplat(15, ValB)); } +TEST(APIntTest, tcDecrement) { + // Test single word decrement. + + // No out borrow. + { + integerPart singleWord = ~integerPart(0) << (integerPartWidth - 1); + integerPart carry = APInt::tcDecrement(&singleWord, 1); + EXPECT_EQ(carry, integerPart(0)); + EXPECT_EQ(singleWord, ~integerPart(0) >> 1); + } + + // With out borrow. + { + integerPart singleWord = 0; + integerPart carry = APInt::tcDecrement(&singleWord, 1); + EXPECT_EQ(carry, integerPart(1)); + EXPECT_EQ(singleWord, ~integerPart(0)); + } + + // Test multiword decrement. + + // No across word borrow, no out borrow. + { + integerPart test[4] = {0x1, 0x1, 0x1, 0x1}; + integerPart expected[4] = {0x0, 0x1, 0x1, 0x1}; + APInt::tcDecrement(test, 4); + EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); + } + + // 1 across word borrow, no out borrow. + { + integerPart test[4] = {0x0, 0xF, 0x1, 0x1}; + integerPart expected[4] = {~integerPart(0), 0xE, 0x1, 0x1}; + integerPart carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, integerPart(0)); + EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); + } + + // 2 across word borrow, no out borrow. + { + integerPart test[4] = {0x0, 0x0, 0xC, 0x1}; + integerPart expected[4] = {~integerPart(0), ~integerPart(0), 0xB, 0x1}; + integerPart carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, integerPart(0)); + EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); + } + + // 3 across word borrow, no out borrow. + { + integerPart test[4] = {0x0, 0x0, 0x0, 0x1}; + integerPart expected[4] = {~integerPart(0), ~integerPart(0), ~integerPart(0), 0x0}; + integerPart carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, integerPart(0)); + EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); + } + + // 3 across word borrow, with out borrow. + { + integerPart test[4] = {0x0, 0x0, 0x0, 0x0}; + integerPart expected[4] = {~integerPart(0), ~integerPart(0), ~integerPart(0), ~integerPart(0)}; + integerPart carry = APInt::tcDecrement(test, 4); + EXPECT_EQ(carry, integerPart(1)); + EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0); + } +} } diff --git a/unittests/ADT/BitVectorTest.cpp b/unittests/ADT/BitVectorTest.cpp index dc298a83d571a..d7cde891fb562 100644 --- a/unittests/ADT/BitVectorTest.cpp +++ b/unittests/ADT/BitVectorTest.cpp @@ -141,6 +141,30 @@ TYPED_TEST(BitVectorTest, TrivialOperation) { EXPECT_TRUE(Vec.none()); EXPECT_FALSE(Vec.empty()); + Vec.flip(); + EXPECT_EQ(130U, Vec.count()); + EXPECT_EQ(130U, Vec.size()); + EXPECT_TRUE(Vec.any()); + EXPECT_TRUE(Vec.all()); + EXPECT_FALSE(Vec.none()); + EXPECT_FALSE(Vec.empty()); + + Vec.resize(64); + EXPECT_EQ(64U, Vec.count()); + EXPECT_EQ(64U, Vec.size()); + EXPECT_TRUE(Vec.any()); + EXPECT_TRUE(Vec.all()); + EXPECT_FALSE(Vec.none()); + EXPECT_FALSE(Vec.empty()); + + Vec.flip(); + EXPECT_EQ(0U, Vec.count()); + EXPECT_EQ(64U, Vec.size()); + EXPECT_FALSE(Vec.any()); + EXPECT_FALSE(Vec.all()); + EXPECT_TRUE(Vec.none()); + EXPECT_FALSE(Vec.empty()); + Inv = TypeParam().flip(); EXPECT_EQ(0U, Inv.count()); EXPECT_EQ(0U, Inv.size()); @@ -333,5 +357,41 @@ TYPED_TEST(BitVectorTest, RangeOps) { EXPECT_TRUE( E.test(32)); EXPECT_FALSE(E.test(33)); } + +TYPED_TEST(BitVectorTest, CompoundTestReset) { + TypeParam A(50, true); + TypeParam B(50, false); + + TypeParam C(100, true); + TypeParam D(100, false); + + EXPECT_FALSE(A.test(A)); + EXPECT_TRUE(A.test(B)); + EXPECT_FALSE(A.test(C)); + EXPECT_TRUE(A.test(D)); + EXPECT_FALSE(B.test(A)); + EXPECT_FALSE(B.test(B)); + EXPECT_FALSE(B.test(C)); + EXPECT_FALSE(B.test(D)); + EXPECT_TRUE(C.test(A)); + EXPECT_TRUE(C.test(B)); + EXPECT_FALSE(C.test(C)); + EXPECT_TRUE(C.test(D)); + + A.reset(B); + A.reset(D); + EXPECT_TRUE(A.all()); + A.reset(A); + EXPECT_TRUE(A.none()); + A.set(); + A.reset(C); + EXPECT_TRUE(A.none()); + A.set(); + + C.reset(A); + EXPECT_EQ(50, C.find_first()); + C.reset(C); + EXPECT_TRUE(C.none()); +} } #endif diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index 9aad793d8bc4d..8ad303a9e1b33 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -21,6 +21,7 @@ set(ADTSources MapVectorTest.cpp OptionalTest.cpp PackedVectorTest.cpp + PointerUnionTest.cpp SCCIteratorTest.cpp SmallPtrSetTest.cpp SmallStringTest.cpp @@ -34,6 +35,7 @@ set(ADTSources TripleTest.cpp TwineTest.cpp VariadicFunctionTest.cpp + polymorphic_ptr_test.cpp ) # They cannot be compiled on MSVC9 due to its bug. diff --git a/unittests/ADT/IntrusiveRefCntPtrTest.cpp b/unittests/ADT/IntrusiveRefCntPtrTest.cpp index 0c8c4ca16dd73..c67ec130912de 100644 --- a/unittests/ADT/IntrusiveRefCntPtrTest.cpp +++ b/unittests/ADT/IntrusiveRefCntPtrTest.cpp @@ -10,11 +10,13 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "gtest/gtest.h" -namespace llvm { - -struct VirtualRefCounted : public RefCountedBaseVPTR { +namespace { +struct VirtualRefCounted : public llvm::RefCountedBaseVPTR { virtual void f() {} }; +} + +namespace llvm { // Run this test with valgrind to detect memory leaks. TEST(IntrusiveRefCntPtr, RefCountedBaseVPTRCopyDoesNotLeak) { diff --git a/unittests/ADT/PointerUnionTest.cpp b/unittests/ADT/PointerUnionTest.cpp new file mode 100644 index 0000000000000..7eb718112d692 --- /dev/null +++ b/unittests/ADT/PointerUnionTest.cpp @@ -0,0 +1,64 @@ +//===- llvm/unittest/ADT/PointerUnionTest.cpp - Optional unit 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/ADT/PointerUnion.h" +using namespace llvm; + +namespace { + +typedef PointerUnion<int*, float*> PU; + +// Test fixture +class PointerUnionTest : public testing::Test { +}; + +float f = 3.14f; +int i = 42; + +const PU a(&f); +const PU b(&i); +const PU n; + +TEST_F(PointerUnionTest, Comparison) { + EXPECT_TRUE(a != b); + EXPECT_FALSE(a == b); + EXPECT_TRUE(b != n); + EXPECT_FALSE(b == n); +} + +TEST_F(PointerUnionTest, Null) { + EXPECT_FALSE(a.isNull()); + EXPECT_FALSE(b.isNull()); + EXPECT_TRUE(n.isNull()); + EXPECT_FALSE(!a); + EXPECT_FALSE(!b); + EXPECT_TRUE(!n); + // workaround an issue with EXPECT macros and explicit bool + EXPECT_TRUE((bool)a); + EXPECT_TRUE((bool)b); + EXPECT_FALSE(n); +} + +TEST_F(PointerUnionTest, Is) { + EXPECT_FALSE(a.is<int*>()); + EXPECT_TRUE(a.is<float*>()); + EXPECT_TRUE(b.is<int*>()); + EXPECT_FALSE(b.is<float*>()); + EXPECT_TRUE(n.is<int*>()); + EXPECT_FALSE(n.is<float*>()); +} + +TEST_F(PointerUnionTest, Get) { + EXPECT_EQ(a.get<float*>(), &f); + EXPECT_EQ(b.get<int*>(), &i); + EXPECT_EQ(n.get<int*>(), (int*)0); +} + +} // end anonymous namespace diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index fa87cd0e2c867..88691ae197494 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/raw_ostream.h" @@ -60,6 +61,9 @@ TEST(StringRefTest, StringOps) { EXPECT_EQ( 0, StringRef("AaB").compare_lower("aab")); EXPECT_EQ( 1, StringRef("AaB").compare_lower("AAA")); EXPECT_EQ(-1, StringRef("AaB").compare_lower("aaBb")); + EXPECT_EQ(-1, StringRef("AaB").compare_lower("bb")); + EXPECT_EQ( 1, StringRef("aaBb").compare_lower("AaB")); + EXPECT_EQ( 1, StringRef("bb").compare_lower("AaB")); EXPECT_EQ( 1, StringRef("AaB").compare_lower("aA")); EXPECT_EQ( 1, StringRef("\xFF").compare_lower("\1")); @@ -247,19 +251,41 @@ TEST(StringRefTest, Trim) { TEST(StringRefTest, StartsWith) { StringRef Str("hello"); + EXPECT_TRUE(Str.startswith("")); EXPECT_TRUE(Str.startswith("he")); EXPECT_FALSE(Str.startswith("helloworld")); EXPECT_FALSE(Str.startswith("hi")); } +TEST(StringRefTest, StartsWithLower) { + StringRef Str("heLLo"); + EXPECT_TRUE(Str.startswith_lower("")); + EXPECT_TRUE(Str.startswith_lower("he")); + EXPECT_TRUE(Str.startswith_lower("hell")); + EXPECT_TRUE(Str.startswith_lower("HELlo")); + EXPECT_FALSE(Str.startswith_lower("helloworld")); + EXPECT_FALSE(Str.startswith_lower("hi")); +} + TEST(StringRefTest, EndsWith) { StringRef Str("hello"); + EXPECT_TRUE(Str.endswith("")); EXPECT_TRUE(Str.endswith("lo")); EXPECT_FALSE(Str.endswith("helloworld")); EXPECT_FALSE(Str.endswith("worldhello")); EXPECT_FALSE(Str.endswith("so")); } +TEST(StringRefTest, EndsWithLower) { + StringRef Str("heLLo"); + EXPECT_TRUE(Str.endswith_lower("")); + EXPECT_TRUE(Str.endswith_lower("lo")); + EXPECT_TRUE(Str.endswith_lower("LO")); + EXPECT_TRUE(Str.endswith_lower("ELlo")); + EXPECT_FALSE(Str.endswith_lower("helloworld")); + EXPECT_FALSE(Str.endswith_lower("hi")); +} + TEST(StringRefTest, Find) { StringRef Str("hello"); EXPECT_EQ(2U, Str.find('l')); @@ -477,6 +503,32 @@ TEST(StringRefTest, getAsUnsignedIntegerBadStrings) { } } +static const char *join_input[] = { "a", "b", "c" }; +static const char join_result1[] = "a"; +static const char join_result2[] = "a:b:c"; +static const char join_result3[] = "a::b::c"; + +TEST(StringRefTest, joinStrings) { + std::vector<StringRef> v1; + std::vector<std::string> v2; + for (size_t i = 0; i < array_lengthof(join_input); ++i) { + v1.push_back(join_input[i]); + v2.push_back(join_input[i]); + } + bool v1_join1 = join(v1.begin(), v1.begin() + 1, ":") == join_result1; + EXPECT_TRUE(v1_join1); + bool v1_join2 = join(v1.begin(), v1.end(), ":") == join_result2; + EXPECT_TRUE(v1_join2); + bool v1_join3 = join(v1.begin(), v1.end(), "::") == join_result3; + EXPECT_TRUE(v1_join3); + + bool v2_join1 = join(v2.begin(), v2.begin() + 1, ":") == join_result1; + EXPECT_TRUE(v2_join1); + bool v2_join2 = join(v2.begin(), v2.end(), ":") == join_result2; + EXPECT_TRUE(v2_join2); + bool v2_join3 = join(v2.begin(), v2.end(), "::") == join_result3; + EXPECT_TRUE(v2_join3); +} } // end anonymous namespace diff --git a/unittests/ADT/polymorphic_ptr_test.cpp b/unittests/ADT/polymorphic_ptr_test.cpp new file mode 100644 index 0000000000000..bd5d83879a83f --- /dev/null +++ b/unittests/ADT/polymorphic_ptr_test.cpp @@ -0,0 +1,129 @@ +//===- llvm/unittest/ADT/polymorphic_ptr.h - polymorphic_ptr<T> 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/ADT/polymorphic_ptr.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { + +struct S { + S(int x) : x(x) {} + S *clone() { return new S(*this); } + int x; +}; + +// A function that forces the return of a copy. +template <typename T> +T dummy_copy(const T &arg) { return arg; } + +TEST(polymorphic_ptr_test, Basic) { + polymorphic_ptr<S> null; + EXPECT_FALSE((bool)null); + EXPECT_TRUE(!null); + EXPECT_EQ((S*)0, null.get()); + + S *s = new S(42); + polymorphic_ptr<S> p(s); + EXPECT_TRUE((bool)p); + EXPECT_FALSE(!p); + EXPECT_TRUE(p != null); + EXPECT_FALSE(p == null); + EXPECT_TRUE(p == s); + EXPECT_TRUE(s == p); + EXPECT_FALSE(p != s); + EXPECT_FALSE(s != p); + EXPECT_EQ(s, &*p); + EXPECT_EQ(s, p.operator->()); + EXPECT_EQ(s, p.get()); + EXPECT_EQ(42, p->x); + + EXPECT_EQ(s, p.take()); + EXPECT_FALSE((bool)p); + EXPECT_TRUE(!p); + p = s; + EXPECT_TRUE((bool)p); + EXPECT_FALSE(!p); + EXPECT_EQ(s, &*p); + EXPECT_EQ(s, p.operator->()); + EXPECT_EQ(s, p.get()); + EXPECT_EQ(42, p->x); + + polymorphic_ptr<S> p2((llvm_move(p))); +#if !LLVM_HAS_RVALUE_REFERENCES + // 'p' may not have been moved from in C++98, fake it for the test. + p2 = p.take(); +#endif + EXPECT_FALSE((bool)p); + EXPECT_TRUE(!p); + EXPECT_TRUE((bool)p2); + EXPECT_FALSE(!p2); + EXPECT_EQ(s, &*p2); + + using std::swap; + swap(p, p2); + EXPECT_TRUE((bool)p); + EXPECT_FALSE(!p); + EXPECT_EQ(s, &*p); + EXPECT_FALSE((bool)p2); + EXPECT_TRUE(!p2); + + // Force copies and that everything survives. + polymorphic_ptr<S> p3 = dummy_copy(polymorphic_ptr<S>(p)); + EXPECT_TRUE((bool)p3); + EXPECT_FALSE(!p3); + EXPECT_NE(s, &*p3); + EXPECT_EQ(42, p3->x); + + // Force copies of null without trying to dereference anything. + polymorphic_ptr<S> null_copy = dummy_copy(polymorphic_ptr<S>(null)); + EXPECT_FALSE((bool)null_copy); + EXPECT_TRUE(!null_copy); + EXPECT_EQ(null, null_copy); +} + +struct Base { + virtual ~Base() {} + virtual Base *clone() = 0; + virtual StringRef name() { return "Base"; } +}; + +struct DerivedA : Base { + virtual DerivedA *clone() { return new DerivedA(); } + virtual StringRef name() { return "DerivedA"; } +}; +struct DerivedB : Base { + virtual DerivedB *clone() { return new DerivedB(); } + virtual StringRef name() { return "DerivedB"; } +}; + +TEST(polymorphic_ptr_test, Polymorphism) { + polymorphic_ptr<Base> a(new DerivedA()); + polymorphic_ptr<Base> b(new DerivedB()); + + EXPECT_EQ("DerivedA", a->name()); + EXPECT_EQ("DerivedB", b->name()); + + polymorphic_ptr<Base> copy = dummy_copy(a); + EXPECT_NE(a, copy); + EXPECT_EQ("DerivedA", copy->name()); + + copy = dummy_copy(b); + EXPECT_NE(b, copy); + EXPECT_EQ("DerivedB", copy->name()); + + // Test creating a copy out of a temporary directly. + copy = dummy_copy<polymorphic_ptr<Base> >(new DerivedA()); + EXPECT_NE(a, copy); + EXPECT_EQ("DerivedA", copy->name()); +} + +} diff --git a/unittests/Analysis/CFGTest.cpp b/unittests/Analysis/CFGTest.cpp new file mode 100644 index 0000000000000..e931709620f32 --- /dev/null +++ b/unittests/Analysis/CFGTest.cpp @@ -0,0 +1,376 @@ +//===- CFGTest.cpp - CFG 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/CFG.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Analysis/Dominators.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Assembly/Parser.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/InstIterator.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Pass.h" +#include "llvm/PassManager.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +// This fixture assists in running the isPotentiallyReachable utility four ways +// and ensuring it produces the correct answer each time. +class IsPotentiallyReachableTest : public testing::Test { +protected: + void ParseAssembly(const char *Assembly) { + M.reset(new Module("Module", getGlobalContext())); + + SMDiagnostic Error; + bool Parsed = ParseAssemblyString(Assembly, M.get(), + Error, M->getContext()) == M.get(); + + std::string errMsg; + raw_string_ostream os(errMsg); + Error.print("", os); + + if (!Parsed) { + // A failure here means that the test itself is buggy. + report_fatal_error(os.str().c_str()); + } + + Function *F = M->getFunction("test"); + if (F == NULL) + report_fatal_error("Test must have a function named @test"); + + A = B = NULL; + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + if (I->hasName()) { + if (I->getName() == "A") + A = &*I; + else if (I->getName() == "B") + B = &*I; + } + } + if (A == NULL) + report_fatal_error("@test must have an instruction %A"); + if (B == NULL) + report_fatal_error("@test must have an instruction %B"); + } + + void ExpectPath(bool ExpectedResult) { + static char ID; + class IsPotentiallyReachableTestPass : public FunctionPass { + public: + IsPotentiallyReachableTestPass(bool ExpectedResult, + Instruction *A, Instruction *B) + : FunctionPass(ID), ExpectedResult(ExpectedResult), A(A), B(B) {} + + static int initialize() { + PassInfo *PI = new PassInfo("isPotentiallyReachable testing pass", + "", &ID, 0, true, true); + PassRegistry::getPassRegistry()->registerPass(*PI, false); + initializeLoopInfoPass(*PassRegistry::getPassRegistry()); + initializeDominatorTreePass(*PassRegistry::getPassRegistry()); + return 0; + } + + void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired<LoopInfo>(); + AU.addRequired<DominatorTree>(); + } + + bool runOnFunction(Function &F) { + if (!F.hasName() || F.getName() != "test") + return false; + + LoopInfo *LI = &getAnalysis<LoopInfo>(); + DominatorTree *DT = &getAnalysis<DominatorTree>(); + EXPECT_EQ(isPotentiallyReachable(A, B, 0, 0), ExpectedResult); + EXPECT_EQ(isPotentiallyReachable(A, B, DT, 0), ExpectedResult); + EXPECT_EQ(isPotentiallyReachable(A, B, 0, LI), ExpectedResult); + EXPECT_EQ(isPotentiallyReachable(A, B, DT, LI), ExpectedResult); + return false; + } + bool ExpectedResult; + Instruction *A, *B; + }; + + static int initialize = IsPotentiallyReachableTestPass::initialize(); + (void)initialize; + + IsPotentiallyReachableTestPass *P = + new IsPotentiallyReachableTestPass(ExpectedResult, A, B); + PassManager PM; + PM.add(P); + PM.run(*M); + } +private: + OwningPtr<Module> M; + Instruction *A, *B; +}; + +} + +TEST_F(IsPotentiallyReachableTest, SameBlockNoPath) { + ParseAssembly( + "define void @test() {\n" + "entry:\n" + " bitcast i8 undef to i8\n" + " %B = bitcast i8 undef to i8\n" + " bitcast i8 undef to i8\n" + " bitcast i8 undef to i8\n" + " %A = bitcast i8 undef to i8\n" + " ret void\n" + "}\n"); + ExpectPath(false); +} + +TEST_F(IsPotentiallyReachableTest, SameBlockPath) { + ParseAssembly( + "define void @test() {\n" + "entry:\n" + " %A = bitcast i8 undef to i8\n" + " bitcast i8 undef to i8\n" + " bitcast i8 undef to i8\n" + " %B = bitcast i8 undef to i8\n" + " ret void\n" + "}\n"); + ExpectPath(true); +} + +TEST_F(IsPotentiallyReachableTest, SameBlockNoLoop) { + ParseAssembly( + "define void @test() {\n" + "entry:\n" + " br label %middle\n" + "middle:\n" + " %B = bitcast i8 undef to i8\n" + " bitcast i8 undef to i8\n" + " bitcast i8 undef to i8\n" + " %A = bitcast i8 undef to i8\n" + " br label %nextblock\n" + "nextblock:\n" + " ret void\n" + "}\n"); + ExpectPath(false); +} + +TEST_F(IsPotentiallyReachableTest, StraightNoPath) { + ParseAssembly( + "define void @test() {\n" + "entry:\n" + " %B = bitcast i8 undef to i8\n" + " br label %exit\n" + "exit:\n" + " %A = bitcast i8 undef to i8\n" + " ret void\n" + "}"); + ExpectPath(false); +} + +TEST_F(IsPotentiallyReachableTest, StraightPath) { + ParseAssembly( + "define void @test() {\n" + "entry:\n" + " %A = bitcast i8 undef to i8\n" + " br label %exit\n" + "exit:\n" + " %B = bitcast i8 undef to i8\n" + " ret void\n" + "}"); + ExpectPath(true); +} + +TEST_F(IsPotentiallyReachableTest, DestUnreachable) { + ParseAssembly( + "define void @test() {\n" + "entry:\n" + " br label %midblock\n" + "midblock:\n" + " %A = bitcast i8 undef to i8\n" + " ret void\n" + "unreachable:\n" + " %B = bitcast i8 undef to i8\n" + " br label %midblock\n" + "}"); + ExpectPath(false); +} + +TEST_F(IsPotentiallyReachableTest, BranchToReturn) { + ParseAssembly( + "define void @test(i1 %x) {\n" + "entry:\n" + " %A = bitcast i8 undef to i8\n" + " br i1 %x, label %block1, label %block2\n" + "block1:\n" + " ret void\n" + "block2:\n" + " %B = bitcast i8 undef to i8\n" + " ret void\n" + "}"); + ExpectPath(true); +} + +TEST_F(IsPotentiallyReachableTest, SimpleLoop1) { + ParseAssembly( + "declare i1 @switch()\n" + "\n" + "define void @test() {\n" + "entry:\n" + " br label %loop\n" + "loop:\n" + " %B = bitcast i8 undef to i8\n" + " %A = bitcast i8 undef to i8\n" + " %x = call i1 @switch()\n" + " br i1 %x, label %loop, label %exit\n" + "exit:\n" + " ret void\n" + "}"); + ExpectPath(true); +} + +TEST_F(IsPotentiallyReachableTest, SimpleLoop2) { + ParseAssembly( + "declare i1 @switch()\n" + "\n" + "define void @test() {\n" + "entry:\n" + " %B = bitcast i8 undef to i8\n" + " br label %loop\n" + "loop:\n" + " %A = bitcast i8 undef to i8\n" + " %x = call i1 @switch()\n" + " br i1 %x, label %loop, label %exit\n" + "exit:\n" + " ret void\n" + "}"); + ExpectPath(false); +} + +TEST_F(IsPotentiallyReachableTest, SimpleLoop3) { + ParseAssembly( + "declare i1 @switch()\n" + "\n" + "define void @test() {\n" + "entry:\n" + " br label %loop\n" + "loop:\n" + " %B = bitcast i8 undef to i8\n" + " %x = call i1 @switch()\n" + " br i1 %x, label %loop, label %exit\n" + "exit:\n" + " %A = bitcast i8 undef to i8\n" + " ret void\n" + "}"); + ExpectPath(false); +} + + +TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOther1) { + ParseAssembly( + "declare i1 @switch()\n" + "\n" + "define void @test() {\n" + "entry:\n" + " br label %loop1\n" + "loop1:\n" + " %A = bitcast i8 undef to i8\n" + " %x = call i1 @switch()\n" + " br i1 %x, label %loop1, label %loop1exit\n" + "loop1exit:\n" + " br label %loop2\n" + "loop2:\n" + " %B = bitcast i8 undef to i8\n" + " %y = call i1 @switch()\n" + " br i1 %x, label %loop2, label %loop2exit\n" + "loop2exit:" + " ret void\n" + "}"); + ExpectPath(true); +} + +TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOther2) { + ParseAssembly( + "declare i1 @switch()\n" + "\n" + "define void @test() {\n" + "entry:\n" + " br label %loop1\n" + "loop1:\n" + " %B = bitcast i8 undef to i8\n" + " %x = call i1 @switch()\n" + " br i1 %x, label %loop1, label %loop1exit\n" + "loop1exit:\n" + " br label %loop2\n" + "loop2:\n" + " %A = bitcast i8 undef to i8\n" + " %y = call i1 @switch()\n" + " br i1 %x, label %loop2, label %loop2exit\n" + "loop2exit:" + " ret void\n" + "}"); + ExpectPath(false); +} + +TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOtherInsideAThirdLoop) { + ParseAssembly( + "declare i1 @switch()\n" + "\n" + "define void @test() {\n" + "entry:\n" + " br label %outerloop3\n" + "outerloop3:\n" + " br label %innerloop1\n" + "innerloop1:\n" + " %B = bitcast i8 undef to i8\n" + " %x = call i1 @switch()\n" + " br i1 %x, label %innerloop1, label %innerloop1exit\n" + "innerloop1exit:\n" + " br label %innerloop2\n" + "innerloop2:\n" + " %A = bitcast i8 undef to i8\n" + " %y = call i1 @switch()\n" + " br i1 %x, label %innerloop2, label %innerloop2exit\n" + "innerloop2exit:" + " ;; In outer loop3 now.\n" + " %z = call i1 @switch()\n" + " br i1 %z, label %outerloop3, label %exit\n" + "exit:\n" + " ret void\n" + "}"); + ExpectPath(true); +} + +TEST_F(IsPotentiallyReachableTest, BranchInsideLoop) { + ParseAssembly( + "declare i1 @switch()\n" + "\n" + "define void @test() {\n" + "entry:\n" + " br label %loop\n" + "loop:\n" + " %x = call i1 @switch()\n" + " br i1 %x, label %nextloopblock, label %exit\n" + "nextloopblock:\n" + " %y = call i1 @switch()\n" + " br i1 %y, label %left, label %right\n" + "left:\n" + " %A = bitcast i8 undef to i8\n" + " br label %loop\n" + "right:\n" + " %B = bitcast i8 undef to i8\n" + " br label %loop\n" + "exit:\n" + " ret void\n" + "}"); + ExpectPath(true); +} diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt index 7991a4101c2cc..7e5b35852749c 100644 --- a/unittests/Analysis/CMakeLists.txt +++ b/unittests/Analysis/CMakeLists.txt @@ -1,7 +1,9 @@ set(LLVM_LINK_COMPONENTS Analysis + AsmParser ) add_llvm_unittest(AnalysisTests + CFGTest.cpp ScalarEvolutionTest.cpp ) diff --git a/unittests/Analysis/Makefile b/unittests/Analysis/Makefile index b548d25d1e5b0..527f4525e87e5 100644 --- a/unittests/Analysis/Makefile +++ b/unittests/Analysis/Makefile @@ -9,7 +9,7 @@ LEVEL = ../.. TESTNAME = Analysis -LINK_COMPONENTS := analysis +LINK_COMPONENTS := analysis asmparser include $(LEVEL)/Makefile.config include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 4b7e418cd180b..52702ba23aac6 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -7,10 +7,13 @@ endfunction() add_subdirectory(ADT) add_subdirectory(Analysis) -add_subdirectory(ExecutionEngine) add_subdirectory(Bitcode) +add_subdirectory(CodeGen) +add_subdirectory(DebugInfo) +add_subdirectory(ExecutionEngine) +add_subdirectory(IR) +add_subdirectory(MC) +add_subdirectory(Object) add_subdirectory(Option) add_subdirectory(Support) add_subdirectory(Transforms) -add_subdirectory(IR) -add_subdirectory(DebugInfo) diff --git a/unittests/CodeGen/CMakeLists.txt b/unittests/CodeGen/CMakeLists.txt new file mode 100644 index 0000000000000..5973bae3aaba2 --- /dev/null +++ b/unittests/CodeGen/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + asmprinter + codegen + support + ) + +set(CodeGenSources + DIEHashTest.cpp + ) + +add_llvm_unittest(CodeGenTests + ${CodeGenSources} + ) diff --git a/unittests/CodeGen/DIEHashTest.cpp b/unittests/CodeGen/DIEHashTest.cpp new file mode 100644 index 0000000000000..8d8fc39a30f58 --- /dev/null +++ b/unittests/CodeGen/DIEHashTest.cpp @@ -0,0 +1,517 @@ +//===- 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 "../lib/CodeGen/AsmPrinter/DIE.h" +#include "../lib/CodeGen/AsmPrinter/DIEHash.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { +TEST(DIEHashTest, Data1) { + DIEHash Hash; + DIE Die(dwarf::DW_TAG_base_type); + DIEInteger Size(4); + Die.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Size); + uint64_t MD5Res = Hash.computeTypeSignature(Die); + ASSERT_EQ(0x1AFE116E83701108ULL, MD5Res); +} + +// struct {}; +TEST(DIEHashTest, TrivialType) { + DIE Unnamed(dwarf::DW_TAG_structure_type); + DIEInteger One(1); + Unnamed.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + // Line and file number are ignored. + Unnamed.addValue(dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, &One); + Unnamed.addValue(dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, &One); + uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed); + + // The exact same hash GCC produces for this DIE. + ASSERT_EQ(0x715305ce6cfd9ad1ULL, MD5Res); +} + +// struct foo { }; +TEST(DIEHashTest, NamedType) { + DIE Foo(dwarf::DW_TAG_structure_type); + DIEInteger One(1); + DIEString FooStr(&One, "foo"); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Foo); + + // The exact same hash GCC produces for this DIE. + ASSERT_EQ(0xd566dbd2ca5265ffULL, MD5Res); +} + +// namespace space { struct foo { }; } +TEST(DIEHashTest, NamespacedType) { + DIE CU(dwarf::DW_TAG_compile_unit); + + DIE *Space = new DIE(dwarf::DW_TAG_namespace); + DIEInteger One(1); + DIEString SpaceStr(&One, "space"); + Space->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &SpaceStr); + // DW_AT_declaration is ignored. + Space->addValue(dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present, &One); + // sibling? + + DIE *Foo = new DIE(dwarf::DW_TAG_structure_type); + DIEString FooStr(&One, "foo"); + Foo->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + Foo->addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + Space->addChild(Foo); + CU.addChild(Space); + + uint64_t MD5Res = DIEHash().computeTypeSignature(*Foo); + + // The exact same hash GCC produces for this DIE. + ASSERT_EQ(0x7b80381fd17f1e33ULL, MD5Res); +} + +// struct { int member; }; +TEST(DIEHashTest, TypeWithMember) { + DIE Unnamed(dwarf::DW_TAG_structure_type); + DIEInteger Four(4); + Unnamed.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Four); + + DIE *Member = new DIE(dwarf::DW_TAG_member); + DIEString MemberStr(&Four, "member"); + Member->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemberStr); + DIEInteger Zero(0); + Member->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Zero); + + Unnamed.addChild(Member); + + DIE Int(dwarf::DW_TAG_base_type); + DIEString IntStr(&Four, "int"); + Int.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &IntStr); + Int.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Four); + DIEInteger Five(5); + Int.addValue(dwarf::DW_AT_encoding, dwarf::DW_FORM_data1, &Five); + + DIEEntry IntRef(&Int); + Member->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &IntRef); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed); + + ASSERT_EQ(0x5646aa436b7e07c6ULL, MD5Res); +} + +// struct foo { int mem1, mem2; }; +TEST(DIEHashTest, ReusedType) { + DIE Unnamed(dwarf::DW_TAG_structure_type); + DIEInteger Eight(8); + Unnamed.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + + DIE *Mem1 = new DIE(dwarf::DW_TAG_member); + DIEInteger Four(4); + DIEString Mem1Str(&Four, "mem1"); + Mem1->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &Mem1Str); + DIEInteger Zero(0); + Mem1->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Zero); + + Unnamed.addChild(Mem1); + + DIE *Mem2 = new DIE(dwarf::DW_TAG_member); + DIEString Mem2Str(&Four, "mem2"); + Mem2->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &Mem2Str); + Mem2->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Four); + + Unnamed.addChild(Mem2); + + DIE Int(dwarf::DW_TAG_base_type); + DIEString IntStr(&Four, "int"); + Int.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &IntStr); + Int.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Four); + DIEInteger Five(5); + Int.addValue(dwarf::DW_AT_encoding, dwarf::DW_FORM_data1, &Five); + + DIEEntry IntRef(&Int); + Mem1->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &IntRef); + Mem2->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &IntRef); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed); + + ASSERT_EQ(0x3a7dc3ed7b76b2f8ULL, MD5Res); +} + +// struct foo { static foo f; }; +TEST(DIEHashTest, RecursiveType) { + DIE Foo(dwarf::DW_TAG_structure_type); + DIEInteger One(1); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + DIEString FooStr(&One, "foo"); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + DIEString MemStr(&One, "mem"); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + DIEEntry FooRef(&Foo); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooRef); + // DW_AT_external and DW_AT_declaration are ignored anyway, so skip them. + + Foo.addChild(Mem); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Foo); + + ASSERT_EQ(0x73d8b25aef227b06ULL, MD5Res); +} + +// struct foo { foo *mem; }; +TEST(DIEHashTest, Pointer) { + DIE Foo(dwarf::DW_TAG_structure_type); + DIEInteger Eight(8); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEString FooStr(&Eight, "foo"); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + DIEString MemStr(&Eight, "mem"); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + DIEInteger Zero(0); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Zero); + + DIE FooPtr(dwarf::DW_TAG_pointer_type); + FooPtr.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEEntry FooRef(&Foo); + FooPtr.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooRef); + + DIEEntry FooPtrRef(&FooPtr); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooPtrRef); + + Foo.addChild(Mem); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Foo); + + ASSERT_EQ(0x74ea73862e8708d2ULL, MD5Res); +} + +// struct foo { foo &mem; }; +TEST(DIEHashTest, Reference) { + DIE Foo(dwarf::DW_TAG_structure_type); + DIEInteger Eight(8); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEString FooStr(&Eight, "foo"); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + DIEString MemStr(&Eight, "mem"); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + DIEInteger Zero(0); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Zero); + + DIE FooRef(dwarf::DW_TAG_reference_type); + FooRef.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEEntry FooEntry(&Foo); + FooRef.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooEntry); + + DIE FooRefConst(dwarf::DW_TAG_const_type); + DIEEntry FooRefRef(&FooRef); + FooRefConst.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooRefRef); + + DIEEntry FooRefConstRef(&FooRefConst); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooRefConstRef); + + Foo.addChild(Mem); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Foo); + + ASSERT_EQ(0xa0b15f467ad4525bULL, MD5Res); +} + +// struct foo { foo &&mem; }; +TEST(DIEHashTest, RValueReference) { + DIE Foo(dwarf::DW_TAG_structure_type); + DIEInteger Eight(8); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEString FooStr(&Eight, "foo"); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + DIEString MemStr(&Eight, "mem"); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + DIEInteger Zero(0); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Zero); + + DIE FooRef(dwarf::DW_TAG_rvalue_reference_type); + FooRef.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEEntry FooEntry(&Foo); + FooRef.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooEntry); + + DIE FooRefConst(dwarf::DW_TAG_const_type); + DIEEntry FooRefRef(&FooRef); + FooRefConst.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooRefRef); + + DIEEntry FooRefConstRef(&FooRefConst); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooRefConstRef); + + Foo.addChild(Mem); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Foo); + + ASSERT_EQ(0xad211c8c3b31e57ULL, MD5Res); +} + +// struct foo { foo foo::*mem; }; +TEST(DIEHashTest, PtrToMember) { + DIE Foo(dwarf::DW_TAG_structure_type); + DIEInteger Eight(8); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEString FooStr(&Eight, "foo"); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + DIEString MemStr(&Eight, "mem"); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + DIEInteger Zero(0); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Zero); + + DIE PtrToFooMem(dwarf::DW_TAG_ptr_to_member_type); + DIEEntry FooEntry(&Foo); + PtrToFooMem.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &FooEntry); + PtrToFooMem.addValue(dwarf::DW_AT_containing_type, dwarf::DW_FORM_ref4, &FooEntry); + + DIEEntry PtrToFooMemRef(&PtrToFooMem); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &PtrToFooMemRef); + + Foo.addChild(Mem); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Foo); + + ASSERT_EQ(0x852e0c9ff7c04ebULL, MD5Res); +} + +// Check that the hash for a pointer-to-member matches regardless of whether the +// pointed-to type is a declaration or a definition. +// +// struct bar; // { }; +// struct foo { bar foo::*mem; }; +TEST(DIEHashTest, PtrToMemberDeclDefMatch) { + DIEInteger Zero(0); + DIEInteger One(1); + DIEInteger Eight(8); + DIEString FooStr(&Eight, "foo"); + DIEString BarStr(&Eight, "bar"); + DIEString MemStr(&Eight, "mem"); + uint64_t MD5ResDecl; + { + DIE Bar(dwarf::DW_TAG_structure_type); + Bar.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &BarStr); + Bar.addValue(dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present, &One); + + DIE Foo(dwarf::DW_TAG_structure_type); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, + &Zero); + + DIE PtrToFooMem(dwarf::DW_TAG_ptr_to_member_type); + DIEEntry BarEntry(&Bar); + PtrToFooMem.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &BarEntry); + DIEEntry FooEntry(&Foo); + PtrToFooMem.addValue(dwarf::DW_AT_containing_type, dwarf::DW_FORM_ref4, + &FooEntry); + + DIEEntry PtrToFooMemRef(&PtrToFooMem); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &PtrToFooMemRef); + + Foo.addChild(Mem); + + MD5ResDecl = DIEHash().computeTypeSignature(Foo); + } + uint64_t MD5ResDef; + { + DIE Bar(dwarf::DW_TAG_structure_type); + Bar.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &BarStr); + Bar.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + DIE Foo(dwarf::DW_TAG_structure_type); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, + &Zero); + + DIE PtrToFooMem(dwarf::DW_TAG_ptr_to_member_type); + DIEEntry BarEntry(&Bar); + PtrToFooMem.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &BarEntry); + DIEEntry FooEntry(&Foo); + PtrToFooMem.addValue(dwarf::DW_AT_containing_type, dwarf::DW_FORM_ref4, + &FooEntry); + + DIEEntry PtrToFooMemRef(&PtrToFooMem); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &PtrToFooMemRef); + + Foo.addChild(Mem); + + MD5ResDef = DIEHash().computeTypeSignature(Foo); + } + ASSERT_EQ(MD5ResDef, MD5ResDecl); +} + +// Check that the hash for a pointer-to-member matches regardless of whether the +// pointed-to type is a declaration or a definition. +// +// struct bar; // { }; +// struct foo { bar bar::*mem; }; +TEST(DIEHashTest, PtrToMemberDeclDefMisMatch) { + DIEInteger Zero(0); + DIEInteger One(1); + DIEInteger Eight(8); + DIEString FooStr(&Eight, "foo"); + DIEString BarStr(&Eight, "bar"); + DIEString MemStr(&Eight, "mem"); + uint64_t MD5ResDecl; + { + DIE Bar(dwarf::DW_TAG_structure_type); + Bar.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &BarStr); + Bar.addValue(dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present, &One); + + DIE Foo(dwarf::DW_TAG_structure_type); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, + &Zero); + + DIE PtrToFooMem(dwarf::DW_TAG_ptr_to_member_type); + DIEEntry BarEntry(&Bar); + PtrToFooMem.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &BarEntry); + PtrToFooMem.addValue(dwarf::DW_AT_containing_type, dwarf::DW_FORM_ref4, + &BarEntry); + + DIEEntry PtrToFooMemRef(&PtrToFooMem); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &PtrToFooMemRef); + + Foo.addChild(Mem); + + MD5ResDecl = DIEHash().computeTypeSignature(Foo); + } + uint64_t MD5ResDef; + { + DIE Bar(dwarf::DW_TAG_structure_type); + Bar.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &BarStr); + Bar.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + DIE Foo(dwarf::DW_TAG_structure_type); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, + &Zero); + + DIE PtrToFooMem(dwarf::DW_TAG_ptr_to_member_type); + DIEEntry BarEntry(&Bar); + PtrToFooMem.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &BarEntry); + PtrToFooMem.addValue(dwarf::DW_AT_containing_type, dwarf::DW_FORM_ref4, + &BarEntry); + + DIEEntry PtrToFooMemRef(&PtrToFooMem); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &PtrToFooMemRef); + + Foo.addChild(Mem); + + MD5ResDef = DIEHash().computeTypeSignature(Foo); + } + // FIXME: This seems to be a bug in the DWARF type hashing specification that + // only uses the brief name hashing for types referenced via DW_AT_type. In + // this case the type is referenced via DW_AT_containing_type and full hashing + // causes a hash to differ when the containing type is a declaration in one TU + // and a definition in another. + ASSERT_NE(MD5ResDef, MD5ResDecl); +} + +// struct { } a; +// struct foo { decltype(a) mem; }; +TEST(DIEHashTest, RefUnnamedType) { + DIEInteger Zero(0); + DIEInteger One(1); + DIEInteger Eight(8); + DIEString FooStr(&Zero, "foo"); + DIEString MemStr(&Zero, "mem"); + + DIE Unnamed(dwarf::DW_TAG_structure_type); + Unnamed.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + DIE Foo(dwarf::DW_TAG_structure_type); + Foo.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + Foo.addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + + DIE *Mem = new DIE(dwarf::DW_TAG_member); + Mem->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &MemStr); + Mem->addValue(dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, &Zero); + + DIE UnnamedPtr(dwarf::DW_TAG_pointer_type); + UnnamedPtr.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &Eight); + DIEEntry UnnamedRef(&Unnamed); + UnnamedPtr.addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &UnnamedRef); + + DIEEntry UnnamedPtrRef(&UnnamedPtr); + Mem->addValue(dwarf::DW_AT_type, dwarf::DW_FORM_ref4, &UnnamedPtrRef); + + Foo.addChild(Mem); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Foo); + + ASSERT_EQ(0x954e026f01c02529ULL, MD5Res); +} + +// struct { struct bar { }; }; +TEST(DIEHashTest, NestedType) { + DIE Unnamed(dwarf::DW_TAG_structure_type); + DIEInteger One(1); + Unnamed.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + DIE *Foo = new DIE(dwarf::DW_TAG_structure_type); + DIEString FooStr(&One, "foo"); + Foo->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FooStr); + Foo->addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + Unnamed.addChild(Foo); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed); + + // The exact same hash GCC produces for this DIE. + ASSERT_EQ(0xde8a3b7b43807f4aULL, MD5Res); +} + +// struct { static void func(); }; +TEST(DIEHashTest, MemberFunc) { + DIE Unnamed(dwarf::DW_TAG_structure_type); + DIEInteger One(1); + Unnamed.addValue(dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, &One); + + DIE *Func = new DIE(dwarf::DW_TAG_subprogram); + DIEString FuncStr(&One, "func"); + Func->addValue(dwarf::DW_AT_name, dwarf::DW_FORM_strp, &FuncStr); + + Unnamed.addChild(Func); + + uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed); + + // The exact same hash GCC produces for this DIE. + ASSERT_EQ(0xd36a1b6dfb604ba0ULL, MD5Res); +} +} diff --git a/unittests/CodeGen/Makefile b/unittests/CodeGen/Makefile new file mode 100644 index 0000000000000..4f07017c2914e --- /dev/null +++ b/unittests/CodeGen/Makefile @@ -0,0 +1,16 @@ +##===- unittests/DebugInfo/Makefile ------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +TESTNAME = CodeGen +LINK_COMPONENTS := asmprinter codegen support + +include $(LEVEL)/Makefile.config + +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/DebugInfo/DWARFFormValueTest.cpp b/unittests/DebugInfo/DWARFFormValueTest.cpp index 04b859bdb9db8..38b932e3c9caf 100644 --- a/unittests/DebugInfo/DWARFFormValueTest.cpp +++ b/unittests/DebugInfo/DWARFFormValueTest.cpp @@ -18,14 +18,32 @@ 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. - const uint8_t *sizes = DWARFFormValue::getFixedFormSizes(4, 2); + 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(0, DWARFFormValue::getFixedFormSizes(16, 2)); + EXPECT_EQ(0U, DWARFFormValue::getFixedFormSizes(16, 2).size()); +} + +bool isFormClass(uint16_t Form, DWARFFormValue::FormClass FC) { + return DWARFFormValue(Form).isFormClass(FC); +} + +TEST(DWARFFormValue, FormClass) { + EXPECT_TRUE(isFormClass(DW_FORM_addr, DWARFFormValue::FC_Address)); + EXPECT_FALSE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Address)); + EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Constant)); + EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_SectionOffset)); + EXPECT_TRUE( + isFormClass(DW_FORM_sec_offset, DWARFFormValue::FC_SectionOffset)); + EXPECT_TRUE(isFormClass(DW_FORM_GNU_str_index, DWARFFormValue::FC_String)); + EXPECT_TRUE(isFormClass(DW_FORM_GNU_addr_index, DWARFFormValue::FC_Address)); + EXPECT_FALSE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Address)); + EXPECT_TRUE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Reference)); + EXPECT_TRUE(isFormClass(DW_FORM_ref_sig8, DWARFFormValue::FC_Reference)); } } // end anonymous namespace diff --git a/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp b/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp index 21ca0d448ced3..731f7807f5934 100644 --- a/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp +++ b/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp @@ -277,4 +277,27 @@ TEST(JITMemoryManagerTest, TestManyStubs) { EXPECT_EQ(3U, MemMgr->GetNumStubSlabs()); } +// Check section allocation and alignment +TEST(JITMemoryManagerTest, AllocateSection) { + OwningPtr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uint8_t *code1 = MemMgr->allocateCodeSection(256, 0, 1, StringRef()); + uint8_t *data1 = MemMgr->allocateDataSection(256, 16, 2, StringRef(), true); + uint8_t *code2 = MemMgr->allocateCodeSection(257, 32, 3, StringRef()); + uint8_t *data2 = MemMgr->allocateDataSection(256, 64, 4, StringRef(), false); + uint8_t *code3 = MemMgr->allocateCodeSection(258, 64, 5, StringRef()); + + EXPECT_NE((uint8_t*)0, code1); + EXPECT_NE((uint8_t*)0, code2); + EXPECT_NE((uint8_t*)0, data1); + EXPECT_NE((uint8_t*)0, data2); + + // Check alignment + EXPECT_EQ((uint64_t)code1 & 0xf, 0u); + EXPECT_EQ((uint64_t)code2 & 0x1f, 0u); + EXPECT_EQ((uint64_t)code3 & 0x3f, 0u); + EXPECT_EQ((uint64_t)data1 & 0xf, 0u); + EXPECT_EQ((uint64_t)data2 & 0x3f, 0u); +} + } diff --git a/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp index e6f4cb9af25f5..4c7b1e23195d9 100644 --- a/unittests/ExecutionEngine/JIT/JITTest.cpp +++ b/unittests/ExecutionEngine/JIT/JITTest.cpp @@ -33,6 +33,22 @@ using namespace llvm; +// This variable is intentionally defined differently in the statically-compiled +// program from the IR input to the JIT to assert that the JIT doesn't use its +// definition. Note that this variable must be defined even on platforms where +// JIT tests are disabled as it is referenced from the .def file. +extern "C" int32_t JITTest_AvailableExternallyGlobal; +int32_t JITTest_AvailableExternallyGlobal LLVM_ATTRIBUTE_USED = 42; + +// This function is intentionally defined differently in the statically-compiled +// program from the IR input to the JIT to assert that the JIT doesn't use its +// definition. Note that this function must be defined even on platforms where +// JIT tests are disabled as it is referenced from the .def file. +extern "C" int32_t JITTest_AvailableExternallyFunction() LLVM_ATTRIBUTE_USED; +extern "C" int32_t JITTest_AvailableExternallyFunction() { + return 42; +} + namespace { // Tests on ARM, PowerPC and SystemZ disabled as we're running the old jit @@ -119,15 +135,19 @@ public: EndFunctionBodyCall(F, FunctionStart, FunctionEnd)); Base->endFunctionBody(F, FunctionStart, FunctionEnd); } - virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, bool IsReadOnly) { - return Base->allocateDataSection(Size, Alignment, SectionID, IsReadOnly); + virtual uint8_t *allocateDataSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID, + StringRef SectionName, bool IsReadOnly) { + return Base->allocateDataSection( + Size, Alignment, SectionID, SectionName, IsReadOnly); } - virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) { - return Base->allocateCodeSection(Size, Alignment, SectionID); + virtual uint8_t *allocateCodeSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID, + StringRef SectionName) { + return Base->allocateCodeSection( + Size, Alignment, SectionID, SectionName); } - virtual bool applyPermissions(std::string *ErrMsg) { return false; } + virtual bool finalizeMemory(std::string *ErrMsg) { return false; } virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { return Base->allocateSpace(Size, Alignment); } @@ -143,54 +163,6 @@ public: deallocateFunctionBodyCalls.push_back(DeallocateFunctionBodyCall(Body)); Base->deallocateFunctionBody(Body); } - struct DeallocateExceptionTableCall { - DeallocateExceptionTableCall(const void *ET) : ET(ET) {} - const void *ET; - }; - std::vector<DeallocateExceptionTableCall> deallocateExceptionTableCalls; - virtual void deallocateExceptionTable(void *ET) { - deallocateExceptionTableCalls.push_back(DeallocateExceptionTableCall(ET)); - Base->deallocateExceptionTable(ET); - } - struct StartExceptionTableCall { - StartExceptionTableCall(uint8_t *Result, const Function *F, - uintptr_t ActualSize, uintptr_t ActualSizeResult) - : Result(Result), F(F), F_dump(DumpFunction(F)), - ActualSize(ActualSize), ActualSizeResult(ActualSizeResult) {} - uint8_t *Result; - const Function *F; - std::string F_dump; - uintptr_t ActualSize; - uintptr_t ActualSizeResult; - }; - std::vector<StartExceptionTableCall> startExceptionTableCalls; - virtual uint8_t *startExceptionTable(const Function *F, - uintptr_t &ActualSize) { - uintptr_t InitialActualSize = ActualSize; - uint8_t *Result = Base->startExceptionTable(F, ActualSize); - startExceptionTableCalls.push_back( - StartExceptionTableCall(Result, F, InitialActualSize, ActualSize)); - return Result; - } - struct EndExceptionTableCall { - EndExceptionTableCall(const Function *F, uint8_t *TableStart, - uint8_t *TableEnd, uint8_t* FrameRegister) - : F(F), F_dump(DumpFunction(F)), - TableStart(TableStart), TableEnd(TableEnd), - FrameRegister(FrameRegister) {} - const Function *F; - std::string F_dump; - uint8_t *TableStart; - uint8_t *TableEnd; - uint8_t *FrameRegister; - }; - std::vector<EndExceptionTableCall> endExceptionTableCalls; - virtual void endExceptionTable(const Function *F, uint8_t *TableStart, - uint8_t *TableEnd, uint8_t* FrameRegister) { - endExceptionTableCalls.push_back( - EndExceptionTableCall(F, TableStart, TableEnd, FrameRegister)); - return Base->endExceptionTable(F, TableStart, TableEnd, FrameRegister); - } }; bool LoadAssemblyInto(Module *M, const char *assembly) { @@ -216,7 +188,6 @@ class JITTest : public testing::Test { RJMM->setPoisonMemory(true); std::string Error; TargetOptions Options; - Options.JITExceptionHandling = true; TheJIT.reset(EngineBuilder(M).setEngineKind(EngineKind::JIT) .setJITMemoryManager(RJMM) .setErrorStr(&Error) @@ -302,46 +273,6 @@ TEST(JIT, GlobalInFunction) { EXPECT_EQ(3, *GPtr); } -// Regression test for a bug. The JITEmitter wasn't checking to verify that -// it hadn't run out of space while generating the DWARF exception information -// for an emitted function. - -class ExceptionMemoryManagerMock : public RecordingJITMemoryManager { - public: - virtual uint8_t *startExceptionTable(const Function *F, - uintptr_t &ActualSize) { - // force an insufficient size the first time through. - bool ChangeActualSize = false; - if (ActualSize == 0) - ChangeActualSize = true;; - uint8_t *result = - RecordingJITMemoryManager::startExceptionTable(F, ActualSize); - if (ChangeActualSize) - ActualSize = 1; - return result; - } -}; - -class JITExceptionMemoryTest : public JITTest { - protected: - virtual RecordingJITMemoryManager *createMemoryManager() { - return new ExceptionMemoryManagerMock; - } -}; - -TEST_F(JITExceptionMemoryTest, ExceptionTableOverflow) { - Function *F = Function::Create(TypeBuilder<void(void), false>::get(Context), - Function::ExternalLinkage, - "func1", M); - BasicBlock *Block = BasicBlock::Create(Context, "block", F); - IRBuilder<> Builder(Block); - Builder.CreateRetVoid(); - TheJIT->getPointerToFunction(F); - ASSERT_TRUE(RJMM->startExceptionTableCalls.size() == 2); - ASSERT_TRUE(RJMM->deallocateExceptionTableCalls.size() == 1); - ASSERT_TRUE(RJMM->endExceptionTableCalls.size() == 1); -} - int PlusOne(int arg) { return arg + 1; } @@ -501,27 +432,6 @@ TEST_F(JITTest, ModuleDeletion) { } EXPECT_EQ(RJMM->startFunctionBodyCalls.size(), RJMM->deallocateFunctionBodyCalls.size()); - - SmallPtrSet<const void*, 2> ExceptionTablesDeallocated; - unsigned NumTablesDeallocated = 0; - for (unsigned i = 0, e = RJMM->deallocateExceptionTableCalls.size(); - i != e; ++i) { - ExceptionTablesDeallocated.insert( - RJMM->deallocateExceptionTableCalls[i].ET); - if (RJMM->deallocateExceptionTableCalls[i].ET != NULL) { - // If JITEmitDebugInfo is off, we'll "deallocate" NULL, which doesn't - // appear in startExceptionTableCalls. - NumTablesDeallocated++; - } - } - for (unsigned i = 0, e = RJMM->startExceptionTableCalls.size(); i != e; ++i) { - EXPECT_TRUE(ExceptionTablesDeallocated.count( - RJMM->startExceptionTableCalls[i].Result)) - << "Function's exception table leaked: \n" - << RJMM->startExceptionTableCalls[i].F_dump; - } - EXPECT_EQ(RJMM->startExceptionTableCalls.size(), - NumTablesDeallocated); } // ARM, MIPS and PPC still emit stubs for calls since the target may be @@ -637,14 +547,6 @@ TEST_F(JITTest, FunctionIsRecompiledAndRelinked) { } #endif // !defined(__arm__) -} // anonymous namespace -// This variable is intentionally defined differently in the statically-compiled -// program from the IR input to the JIT to assert that the JIT doesn't use its -// definition. -extern "C" int32_t JITTest_AvailableExternallyGlobal; -int32_t JITTest_AvailableExternallyGlobal LLVM_ATTRIBUTE_USED = 42; -namespace { - TEST_F(JITTest, AvailableExternallyGlobalIsntEmitted) { TheJIT->DisableLazyCompilation(true); LoadAssembly("@JITTest_AvailableExternallyGlobal = " @@ -661,15 +563,6 @@ TEST_F(JITTest, AvailableExternallyGlobalIsntEmitted) { EXPECT_EQ(42, loader()) << "func should return 42 from the external global," << " not 7 from the IR version."; } -} // anonymous namespace -// This function is intentionally defined differently in the statically-compiled -// program from the IR input to the JIT to assert that the JIT doesn't use its -// definition. -extern "C" int32_t JITTest_AvailableExternallyFunction() LLVM_ATTRIBUTE_USED; -extern "C" int32_t JITTest_AvailableExternallyFunction() { - return 42; -} -namespace { TEST_F(JITTest, AvailableExternallyFunctionIsntCompiled) { TheJIT->DisableLazyCompilation(true); @@ -828,16 +721,4 @@ TEST(LazyLoadedJITTest, EagerCompiledRecursionThroughGhost) { } #endif // !defined(__arm__) && !defined(__powerpc__) && !defined(__s390__) -// This code is copied from JITEventListenerTest, but it only runs once for all -// the tests in this directory. Everything seems fine, but that's strange -// behavior. -class JITEnvironment : public testing::Environment { - virtual void SetUp() { - // Required to create a JIT. - InitializeNativeTarget(); - } -}; -testing::Environment* const jit_env = - testing::AddGlobalTestEnvironment(new JITEnvironment); - } diff --git a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt index 922cb7efd5eb6..ed4309919387d 100644 --- a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt +++ b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt @@ -2,7 +2,6 @@ set(LLVM_LINK_COMPONENTS asmparser bitreader bitwriter - jit mcjit nativecodegen ) @@ -11,6 +10,7 @@ set(MCJITTestsSources MCJITTest.cpp MCJITCAPITest.cpp MCJITMemoryManagerTest.cpp + MCJITMultipleModuleTest.cpp MCJITObjectCacheTest.cpp ) diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp index 07ea1afe1a85f..46d6d9b8a9a60 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp @@ -17,81 +17,180 @@ #include "llvm-c/ExecutionEngine.h" #include "llvm-c/Target.h" #include "llvm-c/Transforms/Scalar.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/Support/Host.h" #include "MCJITTestAPICommon.h" #include "gtest/gtest.h" using namespace llvm; +static bool didCallAllocateCodeSection; + +static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size, + unsigned alignment, + unsigned sectionID, + const char *sectionName) { + didCallAllocateCodeSection = true; + return static_cast<SectionMemoryManager*>(object)->allocateCodeSection( + size, alignment, sectionID, sectionName); +} + +static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size, + unsigned alignment, + unsigned sectionID, + const char *sectionName, + LLVMBool isReadOnly) { + return static_cast<SectionMemoryManager*>(object)->allocateDataSection( + size, alignment, sectionID, sectionName, isReadOnly); +} + +static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) { + std::string errMsgString; + bool result = + static_cast<SectionMemoryManager*>(object)->finalizeMemory(&errMsgString); + if (result) { + *errMsg = LLVMCreateMessage(errMsgString.c_str()); + return 1; + } + return 0; +} + +static void roundTripDestroy(void *object) { + delete static_cast<SectionMemoryManager*>(object); +} + +namespace { class MCJITCAPITest : public testing::Test, public MCJITTestAPICommon { protected: MCJITCAPITest() { // The architectures below are known to be compatible with MCJIT as they // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be // kept in sync. + SupportedArchs.push_back(Triple::aarch64); SupportedArchs.push_back(Triple::arm); SupportedArchs.push_back(Triple::mips); SupportedArchs.push_back(Triple::x86); SupportedArchs.push_back(Triple::x86_64); + // Some architectures have sub-architectures in which tests will fail, like + // ARM. These two vectors will define if they do have sub-archs (to avoid + // extra work for those who don't), and if so, if they are listed to work + HasSubArchs.push_back(Triple::arm); + SupportedSubArchs.push_back("armv6"); + SupportedSubArchs.push_back("armv7"); + // The operating systems below are known to be sufficiently incompatible // that they will fail the MCJIT C API tests. UnsupportedOSs.push_back(Triple::Cygwin); } -}; - -TEST_F(MCJITCAPITest, simple_function) { - SKIP_UNSUPPORTED_PLATFORM; - char *error = 0; + virtual void SetUp() { + didCallAllocateCodeSection = false; + Module = 0; + Function = 0; + Engine = 0; + Error = 0; + } - // Creates a function that returns 42, compiles it, and runs it. + virtual void TearDown() { + if (Engine) + LLVMDisposeExecutionEngine(Engine); + else if (Module) + LLVMDisposeModule(Module); + } - LLVMModuleRef module = LLVMModuleCreateWithName("simple_module"); - - LLVMSetTarget(module, HostTriple.c_str()); + void buildSimpleFunction() { + Module = LLVMModuleCreateWithName("simple_module"); + + LLVMSetTarget(Module, HostTriple.c_str()); + + Function = LLVMAddFunction( + Module, "simple_function", LLVMFunctionType(LLVMInt32Type(), 0, 0, 0)); + LLVMSetFunctionCallConv(Function, LLVMCCallConv); + + LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry"); + LLVMBuilderRef builder = LLVMCreateBuilder(); + LLVMPositionBuilderAtEnd(builder, entry); + LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0)); + + LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error); + LLVMDisposeMessage(Error); + + LLVMDisposeBuilder(builder); + } - LLVMValueRef function = LLVMAddFunction( - module, "simple_function", LLVMFunctionType(LLVMInt32Type(), 0, 0, 0)); - LLVMSetFunctionCallConv(function, LLVMCCallConv); + void buildMCJITOptions() { + LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options)); + Options.OptLevel = 2; + + // Just ensure that this field still exists. + Options.NoFramePointerElim = false; + } - LLVMBasicBlockRef entry = LLVMAppendBasicBlock(function, "entry"); - LLVMBuilderRef builder = LLVMCreateBuilder(); - LLVMPositionBuilderAtEnd(builder, entry); - LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0)); + void useRoundTripSectionMemoryManager() { + Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager( + new SectionMemoryManager(), + roundTripAllocateCodeSection, + roundTripAllocateDataSection, + roundTripFinalizeMemory, + roundTripDestroy); + } - LLVMVerifyModule(module, LLVMAbortProcessAction, &error); - LLVMDisposeMessage(error); + void buildMCJITEngine() { + ASSERT_EQ( + 0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options, + sizeof(Options), &Error)); + } - LLVMDisposeBuilder(builder); + void buildAndRunPasses() { + LLVMPassManagerRef pass = LLVMCreatePassManager(); + LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), pass); + LLVMAddConstantPropagationPass(pass); + LLVMAddInstructionCombiningPass(pass); + LLVMRunPassManager(pass, Module); + LLVMDisposePassManager(pass); + } - LLVMMCJITCompilerOptions options; - LLVMInitializeMCJITCompilerOptions(&options, sizeof(options)); - options.OptLevel = 2; + LLVMModuleRef Module; + LLVMValueRef Function; + LLVMMCJITCompilerOptions Options; + LLVMExecutionEngineRef Engine; + char *Error; +}; +} // end anonymous namespace + +TEST_F(MCJITCAPITest, simple_function) { + SKIP_UNSUPPORTED_PLATFORM; - // Just ensure that this field still exists. - options.NoFramePointerElim = false; + buildSimpleFunction(); + buildMCJITOptions(); + buildMCJITEngine(); + buildAndRunPasses(); - LLVMExecutionEngineRef engine; - ASSERT_EQ( - 0, LLVMCreateMCJITCompilerForModule(&engine, module, &options, - sizeof(options), &error)); + union { + void *raw; + int (*usable)(); + } functionPointer; + functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); - LLVMPassManagerRef pass = LLVMCreatePassManager(); - LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), pass); - LLVMAddConstantPropagationPass(pass); - LLVMAddInstructionCombiningPass(pass); - LLVMRunPassManager(pass, module); - LLVMDisposePassManager(pass); + EXPECT_EQ(42, functionPointer.usable()); +} + +TEST_F(MCJITCAPITest, custom_memory_manager) { + SKIP_UNSUPPORTED_PLATFORM; + + buildSimpleFunction(); + buildMCJITOptions(); + useRoundTripSectionMemoryManager(); + buildMCJITEngine(); + buildAndRunPasses(); union { void *raw; int (*usable)(); } functionPointer; - functionPointer.raw = LLVMGetPointerToGlobal(engine, function); + functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); EXPECT_EQ(42, functionPointer.usable()); - - LLVMDisposeExecutionEngine(engine); + EXPECT_TRUE(didCallAllocateCodeSection); } - diff --git a/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp index ab09acad0d3b6..c24346de84eee 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp @@ -1,172 +1,172 @@ -//===- MCJITMemoryManagerTest.cpp - Unit tests for the JIT memory manager -===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/ExecutionEngine/SectionMemoryManager.h"
-#include "llvm/ADT/OwningPtr.h"
-#include "llvm/ExecutionEngine/JIT.h"
-#include "gtest/gtest.h"
-
-using namespace llvm;
-
-namespace {
-
-TEST(MCJITMemoryManagerTest, BasicAllocations) {
- OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
-
- uint8_t *code1 = MemMgr->allocateCodeSection(256, 0, 1);
- uint8_t *data1 = MemMgr->allocateDataSection(256, 0, 2, true);
- uint8_t *code2 = MemMgr->allocateCodeSection(256, 0, 3);
- uint8_t *data2 = MemMgr->allocateDataSection(256, 0, 4, false);
-
- EXPECT_NE((uint8_t*)0, code1);
- EXPECT_NE((uint8_t*)0, code2);
- EXPECT_NE((uint8_t*)0, data1);
- EXPECT_NE((uint8_t*)0, data2);
-
- // Initialize the data
- for (unsigned i = 0; i < 256; ++i) {
- code1[i] = 1;
- code2[i] = 2;
- data1[i] = 3;
- data2[i] = 4;
- }
-
- // Verify the data (this is checking for overlaps in the addresses)
- for (unsigned i = 0; i < 256; ++i) {
- EXPECT_EQ(1, code1[i]);
- EXPECT_EQ(2, code2[i]);
- EXPECT_EQ(3, data1[i]);
- EXPECT_EQ(4, data2[i]);
- }
-
- std::string Error;
- EXPECT_FALSE(MemMgr->applyPermissions(&Error));
-}
-
-TEST(MCJITMemoryManagerTest, LargeAllocations) {
- OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
-
- uint8_t *code1 = MemMgr->allocateCodeSection(0x100000, 0, 1);
- uint8_t *data1 = MemMgr->allocateDataSection(0x100000, 0, 2, true);
- uint8_t *code2 = MemMgr->allocateCodeSection(0x100000, 0, 3);
- uint8_t *data2 = MemMgr->allocateDataSection(0x100000, 0, 4, false);
-
- EXPECT_NE((uint8_t*)0, code1);
- EXPECT_NE((uint8_t*)0, code2);
- EXPECT_NE((uint8_t*)0, data1);
- EXPECT_NE((uint8_t*)0, data2);
-
- // Initialize the data
- for (unsigned i = 0; i < 0x100000; ++i) {
- code1[i] = 1;
- code2[i] = 2;
- data1[i] = 3;
- data2[i] = 4;
- }
-
- // Verify the data (this is checking for overlaps in the addresses)
- for (unsigned i = 0; i < 0x100000; ++i) {
- EXPECT_EQ(1, code1[i]);
- EXPECT_EQ(2, code2[i]);
- EXPECT_EQ(3, data1[i]);
- EXPECT_EQ(4, data2[i]);
- }
-
- std::string Error;
- EXPECT_FALSE(MemMgr->applyPermissions(&Error));
-}
-
-TEST(MCJITMemoryManagerTest, ManyAllocations) {
- OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
-
- uint8_t* code[10000];
- uint8_t* data[10000];
-
- for (unsigned i = 0; i < 10000; ++i) {
- const bool isReadOnly = i % 2 == 0;
-
- code[i] = MemMgr->allocateCodeSection(32, 0, 1);
- data[i] = MemMgr->allocateDataSection(32, 0, 2, isReadOnly);
-
- for (unsigned j = 0; j < 32; j++) {
- code[i][j] = 1 + (i % 254);
- data[i][j] = 2 + (i % 254);
- }
-
- EXPECT_NE((uint8_t *)0, code[i]);
- EXPECT_NE((uint8_t *)0, data[i]);
- }
-
- // Verify the data (this is checking for overlaps in the addresses)
- for (unsigned i = 0; i < 10000; ++i) {
- for (unsigned j = 0; j < 32;j++ ) {
- uint8_t ExpectedCode = 1 + (i % 254);
- uint8_t ExpectedData = 2 + (i % 254);
- EXPECT_EQ(ExpectedCode, code[i][j]);
- EXPECT_EQ(ExpectedData, data[i][j]);
- }
- }
-
- std::string Error;
- EXPECT_FALSE(MemMgr->applyPermissions(&Error));
-}
-
-TEST(MCJITMemoryManagerTest, ManyVariedAllocations) {
- OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
-
- uint8_t* code[10000];
- uint8_t* data[10000];
-
- for (unsigned i = 0; i < 10000; ++i) {
- uintptr_t CodeSize = i % 16 + 1;
- uintptr_t DataSize = i % 8 + 1;
-
- bool isReadOnly = i % 3 == 0;
- unsigned Align = 8 << (i % 4);
-
- code[i] = MemMgr->allocateCodeSection(CodeSize, Align, i);
- data[i] = MemMgr->allocateDataSection(DataSize, Align, i + 10000,
- isReadOnly);
-
- for (unsigned j = 0; j < CodeSize; j++) {
- code[i][j] = 1 + (i % 254);
- }
-
- for (unsigned j = 0; j < DataSize; j++) {
- data[i][j] = 2 + (i % 254);
- }
-
- EXPECT_NE((uint8_t *)0, code[i]);
- EXPECT_NE((uint8_t *)0, data[i]);
-
- uintptr_t CodeAlign = Align ? (uintptr_t)code[i] % Align : 0;
- uintptr_t DataAlign = Align ? (uintptr_t)data[i] % Align : 0;
-
- EXPECT_EQ((uintptr_t)0, CodeAlign);
- EXPECT_EQ((uintptr_t)0, DataAlign);
- }
-
- for (unsigned i = 0; i < 10000; ++i) {
- uintptr_t CodeSize = i % 16 + 1;
- uintptr_t DataSize = i % 8 + 1;
-
- for (unsigned j = 0; j < CodeSize; j++) {
- uint8_t ExpectedCode = 1 + (i % 254);
- EXPECT_EQ(ExpectedCode, code[i][j]);
- }
-
- for (unsigned j = 0; j < DataSize; j++) {
- uint8_t ExpectedData = 2 + (i % 254);
- EXPECT_EQ(ExpectedData, data[i][j]);
- }
- }
-}
-
-} // Namespace
-
+//===- MCJITMemoryManagerTest.cpp - Unit tests for the JIT memory manager -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(MCJITMemoryManagerTest, BasicAllocations) { + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager()); + + uint8_t *code1 = MemMgr->allocateCodeSection(256, 0, 1, ""); + uint8_t *data1 = MemMgr->allocateDataSection(256, 0, 2, "", true); + uint8_t *code2 = MemMgr->allocateCodeSection(256, 0, 3, ""); + uint8_t *data2 = MemMgr->allocateDataSection(256, 0, 4, "", false); + + EXPECT_NE((uint8_t*)0, code1); + EXPECT_NE((uint8_t*)0, code2); + EXPECT_NE((uint8_t*)0, data1); + EXPECT_NE((uint8_t*)0, data2); + + // Initialize the data + for (unsigned i = 0; i < 256; ++i) { + code1[i] = 1; + code2[i] = 2; + data1[i] = 3; + data2[i] = 4; + } + + // Verify the data (this is checking for overlaps in the addresses) + for (unsigned i = 0; i < 256; ++i) { + EXPECT_EQ(1, code1[i]); + EXPECT_EQ(2, code2[i]); + EXPECT_EQ(3, data1[i]); + EXPECT_EQ(4, data2[i]); + } + + std::string Error; + EXPECT_FALSE(MemMgr->finalizeMemory(&Error)); +} + +TEST(MCJITMemoryManagerTest, LargeAllocations) { + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager()); + + uint8_t *code1 = MemMgr->allocateCodeSection(0x100000, 0, 1, ""); + uint8_t *data1 = MemMgr->allocateDataSection(0x100000, 0, 2, "", true); + uint8_t *code2 = MemMgr->allocateCodeSection(0x100000, 0, 3, ""); + uint8_t *data2 = MemMgr->allocateDataSection(0x100000, 0, 4, "", false); + + EXPECT_NE((uint8_t*)0, code1); + EXPECT_NE((uint8_t*)0, code2); + EXPECT_NE((uint8_t*)0, data1); + EXPECT_NE((uint8_t*)0, data2); + + // Initialize the data + for (unsigned i = 0; i < 0x100000; ++i) { + code1[i] = 1; + code2[i] = 2; + data1[i] = 3; + data2[i] = 4; + } + + // Verify the data (this is checking for overlaps in the addresses) + for (unsigned i = 0; i < 0x100000; ++i) { + EXPECT_EQ(1, code1[i]); + EXPECT_EQ(2, code2[i]); + EXPECT_EQ(3, data1[i]); + EXPECT_EQ(4, data2[i]); + } + + std::string Error; + EXPECT_FALSE(MemMgr->finalizeMemory(&Error)); +} + +TEST(MCJITMemoryManagerTest, ManyAllocations) { + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager()); + + uint8_t* code[10000]; + uint8_t* data[10000]; + + for (unsigned i = 0; i < 10000; ++i) { + const bool isReadOnly = i % 2 == 0; + + code[i] = MemMgr->allocateCodeSection(32, 0, 1, ""); + data[i] = MemMgr->allocateDataSection(32, 0, 2, "", isReadOnly); + + for (unsigned j = 0; j < 32; j++) { + code[i][j] = 1 + (i % 254); + data[i][j] = 2 + (i % 254); + } + + EXPECT_NE((uint8_t *)0, code[i]); + EXPECT_NE((uint8_t *)0, data[i]); + } + + // Verify the data (this is checking for overlaps in the addresses) + for (unsigned i = 0; i < 10000; ++i) { + for (unsigned j = 0; j < 32;j++ ) { + uint8_t ExpectedCode = 1 + (i % 254); + uint8_t ExpectedData = 2 + (i % 254); + EXPECT_EQ(ExpectedCode, code[i][j]); + EXPECT_EQ(ExpectedData, data[i][j]); + } + } + + std::string Error; + EXPECT_FALSE(MemMgr->finalizeMemory(&Error)); +} + +TEST(MCJITMemoryManagerTest, ManyVariedAllocations) { + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager()); + + uint8_t* code[10000]; + uint8_t* data[10000]; + + for (unsigned i = 0; i < 10000; ++i) { + uintptr_t CodeSize = i % 16 + 1; + uintptr_t DataSize = i % 8 + 1; + + bool isReadOnly = i % 3 == 0; + unsigned Align = 8 << (i % 4); + + code[i] = MemMgr->allocateCodeSection(CodeSize, Align, i, ""); + data[i] = MemMgr->allocateDataSection(DataSize, Align, i + 10000, "", + isReadOnly); + + for (unsigned j = 0; j < CodeSize; j++) { + code[i][j] = 1 + (i % 254); + } + + for (unsigned j = 0; j < DataSize; j++) { + data[i][j] = 2 + (i % 254); + } + + EXPECT_NE((uint8_t *)0, code[i]); + EXPECT_NE((uint8_t *)0, data[i]); + + uintptr_t CodeAlign = Align ? (uintptr_t)code[i] % Align : 0; + uintptr_t DataAlign = Align ? (uintptr_t)data[i] % Align : 0; + + EXPECT_EQ((uintptr_t)0, CodeAlign); + EXPECT_EQ((uintptr_t)0, DataAlign); + } + + for (unsigned i = 0; i < 10000; ++i) { + uintptr_t CodeSize = i % 16 + 1; + uintptr_t DataSize = i % 8 + 1; + + for (unsigned j = 0; j < CodeSize; j++) { + uint8_t ExpectedCode = 1 + (i % 254); + EXPECT_EQ(ExpectedCode, code[i][j]); + } + + for (unsigned j = 0; j < DataSize; j++) { + uint8_t ExpectedData = 2 + (i % 254); + EXPECT_EQ(ExpectedData, data[i][j]); + } + } +} + +} // Namespace + diff --git a/unittests/ExecutionEngine/MCJIT/MCJITMultipleModuleTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITMultipleModuleTest.cpp new file mode 100644 index 0000000000000..7c3239ea2598d --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITMultipleModuleTest.cpp @@ -0,0 +1,395 @@ +//===- MCJITMultipeModuleTest.cpp - Unit tests for the MCJIT---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This test suite verifies MCJIT for handling multiple modules in a single +// ExecutionEngine by building multiple modules, making function calls across +// modules, accessing global variables, etc. +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/MCJIT.h" +#include "MCJITTestBase.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class MCJITMultipleModuleTest : public testing::Test, public MCJITTestBase {}; + +// FIXME: ExecutionEngine has no support empty modules +/* +TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) { + SKIP_UNSUPPORTED_PLATFORM; + + createJIT(M.take()); + // JIT-compile + EXPECT_NE(0, TheJIT->getObjectImage()) + << "Unable to generate executable loaded object image"; + + TheJIT->addModule(createEmptyModule("<other module>")); + TheJIT->addModule(createEmptyModule("<other other module>")); + + // JIT again + EXPECT_NE(0, TheJIT->getObjectImage()) + << "Unable to generate executable loaded object image"; +} +*/ + +// Helper Function to test add operation +void checkAdd(uint64_t ptr) { + ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function."; + int (*AddPtr)(int, int) = (int (*)(int, int))ptr; + EXPECT_EQ(0, AddPtr(0, 0)); + EXPECT_EQ(1, AddPtr(1, 0)); + EXPECT_EQ(3, AddPtr(1, 2)); + EXPECT_EQ(-5, AddPtr(-2, -3)); + EXPECT_EQ(30, AddPtr(10, 20)); + EXPECT_EQ(-30, AddPtr(-10, -20)); + EXPECT_EQ(-40, AddPtr(-10, -30)); +} + +void checkAccumulate(uint64_t ptr) { + ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function."; + int32_t (*FPtr)(int32_t) = (int32_t (*)(int32_t))(intptr_t)ptr; + EXPECT_EQ(0, FPtr(0)); + EXPECT_EQ(1, FPtr(1)); + EXPECT_EQ(3, FPtr(2)); + EXPECT_EQ(6, FPtr(3)); + EXPECT_EQ(10, FPtr(4)); + EXPECT_EQ(15, FPtr(5)); +} + +// FIXME: ExecutionEngine has no support empty modules +/* +TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) { + SKIP_UNSUPPORTED_PLATFORM; + + createJIT(M.take()); + // JIT-compile + EXPECT_NE(0, TheJIT->getObjectImage()) + << "Unable to generate executable loaded object image"; + + TheJIT->addModule(createEmptyModule("<other module>")); + TheJIT->addModule(createEmptyModule("<other other module>")); + + // JIT again + EXPECT_NE(0, TheJIT->getObjectImage()) + << "Unable to generate executable loaded object image"; +} +*/ + +// Module A { Function FA }, +// Module B { Function FB }, +// execute FA then FB +TEST_F(MCJITMultipleModuleTest, two_module_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB; + createTwoModuleCase(A, FA, B, FB); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FB->getName().str()); + checkAdd(ptr); +} + +// Module A { Function FA }, +// Module B { Function FB }, +// execute FB then FA +TEST_F(MCJITMultipleModuleTest, two_module_reverse_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB; + createTwoModuleCase(A, FA, B, FB); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str()); + TheJIT->finalizeObject(); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); +} + +// Module A { Function FA }, +// Module B { Extern FA, Function FB which calls FA }, +// execute FB then FA +TEST_F(MCJITMultipleModuleTest, two_module_extern_reverse_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB; + createTwoModuleExternCase(A, FA, B, FB); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str()); + TheJIT->finalizeObject(); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); +} + +// Module A { Function FA }, +// Module B { Extern FA, Function FB which calls FA }, +// execute FA then FB +TEST_F(MCJITMultipleModuleTest, two_module_extern_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB; + createTwoModuleExternCase(A, FA, B, FB); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FB->getName().str()); + checkAdd(ptr); +} + +// Module A { Function FA1, Function FA2 which calls FA1 }, +// Module B { Extern FA1, Function FB which calls FA1 }, +// execute FB then FA2 +TEST_F(MCJITMultipleModuleTest, two_module_consecutive_call_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA1, *FA2, *FB; + createTwoModuleExternCase(A, FA1, B, FB); + FA2 = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(A.get(), FA1); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str()); + TheJIT->finalizeObject(); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FA2->getName().str()); + checkAdd(ptr); +} + +// TODO: +// Module A { Extern Global GVB, Global Variable GVA, Function FA loads GVB }, +// Module B { Extern Global GVA, Global Variable GVB, Function FB loads GVA }, + + +// Module A { Global Variable GVA, Function FA loads GVA }, +// Module B { Global Variable GVB, Function FB loads GVB }, +// execute FB then FA +TEST_F(MCJITMultipleModuleTest, two_module_global_variables_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB; + GlobalVariable *GVA, *GVB; + A.reset(createEmptyModule("A")); + B.reset(createEmptyModule("B")); + + int32_t initialNum = 7; + GVA = insertGlobalInt32(A.get(), "GVA", initialNum); + GVB = insertGlobalInt32(B.get(), "GVB", initialNum); + FA = startFunction<int32_t(void)>(A.get(), "FA"); + endFunctionWithRet(FA, Builder.CreateLoad(GVA)); + FB = startFunction<int32_t(void)>(B.get(), "FB"); + endFunctionWithRet(FB, Builder.CreateLoad(GVB)); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t FBPtr = TheJIT->getFunctionAddress(FB->getName().str()); + TheJIT->finalizeObject(); + EXPECT_TRUE(0 != FBPtr); + int32_t(*FuncPtr)(void) = (int32_t(*)(void))FBPtr; + EXPECT_EQ(initialNum, FuncPtr()) + << "Invalid value for global returned from JITted function in module B"; + + uint64_t FAPtr = TheJIT->getFunctionAddress(FA->getName().str()); + EXPECT_TRUE(0 != FAPtr); + FuncPtr = (int32_t(*)(void))FAPtr; + EXPECT_EQ(initialNum, FuncPtr()) + << "Invalid value for global returned from JITted function in module A"; +} + +// Module A { Function FA }, +// Module B { Extern FA, Function FB which calls FA }, +// Module C { Extern FA, Function FC which calls FA }, +// execute FC, FB, FA +TEST_F(MCJITMultipleModuleTest, three_module_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B, C; + Function *FA, *FB, *FC; + createThreeModuleCase(A, FA, B, FB, C, FC); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + TheJIT->addModule(C.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FB->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); +} + +// Module A { Function FA }, +// Module B { Extern FA, Function FB which calls FA }, +// Module C { Extern FA, Function FC which calls FA }, +// execute FA, FB, FC +TEST_F(MCJITMultipleModuleTest, three_module_case_reverse_order) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B, C; + Function *FA, *FB, *FC; + createThreeModuleCase(A, FA, B, FB, C, FC); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + TheJIT->addModule(C.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FB->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FC->getName().str()); + checkAdd(ptr); +} + +// Module A { Function FA }, +// Module B { Extern FA, Function FB which calls FA }, +// Module C { Extern FB, Function FC which calls FB }, +// execute FC, FB, FA +TEST_F(MCJITMultipleModuleTest, three_module_chain_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B, C; + Function *FA, *FB, *FC; + createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + TheJIT->addModule(C.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FB->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); +} + +// Module A { Function FA }, +// Module B { Extern FA, Function FB which calls FA }, +// Module C { Extern FB, Function FC which calls FB }, +// execute FA, FB, FC +TEST_F(MCJITMultipleModuleTest, three_modules_chain_case_reverse_order) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B, C; + Function *FA, *FB, *FC; + createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + TheJIT->addModule(C.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FB->getName().str()); + checkAdd(ptr); + + ptr = TheJIT->getFunctionAddress(FC->getName().str()); + checkAdd(ptr); +} + +// Module A { Extern FB, Function FA which calls FB1 }, +// Module B { Extern FA, Function FB1, Function FB2 which calls FA }, +// execute FA, then FB1 +// FIXME: this test case is not supported by MCJIT +TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB1, *FB2; + createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAccumulate(ptr); + + ptr = TheJIT->getFunctionAddress(FB1->getName().str()); + checkAccumulate(ptr); +} + +// Module A { Extern FB, Function FA which calls FB1 }, +// Module B { Extern FA, Function FB1, Function FB2 which calls FA }, +// execute FB1 then FA +// FIXME: this test case is not supported by MCJIT +TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case_reverse_order) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB1, *FB2; + createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str()); + checkAccumulate(ptr); + + ptr = TheJIT->getFunctionAddress(FA->getName().str()); + checkAccumulate(ptr); +} + +// Module A { Extern FB1, Function FA which calls FB1 }, +// Module B { Extern FA, Function FB1, Function FB2 which calls FA }, +// execute FB1 then FB2 +// FIXME: this test case is not supported by MCJIT +TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case3) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<Module> A, B; + Function *FA, *FB1, *FB2; + createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); + + createJIT(A.take()); + TheJIT->addModule(B.take()); + + uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str()); + checkAccumulate(ptr); + + ptr = TheJIT->getFunctionAddress(FB2->getName().str()); + checkAccumulate(ptr); +} +} diff --git a/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp index 0061e30e7a541..7073a5265d62c 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp @@ -28,7 +28,7 @@ public: virtual ~TestObjectCache() { // Free any buffers we've allocated. - SmallVector<MemoryBuffer *, 2>::iterator it, end; + SmallVectorImpl<MemoryBuffer *>::iterator it, end; end = AllocatedBuffers.end(); for (it = AllocatedBuffers.begin(); it != end; ++it) { delete *it; @@ -45,6 +45,16 @@ public: ObjMap[ModuleID] = copyBuffer(Obj); } + virtual MemoryBuffer* getObject(const Module* M) { + const MemoryBuffer* BufferFound = getObjectInternal(M); + ModulesLookedUp.insert(M->getModuleIdentifier()); + if (!BufferFound) + return NULL; + // Our test cache wants to maintain ownership of its object buffers + // so we make a copy here for the execution engine. + return MemoryBuffer::getMemBufferCopy(BufferFound->getBuffer()); + } + // Test-harness-specific functions bool wereDuplicatesInserted() { return DuplicateInserted; } @@ -62,13 +72,6 @@ public: return it->second; } -protected: - virtual const MemoryBuffer* getObject(const Module* M) { - const MemoryBuffer* BufferFound = getObjectInternal(M); - ModulesLookedUp.insert(M->getModuleIdentifier()); - return BufferFound; - } - private: MemoryBuffer *copyBuffer(const MemoryBuffer *Buf) { // Create a local copy of the buffer. @@ -98,14 +101,13 @@ protected: void compileAndRun(int ExpectedRC = OriginalRC) { // This function shouldn't be called until after SetUp. - ASSERT_TRUE(0 != TheJIT); + ASSERT_TRUE(TheJIT.isValid()); ASSERT_TRUE(0 != Main); + // We may be using a null cache, so ensure compilation is valid. TheJIT->finalizeObject(); void *vPtr = TheJIT->getPointerToFunction(Main); - static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache(); - EXPECT_TRUE(0 != vPtr) << "Unable to get pointer to main() from JIT"; diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp index e9cf904b1813f..fab81551fa5eb 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp @@ -18,19 +18,24 @@ using namespace llvm; +namespace { + class MCJITTest : public testing::Test, public MCJITTestBase { protected: - - virtual void SetUp() { - M.reset(createEmptyModule("<main>")); - } + virtual void SetUp() { M.reset(createEmptyModule("<main>")); } }; -namespace { +// FIXME: Ensure creating an execution engine does not crash when constructed +// with a null module. +/* +TEST_F(MCJITTest, null_module) { + createJIT(0); +} +*/ // FIXME: In order to JIT an empty module, there needs to be // an interface to ExecutionEngine that forces compilation but -// does require retrieval of a pointer to a function/global. +// does not require retrieval of a pointer to a function/global. /* TEST_F(MCJITTest, empty_module) { createJIT(M.take()); @@ -46,8 +51,6 @@ TEST_F(MCJITTest, global_variable) { GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue); createJIT(M.take()); void *globalPtr = TheJIT->getPointerToGlobal(Global); - MM->applyPermissions(); - static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache(); EXPECT_TRUE(0 != globalPtr) << "Unable to get pointer to global value from JIT"; @@ -60,16 +63,19 @@ TEST_F(MCJITTest, add_function) { Function *F = insertAddFunction(M.get()); createJIT(M.take()); - void *addPtr = TheJIT->getPointerToFunction(F); - MM->applyPermissions(); - static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache(); + uint64_t addPtr = TheJIT->getFunctionAddress(F->getName().str()); EXPECT_TRUE(0 != addPtr) << "Unable to get pointer to function from JIT"; - int (*AddPtrTy)(int, int) = (int(*)(int, int))(intptr_t)addPtr; - EXPECT_EQ(0, AddPtrTy(0, 0)); - EXPECT_EQ(3, AddPtrTy(1, 2)); - EXPECT_EQ(-5, AddPtrTy(-2, -3)); + ASSERT_TRUE(addPtr != 0) << "Unable to get pointer to function ."; + int (*AddPtr)(int, int) = (int(*)(int, int))addPtr ; + EXPECT_EQ(0, AddPtr(0, 0)); + EXPECT_EQ(1, AddPtr(1, 0)); + EXPECT_EQ(3, AddPtr(1, 2)); + EXPECT_EQ(-5, AddPtr(-2, -3)); + EXPECT_EQ(30, AddPtr(10, 20)); + EXPECT_EQ(-30, AddPtr(-10, -20)); + EXPECT_EQ(-40, AddPtr(-10, -30)); } TEST_F(MCJITTest, run_main) { @@ -78,13 +84,11 @@ TEST_F(MCJITTest, run_main) { int rc = 6; Function *Main = insertMainFunction(M.get(), 6); createJIT(M.take()); - void *vPtr = TheJIT->getPointerToFunction(Main); - MM->applyPermissions(); - static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache(); - EXPECT_TRUE(0 != vPtr) + uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str()); + EXPECT_TRUE(0 != ptr) << "Unable to get pointer to main() from JIT"; - int (*FuncPtr)(void) = (int(*)(void))(intptr_t)vPtr; + int (*FuncPtr)(void) = (int(*)(void))ptr; int returnCode = FuncPtr(); EXPECT_EQ(returnCode, rc); } @@ -101,12 +105,10 @@ TEST_F(MCJITTest, return_global) { endFunctionWithRet(ReturnGlobal, ReadGlobal); createJIT(M.take()); - void *rgvPtr = TheJIT->getPointerToFunction(ReturnGlobal); - MM->applyPermissions(); - static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache(); + uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str()); EXPECT_TRUE(0 != rgvPtr); - int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)rgvPtr; + int32_t(*FuncPtr)(void) = (int32_t(*)(void))rgvPtr; EXPECT_EQ(initialNum, FuncPtr()) << "Invalid value for global returned from JITted function"; } @@ -136,7 +138,7 @@ TEST_F(MCJITTest, increment_global) { void *gvPtr = TheJIT->getPointerToGlobal(GV); EXPECT_EQ(initialNum, *(int32_t*)gvPtr); - void *vPtr = TheJIT->getPointerToFunction(IncrementGlobal); + void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str()); EXPECT_TRUE(0 != vPtr) << "Unable to get pointer to main() from JIT"; @@ -150,6 +152,9 @@ TEST_F(MCJITTest, increment_global) { } */ +// PR16013: XFAIL this test on ARM, which currently can't handle multiple relocations. +#if !defined(__arm__) + TEST_F(MCJITTest, multiple_functions) { SKIP_UNSUPPORTED_PLATFORM; @@ -171,65 +176,15 @@ TEST_F(MCJITTest, multiple_functions) { } createJIT(M.take()); - void *vPtr = TheJIT->getPointerToFunction(Outer); - MM->applyPermissions(); - static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache(); - EXPECT_TRUE(0 != vPtr) + uint64_t ptr = TheJIT->getFunctionAddress(Outer->getName().str()); + EXPECT_TRUE(0 != ptr) << "Unable to get pointer to outer function from JIT"; - int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr; + int32_t(*FuncPtr)(void) = (int32_t(*)(void))ptr; EXPECT_EQ(innerRetVal, FuncPtr()) << "Incorrect result returned from function"; } -// FIXME: ExecutionEngine has no support empty modules -/* -TEST_F(MCJITTest, multiple_empty_modules) { - SKIP_UNSUPPORTED_PLATFORM; - - createJIT(M.take()); - // JIT-compile - EXPECT_NE(0, TheJIT->getObjectImage()) - << "Unable to generate executable loaded object image"; - - TheJIT->addModule(createEmptyModule("<other module>")); - TheJIT->addModule(createEmptyModule("<other other module>")); - - // JIT again - EXPECT_NE(0, TheJIT->getObjectImage()) - << "Unable to generate executable loaded object image"; -} -*/ - -// FIXME: MCJIT must support multiple modules -/* -TEST_F(MCJITTest, multiple_modules) { - SKIP_UNSUPPORTED_PLATFORM; - - Function *Callee = insertAddFunction(M.get()); - createJIT(M.take()); - - // caller function is defined in a different module - M.reset(createEmptyModule("<caller module>")); - - Function *CalleeRef = insertExternalReferenceToFunction(M.get(), Callee); - Function *Caller = insertSimpleCallFunction(M.get(), CalleeRef); - - TheJIT->addModule(M.take()); - - // get a function pointer in a module that was not used in EE construction - void *vPtr = TheJIT->getPointerToFunction(Caller); - EXPECT_NE(0, vPtr) - << "Unable to get pointer to caller function from JIT"; - - int(*FuncPtr)(int, int) = (int(*)(int, int))(intptr_t)vPtr; - EXPECT_EQ(0, FuncPtr(0, 0)); - EXPECT_EQ(30, FuncPtr(10, 20)); - EXPECT_EQ(-30, FuncPtr(-10, -20)); - - // ensure caller is destroyed before callee (free use before def) - M.reset(); -} -*/ +#endif /*!defined(__arm__)*/ } diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h index 8160a186f413b..7b6e39fb23854 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h +++ b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h @@ -49,11 +49,23 @@ protected: /// Returns true if the host architecture is known to support MCJIT bool ArchSupportsMCJIT() { Triple Host(HostTriple); + // If ARCH is not supported, bail if (std::find(SupportedArchs.begin(), SupportedArchs.end(), Host.getArch()) - == SupportedArchs.end()) { + == SupportedArchs.end()) return false; - } - return true; + + // If ARCH is supported and has no specific sub-arch support + if (std::find(HasSubArchs.begin(), HasSubArchs.end(), Host.getArch()) + == HasSubArchs.end()) + 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())) + return true; + + return false; } /// Returns true if the host OS is known to support MCJIT @@ -68,6 +80,8 @@ protected: std::string HostTriple; SmallVector<Triple::ArchType, 4> SupportedArchs; + SmallVector<Triple::ArchType, 1> HasSubArchs; + SmallVector<std::string, 2> SupportedSubArchs; // We need to own the memory SmallVector<Triple::OSType, 4> UnsupportedOSs; }; diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h index b0e98a88defc1..b42a9c0980db1 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h +++ b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h @@ -30,35 +30,19 @@ namespace llvm { -class MCJITTestBase : public MCJITTestAPICommon { +/// Helper class that can build very simple Modules +class TrivialModuleBuilder { protected: + LLVMContext Context; + IRBuilder<> Builder; + std::string BuilderTriple; - MCJITTestBase() - : OptLevel(CodeGenOpt::None) - , RelocModel(Reloc::Default) - , CodeModel(CodeModel::Default) - , MArch("") - , Builder(Context) - , MM(new SectionMemoryManager) - { - // The architectures below are known to be compatible with MCJIT as they - // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be - // kept in sync. - SupportedArchs.push_back(Triple::arm); - SupportedArchs.push_back(Triple::mips); - SupportedArchs.push_back(Triple::x86); - SupportedArchs.push_back(Triple::x86_64); - - // The operating systems below are known to be incompatible with MCJIT as - // they are copied from the test/ExecutionEngine/MCJIT/lit.local.cfg and - // should be kept in sync. - UnsupportedOSs.push_back(Triple::Cygwin); - UnsupportedOSs.push_back(Triple::Darwin); - } + TrivialModuleBuilder(const std::string &Triple) + : Builder(Context), BuilderTriple(Triple) {} - Module *createEmptyModule(StringRef Name) { + Module *createEmptyModule(StringRef Name = StringRef()) { Module * M = new Module(Name, Context); - M->setTargetTriple(Triple::normalize(HostTriple)); + M->setTargetTriple(Triple::normalize(BuilderTriple)); return M; } @@ -135,12 +119,13 @@ protected: // Inserts an declaration to a function defined elsewhere Function *insertExternalReferenceToFunction(Module *M, Function *Func) { Function *Result = Function::Create(Func->getFunctionType(), - GlobalValue::AvailableExternallyLinkage, + GlobalValue::ExternalLinkage, Func->getName(), M); return Result; } // Inserts a global variable of type int32 + // FIXME: make this a template function to support any type GlobalVariable *insertGlobalInt32(Module *M, StringRef name, int32_t InitialValue) { @@ -155,6 +140,179 @@ protected: return Global; } + // Inserts a function + // int32_t recursive_add(int32_t num) { + // if (num == 0) { + // return num; + // } else { + // int32_t recursive_param = num - 1; + // return num + Helper(recursive_param); + // } + // } + // NOTE: if Helper is left as the default parameter, Helper == recursive_add. + Function *insertAccumulateFunction(Module *M, + Function *Helper = 0, + StringRef Name = "accumulate") { + Function *Result = startFunction<int32_t(int32_t)>(M, Name); + if (Helper == 0) + Helper = Result; + + BasicBlock *BaseCase = BasicBlock::Create(Context, "", Result); + BasicBlock *RecursiveCase = BasicBlock::Create(Context, "", Result); + + // if (num == 0) + Value *Param = Result->arg_begin(); + Value *Zero = ConstantInt::get(Context, APInt(32, 0)); + Builder.CreateCondBr(Builder.CreateICmpEQ(Param, Zero), + BaseCase, RecursiveCase); + + // return num; + Builder.SetInsertPoint(BaseCase); + Builder.CreateRet(Param); + + // int32_t recursive_param = num - 1; + // return Helper(recursive_param); + Builder.SetInsertPoint(RecursiveCase); + Value *One = ConstantInt::get(Context, APInt(32, 1)); + Value *RecursiveParam = Builder.CreateSub(Param, One); + Value *RecursiveReturn = Builder.CreateCall(Helper, RecursiveParam); + Value *Accumulator = Builder.CreateAdd(Param, RecursiveReturn); + Builder.CreateRet(Accumulator); + + return Result; + } + + // Populates Modules A and B: + // Module A { Extern FB1, Function FA which calls FB1 }, + // Module B { Extern FA, Function FB1, Function FB2 which calls FA }, + void createCrossModuleRecursiveCase(OwningPtr<Module> &A, + Function *&FA, + OwningPtr<Module> &B, + Function *&FB1, + Function *&FB2) { + // Define FB1 in B. + B.reset(createEmptyModule("B")); + FB1 = insertAccumulateFunction(B.get(), 0, "FB1"); + + // Declare FB1 in A (as an external). + A.reset(createEmptyModule("A")); + Function *FB1Extern = insertExternalReferenceToFunction(A.get(), FB1); + + // Define FA in A (with a call to FB1). + FA = insertAccumulateFunction(A.get(), FB1Extern, "FA"); + + // Declare FA in B (as an external) + Function *FAExtern = insertExternalReferenceToFunction(B.get(), FA); + + // Define FB2 in B (with a call to FA) + FB2 = insertAccumulateFunction(B.get(), FAExtern, "FB2"); + } + + // Module A { Function FA }, + // Module B { Extern FA, Function FB which calls FA }, + // Module C { Extern FB, Function FC which calls FB }, + void createThreeModuleChainedCallsCase(OwningPtr<Module> &A, + Function *&FA, + OwningPtr<Module> &B, + Function *&FB, + OwningPtr<Module> &C, + Function *&FC) { + A.reset(createEmptyModule("A")); + FA = insertAddFunction(A.get()); + + B.reset(createEmptyModule("B")); + Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA); + FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B); + + C.reset(createEmptyModule("C")); + Function *FBExtern_in_C = insertExternalReferenceToFunction(C.get(), FB); + FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FBExtern_in_C); + } + + + // Module A { Function FA }, + // Populates Modules A and B: + // Module B { Function FB } + void createTwoModuleCase(OwningPtr<Module> &A, Function *&FA, + OwningPtr<Module> &B, Function *&FB) { + A.reset(createEmptyModule("A")); + FA = insertAddFunction(A.get()); + + B.reset(createEmptyModule("B")); + FB = insertAddFunction(B.get()); + } + + // Module A { Function FA }, + // Module B { Extern FA, Function FB which calls FA } + void createTwoModuleExternCase(OwningPtr<Module> &A, Function *&FA, + OwningPtr<Module> &B, Function *&FB) { + A.reset(createEmptyModule("A")); + FA = insertAddFunction(A.get()); + + B.reset(createEmptyModule("B")); + Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA); + FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), + FAExtern_in_B); + } + + // Module A { Function FA }, + // Module B { Extern FA, Function FB which calls FA }, + // Module C { Extern FB, Function FC which calls FA }, + void createThreeModuleCase(OwningPtr<Module> &A, + Function *&FA, + OwningPtr<Module> &B, + Function *&FB, + OwningPtr<Module> &C, + Function *&FC) { + A.reset(createEmptyModule("A")); + FA = insertAddFunction(A.get()); + + B.reset(createEmptyModule("B")); + Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA); + FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B); + + C.reset(createEmptyModule("C")); + Function *FAExtern_in_C = insertExternalReferenceToFunction(C.get(), FA); + FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FAExtern_in_C); + } +}; + + +class MCJITTestBase : public MCJITTestAPICommon, public TrivialModuleBuilder { +protected: + + MCJITTestBase() + : TrivialModuleBuilder(HostTriple) + , OptLevel(CodeGenOpt::None) + , RelocModel(Reloc::Default) + , CodeModel(CodeModel::Default) + , MArch("") + , MM(new SectionMemoryManager) + { + // The architectures below are known to be compatible with MCJIT as they + // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be + // kept in sync. + SupportedArchs.push_back(Triple::aarch64); + SupportedArchs.push_back(Triple::arm); + SupportedArchs.push_back(Triple::mips); + SupportedArchs.push_back(Triple::mipsel); + SupportedArchs.push_back(Triple::x86); + SupportedArchs.push_back(Triple::x86_64); + + // Some architectures have sub-architectures in which tests will fail, like + // ARM. These two vectors will define if they do have sub-archs (to avoid + // extra work for those who don't), and if so, if they are listed to work + HasSubArchs.push_back(Triple::arm); + SupportedSubArchs.push_back("armv6"); + SupportedSubArchs.push_back("armv7"); + + // The operating systems below are known to be incompatible with MCJIT as + // they are copied from the test/ExecutionEngine/MCJIT/lit.local.cfg and + // should be kept in sync. + UnsupportedOSs.push_back(Triple::Cygwin); + UnsupportedOSs.push_back(Triple::Darwin); + } + void createJIT(Module *M) { // Due to the EngineBuilder constructor, it is required to have a Module @@ -165,7 +323,7 @@ protected: std::string Error; TheJIT.reset(EB.setEngineKind(EngineKind::JIT) .setUseMCJIT(true) /* can this be folded into the EngineKind enum? */ - .setJITMemoryManager(MM) + .setMCJITMemoryManager(MM) .setErrorStr(&Error) .setOptLevel(CodeGenOpt::None) .setAllocateGVsWithCode(false) /*does this do anything?*/ @@ -179,16 +337,13 @@ protected: assert(TheJIT.get() != NULL && "error creating MCJIT with EngineBuilder"); } - LLVMContext Context; CodeGenOpt::Level OptLevel; Reloc::Model RelocModel; CodeModel::Model CodeModel; StringRef MArch; SmallVector<std::string, 1> MAttrs; - OwningPtr<TargetMachine> TM; OwningPtr<ExecutionEngine> TheJIT; - IRBuilder<> Builder; - JITMemoryManager *MM; + RTDyldMemoryManager *MM; OwningPtr<Module> M; }; diff --git a/unittests/ExecutionEngine/MCJIT/Makefile b/unittests/ExecutionEngine/MCJIT/Makefile index 454f83099d4b4..33b043be9ebd0 100644 --- a/unittests/ExecutionEngine/MCJIT/Makefile +++ b/unittests/ExecutionEngine/MCJIT/Makefile @@ -9,7 +9,7 @@ LEVEL = ../../.. TESTNAME = MCJIT -LINK_COMPONENTS := core jit mcjit native support +LINK_COMPONENTS := core mcjit native support include $(LEVEL)/Makefile.config include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/IR/AttributesTest.cpp b/unittests/IR/AttributesTest.cpp index 2368bdf94dc43..ebcb772bc3739 100644 --- a/unittests/IR/AttributesTest.cpp +++ b/unittests/IR/AttributesTest.cpp @@ -31,4 +31,17 @@ TEST(Attributes, Uniquing) { EXPECT_EQ(SetA, SetB); } +TEST(Attributes, Ordering) { + LLVMContext C; + + AttributeSet ASs[] = { + AttributeSet::get(C, 2, Attribute::ZExt), + AttributeSet::get(C, 1, Attribute::SExt) + }; + + AttributeSet SetA = AttributeSet::get(C, ASs); + AttributeSet SetB = SetA.removeAttributes(C, 1, ASs[1]); + EXPECT_NE(SetA, SetB); +} + } // end anonymous namespace diff --git a/unittests/IR/CMakeLists.txt b/unittests/IR/CMakeLists.txt index c53043ef80e11..fd0831f8e1fa1 100644 --- a/unittests/IR/CMakeLists.txt +++ b/unittests/IR/CMakeLists.txt @@ -10,6 +10,7 @@ set(IRSources DominatorTreeTest.cpp IRBuilderTest.cpp InstructionsTest.cpp + LegacyPassManagerTest.cpp MDBuilderTest.cpp MetadataTest.cpp PassManagerTest.cpp diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp index fecc4a4fe6b41..2f390f7f75a2f 100644 --- a/unittests/IR/IRBuilderTest.cpp +++ b/unittests/IR/IRBuilderTest.cpp @@ -25,12 +25,12 @@ namespace { class IRBuilderTest : public testing::Test { protected: virtual void SetUp() { - M.reset(new Module("MyModule", getGlobalContext())); - FunctionType *FTy = FunctionType::get(Type::getVoidTy(getGlobalContext()), + M.reset(new Module("MyModule", Ctx)); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), /*isVarArg=*/false); F = Function::Create(FTy, Function::ExternalLinkage, "", M.get()); - BB = BasicBlock::Create(getGlobalContext(), "", F); - GV = new GlobalVariable(*M, Type::getFloatTy(getGlobalContext()), true, + BB = BasicBlock::Create(Ctx, "", F); + GV = new GlobalVariable(*M, Type::getFloatTy(Ctx), true, GlobalValue::ExternalLinkage, 0); } @@ -39,6 +39,7 @@ protected: M.reset(); } + LLVMContext Ctx; OwningPtr<Module> M; Function *F; BasicBlock *BB; @@ -78,8 +79,8 @@ TEST_F(IRBuilderTest, Lifetime) { TEST_F(IRBuilderTest, CreateCondBr) { IRBuilder<> Builder(BB); - BasicBlock *TBB = BasicBlock::Create(getGlobalContext(), "", F); - BasicBlock *FBB = BasicBlock::Create(getGlobalContext(), "", F); + BasicBlock *TBB = BasicBlock::Create(Ctx, "", F); + BasicBlock *FBB = BasicBlock::Create(Ctx, "", F); BranchInst *BI = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB); TerminatorInst *TI = BB->getTerminator(); @@ -89,7 +90,7 @@ TEST_F(IRBuilderTest, CreateCondBr) { EXPECT_EQ(FBB, TI->getSuccessor(1)); BI->eraseFromParent(); - MDNode *Weights = MDBuilder(getGlobalContext()).createBranchWeights(42, 13); + MDNode *Weights = MDBuilder(Ctx).createBranchWeights(42, 13); BI = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB, Weights); TI = BB->getTerminator(); EXPECT_EQ(BI, TI); @@ -109,12 +110,12 @@ TEST_F(IRBuilderTest, LandingPadName) { TEST_F(IRBuilderTest, GetIntTy) { IRBuilder<> Builder(BB); IntegerType *Ty1 = Builder.getInt1Ty(); - EXPECT_EQ(Ty1, IntegerType::get(getGlobalContext(), 1)); + EXPECT_EQ(Ty1, IntegerType::get(Ctx, 1)); DataLayout* DL = new DataLayout(M.get()); IntegerType *IntPtrTy = Builder.getIntPtrTy(DL); unsigned IntPtrBitSize = DL->getPointerSizeInBits(0); - EXPECT_EQ(IntPtrTy, IntegerType::get(getGlobalContext(), IntPtrBitSize)); + EXPECT_EQ(IntPtrTy, IntegerType::get(Ctx, IntPtrBitSize)); delete DL; } @@ -182,4 +183,40 @@ TEST_F(IRBuilderTest, FastMathFlags) { } +TEST_F(IRBuilderTest, RAIIHelpersTest) { + IRBuilder<> Builder(BB); + EXPECT_FALSE(Builder.getFastMathFlags().allowReciprocal()); + MDBuilder MDB(M->getContext()); + + MDNode *FPMathA = MDB.createFPMath(0.01f); + MDNode *FPMathB = MDB.createFPMath(0.1f); + + Builder.SetDefaultFPMathTag(FPMathA); + + { + IRBuilder<>::FastMathFlagGuard Guard(Builder); + FastMathFlags FMF; + FMF.setAllowReciprocal(); + Builder.SetFastMathFlags(FMF); + Builder.SetDefaultFPMathTag(FPMathB); + EXPECT_TRUE(Builder.getFastMathFlags().allowReciprocal()); + EXPECT_EQ(FPMathB, Builder.getDefaultFPMathTag()); + } + + EXPECT_FALSE(Builder.getFastMathFlags().allowReciprocal()); + EXPECT_EQ(FPMathA, Builder.getDefaultFPMathTag()); + + Value *F = Builder.CreateLoad(GV); + + { + IRBuilder<>::InsertPointGuard Guard(Builder); + Builder.SetInsertPoint(cast<Instruction>(F)); + EXPECT_EQ(F, Builder.GetInsertPoint()); + } + + EXPECT_EQ(BB->end(), Builder.GetInsertPoint()); + EXPECT_EQ(BB, Builder.GetInsertBlock()); +} + + } diff --git a/unittests/IR/InstructionsTest.cpp b/unittests/IR/InstructionsTest.cpp index 9f66af147180e..65f85ba1b7079 100644 --- a/unittests/IR/InstructionsTest.cpp +++ b/unittests/IR/InstructionsTest.cpp @@ -116,15 +116,41 @@ TEST(InstructionsTest, BranchInst) { TEST(InstructionsTest, CastInst) { LLVMContext &C(getGlobalContext()); - Type* Int8Ty = Type::getInt8Ty(C); - Type* Int64Ty = Type::getInt64Ty(C); - Type* V8x8Ty = VectorType::get(Int8Ty, 8); - Type* V8x64Ty = VectorType::get(Int64Ty, 8); - Type* X86MMXTy = Type::getX86_MMXTy(C); + Type *Int8Ty = Type::getInt8Ty(C); + Type *Int16Ty = Type::getInt16Ty(C); + Type *Int32Ty = Type::getInt32Ty(C); + Type *Int64Ty = Type::getInt64Ty(C); + Type *V8x8Ty = VectorType::get(Int8Ty, 8); + Type *V8x64Ty = VectorType::get(Int64Ty, 8); + Type *X86MMXTy = Type::getX86_MMXTy(C); + + Type *HalfTy = Type::getHalfTy(C); + Type *FloatTy = Type::getFloatTy(C); + Type *DoubleTy = Type::getDoubleTy(C); + + Type *V2Int32Ty = VectorType::get(Int32Ty, 2); + Type *V2Int64Ty = VectorType::get(Int64Ty, 2); + Type *V4Int16Ty = VectorType::get(Int16Ty, 4); + + Type *Int32PtrTy = PointerType::get(Int32Ty, 0); + Type *Int64PtrTy = PointerType::get(Int64Ty, 0); + + Type *Int32PtrAS1Ty = PointerType::get(Int32Ty, 1); + Type *Int64PtrAS1Ty = PointerType::get(Int64Ty, 1); + + Type *V2Int32PtrAS1Ty = VectorType::get(Int32PtrAS1Ty, 2); + Type *V2Int64PtrAS1Ty = VectorType::get(Int64PtrAS1Ty, 2); + Type *V4Int32PtrAS1Ty = VectorType::get(Int32PtrAS1Ty, 4); + Type *V4Int64PtrAS1Ty = VectorType::get(Int64PtrAS1Ty, 4); + + Type *V2Int64PtrTy = VectorType::get(Int64PtrTy, 2); + Type *V2Int32PtrTy = VectorType::get(Int32PtrTy, 2); const Constant* c8 = Constant::getNullValue(V8x8Ty); const Constant* c64 = Constant::getNullValue(V8x64Ty); + const Constant *v2ptr32 = Constant::getNullValue(V2Int32PtrTy); + EXPECT_TRUE(CastInst::isCastable(V8x8Ty, X86MMXTy)); EXPECT_TRUE(CastInst::isCastable(X86MMXTy, V8x8Ty)); EXPECT_FALSE(CastInst::isCastable(Int64Ty, X86MMXTy)); @@ -132,16 +158,70 @@ TEST(InstructionsTest, CastInst) { EXPECT_TRUE(CastInst::isCastable(V8x8Ty, V8x64Ty)); EXPECT_EQ(CastInst::Trunc, CastInst::getCastOpcode(c64, true, V8x8Ty, true)); EXPECT_EQ(CastInst::SExt, CastInst::getCastOpcode(c8, true, V8x64Ty, true)); -} - + EXPECT_FALSE(CastInst::isBitCastable(V8x8Ty, X86MMXTy)); + EXPECT_FALSE(CastInst::isBitCastable(X86MMXTy, V8x8Ty)); + EXPECT_FALSE(CastInst::isBitCastable(Int64Ty, X86MMXTy)); + EXPECT_FALSE(CastInst::isBitCastable(V8x64Ty, V8x8Ty)); + EXPECT_FALSE(CastInst::isBitCastable(V8x8Ty, V8x64Ty)); + + // Check address space casts are rejected since we don't know the sizes here + EXPECT_FALSE(CastInst::isBitCastable(Int32PtrTy, Int32PtrAS1Ty)); + EXPECT_FALSE(CastInst::isBitCastable(Int32PtrAS1Ty, Int32PtrTy)); + EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrTy, V2Int32PtrAS1Ty)); + EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V2Int32PtrTy)); + EXPECT_TRUE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V2Int64PtrAS1Ty)); + EXPECT_TRUE(CastInst::isCastable(V2Int32PtrAS1Ty, V2Int32PtrTy)); + EXPECT_EQ(CastInst::AddrSpaceCast, CastInst::getCastOpcode(v2ptr32, true, + V2Int32PtrAS1Ty, + true)); + + // Test mismatched number of elements for pointers + EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V4Int64PtrAS1Ty)); + EXPECT_FALSE(CastInst::isBitCastable(V4Int64PtrAS1Ty, V2Int32PtrAS1Ty)); + EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V4Int32PtrAS1Ty)); + EXPECT_FALSE(CastInst::isBitCastable(Int32PtrTy, V2Int32PtrTy)); + EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrTy, Int32PtrTy)); + + EXPECT_TRUE(CastInst::isBitCastable(Int32PtrTy, Int64PtrTy)); + EXPECT_FALSE(CastInst::isBitCastable(DoubleTy, FloatTy)); + EXPECT_FALSE(CastInst::isBitCastable(FloatTy, DoubleTy)); + EXPECT_TRUE(CastInst::isBitCastable(FloatTy, FloatTy)); + EXPECT_TRUE(CastInst::isBitCastable(FloatTy, FloatTy)); + EXPECT_TRUE(CastInst::isBitCastable(FloatTy, Int32Ty)); + EXPECT_TRUE(CastInst::isBitCastable(Int16Ty, HalfTy)); + EXPECT_TRUE(CastInst::isBitCastable(Int32Ty, FloatTy)); + EXPECT_TRUE(CastInst::isBitCastable(V2Int32Ty, Int64Ty)); + + EXPECT_TRUE(CastInst::isBitCastable(V2Int32Ty, V4Int16Ty)); + EXPECT_FALSE(CastInst::isBitCastable(Int32Ty, Int64Ty)); + EXPECT_FALSE(CastInst::isBitCastable(Int64Ty, Int32Ty)); + + EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrTy, Int64Ty)); + EXPECT_FALSE(CastInst::isBitCastable(Int64Ty, V2Int32PtrTy)); + EXPECT_TRUE(CastInst::isBitCastable(V2Int64PtrTy, V2Int32PtrTy)); + EXPECT_TRUE(CastInst::isBitCastable(V2Int32PtrTy, V2Int64PtrTy)); + EXPECT_FALSE(CastInst::isBitCastable(V2Int32Ty, V2Int64Ty)); + EXPECT_FALSE(CastInst::isBitCastable(V2Int64Ty, V2Int32Ty)); + + + // Check that assertion is not hit when creating a cast with a vector of + // pointers + // First form + BasicBlock *BB = BasicBlock::Create(C); + Constant *NullV2I32Ptr = Constant::getNullValue(V2Int32PtrTy); + CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty, "foo", BB); + + // Second form + CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty); +} TEST(InstructionsTest, VectorGep) { LLVMContext &C(getGlobalContext()); // Type Definitions PointerType *Ptri8Ty = PointerType::get(IntegerType::get(C, 8), 0); - PointerType *Ptri32Ty = PointerType::get(IntegerType::get(C, 8), 0); + PointerType *Ptri32Ty = PointerType::get(IntegerType::get(C, 32), 0); VectorType *V2xi8PTy = VectorType::get(Ptri8Ty, 2); VectorType *V2xi32PTy = VectorType::get(Ptri32Ty, 2); @@ -255,6 +335,7 @@ TEST(InstructionsTest, FPMathOperator) { TEST(InstructionsTest, isEliminableCastPair) { LLVMContext &C(getGlobalContext()); + Type* Int16Ty = Type::getInt16Ty(C); Type* Int32Ty = Type::getInt32Ty(C); Type* Int64Ty = Type::getInt64Ty(C); Type* Int64PtrTy = Type::getInt64PtrTy(C); @@ -266,11 +347,20 @@ TEST(InstructionsTest, isEliminableCastPair) { Int32Ty, 0, Int32Ty), CastInst::BitCast); - // Source and destination pointers have different sizes -> fail. + // Source and destination have unknown sizes, but the same address space and + // the intermediate int is the maximum pointer size -> bitcast EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::PtrToInt, CastInst::IntToPtr, Int64PtrTy, Int64Ty, Int64PtrTy, - Int32Ty, 0, Int64Ty), + 0, 0, 0), + CastInst::BitCast); + + // Source and destination have unknown sizes, but the same address space and + // the intermediate int is not the maximum pointer size -> nothing + EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::PtrToInt, + CastInst::IntToPtr, + Int64PtrTy, Int32Ty, Int64PtrTy, + 0, 0, 0), 0U); // Middle pointer big enough -> bitcast. @@ -286,7 +376,43 @@ TEST(InstructionsTest, isEliminableCastPair) { Int64Ty, Int64PtrTy, Int64Ty, 0, Int32Ty, 0), 0U); + + // Test that we don't eliminate bitcasts between different address spaces, + // or if we don't have available pointer size information. + DataLayout DL("e-p:32:32:32-p1:16:16:16-p2:64:64:64-i1:8:8-i8:8:8-i16:16:16" + "-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64" + "-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"); + + Type* Int64PtrTyAS1 = Type::getInt64PtrTy(C, 1); + Type* Int64PtrTyAS2 = Type::getInt64PtrTy(C, 2); + + IntegerType *Int16SizePtr = DL.getIntPtrType(C, 1); + IntegerType *Int64SizePtr = DL.getIntPtrType(C, 2); + + // Cannot simplify inttoptr, addrspacecast + EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::IntToPtr, + CastInst::AddrSpaceCast, + Int16Ty, Int64PtrTyAS1, Int64PtrTyAS2, + 0, Int16SizePtr, Int64SizePtr), + 0U); + + // Cannot simplify addrspacecast, ptrtoint + EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::AddrSpaceCast, + CastInst::PtrToInt, + Int64PtrTyAS1, Int64PtrTyAS2, Int16Ty, + Int64SizePtr, Int16SizePtr, 0), + 0U); + + // Pass since the bitcast address spaces are the same + EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::IntToPtr, + CastInst::BitCast, + Int16Ty, Int64PtrTyAS1, Int64PtrTyAS1, + 0, 0, 0), + CastInst::IntToPtr); + } } // end anonymous namespace } // end namespace llvm + + diff --git a/unittests/IR/LegacyPassManagerTest.cpp b/unittests/IR/LegacyPassManagerTest.cpp new file mode 100644 index 0000000000000..11841bdeac0dd --- /dev/null +++ b/unittests/IR/LegacyPassManagerTest.cpp @@ -0,0 +1,559 @@ +//===- llvm/unittest/IR/LegacyPassManager.cpp - Legacy PassManager tests --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This unit test exercises the legacy pass manager infrastructure. We use the +// old names as well to ensure that the source-level compatibility wrapper +// works for out-of-tree code that expects to include llvm/PassManager.h and +// subclass the core pass classes. +// +//===----------------------------------------------------------------------===// + +#include "llvm/PassManager.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/CallGraphSCCPass.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/LoopPass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Assembly/PrintModulePass.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace llvm { + void initializeModuleNDMPass(PassRegistry&); + void initializeFPassPass(PassRegistry&); + void initializeCGPassPass(PassRegistry&); + void initializeLPassPass(PassRegistry&); + void initializeBPassPass(PassRegistry&); + + namespace { + // ND = no deps + // NM = no modifications + struct ModuleNDNM: public ModulePass { + public: + static char run; + static char ID; + ModuleNDNM() : ModulePass(ID) { } + virtual bool runOnModule(Module &M) { + run++; + return false; + } + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + } + }; + char ModuleNDNM::ID=0; + char ModuleNDNM::run=0; + + struct ModuleNDM : public ModulePass { + public: + static char run; + static char ID; + ModuleNDM() : ModulePass(ID) {} + virtual bool runOnModule(Module &M) { + run++; + return true; + } + }; + char ModuleNDM::ID=0; + char ModuleNDM::run=0; + + struct ModuleNDM2 : public ModulePass { + public: + static char run; + static char ID; + ModuleNDM2() : ModulePass(ID) {} + virtual bool runOnModule(Module &M) { + run++; + return true; + } + }; + char ModuleNDM2::ID=0; + char ModuleNDM2::run=0; + + struct ModuleDNM : public ModulePass { + public: + static char run; + static char ID; + ModuleDNM() : ModulePass(ID) { + initializeModuleNDMPass(*PassRegistry::getPassRegistry()); + } + virtual bool runOnModule(Module &M) { + EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); + run++; + return false; + } + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired<ModuleNDM>(); + AU.setPreservesAll(); + } + }; + char ModuleDNM::ID=0; + char ModuleDNM::run=0; + + template<typename P> + struct PassTestBase : public P { + protected: + static int runc; + static bool initialized; + static bool finalized; + int allocated; + void run() { + EXPECT_TRUE(initialized); + EXPECT_FALSE(finalized); + EXPECT_EQ(0, allocated); + allocated++; + runc++; + } + public: + static char ID; + static void finishedOK(int run) { + EXPECT_GT(runc, 0); + EXPECT_TRUE(initialized); + EXPECT_TRUE(finalized); + EXPECT_EQ(run, runc); + } + PassTestBase() : P(ID), allocated(0) { + initialized = false; + finalized = false; + runc = 0; + } + + virtual void releaseMemory() { + EXPECT_GT(runc, 0); + EXPECT_GT(allocated, 0); + allocated--; + } + }; + template<typename P> char PassTestBase<P>::ID; + template<typename P> int PassTestBase<P>::runc; + template<typename P> bool PassTestBase<P>::initialized; + template<typename P> bool PassTestBase<P>::finalized; + + template<typename T, typename P> + struct PassTest : public PassTestBase<P> { + public: +#ifndef _MSC_VER // MSVC complains that Pass is not base class. + using llvm::Pass::doInitialization; + using llvm::Pass::doFinalization; +#endif + virtual bool doInitialization(T &t) { + EXPECT_FALSE(PassTestBase<P>::initialized); + PassTestBase<P>::initialized = true; + return false; + } + virtual bool doFinalization(T &t) { + EXPECT_FALSE(PassTestBase<P>::finalized); + PassTestBase<P>::finalized = true; + EXPECT_EQ(0, PassTestBase<P>::allocated); + return false; + } + }; + + struct CGPass : public PassTest<CallGraph, CallGraphSCCPass> { + public: + CGPass() { + initializeCGPassPass(*PassRegistry::getPassRegistry()); + } + virtual bool runOnSCC(CallGraphSCC &SCMM) { + EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); + run(); + return false; + } + }; + + struct FPass : public PassTest<Module, FunctionPass> { + public: + virtual bool runOnFunction(Function &F) { + // FIXME: PR4112 + // EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); + run(); + return false; + } + }; + + struct LPass : public PassTestBase<LoopPass> { + private: + static int initcount; + static int fincount; + public: + LPass() { + initializeLPassPass(*PassRegistry::getPassRegistry()); + initcount = 0; fincount=0; + EXPECT_FALSE(initialized); + } + static void finishedOK(int run, int finalized) { + PassTestBase<LoopPass>::finishedOK(run); + EXPECT_EQ(run, initcount); + EXPECT_EQ(finalized, fincount); + } + using llvm::Pass::doInitialization; + using llvm::Pass::doFinalization; + virtual bool doInitialization(Loop* L, LPPassManager &LPM) { + initialized = true; + initcount++; + return false; + } + virtual bool runOnLoop(Loop *L, LPPassManager &LPM) { + EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); + run(); + return false; + } + virtual bool doFinalization() { + fincount++; + finalized = true; + return false; + } + }; + int LPass::initcount=0; + int LPass::fincount=0; + + struct BPass : public PassTestBase<BasicBlockPass> { + private: + static int inited; + static int fin; + public: + static void finishedOK(int run, int N) { + PassTestBase<BasicBlockPass>::finishedOK(run); + EXPECT_EQ(inited, N); + EXPECT_EQ(fin, N); + } + BPass() { + inited = 0; + fin = 0; + } + virtual bool doInitialization(Module &M) { + EXPECT_FALSE(initialized); + initialized = true; + return false; + } + virtual bool doInitialization(Function &F) { + inited++; + return false; + } + virtual bool runOnBasicBlock(BasicBlock &BB) { + EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); + run(); + return false; + } + virtual bool doFinalization(Function &F) { + fin++; + return false; + } + virtual bool doFinalization(Module &M) { + EXPECT_FALSE(finalized); + finalized = true; + EXPECT_EQ(0, allocated); + return false; + } + }; + int BPass::inited=0; + int BPass::fin=0; + + struct OnTheFlyTest: public ModulePass { + public: + static char ID; + OnTheFlyTest() : ModulePass(ID) { + initializeFPassPass(*PassRegistry::getPassRegistry()); + } + virtual bool runOnModule(Module &M) { + EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); + for (Module::iterator I=M.begin(),E=M.end(); I != E; ++I) { + Function &F = *I; + { + SCOPED_TRACE("Running on the fly function pass"); + getAnalysis<FPass>(F); + } + } + return false; + } + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired<FPass>(); + } + }; + char OnTheFlyTest::ID=0; + + TEST(PassManager, RunOnce) { + Module M("test-once", getGlobalContext()); + struct ModuleNDNM *mNDNM = new ModuleNDNM(); + struct ModuleDNM *mDNM = new ModuleDNM(); + struct ModuleNDM *mNDM = new ModuleNDM(); + struct ModuleNDM2 *mNDM2 = new ModuleNDM2(); + + mNDM->run = mNDNM->run = mDNM->run = mNDM2->run = 0; + + PassManager Passes; + Passes.add(new DataLayout(&M)); + Passes.add(mNDM2); + Passes.add(mNDM); + Passes.add(mNDNM); + Passes.add(mDNM); + + Passes.run(M); + // each pass must be run exactly once, since nothing invalidates them + EXPECT_EQ(1, mNDM->run); + EXPECT_EQ(1, mNDNM->run); + EXPECT_EQ(1, mDNM->run); + EXPECT_EQ(1, mNDM2->run); + } + + TEST(PassManager, ReRun) { + Module M("test-rerun", getGlobalContext()); + struct ModuleNDNM *mNDNM = new ModuleNDNM(); + struct ModuleDNM *mDNM = new ModuleDNM(); + struct ModuleNDM *mNDM = new ModuleNDM(); + struct ModuleNDM2 *mNDM2 = new ModuleNDM2(); + + mNDM->run = mNDNM->run = mDNM->run = mNDM2->run = 0; + + PassManager Passes; + Passes.add(new DataLayout(&M)); + Passes.add(mNDM); + Passes.add(mNDNM); + Passes.add(mNDM2);// invalidates mNDM needed by mDNM + Passes.add(mDNM); + + Passes.run(M); + // Some passes must be rerun because a pass that modified the + // module/function was run in between + EXPECT_EQ(2, mNDM->run); + EXPECT_EQ(1, mNDNM->run); + EXPECT_EQ(1, mNDM2->run); + EXPECT_EQ(1, mDNM->run); + } + + Module* makeLLVMModule(); + + template<typename T> + void MemoryTestHelper(int run) { + OwningPtr<Module> M(makeLLVMModule()); + T *P = new T(); + PassManager Passes; + Passes.add(new DataLayout(M.get())); + Passes.add(P); + Passes.run(*M); + T::finishedOK(run); + } + + template<typename T> + void MemoryTestHelper(int run, int N) { + Module *M = makeLLVMModule(); + T *P = new T(); + PassManager Passes; + Passes.add(new DataLayout(M)); + Passes.add(P); + Passes.run(*M); + T::finishedOK(run, N); + delete M; + } + + TEST(PassManager, Memory) { + // SCC#1: test1->test2->test3->test1 + // SCC#2: test4 + // SCC#3: indirect call node + { + SCOPED_TRACE("Callgraph pass"); + MemoryTestHelper<CGPass>(3); + } + + { + SCOPED_TRACE("Function pass"); + MemoryTestHelper<FPass>(4);// 4 functions + } + + { + SCOPED_TRACE("Loop pass"); + MemoryTestHelper<LPass>(2, 1); //2 loops, 1 function + } + { + SCOPED_TRACE("Basic block pass"); + MemoryTestHelper<BPass>(7, 4); //9 basic blocks + } + + } + + TEST(PassManager, MemoryOnTheFly) { + Module *M = makeLLVMModule(); + { + SCOPED_TRACE("Running OnTheFlyTest"); + struct OnTheFlyTest *O = new OnTheFlyTest(); + PassManager Passes; + Passes.add(new DataLayout(M)); + Passes.add(O); + Passes.run(*M); + + FPass::finishedOK(4); + } + delete M; + } + + Module* makeLLVMModule() { + // Module Construction + Module* mod = new Module("test-mem", getGlobalContext()); + mod->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" + "i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-" + "a0:0:64-s0:64:64-f80:128:128"); + mod->setTargetTriple("x86_64-unknown-linux-gnu"); + + // Type Definitions + std::vector<Type*>FuncTy_0_args; + FunctionType* FuncTy_0 = FunctionType::get( + /*Result=*/IntegerType::get(getGlobalContext(), 32), + /*Params=*/FuncTy_0_args, + /*isVarArg=*/false); + + std::vector<Type*>FuncTy_2_args; + FuncTy_2_args.push_back(IntegerType::get(getGlobalContext(), 1)); + FunctionType* FuncTy_2 = FunctionType::get( + /*Result=*/Type::getVoidTy(getGlobalContext()), + /*Params=*/FuncTy_2_args, + /*isVarArg=*/false); + + + // Function Declarations + + Function* func_test1 = Function::Create( + /*Type=*/FuncTy_0, + /*Linkage=*/GlobalValue::ExternalLinkage, + /*Name=*/"test1", mod); + func_test1->setCallingConv(CallingConv::C); + AttributeSet func_test1_PAL; + func_test1->setAttributes(func_test1_PAL); + + Function* func_test2 = Function::Create( + /*Type=*/FuncTy_0, + /*Linkage=*/GlobalValue::ExternalLinkage, + /*Name=*/"test2", mod); + func_test2->setCallingConv(CallingConv::C); + AttributeSet func_test2_PAL; + func_test2->setAttributes(func_test2_PAL); + + Function* func_test3 = Function::Create( + /*Type=*/FuncTy_0, + /*Linkage=*/GlobalValue::ExternalLinkage, + /*Name=*/"test3", mod); + func_test3->setCallingConv(CallingConv::C); + AttributeSet func_test3_PAL; + func_test3->setAttributes(func_test3_PAL); + + Function* func_test4 = Function::Create( + /*Type=*/FuncTy_2, + /*Linkage=*/GlobalValue::ExternalLinkage, + /*Name=*/"test4", mod); + func_test4->setCallingConv(CallingConv::C); + AttributeSet func_test4_PAL; + func_test4->setAttributes(func_test4_PAL); + + // Global Variable Declarations + + + // Constant Definitions + + // Global Variable Definitions + + // Function Definitions + + // Function: test1 (func_test1) + { + + BasicBlock* label_entry = BasicBlock::Create(getGlobalContext(), "entry",func_test1,0); + + // Block entry (label_entry) + CallInst* int32_3 = CallInst::Create(func_test2, "", label_entry); + int32_3->setCallingConv(CallingConv::C); + int32_3->setTailCall(false);AttributeSet int32_3_PAL; + int32_3->setAttributes(int32_3_PAL); + + ReturnInst::Create(getGlobalContext(), int32_3, label_entry); + + } + + // Function: test2 (func_test2) + { + + BasicBlock* label_entry_5 = BasicBlock::Create(getGlobalContext(), "entry",func_test2,0); + + // Block entry (label_entry_5) + CallInst* int32_6 = CallInst::Create(func_test3, "", label_entry_5); + int32_6->setCallingConv(CallingConv::C); + int32_6->setTailCall(false);AttributeSet int32_6_PAL; + int32_6->setAttributes(int32_6_PAL); + + ReturnInst::Create(getGlobalContext(), int32_6, label_entry_5); + + } + + // Function: test3 (func_test3) + { + + BasicBlock* label_entry_8 = BasicBlock::Create(getGlobalContext(), "entry",func_test3,0); + + // Block entry (label_entry_8) + CallInst* int32_9 = CallInst::Create(func_test1, "", label_entry_8); + int32_9->setCallingConv(CallingConv::C); + int32_9->setTailCall(false);AttributeSet int32_9_PAL; + int32_9->setAttributes(int32_9_PAL); + + ReturnInst::Create(getGlobalContext(), int32_9, label_entry_8); + + } + + // Function: test4 (func_test4) + { + Function::arg_iterator args = func_test4->arg_begin(); + Value* int1_f = args++; + int1_f->setName("f"); + + BasicBlock* label_entry_11 = BasicBlock::Create(getGlobalContext(), "entry",func_test4,0); + BasicBlock* label_bb = BasicBlock::Create(getGlobalContext(), "bb",func_test4,0); + BasicBlock* label_bb1 = BasicBlock::Create(getGlobalContext(), "bb1",func_test4,0); + BasicBlock* label_return = BasicBlock::Create(getGlobalContext(), "return",func_test4,0); + + // Block entry (label_entry_11) + BranchInst::Create(label_bb, label_entry_11); + + // Block bb (label_bb) + BranchInst::Create(label_bb, label_bb1, int1_f, label_bb); + + // Block bb1 (label_bb1) + BranchInst::Create(label_bb1, label_return, int1_f, label_bb1); + + // Block return (label_return) + ReturnInst::Create(getGlobalContext(), label_return); + + } + return mod; + } + + } +} + +INITIALIZE_PASS(ModuleNDM, "mndm", "mndm", false, false) +INITIALIZE_PASS_BEGIN(CGPass, "cgp","cgp", false, false) +INITIALIZE_PASS_DEPENDENCY(CallGraph) +INITIALIZE_PASS_END(CGPass, "cgp","cgp", false, false) +INITIALIZE_PASS(FPass, "fp","fp", false, false) +INITIALIZE_PASS_BEGIN(LPass, "lp","lp", false, false) +INITIALIZE_PASS_DEPENDENCY(LoopInfo) +INITIALIZE_PASS_END(LPass, "lp","lp", false, false) +INITIALIZE_PASS(BPass, "bp","bp", false, false) diff --git a/unittests/IR/PassManagerTest.cpp b/unittests/IR/PassManagerTest.cpp index 1097da61b9d99..7b60e3899a0ef 100644 --- a/unittests/IR/PassManagerTest.cpp +++ b/unittests/IR/PassManagerTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/IR/PassManager.cpp - PassManager unit tests ----------===// +//===- llvm/unittest/IR/PassManager.cpp - PassManager tests ---------------===// // // The LLVM Compiler Infrastructure // @@ -7,546 +7,125 @@ // //===----------------------------------------------------------------------===// -#include "llvm/PassManager.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Analysis/CallGraphSCCPass.h" -#include "llvm/Analysis/LoopInfo.h" -#include "llvm/Analysis/LoopPass.h" -#include "llvm/Analysis/Verifier.h" -#include "llvm/Assembly/PrintModulePass.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/CallingConv.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/DerivedTypes.h" +#include "llvm/Assembly/Parser.h" #include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/InlineAsm.h" -#include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" using namespace llvm; -namespace llvm { - void initializeModuleNDMPass(PassRegistry&); - void initializeFPassPass(PassRegistry&); - void initializeCGPassPass(PassRegistry&); - void initializeLPassPass(PassRegistry&); - void initializeBPassPass(PassRegistry&); - - namespace { - // ND = no deps - // NM = no modifications - struct ModuleNDNM: public ModulePass { - public: - static char run; - static char ID; - ModuleNDNM() : ModulePass(ID) { } - virtual bool runOnModule(Module &M) { - run++; - return false; - } - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - } - }; - char ModuleNDNM::ID=0; - char ModuleNDNM::run=0; - - struct ModuleNDM : public ModulePass { - public: - static char run; - static char ID; - ModuleNDM() : ModulePass(ID) {} - virtual bool runOnModule(Module &M) { - run++; - return true; - } - }; - char ModuleNDM::ID=0; - char ModuleNDM::run=0; - - struct ModuleNDM2 : public ModulePass { - public: - static char run; - static char ID; - ModuleNDM2() : ModulePass(ID) {} - virtual bool runOnModule(Module &M) { - run++; - return true; - } - }; - char ModuleNDM2::ID=0; - char ModuleNDM2::run=0; - - struct ModuleDNM : public ModulePass { - public: - static char run; - static char ID; - ModuleDNM() : ModulePass(ID) { - initializeModuleNDMPass(*PassRegistry::getPassRegistry()); - } - virtual bool runOnModule(Module &M) { - EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); - run++; - return false; - } - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired<ModuleNDM>(); - AU.setPreservesAll(); - } - }; - char ModuleDNM::ID=0; - char ModuleDNM::run=0; - - template<typename P> - struct PassTestBase : public P { - protected: - static int runc; - static bool initialized; - static bool finalized; - int allocated; - void run() { - EXPECT_TRUE(initialized); - EXPECT_FALSE(finalized); - EXPECT_EQ(0, allocated); - allocated++; - runc++; - } - public: - static char ID; - static void finishedOK(int run) { - EXPECT_GT(runc, 0); - EXPECT_TRUE(initialized); - EXPECT_TRUE(finalized); - EXPECT_EQ(run, runc); - } - PassTestBase() : P(ID), allocated(0) { - initialized = false; - finalized = false; - runc = 0; - } - - virtual void releaseMemory() { - EXPECT_GT(runc, 0); - EXPECT_GT(allocated, 0); - allocated--; - } - }; - template<typename P> char PassTestBase<P>::ID; - template<typename P> int PassTestBase<P>::runc; - template<typename P> bool PassTestBase<P>::initialized; - template<typename P> bool PassTestBase<P>::finalized; - - template<typename T, typename P> - struct PassTest : public PassTestBase<P> { - public: -#ifndef _MSC_VER // MSVC complains that Pass is not base class. - using llvm::Pass::doInitialization; - using llvm::Pass::doFinalization; -#endif - virtual bool doInitialization(T &t) { - EXPECT_FALSE(PassTestBase<P>::initialized); - PassTestBase<P>::initialized = true; - return false; - } - virtual bool doFinalization(T &t) { - EXPECT_FALSE(PassTestBase<P>::finalized); - PassTestBase<P>::finalized = true; - EXPECT_EQ(0, PassTestBase<P>::allocated); - return false; - } - }; - - struct CGPass : public PassTest<CallGraph, CallGraphSCCPass> { - public: - CGPass() { - initializeCGPassPass(*PassRegistry::getPassRegistry()); - } - virtual bool runOnSCC(CallGraphSCC &SCMM) { - EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); - run(); - return false; - } - }; - - struct FPass : public PassTest<Module, FunctionPass> { - public: - virtual bool runOnFunction(Function &F) { - // FIXME: PR4112 - // EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); - run(); - return false; - } - }; - - struct LPass : public PassTestBase<LoopPass> { - private: - static int initcount; - static int fincount; - public: - LPass() { - initializeLPassPass(*PassRegistry::getPassRegistry()); - initcount = 0; fincount=0; - EXPECT_FALSE(initialized); - } - static void finishedOK(int run, int finalized) { - PassTestBase<LoopPass>::finishedOK(run); - EXPECT_EQ(run, initcount); - EXPECT_EQ(finalized, fincount); - } - using llvm::Pass::doInitialization; - using llvm::Pass::doFinalization; - virtual bool doInitialization(Loop* L, LPPassManager &LPM) { - initialized = true; - initcount++; - return false; - } - virtual bool runOnLoop(Loop *L, LPPassManager &LPM) { - EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); - run(); - return false; - } - virtual bool doFinalization() { - fincount++; - finalized = true; - return false; - } - }; - int LPass::initcount=0; - int LPass::fincount=0; - - struct BPass : public PassTestBase<BasicBlockPass> { - private: - static int inited; - static int fin; - public: - static void finishedOK(int run, int N) { - PassTestBase<BasicBlockPass>::finishedOK(run); - EXPECT_EQ(inited, N); - EXPECT_EQ(fin, N); - } - BPass() { - inited = 0; - fin = 0; - } - virtual bool doInitialization(Module &M) { - EXPECT_FALSE(initialized); - initialized = true; - return false; - } - virtual bool doInitialization(Function &F) { - inited++; - return false; - } - virtual bool runOnBasicBlock(BasicBlock &BB) { - EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); - run(); - return false; - } - virtual bool doFinalization(Function &F) { - fin++; - return false; - } - virtual bool doFinalization(Module &M) { - EXPECT_FALSE(finalized); - finalized = true; - EXPECT_EQ(0, allocated); - return false; - } - }; - int BPass::inited=0; - int BPass::fin=0; - - struct OnTheFlyTest: public ModulePass { - public: - static char ID; - OnTheFlyTest() : ModulePass(ID) { - initializeFPassPass(*PassRegistry::getPassRegistry()); - } - virtual bool runOnModule(Module &M) { - EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>()); - for (Module::iterator I=M.begin(),E=M.end(); I != E; ++I) { - Function &F = *I; - { - SCOPED_TRACE("Running on the fly function pass"); - getAnalysis<FPass>(F); - } - } - return false; - } - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired<FPass>(); - } - }; - char OnTheFlyTest::ID=0; - - TEST(PassManager, RunOnce) { - Module M("test-once", getGlobalContext()); - struct ModuleNDNM *mNDNM = new ModuleNDNM(); - struct ModuleDNM *mDNM = new ModuleDNM(); - struct ModuleNDM *mNDM = new ModuleNDM(); - struct ModuleNDM2 *mNDM2 = new ModuleNDM2(); - - mNDM->run = mNDNM->run = mDNM->run = mNDM2->run = 0; - - PassManager Passes; - Passes.add(new DataLayout(&M)); - Passes.add(mNDM2); - Passes.add(mNDM); - Passes.add(mNDNM); - Passes.add(mDNM); - - Passes.run(M); - // each pass must be run exactly once, since nothing invalidates them - EXPECT_EQ(1, mNDM->run); - EXPECT_EQ(1, mNDNM->run); - EXPECT_EQ(1, mDNM->run); - EXPECT_EQ(1, mNDM2->run); - } - - TEST(PassManager, ReRun) { - Module M("test-rerun", getGlobalContext()); - struct ModuleNDNM *mNDNM = new ModuleNDNM(); - struct ModuleDNM *mDNM = new ModuleDNM(); - struct ModuleNDM *mNDM = new ModuleNDM(); - struct ModuleNDM2 *mNDM2 = new ModuleNDM2(); - - mNDM->run = mNDNM->run = mDNM->run = mNDM2->run = 0; - - PassManager Passes; - Passes.add(new DataLayout(&M)); - Passes.add(mNDM); - Passes.add(mNDNM); - Passes.add(mNDM2);// invalidates mNDM needed by mDNM - Passes.add(mDNM); - - Passes.run(M); - // Some passes must be rerun because a pass that modified the - // module/function was run in between - EXPECT_EQ(2, mNDM->run); - EXPECT_EQ(1, mNDNM->run); - EXPECT_EQ(1, mNDM2->run); - EXPECT_EQ(1, mDNM->run); - } - - Module* makeLLVMModule(); - - template<typename T> - void MemoryTestHelper(int run) { - OwningPtr<Module> M(makeLLVMModule()); - T *P = new T(); - PassManager Passes; - Passes.add(new DataLayout(M.get())); - Passes.add(P); - Passes.run(*M); - T::finishedOK(run); - } - - template<typename T> - void MemoryTestHelper(int run, int N) { - Module *M = makeLLVMModule(); - T *P = new T(); - PassManager Passes; - Passes.add(new DataLayout(M)); - Passes.add(P); - Passes.run(*M); - T::finishedOK(run, N); - delete M; - } - - TEST(PassManager, Memory) { - // SCC#1: test1->test2->test3->test1 - // SCC#2: test4 - // SCC#3: indirect call node - { - SCOPED_TRACE("Callgraph pass"); - MemoryTestHelper<CGPass>(3); - } - - { - SCOPED_TRACE("Function pass"); - MemoryTestHelper<FPass>(4);// 4 functions - } - - { - SCOPED_TRACE("Loop pass"); - MemoryTestHelper<LPass>(2, 1); //2 loops, 1 function - } - { - SCOPED_TRACE("Basic block pass"); - MemoryTestHelper<BPass>(7, 4); //9 basic blocks - } - - } - - TEST(PassManager, MemoryOnTheFly) { - Module *M = makeLLVMModule(); - { - SCOPED_TRACE("Running OnTheFlyTest"); - struct OnTheFlyTest *O = new OnTheFlyTest(); - PassManager Passes; - Passes.add(new DataLayout(M)); - Passes.add(O); - Passes.run(*M); - - FPass::finishedOK(4); - } - delete M; - } - - Module* makeLLVMModule() { - // Module Construction - Module* mod = new Module("test-mem", getGlobalContext()); - mod->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" - "i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-" - "a0:0:64-s0:64:64-f80:128:128"); - mod->setTargetTriple("x86_64-unknown-linux-gnu"); - - // Type Definitions - std::vector<Type*>FuncTy_0_args; - FunctionType* FuncTy_0 = FunctionType::get( - /*Result=*/IntegerType::get(getGlobalContext(), 32), - /*Params=*/FuncTy_0_args, - /*isVarArg=*/false); - - std::vector<Type*>FuncTy_2_args; - FuncTy_2_args.push_back(IntegerType::get(getGlobalContext(), 1)); - FunctionType* FuncTy_2 = FunctionType::get( - /*Result=*/Type::getVoidTy(getGlobalContext()), - /*Params=*/FuncTy_2_args, - /*isVarArg=*/false); - - - // Function Declarations - - Function* func_test1 = Function::Create( - /*Type=*/FuncTy_0, - /*Linkage=*/GlobalValue::ExternalLinkage, - /*Name=*/"test1", mod); - func_test1->setCallingConv(CallingConv::C); - AttributeSet func_test1_PAL; - func_test1->setAttributes(func_test1_PAL); - - Function* func_test2 = Function::Create( - /*Type=*/FuncTy_0, - /*Linkage=*/GlobalValue::ExternalLinkage, - /*Name=*/"test2", mod); - func_test2->setCallingConv(CallingConv::C); - AttributeSet func_test2_PAL; - func_test2->setAttributes(func_test2_PAL); - - Function* func_test3 = Function::Create( - /*Type=*/FuncTy_0, - /*Linkage=*/GlobalValue::ExternalLinkage, - /*Name=*/"test3", mod); - func_test3->setCallingConv(CallingConv::C); - AttributeSet func_test3_PAL; - func_test3->setAttributes(func_test3_PAL); - - Function* func_test4 = Function::Create( - /*Type=*/FuncTy_2, - /*Linkage=*/GlobalValue::ExternalLinkage, - /*Name=*/"test4", mod); - func_test4->setCallingConv(CallingConv::C); - AttributeSet func_test4_PAL; - func_test4->setAttributes(func_test4_PAL); - - // Global Variable Declarations - - - // Constant Definitions - - // Global Variable Definitions - - // Function Definitions - - // Function: test1 (func_test1) - { - - BasicBlock* label_entry = BasicBlock::Create(getGlobalContext(), "entry",func_test1,0); - - // Block entry (label_entry) - CallInst* int32_3 = CallInst::Create(func_test2, "", label_entry); - int32_3->setCallingConv(CallingConv::C); - int32_3->setTailCall(false);AttributeSet int32_3_PAL; - int32_3->setAttributes(int32_3_PAL); - - ReturnInst::Create(getGlobalContext(), int32_3, label_entry); - - } - - // Function: test2 (func_test2) - { - - BasicBlock* label_entry_5 = BasicBlock::Create(getGlobalContext(), "entry",func_test2,0); - - // Block entry (label_entry_5) - CallInst* int32_6 = CallInst::Create(func_test3, "", label_entry_5); - int32_6->setCallingConv(CallingConv::C); - int32_6->setTailCall(false);AttributeSet int32_6_PAL; - int32_6->setAttributes(int32_6_PAL); - - ReturnInst::Create(getGlobalContext(), int32_6, label_entry_5); - - } - - // Function: test3 (func_test3) - { +namespace { + +class TestAnalysisPass { +public: + typedef Function IRUnitT; + + struct Result { + Result(int Count) : InstructionCount(Count) {} + bool invalidate(Function *) { return true; } + int InstructionCount; + }; + + /// \brief Returns an opaque, unique ID for this pass type. + static void *ID() { return (void *)&PassID; } + + /// \brief Run the analysis pass over the function and return a result. + Result run(Function *F) { + int Count = 0; + for (Function::iterator BBI = F->begin(), BBE = F->end(); BBI != BBE; ++BBI) + for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE; + ++II) + ++Count; + return Result(Count); + } - BasicBlock* label_entry_8 = BasicBlock::Create(getGlobalContext(), "entry",func_test3,0); +private: + /// \brief Private static data to provide unique ID. + static char PassID; +}; - // Block entry (label_entry_8) - CallInst* int32_9 = CallInst::Create(func_test1, "", label_entry_8); - int32_9->setCallingConv(CallingConv::C); - int32_9->setTailCall(false);AttributeSet int32_9_PAL; - int32_9->setAttributes(int32_9_PAL); +char TestAnalysisPass::PassID; - ReturnInst::Create(getGlobalContext(), int32_9, label_entry_8); +struct TestModulePass { + TestModulePass(int &RunCount) : RunCount(RunCount) {} - } + bool run(Module *M) { + ++RunCount; + return true; + } - // Function: test4 (func_test4) - { - Function::arg_iterator args = func_test4->arg_begin(); - Value* int1_f = args++; - int1_f->setName("f"); + int &RunCount; +}; - BasicBlock* label_entry_11 = BasicBlock::Create(getGlobalContext(), "entry",func_test4,0); - BasicBlock* label_bb = BasicBlock::Create(getGlobalContext(), "bb",func_test4,0); - BasicBlock* label_bb1 = BasicBlock::Create(getGlobalContext(), "bb1",func_test4,0); - BasicBlock* label_return = BasicBlock::Create(getGlobalContext(), "return",func_test4,0); +struct TestFunctionPass { + TestFunctionPass(AnalysisManager &AM, int &RunCount, int &AnalyzedInstrCount) + : AM(AM), RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount) { + } - // Block entry (label_entry_11) - BranchInst::Create(label_bb, label_entry_11); + bool run(Function *F) { + ++RunCount; - // Block bb (label_bb) - BranchInst::Create(label_bb, label_bb1, int1_f, label_bb); + const TestAnalysisPass::Result &AR = AM.getResult<TestAnalysisPass>(F); + AnalyzedInstrCount += AR.InstructionCount; - // Block bb1 (label_bb1) - BranchInst::Create(label_bb1, label_return, int1_f, label_bb1); + return true; + } - // Block return (label_return) - ReturnInst::Create(getGlobalContext(), label_return); + AnalysisManager &AM; + int &RunCount; + int &AnalyzedInstrCount; +}; - } - return mod; - } +Module *parseIR(const char *IR) { + LLVMContext &C = getGlobalContext(); + SMDiagnostic Err; + return ParseAssemblyString(IR, 0, Err, C); +} - } +class PassManagerTest : public ::testing::Test { +protected: + OwningPtr<Module> M; + +public: + PassManagerTest() + : M(parseIR("define void @f() {\n" + "entry:\n" + " call void @g()\n" + " call void @h()\n" + " ret void\n" + "}\n" + "define void @g() {\n" + " ret void\n" + "}\n" + "define void @h() {\n" + " ret void\n" + "}\n")) {} +}; + +TEST_F(PassManagerTest, Basic) { + AnalysisManager AM(M.get()); + AM.registerAnalysisPass(TestAnalysisPass()); + + ModulePassManager MPM(M.get(), &AM); + FunctionPassManager FPM(&AM); + + // Count the runs over a module. + int ModulePassRunCount = 0; + MPM.addPass(TestModulePass(ModulePassRunCount)); + + // Count the runs over a Function. + int FunctionPassRunCount = 0; + int AnalyzedInstrCount = 0; + FPM.addPass(TestFunctionPass(AM, FunctionPassRunCount, AnalyzedInstrCount)); + MPM.addPass(FPM); + + MPM.run(); + EXPECT_EQ(1, ModulePassRunCount); + EXPECT_EQ(3, FunctionPassRunCount); + EXPECT_EQ(5, AnalyzedInstrCount); } -INITIALIZE_PASS(ModuleNDM, "mndm", "mndm", false, false) -INITIALIZE_PASS_BEGIN(CGPass, "cgp","cgp", false, false) -INITIALIZE_AG_DEPENDENCY(CallGraph) -INITIALIZE_PASS_END(CGPass, "cgp","cgp", false, false) -INITIALIZE_PASS(FPass, "fp","fp", false, false) -INITIALIZE_PASS_BEGIN(LPass, "lp","lp", false, false) -INITIALIZE_PASS_DEPENDENCY(LoopInfo) -INITIALIZE_PASS_END(LPass, "lp","lp", false, false) -INITIALIZE_PASS(BPass, "bp","bp", false, false) +} diff --git a/unittests/IR/ValueTest.cpp b/unittests/IR/ValueTest.cpp index 52efb1a220aa1..ebe23e869401d 100644 --- a/unittests/IR/ValueTest.cpp +++ b/unittests/IR/ValueTest.cpp @@ -43,4 +43,44 @@ TEST(ValueTest, UsedInBasicBlock) { EXPECT_TRUE(F->arg_begin()->isUsedInBasicBlock(F->begin())); } +TEST(GlobalTest, CreateAddressSpace) { + LLVMContext &Ctx = getGlobalContext(); + OwningPtr<Module> M(new Module("TestModule", Ctx)); + Type *Int8Ty = Type::getInt8Ty(Ctx); + Type *Int32Ty = Type::getInt32Ty(Ctx); + + GlobalVariable *Dummy0 + = new GlobalVariable(*M, + Int32Ty, + true, + GlobalValue::ExternalLinkage, + Constant::getAllOnesValue(Int32Ty), + "dummy", + 0, + GlobalVariable::NotThreadLocal, + 1); + + // Make sure the address space isn't dropped when returning this. + Constant *Dummy1 = M->getOrInsertGlobal("dummy", Int32Ty); + EXPECT_EQ(Dummy0, Dummy1); + EXPECT_EQ(1u, Dummy1->getType()->getPointerAddressSpace()); + + + // This one requires a bitcast, but the address space must also stay the same. + GlobalVariable *DummyCast0 + = new GlobalVariable(*M, + Int32Ty, + true, + GlobalValue::ExternalLinkage, + Constant::getAllOnesValue(Int32Ty), + "dummy_cast", + 0, + GlobalVariable::NotThreadLocal, + 1); + + // Make sure the address space isn't dropped when returning this. + Constant *DummyCast1 = M->getOrInsertGlobal("dummy_cast", Int8Ty); + EXPECT_EQ(1u, DummyCast1->getType()->getPointerAddressSpace()); + EXPECT_NE(DummyCast0, DummyCast1) << *DummyCast1; +} } // end anonymous namespace diff --git a/unittests/IR/VerifierTest.cpp b/unittests/IR/VerifierTest.cpp index 89119368fbd92..31936c392d848 100644 --- a/unittests/IR/VerifierTest.cpp +++ b/unittests/IR/VerifierTest.cpp @@ -24,10 +24,11 @@ namespace { TEST(VerifierTest, Branch_i1) { LLVMContext &C = getGlobalContext(); + Module M("M", C); FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false); - OwningPtr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage)); - BasicBlock *Entry = BasicBlock::Create(C, "entry", F.get()); - BasicBlock *Exit = BasicBlock::Create(C, "exit", F.get()); + Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy)); + BasicBlock *Entry = BasicBlock::Create(C, "entry", F); + BasicBlock *Exit = BasicBlock::Create(C, "exit", F); ReturnInst::Create(C, Exit); // To avoid triggering an assertion in BranchInst::Create, we first create @@ -60,5 +61,21 @@ TEST(VerifierTest, AliasUnnamedAddr) { EXPECT_TRUE(verifyModule(M, ReturnStatusAction, &Error)); EXPECT_TRUE(StringRef(Error).startswith("Alias cannot have unnamed_addr")); } + +TEST(VerifierTest, InvalidRetAttribute) { + LLVMContext &C = getGlobalContext(); + Module M("M", C); + FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false); + Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy)); + AttributeSet AS = F->getAttributes(); + F->setAttributes(AS.addAttribute(C, AttributeSet::ReturnIndex, + Attribute::UWTable)); + + std::string Error; + EXPECT_TRUE(verifyModule(M, ReturnStatusAction, &Error)); + EXPECT_TRUE(StringRef(Error). + startswith("Attribute 'uwtable' only applies to functions!")); +} + } } diff --git a/unittests/IR/WaymarkTest.cpp b/unittests/IR/WaymarkTest.cpp index cf7d76dffc978..9a9b4a2ad4e7b 100644 --- a/unittests/IR/WaymarkTest.cpp +++ b/unittests/IR/WaymarkTest.cpp @@ -9,6 +9,7 @@ // we perform white-box tests // +#include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" diff --git a/unittests/MC/CMakeLists.txt b/unittests/MC/CMakeLists.txt new file mode 100644 index 0000000000000..0e4782c83f40c --- /dev/null +++ b/unittests/MC/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + MC + ) + +set(MCSources + MCAtomTest.cpp + ) + +add_llvm_unittest(MCTests + ${MCSources} + ) diff --git a/unittests/MC/MCAtomTest.cpp b/unittests/MC/MCAtomTest.cpp new file mode 100644 index 0000000000000..17b056cd2dea7 --- /dev/null +++ b/unittests/MC/MCAtomTest.cpp @@ -0,0 +1,31 @@ +//===- llvm/unittest/MC/MCAtomTest.cpp - Instructions 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/MC/MCAtom.h" +#include "llvm/MC/MCModule.h" +#include "gtest/gtest.h" + +namespace llvm { +namespace { + +TEST(MCAtomTest, MCDataSize) { + MCModule M; + MCDataAtom *Atom = M.createDataAtom(0, 0); + EXPECT_EQ(uint64_t(0), Atom->getEndAddr()); + Atom->addData(0); + EXPECT_EQ(uint64_t(0), Atom->getEndAddr()); + Atom->addData(1); + EXPECT_EQ(uint64_t(1), Atom->getEndAddr()); + Atom->addData(2); + EXPECT_EQ(uint64_t(2), Atom->getEndAddr()); + EXPECT_EQ(size_t(3), Atom->getData().size()); +} + +} // end anonymous namespace +} // end namespace llvm diff --git a/unittests/MC/Makefile b/unittests/MC/Makefile new file mode 100644 index 0000000000000..4c25697d28dc0 --- /dev/null +++ b/unittests/MC/Makefile @@ -0,0 +1,15 @@ +##===- unittests/IR/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +TESTNAME = MC +LINK_COMPONENTS := MC + +include $(LEVEL)/Makefile.config +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Makefile b/unittests/Makefile index 61d60611be9ca..06ba93243e385 100644 --- a/unittests/Makefile +++ b/unittests/Makefile @@ -9,8 +9,8 @@ LEVEL = .. -PARALLEL_DIRS = ADT ExecutionEngine Support Transforms IR Analysis Bitcode \ - DebugInfo +PARALLEL_DIRS = ADT Analysis Bitcode CodeGen DebugInfo ExecutionEngine IR \ + MC Object Option Support Transforms include $(LEVEL)/Makefile.common diff --git a/unittests/Object/CMakeLists.txt b/unittests/Object/CMakeLists.txt new file mode 100644 index 0000000000000..b491dd7f6bdf2 --- /dev/null +++ b/unittests/Object/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + object + ) + +add_llvm_unittest(ObjectTests + YAMLTest.cpp + ) diff --git a/unittests/Object/Makefile b/unittests/Object/Makefile new file mode 100644 index 0000000000000..9062149a24d9c --- /dev/null +++ b/unittests/Object/Makefile @@ -0,0 +1,15 @@ +##===- unittests/Object/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +TESTNAME = Object +LINK_COMPONENTS := object + +include $(LEVEL)/Makefile.config +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Object/YAMLTest.cpp b/unittests/Object/YAMLTest.cpp new file mode 100644 index 0000000000000..3ae92ae1baeae --- /dev/null +++ b/unittests/Object/YAMLTest.cpp @@ -0,0 +1,38 @@ +//===- llvm/unittest/Object/YAMLTest.cpp - Tests for Object YAML ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/YAML.h" +#include "llvm/Support/YAMLTraits.h" +#include "gtest/gtest.h" + +using namespace llvm; + +struct BinaryHolder { + object::yaml::BinaryRef Binary; +}; + +namespace llvm { +namespace yaml { +template <> +struct MappingTraits<BinaryHolder> { + static void mapping(IO &IO, BinaryHolder &BH) { + IO.mapRequired("Binary", BH.Binary); + } +}; +} // end namespace yaml +} // end namespace llvm + +TEST(ObjectYAML, BinaryRef) { + BinaryHolder BH; + SmallVector<char, 32> Buf; + llvm::raw_svector_ostream OS(Buf); + yaml::Output YOut(OS); + YOut << BH; + EXPECT_NE(OS.str().find("\"\""), StringRef::npos); +} diff --git a/unittests/Option/Makefile b/unittests/Option/Makefile new file mode 100644 index 0000000000000..8c90a83da872f --- /dev/null +++ b/unittests/Option/Makefile @@ -0,0 +1,23 @@ +##===- unittests/Option/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +TESTNAME = Option +LINK_COMPONENTS := option support + +BUILT_SOURCES = Opts.inc +TABLEGEN_INC_FILES_COMMON = 1 + +include $(LEVEL)/Makefile.config + +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest + +$(ObjDir)/Opts.inc.tmp : Opts.td $(LLVM_TBLGEN) $(ObjDir)/.dir + $(Echo) "Building Driver Option tables with tblgen" + $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< diff --git a/unittests/Option/OptionParsingTest.cpp b/unittests/Option/OptionParsingTest.cpp index 30944d9be7976..11d6d1e87ebb0 100644 --- a/unittests/Option/OptionParsingTest.cpp +++ b/unittests/Option/OptionParsingTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -18,8 +19,8 @@ using namespace llvm::opt; enum ID { OPT_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) OPT_##ID, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR) OPT_##ID, #include "Opts.inc" LastOption #undef OPTION @@ -29,11 +30,17 @@ enum ID { #include "Opts.inc" #undef PREFIX +enum OptionFlags { + OptFlag1 = (1 << 4), + OptFlag2 = (1 << 5), + OptFlag3 = (1 << 6) +}; + static const OptTable::Info InfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR) \ { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, PARAM, \ - FLAGS, OPT_##GROUP, OPT_##ALIAS }, + FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS }, #include "Opts.inc" #undef OPTION }; @@ -41,8 +48,8 @@ static const OptTable::Info InfoTable[] = { namespace { class TestOptTable : public OptTable { public: - TestOptTable() - : OptTable(InfoTable, sizeof(InfoTable) / sizeof(InfoTable[0])) {} + TestOptTable(bool IgnoreCase = false) + : OptTable(InfoTable, array_lengthof(InfoTable), IgnoreCase) {} }; } @@ -58,14 +65,10 @@ const char *Args[] = { "-Gchuu", "2" }; -TEST(Support, OptionParsing) { +TEST(Option, OptionParsing) { TestOptTable T; unsigned MAI, MAC; - OwningPtr<InputArgList> - AL(T.ParseArgs(Args, - Args + (sizeof(Args) / sizeof(Args[0])), - MAI, - MAC)); + OwningPtr<InputArgList> AL(T.ParseArgs(Args, array_endof(Args), MAI, MAC)); // Check they all exist. EXPECT_TRUE(AL->hasArg(OPT_A)); @@ -104,3 +107,99 @@ TEST(Support, OptionParsing) { EXPECT_EQ(StringRef(ASL[0]), "-C"); EXPECT_EQ(StringRef(ASL[1]), "desu"); } + +TEST(Option, ParseWithFlagExclusions) { + TestOptTable T; + unsigned MAI, MAC; + OwningPtr<InputArgList> AL; + + // Exclude flag3 to avoid parsing as OPT_SLASH_C. + AL.reset(T.ParseArgs(Args, array_endof(Args), MAI, MAC, + /*FlagsToInclude=*/0, + /*FlagsToExclude=*/OptFlag3)); + EXPECT_TRUE(AL->hasArg(OPT_A)); + EXPECT_TRUE(AL->hasArg(OPT_C)); + EXPECT_FALSE(AL->hasArg(OPT_SLASH_C)); + + // Exclude flag1 to avoid parsing as OPT_C. + AL.reset(T.ParseArgs(Args, array_endof(Args), MAI, MAC, + /*FlagsToInclude=*/0, + /*FlagsToExclude=*/OptFlag1)); + EXPECT_TRUE(AL->hasArg(OPT_B)); + EXPECT_FALSE(AL->hasArg(OPT_C)); + EXPECT_TRUE(AL->hasArg(OPT_SLASH_C)); + + const char *NewArgs[] = { "/C", "foo", "--C=bar" }; + AL.reset(T.ParseArgs(NewArgs, array_endof(NewArgs), MAI, MAC)); + EXPECT_TRUE(AL->hasArg(OPT_SLASH_C)); + EXPECT_TRUE(AL->hasArg(OPT_C)); + EXPECT_EQ(AL->getLastArgValue(OPT_SLASH_C), "foo"); + EXPECT_EQ(AL->getLastArgValue(OPT_C), "bar"); +} + +TEST(Option, ParseAliasInGroup) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-I" }; + OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC)); + EXPECT_TRUE(AL->hasArg(OPT_H)); +} + +TEST(Option, AliasArgs) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-J", "-Joo" }; + OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC)); + EXPECT_TRUE(AL->hasArg(OPT_B)); + EXPECT_EQ(AL->getAllArgValues(OPT_B)[0], "foo"); + EXPECT_EQ(AL->getAllArgValues(OPT_B)[1], "bar"); +} + +TEST(Option, IgnoreCase) { + TestOptTable T(true); + unsigned MAI, MAC; + + const char *MyArgs[] = { "-a", "-joo" }; + OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC)); + EXPECT_TRUE(AL->hasArg(OPT_A)); + EXPECT_TRUE(AL->hasArg(OPT_B)); +} + +TEST(Option, DoNotIgnoreCase) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-a", "-joo" }; + OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC)); + EXPECT_FALSE(AL->hasArg(OPT_A)); + EXPECT_FALSE(AL->hasArg(OPT_B)); +} + +TEST(Option, SlurpEmpty) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-A", "-slurp" }; + OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC)); + EXPECT_TRUE(AL->hasArg(OPT_A)); + EXPECT_TRUE(AL->hasArg(OPT_Slurp)); + EXPECT_EQ(AL->getAllArgValues(OPT_Slurp).size(), 0U); +} + +TEST(Option, Slurp) { + TestOptTable T; + unsigned MAI, MAC; + + const char *MyArgs[] = { "-A", "-slurp", "-B", "--", "foo" }; + OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC)); + EXPECT_EQ(AL->size(), 2U); + EXPECT_TRUE(AL->hasArg(OPT_A)); + EXPECT_FALSE(AL->hasArg(OPT_B)); + EXPECT_TRUE(AL->hasArg(OPT_Slurp)); + EXPECT_EQ(AL->getAllArgValues(OPT_Slurp).size(), 3U); + EXPECT_EQ(AL->getAllArgValues(OPT_Slurp)[0], "-B"); + EXPECT_EQ(AL->getAllArgValues(OPT_Slurp)[1], "--"); + EXPECT_EQ(AL->getAllArgValues(OPT_Slurp)[2], "foo"); +} diff --git a/unittests/Option/Opts.td b/unittests/Option/Opts.td index 3d6242f5185c8..aaed6b2101e00 100644 --- a/unittests/Option/Opts.td +++ b/unittests/Option/Opts.td @@ -1,13 +1,26 @@ include "llvm/Option/OptParser.td" -def A : Flag<["-"], "A">, HelpText<"The A option">; -def B : Joined<["-"], "B">, HelpText<"The B option">, MetaVarName<"B">; -def C : Separate<["-"], "C">, HelpText<"The C option">, MetaVarName<"C">; +def OptFlag1 : OptionFlag; +def OptFlag2 : OptionFlag; +def OptFlag3 : OptionFlag; + +def A : Flag<["-"], "A">, HelpText<"The A option">, Flags<[OptFlag1]>; +def B : Joined<["-"], "B">, HelpText<"The B option">, MetaVarName<"B">, Flags<[OptFlag2]>; +def C : Separate<["-"], "C">, HelpText<"The C option">, MetaVarName<"C">, Flags<[OptFlag1]>; +def SLASH_C : Separate<["/", "-"], "C">, HelpText<"The C option">, MetaVarName<"C">, Flags<[OptFlag3]>; def D : CommaJoined<["-"], "D">, HelpText<"The D option">, MetaVarName<"D">; -def E : MultiArg<["-"], "E", 2>; +def E : MultiArg<["-"], "E", 2>, Flags<[OptFlag1, OptFlag2]>; def F : JoinedOrSeparate<["-"], "F">, HelpText<"The F option">, MetaVarName<"F">; def G : JoinedAndSeparate<["-"], "G">, HelpText<"The G option">, MetaVarName<"G">; -def Ceq : Joined<["-", "--"], "C=">, Alias<C>; +def Ceq : Joined<["-", "--"], "C=">, Alias<C>, Flags<[OptFlag1]>; def H : Flag<["-"], "H">, Flags<[HelpHidden]>; + +def my_group : OptionGroup<"my group">; +def I : Flag<["-"], "I">, Alias<H>, Group<my_group>; + +def J : Flag<["-"], "J">, Alias<B>, AliasArgs<["foo"]>; +def Joo : Flag<["-"], "Joo">, Alias<B>, AliasArgs<["bar"]>; + +def Slurp : Option<["-"], "slurp", KIND_REMAINING_ARGS>; diff --git a/unittests/Support/BlockFrequencyTest.cpp b/unittests/Support/BlockFrequencyTest.cpp index ff66bc4e45aae..ffdea2c1790fc 100644 --- a/unittests/Support/BlockFrequencyTest.cpp +++ b/unittests/Support/BlockFrequencyTest.cpp @@ -13,6 +13,11 @@ TEST(BlockFrequencyTest, OneToZero) { BranchProbability Prob(UINT32_MAX - 1, UINT32_MAX); Freq *= Prob; EXPECT_EQ(Freq.getFrequency(), 0u); + + Freq = BlockFrequency(1); + uint32_t Remainder = Freq.scale(Prob); + EXPECT_EQ(Freq.getFrequency(), 0u); + EXPECT_EQ(Remainder, UINT32_MAX - 1); } TEST(BlockFrequencyTest, OneToOne) { @@ -20,6 +25,11 @@ TEST(BlockFrequencyTest, OneToOne) { BranchProbability Prob(UINT32_MAX, UINT32_MAX); Freq *= Prob; EXPECT_EQ(Freq.getFrequency(), 1u); + + Freq = BlockFrequency(1); + uint32_t Remainder = Freq.scale(Prob); + EXPECT_EQ(Freq.getFrequency(), 1u); + EXPECT_EQ(Remainder, 0u); } TEST(BlockFrequencyTest, ThreeToOne) { @@ -27,6 +37,11 @@ TEST(BlockFrequencyTest, ThreeToOne) { BranchProbability Prob(3000000, 9000000); Freq *= Prob; EXPECT_EQ(Freq.getFrequency(), 1u); + + Freq = BlockFrequency(3); + uint32_t Remainder = Freq.scale(Prob); + EXPECT_EQ(Freq.getFrequency(), 1u); + EXPECT_EQ(Remainder, 0u); } TEST(BlockFrequencyTest, MaxToHalfMax) { @@ -34,6 +49,11 @@ TEST(BlockFrequencyTest, MaxToHalfMax) { BranchProbability Prob(UINT32_MAX / 2, UINT32_MAX); Freq *= Prob; EXPECT_EQ(Freq.getFrequency(), 9223372034707292159ULL); + + Freq = BlockFrequency(UINT64_MAX); + uint32_t Remainder = Freq.scale(Prob); + EXPECT_EQ(Freq.getFrequency(), 9223372034707292159ULL); + EXPECT_EQ(Remainder, 0u); } TEST(BlockFrequencyTest, BigToBig) { @@ -43,6 +63,11 @@ TEST(BlockFrequencyTest, BigToBig) { BranchProbability Prob(P, P); Freq *= Prob; EXPECT_EQ(Freq.getFrequency(), Big); + + Freq = BlockFrequency(Big); + uint32_t Remainder = Freq.scale(Prob); + EXPECT_EQ(Freq.getFrequency(), Big); + EXPECT_EQ(Remainder, 0u); } TEST(BlockFrequencyTest, MaxToMax) { @@ -50,6 +75,137 @@ TEST(BlockFrequencyTest, MaxToMax) { BranchProbability Prob(UINT32_MAX, UINT32_MAX); Freq *= Prob; EXPECT_EQ(Freq.getFrequency(), UINT64_MAX); + + // This additionally makes sure if we have a value equal to our saturating + // value, we do not signal saturation if the result equals said value, but + // saturating does not occur. + Freq = BlockFrequency(UINT64_MAX); + uint32_t Remainder = Freq.scale(Prob); + EXPECT_EQ(Freq.getFrequency(), UINT64_MAX); + EXPECT_EQ(Remainder, 0u); +} + +TEST(BlockFrequencyTest, ScaleResultRemainderTest) { + struct { + uint64_t Freq; + uint32_t Prob[2]; + uint64_t ExpectedFreq; + uint32_t ExpectedRemainder; + } Tests[80] = { + // Data for scaling that results in <= 64 bit division. + { 0x1423e2a50ULL, { 0x64819521, 0x7765dd13 }, 0x10f418889ULL, 0x92b9d25 }, + { 0x35ef14ceULL, { 0x28ade3c7, 0x304532ae }, 0x2d73c33aULL, 0x2c0fd0b6 }, + { 0xd03dbfbe24ULL, { 0x790079, 0xe419f3 }, 0x6e776fc1fdULL, 0x4a06dd }, + { 0x21d67410bULL, { 0x302a9dc2, 0x3ddb4442 }, 0x1a5948fd6ULL, 0x265d1c2a }, + { 0x8664aeadULL, { 0x3d523513, 0x403523b1 }, 0x805a04cfULL, 0x324c27b8 }, + { 0x201db0cf4ULL, { 0x35112a7b, 0x79fc0c74 }, 0xdf8b07f6ULL, 0x490c1dc4 }, + { 0x13f1e4430aULL, { 0x21c92bf, 0x21e63aae }, 0x13e0cba15ULL, 0x1df47c30 }, + { 0x16c83229ULL, { 0x3793f66f, 0x53180dea }, 0xf3ce7b6ULL, 0x1d0c1b6b }, + { 0xc62415be8ULL, { 0x9cc4a63, 0x4327ae9b }, 0x1ce8b71caULL, 0x3f2c696a }, + { 0x6fac5e434ULL, { 0xe5f9170, 0x1115e10b }, 0x5df23dd4cULL, 0x4dafc7c }, + { 0x1929375f2ULL, { 0x3a851375, 0x76c08456 }, 0xc662b082ULL, 0x343589ee }, + { 0x243c89db6ULL, { 0x354ebfc0, 0x450ef197 }, 0x1bf8c1661ULL, 0x4948e49 }, + { 0x310e9b31aULL, { 0x1b1b8acf, 0x2d3629f0 }, 0x1d69c93f9ULL, 0x73e3b96 }, + { 0xa1fae921dULL, { 0xa7a098c, 0x10469f44 }, 0x684413d6cULL, 0x86a882c }, + { 0xc1582d957ULL, { 0x498e061, 0x59856bc }, 0x9edc5f4e7ULL, 0x29b0653 }, + { 0x57cfee75ULL, { 0x1d061dc3, 0x7c8bfc17 }, 0x1476a220ULL, 0x2383d33f }, + { 0x139220080ULL, { 0x294a6c71, 0x2a2b07c9 }, 0x1329e1c76ULL, 0x7aa5da }, + { 0x1665d353cULL, { 0x7080db5, 0xde0d75c }, 0xb590d9fbULL, 0x7ba8c38 }, + { 0xe8f14541ULL, { 0x5188e8b2, 0x736527ef }, 0xa4971be5ULL, 0x6b612167 }, + { 0x2f4775f29ULL, { 0x254ef0fe, 0x435fcf50 }, 0x1a2e449c1ULL, 0x28bbf5e }, + { 0x27b85d8d7ULL, { 0x304c8220, 0x5de678f2 }, 0x146e3bef9ULL, 0x4b27097e }, + { 0x1d362e36bULL, { 0x36c85b12, 0x37a66f55 }, 0x1cc19b8e6ULL, 0x688e828 }, + { 0x155fd48c7ULL, { 0xf5894d, 0x1256108 }, 0x11e383602ULL, 0x111f0cb }, + { 0xb5db2d15ULL, { 0x39bb26c5, 0x5bdcda3e }, 0x72499259ULL, 0x59c4939b }, + { 0x153990298ULL, { 0x48921c09, 0x706eb817 }, 0xdb3268e8ULL, 0x66bb8a80 }, + { 0x28a7c3ed7ULL, { 0x1f776fd7, 0x349f7a70 }, 0x184f73ae1ULL, 0x28910321 }, + { 0x724dbeabULL, { 0x1bd149f5, 0x253a085e }, 0x5569c0b3ULL, 0xff8e2ed }, + { 0xd8f0c513ULL, { 0x18c8cc4c, 0x1b72bad0 }, 0xc3e30643ULL, 0xd85e134 }, + { 0x17ce3dcbULL, { 0x1e4c6260, 0x233b359e }, 0x1478f4afULL, 0x49ea31e }, + { 0x1ce036ce0ULL, { 0x29e3c8af, 0x5318dd4a }, 0xe8e76196ULL, 0x11d5b9c4 }, + { 0x1473ae2aULL, { 0x29b897ba, 0x2be29378 }, 0x13718185ULL, 0x6f93b2c }, + { 0x1dd41aa68ULL, { 0x3d0a4441, 0x5a0e8f12 }, 0x1437b6bbfULL, 0x54b09ffa }, + { 0x1b49e4a53ULL, { 0x3430c1fe, 0x5a204aed }, 0xfcd6852fULL, 0x15ad6ed7 }, + { 0x217941b19ULL, { 0x12ced2bd, 0x21b68310 }, 0x12aca65b1ULL, 0x1b2a9565 }, + { 0xac6a4dc8ULL, { 0x3ed68da8, 0x6fdca34c }, 0x60da926dULL, 0x22ff53e4 }, + { 0x1c503a4e7ULL, { 0xfcbbd32, 0x11e48d17 }, 0x18fec7d38ULL, 0xa8aa816 }, + { 0x1c885855ULL, { 0x213e919d, 0x25941897 }, 0x193de743ULL, 0x4ea09c }, + { 0x29b9c168eULL, { 0x2b644aea, 0x45725ee7 }, 0x1a122e5d5ULL, 0xbee1099 }, + { 0x806a33f2ULL, { 0x30a80a23, 0x5063733a }, 0x4db9a264ULL, 0x1eaed76e }, + { 0x282afc96bULL, { 0x143ae554, 0x1a9863ff }, 0x1e8de5204ULL, 0x158d9020 }, + // Data for scaling that results in > 64 bit division. + { 0x23ca5f2f672ca41cULL, { 0xecbc641, 0x111373f7 }, 0x1f0301e5e8295ab5ULL, 0xf627f79 }, + { 0x5e4f2468142265e3ULL, { 0x1ddf5837, 0x32189233 }, 0x383ca7ba9fdd2c8cULL, 0x1c8f33e1 }, + { 0x277a1a6f6b266bf6ULL, { 0x415d81a8, 0x61eb5e1e }, 0x1a5a3e1d41b30c0fULL, 0x29cde3ae }, + { 0x1bdbb49a237035cbULL, { 0xea5bf17, 0x1d25ffb3 }, 0xdffc51c53d44b93ULL, 0x5170574 }, + { 0x2bce6d29b64fb8ULL, { 0x3bfd5631, 0x7525c9bb }, 0x166ebedda7ac57ULL, 0x3026dfab }, + { 0x3a02116103df5013ULL, { 0x2ee18a83, 0x3299aea8 }, 0x35be8922ab1e2a84ULL, 0x298d9919 }, + { 0x7b5762390799b18cULL, { 0x12f8e5b9, 0x2563bcd4 }, 0x3e960077aca01209ULL, 0x93afeb8 }, + { 0x69cfd72537021579ULL, { 0x4c35f468, 0x6a40feee }, 0x4be4cb3848be98a3ULL, 0x4ff96b9e }, + { 0x49dfdf835120f1c1ULL, { 0x8cb3759, 0x559eb891 }, 0x79663f7120edadeULL, 0x51b1fb5b }, + { 0x74b5be5c27676381ULL, { 0x47e4c5e0, 0x7c7b19ff }, 0x4367d2dff36a1028ULL, 0x7a7b5608 }, + { 0x4f50f97075e7f431ULL, { 0x9a50a17, 0x11cd1185 }, 0x2af952b34c032df4ULL, 0xfddc6a3 }, + { 0x2f8b0d712e393be4ULL, { 0x1487e386, 0x15aa356e }, 0x2d0df36478a776aaULL, 0x14e2564c }, + { 0x224c1c75999d3deULL, { 0x3b2df0ea, 0x4523b100 }, 0x1d5b481d145f08aULL, 0x15145eec }, + { 0x2bcbcea22a399a76ULL, { 0x28b58212, 0x48dd013e }, 0x187814d084c47cabULL, 0x3a38ebe2 }, + { 0x1dbfca91257cb2d1ULL, { 0x1a8c04d9, 0x5e92502c }, 0x859cf7d00f77545ULL, 0x7431f4d }, + { 0x7f20039b57cda935ULL, { 0xeccf651, 0x323f476e }, 0x25720cd976461a77ULL, 0x202817a3 }, + { 0x40512c6a586aa087ULL, { 0x113b0423, 0x398c9eab }, 0x1341c03de8696a7eULL, 0x1e27284b }, + { 0x63d802693f050a11ULL, { 0xf50cdd6, 0xfce2a44 }, 0x60c0177bb5e46846ULL, 0xf7ad89e }, + { 0x2d956b422838de77ULL, { 0xb2d345b, 0x1321e557 }, 0x1aa0ed16b6aa5319ULL, 0xfe1a5ce }, + { 0x5a1cdf0c1657bc91ULL, { 0x1d77bb0c, 0x1f991ff1 }, 0x54097ee94ff87560ULL, 0x11c4a26c }, + { 0x3801b26d7e00176bULL, { 0xeed25da, 0x1a819d8b }, 0x1f89e96a3a639526ULL, 0xcd51e7c }, + { 0x37655e74338e1e45ULL, { 0x300e170a, 0x5a1595fe }, 0x1d8cfb55fddc0441ULL, 0x3df05434 }, + { 0x7b38703f2a84e6ULL, { 0x66d9053, 0xc79b6b9 }, 0x3f7d4c91774094ULL, 0x26d939e }, + { 0x2245063c0acb3215ULL, { 0x30ce2f5b, 0x610e7271 }, 0x113b916468389235ULL, 0x1b588512 }, + { 0x6bc195877b7b8a7eULL, { 0x392004aa, 0x4a24e60c }, 0x530594fb17db6ba5ULL, 0x35c0a5f0 }, + { 0x40a3fde23c7b43dbULL, { 0x4e712195, 0x6553e56e }, 0x320a799bc76a466aULL, 0x5e23a5eb }, + { 0x1d3dfc2866fbccbaULL, { 0x5075b517, 0x5fc42245 }, 0x18917f0061595bc3ULL, 0x3fcf4527 }, + { 0x19aeb14045a61121ULL, { 0x1bf6edec, 0x707e2f4b }, 0x6626672a070bcc7ULL, 0x3607801f }, + { 0x44ff90486c531e9fULL, { 0x66598a, 0x8a90dc }, 0x32f6f2b0525199b0ULL, 0x5ab576 }, + { 0x3f3e7121092c5bcbULL, { 0x1c754df7, 0x5951a1b9 }, 0x14267f50b7ef375dULL, 0x221220a8 }, + { 0x60e2dafb7e50a67eULL, { 0x4d96c66e, 0x65bd878d }, 0x49e31715ac393f8bULL, 0x4e97b195 }, + { 0x656286667e0e6e29ULL, { 0x9d971a2, 0xacda23b }, 0x5c6ee315ead6cb4fULL, 0x516f5bd }, + { 0x1114e0974255d507ULL, { 0x1c693, 0x2d6ff }, 0xaae42e4b35f6e60ULL, 0x8b65 }, + { 0x508c8baf3a70ff5aULL, { 0x3b26b779, 0x6ad78745 }, 0x2c98387636c4b365ULL, 0x11dc6a51 }, + { 0x5b47bc666bf1f9cfULL, { 0x10a87ed6, 0x187d358a }, 0x3e1767155848368bULL, 0xfb871c }, + { 0x50954e3744460395ULL, { 0x7a42263, 0xcdaa048 }, 0x2fe739f0aee1fee1ULL, 0xb8add57 }, + { 0x20020b406550dd8fULL, { 0x3318539, 0x42eead0 }, 0x186f326325fa346bULL, 0x10d3ae7 }, + { 0x5bcb0b872439ffd5ULL, { 0x6f61fb2, 0x9af7344 }, 0x41fa1e3bec3c1b30ULL, 0x4fee45a }, + { 0x7a670f365db87a53ULL, { 0x417e102, 0x3bb54c67 }, 0x8642a558304fd9eULL, 0x3b65f514 }, + { 0x1ef0db1e7bab1cd0ULL, { 0x2b60cf38, 0x4188f78f }, 0x147ae0d6226b2ee6ULL, 0x336b6106 } + }; + + for (unsigned i = 0; i < 80; i++) { + BlockFrequency Freq(Tests[i].Freq); + uint32_t Remainder = Freq.scale(BranchProbability(Tests[i].Prob[0], + Tests[i].Prob[1])); + EXPECT_EQ(Tests[i].ExpectedFreq, Freq.getFrequency()); + EXPECT_EQ(Tests[i].ExpectedRemainder, Remainder); + } +} + +TEST(BlockFrequency, Divide) { + BlockFrequency Freq(0x3333333333333333ULL); + Freq /= BranchProbability(1, 2); + EXPECT_EQ(Freq.getFrequency(), 0x6666666666666666ULL); +} + +TEST(BlockFrequencyTest, Saturate) { + BlockFrequency Freq(0x3333333333333333ULL); + Freq /= BranchProbability(100, 300); + EXPECT_EQ(Freq.getFrequency(), 0x9999999999999999ULL); + Freq /= BranchProbability(1, 2); + EXPECT_EQ(Freq.getFrequency(), UINT64_MAX); + + Freq = 0x1000000000000000ULL; + Freq /= BranchProbability(10000, 160000); + EXPECT_EQ(Freq.getFrequency(), UINT64_MAX); + + // Try to cheat the multiplication overflow check. + Freq = 0x00000001f0000001ull; + Freq /= BranchProbability(1000, 0xf000000f); + EXPECT_EQ(33506781356485509ULL, Freq.getFrequency()); } TEST(BlockFrequencyTest, ProbabilityCompare) { diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt index e6cafbcaf158b..0abc2ffe1d9c8 100644 --- a/unittests/Support/CMakeLists.txt +++ b/unittests/Support/CMakeLists.txt @@ -12,22 +12,27 @@ add_llvm_unittest(SupportTests CommandLineTest.cpp CompressionTest.cpp ConstantRangeTest.cpp + ConvertUTFTest.cpp DataExtractorTest.cpp EndianTest.cpp ErrorOrTest.cpp FileOutputBufferTest.cpp - IntegersSubsetTest.cpp LeakDetectorTest.cpp + LockFileManagerTest.cpp ManagedStatic.cpp MathExtrasTest.cpp + MD5Test.cpp MemoryBufferTest.cpp MemoryTest.cpp Path.cpp ProcessTest.cpp ProgramTest.cpp RegexTest.cpp + SourceMgrTest.cpp SwapByteOrderTest.cpp - TimeValue.cpp + ThreadLocalTest.cpp + TimeValueTest.cpp + UnicodeTest.cpp ValueHandleTest.cpp YAMLIOTest.cpp YAMLParserTest.cpp diff --git a/unittests/Support/Casting.cpp b/unittests/Support/Casting.cpp index 01583e43e29b4..362abeecd07ad 100644 --- a/unittests/Support/Casting.cpp +++ b/unittests/Support/Casting.cpp @@ -10,10 +10,15 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/IR/User.h" #include "gtest/gtest.h" #include <cstdlib> namespace llvm { +// Used to test illegal cast. If a cast doesn't match any of the "real" ones, +// it will match this one. +struct IllegalCast; +template <typename T> IllegalCast *cast(...) { return 0; } // set up two example classes // with conversion facility @@ -60,10 +65,25 @@ foo *bar::naz() { bar *fub(); + +template <> struct simplify_type<foo> { + typedef int SimpleType; + static SimpleType getSimplifiedValue(foo &Val) { return 0; } +}; + } // End llvm namespace using namespace llvm; + +// Test the peculiar behavior of Use in simplify_type. +int Check1[is_same<simplify_type<Use>::SimpleType, Value *>::value ? 1 : -1]; +int Check2[is_same<simplify_type<Use *>::SimpleType, Value *>::value ? 1 : -1]; + +// Test that a regular class behaves as expected. +int Check3[is_same<simplify_type<foo>::SimpleType, int>::value ? 1 : -1]; +int Check4[is_same<simplify_type<foo *>::SimpleType, foo *>::value ? 1 : -1]; + namespace { const foo *null_foo = NULL; @@ -203,3 +223,8 @@ TEST(CastingTest, InferredUpcastTakesPrecedence) { } // end namespace inferred_upcasting } // end anonymous namespace +// Test that we reject casts of temporaries (and so the illegal cast gets used). +namespace TemporaryCast { +struct pod {}; +IllegalCast *testIllegalCast() { return cast<foo>(pod()); } +} diff --git a/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp index cd235d274e6c0..c54e1b9570f89 100644 --- a/unittests/Support/CommandLineTest.cpp +++ b/unittests/Support/CommandLineTest.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Config/config.h" #include "gtest/gtest.h" @@ -118,4 +119,46 @@ TEST(CommandLineTest, UseOptionCategory) { "Category."; } +class StrDupSaver : public cl::StringSaver { + const char *SaveString(const char *Str) LLVM_OVERRIDE { + return strdup(Str); + } +}; + +typedef void ParserFunction(StringRef Source, llvm::cl::StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv); + + +void testCommandLineTokenizer(ParserFunction *parse, const char *Input, + const char *const Output[], size_t OutputSize) { + SmallVector<const char *, 0> Actual; + StrDupSaver Saver; + parse(Input, Saver, Actual); + EXPECT_EQ(OutputSize, Actual.size()); + for (unsigned I = 0, E = Actual.size(); I != E; ++I) { + if (I < OutputSize) + EXPECT_STREQ(Output[I], Actual[I]); + free(const_cast<char *>(Actual[I])); + } +} + +TEST(CommandLineTest, TokenizeGNUCommandLine) { + const char *Input = "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' " + "foo\"bar\"baz C:\\src\\foo.cpp \"C:\\src\\foo.cpp\""; + const char *const Output[] = { "foo bar", "foo bar", "foo bar", "foo\\bar", + "foobarbaz", "C:\\src\\foo.cpp", + "C:\\src\\foo.cpp" }; + testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output, + array_lengthof(Output)); +} + +TEST(CommandLineTest, TokenizeWindowsCommandLine) { + 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" }; + testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output, + array_lengthof(Output)); +} + } // anonymous namespace diff --git a/unittests/Support/CompressionTest.cpp b/unittests/Support/CompressionTest.cpp index c8e2cd9f02b62..c0a9adadb7887 100644 --- a/unittests/Support/CompressionTest.cpp +++ b/unittests/Support/CompressionTest.cpp @@ -63,6 +63,12 @@ TEST(CompressionTest, Zlib) { TestZlibCompression(BinaryDataStr, zlib::DefaultCompression); } +TEST(CompressionTest, ZlibCRC32) { + EXPECT_EQ( + 0x414FA339U, + zlib::crc32(StringRef("The quick brown fox jumps over the lazy dog"))); +} + #endif } diff --git a/unittests/Support/ConstantRangeTest.cpp b/unittests/Support/ConstantRangeTest.cpp index 4d6bbf6f8402b..3e0a085ed1e7f 100644 --- a/unittests/Support/ConstantRangeTest.cpp +++ b/unittests/Support/ConstantRangeTest.cpp @@ -216,6 +216,9 @@ TEST_F(ConstantRangeTest, SExt) { EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, 140)).signExtend(16), ConstantRange(APInt(16, -128), APInt(16, 128))); + + EXPECT_EQ(ConstantRange(APInt(16, 0x0200), APInt(16, 0x8000)).signExtend(19), + ConstantRange(APInt(19, 0x0200), APInt(19, 0x8000))); } TEST_F(ConstantRangeTest, IntersectWith) { diff --git a/unittests/Support/ConvertUTFTest.cpp b/unittests/Support/ConvertUTFTest.cpp new file mode 100644 index 0000000000000..13ea75b1573b5 --- /dev/null +++ b/unittests/Support/ConvertUTFTest.cpp @@ -0,0 +1,65 @@ +//===- llvm/unittest/Support/ConvertUTFTest.cpp - ConvertUTF 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/ConvertUTF.h" +#include "gtest/gtest.h" +#include <string> + +using namespace llvm; + +TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) { + // Src is the look of disapproval. + static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; + ArrayRef<char> Ref(Src, sizeof(Src) - 1); + std::string Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result); +} + +TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) { + // Src is the look of disapproval. + static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0"; + ArrayRef<char> Ref(Src, sizeof(Src) - 1); + std::string Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result); +} + +TEST(ConvertUTFTest, OddLengthInput) { + std::string Result; + bool Success = convertUTF16ToUTF8String(ArrayRef<char>("xxxxx", 5), Result); + EXPECT_FALSE(Success); +} + +TEST(ConvertUTFTest, Empty) { + std::string Result; + bool Success = convertUTF16ToUTF8String(ArrayRef<char>(), Result); + EXPECT_TRUE(Success); + EXPECT_TRUE(Result.empty()); +} + +TEST(ConvertUTFTest, HasUTF16BOM) { + bool HasBOM = hasUTF16ByteOrderMark(ArrayRef<char>("\xff\xfe", 2)); + EXPECT_TRUE(HasBOM); + HasBOM = hasUTF16ByteOrderMark(ArrayRef<char>("\xfe\xff", 2)); + EXPECT_TRUE(HasBOM); + HasBOM = hasUTF16ByteOrderMark(ArrayRef<char>("\xfe\xff ", 3)); + EXPECT_TRUE(HasBOM); // Don't care about odd lengths. + HasBOM = hasUTF16ByteOrderMark(ArrayRef<char>("\xfe\xff\x00asdf", 6)); + EXPECT_TRUE(HasBOM); + + HasBOM = hasUTF16ByteOrderMark(ArrayRef<char>()); + EXPECT_FALSE(HasBOM); + HasBOM = hasUTF16ByteOrderMark(ArrayRef<char>("\xfe", 1)); + EXPECT_FALSE(HasBOM); +} diff --git a/unittests/Support/ErrorOrTest.cpp b/unittests/Support/ErrorOrTest.cpp index 4853426c94706..feb6a086e1943 100644 --- a/unittests/Support/ErrorOrTest.cpp +++ b/unittests/Support/ErrorOrTest.cpp @@ -45,9 +45,6 @@ TEST(ErrorOr, Types) { *a = 42; EXPECT_EQ(42, x); - EXPECT_FALSE(ErrorOr<void>(errc::broken_pipe)); - EXPECT_TRUE(ErrorOr<void>(errc::success)); - #if LLVM_HAS_CXX11_STDLIB // Move only types. EXPECT_EQ(3, **t3()); @@ -67,38 +64,3 @@ TEST(ErrorOr, Covariant) { #endif } } // end anon namespace - -struct InvalidArgError { - InvalidArgError() {} - InvalidArgError(std::string S) : ArgName(S) {} - std::string ArgName; -}; - -namespace llvm { -template<> -struct ErrorOrUserDataTraits<InvalidArgError> : true_type { - static error_code error() { - return make_error_code(errc::invalid_argument); - } -}; -} // end namespace llvm - -ErrorOr<int> t4() { - return InvalidArgError("adena"); -} - -ErrorOr<void> t5() { - return InvalidArgError("pie"); -} - -namespace { -TEST(ErrorOr, UserErrorData) { - ErrorOr<int> a = t4(); - EXPECT_EQ(errc::invalid_argument, a); - EXPECT_EQ("adena", t4().getError<InvalidArgError>().ArgName); - - ErrorOr<void> b = t5(); - EXPECT_EQ(errc::invalid_argument, b); - EXPECT_EQ("pie", b.getError<InvalidArgError>().ArgName); -} -} // end anon namespace diff --git a/unittests/Support/FileOutputBufferTest.cpp b/unittests/Support/FileOutputBufferTest.cpp index 80d724536821f..5e873193f2886 100644 --- a/unittests/Support/FileOutputBufferTest.cpp +++ b/unittests/Support/FileOutputBufferTest.cpp @@ -11,7 +11,7 @@ #include "llvm/ADT/OwningPtr.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/PathV2.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" @@ -30,12 +30,8 @@ TEST(FileOutputBuffer, Test) { // Create unique temporary directory for these tests SmallString<128> TestDirectory; { - int fd; ASSERT_NO_ERROR( - fs::unique_file("FileOutputBuffer-test-%%-%%-%%-%%/dir", fd, - TestDirectory)); - ::close(fd); - TestDirectory = path::parent_path(TestDirectory); + fs::createUniqueDirectory("FileOutputBuffer-test", TestDirectory)); } // TEST 1: Verify commit case. diff --git a/unittests/Support/IntegersSubsetTest.cpp b/unittests/Support/IntegersSubsetTest.cpp deleted file mode 100644 index f4298bf595aad..0000000000000 --- a/unittests/Support/IntegersSubsetTest.cpp +++ /dev/null @@ -1,326 +0,0 @@ -//===- llvm/unittest/Support/IntegersSubsetTest.cpp - IntegersSubset 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/IntegersSubset.h" -#include "llvm/ADT/APInt.h" -#include "llvm/Support/IntegersSubsetMapping.h" -#include "gtest/gtest.h" -#include <vector> - -using namespace llvm; - -namespace { - - class Int : public APInt { - public: - Int() {} - Int(uint64_t V) : APInt(64, V) {} - Int(const APInt& Src) : APInt(Src) {} - bool operator < (const APInt& RHS) const { return ult(RHS); } - bool operator > (const APInt& RHS) const { return ugt(RHS); } - bool operator <= (const APInt& RHS) const { return ule(RHS); } - bool operator >= (const APInt& RHS) const { return uge(RHS); } - }; - - typedef IntRange<Int> Range; - typedef IntegersSubsetGeneric<Int> Subset; - typedef IntegersSubsetMapping<unsigned,Subset,Int> Mapping; - - TEST(IntegersSubsetTest, GeneralTest) { - - // Test construction. - - std::vector<Range> Ranges; - Ranges.reserve(3); - - // Initialize Subset as union of three pairs: - // { {0, 8}, {10, 18}, {20, 28} } - for (unsigned i = 0; i < 3; ++i) - Ranges.push_back(Range(Int(i*10), Int(i*10 + 8))); - - Subset TheSubset(Ranges); - - for (unsigned i = 0; i < 3; ++i) { - EXPECT_EQ(TheSubset.getItem(i).getLow(), Int(i*10)); - EXPECT_EQ(TheSubset.getItem(i).getHigh(), Int(i*10 + 8)); - } - - EXPECT_EQ(TheSubset.getNumItems(), 3ULL); - - // Test belonging to range. - - EXPECT_TRUE(TheSubset.isSatisfies(Int(5))); - EXPECT_FALSE(TheSubset.isSatisfies(Int(9))); - - // Test when subset contains the only item. - - Ranges.clear(); - Ranges.push_back(Range(Int(10), Int(10))); - - Subset TheSingleNumber(Ranges); - - EXPECT_TRUE(TheSingleNumber.isSingleNumber()); - - Ranges.push_back(Range(Int(12), Int(15))); - - Subset NotASingleNumber(Ranges); - - EXPECT_FALSE(NotASingleNumber.isSingleNumber()); - - // Test when subset contains items that are not a ranges but - // the single numbers. - - Ranges.clear(); - Ranges.push_back(Range(Int(10), Int(10))); - Ranges.push_back(Range(Int(15), Int(19))); - - Subset WithSingleNumberItems(Ranges); - - EXPECT_TRUE(WithSingleNumberItems.isSingleNumber(0)); - EXPECT_FALSE(WithSingleNumberItems.isSingleNumber(1)); - - // Test size of subset. Note subset itself may be not optimized (improper), - // so it may contain duplicates, and the size of subset { {0, 9} {5, 9} } - // will 15 instead of 10. - - Ranges.clear(); - Ranges.push_back(Range(Int(0), Int(9))); - Ranges.push_back(Range(Int(5), Int(9))); - - Subset NotOptimizedSubset(Ranges); - - EXPECT_EQ(NotOptimizedSubset.getSize(), 15ULL); - - // Test access to a single value. - // getSingleValue(idx) method represents subset as flat numbers collection, - // so subset { {0, 3}, {8, 10} } will represented as array - // { 0, 1, 2, 3, 8, 9, 10 }. - - Ranges.clear(); - Ranges.push_back(Range(Int(0), Int(3))); - Ranges.push_back(Range(Int(8), Int(10))); - - Subset OneMoreSubset(Ranges); - - EXPECT_EQ(OneMoreSubset.getSingleValue(5), Int(9)); - } - - TEST(IntegersSubsetTest, MappingTest) { - - Mapping::Cases TheCases; - - unsigned Successors[3] = {0, 1, 2}; - - // Test construction. - - Mapping TheMapping; - for (unsigned i = 0; i < 3; ++i) - TheMapping.add(Int(10*i), Int(10*i + 9), Successors + i); - TheMapping.add(Int(111), Int(222), Successors); - TheMapping.removeItem(--TheMapping.end()); - - TheMapping.getCases(TheCases); - - EXPECT_EQ(TheCases.size(), 3ULL); - - for (unsigned i = 0; i < 3; ++i) { - Mapping::Cases::iterator CaseIt = TheCases.begin(); - std::advance(CaseIt, i); - EXPECT_EQ(CaseIt->first, Successors + i); - EXPECT_EQ(CaseIt->second.getNumItems(), 1ULL); - EXPECT_EQ(CaseIt->second.getItem(0), Range(Int(10*i), Int(10*i + 9))); - } - - // Test verification. - - Mapping ImproperMapping; - ImproperMapping.add(Int(10), Int(11), Successors + 0); - ImproperMapping.add(Int(11), Int(12), Successors + 1); - - Mapping::RangeIterator ErrItem; - EXPECT_FALSE(ImproperMapping.verify(ErrItem)); - EXPECT_EQ(ErrItem, --ImproperMapping.end()); - - Mapping ProperMapping; - ProperMapping.add(Int(10), Int(11), Successors + 0); - ProperMapping.add(Int(12), Int(13), Successors + 1); - - EXPECT_TRUE(ProperMapping.verify(ErrItem)); - - // Test optimization. - - Mapping ToBeOptimized; - - for (unsigned i = 0; i < 3; ++i) { - ToBeOptimized.add(Int(i * 10), Int(i * 10 + 1), Successors + i); - ToBeOptimized.add(Int(i * 10 + 2), Int(i * 10 + 9), Successors + i); - } - - ToBeOptimized.optimize(); - - TheCases.clear(); - ToBeOptimized.getCases(TheCases); - - EXPECT_EQ(TheCases.size(), 3ULL); - - for (unsigned i = 0; i < 3; ++i) { - Mapping::Cases::iterator CaseIt = TheCases.begin(); - std::advance(CaseIt, i); - EXPECT_EQ(CaseIt->first, Successors + i); - EXPECT_EQ(CaseIt->second.getNumItems(), 1ULL); - EXPECT_EQ(CaseIt->second.getItem(0), Range(Int(i * 10), Int(i * 10 + 9))); - } - } - - typedef unsigned unsigned_pair[2]; - typedef unsigned_pair unsigned_ranges[]; - - void TestDiff( - const unsigned_ranges LHS, - unsigned LSize, - const unsigned_ranges RHS, - unsigned RSize, - const unsigned_ranges ExcludeRes, - unsigned ExcludeResSize, - const unsigned_ranges IntersectRes, - unsigned IntersectResSize - ) { - - Mapping::RangesCollection Ranges; - - Mapping LHSMapping; - for (unsigned i = 0; i < LSize; ++i) - Ranges.push_back(Range(Int(LHS[i][0]), Int(LHS[i][1]))); - LHSMapping.add(Ranges); - - Ranges.clear(); - - Mapping RHSMapping; - for (unsigned i = 0; i < RSize; ++i) - Ranges.push_back(Range(Int(RHS[i][0]), Int(RHS[i][1]))); - RHSMapping.add(Ranges); - - Mapping LExclude, Intersection; - - LHSMapping.diff(&LExclude, &Intersection, 0, RHSMapping); - - if (ExcludeResSize) { - EXPECT_EQ(LExclude.size(), ExcludeResSize); - - unsigned i = 0; - for (Mapping::RangeIterator rei = LExclude.begin(), - e = LExclude.end(); rei != e; ++rei, ++i) - EXPECT_EQ(rei->first, Range(ExcludeRes[i][0], ExcludeRes[i][1])); - } else - EXPECT_TRUE(LExclude.empty()); - - if (IntersectResSize) { - EXPECT_EQ(Intersection.size(), IntersectResSize); - - unsigned i = 0; - for (Mapping::RangeIterator ii = Intersection.begin(), - e = Intersection.end(); ii != e; ++ii, ++i) - EXPECT_EQ(ii->first, Range(IntersectRes[i][0], IntersectRes[i][1])); - } else - EXPECT_TRUE(Intersection.empty()); - - LExclude.clear(); - Intersection.clear(); - RHSMapping.diff(0, &Intersection, &LExclude, LHSMapping); - - // Check LExclude again. - if (ExcludeResSize) { - EXPECT_EQ(LExclude.size(), ExcludeResSize); - - unsigned i = 0; - for (Mapping::RangeIterator rei = LExclude.begin(), - e = LExclude.end(); rei != e; ++rei, ++i) - EXPECT_EQ(rei->first, Range(ExcludeRes[i][0], ExcludeRes[i][1])); - } else - EXPECT_TRUE(LExclude.empty()); - } - - TEST(IntegersSubsetTest, DiffTest) { - - static const unsigned NOT_A_NUMBER = 0xffff; - - { - unsigned_ranges LHS = { { 0, 4 }, { 7, 10 }, { 13, 17 } }; - unsigned_ranges RHS = { { 3, 14 } }; - unsigned_ranges ExcludeRes = { { 0, 2 }, { 15, 17 } }; - unsigned_ranges IntersectRes = { { 3, 4 }, { 7, 10 }, { 13, 14 } }; - - TestDiff(LHS, 3, RHS, 1, ExcludeRes, 2, IntersectRes, 3); - } - - { - unsigned_ranges LHS = { { 0, 4 }, { 7, 10 }, { 13, 17 } }; - unsigned_ranges RHS = { { 0, 4 }, { 13, 17 } }; - unsigned_ranges ExcludeRes = { { 7, 10 } }; - unsigned_ranges IntersectRes = { { 0, 4 }, { 13, 17 } }; - - TestDiff(LHS, 3, RHS, 2, ExcludeRes, 1, IntersectRes, 2); - } - - { - unsigned_ranges LHS = { { 0, 17 } }; - unsigned_ranges RHS = { { 1, 5 }, { 10, 12 }, { 15, 16 } }; - unsigned_ranges ExcludeRes = - { { 0, 0 }, { 6, 9 }, { 13, 14 }, { 17, 17 } }; - unsigned_ranges IntersectRes = { { 1, 5 }, { 10, 12 }, { 15, 16 } }; - - TestDiff(LHS, 1, RHS, 3, ExcludeRes, 4, IntersectRes, 3); - } - - { - unsigned_ranges LHS = { { 2, 4 } }; - unsigned_ranges RHS = { { 0, 5 } }; - unsigned_ranges ExcludeRes = { {NOT_A_NUMBER, NOT_A_NUMBER} }; - unsigned_ranges IntersectRes = { { 2, 4 } }; - - TestDiff(LHS, 1, RHS, 1, ExcludeRes, 0, IntersectRes, 1); - } - - { - unsigned_ranges LHS = { { 2, 4 } }; - unsigned_ranges RHS = { { 7, 8 } }; - unsigned_ranges ExcludeRes = { { 2, 4 } }; - unsigned_ranges IntersectRes = { {NOT_A_NUMBER, NOT_A_NUMBER} }; - - TestDiff(LHS, 1, RHS, 1, ExcludeRes, 1, IntersectRes, 0); - } - - { - unsigned_ranges LHS = { { 3, 7 } }; - unsigned_ranges RHS = { { 1, 4 } }; - unsigned_ranges ExcludeRes = { { 5, 7 } }; - unsigned_ranges IntersectRes = { { 3, 4 } }; - - TestDiff(LHS, 1, RHS, 1, ExcludeRes, 1, IntersectRes, 1); - } - - { - unsigned_ranges LHS = { { 0, 7 } }; - unsigned_ranges RHS = { { 0, 5 }, { 6, 9 } }; - unsigned_ranges ExcludeRes = { {NOT_A_NUMBER, NOT_A_NUMBER} }; - unsigned_ranges IntersectRes = { { 0, 5 }, {6, 7} }; - - TestDiff(LHS, 1, RHS, 2, ExcludeRes, 0, IntersectRes, 2); - } - - { - unsigned_ranges LHS = { { 17, 17 } }; - unsigned_ranges RHS = { { 4, 4 } }; - unsigned_ranges ExcludeRes = { {17, 17} }; - unsigned_ranges IntersectRes = { { NOT_A_NUMBER, NOT_A_NUMBER } }; - - TestDiff(LHS, 1, RHS, 1, ExcludeRes, 1, IntersectRes, 0); - } - } -} diff --git a/unittests/Support/LockFileManagerTest.cpp b/unittests/Support/LockFileManagerTest.cpp new file mode 100644 index 0000000000000..5c73b9f5e2352 --- /dev/null +++ b/unittests/Support/LockFileManagerTest.cpp @@ -0,0 +1,48 @@ +//===- unittests/LockFileManagerTest.cpp - LockFileManager 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/LockFileManager.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +#include "gtest/gtest.h" + +#include <memory> + +using namespace llvm; + +namespace { + +TEST(LockFileManagerTest, Basic) { + SmallString<64> TmpDir; + error_code EC; + EC = sys::fs::createUniqueDirectory("LockFileManagerTestDir", TmpDir); + ASSERT_FALSE(EC); + + SmallString<64> LockedFile(TmpDir); + sys::path::append(LockedFile, "file.lock"); + + { + // The lock file should not exist, so we should successfully acquire it. + LockFileManager Locked1(LockedFile); + EXPECT_EQ(LockFileManager::LFS_Owned, Locked1.getState()); + + // Attempting to reacquire the lock should fail. Waiting on it would cause + // deadlock, so don't try that. + LockFileManager Locked2(LockedFile); + EXPECT_NE(LockFileManager::LFS_Owned, Locked2.getState()); + } + + // Now that the lock is out of scope, the file should be gone. + EXPECT_FALSE(sys::fs::exists(StringRef(LockedFile))); + + sys::fs::remove_all(StringRef(TmpDir)); +} + +} // end anonymous namespace diff --git a/unittests/Support/MD5Test.cpp b/unittests/Support/MD5Test.cpp new file mode 100644 index 0000000000000..7c1331b6c5370 --- /dev/null +++ b/unittests/Support/MD5Test.cpp @@ -0,0 +1,60 @@ +//===- llvm/unittest/Support/MD5Test.cpp - MD5 tests ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements unit tests for the MD5 functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/MD5.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { +/// \brief Tests an arbitrary set of bytes passed as \p Input. +void TestMD5Sum(ArrayRef<uint8_t> Input, StringRef Final) { + MD5 Hash; + Hash.update(Input); + MD5::MD5Result MD5Res; + Hash.final(MD5Res); + SmallString<32> Res; + MD5::stringifyResult(MD5Res, Res); + EXPECT_EQ(Res, Final); +} + +void TestMD5Sum(StringRef Input, StringRef Final) { + MD5 Hash; + Hash.update(Input); + MD5::MD5Result MD5Res; + Hash.final(MD5Res); + SmallString<32> Res; + MD5::stringifyResult(MD5Res, Res); + EXPECT_EQ(Res, Final); +} + +TEST(MD5Test, MD5) { + TestMD5Sum(ArrayRef<uint8_t>((const uint8_t *)"", (size_t) 0), + "d41d8cd98f00b204e9800998ecf8427e"); + TestMD5Sum(ArrayRef<uint8_t>((const uint8_t *)"a", (size_t) 1), + "0cc175b9c0f1b6a831c399e269772661"); + TestMD5Sum(ArrayRef<uint8_t>((const uint8_t *)"abcdefghijklmnopqrstuvwxyz", + (size_t) 26), + "c3fcd3d76192e4007dfb496cca67e13b"); + TestMD5Sum(ArrayRef<uint8_t>((const uint8_t *)"\0", (size_t) 1), + "93b885adfe0da089cdf634904fd59f71"); + TestMD5Sum(ArrayRef<uint8_t>((const uint8_t *)"a\0", (size_t) 2), + "4144e195f46de78a3623da7364d04f11"); + TestMD5Sum(ArrayRef<uint8_t>((const uint8_t *)"abcdefghijklmnopqrstuvwxyz\0", + (size_t) 27), + "81948d1f1554f58cd1a56ebb01f808cb"); + TestMD5Sum("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"); +} +} diff --git a/unittests/Support/Makefile b/unittests/Support/Makefile index 815bdd269da56..9c0a7f94d7725 100644 --- a/unittests/Support/Makefile +++ b/unittests/Support/Makefile @@ -1,4 +1,4 @@ -##===- unittests/ADT/Makefile ------------------------------*- Makefile -*-===## +##===- unittests/Support/Makefile --------------------------*- Makefile -*-===## # # The LLVM Compiler Infrastructure # diff --git a/unittests/Support/ManagedStatic.cpp b/unittests/Support/ManagedStatic.cpp index 8ddad38ecf171..1497f4e34082d 100644 --- a/unittests/Support/ManagedStatic.cpp +++ b/unittests/Support/ManagedStatic.cpp @@ -19,7 +19,8 @@ using namespace llvm; namespace { -#if defined(HAVE_PTHREAD_H) && !__has_feature(memory_sanitizer) +#if LLVM_ENABLE_THREADS != 0 && defined(HAVE_PTHREAD_H) && \ + !__has_feature(memory_sanitizer) namespace test1 { llvm::ManagedStatic<int> ms; void *helper(void*) { diff --git a/unittests/Support/MathExtrasTest.cpp b/unittests/Support/MathExtrasTest.cpp index 0a6724c7e7080..93a38cb03fd93 100644 --- a/unittests/Support/MathExtrasTest.cpp +++ b/unittests/Support/MathExtrasTest.cpp @@ -14,6 +14,109 @@ using namespace llvm; namespace { +TEST(MathExtras, countTrailingZeros) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(8u, countTrailingZeros(Z8)); + EXPECT_EQ(16u, countTrailingZeros(Z16)); + EXPECT_EQ(32u, countTrailingZeros(Z32)); + EXPECT_EQ(64u, countTrailingZeros(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(1u, countTrailingZeros(NZ8)); + EXPECT_EQ(1u, countTrailingZeros(NZ16)); + EXPECT_EQ(1u, countTrailingZeros(NZ32)); + EXPECT_EQ(1u, countTrailingZeros(NZ64)); +} + +TEST(MathExtras, countLeadingZeros) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(8u, countLeadingZeros(Z8)); + EXPECT_EQ(16u, countLeadingZeros(Z16)); + EXPECT_EQ(32u, countLeadingZeros(Z32)); + EXPECT_EQ(64u, countLeadingZeros(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(2u, countLeadingZeros(NZ8)); + EXPECT_EQ(10u, countLeadingZeros(NZ16)); + EXPECT_EQ(26u, countLeadingZeros(NZ32)); + EXPECT_EQ(58u, countLeadingZeros(NZ64)); + + EXPECT_EQ(8u, countLeadingZeros(0x00F000FFu)); + EXPECT_EQ(8u, countLeadingZeros(0x00F12345u)); + for (unsigned i = 0; i <= 30; ++i) { + EXPECT_EQ(31 - i, countLeadingZeros(1u << i)); + } + + EXPECT_EQ(8u, countLeadingZeros(0x00F1234500F12345ULL)); + EXPECT_EQ(1u, countLeadingZeros(1ULL << 62)); + for (unsigned i = 0; i <= 62; ++i) { + EXPECT_EQ(63 - i, countLeadingZeros(1ULL << i)); + } +} + +TEST(MathExtras, findFirstSet) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(0xFFULL, findFirstSet(Z8)); + EXPECT_EQ(0xFFFFULL, findFirstSet(Z16)); + EXPECT_EQ(0xFFFFFFFFULL, findFirstSet(Z32)); + EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findFirstSet(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(1u, findFirstSet(NZ8)); + EXPECT_EQ(1u, findFirstSet(NZ16)); + EXPECT_EQ(1u, findFirstSet(NZ32)); + EXPECT_EQ(1u, findFirstSet(NZ64)); +} + +TEST(MathExtras, findLastSet) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(0xFFULL, findLastSet(Z8)); + EXPECT_EQ(0xFFFFULL, findLastSet(Z16)); + EXPECT_EQ(0xFFFFFFFFULL, findLastSet(Z32)); + EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findLastSet(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(5u, findLastSet(NZ8)); + EXPECT_EQ(5u, findLastSet(NZ16)); + EXPECT_EQ(5u, findLastSet(NZ32)); + EXPECT_EQ(5u, findLastSet(NZ64)); +} + +TEST(MathExtras, reverseBits) { + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(0x54ULL, reverseBits(NZ8)); + EXPECT_EQ(0x5400ULL, reverseBits(NZ16)); + EXPECT_EQ(0x54000000ULL, reverseBits(NZ32)); + EXPECT_EQ(0x5400000000000000ULL, reverseBits(NZ64)); +} + TEST(MathExtras, isPowerOf2_32) { EXPECT_TRUE(isPowerOf2_32(1 << 6)); EXPECT_TRUE(isPowerOf2_32(1 << 12)); @@ -38,22 +141,6 @@ TEST(MathExtras, ByteSwap_64) { EXPECT_EQ(0x1100FFEEDDCCBBAAULL, ByteSwap_64(0xAABBCCDDEEFF0011LL)); } -TEST(MathExtras, CountLeadingZeros_32) { - EXPECT_EQ(8u, CountLeadingZeros_32(0x00F000FF)); - EXPECT_EQ(8u, CountLeadingZeros_32(0x00F12345)); - for (unsigned i = 0; i <= 30; ++i) { - EXPECT_EQ(31 - i, CountLeadingZeros_32(1 << i)); - } -} - -TEST(MathExtras, CountLeadingZeros_64) { - EXPECT_EQ(8u, CountLeadingZeros_64(0x00F1234500F12345LL)); - EXPECT_EQ(1u, CountLeadingZeros_64(1LL << 62)); - for (unsigned i = 0; i <= 62; ++i) { - EXPECT_EQ(63 - i, CountLeadingZeros_64(1LL << i)); - } -} - TEST(MathExtras, CountLeadingOnes_32) { for (int i = 30; i >= 0; --i) { // Start with all ones and unset some bit. diff --git a/unittests/Support/MemoryBufferTest.cpp b/unittests/Support/MemoryBufferTest.cpp index 1d9f482c519db..2b8806c394f34 100644 --- a/unittests/Support/MemoryBufferTest.cpp +++ b/unittests/Support/MemoryBufferTest.cpp @@ -11,12 +11,16 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/ADT/OwningPtr.h" #include "gtest/gtest.h" using namespace llvm; +namespace { + class MemoryBufferTest : public testing::Test { protected: MemoryBufferTest() @@ -25,13 +29,18 @@ protected: virtual void SetUp() { } + /// Common testing for different modes of getOpenFileSlice. + /// Creates a temporary file with known contents, and uses + /// MemoryBuffer::getOpenFileSlice to map it. + /// If \p Reopen is true, the file is closed after creating and reopened + /// anew before using MemoryBuffer. + void testGetOpenFileSlice(bool Reopen); + typedef OwningPtr<MemoryBuffer> OwningBuffer; std::string data; }; -namespace { - TEST_F(MemoryBufferTest, get) { // Default name and null-terminator flag OwningBuffer MB1(MemoryBuffer::getMemBuffer(data)); @@ -56,6 +65,28 @@ TEST_F(MemoryBufferTest, get) { EXPECT_EQ("this is some data", data); } +TEST_F(MemoryBufferTest, NullTerminator4K) { + // Test that a file with size that is a multiple of the page size can be null + // terminated correctly by MemoryBuffer. + int TestFD; + SmallString<64> TestPath; + sys::fs::createTemporaryFile("MemoryBufferTest_NullTerminator4K", "temp", + TestFD, TestPath); + raw_fd_ostream OF(TestFD, true, /*unbuffered=*/true); + for (unsigned i = 0; i < 4096 / 16; ++i) { + OF << "0123456789abcdef"; + } + OF.close(); + + OwningPtr<MemoryBuffer> MB; + error_code EC = MemoryBuffer::getFile(TestPath.c_str(), MB); + ASSERT_FALSE(EC); + + const char *BufData = MB->getBufferStart(); + EXPECT_EQ('f', BufData[4095]); + EXPECT_EQ('\0', BufData[4096]); +} + TEST_F(MemoryBufferTest, copy) { // copy with no name OwningBuffer MBC1(MemoryBuffer::getMemBufferCopy(data)); @@ -95,4 +126,46 @@ TEST_F(MemoryBufferTest, make_new) { EXPECT_EQ(0, Four->getBufferStart()[0]); } +void MemoryBufferTest::testGetOpenFileSlice(bool Reopen) { + // Test that MemoryBuffer::getOpenFile works properly when no null + // terminator is requested and the size is large enough to trigger + // the usage of memory mapping. + int TestFD; + SmallString<64> TestPath; + // Create a temporary file and write data into it. + sys::fs::createTemporaryFile("prefix", "temp", TestFD, 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. + raw_fd_ostream OF(TestFD, true, !Reopen); + for (int i = 0; i < 60000; ++i) { + OF << "0123456789"; + } + + if (Reopen) { + OF.close(); + EXPECT_FALSE(sys::fs::openFileForRead(TestPath.c_str(), TestFD)); + } + + OwningBuffer Buf; + error_code EC = MemoryBuffer::getOpenFileSlice(TestFD, TestPath.c_str(), Buf, + 40000, // Size + 80000 // Offset + ); + EXPECT_FALSE(EC); + + StringRef BufData = Buf->getBuffer(); + EXPECT_EQ(BufData.size(), 40000U); + EXPECT_EQ(BufData[0], '0'); + EXPECT_EQ(BufData[9], '9'); +} + +TEST_F(MemoryBufferTest, getOpenFileNoReopen) { + testGetOpenFileSlice(false); +} + +TEST_F(MemoryBufferTest, getOpenFileReopened) { + testGetOpenFileSlice(true); +} + } diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index eec8c62d8c841..031624162d0f1 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -7,9 +7,10 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Support/PathV2.h" +#include "llvm/Support/Path.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" @@ -140,6 +141,75 @@ TEST(Support, Path) { } } +TEST(Support, RelativePathIterator) { + SmallString<64> Path(StringRef("c/d/e/foo.txt")); + typedef SmallVector<StringRef, 4> PathComponents; + PathComponents ExpectedPathComponents; + PathComponents ActualPathComponents; + + StringRef(Path).split(ExpectedPathComponents, "/"); + + for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E; + ++I) { + ActualPathComponents.push_back(*I); + } + + ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size()); + + for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) { + EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str()); + } +} + +TEST(Support, AbsolutePathIterator) { + SmallString<64> Path(StringRef("/c/d/e/foo.txt")); + typedef SmallVector<StringRef, 4> PathComponents; + PathComponents ExpectedPathComponents; + PathComponents ActualPathComponents; + + StringRef(Path).split(ExpectedPathComponents, "/"); + + // The root path will also be a component when iterating + ExpectedPathComponents[0] = "/"; + + for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E; + ++I) { + ActualPathComponents.push_back(*I); + } + + ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size()); + + for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) { + EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str()); + } +} + +#ifdef LLVM_ON_WIN32 +TEST(Support, AbsolutePathIteratorWin32) { + SmallString<64> Path(StringRef("c:\\c\\e\\foo.txt")); + typedef SmallVector<StringRef, 4> PathComponents; + PathComponents ExpectedPathComponents; + PathComponents ActualPathComponents; + + StringRef(Path).split(ExpectedPathComponents, "\\"); + + // The root path (which comes after the drive name) will also be a component + // when iterating. + ExpectedPathComponents.insert(ExpectedPathComponents.begin()+1, "\\"); + + for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E; + ++I) { + ActualPathComponents.push_back(*I); + } + + ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size()); + + for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) { + EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str()); + } +} +#endif // LLVM_ON_WIN32 + class FileSystemTest : public testing::Test { protected: /// Unique temporary directory in which all created filesystem entities must @@ -147,13 +217,9 @@ protected: SmallString<128> TestDirectory; virtual void SetUp() { - int fd; ASSERT_NO_ERROR( - fs::unique_file("file-system-test-%%-%%-%%-%%/test-directory.anchor", fd, - TestDirectory)); + fs::createUniqueDirectory("file-system-test", TestDirectory)); // We don't care about this specific file. - ::close(fd); - TestDirectory = path::parent_path(TestDirectory); errs() << "Test Directory: " << TestDirectory << '\n'; errs().flush(); } @@ -164,12 +230,61 @@ protected: } }; +TEST_F(FileSystemTest, Unique) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + ASSERT_NO_ERROR( + fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + + // The same file should return an identical unique id. + fs::UniqueID F1, F2; + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F1)); + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F2)); + ASSERT_EQ(F1, F2); + + // Different files should return different unique ids. + int FileDescriptor2; + SmallString<64> TempPath2; + ASSERT_NO_ERROR( + fs::createTemporaryFile("prefix", "temp", FileDescriptor2, TempPath2)); + + fs::UniqueID D; + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D)); + ASSERT_NE(D, F1); + ::close(FileDescriptor2); + + ASSERT_NO_ERROR(fs::remove(Twine(TempPath2))); + + // Two paths representing the same file on disk should still provide the + // same unique id. We can test this by making a hard link. + ASSERT_NO_ERROR(fs::create_hard_link(Twine(TempPath), Twine(TempPath2))); + fs::UniqueID D2; + ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D2)); + ASSERT_EQ(D2, F1); + + ::close(FileDescriptor); + + SmallString<128> Dir1; + ASSERT_NO_ERROR( + fs::createUniqueDirectory("dir1", Dir1)); + ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F1)); + ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F2)); + ASSERT_EQ(F1, F2); + + SmallString<128> Dir2; + ASSERT_NO_ERROR( + fs::createUniqueDirectory("dir2", Dir2)); + ASSERT_NO_ERROR(fs::getUniqueID(Dir2.c_str(), F2)); + ASSERT_NE(F1, F2); +} + TEST_F(FileSystemTest, TempFiles) { // Create a temp file. int FileDescriptor; SmallString<64> TempPath; ASSERT_NO_ERROR( - fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath)); + fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); // Make sure it exists. bool TempFileExists; @@ -179,7 +294,8 @@ TEST_F(FileSystemTest, TempFiles) { // Create another temp tile. int FD2; SmallString<64> TempPath2; - ASSERT_NO_ERROR(fs::unique_file("%%-%%-%%-%%.temp", FD2, TempPath2)); + ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD2, TempPath2)); + ASSERT_TRUE(TempPath2.endswith(".temp")); ASSERT_NE(TempPath.str(), TempPath2.str()); fs::file_status A, B; @@ -187,22 +303,24 @@ TEST_F(FileSystemTest, TempFiles) { ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B)); EXPECT_FALSE(fs::equivalent(A, B)); - // Try to copy the first to the second. - EXPECT_EQ( - fs::copy_file(Twine(TempPath), Twine(TempPath2)), errc::file_exists); - ::close(FD2); - // Try again with the proper options. - ASSERT_NO_ERROR(fs::copy_file(Twine(TempPath), Twine(TempPath2), - fs::copy_option::overwrite_if_exists)); + // Remove Temp2. ASSERT_NO_ERROR(fs::remove(Twine(TempPath2), TempFileExists)); EXPECT_TRUE(TempFileExists); + error_code EC = fs::status(TempPath2.c_str(), B); + EXPECT_EQ(EC, errc::no_such_file_or_directory); + EXPECT_EQ(B.type(), fs::file_type::file_not_found); + // Make sure Temp2 doesn't exist. ASSERT_NO_ERROR(fs::exists(Twine(TempPath2), TempFileExists)); EXPECT_FALSE(TempFileExists); + SmallString<64> TempPath3; + ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "", TempPath3)); + ASSERT_FALSE(TempPath3.endswith(".")); + // Create a hard link to Temp1. ASSERT_NO_ERROR(fs::create_hard_link(Twine(TempPath), Twine(TempPath2))); bool equal; @@ -233,7 +351,7 @@ TEST_F(FileSystemTest, TempFiles) { "abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4" "abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2" "abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0"; - EXPECT_EQ(fs::unique_file(Twine(Path270), FileDescriptor, TempPath), + EXPECT_EQ(fs::createUniqueFile(Twine(Path270), FileDescriptor, TempPath), windows_error::path_not_found); #endif } @@ -298,7 +416,25 @@ TEST_F(FileSystemTest, DirectoryIteration) { ASSERT_LT(z0, za1); } -const char elf[] = {0x7f, 'E', 'L', 'F', 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; +const char archive[] = "!<arch>\x0A"; +const char bitcode[] = "\xde\xc0\x17\x0b"; +const char coff_object[] = "\x00\x00......"; +const char coff_import_library[] = "\x00\x00\xff\xff...."; +const char elf_relocatable[] = { 0x7f, 'E', 'L', 'F', 1, 2, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1 }; +const char macho_universal_binary[] = "\xca\xfe\xba\xbe...\0x00"; +const char macho_object[] = "\xfe\xed\xfa\xce..........\x00\x01"; +const char macho_executable[] = "\xfe\xed\xfa\xce..........\x00\x02"; +const char macho_fixed_virtual_memory_shared_lib[] = + "\xfe\xed\xfa\xce..........\x00\x03"; +const char macho_core[] = "\xfe\xed\xfa\xce..........\x00\x04"; +const char macho_preload_executable[] = "\xfe\xed\xfa\xce..........\x00\x05"; +const char macho_dynamically_linked_shared_lib[] = + "\xfe\xed\xfa\xce..........\x00\x06"; +const char macho_dynamic_linker[] = "\xfe\xed\xfa\xce..........\x00\x07"; +const char macho_bundle[] = "\xfe\xed\xfa\xce..........\x00\x08"; +const char macho_dsym_companion[] = "\xfe\xed\xfa\xce..........\x00\x0a"; +const char windows_resource[] = "\x00\x00\x00\x00\x020\x00\x00\x00\xff"; TEST_F(FileSystemTest, Magic) { struct type { @@ -306,11 +442,27 @@ TEST_F(FileSystemTest, Magic) { const char *magic_str; size_t magic_str_len; fs::file_magic magic; - } types [] = { - {"magic.archive", "!<arch>\x0A", 8, fs::file_magic::archive}, - {"magic.elf", elf, sizeof(elf), - fs::file_magic::elf_relocatable} - }; + } types[] = { +#define DEFINE(magic) \ + { #magic, magic, sizeof(magic), fs::file_magic::magic } + DEFINE(archive), + DEFINE(bitcode), + DEFINE(coff_object), + DEFINE(coff_import_library), + DEFINE(elf_relocatable), + DEFINE(macho_universal_binary), + DEFINE(macho_object), + DEFINE(macho_executable), + DEFINE(macho_fixed_virtual_memory_shared_lib), + DEFINE(macho_core), + DEFINE(macho_preload_executable), + DEFINE(macho_dynamically_linked_shared_lib), + DEFINE(macho_dynamic_linker), + DEFINE(macho_bundle), + DEFINE(macho_dsym_companion), + DEFINE(windows_resource) +#undef DEFINE + }; // Create some files filled with magic. for (type *i = types, *e = types + (sizeof(types) / sizeof(type)); i != e; @@ -318,8 +470,7 @@ TEST_F(FileSystemTest, Magic) { SmallString<128> file_pathname(TestDirectory); path::append(file_pathname, i->filename); std::string ErrMsg; - raw_fd_ostream file(file_pathname.c_str(), ErrMsg, - raw_fd_ostream::F_Binary); + raw_fd_ostream file(file_pathname.c_str(), ErrMsg, sys::fs::F_Binary); ASSERT_FALSE(file.has_error()); StringRef magic(i->magic_str, i->magic_str_len); file << magic; @@ -331,31 +482,33 @@ TEST_F(FileSystemTest, Magic) { } } -#if !defined(_WIN32) // FIXME: Win32 has different permission schema. -TEST_F(FileSystemTest, Permissions) { - // Create a temp file. - int FileDescriptor; - SmallString<64> TempPath; - ASSERT_NO_ERROR( - fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath)); - - // Mark file as read-only - const fs::perms AllWrite = fs::owner_write|fs::group_write|fs::others_write; - ASSERT_NO_ERROR(fs::permissions(Twine(TempPath), fs::remove_perms|AllWrite)); - - // Verify file is read-only - fs::file_status Status; - ASSERT_NO_ERROR(fs::status(Twine(TempPath), Status)); - bool AnyWriteBits = (Status.permissions() & AllWrite); - EXPECT_FALSE(AnyWriteBits); - - // Mark file as read-write - ASSERT_NO_ERROR(fs::permissions(Twine(TempPath), fs::add_perms|AllWrite)); - - // Verify file is read-write - ASSERT_NO_ERROR(fs::status(Twine(TempPath), Status)); - AnyWriteBits = (Status.permissions() & AllWrite); - EXPECT_TRUE(AnyWriteBits); +#ifdef LLVM_ON_WIN32 +TEST_F(FileSystemTest, CarriageReturn) { + SmallString<128> FilePathname(TestDirectory); + std::string ErrMsg; + path::append(FilePathname, "test"); + + { + raw_fd_ostream File(FilePathname.c_str(), ErrMsg); + EXPECT_EQ(ErrMsg, ""); + File << '\n'; + } + { + OwningPtr<MemoryBuffer> Buf; + MemoryBuffer::getFile(FilePathname.c_str(), Buf); + EXPECT_EQ(Buf->getBuffer(), "\r\n"); + } + + { + raw_fd_ostream File(FilePathname.c_str(), ErrMsg, sys::fs::F_Binary); + EXPECT_EQ(ErrMsg, ""); + File << '\n'; + } + { + OwningPtr<MemoryBuffer> Buf; + MemoryBuffer::getFile(FilePathname.c_str(), Buf); + EXPECT_EQ(Buf->getBuffer(), "\n"); + } } #endif @@ -364,7 +517,7 @@ TEST_F(FileSystemTest, FileMapping) { int FileDescriptor; SmallString<64> TempPath; ASSERT_NO_ERROR( - fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath)); + fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); // Map in temp file and add some content error_code EC; StringRef Val("hello there"); @@ -381,7 +534,7 @@ TEST_F(FileSystemTest, FileMapping) { mfr.data()[Val.size()] = 0; // Unmap temp file } - + // Map it back in read-only fs::mapped_file_region mfr(Twine(TempPath), fs::mapped_file_region::readonly, @@ -389,10 +542,10 @@ TEST_F(FileSystemTest, FileMapping) { 0, EC); ASSERT_NO_ERROR(EC); - + // Verify content EXPECT_EQ(StringRef(mfr.const_data()), Val); - + // Unmap temp file #if LLVM_HAS_RVALUE_REFERENCES diff --git a/unittests/Support/ProcessTest.cpp b/unittests/Support/ProcessTest.cpp index e57c0e6eaf81a..27c2318215d6b 100644 --- a/unittests/Support/ProcessTest.cpp +++ b/unittests/Support/ProcessTest.cpp @@ -26,7 +26,7 @@ TEST(ProcessTest, SelfProcess) { #if defined(LLVM_ON_UNIX) EXPECT_EQ(getpid(), process::get_self()->get_id()); #elif defined(LLVM_ON_WIN32) - EXPECT_EQ(GetCurrentProcess(), process::get_self()->get_id()); + EXPECT_EQ(GetCurrentProcessId(), process::get_self()->get_id()); #endif EXPECT_LT(1u, process::get_self()->page_size()); @@ -39,4 +39,32 @@ TEST(ProcessTest, SelfProcess) { EXPECT_GT(TimeValue::MaxTime, process::get_self()->get_wall_time()); } +#ifdef _MSC_VER +#define setenv(name, var, ignore) _putenv_s(name, var) +#endif + +#if HAVE_SETENV || _MSC_VER +TEST(ProcessTest, Basic) { + setenv("__LLVM_TEST_ENVIRON_VAR__", "abc", true); + Optional<std::string> val(Process::GetEnv("__LLVM_TEST_ENVIRON_VAR__")); + EXPECT_TRUE(val.hasValue()); + EXPECT_STREQ("abc", val->c_str()); +} + +TEST(ProcessTest, None) { + Optional<std::string> val( + Process::GetEnv("__LLVM_TEST_ENVIRON_NO_SUCH_VAR__")); + EXPECT_FALSE(val.hasValue()); +} +#endif + +#ifdef LLVM_ON_WIN32 +TEST(ProcessTest, Wchar) { + SetEnvironmentVariableW(L"__LLVM_TEST_ENVIRON_VAR__", L"abcdefghijklmnopqrs"); + Optional<std::string> val(Process::GetEnv("__LLVM_TEST_ENVIRON_VAR__")); + EXPECT_TRUE(val.hasValue()); + EXPECT_STREQ("abcdefghijklmnopqrs", val->c_str()); +} +#endif + } // end anonymous namespace diff --git a/unittests/Support/ProgramTest.cpp b/unittests/Support/ProgramTest.cpp index 6cbb05454f804..6eb990f29d2a0 100644 --- a/unittests/Support/ProgramTest.cpp +++ b/unittests/Support/ProgramTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include "gtest/gtest.h" @@ -20,6 +21,20 @@ extern char **environ; #endif +#if defined(LLVM_ON_UNIX) +#include <unistd.h> +void sleep_for(unsigned int seconds) { + sleep(seconds); +} +#elif defined(LLVM_ON_WIN32) +#include <windows.h> +void sleep_for(unsigned int seconds) { + Sleep(seconds * 1000); +} +#else +#error sleep_for is not implemented on your platform. +#endif + // From TestMain.cpp. extern const char *TestMainArgv0; @@ -55,10 +70,11 @@ TEST(ProgramTest, CreateProcessTrailingSlash) { exit(1); } - Path my_exe = Path::GetMainExecutable(TestMainArgv0, &ProgramTestStringArg1); + std::string my_exe = + sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); const char *argv[] = { my_exe.c_str(), - "--gtest_filter=ProgramTest.CreateProcessTrailingSlashChild", + "--gtest_filter=ProgramTest.CreateProcessTrailingSlash", "-program-test-string-arg1", "has\\\\ trailing\\", "-program-test-string-arg2", "has\\\\ trailing\\", 0 @@ -74,16 +90,105 @@ TEST(ProgramTest, CreateProcessTrailingSlash) { bool ExecutionFailed; // Redirect stdout and stdin to NUL, but let stderr through. #ifdef LLVM_ON_WIN32 - Path nul("NUL"); + StringRef nul("NUL"); #else - Path nul("/dev/null"); + StringRef nul("/dev/null"); #endif - const Path *redirects[] = { &nul, &nul, 0 }; - int rc = Program::ExecuteAndWait(my_exe, argv, &envp[0], redirects, - /*secondsToWait=*/10, /*memoryLimit=*/0, - &error, &ExecutionFailed); + const StringRef *redirects[] = { &nul, &nul, 0 }; + int rc = ExecuteAndWait(my_exe, argv, &envp[0], redirects, + /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &error, + &ExecutionFailed); EXPECT_FALSE(ExecutionFailed) << error; EXPECT_EQ(0, rc); } +TEST(ProgramTest, TestExecuteNoWait) { + using namespace llvm::sys; + + if (getenv("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT")) { + sleep_for(/*seconds*/ 1); + exit(0); + } + + std::string Executable = + sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); + const char *argv[] = { + Executable.c_str(), + "--gtest_filter=ProgramTest.TestExecuteNoWait", + 0 + }; + + // Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child. + std::vector<const char *> envp; + CopyEnvironment(envp); + envp.push_back("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1"); + envp.push_back(0); + + std::string Error; + bool ExecutionFailed; + ProcessInfo PI1 = + ExecuteNoWait(Executable, argv, &envp[0], 0, 0, &Error, &ExecutionFailed); + ASSERT_FALSE(ExecutionFailed) << Error; + ASSERT_NE(PI1.Pid, 0) << "Invalid process id"; + + unsigned LoopCount = 0; + + // Test that Wait() with WaitUntilTerminates=true works. In this case, + // LoopCount should only be incremented once. + while (true) { + ++LoopCount; + ProcessInfo WaitResult = Wait(PI1, 0, true, &Error); + ASSERT_TRUE(Error.empty()); + if (WaitResult.Pid == PI1.Pid) + break; + } + + EXPECT_EQ(LoopCount, 1u) << "LoopCount should be 1"; + + ProcessInfo PI2 = + ExecuteNoWait(Executable, argv, &envp[0], 0, 0, &Error, &ExecutionFailed); + ASSERT_FALSE(ExecutionFailed) << Error; + ASSERT_NE(PI2.Pid, 0) << "Invalid process id"; + + // Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this + // cse, LoopCount should be greater than 1 (more than one increment occurs). + while (true) { + ++LoopCount; + ProcessInfo WaitResult = Wait(PI2, 0, false, &Error); + ASSERT_TRUE(Error.empty()); + if (WaitResult.Pid == PI2.Pid) + break; + } + + ASSERT_GT(LoopCount, 1u) << "LoopCount should be >1"; +} + +TEST(ProgramTest, TestExecuteNegative) { + std::string Executable = "i_dont_exist"; + const char *argv[] = { Executable.c_str(), 0 }; + + { + std::string Error; + bool ExecutionFailed; + int RetCode = + ExecuteAndWait(Executable, argv, 0, 0, 0, 0, &Error, &ExecutionFailed); + ASSERT_TRUE(RetCode < 0) << "On error ExecuteAndWait should return 0 or " + "positive value indicating the result code"; + ASSERT_TRUE(ExecutionFailed); + ASSERT_FALSE(Error.empty()); + } + + { + std::string Error; + bool ExecutionFailed; + ProcessInfo PI = + ExecuteNoWait(Executable, argv, 0, 0, 0, &Error, &ExecutionFailed); + ASSERT_EQ(PI.Pid, 0) + << "On error ExecuteNoWait should return an invalid ProcessInfo"; + ASSERT_TRUE(ExecutionFailed); + ASSERT_FALSE(Error.empty()); + } + +} + } // end anonymous namespace diff --git a/unittests/Support/RegexTest.cpp b/unittests/Support/RegexTest.cpp index 3577d1015e915..7b977f7446681 100644 --- a/unittests/Support/RegexTest.cpp +++ b/unittests/Support/RegexTest.cpp @@ -112,4 +112,27 @@ TEST_F(RegexTest, Substitution) { EXPECT_EQ(Error, "invalid backreference string '100'"); } +TEST_F(RegexTest, IsLiteralERE) { + EXPECT_TRUE(Regex::isLiteralERE("abc")); + EXPECT_FALSE(Regex::isLiteralERE("a(bc)")); + EXPECT_FALSE(Regex::isLiteralERE("^abc")); + EXPECT_FALSE(Regex::isLiteralERE("abc$")); + EXPECT_FALSE(Regex::isLiteralERE("a|bc")); + EXPECT_FALSE(Regex::isLiteralERE("abc*")); + EXPECT_FALSE(Regex::isLiteralERE("abc+")); + EXPECT_FALSE(Regex::isLiteralERE("abc?")); + EXPECT_FALSE(Regex::isLiteralERE("abc.")); + EXPECT_FALSE(Regex::isLiteralERE("a[bc]")); + EXPECT_FALSE(Regex::isLiteralERE("abc\\1")); + EXPECT_FALSE(Regex::isLiteralERE("abc{1,2}")); +} + +TEST_F(RegexTest, IsValid) { + std::string Error; + EXPECT_FALSE(Regex("(foo").isValid(Error)); + EXPECT_EQ("parentheses not balanced", Error); + EXPECT_FALSE(Regex("a[b-").isValid(Error)); + EXPECT_EQ("invalid character range", Error); +} + } diff --git a/unittests/Support/SourceMgrTest.cpp b/unittests/Support/SourceMgrTest.cpp new file mode 100644 index 0000000000000..2b69fe984445e --- /dev/null +++ b/unittests/Support/SourceMgrTest.cpp @@ -0,0 +1,174 @@ +//===- unittests/Support/SourceMgrTest.cpp - SourceMgr 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/SourceMgr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class SourceMgrTest : public testing::Test { +public: + SourceMgr SM; + unsigned MainBufferID; + std::string Output; + + void setMainBuffer(StringRef Text, StringRef BufferName) { + MemoryBuffer *MainBuffer = MemoryBuffer::getMemBuffer(Text, BufferName); + MainBufferID = SM.AddNewSourceBuffer(MainBuffer, llvm::SMLoc()); + } + + SMLoc getLoc(unsigned Offset) { + return SMLoc::getFromPointer( + SM.getMemoryBuffer(MainBufferID)->getBufferStart() + Offset); + } + + SMRange getRange(unsigned Offset, unsigned Length) { + return SMRange(getLoc(Offset), getLoc(Offset + Length)); + } + + void printMessage(SMLoc Loc, SourceMgr::DiagKind Kind, + const Twine &Msg, ArrayRef<SMRange> Ranges, + ArrayRef<SMFixIt> FixIts) { + raw_string_ostream OS(Output); + SM.PrintMessage(OS, Loc, Kind, Msg, Ranges, FixIts); + } +}; + +} // unnamed namespace + +TEST_F(SourceMgrTest, BasicError) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:1:5: error: message\n" + "aaa bbb\n" + " ^\n", + Output); +} + +TEST_F(SourceMgrTest, BasicWarning) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Warning, "message", None, None); + + EXPECT_EQ("file.in:1:5: warning: message\n" + "aaa bbb\n" + " ^\n", + Output); +} + +TEST_F(SourceMgrTest, BasicNote) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Note, "message", None, None); + + EXPECT_EQ("file.in:1:5: note: message\n" + "aaa bbb\n" + " ^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationAtEndOfLine) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(6), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:1:7: error: message\n" + "aaa bbb\n" + " ^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationAtNewline) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(7), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:1:8: error: message\n" + "aaa bbb\n" + " ^\n", + Output); +} + +TEST_F(SourceMgrTest, BasicRange) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(4, 3), None); + + EXPECT_EQ("file.in:1:5: error: message\n" + "aaa bbb\n" + " ^~~\n", + Output); +} + +TEST_F(SourceMgrTest, RangeWithTab) { + setMainBuffer("aaa\tbbb\nccc ddd\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(3, 3), None); + + EXPECT_EQ("file.in:1:5: error: message\n" + "aaa bbb\n" + " ~~~~~^~\n", + Output); +} + +TEST_F(SourceMgrTest, MultiLineRange) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(4, 7), None); + + EXPECT_EQ("file.in:1:5: error: message\n" + "aaa bbb\n" + " ^~~\n", + Output); +} + +TEST_F(SourceMgrTest, MultipleRanges) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + SMRange Ranges[] = { getRange(0, 3), getRange(4, 3) }; + printMessage(getLoc(4), SourceMgr::DK_Error, "message", Ranges, None); + + EXPECT_EQ("file.in:1:5: error: message\n" + "aaa bbb\n" + "~~~ ^~~\n", + Output); +} + +TEST_F(SourceMgrTest, OverlappingRanges) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + SMRange Ranges[] = { getRange(0, 3), getRange(2, 4) }; + printMessage(getLoc(4), SourceMgr::DK_Error, "message", Ranges, None); + + EXPECT_EQ("file.in:1:5: error: message\n" + "aaa bbb\n" + "~~~~^~\n", + Output); +} + +TEST_F(SourceMgrTest, BasicFixit) { + setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Error, "message", None, + makeArrayRef(SMFixIt(getRange(4, 3), "zzz"))); + + EXPECT_EQ("file.in:1:5: error: message\n" + "aaa bbb\n" + " ^~~\n" + " zzz\n", + Output); +} + +TEST_F(SourceMgrTest, FixitForTab) { + setMainBuffer("aaa\tbbb\nccc ddd\n", "file.in"); + printMessage(getLoc(3), SourceMgr::DK_Error, "message", None, + makeArrayRef(SMFixIt(getRange(3, 1), "zzz"))); + + EXPECT_EQ("file.in:1:4: error: message\n" + "aaa bbb\n" + " ^^^^^\n" + " zzz\n", + Output); +} + diff --git a/unittests/Support/ThreadLocalTest.cpp b/unittests/Support/ThreadLocalTest.cpp new file mode 100644 index 0000000000000..dd4d706f5cf52 --- /dev/null +++ b/unittests/Support/ThreadLocalTest.cpp @@ -0,0 +1,38 @@ +//===- llvm/unittest/Support/ThreadLocalTest.cpp - Therad Local 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/ThreadLocal.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace sys; + +namespace { + +class ThreadLocalTest : public ::testing::Test { +}; + +struct S { + int i; +}; + +TEST_F(ThreadLocalTest, Basics) { + ThreadLocal<const S> x; + + EXPECT_EQ(0, x.get()); + + S s; + x.set(&s); + EXPECT_EQ(&s, x.get()); + + x.erase(); + EXPECT_EQ(0, x.get()); +} + +} diff --git a/unittests/Support/TimeValue.cpp b/unittests/Support/TimeValue.cpp deleted file mode 100644 index 27883ae335640..0000000000000 --- a/unittests/Support/TimeValue.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//===- llvm/unittest/Support/TimeValue.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(Support, TimeValue) { - sys::TimeValue now = sys::TimeValue::now(); - time_t now_t = time(NULL); - EXPECT_TRUE(abs(static_cast<long>(now_t - now.toEpochTime())) < 2); -} - -} diff --git a/unittests/Support/TimeValueTest.cpp b/unittests/Support/TimeValueTest.cpp new file mode 100644 index 0000000000000..fb2abe382932f --- /dev/null +++ b/unittests/Support/TimeValueTest.cpp @@ -0,0 +1,39 @@ +//===- 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(NULL); + EXPECT_TRUE(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(ns / 100, epoch.toPosixTime()); + + // Confirm it reversible. + EXPECT_EQ(ft1970, epoch.toWin32Time()); +} + +} diff --git a/unittests/Support/UnicodeTest.cpp b/unittests/Support/UnicodeTest.cpp new file mode 100644 index 0000000000000..0733397d45c66 --- /dev/null +++ b/unittests/Support/UnicodeTest.cpp @@ -0,0 +1,93 @@ +//===- unittests/Support/UnicodeTest.cpp - Unicode.h tests ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Unicode.h" +#include "gtest/gtest.h" + +namespace llvm { +namespace sys { +namespace unicode { +namespace { + +TEST(Unicode, columnWidthUTF8) { + EXPECT_EQ(0, columnWidthUTF8("")); + EXPECT_EQ(1, columnWidthUTF8(" ")); + EXPECT_EQ(1, columnWidthUTF8("a")); + EXPECT_EQ(1, columnWidthUTF8("~")); + + EXPECT_EQ(6, columnWidthUTF8("abcdef")); + + EXPECT_EQ(-1, columnWidthUTF8("\x01")); + EXPECT_EQ(-1, columnWidthUTF8("aaaaaaaaaa\x01")); + EXPECT_EQ(-1, columnWidthUTF8("\342\200\213")); // 200B ZERO WIDTH SPACE + + // 00AD SOFT HYPHEN is displayed on most terminals as a space or a dash. Some + // text editors display it only when a line is broken at it, some use it as a + // line-break hint, but don't display. We choose terminal-oriented + // interpretation. + EXPECT_EQ(1, columnWidthUTF8("\302\255")); + + EXPECT_EQ(0, columnWidthUTF8("\314\200")); // 0300 COMBINING GRAVE ACCENT + EXPECT_EQ(1, columnWidthUTF8("\340\270\201")); // 0E01 THAI CHARACTER KO KAI + EXPECT_EQ(2, columnWidthUTF8("\344\270\200")); // CJK UNIFIED IDEOGRAPH-4E00 + + EXPECT_EQ(4, columnWidthUTF8("\344\270\200\344\270\200")); + EXPECT_EQ(3, columnWidthUTF8("q\344\270\200")); + EXPECT_EQ(3, columnWidthUTF8("\314\200\340\270\201\344\270\200")); + + // Invalid UTF-8 strings, columnWidthUTF8 should error out. + EXPECT_EQ(-2, columnWidthUTF8("\344")); + EXPECT_EQ(-2, columnWidthUTF8("\344\270")); + EXPECT_EQ(-2, columnWidthUTF8("\344\270\033")); + EXPECT_EQ(-2, columnWidthUTF8("\344\270\300")); + EXPECT_EQ(-2, columnWidthUTF8("\377\366\355")); + + EXPECT_EQ(-2, columnWidthUTF8("qwer\344")); + EXPECT_EQ(-2, columnWidthUTF8("qwer\344\270")); + EXPECT_EQ(-2, columnWidthUTF8("qwer\344\270\033")); + EXPECT_EQ(-2, columnWidthUTF8("qwer\344\270\300")); + EXPECT_EQ(-2, columnWidthUTF8("qwer\377\366\355")); + + // UTF-8 sequences longer than 4 bytes correspond to unallocated Unicode + // characters. + EXPECT_EQ(-2, columnWidthUTF8("\370\200\200\200\200")); // U+200000 + EXPECT_EQ(-2, columnWidthUTF8("\374\200\200\200\200\200")); // U+4000000 +} + +TEST(Unicode, isPrintable) { + EXPECT_FALSE(isPrintable(0)); // <control-0000>-<control-001F> + EXPECT_FALSE(isPrintable(0x01)); + EXPECT_FALSE(isPrintable(0x1F)); + EXPECT_TRUE(isPrintable(' ')); + EXPECT_TRUE(isPrintable('A')); + EXPECT_TRUE(isPrintable('~')); + EXPECT_FALSE(isPrintable(0x7F)); // <control-007F>..<control-009F> + EXPECT_FALSE(isPrintable(0x90)); + EXPECT_FALSE(isPrintable(0x9F)); + + EXPECT_TRUE(isPrintable(0xAC)); + EXPECT_TRUE(isPrintable(0xAD)); // SOFT HYPHEN is displayed on most terminals + // as either a space or a dash. + EXPECT_TRUE(isPrintable(0xAE)); + + EXPECT_TRUE(isPrintable(0x0377)); // GREEK SMALL LETTER PAMPHYLIAN DIGAMMA + EXPECT_FALSE(isPrintable(0x0378)); // <reserved-0378>..<reserved-0379> + + EXPECT_FALSE(isPrintable(0x0600)); // ARABIC NUMBER SIGN + + EXPECT_FALSE(isPrintable(0x1FFFF)); // <reserved-1F774>..<noncharacter-1FFFF> + EXPECT_TRUE(isPrintable(0x20000)); // CJK UNIFIED IDEOGRAPH-20000 + + EXPECT_FALSE(isPrintable(0x10FFFF)); // noncharacter +} + +} // namespace +} // namespace unicode +} // namespace sys +} // namespace llvm diff --git a/unittests/Support/YAMLIOTest.cpp b/unittests/Support/YAMLIOTest.cpp index 0993d8c0b555e..6c0b9e6151f20 100644 --- a/unittests/Support/YAMLIOTest.cpp +++ b/unittests/Support/YAMLIOTest.cpp @@ -58,14 +58,24 @@ namespace yaml { // TEST(YAMLIO, TestMapRead) { FooBar doc; - Input yin("---\nfoo: 3\nbar: 5\n...\n"); - yin >> doc; + { + Input yin("---\nfoo: 3\nbar: 5\n...\n"); + yin >> doc; - EXPECT_FALSE(yin.error()); - EXPECT_EQ(doc.foo, 3); - EXPECT_EQ(doc.bar,5); -} + EXPECT_FALSE(yin.error()); + EXPECT_EQ(doc.foo, 3); + EXPECT_EQ(doc.bar, 5); + } + { + Input yin("{foo: 3, bar: 5}"); + yin >> doc; + + EXPECT_FALSE(yin.error()); + EXPECT_EQ(doc.foo, 3); + EXPECT_EQ(doc.bar, 5); + } +} // // Test the reading of a yaml sequence of mappings @@ -273,7 +283,64 @@ TEST(YAMLIO, TestReadWriteBuiltInTypes) { } } +struct StringTypes { + llvm::StringRef str1; + llvm::StringRef str2; + llvm::StringRef str3; + llvm::StringRef str4; + llvm::StringRef str5; +}; + +namespace llvm { +namespace yaml { + template <> + struct MappingTraits<StringTypes> { + static void mapping(IO &io, StringTypes& st) { + io.mapRequired("str1", st.str1); + io.mapRequired("str2", st.str2); + io.mapRequired("str3", st.str3); + io.mapRequired("str4", st.str4); + io.mapRequired("str5", st.str5); + } + }; +} +} + +TEST(YAMLIO, TestReadWriteStringTypes) { + std::string intermediate; + { + StringTypes map; + map.str1 = "'aaa"; + map.str2 = "\"bbb"; + map.str3 = "`ccc"; + map.str4 = "@ddd"; + map.str5 = ""; + + llvm::raw_string_ostream ostr(intermediate); + Output yout(ostr); + yout << map; + } + llvm::StringRef flowOut(intermediate); + EXPECT_NE(llvm::StringRef::npos, flowOut.find("'''aaa")); + EXPECT_NE(llvm::StringRef::npos, flowOut.find("'\"bbb'")); + EXPECT_NE(llvm::StringRef::npos, flowOut.find("'`ccc'")); + EXPECT_NE(llvm::StringRef::npos, flowOut.find("'@ddd'")); + EXPECT_NE(llvm::StringRef::npos, flowOut.find("''\n")); + + { + Input yin(intermediate); + StringTypes map; + yin >> map; + + EXPECT_FALSE(yin.error()); + EXPECT_TRUE(map.str1.equals("'aaa")); + EXPECT_TRUE(map.str2.equals("\"bbb")); + EXPECT_TRUE(map.str3.equals("`ccc")); + EXPECT_TRUE(map.str4.equals("@ddd")); + EXPECT_TRUE(map.str5.equals("")); + } +} //===----------------------------------------------------------------------===// // Test ScalarEnumerationTraits @@ -600,9 +667,9 @@ TEST(YAMLIO, TestReadWriteMyFlowSequence) { map.numbers.push_back(1024); llvm::raw_string_ostream ostr(intermediate); - Output yout(ostr); + Output yout(ostr); yout << map; - + // Verify sequences were written in flow style ostr.flush(); llvm::StringRef flowOut(intermediate); @@ -932,6 +999,161 @@ TEST(YAMLIO, TestSequenceDocListWriteAndRead) { } } +//===----------------------------------------------------------------------===// +// Test document tags +//===----------------------------------------------------------------------===// + +struct MyDouble { + MyDouble() : value(0.0) { } + MyDouble(double x) : value(x) { } + double value; +}; + +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyDouble) + + +namespace llvm { +namespace yaml { + template <> + struct MappingTraits<MyDouble> { + static void mapping(IO &io, MyDouble &d) { + if (io.mapTag("!decimal", true)) { + mappingDecimal(io, d); + } else if (io.mapTag("!fraction")) { + mappingFraction(io, d); + } + } + static void mappingDecimal(IO &io, MyDouble &d) { + io.mapRequired("value", d.value); + } + static void mappingFraction(IO &io, MyDouble &d) { + double num, denom; + io.mapRequired("numerator", num); + io.mapRequired("denominator", denom); + // convert fraction to double + d.value = num/denom; + } + }; + } +} + + +// +// Test the reading of two different tagged yaml documents. +// +TEST(YAMLIO, TestTaggedDocuments) { + std::vector<MyDouble> docList; + Input yin("--- !decimal\nvalue: 3.0\n" + "--- !fraction\nnumerator: 9.0\ndenominator: 2\n...\n"); + yin >> docList; + EXPECT_FALSE(yin.error()); + EXPECT_EQ(docList.size(), 2UL); + EXPECT_EQ(docList[0].value, 3.0); + EXPECT_EQ(docList[1].value, 4.5); +} + + + +// +// Test writing then reading back tagged documents +// +TEST(YAMLIO, TestTaggedDocumentsWriteAndRead) { + std::string intermediate; + { + MyDouble a(10.25); + MyDouble b(-3.75); + std::vector<MyDouble> docList; + docList.push_back(a); + docList.push_back(b); + + llvm::raw_string_ostream ostr(intermediate); + Output yout(ostr); + yout << docList; + } + + { + Input yin(intermediate); + std::vector<MyDouble> docList2; + yin >> docList2; + + EXPECT_FALSE(yin.error()); + EXPECT_EQ(docList2.size(), 2UL); + EXPECT_EQ(docList2[0].value, 10.25); + EXPECT_EQ(docList2[1].value, -3.75); + } +} + + +//===----------------------------------------------------------------------===// +// Test dyn_cast<> on IO object +//===----------------------------------------------------------------------===// + +struct DynCast { + int value; +}; +typedef std::vector<DynCast> DynCastSequence; + +LLVM_YAML_IS_SEQUENCE_VECTOR(DynCast) + +namespace llvm { +namespace yaml { + template <> + struct MappingTraits<DynCast> { + static void mapping(IO &io, DynCast& info) { + // Change 10 to 13 when writing yaml. + if (Output *output = dyn_cast<Output>(&io)) { + (void)output; + if (info.value == 10) + info.value = 13; + } + io.mapRequired("value", info.value); + // Change 20 to 23 when parsing yaml. + if (Input *input = dyn_cast<Input>(&io)) { + (void)input; + if (info.value == 20) + info.value = 23; + } + } + }; +} +} + +// +// Test writing then reading back a sequence of mappings +// +TEST(YAMLIO, TestDynCast) { + std::string intermediate; + { + DynCast entry1; + entry1.value = 10; + DynCast entry2; + entry2.value = 20; + DynCast entry3; + entry3.value = 30; + DynCastSequence seq; + seq.push_back(entry1); + seq.push_back(entry2); + seq.push_back(entry3); + + llvm::raw_string_ostream ostr(intermediate); + Output yout(ostr); + yout << seq; + } + + { + Input yin(intermediate); + DynCastSequence seq2; + yin >> seq2; + + EXPECT_FALSE(yin.error()); + EXPECT_EQ(seq2.size(), 3UL); + EXPECT_EQ(seq2[0].value, 13); // Verify changed to 13. + EXPECT_EQ(seq2[1].value, 23); // Verify changed to 23. + EXPECT_EQ(seq2[2].value, 30); // Verify stays same. + } +} + + //===----------------------------------------------------------------------===// // Test error handling @@ -952,8 +1174,9 @@ TEST(YAMLIO, TestColorsReadError) { "c1: blue\n" "c2: purple\n" "c3: green\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> map; EXPECT_TRUE(yin.error()); } @@ -968,8 +1191,9 @@ TEST(YAMLIO, TestFlagsReadError) { "f1: [ big ]\n" "f2: [ round, hollow ]\n" "f3: []\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> map; EXPECT_TRUE(yin.error()); @@ -986,8 +1210,9 @@ TEST(YAMLIO, TestReadBuiltInTypesUint8Error) { "- 255\n" "- 0\n" "- 257\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1004,8 +1229,9 @@ TEST(YAMLIO, TestReadBuiltInTypesUint16Error) { "- 65535\n" "- 0\n" "- 66000\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1022,8 +1248,9 @@ TEST(YAMLIO, TestReadBuiltInTypesUint32Error) { "- 4000000000\n" "- 0\n" "- 5000000000\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1040,8 +1267,9 @@ TEST(YAMLIO, TestReadBuiltInTypesUint64Error) { "- 18446744073709551615\n" "- 0\n" "- 19446744073709551615\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1059,8 +1287,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint8OverError) { "- 0\n" "- 127\n" "- 128\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1076,8 +1305,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint8UnderError) { "- 0\n" "- 127\n" "- -129\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1095,8 +1325,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint16UnderError) { "- 0\n" "- -32768\n" "- -32769\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1113,8 +1344,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint16OverError) { "- 0\n" "- -32768\n" "- 32768\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1132,8 +1364,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint32UnderError) { "- 0\n" "- -2147483648\n" "- -2147483649\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1149,8 +1382,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint32OverError) { "- 0\n" "- -2147483648\n" "- 2147483649\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1168,8 +1402,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint64UnderError) { "- 0\n" "- 9223372036854775807\n" "- -9223372036854775809\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1185,8 +1420,9 @@ TEST(YAMLIO, TestReadBuiltInTypesint64OverError) { "- 0\n" "- 9223372036854775807\n" "- 9223372036854775809\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1203,8 +1439,9 @@ TEST(YAMLIO, TestReadBuiltInTypesFloatError) { "- 1000.1\n" "- -123.456\n" "- 1.2.3\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1221,8 +1458,9 @@ TEST(YAMLIO, TestReadBuiltInTypesDoubleError) { "- 1000.1\n" "- -123.456\n" "- 1.2.3\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1238,8 +1476,9 @@ TEST(YAMLIO, TestReadBuiltInTypesHex8Error) { "- 0x12\n" "- 0xFE\n" "- 0x123\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1256,8 +1495,9 @@ TEST(YAMLIO, TestReadBuiltInTypesHex16Error) { "- 0x0012\n" "- 0xFEFF\n" "- 0x12345\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1273,8 +1513,9 @@ TEST(YAMLIO, TestReadBuiltInTypesHex32Error) { "- 0x0012\n" "- 0xFEFF0000\n" "- 0x1234556789\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); @@ -1290,10 +1531,114 @@ TEST(YAMLIO, TestReadBuiltInTypesHex64Error) { "- 0x0012\n" "- 0xFFEEDDCCBBAA9988\n" "- 0x12345567890ABCDEF0\n" - "...\n"); - yin.setDiagHandler(suppressErrorMessages); + "...\n", + /*Ctxt=*/NULL, + suppressErrorMessages); yin >> seq; EXPECT_TRUE(yin.error()); } +TEST(YAMLIO, TestMalformedMapFailsGracefully) { + FooBar doc; + { + // We pass the suppressErrorMessages handler to handle the error + // message generated in the constructor of Input. + Input yin("{foo:3, bar: 5}", /*Ctxt=*/NULL, suppressErrorMessages); + yin >> doc; + EXPECT_TRUE(yin.error()); + } + + { + Input yin("---\nfoo:3\nbar: 5\n...\n", /*Ctxt=*/NULL, suppressErrorMessages); + yin >> doc; + EXPECT_TRUE(yin.error()); + } +} + +struct OptionalTest { + std::vector<int> Numbers; +}; + +struct OptionalTestSeq { + std::vector<OptionalTest> Tests; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(OptionalTest) +namespace llvm { +namespace yaml { + template <> + struct MappingTraits<OptionalTest> { + static void mapping(IO& IO, OptionalTest &OT) { + IO.mapOptional("Numbers", OT.Numbers); + } + }; + + template <> + struct MappingTraits<OptionalTestSeq> { + static void mapping(IO &IO, OptionalTestSeq &OTS) { + IO.mapOptional("Tests", OTS.Tests); + } + }; +} +} + +TEST(YAMLIO, SequenceElideTest) { + // Test that writing out a purely optional structure with its fields set to + // default followed by other data is properly read back in. + OptionalTestSeq Seq; + OptionalTest One, Two, Three, Four; + int N[] = {1, 2, 3}; + Three.Numbers.assign(N, N + 3); + Seq.Tests.push_back(One); + Seq.Tests.push_back(Two); + Seq.Tests.push_back(Three); + Seq.Tests.push_back(Four); + + std::string intermediate; + { + llvm::raw_string_ostream ostr(intermediate); + Output yout(ostr); + yout << Seq; + } + + Input yin(intermediate); + OptionalTestSeq Seq2; + yin >> Seq2; + + EXPECT_FALSE(yin.error()); + + EXPECT_EQ(4UL, Seq2.Tests.size()); + + EXPECT_TRUE(Seq2.Tests[0].Numbers.empty()); + EXPECT_TRUE(Seq2.Tests[1].Numbers.empty()); + + EXPECT_EQ(1, Seq2.Tests[2].Numbers[0]); + EXPECT_EQ(2, Seq2.Tests[2].Numbers[1]); + EXPECT_EQ(3, Seq2.Tests[2].Numbers[2]); + + EXPECT_TRUE(Seq2.Tests[3].Numbers.empty()); +} + +TEST(YAMLIO, TestEmptyStringFailsForMapWithRequiredFields) { + FooBar doc; + Input yin(""); + yin >> doc; + EXPECT_TRUE(yin.error()); +} + +TEST(YAMLIO, TestEmptyStringSucceedsForMapWithOptionalFields) { + OptionalTest doc; + Input yin(""); + yin >> doc; + EXPECT_FALSE(yin.error()); +} + +TEST(YAMLIO, TestEmptyStringSucceedsForSequence) { + std::vector<uint8_t> seq; + Input yin("", /*Ctxt=*/NULL, suppressErrorMessages); + yin >> seq; + + EXPECT_FALSE(yin.error()); + EXPECT_TRUE(seq.empty()); +} diff --git a/unittests/Transforms/CMakeLists.txt b/unittests/Transforms/CMakeLists.txt index e3ce185e0d5b4..8ec56f10d9739 100644 --- a/unittests/Transforms/CMakeLists.txt +++ b/unittests/Transforms/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(DebugIR) add_subdirectory(Utils) diff --git a/unittests/Transforms/DebugIR/CMakeLists.txt b/unittests/Transforms/DebugIR/CMakeLists.txt new file mode 100644 index 0000000000000..4b471939ef1fa --- /dev/null +++ b/unittests/Transforms/DebugIR/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + Instrumentation + ) + +add_llvm_unittest(DebugIRTests + DebugIR.cpp + ) diff --git a/unittests/Transforms/DebugIR/DebugIR.cpp b/unittests/Transforms/DebugIR/DebugIR.cpp new file mode 100644 index 0000000000000..7d213fd0567ad --- /dev/null +++ b/unittests/Transforms/DebugIR/DebugIR.cpp @@ -0,0 +1,307 @@ +//===- DebugIR.cpp - Unit tests for the DebugIR pass ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The tests in this file verify the DebugIR pass that generates debug metadata +// for LLVM IR. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Triple.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo.h" +#include "llvm/DIBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Transforms/Instrumentation.h" + +#include "../lib/Transforms/Instrumentation/DebugIR.h" + +// These tests do not depend on MCJIT, but we use the TrivialModuleBuilder +// helper class to construct some trivial Modules. +#include "../unittests/ExecutionEngine/MCJIT/MCJITTestBase.h" + +#include <string> + +#include "gtest/gtest.h" + +#if defined(LLVM_ON_WIN32) +#include <direct.h> +#define getcwd_impl _getcwd +#elif defined (HAVE_GETCWD) +#include <unistd.h> +#define getcwd_impl getcwd +#endif // LLVM_ON_WIN32 + +using namespace llvm; +using namespace std; + +namespace { + +/// Insert a mock CUDescriptor with the specified producer +void insertCUDescriptor(Module *M, StringRef File, StringRef Dir, + StringRef Producer) { + DIBuilder B(*M); + B.createCompileUnit(dwarf::DW_LANG_C99, File, Dir, Producer, false, "", 0); + B.finalize(); +} + +/// Attempts to remove file at Path and returns true if it existed, or false if +/// it did not. +bool removeIfExists(StringRef Path) { + bool existed = false; + sys::fs::remove(Path, existed); + return existed; +} + +char * current_dir() { +#if defined(LLVM_ON_WIN32) || defined(HAVE_GETCWD) + // calling getcwd (or _getcwd() on windows) with a null buffer makes it + // allocate a sufficiently sized buffer to store the current working dir. + return getcwd_impl(0, 0); +#else + return 0; +#endif +} + +class TestDebugIR : public ::testing::Test, public TrivialModuleBuilder { +protected: + TestDebugIR() + : TrivialModuleBuilder(sys::getProcessTriple()) + , cwd(current_dir()) {} + + ~TestDebugIR() { free(cwd); } + + /// Returns a concatenated path string consisting of Dir and Filename + string getPath(const string &Dir, const string &Filename) { + SmallVector<char, 8> Path; + sys::path::append(Path, Dir, Filename); + Path.resize(Dir.size() + Filename.size() + 2); + Path[Dir.size() + Filename.size() + 1] = '\0'; + return string(Path.data()); + } + + LLVMContext Context; + char *cwd; + OwningPtr<Module> M; + OwningPtr<DebugIR> D; +}; + +// Test empty named Module that is not supposed to be output to disk. +TEST_F(TestDebugIR, EmptyNamedModuleNoWrite) { + string Dir = "MadeUpDirectory"; + string File = "empty_module.ll"; + string Path(getPath(Dir, File)); + + M.reset(createEmptyModule(Path)); + + // constructing DebugIR with no args should not result in any file generated. + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass())); + D->runOnModule(*M); + + // verify DebugIR did not generate a file + ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path; +} + +// Test a non-empty unnamed module that is output to an autogenerated file name. +TEST_F(TestDebugIR, NonEmptyUnnamedModuleWriteToAutogeneratedFile) { + M.reset(createEmptyModule()); + insertAddFunction(M.get()); + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true))); + + string Path; + D->runOnModule(*M, Path); + + // verify DebugIR generated a file, and clean it up + ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path; +} + +// Test not specifying a name in the module -- DebugIR should generate a name +// and write the file contents. +TEST_F(TestDebugIR, EmptyModuleWriteAnonymousFile) { + M.reset(createEmptyModule()); + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(false, false))); + + string Path; + D->runOnModule(*M, Path); + + // verify DebugIR generated a file and clean it up + ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path; +} + +#ifdef HAVE_GETCWD // These tests require get_current_dir_name() + +// Test empty named Module that is to be output to path specified at Module +// construction. +TEST_F(TestDebugIR, EmptyNamedModuleWriteFile) { + string Filename("NamedFile1"); + string ExpectedPath(getPath(cwd, Filename)); + + M.reset(createEmptyModule(ExpectedPath)); + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true))); + + string Path; + D->runOnModule(*M, Path); + + // verify DebugIR was able to correctly parse the file name from module ID + ASSERT_EQ(ExpectedPath, Path); + + // verify DebugIR generated a file, and clean it up + ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path; +} + +// Test an empty unnamed module generates an output file whose path is specified +// at DebugIR construction. +TEST_F(TestDebugIR, EmptyUnnamedModuleWriteNamedFile) { + string Filename("NamedFile2"); + + M.reset(createEmptyModule()); + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass( + false, false, StringRef(cwd), StringRef(Filename)))); + string Path; + D->runOnModule(*M, Path); + + string ExpectedPath(getPath(cwd, Filename)); + ASSERT_EQ(ExpectedPath, Path); + + // verify DebugIR generated a file, and clean it up + ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path; +} + +// Test an empty named module generates an output file at the path specified +// during DebugIR construction. +TEST_F(TestDebugIR, EmptyNamedModuleWriteNamedFile) { + string Filename("NamedFile3"); + + string UnexpectedPath(getPath(cwd, "UnexpectedFilename")); + M.reset(createEmptyModule(UnexpectedPath)); + + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass( + false, false, StringRef(cwd), StringRef(Filename)))); + string Path; + D->runOnModule(*M, Path); + + string ExpectedPath(getPath(cwd, Filename)); + ASSERT_EQ(ExpectedPath, Path); + + // verify DebugIR generated a file, and clean it up + ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path; + + // verify DebugIR did not generate a file at the path specified at Module + // construction. + ASSERT_FALSE(removeIfExists(UnexpectedPath)) << "Unexpected file " << Path; +} + +// Test a non-empty named module that is not supposed to be output to disk +TEST_F(TestDebugIR, NonEmptyNamedModuleNoWrite) { + string Filename("NamedFile4"); + string ExpectedPath(getPath(cwd, Filename)); + + M.reset(createEmptyModule(ExpectedPath)); + insertAddFunction(M.get()); + + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass())); + + string Path; + D->runOnModule(*M, Path); + ASSERT_EQ(ExpectedPath, Path); + + // verify DebugIR did not generate a file + ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path; +} + +// Test a non-empty named module that is output to disk. +TEST_F(TestDebugIR, NonEmptyNamedModuleWriteFile) { + string Filename("NamedFile5"); + string ExpectedPath(getPath(cwd, Filename)); + + M.reset(createEmptyModule(ExpectedPath)); + insertAddFunction(M.get()); + + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true))); + + string Path; + D->runOnModule(*M, Path); + ASSERT_EQ(ExpectedPath, Path); + + // verify DebugIR generated a file, and clean it up + ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path; +} + +// Test a non-empty unnamed module is output to a path specified at DebugIR +// construction. +TEST_F(TestDebugIR, NonEmptyUnnamedModuleWriteToNamedFile) { + string Filename("NamedFile6"); + + M.reset(createEmptyModule()); + insertAddFunction(M.get()); + + D.reset(static_cast<DebugIR *>( + llvm::createDebugIRPass(true, true, cwd, Filename))); + string Path; + D->runOnModule(*M, Path); + + string ExpectedPath(getPath(cwd, Filename)); + ASSERT_EQ(ExpectedPath, Path); + + // verify DebugIR generated a file, and clean it up + ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path; +} + +// Test that information inside existing debug metadata is retained +TEST_F(TestDebugIR, ExistingMetadataRetained) { + string Filename("NamedFile7"); + string ExpectedPath(getPath(cwd, Filename)); + + M.reset(createEmptyModule(ExpectedPath)); + insertAddFunction(M.get()); + + StringRef Producer("TestProducer"); + insertCUDescriptor(M.get(), Filename, cwd, Producer); + + DebugInfoFinder Finder; + Finder.processModule(*M); + ASSERT_EQ((unsigned)1, Finder.compile_unit_count()); + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass())); + + string Path; + D->runOnModule(*M, Path); + ASSERT_EQ(ExpectedPath, Path); + + // verify DebugIR did not generate a file + ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path; + + DICompileUnit CU(*Finder.compile_unit_begin()); + + // Verify original CU information is retained + ASSERT_EQ(Filename, CU.getFilename()); + ASSERT_EQ(cwd, CU.getDirectory()); + ASSERT_EQ(Producer, CU.getProducer()); +} + +#endif // HAVE_GETCWD + +#ifdef GTEST_HAS_DEATH_TEST + +// Test a non-empty unnamed module that is not supposed to be output to disk +// NOTE: this test is expected to die with LLVM_ERROR, and such depends on +// google test's "death test" mode. +TEST_F(TestDebugIR, NonEmptyUnnamedModuleNoWrite) { + M.reset(createEmptyModule(StringRef())); + insertAddFunction(M.get()); + D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass())); + + // No name in module or on DebugIR construction ==> DebugIR should assert + EXPECT_DEATH(D->runOnModule(*M), + "DebugIR unable to determine file name in input."); +} + +#endif // GTEST_HAS_DEATH_TEST +} diff --git a/unittests/Transforms/DebugIR/Makefile b/unittests/Transforms/DebugIR/Makefile new file mode 100644 index 0000000000000..9ace8c33c41ce --- /dev/null +++ b/unittests/Transforms/DebugIR/Makefile @@ -0,0 +1,15 @@ +##===- unittests/Transforms/Utils/Makefile -----------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../.. +TESTNAME = DebugIR +LINK_COMPONENTS := Instrumentation + +include $(LEVEL)/Makefile.config +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Transforms/Makefile b/unittests/Transforms/Makefile index 599b18a057dcf..d5cca397b6af3 100644 --- a/unittests/Transforms/Makefile +++ b/unittests/Transforms/Makefile @@ -9,7 +9,7 @@ LEVEL = ../.. -PARALLEL_DIRS = Utils +PARALLEL_DIRS = DebugIR Utils include $(LEVEL)/Makefile.common diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt index 730d83b838fb6..0656390cbbeaf 100644 --- a/unittests/Transforms/Utils/CMakeLists.txt +++ b/unittests/Transforms/Utils/CMakeLists.txt @@ -6,4 +6,5 @@ add_llvm_unittest(UtilsTests Cloning.cpp IntegerDivision.cpp Local.cpp + SpecialCaseList.cpp ) diff --git a/unittests/Transforms/Utils/SpecialCaseList.cpp b/unittests/Transforms/Utils/SpecialCaseList.cpp new file mode 100644 index 0000000000000..07ac908caabcc --- /dev/null +++ b/unittests/Transforms/Utils/SpecialCaseList.cpp @@ -0,0 +1,232 @@ +//===- SpecialCaseList.cpp - Unit tests for SpecialCaseList ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Transforms/Utils/SpecialCaseList.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class SpecialCaseListTest : public ::testing::Test { +protected: + Function *makeFunction(StringRef Name, Module &M) { + return Function::Create(FunctionType::get(Type::getVoidTy(Ctx), false), + GlobalValue::ExternalLinkage, + Name, + &M); + } + + GlobalVariable *makeGlobal(StringRef Name, StringRef StructName, Module &M) { + StructType *ST = + StructType::create(StructName, Type::getInt32Ty(Ctx), (Type*)0); + return new GlobalVariable( + M, ST, false, GlobalValue::ExternalLinkage, 0, Name); + } + + GlobalAlias *makeAlias(StringRef Name, GlobalValue *Aliasee) { + return new GlobalAlias(Aliasee->getType(), GlobalValue::ExternalLinkage, + Name, Aliasee, Aliasee->getParent()); + } + + SpecialCaseList *makeSpecialCaseList(StringRef List, std::string &Error) { + OwningPtr<MemoryBuffer> MB(MemoryBuffer::getMemBuffer(List)); + return SpecialCaseList::create(MB.get(), Error); + } + + SpecialCaseList *makeSpecialCaseList(StringRef List) { + std::string Error; + SpecialCaseList *SCL = makeSpecialCaseList(List, Error); + assert(SCL); + assert(Error == ""); + return SCL; + } + + LLVMContext Ctx; +}; + +TEST_F(SpecialCaseListTest, ModuleIsIn) { + Module M("hello", Ctx); + Function *F = makeFunction("foo", M); + GlobalVariable *GV = makeGlobal("bar", "t", M); + + OwningPtr<SpecialCaseList> SCL(makeSpecialCaseList("# This is a comment.\n" + "\n" + "src:hello\n")); + EXPECT_TRUE(SCL->isIn(M)); + EXPECT_TRUE(SCL->isIn(*F)); + EXPECT_TRUE(SCL->isIn(*GV)); + + SCL.reset(makeSpecialCaseList("src:he*o\n")); + EXPECT_TRUE(SCL->isIn(M)); + EXPECT_TRUE(SCL->isIn(*F)); + EXPECT_TRUE(SCL->isIn(*GV)); + + SCL.reset(makeSpecialCaseList("src:hi\n")); + EXPECT_FALSE(SCL->isIn(M)); + EXPECT_FALSE(SCL->isIn(*F)); + EXPECT_FALSE(SCL->isIn(*GV)); +} + +TEST_F(SpecialCaseListTest, FunctionIsIn) { + Module M("hello", Ctx); + Function *Foo = makeFunction("foo", M); + Function *Bar = makeFunction("bar", M); + + OwningPtr<SpecialCaseList> SCL(makeSpecialCaseList("fun:foo\n")); + EXPECT_TRUE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + + SCL.reset(makeSpecialCaseList("fun:b*\n")); + EXPECT_FALSE(SCL->isIn(*Foo)); + EXPECT_TRUE(SCL->isIn(*Bar)); + + SCL.reset(makeSpecialCaseList("fun:f*\n" + "fun:bar\n")); + EXPECT_TRUE(SCL->isIn(*Foo)); + EXPECT_TRUE(SCL->isIn(*Bar)); + + SCL.reset(makeSpecialCaseList("fun:foo=functional\n")); + EXPECT_TRUE(SCL->isIn(*Foo, "functional")); + StringRef Category; + EXPECT_FALSE(SCL->isIn(*Bar, "functional")); +} + +TEST_F(SpecialCaseListTest, GlobalIsIn) { + Module M("hello", Ctx); + GlobalVariable *Foo = makeGlobal("foo", "t1", M); + GlobalVariable *Bar = makeGlobal("bar", "t2", M); + + OwningPtr<SpecialCaseList> SCL(makeSpecialCaseList("global:foo\n")); + EXPECT_TRUE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + EXPECT_FALSE(SCL->isIn(*Foo, "init")); + EXPECT_FALSE(SCL->isIn(*Bar, "init")); + + SCL.reset(makeSpecialCaseList("global:foo=init\n")); + EXPECT_FALSE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + EXPECT_TRUE(SCL->isIn(*Foo, "init")); + EXPECT_FALSE(SCL->isIn(*Bar, "init")); + + SCL.reset(makeSpecialCaseList("global-init:foo\n")); + EXPECT_FALSE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + EXPECT_TRUE(SCL->isIn(*Foo, "init")); + EXPECT_FALSE(SCL->isIn(*Bar, "init")); + + SCL.reset(makeSpecialCaseList("type:t2=init\n")); + EXPECT_FALSE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + EXPECT_FALSE(SCL->isIn(*Foo, "init")); + EXPECT_TRUE(SCL->isIn(*Bar, "init")); + + SCL.reset(makeSpecialCaseList("global-init-type:t2\n")); + EXPECT_FALSE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + EXPECT_FALSE(SCL->isIn(*Foo, "init")); + EXPECT_TRUE(SCL->isIn(*Bar, "init")); + + SCL.reset(makeSpecialCaseList("src:hello=init\n")); + EXPECT_FALSE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + EXPECT_TRUE(SCL->isIn(*Foo, "init")); + EXPECT_TRUE(SCL->isIn(*Bar, "init")); + + SCL.reset(makeSpecialCaseList("global-init-src:hello\n")); + EXPECT_FALSE(SCL->isIn(*Foo)); + EXPECT_FALSE(SCL->isIn(*Bar)); + EXPECT_TRUE(SCL->isIn(*Foo, "init")); + EXPECT_TRUE(SCL->isIn(*Bar, "init")); +} + +TEST_F(SpecialCaseListTest, AliasIsIn) { + Module M("hello", Ctx); + Function *Foo = makeFunction("foo", M); + GlobalVariable *Bar = makeGlobal("bar", "t", M); + GlobalAlias *FooAlias = makeAlias("fooalias", Foo); + GlobalAlias *BarAlias = makeAlias("baralias", Bar); + + OwningPtr<SpecialCaseList> SCL(makeSpecialCaseList("fun:foo\n")); + EXPECT_FALSE(SCL->isIn(*FooAlias)); + EXPECT_FALSE(SCL->isIn(*BarAlias)); + + SCL.reset(makeSpecialCaseList("global:bar\n")); + EXPECT_FALSE(SCL->isIn(*FooAlias)); + EXPECT_FALSE(SCL->isIn(*BarAlias)); + + SCL.reset(makeSpecialCaseList("global:fooalias\n")); + EXPECT_FALSE(SCL->isIn(*FooAlias)); + EXPECT_FALSE(SCL->isIn(*BarAlias)); + + SCL.reset(makeSpecialCaseList("fun:fooalias\n")); + EXPECT_TRUE(SCL->isIn(*FooAlias)); + EXPECT_FALSE(SCL->isIn(*BarAlias)); + + SCL.reset(makeSpecialCaseList("global:baralias=init\n")); + EXPECT_FALSE(SCL->isIn(*FooAlias, "init")); + EXPECT_TRUE(SCL->isIn(*BarAlias, "init")); + + SCL.reset(makeSpecialCaseList("type:t=init\n")); + EXPECT_FALSE(SCL->isIn(*FooAlias, "init")); + EXPECT_TRUE(SCL->isIn(*BarAlias, "init")); + + SCL.reset(makeSpecialCaseList("fun:baralias=init\n")); + EXPECT_FALSE(SCL->isIn(*FooAlias, "init")); + EXPECT_FALSE(SCL->isIn(*BarAlias, "init")); +} + +TEST_F(SpecialCaseListTest, Substring) { + Module M("othello", Ctx); + Function *F = makeFunction("tomfoolery", M); + GlobalVariable *GV = makeGlobal("bartender", "t", M); + GlobalAlias *GA1 = makeAlias("buffoonery", F); + GlobalAlias *GA2 = makeAlias("foobar", GV); + + OwningPtr<SpecialCaseList> SCL(makeSpecialCaseList("src:hello\n" + "fun:foo\n" + "global:bar\n")); + EXPECT_FALSE(SCL->isIn(M)); + EXPECT_FALSE(SCL->isIn(*F)); + EXPECT_FALSE(SCL->isIn(*GV)); + EXPECT_FALSE(SCL->isIn(*GA1)); + EXPECT_FALSE(SCL->isIn(*GA2)); + + SCL.reset(makeSpecialCaseList("fun:*foo*\n")); + EXPECT_TRUE(SCL->isIn(*F)); + EXPECT_TRUE(SCL->isIn(*GA1)); +} + +TEST_F(SpecialCaseListTest, InvalidSpecialCaseList) { + std::string Error; + EXPECT_EQ(0, makeSpecialCaseList("badline", Error)); + EXPECT_EQ("Malformed line 1: 'badline'", Error); + EXPECT_EQ(0, makeSpecialCaseList("src:bad[a-", Error)); + EXPECT_EQ("Malformed regex in line 1: 'bad[a-': invalid character range", + Error); + EXPECT_EQ(0, makeSpecialCaseList("src:a.c\n" + "fun:fun(a\n", + Error)); + EXPECT_EQ("Malformed regex in line 2: 'fun(a': parentheses not balanced", + Error); + EXPECT_EQ(0, SpecialCaseList::create("unexisting", Error)); + EXPECT_EQ(0U, Error.find("Can't open file 'unexisting':")); +} + +TEST_F(SpecialCaseListTest, EmptySpecialCaseList) { + OwningPtr<SpecialCaseList> SCL(makeSpecialCaseList("")); + Module M("foo", Ctx); + EXPECT_FALSE(SCL->isIn(M)); +} + +} |