summaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2016-07-23 20:41:05 +0000
committerDimitry Andric <dim@FreeBSD.org>2016-07-23 20:41:05 +0000
commit01095a5d43bbfde13731688ddcf6048ebb8b7721 (patch)
tree4def12e759965de927d963ac65840d663ef9d1ea /unittests
parentf0f4822ed4b66e3579e92a89f368f8fb860e218e (diff)
Notes
Diffstat (limited to 'unittests')
-rw-r--r--unittests/ADT/APFloatTest.cpp321
-rw-r--r--unittests/ADT/APIntTest.cpp89
-rw-r--r--unittests/ADT/ArrayRefTest.cpp22
-rw-r--r--unittests/ADT/BitVectorTest.cpp26
-rw-r--r--unittests/ADT/BitmaskEnumTest.cpp134
-rw-r--r--unittests/ADT/CMakeLists.txt4
-rw-r--r--unittests/ADT/DenseMapTest.cpp181
-rw-r--r--unittests/ADT/FoldingSet.cpp133
-rw-r--r--unittests/ADT/ImmutableSetTest.cpp9
-rw-r--r--unittests/ADT/Makefile23
-rw-r--r--unittests/ADT/PointerEmbeddedIntTest.cpp34
-rw-r--r--unittests/ADT/PriorityWorklistTest.cpp106
-rw-r--r--unittests/ADT/SequenceTest.cpp41
-rw-r--r--unittests/ADT/SetVectorTest.cpp34
-rw-r--r--unittests/ADT/SmallPtrSetTest.cpp63
-rw-r--r--unittests/ADT/SmallVectorTest.cpp6
-rw-r--r--unittests/ADT/SparseSetTest.cpp20
-rw-r--r--unittests/ADT/StringMapTest.cpp115
-rw-r--r--unittests/ADT/StringRefTest.cpp11
-rw-r--r--unittests/ADT/TinyPtrVectorTest.cpp2
-rw-r--r--unittests/ADT/TripleTest.cpp65
-rw-r--r--unittests/Analysis/AliasAnalysisTest.cpp26
-rw-r--r--unittests/Analysis/BlockFrequencyInfoTest.cpp86
-rw-r--r--unittests/Analysis/CFGTest.cpp3
-rw-r--r--unittests/Analysis/CGSCCPassManagerTest.cpp311
-rw-r--r--unittests/Analysis/CMakeLists.txt4
-rw-r--r--unittests/Analysis/CallGraphTest.cpp6
-rw-r--r--unittests/Analysis/LazyCallGraphTest.cpp1237
-rw-r--r--unittests/Analysis/LoopPassManagerTest.cpp205
-rw-r--r--unittests/Analysis/Makefile15
-rw-r--r--unittests/Analysis/MixedTBAATest.cpp1
-rw-r--r--unittests/Analysis/ScalarEvolutionTest.cpp27
-rw-r--r--unittests/Analysis/UnrollAnalyzer.cpp331
-rw-r--r--unittests/Analysis/ValueTrackingTest.cpp3
-rw-r--r--unittests/AsmParser/AsmParserTest.cpp273
-rw-r--r--unittests/AsmParser/Makefile15
-rw-r--r--unittests/Bitcode/BitReaderTest.cpp10
-rw-r--r--unittests/Bitcode/BitstreamReaderTest.cpp185
-rw-r--r--unittests/Bitcode/BitstreamWriterTest.cpp59
-rw-r--r--unittests/Bitcode/CMakeLists.txt1
-rw-r--r--unittests/Bitcode/Makefile15
-rw-r--r--unittests/CMakeLists.txt2
-rw-r--r--unittests/CodeGen/DIEHashTest.cpp2
-rw-r--r--unittests/CodeGen/Makefile16
-rw-r--r--unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp1
-rw-r--r--unittests/DebugInfo/DWARF/Makefile16
-rw-r--r--unittests/DebugInfo/Makefile15
-rw-r--r--unittests/DebugInfo/PDB/CMakeLists.txt3
-rw-r--r--unittests/DebugInfo/PDB/ErrorChecking.h41
-rw-r--r--unittests/DebugInfo/PDB/Makefile16
-rw-r--r--unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp442
-rw-r--r--unittests/DebugInfo/PDB/MsfBuilderTest.cpp360
-rw-r--r--unittests/DebugInfo/PDB/PDBApiTest.cpp27
-rw-r--r--unittests/ExecutionEngine/ExecutionEngineTest.cpp50
-rw-r--r--unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp3
-rw-r--r--unittests/ExecutionEngine/MCJIT/MCJITTestBase.h3
-rw-r--r--unittests/ExecutionEngine/MCJIT/Makefile18
-rw-r--r--unittests/ExecutionEngine/Makefile22
-rw-r--r--unittests/ExecutionEngine/Orc/CMakeLists.txt2
-rw-r--r--unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp34
-rw-r--r--unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp3
-rw-r--r--unittests/ExecutionEngine/Orc/Makefile16
-rw-r--r--unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp99
-rw-r--r--unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp77
-rw-r--r--unittests/ExecutionEngine/Orc/OrcCAPITest.cpp4
-rw-r--r--unittests/ExecutionEngine/Orc/OrcTestCommon.h5
-rw-r--r--unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp247
-rw-r--r--unittests/IR/AttributesTest.cpp9
-rw-r--r--unittests/IR/CMakeLists.txt3
-rw-r--r--unittests/IR/ConstantRangeTest.cpp154
-rw-r--r--unittests/IR/ConstantsTest.cpp57
-rw-r--r--unittests/IR/DebugTypeODRUniquingTest.cpp156
-rw-r--r--unittests/IR/DominatorTreeTest.cpp15
-rw-r--r--unittests/IR/FunctionTest.cpp112
-rw-r--r--unittests/IR/IRBuilderTest.cpp16
-rw-r--r--unittests/IR/InstructionsTest.cpp81
-rw-r--r--unittests/IR/IntrinsicsTest.cpp40
-rw-r--r--unittests/IR/LegacyPassManagerTest.cpp74
-rw-r--r--unittests/IR/Makefile15
-rw-r--r--unittests/IR/MetadataTest.cpp421
-rw-r--r--unittests/IR/PassManagerTest.cpp107
-rw-r--r--unittests/IR/PatternMatch.cpp2
-rw-r--r--unittests/IR/TypeBuilderTest.cpp303
-rw-r--r--unittests/IR/UserTest.cpp6
-rw-r--r--unittests/IR/ValueHandleTest.cpp44
-rw-r--r--unittests/IR/ValueMapTest.cpp12
-rw-r--r--unittests/IR/ValueTest.cpp4
-rw-r--r--unittests/IR/VerifierTest.cpp127
-rw-r--r--unittests/IR/WaymarkTest.cpp12
-rw-r--r--unittests/LineEditor/Makefile15
-rw-r--r--unittests/Linker/LinkModulesTest.cpp78
-rw-r--r--unittests/Linker/Makefile15
-rw-r--r--unittests/MC/CMakeLists.txt3
-rw-r--r--unittests/MC/DwarfLineTables.cpp179
-rw-r--r--unittests/MC/Makefile15
-rw-r--r--unittests/MC/StringTableBuilderTest.cpp23
-rw-r--r--unittests/MC/TargetRegistry.cpp (renamed from unittests/Support/TargetRegistry.cpp)6
-rw-r--r--unittests/MI/CMakeLists.txt13
-rw-r--r--unittests/MI/LiveIntervalTest.cpp360
-rw-r--r--unittests/Makefile20
-rw-r--r--unittests/Makefile.unittest69
-rw-r--r--unittests/ObjectYAML/CMakeLists.txt8
-rw-r--r--unittests/ObjectYAML/YAMLTest.cpp (renamed from unittests/MC/YAMLTest.cpp)4
-rw-r--r--unittests/Option/Makefile23
-rw-r--r--unittests/Option/OptionParsingTest.cpp101
-rw-r--r--unittests/Option/Opts.td2
-rw-r--r--unittests/ProfileData/CMakeLists.txt1
-rw-r--r--unittests/ProfileData/CoverageMappingTest.cpp444
-rw-r--r--unittests/ProfileData/InstrProfTest.cpp684
-rw-r--r--unittests/ProfileData/Makefile15
-rw-r--r--unittests/ProfileData/SampleProfTest.cpp81
-rw-r--r--unittests/Support/AlignOfTest.cpp6
-rw-r--r--unittests/Support/CMakeLists.txt12
-rw-r--r--unittests/Support/CommandLineTest.cpp227
-rw-r--r--unittests/Support/ConvertUTFTest.cpp38
-rw-r--r--unittests/Support/EndianStreamTest.cpp51
-rw-r--r--unittests/Support/ErrorOrTest.cpp42
-rw-r--r--unittests/Support/ErrorTest.cpp627
-rw-r--r--unittests/Support/Makefile15
-rw-r--r--unittests/Support/MathExtrasTest.cpp90
-rw-r--r--unittests/Support/Path.cpp190
-rw-r--r--unittests/Support/ProgramTest.cpp10
-rw-r--r--unittests/Support/StreamingMemoryObject.cpp39
-rw-r--r--unittests/Support/StreamingMemoryObjectTest.cpp68
-rw-r--r--unittests/Support/TargetParserTest.cpp92
-rw-r--r--unittests/Support/ThreadPool.cpp2
-rw-r--r--unittests/Support/TrailingObjectsTest.cpp13
-rw-r--r--unittests/Support/TypeNameTest.cpp49
-rw-r--r--unittests/Support/YAMLIOTest.cpp107
-rw-r--r--unittests/Support/YAMLParserTest.cpp1
-rw-r--r--unittests/Support/raw_ostream_test.cpp8
-rw-r--r--unittests/Support/raw_sha1_ostream_test.cpp71
-rw-r--r--unittests/Transforms/IPO/CMakeLists.txt3
-rw-r--r--unittests/Transforms/IPO/LowerTypeTests.cpp (renamed from unittests/Transforms/IPO/LowerBitSets.cpp)11
-rw-r--r--unittests/Transforms/IPO/Makefile15
-rw-r--r--unittests/Transforms/IPO/WholeProgramDevirt.cpp165
-rw-r--r--unittests/Transforms/Makefile17
-rw-r--r--unittests/Transforms/Utils/CMakeLists.txt2
-rw-r--r--unittests/Transforms/Utils/Cloning.cpp61
-rw-r--r--unittests/Transforms/Utils/IntegerDivision.cpp16
-rw-r--r--unittests/Transforms/Utils/Local.cpp4
-rw-r--r--unittests/Transforms/Utils/Makefile15
-rw-r--r--unittests/Transforms/Utils/MemorySSA.cpp310
-rw-r--r--unittests/Transforms/Utils/ValueMapperTest.cpp306
144 files changed, 10383 insertions, 2005 deletions
diff --git a/unittests/ADT/APFloatTest.cpp b/unittests/ADT/APFloatTest.cpp
index 55c3f48f00d4..18734eb72b82 100644
--- a/unittests/ADT/APFloatTest.cpp
+++ b/unittests/ADT/APFloatTest.cpp
@@ -9,7 +9,6 @@
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APSInt.h"
-#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
@@ -140,14 +139,14 @@ TEST(APFloatTest, next) {
test = APFloat(APFloat::IEEEquad, "0x0.0000000000000000000000000001p-16382");
expected = APFloat::getZero(APFloat::IEEEquad, false);
EXPECT_EQ(test.next(true), APFloat::opOK);
- EXPECT_TRUE(test.isZero() && !test.isNegative());
+ EXPECT_TRUE(test.isPosZero());
EXPECT_TRUE(test.bitwiseIsEqual(expected));
// nextUp(-getSmallest()) = -0.
test = APFloat(APFloat::IEEEquad, "-0x0.0000000000000000000000000001p-16382");
expected = APFloat::getZero(APFloat::IEEEquad, true);
EXPECT_EQ(test.next(false), APFloat::opOK);
- EXPECT_TRUE(test.isZero() && test.isNegative());
+ EXPECT_TRUE(test.isNegZero());
EXPECT_TRUE(test.bitwiseIsEqual(expected));
// nextDown(-getSmallest()) = -nextUp(getSmallest()) = -getSmallest() - inc.
@@ -1314,7 +1313,7 @@ TEST(APFloatTest, roundToIntegral) {
P.roundToIntegral(APFloat::rmTowardZero);
EXPECT_TRUE(std::isinf(P.convertToDouble()) && P.convertToDouble() < 0.0);
}
-
+
TEST(APFloatTest, isInteger) {
APFloat T(-0.0);
EXPECT_TRUE(T.isInteger());
@@ -2821,6 +2820,19 @@ TEST(APFloatTest, abs) {
}
TEST(APFloatTest, ilogb) {
+ EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble, false)));
+ EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble, true)));
+ EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1024")));
+ EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023")));
+ EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023")));
+ EXPECT_EQ(-51, ilogb(APFloat(APFloat::IEEEdouble, "0x1p-51")));
+ EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1023")));
+ EXPECT_EQ(-2, ilogb(APFloat(APFloat::IEEEdouble, "0x0.ffffp-1")));
+ EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble, "0x1.fffep-1023")));
+ EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble, false)));
+ EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble, true)));
+
+
EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle, "0x1p+0")));
EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle, "-0x1p+0")));
EXPECT_EQ(42, ilogb(APFloat(APFloat::IEEEsingle, "0x1p+42")));
@@ -2841,8 +2853,9 @@ TEST(APFloatTest, ilogb) {
EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle, false)));
EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle, true)));
- EXPECT_EQ(-126, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, false)));
- EXPECT_EQ(-126, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, true)));
+
+ EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, false)));
+ EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle, true)));
EXPECT_EQ(-126,
ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle, false)));
EXPECT_EQ(-126,
@@ -2850,15 +2863,17 @@ TEST(APFloatTest, ilogb) {
}
TEST(APFloatTest, scalbn) {
+
+ const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;
EXPECT_TRUE(
APFloat(APFloat::IEEEsingle, "0x1p+0")
- .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 0)));
+ .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 0, RM)));
EXPECT_TRUE(
APFloat(APFloat::IEEEsingle, "0x1p+42")
- .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 42)));
+ .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 42, RM)));
EXPECT_TRUE(
APFloat(APFloat::IEEEsingle, "0x1p-42")
- .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -42)));
+ .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -42, RM)));
APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false);
APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true);
@@ -2868,27 +2883,279 @@ TEST(APFloatTest, scalbn) {
APFloat QMNaN = APFloat::getNaN(APFloat::IEEEsingle, true);
APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false);
- EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0)));
- EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0)));
- EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(PZero, 0)));
- EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(MZero, 0)));
- EXPECT_TRUE(QPNaN.bitwiseIsEqual(scalbn(QPNaN, 0)));
- EXPECT_TRUE(QMNaN.bitwiseIsEqual(scalbn(QMNaN, 0)));
- EXPECT_TRUE(SNaN.bitwiseIsEqual(scalbn(SNaN, 0)));
-
- EXPECT_TRUE(
- PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 128)));
+ EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0, RM)));
+ EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0, RM)));
+ EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(PZero, 0, RM)));
+ EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(MZero, 0, RM)));
+ EXPECT_TRUE(QPNaN.bitwiseIsEqual(scalbn(QPNaN, 0, RM)));
+ EXPECT_TRUE(QMNaN.bitwiseIsEqual(scalbn(QMNaN, 0, RM)));
+ EXPECT_FALSE(scalbn(SNaN, 0, RM).isSignaling());
+
+ APFloat ScalbnSNaN = scalbn(SNaN, 1, RM);
+ EXPECT_TRUE(ScalbnSNaN.isNaN() && !ScalbnSNaN.isSignaling());
+
+ // Make sure highest bit of payload is preserved.
+ const APInt Payload(64, (UINT64_C(1) << 50) |
+ (UINT64_C(1) << 49) |
+ (UINT64_C(1234) << 32) |
+ 1);
+
+ APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble, false,
+ &Payload);
+ APFloat QuietPayload = scalbn(SNaNWithPayload, 1, RM);
+ EXPECT_TRUE(QuietPayload.isNaN() && !QuietPayload.isSignaling());
+ EXPECT_EQ(Payload, QuietPayload.bitcastToAPInt().getLoBits(51));
+
+ EXPECT_TRUE(PInf.bitwiseIsEqual(
+ scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 128, RM)));
EXPECT_TRUE(MInf.bitwiseIsEqual(
- scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), 128)));
- EXPECT_TRUE(
- PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+127"), 1)));
+ scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), 128, RM)));
+ EXPECT_TRUE(PInf.bitwiseIsEqual(
+ scalbn(APFloat(APFloat::IEEEsingle, "0x1p+127"), 1, RM)));
EXPECT_TRUE(PZero.bitwiseIsEqual(
- scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -127)));
+ scalbn(APFloat(APFloat::IEEEsingle, "0x1p-127"), -127, RM)));
EXPECT_TRUE(MZero.bitwiseIsEqual(
- scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), -127)));
+ scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -127, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "-0x1p-149").bitwiseIsEqual(
+ scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -22, RM)));
EXPECT_TRUE(PZero.bitwiseIsEqual(
- scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -1)));
- EXPECT_TRUE(PZero.bitwiseIsEqual(
- scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -1)));
+ scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -24, RM)));
+
+
+ APFloat SmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, false);
+ APFloat NegSmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, true);
+
+ APFloat LargestF64 = APFloat::getLargest(APFloat::IEEEdouble, false);
+ APFloat NegLargestF64 = APFloat::getLargest(APFloat::IEEEdouble, true);
+
+ APFloat SmallestNormalizedF64
+ = APFloat::getSmallestNormalized(APFloat::IEEEdouble, false);
+ APFloat NegSmallestNormalizedF64
+ = APFloat::getSmallestNormalized(APFloat::IEEEdouble, true);
+
+ APFloat LargestDenormalF64(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023");
+ APFloat NegLargestDenormalF64(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023");
+
+
+ EXPECT_TRUE(SmallestF64.bitwiseIsEqual(
+ scalbn(APFloat(APFloat::IEEEdouble, "0x1p-1074"), 0, RM)));
+ EXPECT_TRUE(NegSmallestF64.bitwiseIsEqual(
+ scalbn(APFloat(APFloat::IEEEdouble, "-0x1p-1074"), 0, RM)));
+
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023")
+ .bitwiseIsEqual(scalbn(SmallestF64, 2097, RM)));
+
+ EXPECT_TRUE(scalbn(SmallestF64, -2097, RM).isPosZero());
+ EXPECT_TRUE(scalbn(SmallestF64, -2098, RM).isPosZero());
+ EXPECT_TRUE(scalbn(SmallestF64, -2099, RM).isPosZero());
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1022")
+ .bitwiseIsEqual(scalbn(SmallestF64, 2096, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023")
+ .bitwiseIsEqual(scalbn(SmallestF64, 2097, RM)));
+ EXPECT_TRUE(scalbn(SmallestF64, 2098, RM).isInfinity());
+ EXPECT_TRUE(scalbn(SmallestF64, 2099, RM).isInfinity());
+
+ // Test for integer overflows when adding to exponent.
+ EXPECT_TRUE(scalbn(SmallestF64, -INT_MAX, RM).isPosZero());
+ EXPECT_TRUE(scalbn(LargestF64, INT_MAX, RM).isInfinity());
+
+ EXPECT_TRUE(LargestDenormalF64
+ .bitwiseIsEqual(scalbn(LargestDenormalF64, 0, RM)));
+ EXPECT_TRUE(NegLargestDenormalF64
+ .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 0, RM)));
+
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1022")
+ .bitwiseIsEqual(scalbn(LargestDenormalF64, 1, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1021")
+ .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 2, RM)));
+
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1")
+ .bitwiseIsEqual(scalbn(LargestDenormalF64, 1024, RM)));
+ EXPECT_TRUE(scalbn(LargestDenormalF64, -1023, RM).isPosZero());
+ EXPECT_TRUE(scalbn(LargestDenormalF64, -1024, RM).isPosZero());
+ EXPECT_TRUE(scalbn(LargestDenormalF64, -2048, RM).isPosZero());
+ EXPECT_TRUE(scalbn(LargestDenormalF64, 2047, RM).isInfinity());
+ EXPECT_TRUE(scalbn(LargestDenormalF64, 2098, RM).isInfinity());
+ EXPECT_TRUE(scalbn(LargestDenormalF64, 2099, RM).isInfinity());
+
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-2")
+ .bitwiseIsEqual(scalbn(LargestDenormalF64, 1021, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1")
+ .bitwiseIsEqual(scalbn(LargestDenormalF64, 1022, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+0")
+ .bitwiseIsEqual(scalbn(LargestDenormalF64, 1023, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1023")
+ .bitwiseIsEqual(scalbn(LargestDenormalF64, 2046, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+974")
+ .bitwiseIsEqual(scalbn(SmallestF64, 2048, RM)));
+
+ APFloat RandomDenormalF64(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+51");
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-972")
+ .bitwiseIsEqual(scalbn(RandomDenormalF64, -1023, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1")
+ .bitwiseIsEqual(scalbn(RandomDenormalF64, -52, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-2")
+ .bitwiseIsEqual(scalbn(RandomDenormalF64, -53, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+0")
+ .bitwiseIsEqual(scalbn(RandomDenormalF64, -51, RM)));
+
+ EXPECT_TRUE(scalbn(RandomDenormalF64, -2097, RM).isPosZero());
+ EXPECT_TRUE(scalbn(RandomDenormalF64, -2090, RM).isPosZero());
+
+
+ EXPECT_TRUE(
+ APFloat(APFloat::IEEEdouble, "-0x1p-1073")
+ .bitwiseIsEqual(scalbn(NegLargestF64, -2097, RM)));
+
+ EXPECT_TRUE(
+ APFloat(APFloat::IEEEdouble, "-0x1p-1024")
+ .bitwiseIsEqual(scalbn(NegLargestF64, -2048, RM)));
+
+ EXPECT_TRUE(
+ APFloat(APFloat::IEEEdouble, "0x1p-1073")
+ .bitwiseIsEqual(scalbn(LargestF64, -2097, RM)));
+
+ EXPECT_TRUE(
+ APFloat(APFloat::IEEEdouble, "0x1p-1074")
+ .bitwiseIsEqual(scalbn(LargestF64, -2098, RM)));
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1074")
+ .bitwiseIsEqual(scalbn(NegLargestF64, -2098, RM)));
+ EXPECT_TRUE(scalbn(NegLargestF64, -2099, RM).isNegZero());
+ EXPECT_TRUE(scalbn(LargestF64, 1, RM).isInfinity());
+
+
+ EXPECT_TRUE(
+ APFloat(APFloat::IEEEdouble, "0x1p+0")
+ .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble, "0x1p+52"), -52, RM)));
+
+ EXPECT_TRUE(
+ APFloat(APFloat::IEEEdouble, "0x1p-103")
+ .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble, "0x1p-51"), -52, RM)));
+}
+
+TEST(APFloatTest, frexp) {
+ const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;
+
+ APFloat PZero = APFloat::getZero(APFloat::IEEEdouble, false);
+ APFloat MZero = APFloat::getZero(APFloat::IEEEdouble, true);
+ APFloat One(1.0);
+ APFloat MOne(-1.0);
+ APFloat Two(2.0);
+ APFloat MTwo(-2.0);
+
+ APFloat LargestDenormal(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023");
+ APFloat NegLargestDenormal(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023");
+
+ APFloat Smallest = APFloat::getSmallest(APFloat::IEEEdouble, false);
+ APFloat NegSmallest = APFloat::getSmallest(APFloat::IEEEdouble, true);
+
+ APFloat Largest = APFloat::getLargest(APFloat::IEEEdouble, false);
+ APFloat NegLargest = APFloat::getLargest(APFloat::IEEEdouble, true);
+
+ APFloat PInf = APFloat::getInf(APFloat::IEEEdouble, false);
+ APFloat MInf = APFloat::getInf(APFloat::IEEEdouble, true);
+
+ APFloat QPNaN = APFloat::getNaN(APFloat::IEEEdouble, false);
+ APFloat QMNaN = APFloat::getNaN(APFloat::IEEEdouble, true);
+ APFloat SNaN = APFloat::getSNaN(APFloat::IEEEdouble, false);
+
+ // Make sure highest bit of payload is preserved.
+ const APInt Payload(64, (UINT64_C(1) << 50) |
+ (UINT64_C(1) << 49) |
+ (UINT64_C(1234) << 32) |
+ 1);
+
+ APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble, false,
+ &Payload);
+
+ APFloat SmallestNormalized
+ = APFloat::getSmallestNormalized(APFloat::IEEEdouble, false);
+ APFloat NegSmallestNormalized
+ = APFloat::getSmallestNormalized(APFloat::IEEEdouble, true);
+
+ int Exp;
+ APFloat Frac(APFloat::IEEEdouble);
+
+
+ Frac = frexp(PZero, Exp, RM);
+ EXPECT_EQ(0, Exp);
+ EXPECT_TRUE(Frac.isPosZero());
+
+ Frac = frexp(MZero, Exp, RM);
+ EXPECT_EQ(0, Exp);
+ EXPECT_TRUE(Frac.isNegZero());
+
+
+ Frac = frexp(One, Exp, RM);
+ EXPECT_EQ(1, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac));
+
+ Frac = frexp(MOne, Exp, RM);
+ EXPECT_EQ(1, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1").bitwiseIsEqual(Frac));
+
+ Frac = frexp(LargestDenormal, Exp, RM);
+ EXPECT_EQ(-1022, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1").bitwiseIsEqual(Frac));
+
+ Frac = frexp(NegLargestDenormal, Exp, RM);
+ EXPECT_EQ(-1022, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1").bitwiseIsEqual(Frac));
+
+
+ Frac = frexp(Smallest, Exp, RM);
+ EXPECT_EQ(-1073, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac));
+
+ Frac = frexp(NegSmallest, Exp, RM);
+ EXPECT_EQ(-1073, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1").bitwiseIsEqual(Frac));
+
+
+ Frac = frexp(Largest, Exp, RM);
+ EXPECT_EQ(1024, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.fffffffffffffp-1").bitwiseIsEqual(Frac));
+
+ Frac = frexp(NegLargest, Exp, RM);
+ EXPECT_EQ(1024, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.fffffffffffffp-1").bitwiseIsEqual(Frac));
+
+
+ Frac = frexp(PInf, Exp, RM);
+ EXPECT_EQ(INT_MAX, Exp);
+ EXPECT_TRUE(Frac.isInfinity() && !Frac.isNegative());
+
+ Frac = frexp(MInf, Exp, RM);
+ EXPECT_EQ(INT_MAX, Exp);
+ EXPECT_TRUE(Frac.isInfinity() && Frac.isNegative());
+
+ Frac = frexp(QPNaN, Exp, RM);
+ EXPECT_EQ(INT_MIN, Exp);
+ EXPECT_TRUE(Frac.isNaN());
+
+ Frac = frexp(QMNaN, Exp, RM);
+ EXPECT_EQ(INT_MIN, Exp);
+ EXPECT_TRUE(Frac.isNaN());
+
+ Frac = frexp(SNaN, Exp, RM);
+ EXPECT_EQ(INT_MIN, Exp);
+ EXPECT_TRUE(Frac.isNaN() && !Frac.isSignaling());
+
+ Frac = frexp(SNaNWithPayload, Exp, RM);
+ EXPECT_EQ(INT_MIN, Exp);
+ EXPECT_TRUE(Frac.isNaN() && !Frac.isSignaling());
+ EXPECT_EQ(Payload, Frac.bitcastToAPInt().getLoBits(51));
+
+ Frac = frexp(APFloat(APFloat::IEEEdouble, "0x0.ffffp-1"), Exp, RM);
+ EXPECT_EQ(-1, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.fffep-1").bitwiseIsEqual(Frac));
+
+ Frac = frexp(APFloat(APFloat::IEEEdouble, "0x1p-51"), Exp, RM);
+ EXPECT_EQ(-50, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p-1").bitwiseIsEqual(Frac));
+
+ Frac = frexp(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+51"), Exp, RM);
+ EXPECT_EQ(52, Exp);
+ EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac));
}
}
diff --git a/unittests/ADT/APIntTest.cpp b/unittests/ADT/APIntTest.cpp
index 0002dad8555f..b0d80c3c6819 100644
--- a/unittests/ADT/APIntTest.cpp
+++ b/unittests/ADT/APIntTest.cpp
@@ -8,10 +8,10 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "gtest/gtest.h"
#include <array>
-#include <ostream>
using namespace llvm;
@@ -388,6 +388,34 @@ TEST(APIntTest, compareWithHalfInt64Max) {
EXPECT_TRUE( a.sge(edgeM1));
}
+TEST(APIntTest, compareLargeIntegers) {
+ // Make sure all the combinations of signed comparisons work with big ints.
+ auto One = APInt{128, static_cast<uint64_t>(1), true};
+ auto Two = APInt{128, static_cast<uint64_t>(2), true};
+ auto MinusOne = APInt{128, static_cast<uint64_t>(-1), true};
+ auto MinusTwo = APInt{128, static_cast<uint64_t>(-2), true};
+
+ EXPECT_TRUE(!One.slt(One));
+ EXPECT_TRUE(!Two.slt(One));
+ EXPECT_TRUE(MinusOne.slt(One));
+ EXPECT_TRUE(MinusTwo.slt(One));
+
+ EXPECT_TRUE(One.slt(Two));
+ EXPECT_TRUE(!Two.slt(Two));
+ EXPECT_TRUE(MinusOne.slt(Two));
+ EXPECT_TRUE(MinusTwo.slt(Two));
+
+ EXPECT_TRUE(!One.slt(MinusOne));
+ EXPECT_TRUE(!Two.slt(MinusOne));
+ EXPECT_TRUE(!MinusOne.slt(MinusOne));
+ EXPECT_TRUE(MinusTwo.slt(MinusOne));
+
+ EXPECT_TRUE(!One.slt(MinusTwo));
+ EXPECT_TRUE(!Two.slt(MinusTwo));
+ EXPECT_TRUE(!MinusOne.slt(MinusTwo));
+ EXPECT_TRUE(!MinusTwo.slt(MinusTwo));
+}
+
// Tests different div/rem varaints using scheme (a * b + c) / a
void testDiv(APInt a, APInt b, APInt c) {
@@ -994,6 +1022,23 @@ TEST(APIntTest, IsSplat) {
EXPECT_TRUE(E.isSplat(32));
}
+TEST(APIntTest, isMask) {
+ EXPECT_FALSE(APIntOps::isMask(APInt(32, 0x01010101)));
+ EXPECT_FALSE(APIntOps::isMask(APInt(32, 0xf0000000)));
+ EXPECT_FALSE(APIntOps::isMask(APInt(32, 0xffff0000)));
+ EXPECT_FALSE(APIntOps::isMask(APInt(32, 0xff << 1)));
+
+ for (int N : { 1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256 }) {
+ EXPECT_FALSE(APIntOps::isMask(APInt(N, 0)));
+
+ APInt One(N, 1);
+ for (int I = 1; I <= N; ++I) {
+ APInt MaskVal = One.shl(I) - 1;
+ EXPECT_TRUE(APIntOps::isMask(MaskVal));
+ }
+ }
+}
+
#if defined(__clang__)
// Disable the pragma warning from versions of Clang without -Wself-move
#pragma clang diagnostic push
@@ -1023,3 +1068,45 @@ TEST(APIntTest, SelfMoveAssignment) {
#pragma clang diagnostic pop
#endif
}
+
+TEST(APIntTest, reverseBits) {
+ EXPECT_EQ(1, APInt(1, 1).reverseBits());
+ EXPECT_EQ(0, APInt(1, 0).reverseBits());
+
+ EXPECT_EQ(3, APInt(2, 3).reverseBits());
+ EXPECT_EQ(3, APInt(2, 3).reverseBits());
+
+ EXPECT_EQ(0xb, APInt(4, 0xd).reverseBits());
+ EXPECT_EQ(0xd, APInt(4, 0xb).reverseBits());
+ EXPECT_EQ(0xf, APInt(4, 0xf).reverseBits());
+
+ EXPECT_EQ(0x30, APInt(7, 0x6).reverseBits());
+ EXPECT_EQ(0x5a, APInt(7, 0x2d).reverseBits());
+
+ EXPECT_EQ(0x0f, APInt(8, 0xf0).reverseBits());
+ EXPECT_EQ(0xf0, APInt(8, 0x0f).reverseBits());
+
+ EXPECT_EQ(0x0f0f, APInt(16, 0xf0f0).reverseBits());
+ EXPECT_EQ(0xf0f0, APInt(16, 0x0f0f).reverseBits());
+
+ EXPECT_EQ(0x0f0f0f0f, APInt(32, 0xf0f0f0f0).reverseBits());
+ EXPECT_EQ(0xf0f0f0f0, APInt(32, 0x0f0f0f0f).reverseBits());
+
+ EXPECT_EQ(0x402880a0 >> 1, APInt(31, 0x05011402).reverseBits());
+
+ EXPECT_EQ(0x0f0f0f0f, APInt(32, 0xf0f0f0f0).reverseBits());
+ EXPECT_EQ(0xf0f0f0f0, APInt(32, 0x0f0f0f0f).reverseBits());
+
+ EXPECT_EQ(0x0f0f0f0f0f0f0f0f, APInt(64, 0xf0f0f0f0f0f0f0f0).reverseBits());
+ EXPECT_EQ(0xf0f0f0f0f0f0f0f0, APInt(64, 0x0f0f0f0f0f0f0f0f).reverseBits());
+
+ for (unsigned N : { 1, 8, 16, 24, 31, 32, 33,
+ 63, 64, 65, 127, 128, 257, 1024 }) {
+ for (unsigned I = 0; I < N; ++I) {
+ APInt X = APInt::getOneBitSet(N, I);
+ APInt Y = APInt::getOneBitSet(N, N - (I + 1));
+ EXPECT_EQ(Y, X.reverseBits());
+ EXPECT_EQ(X, Y.reverseBits());
+ }
+ }
+}
diff --git a/unittests/ADT/ArrayRefTest.cpp b/unittests/ADT/ArrayRefTest.cpp
index 6cbadd6bc228..b5b71f06f65b 100644
--- a/unittests/ADT/ArrayRefTest.cpp
+++ b/unittests/ADT/ArrayRefTest.cpp
@@ -65,6 +65,21 @@ TEST(ArrayRefTest, DropBack) {
ArrayRef<int> AR1(TheNumbers);
ArrayRef<int> AR2(TheNumbers, AR1.size() - 1);
EXPECT_TRUE(AR1.drop_back().equals(AR2));
+
+ // Check that drop_back accepts size_t-sized numbers.
+ ArrayRef<char> AR3((const char *)0x10000, SIZE_MAX - 0x10000);
+ EXPECT_EQ(1U, AR3.drop_back(AR3.size() - 1).size());
+}
+
+TEST(ArrayRefTest, DropFront) {
+ static const int TheNumbers[] = {4, 8, 15, 16, 23, 42};
+ ArrayRef<int> AR1(TheNumbers);
+ ArrayRef<int> AR2(&TheNumbers[2], AR1.size() - 2);
+ EXPECT_TRUE(AR1.drop_front(2).equals(AR2));
+
+ // Check that drop_front accepts size_t-sized numbers.
+ ArrayRef<char> AR3((const char *)0x10000, SIZE_MAX - 0x10000);
+ EXPECT_EQ(1U, AR3.drop_front(AR3.size() - 1).size());
}
TEST(ArrayRefTest, Equals) {
@@ -94,6 +109,13 @@ TEST(ArrayRefTest, EmptyEquals) {
EXPECT_TRUE(ArrayRef<unsigned>() == ArrayRef<unsigned>());
}
+TEST(ArrayRefTest, Slice) {
+ // Check that slice accepts size_t-sized numbers.
+ ArrayRef<char> AR((const char *)0x10000, SIZE_MAX - 0x10000);
+ EXPECT_EQ(1U, AR.slice(AR.size() - 1).size());
+ EXPECT_EQ(AR.size() - 1, AR.slice(1, AR.size() - 1).size());
+}
+
TEST(ArrayRefTest, ConstConvert) {
int buf[4];
for (int i = 0; i < 4; ++i)
diff --git a/unittests/ADT/BitVectorTest.cpp b/unittests/ADT/BitVectorTest.cpp
index 95ff93fa9c4c..78fd5ce65677 100644
--- a/unittests/ADT/BitVectorTest.cpp
+++ b/unittests/ADT/BitVectorTest.cpp
@@ -399,5 +399,31 @@ TYPED_TEST(BitVectorTest, CompoundTestReset) {
C.reset(C);
EXPECT_TRUE(C.none());
}
+
+TYPED_TEST(BitVectorTest, MoveConstructor) {
+ TypeParam A(10, true);
+ TypeParam B(std::move(A));
+ // Check that the move ctor leaves the moved-from object in a valid state.
+ // The following line used to crash.
+ A = B;
+
+ TypeParam C(10, true);
+ EXPECT_EQ(C, A);
+ EXPECT_EQ(C, B);
+}
+
+TYPED_TEST(BitVectorTest, MoveAssignment) {
+ TypeParam A(10, true);
+ TypeParam B;
+ B = std::move(A);
+ // Check that move assignment leaves the moved-from object in a valid state.
+ // The following line used to crash.
+ A = B;
+
+ TypeParam C(10, true);
+ EXPECT_EQ(C, A);
+ EXPECT_EQ(C, B);
+}
+
}
#endif
diff --git a/unittests/ADT/BitmaskEnumTest.cpp b/unittests/ADT/BitmaskEnumTest.cpp
new file mode 100644
index 000000000000..77635c638e93
--- /dev/null
+++ b/unittests/ADT/BitmaskEnumTest.cpp
@@ -0,0 +1,134 @@
+//===- llvm/unittest/ADT/BitmaskEnumTest.cpp - BitmaskEnum unit tests -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/BitmaskEnum.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+enum Flags {
+ F0 = 0,
+ F1 = 1,
+ F2 = 2,
+ F3 = 4,
+ F4 = 8,
+ LLVM_MARK_AS_BITMASK_ENUM(F4)
+};
+
+TEST(BitmaskEnumTest, BitwiseOr) {
+ Flags f = F1 | F2;
+ EXPECT_EQ(3, f);
+
+ f = f | F3;
+ EXPECT_EQ(7, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseOrEquals) {
+ Flags f = F1;
+ f |= F3;
+ EXPECT_EQ(5, f);
+
+ // |= should return a reference to the LHS.
+ f = F2;
+ (f |= F3) = F1;
+ EXPECT_EQ(F1, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseAnd) {
+ Flags f = static_cast<Flags>(3) & F2;
+ EXPECT_EQ(F2, f);
+
+ f = (f | F3) & (F1 | F2 | F3);
+ EXPECT_EQ(6, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseAndEquals) {
+ Flags f = F1 | F2 | F3;
+ f &= F1 | F2;
+ EXPECT_EQ(3, f);
+
+ // &= should return a reference to the LHS.
+ (f &= F1) = F3;
+ EXPECT_EQ(F3, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseXor) {
+ Flags f = (F1 | F2) ^ (F2 | F3);
+ EXPECT_EQ(5, f);
+
+ f = f ^ F1;
+ EXPECT_EQ(4, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseXorEquals) {
+ Flags f = (F1 | F2);
+ f ^= (F2 | F4);
+ EXPECT_EQ(9, f);
+
+ // ^= should return a reference to the LHS.
+ (f ^= F4) = F3;
+ EXPECT_EQ(F3, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseNot) {
+ Flags f = ~F1;
+ EXPECT_EQ(14, f); // Largest value for f is 15.
+ EXPECT_EQ(15, ~F0);
+}
+
+enum class FlagsClass {
+ F0 = 0,
+ F1 = 1,
+ F2 = 2,
+ F3 = 4,
+ LLVM_MARK_AS_BITMASK_ENUM(F3)
+};
+
+TEST(BitmaskEnumTest, ScopedEnum) {
+ FlagsClass f = (FlagsClass::F1 & ~FlagsClass::F0) | FlagsClass::F2;
+ f |= FlagsClass::F3;
+ EXPECT_EQ(7, static_cast<int>(f));
+}
+
+struct Container {
+ enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, LLVM_MARK_AS_BITMASK_ENUM(F3) };
+
+ static Flags getFlags() {
+ Flags f = F0 | F1;
+ f |= F2;
+ return f;
+ }
+};
+
+TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); }
+
+} // namespace
+
+namespace foo {
+namespace bar {
+namespace {
+enum FlagsInNamespace {
+ F0 = 0,
+ F1 = 1,
+ F2 = 2,
+ F3 = 4,
+ LLVM_MARK_AS_BITMASK_ENUM(F3)
+};
+} // namespace
+} // namespace foo
+} // namespace bar
+
+namespace {
+TEST(BitmaskEnumTest, EnumInNamespace) {
+ foo::bar::FlagsInNamespace f = ~foo::bar::F0 & (foo::bar::F1 | foo::bar::F2);
+ f |= foo::bar::F3;
+ EXPECT_EQ(7, f);
+}
+} // namespace
diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt
index bce1bf93a338..ca1644b5346e 100644
--- a/unittests/ADT/CMakeLists.txt
+++ b/unittests/ADT/CMakeLists.txt
@@ -7,6 +7,7 @@ set(ADTSources
APIntTest.cpp
APSIntTest.cpp
ArrayRefTest.cpp
+ BitmaskEnumTest.cpp
BitVectorTest.cpp
DAGDeltaAlgorithmTest.cpp
DeltaAlgorithmTest.cpp
@@ -30,8 +31,11 @@ set(ADTSources
PointerSumTypeTest.cpp
PointerUnionTest.cpp
PostOrderIteratorTest.cpp
+ PriorityWorklistTest.cpp
RangeAdapterTest.cpp
SCCIteratorTest.cpp
+ SequenceTest.cpp
+ SetVectorTest.cpp
SmallPtrSetTest.cpp
SmallStringTest.cpp
SmallVectorTest.cpp
diff --git a/unittests/ADT/DenseMapTest.cpp b/unittests/ADT/DenseMapTest.cpp
index f3dcf95e92fd..db00f8cf8e57 100644
--- a/unittests/ADT/DenseMapTest.cpp
+++ b/unittests/ADT/DenseMapTest.cpp
@@ -339,6 +339,138 @@ TYPED_TEST(DenseMapTest, ConstIteratorTest) {
EXPECT_TRUE(cit == cit2);
}
+namespace {
+// Simple class that counts how many moves and copy happens when growing a map
+struct CountCopyAndMove {
+ static int Move;
+ static int Copy;
+ CountCopyAndMove() {}
+
+ CountCopyAndMove(const CountCopyAndMove &) { Copy++; }
+ CountCopyAndMove &operator=(const CountCopyAndMove &) {
+ Copy++;
+ return *this;
+ }
+ CountCopyAndMove(CountCopyAndMove &&) { Move++; }
+ CountCopyAndMove &operator=(const CountCopyAndMove &&) {
+ Move++;
+ return *this;
+ }
+};
+int CountCopyAndMove::Copy = 0;
+int CountCopyAndMove::Move = 0;
+
+} // anonymous namespace
+
+// Test for the default minimum size of a DenseMap
+TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) {
+ // IF THIS VALUE CHANGE, please update InitialSizeTest, InitFromIterator, and
+ // ReserveTest as well!
+ const int ExpectedInitialBucketCount = 64;
+ // Formula from DenseMap::getMinBucketToReserveForEntries()
+ const int ExpectedMaxInitialEntries = ExpectedInitialBucketCount * 3 / 4 - 1;
+
+ DenseMap<int, CountCopyAndMove> Map;
+ // Will allocate 64 buckets
+ Map.reserve(1);
+ unsigned MemorySize = Map.getMemorySize();
+ CountCopyAndMove::Copy = 0;
+ CountCopyAndMove::Move = 0;
+ for (int i = 0; i < ExpectedMaxInitialEntries; ++i)
+ Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,
+ std::forward_as_tuple(i),
+ std::forward_as_tuple()));
+ // Check that we didn't grow
+ EXPECT_EQ(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ EXPECT_EQ(ExpectedMaxInitialEntries, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+
+ // Adding one extra element should grow the map
+ Map.insert(std::pair<int, CountCopyAndMove>(
+ std::piecewise_construct,
+ std::forward_as_tuple(ExpectedMaxInitialEntries),
+ std::forward_as_tuple()));
+ // Check that we grew
+ EXPECT_NE(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ // This relies on move-construction elision, and cannot be reliably tested.
+ // EXPECT_EQ(ExpectedMaxInitialEntries + 2, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+}
+
+// Make sure creating the map with an initial size of N actually gives us enough
+// buckets to insert N items without increasing allocation size.
+TEST(DenseMapCustomTest, InitialSizeTest) {
+ // Test a few different sizes, 48 is *not* a random choice: we need a value
+ // that is 2/3 of a power of two to stress the grow() condition, and the power
+ // of two has to be at least 64 because of minimum size allocation in the
+ // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the
+ // 64 default init.
+ for (auto Size : {1, 2, 48, 66}) {
+ DenseMap<int, CountCopyAndMove> Map(Size);
+ unsigned MemorySize = Map.getMemorySize();
+ CountCopyAndMove::Copy = 0;
+ CountCopyAndMove::Move = 0;
+ for (int i = 0; i < Size; ++i)
+ Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,
+ std::forward_as_tuple(i),
+ std::forward_as_tuple()));
+ // Check that we didn't grow
+ EXPECT_EQ(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ EXPECT_EQ(Size, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+ }
+}
+
+// Make sure creating the map with a iterator range does not trigger grow()
+TEST(DenseMapCustomTest, InitFromIterator) {
+ std::vector<std::pair<int, CountCopyAndMove>> Values;
+ // The size is a random value greater than 64 (hardcoded DenseMap min init)
+ const int Count = 65;
+ for (int i = 0; i < Count; i++)
+ Values.emplace_back(i, CountCopyAndMove());
+
+ CountCopyAndMove::Move = 0;
+ CountCopyAndMove::Copy = 0;
+ DenseMap<int, CountCopyAndMove> Map(Values.begin(), Values.end());
+ // Check that no move occured
+ EXPECT_EQ(0, CountCopyAndMove::Move);
+ // Check that copy was called the expected number of times
+ EXPECT_EQ(Count, CountCopyAndMove::Copy);
+}
+
+// Make sure reserve actually gives us enough buckets to insert N items
+// without increasing allocation size.
+TEST(DenseMapCustomTest, ReserveTest) {
+ // Test a few different size, 48 is *not* a random choice: we need a value
+ // that is 2/3 of a power of two to stress the grow() condition, and the power
+ // of two has to be at least 64 because of minimum size allocation in the
+ // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the
+ // 64 default init.
+ for (auto Size : {1, 2, 48, 66}) {
+ DenseMap<int, CountCopyAndMove> Map;
+ Map.reserve(Size);
+ unsigned MemorySize = Map.getMemorySize();
+ CountCopyAndMove::Copy = 0;
+ CountCopyAndMove::Move = 0;
+ for (int i = 0; i < Size; ++i)
+ Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,
+ std::forward_as_tuple(i),
+ std::forward_as_tuple()));
+ // Check that we didn't grow
+ EXPECT_EQ(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ EXPECT_EQ(Size, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+ }
+}
+
// Make sure DenseMap works with StringRef keys.
TEST(DenseMapCustomTest, StringRefTest) {
DenseMap<StringRef, int> M;
@@ -364,6 +496,55 @@ TEST(DenseMapCustomTest, StringRefTest) {
EXPECT_EQ(42, M.lookup(StringRef("a", 0)));
}
+struct CachedHashTest {
+ unsigned Val;
+ unsigned *Counter = nullptr;
+ CachedHashTest(unsigned Val) : Val(Val) {}
+ CachedHashTest(unsigned Val, unsigned *Counter)
+ : Val(Val), Counter(Counter) {}
+};
+}
+namespace llvm {
+template <> struct DenseMapInfo<CachedHashTest> {
+ static CachedHashTest getEmptyKey() { return ~0; }
+ static CachedHashTest getTombstoneKey() { return ~0U - 1; }
+ static unsigned getHashValue(const CachedHashTest &X) {
+ ++*X.Counter;
+ return X.Val;
+ }
+ static bool isEqual(const CachedHashTest &LHS, const CachedHashTest &RHS) {
+ return LHS.Val == RHS.Val;
+ }
+};
+}
+namespace {
+
+TEST(DenseMapCustomTest, CachedHashTest) {
+ unsigned Counter = 0;
+ CachedHashTest Val(0, &Counter);
+ DenseMap<CachedHashTest, int> Map;
+
+ Map[Val] = 0;
+ ASSERT_EQ(1u, Counter);
+
+ Map.reserve(64);
+ ASSERT_EQ(2u, Counter);
+}
+
+// Like above, but now cache the hash.
+TEST(DenseMapCustomTest, CachedHashTest2) {
+ unsigned Counter = 0;
+ CachedHashTest Val(0, &Counter);
+ typedef CachedHash<CachedHashTest> Cached;
+ DenseMap<Cached, int> Map;
+
+ Map[Val] = 0;
+ ASSERT_EQ(1u, Counter);
+
+ Map.reserve(64);
+ ASSERT_EQ(1u, Counter);
+}
+
// Key traits that allows lookup with either an unsigned or char* key;
// In the latter case, "a" == 0, "b" == 1 and so on.
struct TestDenseMapInfo {
diff --git a/unittests/ADT/FoldingSet.cpp b/unittests/ADT/FoldingSet.cpp
index 5addf27e1363..927ef313cb93 100644
--- a/unittests/ADT/FoldingSet.cpp
+++ b/unittests/ADT/FoldingSet.cpp
@@ -35,5 +35,138 @@ TEST(FoldingSetTest, UnalignedStringTest) {
EXPECT_EQ(a.ComputeHash(), b.ComputeHash());
}
+struct TrivialPair : public FoldingSetNode {
+ unsigned Key = 0;
+ unsigned Value = 0;
+ TrivialPair(unsigned K, unsigned V) : FoldingSetNode(), Key(K), Value(V) {}
+
+ void Profile(FoldingSetNodeID &ID) const {
+ ID.AddInteger(Key);
+ ID.AddInteger(Value);
+ }
+};
+
+TEST(FoldingSetTest, IDComparison) {
+ FoldingSet<TrivialPair> Trivial;
+
+ TrivialPair T(99, 42);
+ Trivial.InsertNode(&T);
+
+ void *InsertPos = nullptr;
+ FoldingSetNodeID ID;
+ T.Profile(ID);
+ TrivialPair *N = Trivial.FindNodeOrInsertPos(ID, InsertPos);
+ EXPECT_EQ(&T, N);
+ EXPECT_EQ(nullptr, InsertPos);
+}
+
+TEST(FoldingSetTest, MissedIDComparison) {
+ FoldingSet<TrivialPair> Trivial;
+
+ TrivialPair S(100, 42);
+ TrivialPair T(99, 42);
+ Trivial.InsertNode(&T);
+
+ void *InsertPos = nullptr;
+ FoldingSetNodeID ID;
+ S.Profile(ID);
+ TrivialPair *N = Trivial.FindNodeOrInsertPos(ID, InsertPos);
+ EXPECT_EQ(nullptr, N);
+ EXPECT_NE(nullptr, InsertPos);
+}
+
+TEST(FoldingSetTest, RemoveNodeThatIsPresent) {
+ FoldingSet<TrivialPair> Trivial;
+
+ TrivialPair T(99, 42);
+ Trivial.InsertNode(&T);
+ EXPECT_EQ(Trivial.size(), 1U);
+
+ bool WasThere = Trivial.RemoveNode(&T);
+ EXPECT_TRUE(WasThere);
+ EXPECT_EQ(0U, Trivial.size());
+}
+
+TEST(FoldingSetTest, RemoveNodeThatIsAbsent) {
+ FoldingSet<TrivialPair> Trivial;
+
+ TrivialPair T(99, 42);
+ bool WasThere = Trivial.RemoveNode(&T);
+ EXPECT_FALSE(WasThere);
+ EXPECT_EQ(0U, Trivial.size());
+}
+
+TEST(FoldingSetTest, GetOrInsertInserting) {
+ FoldingSet<TrivialPair> Trivial;
+
+ TrivialPair T(99, 42);
+ TrivialPair *N = Trivial.GetOrInsertNode(&T);
+ EXPECT_EQ(&T, N);
+}
+
+TEST(FoldingSetTest, GetOrInsertGetting) {
+ FoldingSet<TrivialPair> Trivial;
+
+ TrivialPair T(99, 42);
+ TrivialPair T2(99, 42);
+ Trivial.InsertNode(&T);
+ TrivialPair *N = Trivial.GetOrInsertNode(&T2);
+ EXPECT_EQ(&T, N);
+}
+
+TEST(FoldingSetTest, InsertAtPos) {
+ FoldingSet<TrivialPair> Trivial;
+
+ void *InsertPos = nullptr;
+ TrivialPair Finder(99, 42);
+ FoldingSetNodeID ID;
+ Finder.Profile(ID);
+ Trivial.FindNodeOrInsertPos(ID, InsertPos);
+
+ TrivialPair T(99, 42);
+ Trivial.InsertNode(&T, InsertPos);
+ EXPECT_EQ(1U, Trivial.size());
+}
+
+TEST(FoldingSetTest, EmptyIsTrue) {
+ FoldingSet<TrivialPair> Trivial;
+ EXPECT_TRUE(Trivial.empty());
+}
+
+TEST(FoldingSetTest, EmptyIsFalse) {
+ FoldingSet<TrivialPair> Trivial;
+ TrivialPair T(99, 42);
+ Trivial.InsertNode(&T);
+ EXPECT_FALSE(Trivial.empty());
+}
+
+TEST(FoldingSetTest, ClearOnEmpty) {
+ FoldingSet<TrivialPair> Trivial;
+ Trivial.clear();
+ EXPECT_TRUE(Trivial.empty());
+}
+
+TEST(FoldingSetTest, ClearOnNonEmpty) {
+ FoldingSet<TrivialPair> Trivial;
+ TrivialPair T(99, 42);
+ Trivial.InsertNode(&T);
+ Trivial.clear();
+ EXPECT_TRUE(Trivial.empty());
+}
+
+TEST(FoldingSetTest, CapacityLargerThanReserve) {
+ FoldingSet<TrivialPair> Trivial;
+ auto OldCapacity = Trivial.capacity();
+ Trivial.reserve(OldCapacity + 1);
+ EXPECT_GE(Trivial.capacity(), OldCapacity + 1);
+}
+
+TEST(FoldingSetTest, SmallReserveChangesNothing) {
+ FoldingSet<TrivialPair> Trivial;
+ auto OldCapacity = Trivial.capacity();
+ Trivial.reserve(OldCapacity - 1);
+ EXPECT_EQ(Trivial.capacity(), OldCapacity);
+}
+
}
diff --git a/unittests/ADT/ImmutableSetTest.cpp b/unittests/ADT/ImmutableSetTest.cpp
index febd441db166..a6eb405db720 100644
--- a/unittests/ADT/ImmutableSetTest.cpp
+++ b/unittests/ADT/ImmutableSetTest.cpp
@@ -181,19 +181,22 @@ TEST_F(ImmutableSetTest, IterLongSetTest) {
int i = 0;
for (ImmutableSet<long>::iterator I = S.begin(), E = S.end(); I != E; ++I) {
- ASSERT_EQ(i++, *I);
+ ASSERT_EQ(i, *I);
+ i++;
}
ASSERT_EQ(0, i);
i = 0;
for (ImmutableSet<long>::iterator I = S2.begin(), E = S2.end(); I != E; ++I) {
- ASSERT_EQ(i++, *I);
+ ASSERT_EQ(i, *I);
+ i++;
}
ASSERT_EQ(3, i);
i = 0;
for (ImmutableSet<long>::iterator I = S3.begin(), E = S3.end(); I != E; I++) {
- ASSERT_EQ(i++, *I);
+ ASSERT_EQ(i, *I);
+ i++;
}
ASSERT_EQ(6, i);
}
diff --git a/unittests/ADT/Makefile b/unittests/ADT/Makefile
deleted file mode 100644
index c255a0b44d09..000000000000
--- a/unittests/ADT/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-##===- unittests/ADT/Makefile ------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = ADT
-LINK_COMPONENTS := support
-
-include $(LEVEL)/Makefile.config
-
-# Xfail BitVectorTest for now on PPC Darwin. 7598360.
-ifeq ($(ARCH),PowerPC)
-ifeq ($(TARGET_OS),Darwin)
-CPP.Flags += -DXFAIL
-endif
-endif
-
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/ADT/PointerEmbeddedIntTest.cpp b/unittests/ADT/PointerEmbeddedIntTest.cpp
index b10365a2f618..9c27f8ee655f 100644
--- a/unittests/ADT/PointerEmbeddedIntTest.cpp
+++ b/unittests/ADT/PointerEmbeddedIntTest.cpp
@@ -43,4 +43,38 @@ TEST(PointerEmbeddedIntTest, Basic) {
EXPECT_FALSE(42 >= J);
}
+TEST(PointerEmbeddedIntTest, intptr_t) {
+ PointerEmbeddedInt<intptr_t, CHAR_BIT> IPos = 42, INeg = -42;
+ EXPECT_EQ(42, IPos);
+ EXPECT_EQ(-42, INeg);
+
+ PointerEmbeddedInt<uintptr_t, CHAR_BIT> U = 42, USaturated = 255;
+ EXPECT_EQ(42U, U);
+ EXPECT_EQ(255U, USaturated);
+
+ PointerEmbeddedInt<intptr_t, std::numeric_limits<intptr_t>::digits>
+ IMax = std::numeric_limits<intptr_t>::max() >> 1,
+ IMin = std::numeric_limits<intptr_t>::min() >> 1;
+ EXPECT_EQ(std::numeric_limits<intptr_t>::max() >> 1, IMax);
+ EXPECT_EQ(std::numeric_limits<intptr_t>::min() >> 1, IMin);
+
+ PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1>
+ UMax = std::numeric_limits<uintptr_t>::max() >> 1,
+ UMin = std::numeric_limits<uintptr_t>::min() >> 1;
+ EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1, UMax);
+ EXPECT_EQ(std::numeric_limits<uintptr_t>::min() >> 1, UMin);
+}
+
+TEST(PointerEmbeddedIntTest, PointerLikeTypeTraits) {
+ PointerEmbeddedInt<int, CHAR_BIT> I = 42;
+ using ITraits = PointerLikeTypeTraits<decltype(I)>;
+ EXPECT_EQ(42, ITraits::getFromVoidPointer(ITraits::getAsVoidPointer(I)));
+
+ PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1>
+ Max = std::numeric_limits<uintptr_t>::max() >> 1;
+ using MaxTraits = PointerLikeTypeTraits<decltype(Max)>;
+ EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1,
+ MaxTraits::getFromVoidPointer(MaxTraits::getAsVoidPointer(Max)));
+}
+
} // end anonymous namespace
diff --git a/unittests/ADT/PriorityWorklistTest.cpp b/unittests/ADT/PriorityWorklistTest.cpp
new file mode 100644
index 000000000000..bbe026434c63
--- /dev/null
+++ b/unittests/ADT/PriorityWorklistTest.cpp
@@ -0,0 +1,106 @@
+//===- llvm/unittest/ADT/PriorityWorklist.cpp -----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// PriorityWorklist unit tests.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/PriorityWorklist.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace llvm;
+
+template <typename T> class PriorityWorklistTest : public ::testing::Test {};
+typedef ::testing::Types<PriorityWorklist<int>, SmallPriorityWorklist<int, 2>>
+ TestTypes;
+TYPED_TEST_CASE(PriorityWorklistTest, TestTypes);
+
+TYPED_TEST(PriorityWorklistTest, Basic) {
+ TypeParam W;
+ EXPECT_TRUE(W.empty());
+ EXPECT_EQ(0u, W.size());
+ EXPECT_FALSE(W.count(42));
+
+ EXPECT_TRUE(W.insert(21));
+ EXPECT_TRUE(W.insert(42));
+ EXPECT_TRUE(W.insert(17));
+
+ EXPECT_FALSE(W.empty());
+ EXPECT_EQ(3u, W.size());
+ EXPECT_TRUE(W.count(42));
+
+ EXPECT_FALSE(W.erase(75));
+ EXPECT_EQ(3u, W.size());
+ EXPECT_EQ(17, W.back());
+
+ EXPECT_TRUE(W.erase(17));
+ EXPECT_FALSE(W.count(17));
+ EXPECT_EQ(2u, W.size());
+ EXPECT_EQ(42, W.back());
+
+ W.clear();
+ EXPECT_TRUE(W.empty());
+ EXPECT_EQ(0u, W.size());
+
+ EXPECT_TRUE(W.insert(21));
+ EXPECT_TRUE(W.insert(42));
+ EXPECT_TRUE(W.insert(12));
+ EXPECT_TRUE(W.insert(17));
+ EXPECT_TRUE(W.count(12));
+ EXPECT_TRUE(W.count(17));
+ EXPECT_EQ(4u, W.size());
+ EXPECT_EQ(17, W.back());
+ EXPECT_TRUE(W.erase(12));
+ EXPECT_FALSE(W.count(12));
+ EXPECT_TRUE(W.count(17));
+ EXPECT_EQ(3u, W.size());
+ EXPECT_EQ(17, W.back());
+
+ EXPECT_FALSE(W.insert(42));
+ EXPECT_EQ(3u, W.size());
+ EXPECT_EQ(42, W.pop_back_val());
+ EXPECT_EQ(17, W.pop_back_val());
+ EXPECT_EQ(21, W.pop_back_val());
+ EXPECT_TRUE(W.empty());
+}
+
+TYPED_TEST(PriorityWorklistTest, EraseIf) {
+ TypeParam W;
+ W.insert(23);
+ W.insert(10);
+ W.insert(47);
+ W.insert(42);
+ W.insert(23);
+ W.insert(13);
+ W.insert(26);
+ W.insert(42);
+ EXPECT_EQ(6u, W.size());
+
+ EXPECT_FALSE(W.erase_if([](int i) { return i > 100; }));
+ EXPECT_EQ(6u, W.size());
+ EXPECT_EQ(42, W.back());
+
+ EXPECT_TRUE(W.erase_if([](int i) {
+ assert(i != 0 && "Saw a null value!");
+ return (i & 1) == 0;
+ }));
+ EXPECT_EQ(3u, W.size());
+ EXPECT_FALSE(W.count(42));
+ EXPECT_FALSE(W.count(26));
+ EXPECT_FALSE(W.count(10));
+ EXPECT_FALSE(W.insert(47));
+ EXPECT_FALSE(W.insert(23));
+ EXPECT_EQ(23, W.pop_back_val());
+ EXPECT_EQ(47, W.pop_back_val());
+ EXPECT_EQ(13, W.pop_back_val());
+}
+
+}
diff --git a/unittests/ADT/SequenceTest.cpp b/unittests/ADT/SequenceTest.cpp
new file mode 100644
index 000000000000..cc82f86c30d5
--- /dev/null
+++ b/unittests/ADT/SequenceTest.cpp
@@ -0,0 +1,41 @@
+//===- SequenceTest.cpp - Unit tests for a sequence abstraciton -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Sequence.h"
+#include "gtest/gtest.h"
+
+#include <list>
+
+using namespace llvm;
+
+namespace {
+
+TEST(SequenceTest, Basic) {
+ int x = 0;
+ for (int i : seq(0, 10)) {
+ EXPECT_EQ(x, i);
+ x++;
+ }
+ EXPECT_EQ(10, x);
+
+ auto my_seq = seq(0, 4);
+ EXPECT_EQ(4, my_seq.end() - my_seq.begin());
+ for (int i : {0, 1, 2, 3})
+ EXPECT_EQ(i, (int)my_seq.begin()[i]);
+
+ EXPECT_TRUE(my_seq.begin() < my_seq.end());
+
+ auto adjusted_begin = my_seq.begin() + 2;
+ auto adjusted_end = my_seq.end() - 2;
+ EXPECT_TRUE(adjusted_begin == adjusted_end);
+ EXPECT_EQ(2, *adjusted_begin);
+ EXPECT_EQ(2, *adjusted_end);
+}
+
+} // anonymous namespace
diff --git a/unittests/ADT/SetVectorTest.cpp b/unittests/ADT/SetVectorTest.cpp
new file mode 100644
index 000000000000..b8cac0dc311e
--- /dev/null
+++ b/unittests/ADT/SetVectorTest.cpp
@@ -0,0 +1,34 @@
+//===- llvm/unittest/ADT/SetVector.cpp ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// SetVector unit tests.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SetVector.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(SetVector, EraseTest) {
+ SetVector<int> S;
+ S.insert(0);
+ S.insert(1);
+ S.insert(2);
+
+ auto I = S.erase(std::next(S.begin()));
+
+ // Test that the returned iterator is the expected one-after-erase
+ // and the size/contents is the expected sequence {0, 2}.
+ EXPECT_EQ(std::next(S.begin()), I);
+ EXPECT_EQ(2u, S.size());
+ EXPECT_EQ(0, *S.begin());
+ EXPECT_EQ(2, *std::next(S.begin()));
+}
+
diff --git a/unittests/ADT/SmallPtrSetTest.cpp b/unittests/ADT/SmallPtrSetTest.cpp
index fdd1cbb60040..d8d07b16cfe3 100644
--- a/unittests/ADT/SmallPtrSetTest.cpp
+++ b/unittests/ADT/SmallPtrSetTest.cpp
@@ -167,13 +167,29 @@ TEST(SmallPtrSetTest, SwapTest) {
a.insert(&buf[1]);
b.insert(&buf[2]);
+ EXPECT_EQ(2U, a.size());
+ EXPECT_EQ(1U, b.size());
+ EXPECT_TRUE(a.count(&buf[0]));
+ EXPECT_TRUE(a.count(&buf[1]));
+ EXPECT_FALSE(a.count(&buf[2]));
+ EXPECT_FALSE(a.count(&buf[3]));
+ EXPECT_FALSE(b.count(&buf[0]));
+ EXPECT_FALSE(b.count(&buf[1]));
+ EXPECT_TRUE(b.count(&buf[2]));
+ EXPECT_FALSE(b.count(&buf[3]));
+
std::swap(a, b);
EXPECT_EQ(1U, a.size());
EXPECT_EQ(2U, b.size());
+ EXPECT_FALSE(a.count(&buf[0]));
+ EXPECT_FALSE(a.count(&buf[1]));
EXPECT_TRUE(a.count(&buf[2]));
+ EXPECT_FALSE(a.count(&buf[3]));
EXPECT_TRUE(b.count(&buf[0]));
EXPECT_TRUE(b.count(&buf[1]));
+ EXPECT_FALSE(b.count(&buf[2]));
+ EXPECT_FALSE(b.count(&buf[3]));
b.insert(&buf[3]);
std::swap(a, b);
@@ -182,16 +198,24 @@ TEST(SmallPtrSetTest, SwapTest) {
EXPECT_EQ(1U, b.size());
EXPECT_TRUE(a.count(&buf[0]));
EXPECT_TRUE(a.count(&buf[1]));
+ EXPECT_FALSE(a.count(&buf[2]));
EXPECT_TRUE(a.count(&buf[3]));
+ EXPECT_FALSE(b.count(&buf[0]));
+ EXPECT_FALSE(b.count(&buf[1]));
EXPECT_TRUE(b.count(&buf[2]));
+ EXPECT_FALSE(b.count(&buf[3]));
std::swap(a, b);
EXPECT_EQ(1U, a.size());
EXPECT_EQ(3U, b.size());
+ EXPECT_FALSE(a.count(&buf[0]));
+ EXPECT_FALSE(a.count(&buf[1]));
EXPECT_TRUE(a.count(&buf[2]));
+ EXPECT_FALSE(a.count(&buf[3]));
EXPECT_TRUE(b.count(&buf[0]));
EXPECT_TRUE(b.count(&buf[1]));
+ EXPECT_FALSE(b.count(&buf[2]));
EXPECT_TRUE(b.count(&buf[3]));
a.insert(&buf[4]);
@@ -210,3 +234,42 @@ TEST(SmallPtrSetTest, SwapTest) {
EXPECT_TRUE(a.count(&buf[1]));
EXPECT_TRUE(a.count(&buf[3]));
}
+
+void checkEraseAndIterators(SmallPtrSetImpl<int*> &S) {
+ int buf[3];
+
+ S.insert(&buf[0]);
+ S.insert(&buf[1]);
+ S.insert(&buf[2]);
+
+ // Iterators must still be valid after erase() calls;
+ auto B = S.begin();
+ auto M = std::next(B);
+ auto E = S.end();
+ EXPECT_TRUE(*B == &buf[0] || *B == &buf[1] || *B == &buf[2]);
+ EXPECT_TRUE(*M == &buf[0] || *M == &buf[1] || *M == &buf[2]);
+ EXPECT_TRUE(*B != *M);
+ int *Removable = *std::next(M);
+ // No iterator points to Removable now.
+ EXPECT_TRUE(Removable == &buf[0] || Removable == &buf[1] ||
+ Removable == &buf[2]);
+ EXPECT_TRUE(Removable != *B && Removable != *M);
+
+ S.erase(Removable);
+
+ // B,M,E iterators should still be valid
+ EXPECT_EQ(B, S.begin());
+ EXPECT_EQ(M, std::next(B));
+ EXPECT_EQ(E, S.end());
+ EXPECT_EQ(std::next(M), E);
+}
+
+TEST(SmallPtrSetTest, EraseTest) {
+ // Test when set stays small.
+ SmallPtrSet<int *, 8> B;
+ checkEraseAndIterators(B);
+
+ // Test when set grows big.
+ SmallPtrSet<int *, 2> A;
+ checkEraseAndIterators(A);
+}
diff --git a/unittests/ADT/SmallVectorTest.cpp b/unittests/ADT/SmallVectorTest.cpp
index 46f7021ac165..7367ad470e3a 100644
--- a/unittests/ADT/SmallVectorTest.cpp
+++ b/unittests/ADT/SmallVectorTest.cpp
@@ -459,7 +459,8 @@ TYPED_TEST(SmallVectorTest, EraseTest) {
SCOPED_TRACE("EraseTest");
this->makeSequence(this->theVector, 1, 3);
- this->theVector.erase(this->theVector.begin());
+ const auto &theConstVector = this->theVector;
+ this->theVector.erase(theConstVector.begin());
this->assertValuesInOrder(this->theVector, 2u, 2, 3);
}
@@ -468,7 +469,8 @@ TYPED_TEST(SmallVectorTest, EraseRangeTest) {
SCOPED_TRACE("EraseRangeTest");
this->makeSequence(this->theVector, 1, 3);
- this->theVector.erase(this->theVector.begin(), this->theVector.begin() + 2);
+ const auto &theConstVector = this->theVector;
+ this->theVector.erase(theConstVector.begin(), theConstVector.begin() + 2);
this->assertValuesInOrder(this->theVector, 1u, 3);
}
diff --git a/unittests/ADT/SparseSetTest.cpp b/unittests/ADT/SparseSetTest.cpp
index eb0e0db283b7..4db7a7d61fb2 100644
--- a/unittests/ADT/SparseSetTest.cpp
+++ b/unittests/ADT/SparseSetTest.cpp
@@ -183,4 +183,24 @@ TEST(SparseSetTest, AltStructSet) {
EXPECT_FALSE(Set.erase(5));
EXPECT_TRUE(Set.erase(6));
}
+
+TEST(SparseSetTest, PopBack) {
+ USet Set;
+ const unsigned UpperBound = 300;
+ Set.setUniverse(UpperBound);
+ for (unsigned i = 0; i < UpperBound; ++i)
+ Set.insert(i);
+
+ // Make sure pop back returns the values in the reverse order we
+ // inserted them.
+ unsigned Expected = UpperBound;
+ while (!Set.empty())
+ ASSERT_TRUE(--Expected == Set.pop_back_val());
+
+ // Insert again the same elements in the sparse set and make sure
+ // each insertion actually inserts the elements. I.e., check
+ // that the underlying data structure are properly cleared.
+ for (unsigned i = 0; i < UpperBound; ++i)
+ ASSERT_TRUE(Set.insert(i).second);
+}
} // namespace
diff --git a/unittests/ADT/StringMapTest.cpp b/unittests/ADT/StringMapTest.cpp
index 4ed0b76f0f41..6ca701bb23a5 100644
--- a/unittests/ADT/StringMapTest.cpp
+++ b/unittests/ADT/StringMapTest.cpp
@@ -7,9 +7,10 @@
//
//===----------------------------------------------------------------------===//
-#include "gtest/gtest.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/Support/DataTypes.h"
+#include "gtest/gtest.h"
#include <tuple>
using namespace llvm;
@@ -157,6 +158,33 @@ TEST_F(StringMapTest, SmallFullMapTest) {
EXPECT_EQ(5, Map.lookup("funf"));
}
+TEST_F(StringMapTest, CopyCtorTest) {
+ llvm::StringMap<int> Map;
+
+ Map["eins"] = 1;
+ Map["zwei"] = 2;
+ Map["drei"] = 3;
+ Map.erase("drei");
+ Map.erase("eins");
+ Map["veir"] = 4;
+ Map["funf"] = 5;
+
+ EXPECT_EQ(3u, Map.size());
+ EXPECT_EQ(0, Map.lookup("eins"));
+ EXPECT_EQ(2, Map.lookup("zwei"));
+ EXPECT_EQ(0, Map.lookup("drei"));
+ EXPECT_EQ(4, Map.lookup("veir"));
+ EXPECT_EQ(5, Map.lookup("funf"));
+
+ llvm::StringMap<int> Map2(Map);
+ EXPECT_EQ(3u, Map2.size());
+ EXPECT_EQ(0, Map2.lookup("eins"));
+ EXPECT_EQ(2, Map2.lookup("zwei"));
+ EXPECT_EQ(0, Map2.lookup("drei"));
+ EXPECT_EQ(4, Map2.lookup("veir"));
+ EXPECT_EQ(5, Map2.lookup("funf"));
+}
+
// A more complex iteration test.
TEST_F(StringMapTest, IterationTest) {
bool visited[100];
@@ -231,12 +259,12 @@ TEST_F(StringMapTest, InsertRehashingPairTest) {
// moved to a different bucket during internal rehashing. This depends on
// the particular key, and the implementation of StringMap and HashString.
// Changes to those might result in this test not actually checking that.
- StringMap<uint32_t> t(1);
- EXPECT_EQ(1u, t.getNumBuckets());
+ StringMap<uint32_t> t(0);
+ EXPECT_EQ(0u, t.getNumBuckets());
StringMap<uint32_t>::iterator It =
t.insert(std::make_pair("abcdef", 42)).first;
- EXPECT_EQ(2u, t.getNumBuckets());
+ EXPECT_EQ(16u, t.getNumBuckets());
EXPECT_EQ("abcdef", It->first());
EXPECT_EQ(42u, It->second);
}
@@ -356,4 +384,83 @@ TEST_F(StringMapTest, MoveDtor) {
ASSERT_TRUE(B.empty());
}
+namespace {
+// Simple class that counts how many moves and copy happens when growing a map
+struct CountCtorCopyAndMove {
+ static unsigned Ctor;
+ static unsigned Move;
+ static unsigned Copy;
+ int Data = 0;
+ CountCtorCopyAndMove(int Data) : Data(Data) { Ctor++; }
+ CountCtorCopyAndMove() { Ctor++; }
+
+ CountCtorCopyAndMove(const CountCtorCopyAndMove &) { Copy++; }
+ CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &) {
+ Copy++;
+ return *this;
+ }
+ CountCtorCopyAndMove(CountCtorCopyAndMove &&) { Move++; }
+ CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &&) {
+ Move++;
+ return *this;
+ }
+};
+unsigned CountCtorCopyAndMove::Copy = 0;
+unsigned CountCtorCopyAndMove::Move = 0;
+unsigned CountCtorCopyAndMove::Ctor = 0;
+
+} // anonymous namespace
+
+// Make sure creating the map with an initial size of N actually gives us enough
+// buckets to insert N items without increasing allocation size.
+TEST(StringMapCustomTest, InitialSizeTest) {
+ // 1 is an "edge value", 32 is an arbitrary power of two, and 67 is an
+ // arbitrary prime, picked without any good reason.
+ for (auto Size : {1, 32, 67}) {
+ StringMap<CountCtorCopyAndMove> Map(Size);
+ auto NumBuckets = Map.getNumBuckets();
+ CountCtorCopyAndMove::Move = 0;
+ CountCtorCopyAndMove::Copy = 0;
+ for (int i = 0; i < Size; ++i)
+ Map.insert(std::pair<std::string, CountCtorCopyAndMove>(
+ std::piecewise_construct, std::forward_as_tuple(Twine(i).str()),
+ std::forward_as_tuple(i)));
+ // After the inital move, the map will move the Elts in the Entry.
+ EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move);
+ // We copy once the pair from the Elts vector
+ EXPECT_EQ(0u, CountCtorCopyAndMove::Copy);
+ // Check that the map didn't grow
+ EXPECT_EQ(Map.getNumBuckets(), NumBuckets);
+ }
+}
+
+TEST(StringMapCustomTest, BracketOperatorCtor) {
+ StringMap<CountCtorCopyAndMove> Map;
+ CountCtorCopyAndMove::Ctor = 0;
+ Map["abcd"];
+ EXPECT_EQ(1u, CountCtorCopyAndMove::Ctor);
+ // Test that operator[] does not create a value when it is already in the map
+ CountCtorCopyAndMove::Ctor = 0;
+ Map["abcd"];
+ EXPECT_EQ(0u, CountCtorCopyAndMove::Ctor);
+}
+
+namespace {
+struct NonMoveableNonCopyableType {
+ int Data = 0;
+ NonMoveableNonCopyableType() = default;
+ NonMoveableNonCopyableType(int Data) : Data(Data) {}
+ NonMoveableNonCopyableType(const NonMoveableNonCopyableType &) = delete;
+ NonMoveableNonCopyableType(NonMoveableNonCopyableType &&) = delete;
+};
+}
+
+// Test that we can "emplace" an element in the map without involving map/move
+TEST(StringMapCustomTest, EmplaceTest) {
+ StringMap<NonMoveableNonCopyableType> Map;
+ Map.emplace_second("abcd", 42);
+ EXPECT_EQ(1u, Map.count("abcd"));
+ EXPECT_EQ(42, Map["abcd"].Data);
+}
+
} // end anonymous namespace
diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp
index 6cf2e6a0454d..66e5944b56eb 100644
--- a/unittests/ADT/StringRefTest.cpp
+++ b/unittests/ADT/StringRefTest.cpp
@@ -301,7 +301,7 @@ TEST(StringRefTest, Trim) {
EXPECT_EQ(StringRef(""), StringRef(" ").trim());
EXPECT_EQ(StringRef("\0", 1), StringRef(" \0 ", 3).trim());
EXPECT_EQ(StringRef("\0\0", 2), StringRef("\0\0", 2).trim());
- EXPECT_EQ(StringRef("x"), StringRef("\0\0x\0\0", 5).trim(StringRef("\0", 1)));
+ EXPECT_EQ(StringRef("x"), StringRef("\0\0x\0\0", 5).trim('\0'));
}
TEST(StringRefTest, StartsWith) {
@@ -589,6 +589,15 @@ TEST(StringRefTest, joinStrings) {
TEST(StringRefTest, AllocatorCopy) {
BumpPtrAllocator Alloc;
+ // First test empty strings. We don't want these to allocate anything on the
+ // allocator.
+ StringRef StrEmpty = "";
+ StringRef StrEmptyc = StrEmpty.copy(Alloc);
+ EXPECT_TRUE(StrEmpty.equals(StrEmptyc));
+ EXPECT_EQ(StrEmptyc.data(), nullptr);
+ EXPECT_EQ(StrEmptyc.size(), 0u);
+ EXPECT_EQ(Alloc.getTotalMemory(), 0u);
+
StringRef Str1 = "hello";
StringRef Str2 = "bye";
StringRef Str1c = Str1.copy(Alloc);
diff --git a/unittests/ADT/TinyPtrVectorTest.cpp b/unittests/ADT/TinyPtrVectorTest.cpp
index 294dfac0c633..26189b76394f 100644
--- a/unittests/ADT/TinyPtrVectorTest.cpp
+++ b/unittests/ADT/TinyPtrVectorTest.cpp
@@ -14,11 +14,9 @@
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/type_traits.h"
#include "gtest/gtest.h"
#include <algorithm>
-#include <list>
#include <vector>
using namespace llvm;
diff --git a/unittests/ADT/TripleTest.cpp b/unittests/ADT/TripleTest.cpp
index ac4fa2274e9e..984f4a2a595a 100644
--- a/unittests/ADT/TripleTest.cpp
+++ b/unittests/ADT/TripleTest.cpp
@@ -93,6 +93,12 @@ TEST(TripleTest, ParsedIDs) {
EXPECT_EQ(Triple::Linux, T.getOS());
EXPECT_EQ(Triple::GNU, T.getEnvironment());
+ T = Triple("x86_64-pc-linux-musl");
+ EXPECT_EQ(Triple::x86_64, T.getArch());
+ EXPECT_EQ(Triple::PC, T.getVendor());
+ EXPECT_EQ(Triple::Linux, T.getOS());
+ EXPECT_EQ(Triple::Musl, T.getEnvironment());
+
T = Triple("powerpc-bgp-linux");
EXPECT_EQ(Triple::ppc, T.getArch());
EXPECT_EQ(Triple::BGP, T.getVendor());
@@ -135,6 +141,12 @@ TEST(TripleTest, ParsedIDs) {
EXPECT_EQ(Triple::UnknownOS, T.getOS());
EXPECT_EQ(Triple::EABI, T.getEnvironment());
+ T = Triple("arm-none-linux-musleabi");
+ EXPECT_EQ(Triple::arm, T.getArch());
+ EXPECT_EQ(Triple::UnknownVendor, T.getVendor());
+ EXPECT_EQ(Triple::Linux, T.getOS());
+ EXPECT_EQ(Triple::MuslEABI, T.getEnvironment());
+
T = Triple("armv6hl-none-linux-gnueabi");
EXPECT_EQ(Triple::arm, T.getArch());
EXPECT_EQ(Triple::Linux, T.getOS());
@@ -212,6 +224,30 @@ TEST(TripleTest, ParsedIDs) {
EXPECT_EQ(Triple::UnknownOS, T.getOS());
EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+ T = Triple("lanai-unknown-unknown");
+ EXPECT_EQ(Triple::lanai, T.getArch());
+ EXPECT_EQ(Triple::UnknownVendor, T.getVendor());
+ EXPECT_EQ(Triple::UnknownOS, T.getOS());
+ EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+
+ T = Triple("lanai");
+ EXPECT_EQ(Triple::lanai, T.getArch());
+ EXPECT_EQ(Triple::UnknownVendor, T.getVendor());
+ EXPECT_EQ(Triple::UnknownOS, T.getOS());
+ EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+
+ T = Triple("amdgcn-mesa-mesa3d");
+ EXPECT_EQ(Triple::amdgcn, T.getArch());
+ EXPECT_EQ(Triple::Mesa, T.getVendor());
+ EXPECT_EQ(Triple::Mesa3D, T.getOS());
+ EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+
+ T = Triple("amdgcn-amd-amdhsa");
+ EXPECT_EQ(Triple::amdgcn, T.getArch());
+ EXPECT_EQ(Triple::AMD, T.getVendor());
+ EXPECT_EQ(Triple::AMDHSA, T.getOS());
+ EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+
T = Triple("huh");
EXPECT_EQ(Triple::UnknownArch, T.getArch());
}
@@ -486,6 +522,11 @@ TEST(TripleTest, BitWidthPredicates) {
EXPECT_TRUE(T.isArch16Bit());
EXPECT_FALSE(T.isArch32Bit());
EXPECT_FALSE(T.isArch64Bit());
+
+ T.setArch(Triple::lanai);
+ EXPECT_FALSE(T.isArch16Bit());
+ EXPECT_TRUE(T.isArch32Bit());
+ EXPECT_FALSE(T.isArch64Bit());
}
TEST(TripleTest, BitWidthArchVariants) {
@@ -602,6 +643,14 @@ TEST(TripleTest, EndianArchVariants) {
T.setArch(Triple::arm);
EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());
EXPECT_EQ(Triple::arm, T.getLittleEndianArchVariant().getArch());
+ T = Triple("arm");
+ EXPECT_TRUE(T.isLittleEndian());
+ T = Triple("thumb");
+ EXPECT_TRUE(T.isLittleEndian());
+ T = Triple("armeb");
+ EXPECT_FALSE(T.isLittleEndian());
+ T = Triple("thumbeb");
+ EXPECT_FALSE(T.isLittleEndian());
T.setArch(Triple::bpfeb);
EXPECT_EQ(Triple::bpfeb, T.getBigEndianArchVariant().getArch());
@@ -654,6 +703,10 @@ TEST(TripleTest, EndianArchVariants) {
T.setArch(Triple::thumbeb);
EXPECT_EQ(Triple::thumbeb, T.getBigEndianArchVariant().getArch());
EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());
+
+ T.setArch(Triple::lanai);
+ EXPECT_EQ(Triple::lanai, T.getBigEndianArchVariant().getArch());
+ EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());
}
TEST(TripleTest, getOSVersion) {
@@ -962,6 +1015,18 @@ TEST(TripleTest, getARMCPUForArch) {
EXPECT_EQ("swift", Triple.getARMCPUForArch());
}
{
+ llvm::Triple Triple("armv7k-apple-ios9");
+ EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch());
+ }
+ {
+ llvm::Triple Triple("armv7k-apple-watchos3");
+ EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch());
+ }
+ {
+ llvm::Triple Triple("armv7k-apple-tvos9");
+ EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch());
+ }
+ {
llvm::Triple Triple("armv7em-apple-ios7");
EXPECT_EQ("cortex-m4", Triple.getARMCPUForArch());
}
diff --git a/unittests/Analysis/AliasAnalysisTest.cpp b/unittests/Analysis/AliasAnalysisTest.cpp
index ee116992fe76..84a04257bc27 100644
--- a/unittests/Analysis/AliasAnalysisTest.cpp
+++ b/unittests/Analysis/AliasAnalysisTest.cpp
@@ -14,12 +14,11 @@
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Constants.h"
-#include "llvm/IR/Instructions.h"
#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
@@ -80,9 +79,8 @@ struct TestCustomAAResult : AAResultBase<TestCustomAAResult> {
std::function<void()> CB;
- explicit TestCustomAAResult(const TargetLibraryInfo &TLI,
- std::function<void()> CB)
- : AAResultBase(TLI), CB(std::move(CB)) {}
+ explicit TestCustomAAResult(std::function<void()> CB)
+ : AAResultBase(), CB(std::move(CB)) {}
TestCustomAAResult(TestCustomAAResult &&Arg)
: AAResultBase(std::move(Arg)), CB(std::move(Arg.CB)) {}
@@ -117,8 +115,7 @@ public:
}
bool doInitialization(Module &M) override {
- Result.reset(new TestCustomAAResult(
- getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(), std::move(CB)));
+ Result.reset(new TestCustomAAResult(std::move(CB)));
return true;
}
@@ -155,7 +152,7 @@ protected:
AAResults &getAAResults(Function &F) {
// Reset the Function AA results first to clear out any references.
- AAR.reset(new AAResults());
+ AAR.reset(new AAResults(TLI));
// Build the various AA results and register them.
AC.reset(new AssumptionCache(F));
@@ -181,12 +178,12 @@ TEST_F(AliasAnalysisTest, getModRefInfo) {
auto *Load1 = new LoadInst(Addr, "load", BB);
auto *Add1 = BinaryOperator::CreateAdd(Value, Value, "add", BB);
auto *VAArg1 = new VAArgInst(Addr, PtrType, "vaarg", BB);
- auto *CmpXChg1 = new AtomicCmpXchgInst(Addr, ConstantInt::get(IntType, 0),
- ConstantInt::get(IntType, 1),
- Monotonic, Monotonic, CrossThread, BB);
+ auto *CmpXChg1 = new AtomicCmpXchgInst(
+ Addr, ConstantInt::get(IntType, 0), ConstantInt::get(IntType, 1),
+ AtomicOrdering::Monotonic, AtomicOrdering::Monotonic, CrossThread, BB);
auto *AtomicRMW =
new AtomicRMWInst(AtomicRMWInst::Xchg, Addr, ConstantInt::get(IntType, 1),
- Monotonic, CrossThread, BB);
+ AtomicOrdering::Monotonic, CrossThread, BB);
ReturnInst::Create(C, nullptr, BB);
@@ -209,14 +206,13 @@ TEST_F(AliasAnalysisTest, getModRefInfo) {
class AAPassInfraTest : public testing::Test {
protected:
- LLVMContext &C;
+ LLVMContext C;
SMDiagnostic Err;
std::unique_ptr<Module> M;
public:
AAPassInfraTest()
- : C(getGlobalContext()),
- M(parseAssemblyString("define i32 @f(i32* %x, i32* %y) {\n"
+ : M(parseAssemblyString("define i32 @f(i32* %x, i32* %y) {\n"
"entry:\n"
" %lx = load i32, i32* %x\n"
" %ly = load i32, i32* %y\n"
diff --git a/unittests/Analysis/BlockFrequencyInfoTest.cpp b/unittests/Analysis/BlockFrequencyInfoTest.cpp
new file mode 100644
index 000000000000..b3b0fcfb049b
--- /dev/null
+++ b/unittests/Analysis/BlockFrequencyInfoTest.cpp
@@ -0,0 +1,86 @@
+//===- BlockFrequencyInfoTest.cpp - BlockFrequencyInfo unit tests ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/BlockFrequencyInfo.h"
+#include "llvm/Analysis/BlockFrequencyInfoImpl.h"
+#include "llvm/Analysis/BranchProbabilityInfo.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace {
+
+class BlockFrequencyInfoTest : public testing::Test {
+protected:
+ std::unique_ptr<BranchProbabilityInfo> BPI;
+ std::unique_ptr<DominatorTree> DT;
+ std::unique_ptr<LoopInfo> LI;
+ LLVMContext C;
+
+ BlockFrequencyInfo buildBFI(Function &F) {
+ DT.reset(new DominatorTree(F));
+ LI.reset(new LoopInfo(*DT));
+ BPI.reset(new BranchProbabilityInfo(F, *LI));
+ return BlockFrequencyInfo(F, *BPI, *LI);
+ }
+ std::unique_ptr<Module> makeLLVMModule() {
+ const char *ModuleStrig = "define i32 @f(i32 %x) {\n"
+ "bb0:\n"
+ " %y1 = icmp eq i32 %x, 0 \n"
+ " br i1 %y1, label %bb1, label %bb2 \n"
+ "bb1:\n"
+ " br label %bb3\n"
+ "bb2:\n"
+ " br label %bb3\n"
+ "bb3:\n"
+ " %y2 = phi i32 [0, %bb1], [1, %bb2] \n"
+ " ret i32 %y2\n"
+ "}\n";
+ SMDiagnostic Err;
+ return parseAssemblyString(ModuleStrig, Err, C);
+ }
+};
+
+TEST_F(BlockFrequencyInfoTest, Basic) {
+ auto M = makeLLVMModule();
+ Function *F = M->getFunction("f");
+ F->setEntryCount(100);
+
+ BlockFrequencyInfo BFI = buildBFI(*F);
+ BasicBlock &BB0 = F->getEntryBlock();
+ BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);
+ BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1);
+ BasicBlock *BB3 = BB1->getSingleSuccessor();
+
+ uint64_t BB0Freq = BFI.getBlockFreq(&BB0).getFrequency();
+ uint64_t BB1Freq = BFI.getBlockFreq(BB1).getFrequency();
+ uint64_t BB2Freq = BFI.getBlockFreq(BB2).getFrequency();
+ uint64_t BB3Freq = BFI.getBlockFreq(BB3).getFrequency();
+
+ EXPECT_EQ(BB0Freq, BB3Freq);
+ EXPECT_EQ(BB0Freq, BB1Freq + BB2Freq);
+ EXPECT_EQ(BB0Freq, BB3Freq);
+
+ EXPECT_EQ(BFI.getBlockProfileCount(&BB0).getValue(), UINT64_C(100));
+ EXPECT_EQ(BFI.getBlockProfileCount(BB3).getValue(), UINT64_C(100));
+ EXPECT_EQ(BFI.getBlockProfileCount(BB1).getValue(), 100 * BB1Freq / BB0Freq);
+ EXPECT_EQ(BFI.getBlockProfileCount(BB2).getValue(), 100 * BB2Freq / BB0Freq);
+}
+
+} // end anonymous namespace
+} // end namespace llvm
diff --git a/unittests/Analysis/CFGTest.cpp b/unittests/Analysis/CFGTest.cpp
index 44f0fe681dff..c60044fa52df 100644
--- a/unittests/Analysis/CFGTest.cpp
+++ b/unittests/Analysis/CFGTest.cpp
@@ -31,7 +31,7 @@ class IsPotentiallyReachableTest : public testing::Test {
protected:
void ParseAssembly(const char *Assembly) {
SMDiagnostic Error;
- M = parseAssemblyString(Assembly, Error, getGlobalContext());
+ M = parseAssemblyString(Assembly, Error, Context);
std::string errMsg;
raw_string_ostream os(errMsg);
@@ -112,6 +112,7 @@ protected:
PM.run(*M);
}
+ LLVMContext Context;
std::unique_ptr<Module> M;
Instruction *A, *B;
};
diff --git a/unittests/Analysis/CGSCCPassManagerTest.cpp b/unittests/Analysis/CGSCCPassManagerTest.cpp
new file mode 100644
index 000000000000..224f2df13181
--- /dev/null
+++ b/unittests/Analysis/CGSCCPassManagerTest.cpp
@@ -0,0 +1,311 @@
+//===- CGSCCPassManagerTest.cpp -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/CGSCCPassManager.h"
+#include "llvm/Analysis/LazyCallGraph.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+class TestModuleAnalysis {
+public:
+ struct Result {
+ Result(int Count) : FunctionCount(Count) {}
+ int FunctionCount;
+ };
+
+ static void *ID() { return (void *)&PassID; }
+ static StringRef name() { return "TestModuleAnalysis"; }
+
+ TestModuleAnalysis(int &Runs) : Runs(Runs) {}
+
+ Result run(Module &M, ModuleAnalysisManager &AM) {
+ ++Runs;
+ return Result(M.size());
+ }
+
+private:
+ static char PassID;
+
+ int &Runs;
+};
+
+char TestModuleAnalysis::PassID;
+
+class TestSCCAnalysis {
+public:
+ struct Result {
+ Result(int Count) : FunctionCount(Count) {}
+ int FunctionCount;
+ };
+
+ static void *ID() { return (void *)&PassID; }
+ static StringRef name() { return "TestSCCAnalysis"; }
+
+ TestSCCAnalysis(int &Runs) : Runs(Runs) {}
+
+ Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM) {
+ ++Runs;
+ return Result(C.size());
+ }
+
+private:
+ static char PassID;
+
+ int &Runs;
+};
+
+char TestSCCAnalysis::PassID;
+
+class TestFunctionAnalysis {
+public:
+ struct Result {
+ Result(int Count) : InstructionCount(Count) {}
+ int InstructionCount;
+ };
+
+ static void *ID() { return (void *)&PassID; }
+ static StringRef name() { return "TestFunctionAnalysis"; }
+
+ TestFunctionAnalysis(int &Runs) : Runs(Runs) {}
+
+ Result run(Function &F, FunctionAnalysisManager &AM) {
+ ++Runs;
+ int Count = 0;
+ for (Instruction &I : instructions(F)) {
+ (void)I;
+ ++Count;
+ }
+ return Result(Count);
+ }
+
+private:
+ static char PassID;
+
+ int &Runs;
+};
+
+char TestFunctionAnalysis::PassID;
+
+class TestImmutableFunctionAnalysis {
+public:
+ struct Result {
+ bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ };
+
+ static void *ID() { return (void *)&PassID; }
+ static StringRef name() { return "TestImmutableFunctionAnalysis"; }
+
+ TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {}
+
+ Result run(Function &F, FunctionAnalysisManager &AM) {
+ ++Runs;
+ return Result();
+ }
+
+private:
+ static char PassID;
+
+ int &Runs;
+};
+
+char TestImmutableFunctionAnalysis::PassID;
+
+struct TestModulePass {
+ TestModulePass(int &RunCount) : RunCount(RunCount) {}
+
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
+ ++RunCount;
+ (void)AM.getResult<TestModuleAnalysis>(M);
+ return PreservedAnalyses::all();
+ }
+
+ static StringRef name() { return "TestModulePass"; }
+
+ int &RunCount;
+};
+
+struct TestSCCPass {
+ TestSCCPass(int &RunCount, int &AnalyzedInstrCount,
+ int &AnalyzedSCCFunctionCount, int &AnalyzedModuleFunctionCount,
+ bool OnlyUseCachedResults = false)
+ : RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount),
+ AnalyzedSCCFunctionCount(AnalyzedSCCFunctionCount),
+ AnalyzedModuleFunctionCount(AnalyzedModuleFunctionCount),
+ OnlyUseCachedResults(OnlyUseCachedResults) {}
+
+ PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM) {
+ ++RunCount;
+
+ const ModuleAnalysisManager &MAM =
+ AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C).getManager();
+ FunctionAnalysisManager &FAM =
+ AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C).getManager();
+ if (TestModuleAnalysis::Result *TMA =
+ MAM.getCachedResult<TestModuleAnalysis>(
+ *C.begin()->getFunction().getParent()))
+ AnalyzedModuleFunctionCount += TMA->FunctionCount;
+
+ if (OnlyUseCachedResults) {
+ // Hack to force the use of the cached interface.
+ if (TestSCCAnalysis::Result *AR = AM.getCachedResult<TestSCCAnalysis>(C))
+ AnalyzedSCCFunctionCount += AR->FunctionCount;
+ for (LazyCallGraph::Node &N : C)
+ if (TestFunctionAnalysis::Result *FAR =
+ FAM.getCachedResult<TestFunctionAnalysis>(N.getFunction()))
+ AnalyzedInstrCount += FAR->InstructionCount;
+ } else {
+ // Typical path just runs the analysis as needed.
+ TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C);
+ AnalyzedSCCFunctionCount += AR.FunctionCount;
+ for (LazyCallGraph::Node &N : C) {
+ TestFunctionAnalysis::Result &FAR =
+ FAM.getResult<TestFunctionAnalysis>(N.getFunction());
+ AnalyzedInstrCount += FAR.InstructionCount;
+
+ // Just ensure we get the immutable results.
+ (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction());
+ }
+ }
+
+ return PreservedAnalyses::all();
+ }
+
+ static StringRef name() { return "TestSCCPass"; }
+
+ int &RunCount;
+ int &AnalyzedInstrCount;
+ int &AnalyzedSCCFunctionCount;
+ int &AnalyzedModuleFunctionCount;
+ bool OnlyUseCachedResults;
+};
+
+struct TestFunctionPass {
+ TestFunctionPass(int &RunCount) : RunCount(RunCount) {}
+
+ PreservedAnalyses run(Function &F, AnalysisManager<Function> &) {
+ ++RunCount;
+ return PreservedAnalyses::none();
+ }
+
+ static StringRef name() { return "TestFunctionPass"; }
+
+ int &RunCount;
+};
+
+std::unique_ptr<Module> parseIR(const char *IR) {
+ // We just use a static context here. This is never called from multiple
+ // threads so it is harmless no matter how it is implemented. We just need
+ // the context to outlive the module which it does.
+ static LLVMContext C;
+ SMDiagnostic Err;
+ return parseAssemblyString(IR, Err, C);
+}
+
+TEST(CGSCCPassManagerTest, Basic) {
+ auto M = parseIR("define void @f() {\n"
+ "entry:\n"
+ " call void @g()\n"
+ " call void @h1()\n"
+ " ret void\n"
+ "}\n"
+ "define void @g() {\n"
+ "entry:\n"
+ " call void @g()\n"
+ " call void @x()\n"
+ " ret void\n"
+ "}\n"
+ "define void @h1() {\n"
+ "entry:\n"
+ " call void @h2()\n"
+ " ret void\n"
+ "}\n"
+ "define void @h2() {\n"
+ "entry:\n"
+ " call void @h3()\n"
+ " call void @x()\n"
+ " ret void\n"
+ "}\n"
+ "define void @h3() {\n"
+ "entry:\n"
+ " call void @h1()\n"
+ " ret void\n"
+ "}\n"
+ "define void @x() {\n"
+ "entry:\n"
+ " ret void\n"
+ "}\n");
+ FunctionAnalysisManager FAM(/*DebugLogging*/ true);
+ int FunctionAnalysisRuns = 0;
+ FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });
+ int ImmutableFunctionAnalysisRuns = 0;
+ FAM.registerPass([&] {
+ return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns);
+ });
+
+ CGSCCAnalysisManager CGAM(/*DebugLogging*/ true);
+ int SCCAnalysisRuns = 0;
+ CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });
+
+ ModuleAnalysisManager MAM(/*DebugLogging*/ true);
+ int ModuleAnalysisRuns = 0;
+ MAM.registerPass([&] { return LazyCallGraphAnalysis(); });
+ MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });
+
+ MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
+ MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); });
+ CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(FAM); });
+ CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); });
+ FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); });
+ FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
+
+ ModulePassManager MPM(/*DebugLogging*/ true);
+ int ModulePassRunCount1 = 0;
+ MPM.addPass(TestModulePass(ModulePassRunCount1));
+
+ CGSCCPassManager CGPM1(/*DebugLogging*/ true);
+ int SCCPassRunCount1 = 0;
+ int AnalyzedInstrCount1 = 0;
+ int AnalyzedSCCFunctionCount1 = 0;
+ int AnalyzedModuleFunctionCount1 = 0;
+ CGPM1.addPass(TestSCCPass(SCCPassRunCount1, AnalyzedInstrCount1,
+ AnalyzedSCCFunctionCount1,
+ AnalyzedModuleFunctionCount1));
+
+ FunctionPassManager FPM1(/*DebugLogging*/ true);
+ int FunctionPassRunCount1 = 0;
+ FPM1.addPass(TestFunctionPass(FunctionPassRunCount1));
+ CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));
+ MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));
+
+ MPM.run(*M, MAM);
+
+ EXPECT_EQ(1, ModulePassRunCount1);
+
+ EXPECT_EQ(1, ModuleAnalysisRuns);
+ EXPECT_EQ(4, SCCAnalysisRuns);
+ EXPECT_EQ(6, FunctionAnalysisRuns);
+ EXPECT_EQ(6, ImmutableFunctionAnalysisRuns);
+
+ EXPECT_EQ(4, SCCPassRunCount1);
+ EXPECT_EQ(14, AnalyzedInstrCount1);
+ EXPECT_EQ(6, AnalyzedSCCFunctionCount1);
+ EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1);
+}
+
+}
diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt
index 06560cf14d4a..2292a454c27a 100644
--- a/unittests/Analysis/CMakeLists.txt
+++ b/unittests/Analysis/CMakeLists.txt
@@ -7,10 +7,14 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(AnalysisTests
AliasAnalysisTest.cpp
+ BlockFrequencyInfoTest.cpp
CallGraphTest.cpp
CFGTest.cpp
+ CGSCCPassManagerTest.cpp
LazyCallGraphTest.cpp
+ LoopPassManagerTest.cpp
ScalarEvolutionTest.cpp
MixedTBAATest.cpp
ValueTrackingTest.cpp
+ UnrollAnalyzer.cpp
)
diff --git a/unittests/Analysis/CallGraphTest.cpp b/unittests/Analysis/CallGraphTest.cpp
index 777907a55b11..af46291074c2 100644
--- a/unittests/Analysis/CallGraphTest.cpp
+++ b/unittests/Analysis/CallGraphTest.cpp
@@ -44,14 +44,16 @@ template <typename Ty> void canSpecializeGraphTraitsIterators(Ty *G) {
}
TEST(CallGraphTest, GraphTraitsSpecialization) {
- Module M("", getGlobalContext());
+ LLVMContext Context;
+ Module M("", Context);
CallGraph CG(M);
canSpecializeGraphTraitsIterators(&CG);
}
TEST(CallGraphTest, GraphTraitsConstSpecialization) {
- Module M("", getGlobalContext());
+ LLVMContext Context;
+ Module M("", Context);
CallGraph CG(M);
canSpecializeGraphTraitsIterators(const_cast<const CallGraph *>(&CG));
diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp
index 6caccb892395..224a9458cc88 100644
--- a/unittests/Analysis/LazyCallGraphTest.cpp
+++ b/unittests/Analysis/LazyCallGraphTest.cpp
@@ -21,10 +21,10 @@ using namespace llvm;
namespace {
-std::unique_ptr<Module> parseAssembly(const char *Assembly) {
+std::unique_ptr<Module> parseAssembly(LLVMContext &Context,
+ const char *Assembly) {
SMDiagnostic Error;
- std::unique_ptr<Module> M =
- parseAssemblyString(Assembly, Error, getGlobalContext());
+ std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);
std::string ErrMsg;
raw_string_ostream OS(ErrMsg);
@@ -121,36 +121,37 @@ static const char DiamondOfTriangles[] =
"}\n";
TEST(LazyCallGraphTest, BasicGraphFormation) {
- std::unique_ptr<Module> M = parseAssembly(DiamondOfTriangles);
+ LLVMContext Context;
+ std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);
LazyCallGraph CG(*M);
// The order of the entry nodes should be stable w.r.t. the source order of
// the IR, and everything in our module is an entry node, so just directly
// build variables for each node.
auto I = CG.begin();
- LazyCallGraph::Node &A1 = *I++;
+ LazyCallGraph::Node &A1 = (I++)->getNode(CG);
EXPECT_EQ("a1", A1.getFunction().getName());
- LazyCallGraph::Node &A2 = *I++;
+ LazyCallGraph::Node &A2 = (I++)->getNode(CG);
EXPECT_EQ("a2", A2.getFunction().getName());
- LazyCallGraph::Node &A3 = *I++;
+ LazyCallGraph::Node &A3 = (I++)->getNode(CG);
EXPECT_EQ("a3", A3.getFunction().getName());
- LazyCallGraph::Node &B1 = *I++;
+ LazyCallGraph::Node &B1 = (I++)->getNode(CG);
EXPECT_EQ("b1", B1.getFunction().getName());
- LazyCallGraph::Node &B2 = *I++;
+ LazyCallGraph::Node &B2 = (I++)->getNode(CG);
EXPECT_EQ("b2", B2.getFunction().getName());
- LazyCallGraph::Node &B3 = *I++;
+ LazyCallGraph::Node &B3 = (I++)->getNode(CG);
EXPECT_EQ("b3", B3.getFunction().getName());
- LazyCallGraph::Node &C1 = *I++;
+ LazyCallGraph::Node &C1 = (I++)->getNode(CG);
EXPECT_EQ("c1", C1.getFunction().getName());
- LazyCallGraph::Node &C2 = *I++;
+ LazyCallGraph::Node &C2 = (I++)->getNode(CG);
EXPECT_EQ("c2", C2.getFunction().getName());
- LazyCallGraph::Node &C3 = *I++;
+ LazyCallGraph::Node &C3 = (I++)->getNode(CG);
EXPECT_EQ("c3", C3.getFunction().getName());
- LazyCallGraph::Node &D1 = *I++;
+ LazyCallGraph::Node &D1 = (I++)->getNode(CG);
EXPECT_EQ("d1", D1.getFunction().getName());
- LazyCallGraph::Node &D2 = *I++;
+ LazyCallGraph::Node &D2 = (I++)->getNode(CG);
EXPECT_EQ("d2", D2.getFunction().getName());
- LazyCallGraph::Node &D3 = *I++;
+ LazyCallGraph::Node &D3 = (I++)->getNode(CG);
EXPECT_EQ("d3", D3.getFunction().getName());
EXPECT_EQ(CG.end(), I);
@@ -158,8 +159,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
// independent of order.
std::vector<std::string> Nodes;
- for (LazyCallGraph::Node &N : A1)
- Nodes.push_back(N.getFunction().getName());
+ for (LazyCallGraph::Edge &E : A1)
+ Nodes.push_back(E.getFunction().getName());
std::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ("a2", Nodes[0]);
EXPECT_EQ("b2", Nodes[1]);
@@ -171,8 +172,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
EXPECT_EQ(A3.end(), std::next(A3.begin()));
EXPECT_EQ("a1", A3.begin()->getFunction().getName());
- for (LazyCallGraph::Node &N : B1)
- Nodes.push_back(N.getFunction().getName());
+ for (LazyCallGraph::Edge &E : B1)
+ Nodes.push_back(E.getFunction().getName());
std::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ("b2", Nodes[0]);
EXPECT_EQ("d3", Nodes[1]);
@@ -183,8 +184,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
EXPECT_EQ(B3.end(), std::next(B3.begin()));
EXPECT_EQ("b1", B3.begin()->getFunction().getName());
- for (LazyCallGraph::Node &N : C1)
- Nodes.push_back(N.getFunction().getName());
+ for (LazyCallGraph::Edge &E : C1)
+ Nodes.push_back(E.getFunction().getName());
std::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ("c2", Nodes[0]);
EXPECT_EQ("d2", Nodes[1]);
@@ -202,12 +203,13 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
EXPECT_EQ(D3.end(), std::next(D3.begin()));
EXPECT_EQ("d1", D3.begin()->getFunction().getName());
- // Now lets look at the SCCs.
- auto SCCI = CG.postorder_scc_begin();
+ // Now lets look at the RefSCCs and SCCs.
+ auto J = CG.postorder_ref_scc_begin();
- LazyCallGraph::SCC &D = *SCCI++;
- for (LazyCallGraph::Node *N : D)
- Nodes.push_back(N->getFunction().getName());
+ LazyCallGraph::RefSCC &D = *J++;
+ ASSERT_EQ(1, D.size());
+ for (LazyCallGraph::Node &N : *D.begin())
+ Nodes.push_back(N.getFunction().getName());
std::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("d1", Nodes[0]);
@@ -219,9 +221,10 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
EXPECT_FALSE(D.isAncestorOf(D));
EXPECT_FALSE(D.isDescendantOf(D));
- LazyCallGraph::SCC &C = *SCCI++;
- for (LazyCallGraph::Node *N : C)
- Nodes.push_back(N->getFunction().getName());
+ LazyCallGraph::RefSCC &C = *J++;
+ ASSERT_EQ(1, C.size());
+ for (LazyCallGraph::Node &N : *C.begin())
+ Nodes.push_back(N.getFunction().getName());
std::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("c1", Nodes[0]);
@@ -233,9 +236,10 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
EXPECT_TRUE(C.isAncestorOf(D));
EXPECT_FALSE(C.isDescendantOf(D));
- LazyCallGraph::SCC &B = *SCCI++;
- for (LazyCallGraph::Node *N : B)
- Nodes.push_back(N->getFunction().getName());
+ LazyCallGraph::RefSCC &B = *J++;
+ ASSERT_EQ(1, B.size());
+ for (LazyCallGraph::Node &N : *B.begin())
+ Nodes.push_back(N.getFunction().getName());
std::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("b1", Nodes[0]);
@@ -249,9 +253,10 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
EXPECT_FALSE(B.isAncestorOf(C));
EXPECT_FALSE(C.isAncestorOf(B));
- LazyCallGraph::SCC &A = *SCCI++;
- for (LazyCallGraph::Node *N : A)
- Nodes.push_back(N->getFunction().getName());
+ LazyCallGraph::RefSCC &A = *J++;
+ ASSERT_EQ(1, A.size());
+ for (LazyCallGraph::Node &N : *A.begin())
+ Nodes.push_back(N.getFunction().getName());
std::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("a1", Nodes[0]);
@@ -265,7 +270,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
EXPECT_TRUE(A.isAncestorOf(C));
EXPECT_TRUE(A.isAncestorOf(D));
- EXPECT_EQ(CG.postorder_scc_end(), SCCI);
+ EXPECT_EQ(CG.postorder_ref_scc_end(), J);
}
static Function &lookupFunction(Module &M, StringRef Name) {
@@ -276,21 +281,21 @@ static Function &lookupFunction(Module &M, StringRef Name) {
}
TEST(LazyCallGraphTest, BasicGraphMutation) {
- std::unique_ptr<Module> M = parseAssembly(
- "define void @a() {\n"
- "entry:\n"
- " call void @b()\n"
- " call void @c()\n"
- " ret void\n"
- "}\n"
- "define void @b() {\n"
- "entry:\n"
- " ret void\n"
- "}\n"
- "define void @c() {\n"
- "entry:\n"
- " ret void\n"
- "}\n");
+ LLVMContext Context;
+ std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n"
+ "entry:\n"
+ " call void @b()\n"
+ " call void @c()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b() {\n"
+ "entry:\n"
+ " ret void\n"
+ "}\n"
+ "define void @c() {\n"
+ "entry:\n"
+ " ret void\n"
+ "}\n");
LazyCallGraph CG(*M);
LazyCallGraph::Node &A = CG.get(lookupFunction(*M, "a"));
@@ -298,23 +303,23 @@ TEST(LazyCallGraphTest, BasicGraphMutation) {
EXPECT_EQ(2, std::distance(A.begin(), A.end()));
EXPECT_EQ(0, std::distance(B.begin(), B.end()));
- CG.insertEdge(B, lookupFunction(*M, "c"));
+ CG.insertEdge(B, lookupFunction(*M, "c"), LazyCallGraph::Edge::Call);
EXPECT_EQ(1, std::distance(B.begin(), B.end()));
- LazyCallGraph::Node &C = *B.begin();
+ LazyCallGraph::Node &C = B.begin()->getNode(CG);
EXPECT_EQ(0, std::distance(C.begin(), C.end()));
- CG.insertEdge(C, B.getFunction());
+ CG.insertEdge(C, B.getFunction(), LazyCallGraph::Edge::Call);
EXPECT_EQ(1, std::distance(C.begin(), C.end()));
- EXPECT_EQ(&B, &*C.begin());
+ EXPECT_EQ(&B, C.begin()->getNode());
- CG.insertEdge(C, C.getFunction());
+ CG.insertEdge(C, C.getFunction(), LazyCallGraph::Edge::Call);
EXPECT_EQ(2, std::distance(C.begin(), C.end()));
- EXPECT_EQ(&B, &*C.begin());
- EXPECT_EQ(&C, &*std::next(C.begin()));
+ EXPECT_EQ(&B, C.begin()->getNode());
+ EXPECT_EQ(&C, std::next(C.begin())->getNode());
CG.removeEdge(C, B.getFunction());
EXPECT_EQ(1, std::distance(C.begin(), C.end()));
- EXPECT_EQ(&C, &*C.begin());
+ EXPECT_EQ(&C, C.begin()->getNode());
CG.removeEdge(C, C.getFunction());
EXPECT_EQ(0, std::distance(C.begin(), C.end()));
@@ -323,83 +328,157 @@ TEST(LazyCallGraphTest, BasicGraphMutation) {
EXPECT_EQ(0, std::distance(B.begin(), B.end()));
}
+TEST(LazyCallGraphTest, InnerSCCFormation) {
+ LLVMContext Context;
+ std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);
+ LazyCallGraph CG(*M);
+
+ // Now mutate the graph to connect every node into a single RefSCC to ensure
+ // that our inner SCC formation handles the rest.
+ CG.insertEdge(lookupFunction(*M, "d1"), lookupFunction(*M, "a1"),
+ LazyCallGraph::Edge::Ref);
+
+ // Build vectors and sort them for the rest of the assertions to make them
+ // independent of order.
+ std::vector<std::string> Nodes;
+
+ // We should build a single RefSCC for the entire graph.
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+
+ // Now walk the four SCCs which should be in post-order.
+ auto J = RC.begin();
+ LazyCallGraph::SCC &D = *J++;
+ for (LazyCallGraph::Node &N : D)
+ Nodes.push_back(N.getFunction().getName());
+ std::sort(Nodes.begin(), Nodes.end());
+ EXPECT_EQ(3u, Nodes.size());
+ EXPECT_EQ("d1", Nodes[0]);
+ EXPECT_EQ("d2", Nodes[1]);
+ EXPECT_EQ("d3", Nodes[2]);
+ Nodes.clear();
+
+ LazyCallGraph::SCC &B = *J++;
+ for (LazyCallGraph::Node &N : B)
+ Nodes.push_back(N.getFunction().getName());
+ std::sort(Nodes.begin(), Nodes.end());
+ EXPECT_EQ(3u, Nodes.size());
+ EXPECT_EQ("b1", Nodes[0]);
+ EXPECT_EQ("b2", Nodes[1]);
+ EXPECT_EQ("b3", Nodes[2]);
+ Nodes.clear();
+
+ LazyCallGraph::SCC &C = *J++;
+ for (LazyCallGraph::Node &N : C)
+ Nodes.push_back(N.getFunction().getName());
+ std::sort(Nodes.begin(), Nodes.end());
+ EXPECT_EQ(3u, Nodes.size());
+ EXPECT_EQ("c1", Nodes[0]);
+ EXPECT_EQ("c2", Nodes[1]);
+ EXPECT_EQ("c3", Nodes[2]);
+ Nodes.clear();
+
+ LazyCallGraph::SCC &A = *J++;
+ for (LazyCallGraph::Node &N : A)
+ Nodes.push_back(N.getFunction().getName());
+ std::sort(Nodes.begin(), Nodes.end());
+ EXPECT_EQ(3u, Nodes.size());
+ EXPECT_EQ("a1", Nodes[0]);
+ EXPECT_EQ("a2", Nodes[1]);
+ EXPECT_EQ("a3", Nodes[2]);
+ Nodes.clear();
+
+ EXPECT_EQ(RC.end(), J);
+}
+
TEST(LazyCallGraphTest, MultiArmSCC) {
+ LLVMContext Context;
// Two interlocking cycles. The really useful thing about this SCC is that it
// will require Tarjan's DFS to backtrack and finish processing all of the
- // children of each node in the SCC.
- std::unique_ptr<Module> M = parseAssembly(
- "define void @a() {\n"
- "entry:\n"
- " call void @b()\n"
- " call void @d()\n"
- " ret void\n"
- "}\n"
- "define void @b() {\n"
- "entry:\n"
- " call void @c()\n"
- " ret void\n"
- "}\n"
- "define void @c() {\n"
- "entry:\n"
- " call void @a()\n"
- " ret void\n"
- "}\n"
- "define void @d() {\n"
- "entry:\n"
- " call void @e()\n"
- " ret void\n"
- "}\n"
- "define void @e() {\n"
- "entry:\n"
- " call void @a()\n"
- " ret void\n"
- "}\n");
+ // children of each node in the SCC. Since this involves call edges, both
+ // Tarjan implementations will have to successfully navigate the structure.
+ std::unique_ptr<Module> M = parseAssembly(Context, "define void @f1() {\n"
+ "entry:\n"
+ " call void @f2()\n"
+ " call void @f4()\n"
+ " ret void\n"
+ "}\n"
+ "define void @f2() {\n"
+ "entry:\n"
+ " call void @f3()\n"
+ " ret void\n"
+ "}\n"
+ "define void @f3() {\n"
+ "entry:\n"
+ " call void @f1()\n"
+ " ret void\n"
+ "}\n"
+ "define void @f4() {\n"
+ "entry:\n"
+ " call void @f5()\n"
+ " ret void\n"
+ "}\n"
+ "define void @f5() {\n"
+ "entry:\n"
+ " call void @f1()\n"
+ " ret void\n"
+ "}\n");
LazyCallGraph CG(*M);
// Force the graph to be fully expanded.
- auto SCCI = CG.postorder_scc_begin();
- LazyCallGraph::SCC &SCC = *SCCI++;
- EXPECT_EQ(CG.postorder_scc_end(), SCCI);
-
- LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
- LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));
- LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));
- LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));
- LazyCallGraph::Node &E = *CG.lookup(lookupFunction(*M, "e"));
- EXPECT_EQ(&SCC, CG.lookupSCC(A));
- EXPECT_EQ(&SCC, CG.lookupSCC(B));
- EXPECT_EQ(&SCC, CG.lookupSCC(C));
- EXPECT_EQ(&SCC, CG.lookupSCC(D));
- EXPECT_EQ(&SCC, CG.lookupSCC(E));
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+
+ LazyCallGraph::Node &N1 = *CG.lookup(lookupFunction(*M, "f1"));
+ LazyCallGraph::Node &N2 = *CG.lookup(lookupFunction(*M, "f2"));
+ LazyCallGraph::Node &N3 = *CG.lookup(lookupFunction(*M, "f3"));
+ LazyCallGraph::Node &N4 = *CG.lookup(lookupFunction(*M, "f4"));
+ LazyCallGraph::Node &N5 = *CG.lookup(lookupFunction(*M, "f4"));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(N1));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(N2));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(N3));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(N4));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(N5));
+
+ ASSERT_EQ(1, RC.size());
+
+ LazyCallGraph::SCC &C = *RC.begin();
+ EXPECT_EQ(&C, CG.lookupSCC(N1));
+ EXPECT_EQ(&C, CG.lookupSCC(N2));
+ EXPECT_EQ(&C, CG.lookupSCC(N3));
+ EXPECT_EQ(&C, CG.lookupSCC(N4));
+ EXPECT_EQ(&C, CG.lookupSCC(N5));
}
-TEST(LazyCallGraphTest, OutgoingSCCEdgeInsertion) {
- std::unique_ptr<Module> M = parseAssembly(
- "define void @a() {\n"
- "entry:\n"
- " call void @b()\n"
- " call void @c()\n"
- " ret void\n"
- "}\n"
- "define void @b() {\n"
- "entry:\n"
- " call void @d()\n"
- " ret void\n"
- "}\n"
- "define void @c() {\n"
- "entry:\n"
- " call void @d()\n"
- " ret void\n"
- "}\n"
- "define void @d() {\n"
- "entry:\n"
- " ret void\n"
- "}\n");
+TEST(LazyCallGraphTest, OutgoingEdgeMutation) {
+ LLVMContext Context;
+ std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n"
+ "entry:\n"
+ " call void @b()\n"
+ " call void @c()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b() {\n"
+ "entry:\n"
+ " call void @d()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c() {\n"
+ "entry:\n"
+ " call void @d()\n"
+ " ret void\n"
+ "}\n"
+ "define void @d() {\n"
+ "entry:\n"
+ " ret void\n"
+ "}\n");
LazyCallGraph CG(*M);
// Force the graph to be fully expanded.
- for (LazyCallGraph::SCC &C : CG.postorder_sccs())
- (void)C;
+ for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())
+ (void)RC;
LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));
@@ -409,24 +488,96 @@ TEST(LazyCallGraphTest, OutgoingSCCEdgeInsertion) {
LazyCallGraph::SCC &BC = *CG.lookupSCC(B);
LazyCallGraph::SCC &CC = *CG.lookupSCC(C);
LazyCallGraph::SCC &DC = *CG.lookupSCC(D);
- EXPECT_TRUE(AC.isAncestorOf(BC));
- EXPECT_TRUE(AC.isAncestorOf(CC));
- EXPECT_TRUE(AC.isAncestorOf(DC));
- EXPECT_TRUE(DC.isDescendantOf(AC));
- EXPECT_TRUE(DC.isDescendantOf(BC));
- EXPECT_TRUE(DC.isDescendantOf(CC));
+ LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A);
+ LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B);
+ LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C);
+ LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D);
+ EXPECT_TRUE(ARC.isParentOf(BRC));
+ EXPECT_TRUE(ARC.isParentOf(CRC));
+ EXPECT_FALSE(ARC.isParentOf(DRC));
+ EXPECT_TRUE(ARC.isAncestorOf(DRC));
+ EXPECT_FALSE(DRC.isChildOf(ARC));
+ EXPECT_TRUE(DRC.isDescendantOf(ARC));
+ EXPECT_TRUE(DRC.isChildOf(BRC));
+ EXPECT_TRUE(DRC.isChildOf(CRC));
EXPECT_EQ(2, std::distance(A.begin(), A.end()));
- AC.insertOutgoingEdge(A, D);
+ ARC.insertOutgoingEdge(A, D, LazyCallGraph::Edge::Call);
EXPECT_EQ(3, std::distance(A.begin(), A.end()));
- EXPECT_TRUE(AC.isParentOf(DC));
+ const LazyCallGraph::Edge &NewE = A[D];
+ EXPECT_TRUE(NewE);
+ EXPECT_TRUE(NewE.isCall());
+ EXPECT_EQ(&D, NewE.getNode());
+
+ // Only the parent and child tests sholud have changed. The rest of the graph
+ // remains the same.
+ EXPECT_TRUE(ARC.isParentOf(DRC));
+ EXPECT_TRUE(ARC.isAncestorOf(DRC));
+ EXPECT_TRUE(DRC.isChildOf(ARC));
+ EXPECT_TRUE(DRC.isDescendantOf(ARC));
+ EXPECT_EQ(&AC, CG.lookupSCC(A));
+ EXPECT_EQ(&BC, CG.lookupSCC(B));
+ EXPECT_EQ(&CC, CG.lookupSCC(C));
+ EXPECT_EQ(&DC, CG.lookupSCC(D));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C));
+ EXPECT_EQ(&DRC, CG.lookupRefSCC(D));
+
+ ARC.switchOutgoingEdgeToRef(A, D);
+ EXPECT_FALSE(NewE.isCall());
+
+ // Verify the graph remains the same.
+ EXPECT_TRUE(ARC.isParentOf(DRC));
+ EXPECT_TRUE(ARC.isAncestorOf(DRC));
+ EXPECT_TRUE(DRC.isChildOf(ARC));
+ EXPECT_TRUE(DRC.isDescendantOf(ARC));
EXPECT_EQ(&AC, CG.lookupSCC(A));
EXPECT_EQ(&BC, CG.lookupSCC(B));
EXPECT_EQ(&CC, CG.lookupSCC(C));
EXPECT_EQ(&DC, CG.lookupSCC(D));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C));
+ EXPECT_EQ(&DRC, CG.lookupRefSCC(D));
+
+ ARC.switchOutgoingEdgeToCall(A, D);
+ EXPECT_TRUE(NewE.isCall());
+
+ // Verify the graph remains the same.
+ EXPECT_TRUE(ARC.isParentOf(DRC));
+ EXPECT_TRUE(ARC.isAncestorOf(DRC));
+ EXPECT_TRUE(DRC.isChildOf(ARC));
+ EXPECT_TRUE(DRC.isDescendantOf(ARC));
+ EXPECT_EQ(&AC, CG.lookupSCC(A));
+ EXPECT_EQ(&BC, CG.lookupSCC(B));
+ EXPECT_EQ(&CC, CG.lookupSCC(C));
+ EXPECT_EQ(&DC, CG.lookupSCC(D));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C));
+ EXPECT_EQ(&DRC, CG.lookupRefSCC(D));
+
+ ARC.removeOutgoingEdge(A, D);
+ EXPECT_EQ(2, std::distance(A.begin(), A.end()));
+
+ // Now the parent and child tests fail again but the rest remains the same.
+ EXPECT_FALSE(ARC.isParentOf(DRC));
+ EXPECT_TRUE(ARC.isAncestorOf(DRC));
+ EXPECT_FALSE(DRC.isChildOf(ARC));
+ EXPECT_TRUE(DRC.isDescendantOf(ARC));
+ EXPECT_EQ(&AC, CG.lookupSCC(A));
+ EXPECT_EQ(&BC, CG.lookupSCC(B));
+ EXPECT_EQ(&CC, CG.lookupSCC(C));
+ EXPECT_EQ(&DC, CG.lookupSCC(D));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C));
+ EXPECT_EQ(&DRC, CG.lookupRefSCC(D));
}
-TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) {
+TEST(LazyCallGraphTest, IncomingEdgeInsertion) {
+ LLVMContext Context;
// We want to ensure we can add edges even across complex diamond graphs, so
// we use the diamond of triangles graph defined above. The ascii diagram is
// repeated here for easy reference.
@@ -443,12 +594,12 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) {
// / \ |
// a3--a2 |
//
- std::unique_ptr<Module> M = parseAssembly(DiamondOfTriangles);
+ std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);
LazyCallGraph CG(*M);
// Force the graph to be fully expanded.
- for (LazyCallGraph::SCC &C : CG.postorder_sccs())
- (void)C;
+ for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())
+ (void)RC;
LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1"));
LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2"));
@@ -462,18 +613,18 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) {
LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1"));
LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2"));
LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3"));
- LazyCallGraph::SCC &AC = *CG.lookupSCC(A1);
- LazyCallGraph::SCC &BC = *CG.lookupSCC(B1);
- LazyCallGraph::SCC &CC = *CG.lookupSCC(C1);
- LazyCallGraph::SCC &DC = *CG.lookupSCC(D1);
- ASSERT_EQ(&AC, CG.lookupSCC(A2));
- ASSERT_EQ(&AC, CG.lookupSCC(A3));
- ASSERT_EQ(&BC, CG.lookupSCC(B2));
- ASSERT_EQ(&BC, CG.lookupSCC(B3));
- ASSERT_EQ(&CC, CG.lookupSCC(C2));
- ASSERT_EQ(&CC, CG.lookupSCC(C3));
- ASSERT_EQ(&DC, CG.lookupSCC(D2));
- ASSERT_EQ(&DC, CG.lookupSCC(D3));
+ LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A1);
+ LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B1);
+ LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C1);
+ LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D1);
+ ASSERT_EQ(&ARC, CG.lookupRefSCC(A2));
+ ASSERT_EQ(&ARC, CG.lookupRefSCC(A3));
+ ASSERT_EQ(&BRC, CG.lookupRefSCC(B2));
+ ASSERT_EQ(&BRC, CG.lookupRefSCC(B3));
+ ASSERT_EQ(&CRC, CG.lookupRefSCC(C2));
+ ASSERT_EQ(&CRC, CG.lookupRefSCC(C3));
+ ASSERT_EQ(&DRC, CG.lookupRefSCC(D2));
+ ASSERT_EQ(&DRC, CG.lookupRefSCC(D3));
ASSERT_EQ(1, std::distance(D2.begin(), D2.end()));
// Add an edge to make the graph:
@@ -489,47 +640,52 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) {
// a1 |
// / \ |
// a3--a2 |
- CC.insertIncomingEdge(D2, C2);
+ auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2);
// Make sure we connected the nodes.
- EXPECT_EQ(2, std::distance(D2.begin(), D2.end()));
+ for (LazyCallGraph::Edge E : D2) {
+ if (E.getNode() == &D3)
+ continue;
+ EXPECT_EQ(&C2, E.getNode());
+ }
+ // And marked the D ref-SCC as no longer valid.
+ EXPECT_EQ(1u, MergedRCs.size());
+ EXPECT_EQ(&DRC, MergedRCs[0]);
// Make sure we have the correct nodes in the SCC sets.
- EXPECT_EQ(&AC, CG.lookupSCC(A1));
- EXPECT_EQ(&AC, CG.lookupSCC(A2));
- EXPECT_EQ(&AC, CG.lookupSCC(A3));
- EXPECT_EQ(&BC, CG.lookupSCC(B1));
- EXPECT_EQ(&BC, CG.lookupSCC(B2));
- EXPECT_EQ(&BC, CG.lookupSCC(B3));
- EXPECT_EQ(&CC, CG.lookupSCC(C1));
- EXPECT_EQ(&CC, CG.lookupSCC(C2));
- EXPECT_EQ(&CC, CG.lookupSCC(C3));
- EXPECT_EQ(&CC, CG.lookupSCC(D1));
- EXPECT_EQ(&CC, CG.lookupSCC(D2));
- EXPECT_EQ(&CC, CG.lookupSCC(D3));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(A1));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(A2));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(A3));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(B1));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(B2));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(B3));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C1));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C2));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C3));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(D1));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(D2));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(D3));
// And that ancestry tests have been updated.
- EXPECT_TRUE(AC.isParentOf(BC));
- EXPECT_TRUE(AC.isParentOf(CC));
- EXPECT_FALSE(AC.isAncestorOf(DC));
- EXPECT_FALSE(BC.isAncestorOf(DC));
- EXPECT_FALSE(CC.isAncestorOf(DC));
+ EXPECT_TRUE(ARC.isParentOf(CRC));
+ EXPECT_TRUE(BRC.isParentOf(CRC));
}
-TEST(LazyCallGraphTest, IncomingSCCEdgeInsertionMidTraversal) {
+TEST(LazyCallGraphTest, IncomingEdgeInsertionMidTraversal) {
+ LLVMContext Context;
// This is the same fundamental test as the previous, but we perform it
- // having only partially walked the SCCs of the graph.
- std::unique_ptr<Module> M = parseAssembly(DiamondOfTriangles);
+ // having only partially walked the RefSCCs of the graph.
+ std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);
LazyCallGraph CG(*M);
- // Walk the SCCs until we find the one containing 'c1'.
- auto SCCI = CG.postorder_scc_begin(), SCCE = CG.postorder_scc_end();
- ASSERT_NE(SCCI, SCCE);
- LazyCallGraph::SCC &DC = *SCCI;
- ASSERT_NE(&DC, nullptr);
- ++SCCI;
- ASSERT_NE(SCCI, SCCE);
- LazyCallGraph::SCC &CC = *SCCI;
- ASSERT_NE(&CC, nullptr);
+ // Walk the RefSCCs until we find the one containing 'c1'.
+ auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();
+ ASSERT_NE(I, E);
+ LazyCallGraph::RefSCC &DRC = *I;
+ ASSERT_NE(&DRC, nullptr);
+ ++I;
+ ASSERT_NE(I, E);
+ LazyCallGraph::RefSCC &CRC = *I;
+ ASSERT_NE(&CRC, nullptr);
ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a1")));
ASSERT_EQ(nullptr, CG.lookup(lookupFunction(*M, "a2")));
@@ -543,178 +699,607 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertionMidTraversal) {
LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1"));
LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2"));
LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3"));
- ASSERT_EQ(&CC, CG.lookupSCC(C1));
- ASSERT_EQ(&CC, CG.lookupSCC(C2));
- ASSERT_EQ(&CC, CG.lookupSCC(C3));
- ASSERT_EQ(&DC, CG.lookupSCC(D1));
- ASSERT_EQ(&DC, CG.lookupSCC(D2));
- ASSERT_EQ(&DC, CG.lookupSCC(D3));
+ ASSERT_EQ(&CRC, CG.lookupRefSCC(C1));
+ ASSERT_EQ(&CRC, CG.lookupRefSCC(C2));
+ ASSERT_EQ(&CRC, CG.lookupRefSCC(C3));
+ ASSERT_EQ(&DRC, CG.lookupRefSCC(D1));
+ ASSERT_EQ(&DRC, CG.lookupRefSCC(D2));
+ ASSERT_EQ(&DRC, CG.lookupRefSCC(D3));
ASSERT_EQ(1, std::distance(D2.begin(), D2.end()));
- CC.insertIncomingEdge(D2, C2);
- EXPECT_EQ(2, std::distance(D2.begin(), D2.end()));
+ auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2);
+ // Make sure we connected the nodes.
+ for (LazyCallGraph::Edge E : D2) {
+ if (E.getNode() == &D3)
+ continue;
+ EXPECT_EQ(&C2, E.getNode());
+ }
+ // And marked the D ref-SCC as no longer valid.
+ EXPECT_EQ(1u, MergedRCs.size());
+ EXPECT_EQ(&DRC, MergedRCs[0]);
+
+ // Make sure we have the correct nodes in the RefSCCs.
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C1));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C2));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(C3));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(D1));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(D2));
+ EXPECT_EQ(&CRC, CG.lookupRefSCC(D3));
+
+ // Check that we can form the last two RefSCCs now in a coherent way.
+ ++I;
+ EXPECT_NE(I, E);
+ LazyCallGraph::RefSCC &BRC = *I;
+ EXPECT_NE(&BRC, nullptr);
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b1"))));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b2"))));
+ EXPECT_EQ(&BRC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "b3"))));
+ EXPECT_TRUE(BRC.isParentOf(CRC));
+ ++I;
+ EXPECT_NE(I, E);
+ LazyCallGraph::RefSCC &ARC = *I;
+ EXPECT_NE(&ARC, nullptr);
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a1"))));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a2"))));
+ EXPECT_EQ(&ARC, CG.lookupRefSCC(*CG.lookup(lookupFunction(*M, "a3"))));
+ EXPECT_TRUE(ARC.isParentOf(CRC));
+ ++I;
+ EXPECT_EQ(E, I);
+}
- // Make sure we have the correct nodes in the SCC sets.
- EXPECT_EQ(&CC, CG.lookupSCC(C1));
- EXPECT_EQ(&CC, CG.lookupSCC(C2));
- EXPECT_EQ(&CC, CG.lookupSCC(C3));
- EXPECT_EQ(&CC, CG.lookupSCC(D1));
- EXPECT_EQ(&CC, CG.lookupSCC(D2));
- EXPECT_EQ(&CC, CG.lookupSCC(D3));
-
- // Check that we can form the last two SCCs now in a coherent way.
- ++SCCI;
- EXPECT_NE(SCCI, SCCE);
- LazyCallGraph::SCC &BC = *SCCI;
- EXPECT_NE(&BC, nullptr);
- EXPECT_EQ(&BC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "b1"))));
- EXPECT_EQ(&BC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "b2"))));
- EXPECT_EQ(&BC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "b3"))));
- ++SCCI;
- EXPECT_NE(SCCI, SCCE);
- LazyCallGraph::SCC &AC = *SCCI;
- EXPECT_NE(&AC, nullptr);
- EXPECT_EQ(&AC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "a1"))));
- EXPECT_EQ(&AC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "a2"))));
- EXPECT_EQ(&AC, CG.lookupSCC(*CG.lookup(lookupFunction(*M, "a3"))));
- ++SCCI;
- EXPECT_EQ(SCCI, SCCE);
+TEST(LazyCallGraphTest, InternalEdgeMutation) {
+ LLVMContext Context;
+ std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n"
+ "entry:\n"
+ " call void @b()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b() {\n"
+ "entry:\n"
+ " call void @c()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c() {\n"
+ "entry:\n"
+ " call void @a()\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG(*M);
+
+ // Force the graph to be fully expanded.
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+
+ LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));
+ LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(C));
+ EXPECT_EQ(1, RC.size());
+ EXPECT_EQ(&*RC.begin(), CG.lookupSCC(A));
+ EXPECT_EQ(&*RC.begin(), CG.lookupSCC(B));
+ EXPECT_EQ(&*RC.begin(), CG.lookupSCC(C));
+
+ // Insert an edge from 'a' to 'c'. Nothing changes about the graph.
+ RC.insertInternalRefEdge(A, C);
+ EXPECT_EQ(2, std::distance(A.begin(), A.end()));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(C));
+ EXPECT_EQ(1, RC.size());
+ EXPECT_EQ(&*RC.begin(), CG.lookupSCC(A));
+ EXPECT_EQ(&*RC.begin(), CG.lookupSCC(B));
+ EXPECT_EQ(&*RC.begin(), CG.lookupSCC(C));
+
+ // Switch the call edge from 'b' to 'c' to a ref edge. This will break the
+ // call cycle and cause us to form more SCCs. The RefSCC will remain the same
+ // though.
+ RC.switchInternalEdgeToRef(B, C);
+ EXPECT_EQ(&RC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(C));
+ auto J = RC.begin();
+ // The SCCs must be in *post-order* which means successors before
+ // predecessors. At this point we have call edges from C to A and from A to
+ // B. The only valid postorder is B, A, C.
+ EXPECT_EQ(&*J++, CG.lookupSCC(B));
+ EXPECT_EQ(&*J++, CG.lookupSCC(A));
+ EXPECT_EQ(&*J++, CG.lookupSCC(C));
+ EXPECT_EQ(RC.end(), J);
+
+ // Test turning the ref edge from A to C into a call edge. This will form an
+ // SCC out of A and C. Since we previously had a call edge from C to A, the
+ // C SCC should be preserved and have A merged into it while the A SCC should
+ // be invalidated.
+ LazyCallGraph::SCC &AC = *CG.lookupSCC(A);
+ LazyCallGraph::SCC &CC = *CG.lookupSCC(C);
+ auto InvalidatedSCCs = RC.switchInternalEdgeToCall(A, C);
+ ASSERT_EQ(1u, InvalidatedSCCs.size());
+ EXPECT_EQ(&AC, InvalidatedSCCs[0]);
+ EXPECT_EQ(2, CC.size());
+ EXPECT_EQ(&CC, CG.lookupSCC(A));
+ EXPECT_EQ(&CC, CG.lookupSCC(C));
+ J = RC.begin();
+ EXPECT_EQ(&*J++, CG.lookupSCC(B));
+ EXPECT_EQ(&*J++, CG.lookupSCC(C));
+ EXPECT_EQ(RC.end(), J);
}
-TEST(LazyCallGraphTest, InterSCCEdgeRemoval) {
+TEST(LazyCallGraphTest, InternalEdgeRemoval) {
+ LLVMContext Context;
+ // A nice fully connected (including self-edges) RefSCC.
std::unique_ptr<Module> M = parseAssembly(
- "define void @a() {\n"
- "entry:\n"
- " call void @b()\n"
- " ret void\n"
- "}\n"
- "define void @b() {\n"
- "entry:\n"
- " ret void\n"
- "}\n");
+ Context, "define void @a(i8** %ptr) {\n"
+ "entry:\n"
+ " store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"
+ " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"
+ " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"
+ " ret void\n"
+ "}\n"
+ "define void @b(i8** %ptr) {\n"
+ "entry:\n"
+ " store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"
+ " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"
+ " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"
+ " ret void\n"
+ "}\n"
+ "define void @c(i8** %ptr) {\n"
+ "entry:\n"
+ " store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"
+ " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"
+ " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"
+ " ret void\n"
+ "}\n");
LazyCallGraph CG(*M);
// Force the graph to be fully expanded.
- for (LazyCallGraph::SCC &C : CG.postorder_sccs())
- (void)C;
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));
- LazyCallGraph::SCC &AC = *CG.lookupSCC(A);
- LazyCallGraph::SCC &BC = *CG.lookupSCC(B);
+ LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(C));
+
+ // Remove the edge from b -> a, which should leave the 3 functions still in
+ // a single connected component because of a -> b -> c -> a.
+ SmallVector<LazyCallGraph::RefSCC *, 1> NewRCs =
+ RC.removeInternalRefEdge(B, A);
+ EXPECT_EQ(0u, NewRCs.size());
+ EXPECT_EQ(&RC, CG.lookupRefSCC(A));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(B));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(C));
+
+ // Remove the edge from c -> a, which should leave 'a' in the original RefSCC
+ // and form a new RefSCC for 'b' and 'c'.
+ NewRCs = RC.removeInternalRefEdge(C, A);
+ EXPECT_EQ(1u, NewRCs.size());
+ EXPECT_EQ(&RC, CG.lookupRefSCC(A));
+ EXPECT_EQ(1, std::distance(RC.begin(), RC.end()));
+ LazyCallGraph::RefSCC *RC2 = CG.lookupRefSCC(B);
+ EXPECT_EQ(RC2, CG.lookupRefSCC(C));
+ EXPECT_EQ(RC2, NewRCs[0]);
+}
+
+TEST(LazyCallGraphTest, InternalCallEdgeToRef) {
+ LLVMContext Context;
+ // A nice fully connected (including self-edges) SCC (and RefSCC)
+ std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n"
+ "entry:\n"
+ " call void @a()\n"
+ " call void @b()\n"
+ " call void @c()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b() {\n"
+ "entry:\n"
+ " call void @a()\n"
+ " call void @b()\n"
+ " call void @c()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c() {\n"
+ "entry:\n"
+ " call void @a()\n"
+ " call void @b()\n"
+ " call void @c()\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG(*M);
+
+ // Force the graph to be fully expanded.
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
- EXPECT_EQ("b", A.begin()->getFunction().getName());
- EXPECT_EQ(B.end(), B.begin());
- EXPECT_EQ(&AC, &*BC.parent_begin());
+ EXPECT_EQ(1, RC.size());
+ LazyCallGraph::SCC &CallC = *RC.begin();
- AC.removeInterSCCEdge(A, B);
+ LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));
+ LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));
+ EXPECT_EQ(&CallC, CG.lookupSCC(A));
+ EXPECT_EQ(&CallC, CG.lookupSCC(B));
+ EXPECT_EQ(&CallC, CG.lookupSCC(C));
+
+ // Remove the call edge from b -> a to a ref edge, which should leave the
+ // 3 functions still in a single connected component because of a -> b ->
+ // c -> a.
+ RC.switchInternalEdgeToRef(B, A);
+ EXPECT_EQ(1, RC.size());
+ EXPECT_EQ(&CallC, CG.lookupSCC(A));
+ EXPECT_EQ(&CallC, CG.lookupSCC(B));
+ EXPECT_EQ(&CallC, CG.lookupSCC(C));
- EXPECT_EQ(A.end(), A.begin());
- EXPECT_EQ(B.end(), B.begin());
- EXPECT_EQ(BC.parent_end(), BC.parent_begin());
+ // Remove the edge from c -> a, which should leave 'a' in the original SCC
+ // and form a new SCC for 'b' and 'c'.
+ RC.switchInternalEdgeToRef(C, A);
+ EXPECT_EQ(2, RC.size());
+ EXPECT_EQ(&CallC, CG.lookupSCC(A));
+ LazyCallGraph::SCC &BCallC = *CG.lookupSCC(B);
+ EXPECT_NE(&BCallC, &CallC);
+ EXPECT_EQ(&BCallC, CG.lookupSCC(C));
+ auto J = RC.find(CallC);
+ EXPECT_EQ(&CallC, &*J);
+ --J;
+ EXPECT_EQ(&BCallC, &*J);
+ EXPECT_EQ(RC.begin(), J);
+
+ // Remove the edge from c -> b, which should leave 'b' in the original SCC
+ // and form a new SCC for 'c'. It shouldn't change 'a's SCC.
+ RC.switchInternalEdgeToRef(C, B);
+ EXPECT_EQ(3, RC.size());
+ EXPECT_EQ(&CallC, CG.lookupSCC(A));
+ EXPECT_EQ(&BCallC, CG.lookupSCC(B));
+ LazyCallGraph::SCC &CCallC = *CG.lookupSCC(C);
+ EXPECT_NE(&CCallC, &CallC);
+ EXPECT_NE(&CCallC, &BCallC);
+ J = RC.find(CallC);
+ EXPECT_EQ(&CallC, &*J);
+ --J;
+ EXPECT_EQ(&BCallC, &*J);
+ --J;
+ EXPECT_EQ(&CCallC, &*J);
+ EXPECT_EQ(RC.begin(), J);
}
-TEST(LazyCallGraphTest, IntraSCCEdgeInsertion) {
- std::unique_ptr<Module> M1 = parseAssembly(
- "define void @a() {\n"
- "entry:\n"
- " call void @b()\n"
- " ret void\n"
- "}\n"
- "define void @b() {\n"
- "entry:\n"
- " call void @c()\n"
- " ret void\n"
- "}\n"
- "define void @c() {\n"
- "entry:\n"
- " call void @a()\n"
- " ret void\n"
- "}\n");
- LazyCallGraph CG1(*M1);
+TEST(LazyCallGraphTest, InternalRefEdgeToCall) {
+ LLVMContext Context;
+ // Basic tests for making a ref edge a call. This hits the basics of the
+ // process only.
+ std::unique_ptr<Module> M =
+ parseAssembly(Context, "define void @a() {\n"
+ "entry:\n"
+ " call void @b()\n"
+ " call void @c()\n"
+ " store void()* @d, void()** undef\n"
+ " ret void\n"
+ "}\n"
+ "define void @b() {\n"
+ "entry:\n"
+ " store void()* @c, void()** undef\n"
+ " call void @d()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c() {\n"
+ "entry:\n"
+ " store void()* @b, void()** undef\n"
+ " call void @d()\n"
+ " ret void\n"
+ "}\n"
+ "define void @d() {\n"
+ "entry:\n"
+ " store void()* @a, void()** undef\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG(*M);
// Force the graph to be fully expanded.
- auto SCCI = CG1.postorder_scc_begin();
- LazyCallGraph::SCC &SCC = *SCCI++;
- EXPECT_EQ(CG1.postorder_scc_end(), SCCI);
-
- LazyCallGraph::Node &A = *CG1.lookup(lookupFunction(*M1, "a"));
- LazyCallGraph::Node &B = *CG1.lookup(lookupFunction(*M1, "b"));
- LazyCallGraph::Node &C = *CG1.lookup(lookupFunction(*M1, "c"));
- EXPECT_EQ(&SCC, CG1.lookupSCC(A));
- EXPECT_EQ(&SCC, CG1.lookupSCC(B));
- EXPECT_EQ(&SCC, CG1.lookupSCC(C));
-
- // Insert an edge from 'a' to 'c'. Nothing changes about the SCCs.
- SCC.insertIntraSCCEdge(A, C);
- EXPECT_EQ(2, std::distance(A.begin(), A.end()));
- EXPECT_EQ(&SCC, CG1.lookupSCC(A));
- EXPECT_EQ(&SCC, CG1.lookupSCC(B));
- EXPECT_EQ(&SCC, CG1.lookupSCC(C));
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
- // Insert a self edge from 'a' back to 'a'.
- SCC.insertIntraSCCEdge(A, A);
- EXPECT_EQ(3, std::distance(A.begin(), A.end()));
- EXPECT_EQ(&SCC, CG1.lookupSCC(A));
- EXPECT_EQ(&SCC, CG1.lookupSCC(B));
- EXPECT_EQ(&SCC, CG1.lookupSCC(C));
+ LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));
+ LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));
+ LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));
+ LazyCallGraph::SCC &AC = *CG.lookupSCC(A);
+ LazyCallGraph::SCC &BC = *CG.lookupSCC(B);
+ LazyCallGraph::SCC &CC = *CG.lookupSCC(C);
+ LazyCallGraph::SCC &DC = *CG.lookupSCC(D);
+
+ // Check the initial post-order. Note that B and C could be flipped here (and
+ // in our mutation) without changing the nature of this test.
+ ASSERT_EQ(4, RC.size());
+ EXPECT_EQ(&DC, &RC[0]);
+ EXPECT_EQ(&BC, &RC[1]);
+ EXPECT_EQ(&CC, &RC[2]);
+ EXPECT_EQ(&AC, &RC[3]);
+
+ // Switch the ref edge from A -> D to a call edge. This should have no
+ // effect as it is already in postorder and no new cycles are formed.
+ auto MergedCs = RC.switchInternalEdgeToCall(A, D);
+ EXPECT_EQ(0u, MergedCs.size());
+ ASSERT_EQ(4, RC.size());
+ EXPECT_EQ(&DC, &RC[0]);
+ EXPECT_EQ(&BC, &RC[1]);
+ EXPECT_EQ(&CC, &RC[2]);
+ EXPECT_EQ(&AC, &RC[3]);
+
+ // Switch B -> C to a call edge. This doesn't form any new cycles but does
+ // require reordering the SCCs.
+ MergedCs = RC.switchInternalEdgeToCall(B, C);
+ EXPECT_EQ(0u, MergedCs.size());
+ ASSERT_EQ(4, RC.size());
+ EXPECT_EQ(&DC, &RC[0]);
+ EXPECT_EQ(&CC, &RC[1]);
+ EXPECT_EQ(&BC, &RC[2]);
+ EXPECT_EQ(&AC, &RC[3]);
+
+ // Switch C -> B to a call edge. This forms a cycle and forces merging SCCs.
+ MergedCs = RC.switchInternalEdgeToCall(C, B);
+ ASSERT_EQ(1u, MergedCs.size());
+ EXPECT_EQ(&CC, MergedCs[0]);
+ ASSERT_EQ(3, RC.size());
+ EXPECT_EQ(&DC, &RC[0]);
+ EXPECT_EQ(&BC, &RC[1]);
+ EXPECT_EQ(&AC, &RC[2]);
+ EXPECT_EQ(2, BC.size());
+ EXPECT_EQ(&BC, CG.lookupSCC(B));
+ EXPECT_EQ(&BC, CG.lookupSCC(C));
}
-TEST(LazyCallGraphTest, IntraSCCEdgeRemoval) {
- // A nice fully connected (including self-edges) SCC.
- std::unique_ptr<Module> M1 = parseAssembly(
- "define void @a() {\n"
- "entry:\n"
- " call void @a()\n"
- " call void @b()\n"
- " call void @c()\n"
- " ret void\n"
- "}\n"
- "define void @b() {\n"
- "entry:\n"
- " call void @a()\n"
- " call void @b()\n"
- " call void @c()\n"
- " ret void\n"
- "}\n"
- "define void @c() {\n"
- "entry:\n"
- " call void @a()\n"
- " call void @b()\n"
- " call void @c()\n"
- " ret void\n"
- "}\n");
- LazyCallGraph CG1(*M1);
+TEST(LazyCallGraphTest, InternalRefEdgeToCallNoCycleInterleaved) {
+ LLVMContext Context;
+ // Test for having a post-order prior to changing a ref edge to a call edge
+ // with SCCs connecting to the source and connecting to the target, but not
+ // connecting to both, interleaved between the source and target. This
+ // ensures we correctly partition the range rather than simply moving one or
+ // the other.
+ std::unique_ptr<Module> M =
+ parseAssembly(Context, "define void @a() {\n"
+ "entry:\n"
+ " call void @b1()\n"
+ " call void @c1()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b1() {\n"
+ "entry:\n"
+ " call void @c1()\n"
+ " call void @b2()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c1() {\n"
+ "entry:\n"
+ " call void @b2()\n"
+ " call void @c2()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b2() {\n"
+ "entry:\n"
+ " call void @c2()\n"
+ " call void @b3()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c2() {\n"
+ "entry:\n"
+ " call void @b3()\n"
+ " call void @c3()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b3() {\n"
+ "entry:\n"
+ " call void @c3()\n"
+ " call void @d()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c3() {\n"
+ "entry:\n"
+ " store void()* @b1, void()** undef\n"
+ " call void @d()\n"
+ " ret void\n"
+ "}\n"
+ "define void @d() {\n"
+ "entry:\n"
+ " store void()* @a, void()** undef\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG(*M);
// Force the graph to be fully expanded.
- auto SCCI = CG1.postorder_scc_begin();
- LazyCallGraph::SCC &SCC = *SCCI++;
- EXPECT_EQ(CG1.postorder_scc_end(), SCCI);
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
- LazyCallGraph::Node &A = *CG1.lookup(lookupFunction(*M1, "a"));
- LazyCallGraph::Node &B = *CG1.lookup(lookupFunction(*M1, "b"));
- LazyCallGraph::Node &C = *CG1.lookup(lookupFunction(*M1, "c"));
- EXPECT_EQ(&SCC, CG1.lookupSCC(A));
- EXPECT_EQ(&SCC, CG1.lookupSCC(B));
- EXPECT_EQ(&SCC, CG1.lookupSCC(C));
+ LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &B1 = *CG.lookup(lookupFunction(*M, "b1"));
+ LazyCallGraph::Node &B2 = *CG.lookup(lookupFunction(*M, "b2"));
+ LazyCallGraph::Node &B3 = *CG.lookup(lookupFunction(*M, "b3"));
+ LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1"));
+ LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2"));
+ LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3"));
+ LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));
+ LazyCallGraph::SCC &AC = *CG.lookupSCC(A);
+ LazyCallGraph::SCC &B1C = *CG.lookupSCC(B1);
+ LazyCallGraph::SCC &B2C = *CG.lookupSCC(B2);
+ LazyCallGraph::SCC &B3C = *CG.lookupSCC(B3);
+ LazyCallGraph::SCC &C1C = *CG.lookupSCC(C1);
+ LazyCallGraph::SCC &C2C = *CG.lookupSCC(C2);
+ LazyCallGraph::SCC &C3C = *CG.lookupSCC(C3);
+ LazyCallGraph::SCC &DC = *CG.lookupSCC(D);
- // Remove the edge from b -> a, which should leave the 3 functions still in
- // a single connected component because of a -> b -> c -> a.
- SmallVector<LazyCallGraph::SCC *, 1> NewSCCs = SCC.removeIntraSCCEdge(B, A);
- EXPECT_EQ(0u, NewSCCs.size());
- EXPECT_EQ(&SCC, CG1.lookupSCC(A));
- EXPECT_EQ(&SCC, CG1.lookupSCC(B));
- EXPECT_EQ(&SCC, CG1.lookupSCC(C));
+ // Several call edges are initially present to force a particual post-order.
+ // Remove them now, leaving an interleaved post-order pattern.
+ RC.switchInternalEdgeToRef(B3, C3);
+ RC.switchInternalEdgeToRef(C2, B3);
+ RC.switchInternalEdgeToRef(B2, C2);
+ RC.switchInternalEdgeToRef(C1, B2);
+ RC.switchInternalEdgeToRef(B1, C1);
+
+ // Check the initial post-order. We ensure this order with the extra edges
+ // that are nuked above.
+ ASSERT_EQ(8, RC.size());
+ EXPECT_EQ(&DC, &RC[0]);
+ EXPECT_EQ(&C3C, &RC[1]);
+ EXPECT_EQ(&B3C, &RC[2]);
+ EXPECT_EQ(&C2C, &RC[3]);
+ EXPECT_EQ(&B2C, &RC[4]);
+ EXPECT_EQ(&C1C, &RC[5]);
+ EXPECT_EQ(&B1C, &RC[6]);
+ EXPECT_EQ(&AC, &RC[7]);
+
+ // Switch C3 -> B1 to a call edge. This doesn't form any new cycles but does
+ // require reordering the SCCs in the face of tricky internal node
+ // structures.
+ auto MergedCs = RC.switchInternalEdgeToCall(C3, B1);
+ EXPECT_EQ(0u, MergedCs.size());
+ ASSERT_EQ(8, RC.size());
+ EXPECT_EQ(&DC, &RC[0]);
+ EXPECT_EQ(&B3C, &RC[1]);
+ EXPECT_EQ(&B2C, &RC[2]);
+ EXPECT_EQ(&B1C, &RC[3]);
+ EXPECT_EQ(&C3C, &RC[4]);
+ EXPECT_EQ(&C2C, &RC[5]);
+ EXPECT_EQ(&C1C, &RC[6]);
+ EXPECT_EQ(&AC, &RC[7]);
+}
- // Remove the edge from c -> a, which should leave 'a' in the original SCC
- // and form a new SCC for 'b' and 'c'.
- NewSCCs = SCC.removeIntraSCCEdge(C, A);
- EXPECT_EQ(1u, NewSCCs.size());
- EXPECT_EQ(&SCC, CG1.lookupSCC(A));
- EXPECT_EQ(1, std::distance(SCC.begin(), SCC.end()));
- LazyCallGraph::SCC *SCC2 = CG1.lookupSCC(B);
- EXPECT_EQ(SCC2, CG1.lookupSCC(C));
- EXPECT_EQ(SCC2, NewSCCs[0]);
+TEST(LazyCallGraphTest, InternalRefEdgeToCallBothPartitionAndMerge) {
+ LLVMContext Context;
+ // Test for having a postorder where between the source and target are all
+ // three kinds of other SCCs:
+ // 1) One connected to the target only that have to be shifted below the
+ // source.
+ // 2) One connected to the source only that have to be shifted below the
+ // target.
+ // 3) One connected to both source and target that has to remain and get
+ // merged away.
+ //
+ // To achieve this we construct a heavily connected graph to force
+ // a particular post-order. Then we remove the forcing edges and connect
+ // a cycle.
+ //
+ // Diagram for the graph we want on the left and the graph we use to force
+ // the ordering on the right. Edges ponit down or right.
+ //
+ // A | A |
+ // / \ | / \ |
+ // B E | B \ |
+ // |\ | | |\ | |
+ // | D | | C-D-E |
+ // | \| | | \| |
+ // C F | \ F |
+ // \ / | \ / |
+ // G | G |
+ //
+ // And we form a cycle by connecting F to B.
+ std::unique_ptr<Module> M =
+ parseAssembly(Context, "define void @a() {\n"
+ "entry:\n"
+ " call void @b()\n"
+ " call void @e()\n"
+ " ret void\n"
+ "}\n"
+ "define void @b() {\n"
+ "entry:\n"
+ " call void @c()\n"
+ " call void @d()\n"
+ " ret void\n"
+ "}\n"
+ "define void @c() {\n"
+ "entry:\n"
+ " call void @d()\n"
+ " call void @g()\n"
+ " ret void\n"
+ "}\n"
+ "define void @d() {\n"
+ "entry:\n"
+ " call void @e()\n"
+ " call void @f()\n"
+ " ret void\n"
+ "}\n"
+ "define void @e() {\n"
+ "entry:\n"
+ " call void @f()\n"
+ " ret void\n"
+ "}\n"
+ "define void @f() {\n"
+ "entry:\n"
+ " store void()* @b, void()** undef\n"
+ " call void @g()\n"
+ " ret void\n"
+ "}\n"
+ "define void @g() {\n"
+ "entry:\n"
+ " store void()* @a, void()** undef\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG(*M);
+
+ // Force the graph to be fully expanded.
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+
+ LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));
+ LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));
+ LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));
+ LazyCallGraph::Node &E = *CG.lookup(lookupFunction(*M, "e"));
+ LazyCallGraph::Node &F = *CG.lookup(lookupFunction(*M, "f"));
+ LazyCallGraph::Node &G = *CG.lookup(lookupFunction(*M, "g"));
+ LazyCallGraph::SCC &AC = *CG.lookupSCC(A);
+ LazyCallGraph::SCC &BC = *CG.lookupSCC(B);
+ LazyCallGraph::SCC &CC = *CG.lookupSCC(C);
+ LazyCallGraph::SCC &DC = *CG.lookupSCC(D);
+ LazyCallGraph::SCC &EC = *CG.lookupSCC(E);
+ LazyCallGraph::SCC &FC = *CG.lookupSCC(F);
+ LazyCallGraph::SCC &GC = *CG.lookupSCC(G);
+
+ // Remove the extra edges that were used to force a particular post-order.
+ RC.switchInternalEdgeToRef(C, D);
+ RC.switchInternalEdgeToRef(D, E);
+
+ // Check the initial post-order. We ensure this order with the extra edges
+ // that are nuked above.
+ ASSERT_EQ(7, RC.size());
+ EXPECT_EQ(&GC, &RC[0]);
+ EXPECT_EQ(&FC, &RC[1]);
+ EXPECT_EQ(&EC, &RC[2]);
+ EXPECT_EQ(&DC, &RC[3]);
+ EXPECT_EQ(&CC, &RC[4]);
+ EXPECT_EQ(&BC, &RC[5]);
+ EXPECT_EQ(&AC, &RC[6]);
+
+ // Switch F -> B to a call edge. This merges B, D, and F into a single SCC,
+ // and has to place the C and E SCCs on either side of it:
+ // A A |
+ // / \ / \ |
+ // B E | E |
+ // |\ | \ / |
+ // | D | -> B |
+ // | \| / \ |
+ // C F C | |
+ // \ / \ / |
+ // G G |
+ auto MergedCs = RC.switchInternalEdgeToCall(F, B);
+ ASSERT_EQ(2u, MergedCs.size());
+ EXPECT_EQ(&FC, MergedCs[0]);
+ EXPECT_EQ(&DC, MergedCs[1]);
+ EXPECT_EQ(3, BC.size());
+
+ // And make sure the postorder was updated.
+ ASSERT_EQ(5, RC.size());
+ EXPECT_EQ(&GC, &RC[0]);
+ EXPECT_EQ(&CC, &RC[1]);
+ EXPECT_EQ(&BC, &RC[2]);
+ EXPECT_EQ(&EC, &RC[3]);
+ EXPECT_EQ(&AC, &RC[4]);
}
}
diff --git a/unittests/Analysis/LoopPassManagerTest.cpp b/unittests/Analysis/LoopPassManagerTest.cpp
new file mode 100644
index 000000000000..5858e174aabb
--- /dev/null
+++ b/unittests/Analysis/LoopPassManagerTest.cpp
@@ -0,0 +1,205 @@
+//===- llvm/unittest/Analysis/LoopPassManagerTest.cpp - LPM tests ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+#include "llvm/Analysis/LoopPassManager.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/SourceMgr.h"
+
+using namespace llvm;
+
+namespace {
+
+class TestLoopAnalysis {
+ /// \brief Private static data to provide unique ID.
+ static char PassID;
+
+ int &Runs;
+
+public:
+ struct Result {
+ Result(int Count) : BlockCount(Count) {}
+ int BlockCount;
+ };
+
+ /// \brief Returns an opaque, unique ID for this pass type.
+ static void *ID() { return (void *)&PassID; }
+
+ /// \brief Returns the name of the analysis.
+ static StringRef name() { return "TestLoopAnalysis"; }
+
+ TestLoopAnalysis(int &Runs) : Runs(Runs) {}
+
+ /// \brief Run the analysis pass over the loop and return a result.
+ Result run(Loop &L, AnalysisManager<Loop> &AM) {
+ ++Runs;
+ int Count = 0;
+
+ for (auto I = L.block_begin(), E = L.block_end(); I != E; ++I)
+ ++Count;
+ return Result(Count);
+ }
+};
+
+char TestLoopAnalysis::PassID;
+
+class TestLoopPass {
+ std::vector<StringRef> &VisitedLoops;
+ int &AnalyzedBlockCount;
+ bool OnlyUseCachedResults;
+
+public:
+ TestLoopPass(std::vector<StringRef> &VisitedLoops, int &AnalyzedBlockCount,
+ bool OnlyUseCachedResults = false)
+ : VisitedLoops(VisitedLoops), AnalyzedBlockCount(AnalyzedBlockCount),
+ OnlyUseCachedResults(OnlyUseCachedResults) {}
+
+ PreservedAnalyses run(Loop &L, AnalysisManager<Loop> &AM) {
+ VisitedLoops.push_back(L.getName());
+
+ if (OnlyUseCachedResults) {
+ // Hack to force the use of the cached interface.
+ if (auto *AR = AM.getCachedResult<TestLoopAnalysis>(L))
+ AnalyzedBlockCount += AR->BlockCount;
+ } else {
+ // Typical path just runs the analysis as needed.
+ auto &AR = AM.getResult<TestLoopAnalysis>(L);
+ AnalyzedBlockCount += AR.BlockCount;
+ }
+
+ return PreservedAnalyses::all();
+ }
+
+ static StringRef name() { return "TestLoopPass"; }
+};
+
+// A test loop pass that invalidates the analysis for loops with the given name.
+class TestLoopInvalidatingPass {
+ StringRef Name;
+
+public:
+ TestLoopInvalidatingPass(StringRef LoopName) : Name(LoopName) {}
+
+ PreservedAnalyses run(Loop &L, AnalysisManager<Loop> &AM) {
+ return L.getName() == Name ? getLoopPassPreservedAnalyses()
+ : PreservedAnalyses::all();
+ }
+
+ static StringRef name() { return "TestLoopInvalidatingPass"; }
+};
+
+std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
+ SMDiagnostic Err;
+ return parseAssemblyString(IR, Err, C);
+}
+
+class LoopPassManagerTest : public ::testing::Test {
+protected:
+ LLVMContext Context;
+ std::unique_ptr<Module> M;
+
+public:
+ LoopPassManagerTest()
+ : M(parseIR(Context, "define void @f() {\n"
+ "entry:\n"
+ " br label %loop.0\n"
+ "loop.0:\n"
+ " br i1 undef, label %loop.0.0, label %end\n"
+ "loop.0.0:\n"
+ " br i1 undef, label %loop.0.0, label %loop.0.1\n"
+ "loop.0.1:\n"
+ " br i1 undef, label %loop.0.1, label %loop.0\n"
+ "end:\n"
+ " ret void\n"
+ "}\n"
+ "\n"
+ "define void @g() {\n"
+ "entry:\n"
+ " br label %loop.g.0\n"
+ "loop.g.0:\n"
+ " br i1 undef, label %loop.g.0, label %end\n"
+ "end:\n"
+ " ret void\n"
+ "}\n")) {}
+};
+
+#define EXPECT_N_ELEMENTS_EQ(N, EXPECTED, ACTUAL) \
+ do { \
+ EXPECT_EQ(N##UL, ACTUAL.size()); \
+ for (int I = 0; I < N; ++I) \
+ EXPECT_TRUE(EXPECTED[I] == ACTUAL[I]) << "Element " << I << " is " \
+ << ACTUAL[I] << ". Expected " \
+ << EXPECTED[I] << "."; \
+ } while (0)
+
+TEST_F(LoopPassManagerTest, Basic) {
+ LoopAnalysisManager LAM(true);
+ int LoopAnalysisRuns = 0;
+ LAM.registerPass([&] { return TestLoopAnalysis(LoopAnalysisRuns); });
+
+ FunctionAnalysisManager FAM(true);
+ // We need DominatorTreeAnalysis for LoopAnalysis.
+ FAM.registerPass([&] { return DominatorTreeAnalysis(); });
+ FAM.registerPass([&] { return LoopAnalysis(); });
+ FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); });
+ LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); });
+
+ ModuleAnalysisManager MAM(true);
+ MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
+ FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
+
+ ModulePassManager MPM(true);
+ FunctionPassManager FPM(true);
+
+ // Visit all of the loops.
+ std::vector<StringRef> VisitedLoops1;
+ int AnalyzedBlockCount1 = 0;
+ {
+ LoopPassManager LPM;
+ LPM.addPass(TestLoopPass(VisitedLoops1, AnalyzedBlockCount1));
+
+ FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
+ }
+
+ // Only use cached analyses.
+ std::vector<StringRef> VisitedLoops2;
+ int AnalyzedBlockCount2 = 0;
+ {
+ LoopPassManager LPM;
+ LPM.addPass(TestLoopInvalidatingPass("loop.g.0"));
+ LPM.addPass(TestLoopPass(VisitedLoops2, AnalyzedBlockCount2,
+ /*OnlyUseCachedResults=*/true));
+
+ FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
+ }
+
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+ MPM.run(*M, MAM);
+
+ StringRef ExpectedLoops[] = {"loop.0.0", "loop.0.1", "loop.0", "loop.g.0"};
+
+ // Validate the counters and order of loops visited.
+ // loop.0 has 3 blocks whereas loop.0.0, loop.0.1, and loop.g.0 each have 1.
+ EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops1);
+ EXPECT_EQ(6, AnalyzedBlockCount1);
+
+ EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops2);
+ // The block from loop.g.0 won't be counted, since it wasn't cached.
+ EXPECT_EQ(5, AnalyzedBlockCount2);
+
+ // The first LPM runs the loop analysis for all four loops, the second uses
+ // cached results for everything.
+ EXPECT_EQ(4, LoopAnalysisRuns);
+}
+}
diff --git a/unittests/Analysis/Makefile b/unittests/Analysis/Makefile
deleted file mode 100644
index 527f4525e87e..000000000000
--- a/unittests/Analysis/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Analysis/Makefile -------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = Analysis
-LINK_COMPONENTS := analysis asmparser
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/Analysis/MixedTBAATest.cpp b/unittests/Analysis/MixedTBAATest.cpp
index d0cfa59f6459..d70324f2c6ab 100644
--- a/unittests/Analysis/MixedTBAATest.cpp
+++ b/unittests/Analysis/MixedTBAATest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/TypeBasedAliasAnalysis.h"
+#include "llvm/Analysis/AliasAnalysisEvaluator.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
diff --git a/unittests/Analysis/ScalarEvolutionTest.cpp b/unittests/Analysis/ScalarEvolutionTest.cpp
index 938dafe60384..c4cd74f01c72 100644
--- a/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -237,5 +237,32 @@ TEST_F(ScalarEvolutionsTest, SCEVMultiplyAddRecs) {
EXPECT_EQ(Product->getOperand(8), SE.getAddExpr(Sum));
}
+TEST_F(ScalarEvolutionsTest, SimplifiedPHI) {
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context),
+ std::vector<Type *>(), false);
+ Function *F = cast<Function>(M.getOrInsertFunction("f", FTy));
+ BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);
+ BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F);
+ BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F);
+ BranchInst::Create(LoopBB, EntryBB);
+ BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)),
+ LoopBB);
+ ReturnInst::Create(Context, nullptr, ExitBB);
+ auto *Ty = Type::getInt32Ty(Context);
+ auto *PN = PHINode::Create(Ty, 2, "", &*LoopBB->begin());
+ PN->addIncoming(Constant::getNullValue(Ty), EntryBB);
+ PN->addIncoming(UndefValue::get(Ty), LoopBB);
+ ScalarEvolution SE = buildSE(*F);
+ auto *S1 = SE.getSCEV(PN);
+ auto *S2 = SE.getSCEV(PN);
+ auto *ZeroConst = SE.getConstant(Ty, 0);
+
+ // At some point, only the first call to getSCEV returned the simplified
+ // SCEVConstant and later calls just returned a SCEVUnknown referencing the
+ // PHI node.
+ EXPECT_EQ(S1, ZeroConst);
+ EXPECT_EQ(S1, S2);
+}
+
} // end anonymous namespace
} // end namespace llvm
diff --git a/unittests/Analysis/UnrollAnalyzer.cpp b/unittests/Analysis/UnrollAnalyzer.cpp
new file mode 100644
index 000000000000..6d11ab1f2f1b
--- /dev/null
+++ b/unittests/Analysis/UnrollAnalyzer.cpp
@@ -0,0 +1,331 @@
+//===- UnrollAnalyzerTest.cpp - UnrollAnalyzer unit tests -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Analysis/LoopUnrollAnalyzer.h"
+#include "llvm/IR/Dominators.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+namespace llvm {
+void initializeUnrollAnalyzerTestPass(PassRegistry &);
+
+static SmallVector<DenseMap<Value *, Constant *>, 16> SimplifiedValuesVector;
+static unsigned TripCount = 0;
+
+namespace {
+struct UnrollAnalyzerTest : public FunctionPass {
+ static char ID;
+ bool runOnFunction(Function &F) override {
+ LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ ScalarEvolution *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+
+ Function::iterator FI = F.begin();
+ FI++; // First basic block is entry - skip it.
+ BasicBlock *Header = &*FI++;
+ Loop *L = LI->getLoopFor(Header);
+ BasicBlock *Exiting = L->getExitingBlock();
+
+ SimplifiedValuesVector.clear();
+ TripCount = SE->getSmallConstantTripCount(L, Exiting);
+ for (unsigned Iteration = 0; Iteration < TripCount; Iteration++) {
+ DenseMap<Value *, Constant *> SimplifiedValues;
+ UnrolledInstAnalyzer Analyzer(Iteration, SimplifiedValues, *SE, L);
+ for (auto *BB : L->getBlocks())
+ for (Instruction &I : *BB)
+ Analyzer.visit(I);
+ SimplifiedValuesVector.push_back(SimplifiedValues);
+ }
+ return false;
+ }
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.addRequired<ScalarEvolutionWrapperPass>();
+ AU.setPreservesAll();
+ }
+ UnrollAnalyzerTest() : FunctionPass(ID) {
+ initializeUnrollAnalyzerTestPass(*PassRegistry::getPassRegistry());
+ }
+};
+}
+
+char UnrollAnalyzerTest::ID = 0;
+
+std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,
+ UnrollAnalyzerTest *P,
+ const char *ModuleStr) {
+ SMDiagnostic Err;
+ return parseAssemblyString(ModuleStr, Err, Context);
+}
+
+TEST(UnrollAnalyzerTest, BasicSimplifications) {
+ const char *ModuleStr =
+ "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"
+ "define i64 @propagate_loop_phis() {\n"
+ "entry:\n"
+ " br label %loop\n"
+ "loop:\n"
+ " %iv = phi i64 [ 0, %entry ], [ %inc, %loop ]\n"
+ " %x0 = phi i64 [ 0, %entry ], [ %x2, %loop ]\n"
+ " %x1 = or i64 %x0, 1\n"
+ " %x2 = or i64 %x1, 2\n"
+ " %inc = add nuw nsw i64 %iv, 1\n"
+ " %cond = icmp sge i64 %inc, 8\n"
+ " br i1 %cond, label %loop.end, label %loop\n"
+ "loop.end:\n"
+ " %x.lcssa = phi i64 [ %x2, %loop ]\n"
+ " ret i64 %x.lcssa\n"
+ "}\n";
+ UnrollAnalyzerTest *P = new UnrollAnalyzerTest();
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr);
+ legacy::PassManager Passes;
+ Passes.add(P);
+ Passes.run(*M);
+
+ // Perform checks
+ Module::iterator MI = M->begin();
+ Function *F = &*MI++;
+ Function::iterator FI = F->begin();
+ FI++; // First basic block is entry - skip it.
+ BasicBlock *Header = &*FI++;
+
+ BasicBlock::iterator BBI = Header->begin();
+ std::advance(BBI, 4);
+ Instruction *Y1 = &*BBI++;
+ Instruction *Y2 = &*BBI++;
+ // Check simplification expected on the 1st iteration.
+ // Check that "%inc = add nuw nsw i64 %iv, 1" is simplified to 1
+ auto I1 = SimplifiedValuesVector[0].find(Y1);
+ EXPECT_TRUE(I1 != SimplifiedValuesVector[0].end());
+ EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 1U);
+
+ // Check that "%cond = icmp sge i64 %inc, 10" is simplified to false
+ auto I2 = SimplifiedValuesVector[0].find(Y2);
+ EXPECT_TRUE(I2 != SimplifiedValuesVector[0].end());
+ EXPECT_FALSE(cast<ConstantInt>((*I2).second)->getZExtValue());
+
+ // Check simplification expected on the last iteration.
+ // Check that "%inc = add nuw nsw i64 %iv, 1" is simplified to 8
+ I1 = SimplifiedValuesVector[TripCount - 1].find(Y1);
+ EXPECT_TRUE(I1 != SimplifiedValuesVector[TripCount - 1].end());
+ EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), TripCount);
+
+ // Check that "%cond = icmp sge i64 %inc, 10" is simplified to false
+ I2 = SimplifiedValuesVector[TripCount - 1].find(Y2);
+ EXPECT_TRUE(I2 != SimplifiedValuesVector[TripCount - 1].end());
+ EXPECT_TRUE(cast<ConstantInt>((*I2).second)->getZExtValue());
+}
+
+TEST(UnrollAnalyzerTest, OuterLoopSimplification) {
+ const char *ModuleStr =
+ "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"
+ "define void @foo() {\n"
+ "entry:\n"
+ " br label %outer.loop\n"
+ "outer.loop:\n"
+ " %iv.outer = phi i64 [ 0, %entry ], [ %iv.outer.next, %outer.loop.latch ]\n"
+ " %iv.outer.next = add nuw nsw i64 %iv.outer, 1\n"
+ " br label %inner.loop\n"
+ "inner.loop:\n"
+ " %iv.inner = phi i64 [ 0, %outer.loop ], [ %iv.inner.next, %inner.loop ]\n"
+ " %iv.inner.next = add nuw nsw i64 %iv.inner, 1\n"
+ " %exitcond.inner = icmp eq i64 %iv.inner.next, 1000\n"
+ " br i1 %exitcond.inner, label %outer.loop.latch, label %inner.loop\n"
+ "outer.loop.latch:\n"
+ " %exitcond.outer = icmp eq i64 %iv.outer.next, 40\n"
+ " br i1 %exitcond.outer, label %exit, label %outer.loop\n"
+ "exit:\n"
+ " ret void\n"
+ "}\n";
+
+ UnrollAnalyzerTest *P = new UnrollAnalyzerTest();
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr);
+ legacy::PassManager Passes;
+ Passes.add(P);
+ Passes.run(*M);
+
+ Module::iterator MI = M->begin();
+ Function *F = &*MI++;
+ Function::iterator FI = F->begin();
+ FI++;
+ BasicBlock *Header = &*FI++;
+ BasicBlock *InnerBody = &*FI++;
+
+ BasicBlock::iterator BBI = Header->begin();
+ BBI++;
+ Instruction *Y1 = &*BBI;
+ BBI = InnerBody->begin();
+ BBI++;
+ Instruction *Y2 = &*BBI;
+ // Check that we can simplify IV of the outer loop, but can't simplify the IV
+ // of the inner loop if we only know the iteration number of the outer loop.
+ //
+ // Y1 is %iv.outer.next, Y2 is %iv.inner.next
+ auto I1 = SimplifiedValuesVector[0].find(Y1);
+ EXPECT_TRUE(I1 != SimplifiedValuesVector[0].end());
+ auto I2 = SimplifiedValuesVector[0].find(Y2);
+ EXPECT_TRUE(I2 == SimplifiedValuesVector[0].end());
+}
+TEST(UnrollAnalyzerTest, CmpSimplifications) {
+ const char *ModuleStr =
+ "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"
+ "define void @branch_iv_trunc() {\n"
+ "entry:\n"
+ " br label %for.body\n"
+ "for.body:\n"
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %tmp3, %for.body ]\n"
+ " %tmp2 = trunc i64 %indvars.iv to i32\n"
+ " %cmp3 = icmp eq i32 %tmp2, 5\n"
+ " %tmp3 = add nuw nsw i64 %indvars.iv, 1\n"
+ " %exitcond = icmp eq i64 %tmp3, 10\n"
+ " br i1 %exitcond, label %for.end, label %for.body\n"
+ "for.end:\n"
+ " ret void\n"
+ "}\n";
+ UnrollAnalyzerTest *P = new UnrollAnalyzerTest();
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr);
+ legacy::PassManager Passes;
+ Passes.add(P);
+ Passes.run(*M);
+
+ // Perform checks
+ Module::iterator MI = M->begin();
+ Function *F = &*MI++;
+ Function::iterator FI = F->begin();
+ FI++; // First basic block is entry - skip it.
+ BasicBlock *Header = &*FI++;
+
+ BasicBlock::iterator BBI = Header->begin();
+ BBI++;
+ Instruction *Y1 = &*BBI++;
+ Instruction *Y2 = &*BBI++;
+ // Check simplification expected on the 5th iteration.
+ // Check that "%tmp2 = trunc i64 %indvars.iv to i32" is simplified to 5
+ // and "%cmp3 = icmp eq i32 %tmp2, 5" is simplified to 1 (i.e. true).
+ auto I1 = SimplifiedValuesVector[5].find(Y1);
+ EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end());
+ EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 5U);
+ auto I2 = SimplifiedValuesVector[5].find(Y2);
+ EXPECT_TRUE(I2 != SimplifiedValuesVector[5].end());
+ EXPECT_EQ(cast<ConstantInt>((*I2).second)->getZExtValue(), 1U);
+}
+TEST(UnrollAnalyzerTest, PtrCmpSimplifications) {
+ const char *ModuleStr =
+ "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"
+ "define void @ptr_cmp(i8 *%a) {\n"
+ "entry:\n"
+ " %limit = getelementptr i8, i8* %a, i64 40\n"
+ " %start.iv2 = getelementptr i8, i8* %a, i64 7\n"
+ " br label %loop.body\n"
+ "loop.body:\n"
+ " %iv.0 = phi i8* [ %a, %entry ], [ %iv.1, %loop.body ]\n"
+ " %iv2.0 = phi i8* [ %start.iv2, %entry ], [ %iv2.1, %loop.body ]\n"
+ " %cmp = icmp eq i8* %iv2.0, %iv.0\n"
+ " %iv.1 = getelementptr inbounds i8, i8* %iv.0, i64 1\n"
+ " %iv2.1 = getelementptr inbounds i8, i8* %iv2.0, i64 1\n"
+ " %exitcond = icmp ne i8* %iv.1, %limit\n"
+ " br i1 %exitcond, label %loop.body, label %loop.exit\n"
+ "loop.exit:\n"
+ " ret void\n"
+ "}\n";
+ UnrollAnalyzerTest *P = new UnrollAnalyzerTest();
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr);
+ legacy::PassManager Passes;
+ Passes.add(P);
+ Passes.run(*M);
+
+ // Perform checks
+ Module::iterator MI = M->begin();
+ Function *F = &*MI++;
+ Function::iterator FI = F->begin();
+ FI++; // First basic block is entry - skip it.
+ BasicBlock *Header = &*FI;
+
+ BasicBlock::iterator BBI = Header->begin();
+ std::advance(BBI, 2);
+ Instruction *Y1 = &*BBI;
+ // Check simplification expected on the 5th iteration.
+ // Check that "%cmp = icmp eq i8* %iv2.0, %iv.0" is simplified to 0.
+ auto I1 = SimplifiedValuesVector[5].find(Y1);
+ EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end());
+ EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 0U);
+}
+TEST(UnrollAnalyzerTest, CastSimplifications) {
+ const char *ModuleStr =
+ "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"
+ "@known_constant = internal unnamed_addr constant [10 x i32] [i32 0, i32 1, i32 0, i32 1, i32 0, i32 259, i32 0, i32 1, i32 0, i32 1], align 16\n"
+ "define void @const_load_cast() {\n"
+ "entry:\n"
+ " br label %loop\n"
+ "\n"
+ "loop:\n"
+ " %iv = phi i64 [ 0, %entry ], [ %inc, %loop ]\n"
+ " %array_const_idx = getelementptr inbounds [10 x i32], [10 x i32]* @known_constant, i64 0, i64 %iv\n"
+ " %const_array_element = load i32, i32* %array_const_idx, align 4\n"
+ " %se = sext i32 %const_array_element to i64\n"
+ " %ze = zext i32 %const_array_element to i64\n"
+ " %tr = trunc i32 %const_array_element to i8\n"
+ " %inc = add nuw nsw i64 %iv, 1\n"
+ " %exitcond86.i = icmp eq i64 %inc, 10\n"
+ " br i1 %exitcond86.i, label %loop.end, label %loop\n"
+ "\n"
+ "loop.end:\n"
+ " ret void\n"
+ "}\n";
+
+ UnrollAnalyzerTest *P = new UnrollAnalyzerTest();
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, P, ModuleStr);
+ legacy::PassManager Passes;
+ Passes.add(P);
+ Passes.run(*M);
+
+ // Perform checks
+ Module::iterator MI = M->begin();
+ Function *F = &*MI++;
+ Function::iterator FI = F->begin();
+ FI++; // First basic block is entry - skip it.
+ BasicBlock *Header = &*FI++;
+
+ BasicBlock::iterator BBI = Header->begin();
+ std::advance(BBI, 3);
+ Instruction *Y1 = &*BBI++;
+ Instruction *Y2 = &*BBI++;
+ Instruction *Y3 = &*BBI++;
+ // Check simplification expected on the 5th iteration.
+ // "%se = sext i32 %const_array_element to i64" should be simplified to 259,
+ // "%ze = zext i32 %const_array_element to i64" should be simplified to 259,
+ // "%tr = trunc i32 %const_array_element to i8" should be simplified to 3.
+ auto I1 = SimplifiedValuesVector[5].find(Y1);
+ EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end());
+ EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 259U);
+ auto I2 = SimplifiedValuesVector[5].find(Y2);
+ EXPECT_TRUE(I2 != SimplifiedValuesVector[5].end());
+ EXPECT_EQ(cast<ConstantInt>((*I2).second)->getZExtValue(), 259U);
+ auto I3 = SimplifiedValuesVector[5].find(Y3);
+ EXPECT_TRUE(I3 != SimplifiedValuesVector[5].end());
+ EXPECT_EQ(cast<ConstantInt>((*I3).second)->getZExtValue(), 3U);
+}
+
+} // end namespace llvm
+
+INITIALIZE_PASS_BEGIN(UnrollAnalyzerTest, "unrollanalyzertestpass",
+ "unrollanalyzertestpass", false, false)
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
+INITIALIZE_PASS_END(UnrollAnalyzerTest, "unrollanalyzertestpass",
+ "unrollanalyzertestpass", false, false)
diff --git a/unittests/Analysis/ValueTrackingTest.cpp b/unittests/Analysis/ValueTrackingTest.cpp
index 3af856ea2039..f429c3b99149 100644
--- a/unittests/Analysis/ValueTrackingTest.cpp
+++ b/unittests/Analysis/ValueTrackingTest.cpp
@@ -25,7 +25,7 @@ class MatchSelectPatternTest : public testing::Test {
protected:
void parseAssembly(const char *Assembly) {
SMDiagnostic Error;
- M = parseAssemblyString(Assembly, Error, getGlobalContext());
+ M = parseAssemblyString(Assembly, Error, Context);
std::string errMsg;
raw_string_ostream os(errMsg);
@@ -59,6 +59,7 @@ protected:
EXPECT_EQ(P.Ordered, R.Ordered);
}
+ LLVMContext Context;
std::unique_ptr<Module> M;
Instruction *A, *B;
};
diff --git a/unittests/AsmParser/AsmParserTest.cpp b/unittests/AsmParser/AsmParserTest.cpp
index 4189310fda23..ddbedd05c7ba 100644
--- a/unittests/AsmParser/AsmParserTest.cpp
+++ b/unittests/AsmParser/AsmParserTest.cpp
@@ -21,7 +21,7 @@ using namespace llvm;
namespace {
TEST(AsmParserTest, NullTerminatedInput) {
- LLVMContext &Ctx = getGlobalContext();
+ LLVMContext Ctx;
StringRef Source = "; Empty module \n";
SMDiagnostic Error;
auto Mod = parseAssemblyString(Source, Error, Ctx);
@@ -34,7 +34,7 @@ TEST(AsmParserTest, NullTerminatedInput) {
#ifndef NDEBUG
TEST(AsmParserTest, NonNullTerminatedInput) {
- LLVMContext &Ctx = getGlobalContext();
+ LLVMContext Ctx;
StringRef Source = "; Empty module \n\1\2";
SMDiagnostic Error;
std::unique_ptr<Module> Mod;
@@ -47,7 +47,7 @@ TEST(AsmParserTest, NonNullTerminatedInput) {
#endif
TEST(AsmParserTest, SlotMappingTest) {
- LLVMContext &Ctx = getGlobalContext();
+ LLVMContext Ctx;
StringRef Source = "@0 = global i32 0\n !0 = !{}\n !42 = !{i32 42}";
SMDiagnostic Error;
SlotMapping Mapping;
@@ -66,7 +66,7 @@ TEST(AsmParserTest, SlotMappingTest) {
}
TEST(AsmParserTest, TypeAndConstantValueParsing) {
- LLVMContext &Ctx = getGlobalContext();
+ LLVMContext Ctx;
SMDiagnostic Error;
StringRef Source = "define void @test() {\n entry:\n ret void\n}";
auto Mod = parseAssemblyString(Source, Error, Ctx);
@@ -117,7 +117,7 @@ TEST(AsmParserTest, TypeAndConstantValueParsing) {
}
TEST(AsmParserTest, TypeAndConstantValueWithSlotMappingParsing) {
- LLVMContext &Ctx = getGlobalContext();
+ LLVMContext Ctx;
SMDiagnostic Error;
StringRef Source =
"%st = type { i32, i32 }\n"
@@ -152,4 +152,267 @@ TEST(AsmParserTest, TypeAndConstantValueWithSlotMappingParsing) {
ASSERT_TRUE(isa<ConstantExpr>(V));
}
+TEST(AsmParserTest, TypeWithSlotMappingParsing) {
+ LLVMContext Ctx;
+ SMDiagnostic Error;
+ StringRef Source =
+ "%st = type { i32, i32 }\n"
+ "@v = common global [50 x %st] zeroinitializer, align 16\n"
+ "%0 = type { i32, i32, i32, i32 }\n"
+ "@g = common global [50 x %0] zeroinitializer, align 16\n"
+ "define void @marker4(i64 %d) {\n"
+ "entry:\n"
+ " %conv = trunc i64 %d to i32\n"
+ " store i32 %conv, i32* getelementptr inbounds "
+ " ([50 x %st], [50 x %st]* @v, i64 0, i64 0, i32 0), align 16\n"
+ " store i32 %conv, i32* getelementptr inbounds "
+ " ([50 x %0], [50 x %0]* @g, i64 0, i64 0, i32 0), align 16\n"
+ " ret void\n"
+ "}";
+ SlotMapping Mapping;
+ auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping);
+ ASSERT_TRUE(Mod != nullptr);
+ auto &M = *Mod;
+
+ // Check we properly parse integer types.
+ Type *Ty;
+ Ty = parseType("i32", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+
+ // Check we properly parse integer types with exotic size.
+ Ty = parseType("i13", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13);
+
+ // Check we properly parse floating point types.
+ Ty = parseType("float", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isFloatTy());
+
+ Ty = parseType("double", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isDoubleTy());
+
+ // Check we properly parse struct types.
+ // Named struct.
+ Ty = parseType("%st", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isStructTy());
+
+ // Check the details of the struct.
+ StructType *ST = cast<StructType>(Ty);
+ ASSERT_TRUE(ST->getNumElements() == 2);
+ for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {
+ Ty = ST->getElementType(i);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+ }
+
+ // Anonymous struct.
+ Ty = parseType("%0", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isStructTy());
+
+ // Check the details of the struct.
+ ST = cast<StructType>(Ty);
+ ASSERT_TRUE(ST->getNumElements() == 4);
+ for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {
+ Ty = ST->getElementType(i);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+ }
+
+ // Check we properly parse vector types.
+ Ty = parseType("<5 x i32>", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isVectorTy());
+
+ // Check the details of the vector.
+ VectorType *VT = cast<VectorType>(Ty);
+ ASSERT_TRUE(VT->getNumElements() == 5);
+ ASSERT_TRUE(VT->getBitWidth() == 160);
+ Ty = VT->getElementType();
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+
+ // Opaque struct.
+ Ty = parseType("%opaque", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isStructTy());
+
+ ST = cast<StructType>(Ty);
+ ASSERT_TRUE(ST->isOpaque());
+
+ // Check we properly parse pointer types.
+ // One indirection.
+ Ty = parseType("i32*", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isPointerTy());
+
+ PointerType *PT = cast<PointerType>(Ty);
+ Ty = PT->getElementType();
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+
+ // Two indirections.
+ Ty = parseType("i32**", Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isPointerTy());
+
+ PT = cast<PointerType>(Ty);
+ Ty = PT->getElementType();
+ ASSERT_TRUE(Ty->isPointerTy());
+
+ PT = cast<PointerType>(Ty);
+ Ty = PT->getElementType();
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+
+ // Check that we reject types with garbage.
+ Ty = parseType("i32 garbage", Error, M, &Mapping);
+ ASSERT_TRUE(!Ty);
+}
+
+TEST(AsmParserTest, TypeAtBeginningWithSlotMappingParsing) {
+ LLVMContext Ctx;
+ SMDiagnostic Error;
+ StringRef Source =
+ "%st = type { i32, i32 }\n"
+ "@v = common global [50 x %st] zeroinitializer, align 16\n"
+ "%0 = type { i32, i32, i32, i32 }\n"
+ "@g = common global [50 x %0] zeroinitializer, align 16\n"
+ "define void @marker4(i64 %d) {\n"
+ "entry:\n"
+ " %conv = trunc i64 %d to i32\n"
+ " store i32 %conv, i32* getelementptr inbounds "
+ " ([50 x %st], [50 x %st]* @v, i64 0, i64 0, i32 0), align 16\n"
+ " store i32 %conv, i32* getelementptr inbounds "
+ " ([50 x %0], [50 x %0]* @g, i64 0, i64 0, i32 0), align 16\n"
+ " ret void\n"
+ "}";
+ SlotMapping Mapping;
+ auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping);
+ ASSERT_TRUE(Mod != nullptr);
+ auto &M = *Mod;
+ unsigned Read;
+
+ // Check we properly parse integer types.
+ Type *Ty;
+ Ty = parseTypeAtBeginning("i32", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+ ASSERT_TRUE(Read == 3);
+
+ // Check we properly parse integer types with exotic size.
+ Ty = parseTypeAtBeginning("i13", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13);
+ ASSERT_TRUE(Read == 3);
+
+ // Check we properly parse floating point types.
+ Ty = parseTypeAtBeginning("float", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isFloatTy());
+ ASSERT_TRUE(Read == 5);
+
+ Ty = parseTypeAtBeginning("double", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isDoubleTy());
+ ASSERT_TRUE(Read == 6);
+
+ // Check we properly parse struct types.
+ // Named struct.
+ Ty = parseTypeAtBeginning("%st", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isStructTy());
+ ASSERT_TRUE(Read == 3);
+
+ // Check the details of the struct.
+ StructType *ST = cast<StructType>(Ty);
+ ASSERT_TRUE(ST->getNumElements() == 2);
+ for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {
+ Ty = ST->getElementType(i);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+ }
+
+ // Anonymous struct.
+ Ty = parseTypeAtBeginning("%0", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isStructTy());
+ ASSERT_TRUE(Read == 2);
+
+ // Check the details of the struct.
+ ST = cast<StructType>(Ty);
+ ASSERT_TRUE(ST->getNumElements() == 4);
+ for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {
+ Ty = ST->getElementType(i);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+ }
+
+ // Check we properly parse vector types.
+ Ty = parseTypeAtBeginning("<5 x i32>", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isVectorTy());
+ ASSERT_TRUE(Read == 9);
+
+ // Check the details of the vector.
+ VectorType *VT = cast<VectorType>(Ty);
+ ASSERT_TRUE(VT->getNumElements() == 5);
+ ASSERT_TRUE(VT->getBitWidth() == 160);
+ Ty = VT->getElementType();
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+
+ // Opaque struct.
+ Ty = parseTypeAtBeginning("%opaque", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isStructTy());
+ ASSERT_TRUE(Read == 7);
+
+ ST = cast<StructType>(Ty);
+ ASSERT_TRUE(ST->isOpaque());
+
+ // Check we properly parse pointer types.
+ // One indirection.
+ Ty = parseTypeAtBeginning("i32*", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isPointerTy());
+ ASSERT_TRUE(Read == 4);
+
+ PointerType *PT = cast<PointerType>(Ty);
+ Ty = PT->getElementType();
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+
+ // Two indirections.
+ Ty = parseTypeAtBeginning("i32**", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isPointerTy());
+ ASSERT_TRUE(Read == 5);
+
+ PT = cast<PointerType>(Ty);
+ Ty = PT->getElementType();
+ ASSERT_TRUE(Ty->isPointerTy());
+
+ PT = cast<PointerType>(Ty);
+ Ty = PT->getElementType();
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+
+ // Check that we reject types with garbage.
+ Ty = parseTypeAtBeginning("i32 garbage", Read, Error, M, &Mapping);
+ ASSERT_TRUE(Ty);
+ ASSERT_TRUE(Ty->isIntegerTy());
+ ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);
+ // We go to the next token, i.e., we read "i32" + ' '.
+ ASSERT_TRUE(Read == 4);
+}
+
} // end anonymous namespace
diff --git a/unittests/AsmParser/Makefile b/unittests/AsmParser/Makefile
deleted file mode 100644
index 41eadd430433..000000000000
--- a/unittests/AsmParser/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/AsmParser/Makefile ------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = AsmParser
-LINK_COMPONENTS := AsmParser Core Support
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/Bitcode/BitReaderTest.cpp b/unittests/Bitcode/BitReaderTest.cpp
index 420aca2443bb..d0f33d12d5bc 100644
--- a/unittests/Bitcode/BitReaderTest.cpp
+++ b/unittests/Bitcode/BitReaderTest.cpp
@@ -29,10 +29,10 @@ using namespace llvm;
namespace {
-std::unique_ptr<Module> parseAssembly(const char *Assembly) {
+std::unique_ptr<Module> parseAssembly(LLVMContext &Context,
+ const char *Assembly) {
SMDiagnostic Error;
- std::unique_ptr<Module> M =
- parseAssemblyString(Assembly, Error, getGlobalContext());
+ std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);
std::string ErrMsg;
raw_string_ostream OS(ErrMsg);
@@ -54,7 +54,7 @@ static void writeModuleToBuffer(std::unique_ptr<Module> Mod,
static std::unique_ptr<Module> getLazyModuleFromAssembly(LLVMContext &Context,
SmallString<1024> &Mem,
const char *Assembly) {
- writeModuleToBuffer(parseAssembly(Assembly), Mem);
+ writeModuleToBuffer(parseAssembly(Context, Assembly), Mem);
std::unique_ptr<MemoryBuffer> Buffer =
MemoryBuffer::getMemBuffer(Mem.str(), "test", false);
ErrorOr<std::unique_ptr<Module>> ModuleOrErr =
@@ -82,7 +82,7 @@ public:
static std::unique_ptr<Module>
getStreamedModuleFromAssembly(LLVMContext &Context, SmallString<1024> &Mem,
const char *Assembly) {
- writeModuleToBuffer(parseAssembly(Assembly), Mem);
+ writeModuleToBuffer(parseAssembly(Context, Assembly), Mem);
std::unique_ptr<MemoryBuffer> Buffer =
MemoryBuffer::getMemBuffer(Mem.str(), "test", false);
auto Streamer = llvm::make_unique<BufferDataStreamer>(std::move(Buffer));
diff --git a/unittests/Bitcode/BitstreamReaderTest.cpp b/unittests/Bitcode/BitstreamReaderTest.cpp
index b11d7fde7749..2be774cc5394 100644
--- a/unittests/Bitcode/BitstreamReaderTest.cpp
+++ b/unittests/Bitcode/BitstreamReaderTest.cpp
@@ -7,13 +7,31 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/BitstreamReader.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+#include "llvm/Support/StreamingMemoryObject.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
+class BufferStreamer : public DataStreamer {
+ StringRef Buffer;
+
+public:
+ BufferStreamer(StringRef Buffer) : Buffer(Buffer) {}
+ size_t GetBytes(unsigned char *OutBuffer, size_t Length) override {
+ if (Length >= Buffer.size())
+ Length = Buffer.size();
+
+ std::copy(Buffer.begin(), Buffer.begin() + Length, OutBuffer);
+ Buffer = Buffer.drop_front(Length);
+ return Length;
+ }
+};
+
TEST(BitstreamReaderTest, AtEndOfStream) {
uint8_t Bytes[4] = {
0x00, 0x01, 0x02, 0x03
@@ -53,4 +71,171 @@ TEST(BitstreamReaderTest, AtEndOfStreamEmpty) {
EXPECT_TRUE(Cursor.AtEndOfStream());
}
+TEST(BitstreamReaderTest, getCurrentByteNo) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ for (unsigned I = 0, E = 33; I != E; ++I) {
+ EXPECT_EQ(I / 8, Cursor.getCurrentByteNo());
+ (void)Cursor.Read(1);
+ }
+ EXPECT_EQ(4u, Cursor.getCurrentByteNo());
+}
+
+TEST(BitstreamReaderTest, getPointerToByte) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ for (unsigned I = 0, E = 8; I != E; ++I) {
+ EXPECT_EQ(Bytes + I, Cursor.getPointerToByte(I, 1));
+ }
+}
+
+TEST(BitstreamReaderTest, getPointerToBit) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ for (unsigned I = 0, E = 8; I != E; ++I) {
+ EXPECT_EQ(Bytes + I, Cursor.getPointerToBit(I * 8, 1));
+ }
+}
+
+TEST(BitstreamReaderTest, jumpToPointer) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ for (unsigned I : {0, 6, 2, 7}) {
+ Cursor.jumpToPointer(Bytes + I);
+ EXPECT_EQ(I, Cursor.getCurrentByteNo());
+ }
+}
+
+TEST(BitstreamReaderTest, setArtificialByteLimit) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ Cursor.setArtificialByteLimit(8);
+ EXPECT_EQ(8u, Cursor.getSizeIfKnown());
+ while (!Cursor.AtEndOfStream())
+ (void)Cursor.Read(1);
+
+ EXPECT_EQ(8u, Cursor.getCurrentByteNo());
+}
+
+TEST(BitstreamReaderTest, setArtificialByteLimitNotWordBoundary) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ Cursor.setArtificialByteLimit(5);
+ EXPECT_EQ(8u, Cursor.getSizeIfKnown());
+ while (!Cursor.AtEndOfStream())
+ (void)Cursor.Read(1);
+
+ EXPECT_EQ(8u, Cursor.getCurrentByteNo());
+}
+
+TEST(BitstreamReaderTest, setArtificialByteLimitPastTheEnd) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ // The size of the memory object isn't known yet. Set it too high and
+ // confirm that we don't read too far.
+ Cursor.setArtificialByteLimit(24);
+ EXPECT_EQ(24u, Cursor.getSizeIfKnown());
+ while (!Cursor.AtEndOfStream())
+ (void)Cursor.Read(1);
+
+ EXPECT_EQ(12u, Cursor.getCurrentByteNo());
+ EXPECT_EQ(12u, Cursor.getSizeIfKnown());
+}
+
+TEST(BitstreamReaderTest, setArtificialByteLimitPastTheEndKnown) {
+ uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b};
+ BitstreamReader Reader(std::begin(Bytes), std::end(Bytes));
+ SimpleBitstreamCursor Cursor(Reader);
+
+ // Save the size of the memory object in the cursor.
+ while (!Cursor.AtEndOfStream())
+ (void)Cursor.Read(1);
+ EXPECT_EQ(12u, Cursor.getCurrentByteNo());
+ EXPECT_EQ(12u, Cursor.getSizeIfKnown());
+
+ Cursor.setArtificialByteLimit(20);
+ EXPECT_TRUE(Cursor.AtEndOfStream());
+ EXPECT_EQ(12u, Cursor.getSizeIfKnown());
+}
+
+TEST(BitstreamReaderTest, readRecordWithBlobWhileStreaming) {
+ SmallVector<uint8_t, 1> BlobData;
+ for (unsigned I = 0, E = 1024; I != E; ++I)
+ BlobData.push_back(I);
+
+ // Try a bunch of different sizes.
+ const unsigned Magic = 0x12345678;
+ const unsigned BlockID = bitc::FIRST_APPLICATION_BLOCKID;
+ const unsigned RecordID = 1;
+ for (unsigned I = 0, BlobSize = 0, E = BlobData.size(); BlobSize < E;
+ BlobSize += ++I) {
+ StringRef BlobIn((const char *)BlobData.begin(), BlobSize);
+
+ // Write the bitcode.
+ SmallVector<char, 1> Buffer;
+ unsigned AbbrevID;
+ {
+ BitstreamWriter Stream(Buffer);
+ Stream.Emit(Magic, 32);
+ Stream.EnterSubblock(BlockID, 3);
+
+ BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
+ Abbrev->Add(BitCodeAbbrevOp(RecordID));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+ AbbrevID = Stream.EmitAbbrev(Abbrev);
+ unsigned Record[] = {RecordID};
+ Stream.EmitRecordWithBlob(AbbrevID, makeArrayRef(Record), BlobIn);
+
+ Stream.ExitBlock();
+ }
+
+ // Stream the buffer into the reader.
+ BitstreamReader R(llvm::make_unique<StreamingMemoryObject>(
+ llvm::make_unique<BufferStreamer>(
+ StringRef(Buffer.begin(), Buffer.size()))));
+ BitstreamCursor Stream(R);
+
+ // Header. Included in test so that we can run llvm-bcanalyzer to debug
+ // when there are problems.
+ ASSERT_EQ(Magic, Stream.Read(32));
+
+ // Block.
+ BitstreamEntry Entry =
+ Stream.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs);
+ ASSERT_EQ(BitstreamEntry::SubBlock, Entry.Kind);
+ ASSERT_EQ(BlockID, Entry.ID);
+ ASSERT_FALSE(Stream.EnterSubBlock(BlockID));
+
+ // Abbreviation.
+ Entry = Stream.advance();
+ ASSERT_EQ(BitstreamEntry::Record, Entry.Kind);
+ ASSERT_EQ(AbbrevID, Entry.ID);
+
+ // Record.
+ StringRef BlobOut;
+ SmallVector<uint64_t, 1> Record;
+ ASSERT_EQ(RecordID, Stream.readRecord(Entry.ID, Record, &BlobOut));
+ EXPECT_TRUE(Record.empty());
+ EXPECT_EQ(BlobIn, BlobOut);
+ }
+}
+
} // end anonymous namespace
diff --git a/unittests/Bitcode/BitstreamWriterTest.cpp b/unittests/Bitcode/BitstreamWriterTest.cpp
new file mode 100644
index 000000000000..f17cc157cde9
--- /dev/null
+++ b/unittests/Bitcode/BitstreamWriterTest.cpp
@@ -0,0 +1,59 @@
+//===- BitstreamWriterTest.cpp - Tests for BitstreamWriter ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(BitstreamWriterTest, emitBlob) {
+ SmallString<64> Buffer;
+ BitstreamWriter W(Buffer);
+ W.emitBlob("str", /* ShouldEmitSize */ false);
+ EXPECT_EQ(StringRef("str\0", 4), Buffer);
+}
+
+TEST(BitstreamWriterTest, emitBlobWithSize) {
+ SmallString<64> Buffer;
+ {
+ BitstreamWriter W(Buffer);
+ W.emitBlob("str");
+ }
+ SmallString<64> Expected;
+ {
+ BitstreamWriter W(Expected);
+ W.EmitVBR(3, 6);
+ W.FlushToWord();
+ W.Emit('s', 8);
+ W.Emit('t', 8);
+ W.Emit('r', 8);
+ W.Emit(0, 8);
+ }
+ EXPECT_EQ(StringRef(Expected), Buffer);
+}
+
+TEST(BitstreamWriterTest, emitBlobEmpty) {
+ SmallString<64> Buffer;
+ BitstreamWriter W(Buffer);
+ W.emitBlob("", /* ShouldEmitSize */ false);
+ EXPECT_EQ(StringRef(""), Buffer);
+}
+
+TEST(BitstreamWriterTest, emitBlob4ByteAligned) {
+ SmallString<64> Buffer;
+ BitstreamWriter W(Buffer);
+ W.emitBlob("str0", /* ShouldEmitSize */ false);
+ EXPECT_EQ(StringRef("str0"), Buffer);
+}
+
+} // end namespace
diff --git a/unittests/Bitcode/CMakeLists.txt b/unittests/Bitcode/CMakeLists.txt
index 09cbcdc7284d..4d06f8008d38 100644
--- a/unittests/Bitcode/CMakeLists.txt
+++ b/unittests/Bitcode/CMakeLists.txt
@@ -9,4 +9,5 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(BitcodeTests
BitReaderTest.cpp
BitstreamReaderTest.cpp
+ BitstreamWriterTest.cpp
)
diff --git a/unittests/Bitcode/Makefile b/unittests/Bitcode/Makefile
deleted file mode 100644
index 33b09b914b56..000000000000
--- a/unittests/Bitcode/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Bitcode/Makefile --------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = Bitcode
-LINK_COMPONENTS := AsmParser BitReader BitWriter Core Support
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index e5befcec6e0f..49a9b31b60d9 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -22,6 +22,8 @@ add_subdirectory(IR)
add_subdirectory(LineEditor)
add_subdirectory(Linker)
add_subdirectory(MC)
+add_subdirectory(MI)
+add_subdirectory(ObjectYAML)
add_subdirectory(Option)
add_subdirectory(ProfileData)
add_subdirectory(Support)
diff --git a/unittests/CodeGen/DIEHashTest.cpp b/unittests/CodeGen/DIEHashTest.cpp
index e3a9e5628276..dda08fcd6654 100644
--- a/unittests/CodeGen/DIEHashTest.cpp
+++ b/unittests/CodeGen/DIEHashTest.cpp
@@ -1,4 +1,4 @@
-//===- llvm/unittest/DebugInfo/DWARFFormValueTest.cpp ---------------------===//
+//===- llvm/unittest/CodeGen/DIEHashTest.cpp ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/unittests/CodeGen/Makefile b/unittests/CodeGen/Makefile
deleted file mode 100644
index 4f07017c2914..000000000000
--- a/unittests/CodeGen/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-##===- unittests/DebugInfo/Makefile ------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = CodeGen
-LINK_COMPONENTS := asmprinter codegen support
-
-include $(LEVEL)/Makefile.config
-
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp
index 371e2af95dec..eb9607ade16d 100644
--- a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp
+++ b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/Host.h"
diff --git a/unittests/DebugInfo/DWARF/Makefile b/unittests/DebugInfo/DWARF/Makefile
deleted file mode 100644
index b0f40e1ca9d9..000000000000
--- a/unittests/DebugInfo/DWARF/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-##===- unittests/DebugInfo/Makefile ------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-TESTNAME = DebugInfoDWARF
-LINK_COMPONENTS := DebugInfoDWARF object support
-
-include $(LEVEL)/Makefile.config
-
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/DebugInfo/Makefile b/unittests/DebugInfo/Makefile
deleted file mode 100644
index 1889ad22db11..000000000000
--- a/unittests/DebugInfo/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/DebugInfo/Makefile ------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-LEVEL = ../..
-
-include $(LEVEL)/Makefile.config
-
-PARALLEL_DIRS := DWARF PDB
-
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/DebugInfo/PDB/CMakeLists.txt b/unittests/DebugInfo/PDB/CMakeLists.txt
index 91924a5a8754..405b7bb9b1a6 100644
--- a/unittests/DebugInfo/PDB/CMakeLists.txt
+++ b/unittests/DebugInfo/PDB/CMakeLists.txt
@@ -1,8 +1,11 @@
set(LLVM_LINK_COMPONENTS
+ DebugInfoCodeView
DebugInfoPDB
)
set(DebugInfoPDBSources
+ MappedBlockStreamTest.cpp
+ MsfBuilderTest.cpp
PDBApiTest.cpp
)
diff --git a/unittests/DebugInfo/PDB/ErrorChecking.h b/unittests/DebugInfo/PDB/ErrorChecking.h
new file mode 100644
index 000000000000..da734a9b1b5b
--- /dev/null
+++ b/unittests/DebugInfo/PDB/ErrorChecking.h
@@ -0,0 +1,41 @@
+//===- ErrorChecking.h - Helpers for verifying llvm::Errors -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UNITTESTS_DEBUGINFO_PDB_ERRORCHECKING_H
+#define LLVM_UNITTESTS_DEBUGINFO_PDB_ERRORCHECKING_H
+
+#define EXPECT_NO_ERROR(Err) \
+ { \
+ auto E = Err; \
+ EXPECT_FALSE(static_cast<bool>(E)); \
+ if (E) \
+ consumeError(std::move(E)); \
+ }
+
+#define EXPECT_ERROR(Err) \
+ { \
+ auto E = Err; \
+ EXPECT_TRUE(static_cast<bool>(E)); \
+ if (E) \
+ consumeError(std::move(E)); \
+ }
+
+#define EXPECT_EXPECTED(Exp) \
+ { \
+ auto E = Exp.takeError(); \
+ EXPECT_FALSE(static_cast<bool>(E)); \
+ if (E) { \
+ consumeError(std::move(E)); \
+ return; \
+ } \
+ }
+
+#define EXPECT_UNEXPECTED(Exp) EXPECT_ERROR(Err)
+
+#endif
diff --git a/unittests/DebugInfo/PDB/Makefile b/unittests/DebugInfo/PDB/Makefile
deleted file mode 100644
index eb118a3fd2bd..000000000000
--- a/unittests/DebugInfo/PDB/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-##===- unittests/DebugInfo/PDB/Makefile -------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-TESTNAME = DebugInfoPDB
-LINK_COMPONENTS := DebugInfoPDB support
-
-include $(LEVEL)/Makefile.config
-
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp
new file mode 100644
index 000000000000..6f9e86c4f262
--- /dev/null
+++ b/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp
@@ -0,0 +1,442 @@
+//===- llvm/unittest/DebugInfo/PDB/MappedBlockStreamTest.cpp --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ErrorChecking.h"
+
+#include "llvm/DebugInfo/CodeView/ByteStream.h"
+#include "llvm/DebugInfo/CodeView/StreamReader.h"
+#include "llvm/DebugInfo/CodeView/StreamRef.h"
+#include "llvm/DebugInfo/CodeView/StreamWriter.h"
+#include "llvm/DebugInfo/PDB/Raw/IPDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h"
+#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h"
+#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h"
+#include "gtest/gtest.h"
+
+#include <unordered_map>
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+namespace {
+
+static const uint32_t BlocksAry[] = {0, 1, 2, 5, 4, 3, 6, 7, 8, 9};
+static uint8_t DataAry[] = {'A', 'B', 'C', 'F', 'E', 'D', 'G', 'H', 'I', 'J'};
+
+class DiscontiguousFile : public IPDBFile {
+public:
+ DiscontiguousFile(ArrayRef<uint32_t> Blocks, MutableArrayRef<uint8_t> Data)
+ : Blocks(Blocks.begin(), Blocks.end()), Data(Data.begin(), Data.end()) {}
+
+ uint32_t getBlockSize() const override { return 1; }
+ uint32_t getBlockCount() const override { return Blocks.size(); }
+ uint32_t getNumStreams() const override { return 1; }
+ uint32_t getStreamByteSize(uint32_t StreamIndex) const override {
+ return getBlockCount() * getBlockSize();
+ }
+ ArrayRef<support::ulittle32_t>
+ getStreamBlockList(uint32_t StreamIndex) const override {
+ if (StreamIndex != 0)
+ return ArrayRef<support::ulittle32_t>();
+ return Blocks;
+ }
+ Expected<ArrayRef<uint8_t>> getBlockData(uint32_t BlockIndex,
+ uint32_t NumBytes) const override {
+ return ArrayRef<uint8_t>(&Data[BlockIndex], NumBytes);
+ }
+
+ Error setBlockData(uint32_t BlockIndex, uint32_t Offset,
+ ArrayRef<uint8_t> SrcData) const override {
+ if (BlockIndex >= Blocks.size())
+ return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
+ if (Offset > getBlockSize() - SrcData.size())
+ return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
+ ::memcpy(&Data[BlockIndex] + Offset, SrcData.data(), SrcData.size());
+ return Error::success();
+ }
+
+private:
+ std::vector<support::ulittle32_t> Blocks;
+ MutableArrayRef<uint8_t> Data;
+};
+
+class MappedBlockStreamImpl : public MappedBlockStream {
+public:
+ MappedBlockStreamImpl(std::unique_ptr<IPDBStreamData> Data,
+ const IPDBFile &File)
+ : MappedBlockStream(std::move(Data), File) {}
+};
+
+// Tests that a read which is entirely contained within a single block works
+// and does not allocate.
+TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StreamRef SR;
+ EXPECT_NO_ERROR(R.readStreamRef(SR, 0U));
+ ArrayRef<uint8_t> Buffer;
+ EXPECT_ERROR(SR.readBytes(0U, 1U, Buffer));
+ EXPECT_NO_ERROR(R.readStreamRef(SR, 1U));
+ EXPECT_ERROR(SR.readBytes(1U, 1U, Buffer));
+}
+
+// Tests that a read which outputs into a full destination buffer works and
+// does not fail due to the length of the output buffer.
+TEST(MappedBlockStreamTest, ReadOntoNonEmptyBuffer) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA";
+ EXPECT_NO_ERROR(R.readFixedString(Str, 1));
+ EXPECT_EQ(Str, StringRef("A"));
+ EXPECT_EQ(0U, S.getNumBytesCopied());
+}
+
+// Tests that a read which crosses a block boundary, but where the subsequent
+// blocks are still contiguous in memory to the previous block works and does
+// not allocate memory.
+TEST(MappedBlockStreamTest, ZeroCopyReadContiguousBreak) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str;
+ EXPECT_NO_ERROR(R.readFixedString(Str, 2));
+ EXPECT_EQ(Str, StringRef("AB"));
+ EXPECT_EQ(0U, S.getNumBytesCopied());
+
+ R.setOffset(6);
+ EXPECT_NO_ERROR(R.readFixedString(Str, 4));
+ EXPECT_EQ(Str, StringRef("GHIJ"));
+ EXPECT_EQ(0U, S.getNumBytesCopied());
+}
+
+// Tests that a read which crosses a block boundary and cannot be referenced
+// contiguously works and allocates only the precise amount of bytes
+// requested.
+TEST(MappedBlockStreamTest, CopyReadNonContiguousBreak) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str;
+ EXPECT_NO_ERROR(R.readFixedString(Str, 10));
+ EXPECT_EQ(Str, StringRef("ABCDEFGHIJ"));
+ EXPECT_EQ(10U, S.getNumBytesCopied());
+}
+
+// Test that an out of bounds read which doesn't cross a block boundary
+// fails and allocates no memory.
+TEST(MappedBlockStreamTest, InvalidReadSizeNoBreak) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str;
+
+ R.setOffset(10);
+ EXPECT_ERROR(R.readFixedString(Str, 1));
+ EXPECT_EQ(0U, S.getNumBytesCopied());
+}
+
+// Test that an out of bounds read which crosses a contiguous block boundary
+// fails and allocates no memory.
+TEST(MappedBlockStreamTest, InvalidReadSizeContiguousBreak) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str;
+
+ R.setOffset(6);
+ EXPECT_ERROR(R.readFixedString(Str, 5));
+ EXPECT_EQ(0U, S.getNumBytesCopied());
+}
+
+// Test that an out of bounds read which crosses a discontiguous block
+// boundary fails and allocates no memory.
+TEST(MappedBlockStreamTest, InvalidReadSizeNonContiguousBreak) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str;
+
+ EXPECT_ERROR(R.readFixedString(Str, 11));
+ EXPECT_EQ(0U, S.getNumBytesCopied());
+}
+
+// Tests that a read which is entirely contained within a single block but
+// beyond the end of a StreamRef fails.
+TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str;
+ EXPECT_NO_ERROR(R.readFixedString(Str, 1));
+ EXPECT_EQ(Str, StringRef("A"));
+ EXPECT_EQ(0U, S.getNumBytesCopied());
+}
+
+// Tests that a read which is not aligned on the same boundary as a previous
+// cached request, but which is known to overlap that request, shares the
+// previous allocation.
+TEST(MappedBlockStreamTest, UnalignedOverlappingRead) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str1;
+ StringRef Str2;
+ EXPECT_NO_ERROR(R.readFixedString(Str1, 7));
+ EXPECT_EQ(Str1, StringRef("ABCDEFG"));
+ EXPECT_EQ(7U, S.getNumBytesCopied());
+
+ R.setOffset(2);
+ EXPECT_NO_ERROR(R.readFixedString(Str2, 3));
+ EXPECT_EQ(Str2, StringRef("CDE"));
+ EXPECT_EQ(Str1.data() + 2, Str2.data());
+ EXPECT_EQ(7U, S.getNumBytesCopied());
+}
+
+// Tests that a read which is not aligned on the same boundary as a previous
+// cached request, but which only partially overlaps a previous cached request,
+// still works correctly and allocates again from the shared pool.
+TEST(MappedBlockStreamTest, UnalignedOverlappingReadFail) {
+ DiscontiguousFile F(BlocksAry, DataAry);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ StreamReader R(S);
+ StringRef Str1;
+ StringRef Str2;
+ EXPECT_NO_ERROR(R.readFixedString(Str1, 6));
+ EXPECT_EQ(Str1, StringRef("ABCDEF"));
+ EXPECT_EQ(6U, S.getNumBytesCopied());
+
+ R.setOffset(4);
+ EXPECT_NO_ERROR(R.readFixedString(Str2, 4));
+ EXPECT_EQ(Str2, StringRef("EFGH"));
+ EXPECT_EQ(10U, S.getNumBytesCopied());
+}
+
+TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) {
+ static uint8_t Data[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
+ static uint8_t LargeBuffer[] = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'A'};
+ static uint8_t SmallBuffer[] = {'0', '1', '2'};
+ static_assert(sizeof(LargeBuffer) > sizeof(Data),
+ "LargeBuffer is not big enough");
+
+ DiscontiguousFile F(BlocksAry, Data);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ ArrayRef<uint8_t> Buffer;
+
+ EXPECT_ERROR(S.writeBytes(0, ArrayRef<uint8_t>(LargeBuffer)));
+ EXPECT_NO_ERROR(S.writeBytes(0, ArrayRef<uint8_t>(SmallBuffer)));
+ EXPECT_NO_ERROR(S.writeBytes(7, ArrayRef<uint8_t>(SmallBuffer)));
+ EXPECT_ERROR(S.writeBytes(8, ArrayRef<uint8_t>(SmallBuffer)));
+}
+
+TEST(MappedBlockStreamTest, TestWriteBytesNoBreakBoundary) {
+ static uint8_t Data[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
+ DiscontiguousFile F(BlocksAry, Data);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ ArrayRef<uint8_t> Buffer;
+
+ EXPECT_NO_ERROR(S.readBytes(0, 1, Buffer));
+ EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A'));
+ EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer));
+ EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J'));
+
+ EXPECT_NO_ERROR(S.writeBytes(0, ArrayRef<uint8_t>('J')));
+ EXPECT_NO_ERROR(S.writeBytes(9, ArrayRef<uint8_t>('A')));
+
+ EXPECT_NO_ERROR(S.readBytes(0, 1, Buffer));
+ EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J'));
+ EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer));
+ EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A'));
+
+ EXPECT_NO_ERROR(S.writeBytes(0, ArrayRef<uint8_t>('A')));
+ EXPECT_NO_ERROR(S.writeBytes(9, ArrayRef<uint8_t>('J')));
+
+ EXPECT_NO_ERROR(S.readBytes(0, 1, Buffer));
+ EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A'));
+ EXPECT_NO_ERROR(S.readBytes(9, 1, Buffer));
+ EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J'));
+}
+
+TEST(MappedBlockStreamTest, TestWriteBytesBreakBoundary) {
+ static uint8_t Data[] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
+ static uint8_t TestData[] = {'T', 'E', 'S', 'T', 'I', 'N', 'G', '.'};
+ static uint8_t Expected[] = {'T', 'E', 'S', 'N', 'I',
+ 'T', 'G', '.', '0', '0'};
+
+ DiscontiguousFile F(BlocksAry, Data);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+ ArrayRef<uint8_t> Buffer;
+
+ EXPECT_NO_ERROR(S.writeBytes(0, TestData));
+ // First just compare the memory, then compare the result of reading the
+ // string out.
+ EXPECT_EQ(ArrayRef<uint8_t>(Data), ArrayRef<uint8_t>(Expected));
+
+ EXPECT_NO_ERROR(S.readBytes(0, 8, Buffer));
+ EXPECT_EQ(Buffer, ArrayRef<uint8_t>(TestData));
+}
+
+TEST(MappedBlockStreamTest, TestWriteThenRead) {
+ std::vector<uint8_t> DataBytes(10);
+ MutableArrayRef<uint8_t> Data(DataBytes);
+ const uint32_t Blocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};
+
+ DiscontiguousFile F(Blocks, Data);
+ MappedBlockStreamImpl S(llvm::make_unique<IndexedStreamData>(0, F), F);
+
+ enum class MyEnum : uint32_t { Val1 = 2908234, Val2 = 120891234 };
+ using support::ulittle32_t;
+
+ uint16_t u16[] = {31468, 0};
+ uint32_t u32[] = {890723408, 0};
+ MyEnum Enum[] = {MyEnum::Val1, MyEnum::Val2};
+ StringRef ZStr[] = {"Zero Str", ""};
+ StringRef FStr[] = {"Fixed Str", ""};
+ uint8_t byteArray0[] = {'1', '2'};
+ uint8_t byteArray1[] = {'0', '0'};
+ ArrayRef<uint8_t> byteArrayRef0(byteArray0);
+ ArrayRef<uint8_t> byteArrayRef1(byteArray1);
+ ArrayRef<uint8_t> byteArray[] = { byteArrayRef0, byteArrayRef1 };
+ ArrayRef<uint32_t> intArray[] = {{890723408, 29082234}, {0, 0}};
+
+ StreamReader Reader(S);
+ StreamWriter Writer(S);
+ EXPECT_NO_ERROR(Writer.writeInteger(u16[0]));
+ EXPECT_NO_ERROR(Reader.readInteger(u16[1]));
+ EXPECT_EQ(u16[0], u16[1]);
+ EXPECT_EQ(std::vector<uint8_t>({0, 0x7A, 0xEC, 0, 0, 0, 0, 0, 0, 0}),
+ DataBytes);
+
+ Reader.setOffset(0);
+ Writer.setOffset(0);
+ ::memset(DataBytes.data(), 0, 10);
+ EXPECT_NO_ERROR(Writer.writeInteger(u32[0]));
+ EXPECT_NO_ERROR(Reader.readInteger(u32[1]));
+ EXPECT_EQ(u32[0], u32[1]);
+ EXPECT_EQ(std::vector<uint8_t>({0x17, 0x5C, 0x50, 0, 0, 0, 0x35, 0, 0, 0}),
+ DataBytes);
+
+ Reader.setOffset(0);
+ Writer.setOffset(0);
+ ::memset(DataBytes.data(), 0, 10);
+ EXPECT_NO_ERROR(Writer.writeEnum(Enum[0]));
+ EXPECT_NO_ERROR(Reader.readEnum(Enum[1]));
+ EXPECT_EQ(Enum[0], Enum[1]);
+ EXPECT_EQ(std::vector<uint8_t>({0x2C, 0x60, 0x4A, 0, 0, 0, 0, 0, 0, 0}),
+ DataBytes);
+
+ Reader.setOffset(0);
+ Writer.setOffset(0);
+ ::memset(DataBytes.data(), 0, 10);
+ EXPECT_NO_ERROR(Writer.writeZeroString(ZStr[0]));
+ EXPECT_NO_ERROR(Reader.readZeroString(ZStr[1]));
+ EXPECT_EQ(ZStr[0], ZStr[1]);
+ EXPECT_EQ(
+ std::vector<uint8_t>({'r', 'e', 'Z', ' ', 'S', 't', 'o', 'r', 0, 0}),
+ DataBytes);
+
+ Reader.setOffset(0);
+ Writer.setOffset(0);
+ ::memset(DataBytes.data(), 0, 10);
+ EXPECT_NO_ERROR(Writer.writeFixedString(FStr[0]));
+ EXPECT_NO_ERROR(Reader.readFixedString(FStr[1], FStr[0].size()));
+ EXPECT_EQ(FStr[0], FStr[1]);
+ EXPECT_EQ(
+ std::vector<uint8_t>({'x', 'i', 'F', 'd', ' ', 'S', 'e', 't', 0, 'r'}),
+ DataBytes);
+
+ Reader.setOffset(0);
+ Writer.setOffset(0);
+ ::memset(DataBytes.data(), 0, 10);
+ EXPECT_NO_ERROR(Writer.writeArray(byteArray[0]));
+ EXPECT_NO_ERROR(Reader.readArray(byteArray[1], byteArray[0].size()));
+ EXPECT_EQ(byteArray[0], byteArray[1]);
+ EXPECT_EQ(std::vector<uint8_t>({0, 0x32, 0x31, 0, 0, 0, 0, 0, 0, 0}),
+ DataBytes);
+
+ Reader.setOffset(0);
+ Writer.setOffset(0);
+ ::memset(DataBytes.data(), 0, 10);
+ EXPECT_NO_ERROR(Writer.writeArray(intArray[0]));
+ EXPECT_NO_ERROR(Reader.readArray(intArray[1], intArray[0].size()));
+ EXPECT_EQ(intArray[0], intArray[1]);
+}
+
+TEST(MappedBlockStreamTest, TestWriteContiguousStreamRef) {
+ std::vector<uint8_t> DestDataBytes(10);
+ MutableArrayRef<uint8_t> DestData(DestDataBytes);
+ const uint32_t DestBlocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};
+
+ std::vector<uint8_t> SrcDataBytes(10);
+ MutableArrayRef<uint8_t> SrcData(SrcDataBytes);
+
+ DiscontiguousFile F(DestBlocks, DestData);
+ MappedBlockStreamImpl DestStream(llvm::make_unique<IndexedStreamData>(0, F),
+ F);
+
+ // First write "Test Str" into the source stream.
+ ByteStream<true> SourceStream(SrcData);
+ StreamWriter SourceWriter(SourceStream);
+ EXPECT_NO_ERROR(SourceWriter.writeZeroString("Test Str"));
+ EXPECT_EQ(SrcDataBytes, std::vector<uint8_t>(
+ {'T', 'e', 's', 't', ' ', 'S', 't', 'r', 0, 0}));
+
+ // Then write the source stream into the dest stream.
+ StreamWriter DestWriter(DestStream);
+ EXPECT_NO_ERROR(DestWriter.writeStreamRef(SourceStream));
+ EXPECT_EQ(DestDataBytes, std::vector<uint8_t>(
+ {'s', 'e', 'T', ' ', 'S', 't', 't', 'r', 0, 0}));
+
+ // Then read the string back out of the dest stream.
+ StringRef Result;
+ StreamReader DestReader(DestStream);
+ EXPECT_NO_ERROR(DestReader.readZeroString(Result));
+ EXPECT_EQ(Result, "Test Str");
+}
+
+TEST(MappedBlockStreamTest, TestWriteDiscontiguousStreamRef) {
+ std::vector<uint8_t> DestDataBytes(10);
+ MutableArrayRef<uint8_t> DestData(DestDataBytes);
+ const uint32_t DestBlocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};
+
+ std::vector<uint8_t> SrcDataBytes(10);
+ MutableArrayRef<uint8_t> SrcData(SrcDataBytes);
+ const uint32_t SrcBlocks[] = {1, 0, 6, 3, 4, 5, 2, 7, 8, 9};
+
+ DiscontiguousFile DestFile(DestBlocks, DestData);
+ DiscontiguousFile SrcFile(SrcBlocks, SrcData);
+
+ MappedBlockStreamImpl DestStream(
+ llvm::make_unique<IndexedStreamData>(0, DestFile), DestFile);
+ MappedBlockStreamImpl SrcStream(
+ llvm::make_unique<IndexedStreamData>(0, SrcFile), SrcFile);
+
+ // First write "Test Str" into the source stream.
+ StreamWriter SourceWriter(SrcStream);
+ EXPECT_NO_ERROR(SourceWriter.writeZeroString("Test Str"));
+ EXPECT_EQ(SrcDataBytes, std::vector<uint8_t>(
+ {'e', 'T', 't', 't', ' ', 'S', 's', 'r', 0, 0}));
+
+ // Then write the source stream into the dest stream.
+ StreamWriter DestWriter(DestStream);
+ EXPECT_NO_ERROR(DestWriter.writeStreamRef(SrcStream));
+ EXPECT_EQ(DestDataBytes, std::vector<uint8_t>(
+ {'s', 'e', 'T', ' ', 'S', 't', 't', 'r', 0, 0}));
+
+ // Then read the string back out of the dest stream.
+ StringRef Result;
+ StreamReader DestReader(DestStream);
+ EXPECT_NO_ERROR(DestReader.readZeroString(Result));
+ EXPECT_EQ(Result, "Test Str");
+}
+
+} // end anonymous namespace
diff --git a/unittests/DebugInfo/PDB/MsfBuilderTest.cpp b/unittests/DebugInfo/PDB/MsfBuilderTest.cpp
new file mode 100644
index 000000000000..ac292a73a8fb
--- /dev/null
+++ b/unittests/DebugInfo/PDB/MsfBuilderTest.cpp
@@ -0,0 +1,360 @@
+//===- MsfBuilderTest.cpp Tests manipulation of MSF stream metadata ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ErrorChecking.h"
+
+#include "llvm/DebugInfo/PDB/Raw/MsfBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/MsfCommon.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+using namespace llvm::pdb::msf;
+
+namespace {
+class MsfBuilderTest : public testing::Test {
+protected:
+ void initializeSimpleSuperBlock(msf::SuperBlock &SB) {
+ initializeSuperBlock(SB);
+ SB.NumBlocks = 1000;
+ SB.NumDirectoryBytes = 8192;
+ }
+
+ void initializeSuperBlock(msf::SuperBlock &SB) {
+ ::memset(&SB, 0, sizeof(SB));
+
+ ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic));
+ SB.BlockMapAddr = 1;
+ SB.BlockSize = 4096;
+ SB.NumDirectoryBytes = 0;
+ SB.NumBlocks = 2; // one for the Super Block, one for the directory
+ }
+
+ BumpPtrAllocator Allocator;
+};
+}
+
+TEST_F(MsfBuilderTest, ValidateSuperBlockAccept) {
+ // Test that a known good super block passes validation.
+ SuperBlock SB;
+ initializeSuperBlock(SB);
+
+ EXPECT_NO_ERROR(msf::validateSuperBlock(SB));
+}
+
+TEST_F(MsfBuilderTest, ValidateSuperBlockReject) {
+ // Test that various known problems cause a super block to be rejected.
+ SuperBlock SB;
+ initializeSimpleSuperBlock(SB);
+
+ // Mismatched magic
+ SB.MagicBytes[0] = 8;
+ EXPECT_ERROR(msf::validateSuperBlock(SB));
+ initializeSimpleSuperBlock(SB);
+
+ // Block 0 is reserved for super block, can't be occupied by the block map
+ SB.BlockMapAddr = 0;
+ EXPECT_ERROR(msf::validateSuperBlock(SB));
+ initializeSimpleSuperBlock(SB);
+
+ // Block sizes have to be powers of 2.
+ SB.BlockSize = 3120;
+ EXPECT_ERROR(msf::validateSuperBlock(SB));
+ initializeSimpleSuperBlock(SB);
+
+ // The directory itself has a maximum size.
+ SB.NumDirectoryBytes = SB.BlockSize * SB.BlockSize / 4;
+ EXPECT_NO_ERROR(msf::validateSuperBlock(SB));
+ SB.NumDirectoryBytes = SB.NumDirectoryBytes + 4;
+ EXPECT_ERROR(msf::validateSuperBlock(SB));
+}
+
+TEST_F(MsfBuilderTest, TestUsedBlocksMarkedAsUsed) {
+ // Test that when assigning a stream to a known list of blocks, the blocks
+ // are correctly marked as used after adding, but no other incorrect blocks
+ // are accidentally marked as used.
+
+ std::vector<uint32_t> Blocks = {4, 5, 6, 7, 8, 9, 10, 11, 12};
+ // Allocate some extra blocks at the end so we can verify that they're free
+ // after the initialization.
+ uint32_t NumBlocks = msf::getMinimumBlockCount() + Blocks.size() + 10;
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096, NumBlocks);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ EXPECT_NO_ERROR(Msf.addStream(Blocks.size() * 4096, Blocks));
+
+ for (auto B : Blocks) {
+ EXPECT_FALSE(Msf.isBlockFree(B));
+ }
+
+ uint32_t FreeBlockStart = Blocks.back() + 1;
+ for (uint32_t I = FreeBlockStart; I < NumBlocks; ++I) {
+ EXPECT_TRUE(Msf.isBlockFree(I));
+ }
+}
+
+TEST_F(MsfBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {
+ // Test that adding a new stream correctly updates the directory. This only
+ // tests the case where the directory *DOES NOT* grow large enough that it
+ // crosses a Block boundary.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ auto ExpectedL1 = Msf.build();
+ EXPECT_EXPECTED(ExpectedL1);
+ Layout &L1 = *ExpectedL1;
+
+ auto OldDirBlocks = L1.DirectoryBlocks;
+ EXPECT_EQ(1U, OldDirBlocks.size());
+
+ auto ExpectedMsf2 = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf2);
+ auto &Msf2 = *ExpectedMsf2;
+
+ EXPECT_NO_ERROR(Msf2.addStream(4000));
+ EXPECT_EQ(1U, Msf2.getNumStreams());
+ EXPECT_EQ(4000U, Msf2.getStreamSize(0));
+ auto Blocks = Msf2.getStreamBlocks(0);
+ EXPECT_EQ(1U, Blocks.size());
+
+ auto ExpectedL2 = Msf2.build();
+ EXPECT_EXPECTED(ExpectedL2);
+ Layout &L2 = *ExpectedL2;
+ auto NewDirBlocks = L2.DirectoryBlocks;
+ EXPECT_EQ(1U, NewDirBlocks.size());
+}
+
+TEST_F(MsfBuilderTest, TestAddStreamWithDirectoryBlockIncrease) {
+ // Test that adding a new stream correctly updates the directory. This only
+ // tests the case where the directory *DOES* grow large enough that it
+ // crosses a Block boundary. This is because the newly added stream occupies
+ // so many Blocks that need to be indexed in the directory that the directory
+ // crosses a Block boundary.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ EXPECT_NO_ERROR(Msf.addStream(4096 * 4096 / sizeof(uint32_t)));
+
+ auto ExpectedL1 = Msf.build();
+ EXPECT_EXPECTED(ExpectedL1);
+ Layout &L1 = *ExpectedL1;
+ auto DirBlocks = L1.DirectoryBlocks;
+ EXPECT_EQ(2U, DirBlocks.size());
+}
+
+TEST_F(MsfBuilderTest, TestGrowStreamNoBlockIncrease) {
+ // Test growing an existing stream by a value that does not affect the number
+ // of blocks it occupies.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ EXPECT_NO_ERROR(Msf.addStream(1024));
+ EXPECT_EQ(1024U, Msf.getStreamSize(0));
+ auto OldStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(1U, OldStreamBlocks.size());
+
+ EXPECT_NO_ERROR(Msf.setStreamSize(0, 2048));
+ EXPECT_EQ(2048U, Msf.getStreamSize(0));
+ auto NewStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(1U, NewStreamBlocks.size());
+
+ EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);
+}
+
+TEST_F(MsfBuilderTest, TestGrowStreamWithBlockIncrease) {
+ // Test that growing an existing stream to a value large enough that it causes
+ // the need to allocate new Blocks to the stream correctly updates the
+ // stream's
+ // block list.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ EXPECT_NO_ERROR(Msf.addStream(2048));
+ EXPECT_EQ(2048U, Msf.getStreamSize(0));
+ std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(1U, OldStreamBlocks.size());
+
+ EXPECT_NO_ERROR(Msf.setStreamSize(0, 6144));
+ EXPECT_EQ(6144U, Msf.getStreamSize(0));
+ std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(2U, NewStreamBlocks.size());
+
+ EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);
+ EXPECT_NE(NewStreamBlocks[0], NewStreamBlocks[1]);
+}
+
+TEST_F(MsfBuilderTest, TestShrinkStreamNoBlockDecrease) {
+ // Test that shrinking an existing stream by a value that does not affect the
+ // number of Blocks it occupies makes no changes to stream's block list.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ EXPECT_NO_ERROR(Msf.addStream(2048));
+ EXPECT_EQ(2048U, Msf.getStreamSize(0));
+ std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(1U, OldStreamBlocks.size());
+
+ EXPECT_NO_ERROR(Msf.setStreamSize(0, 1024));
+ EXPECT_EQ(1024U, Msf.getStreamSize(0));
+ std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(1U, NewStreamBlocks.size());
+
+ EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);
+}
+
+TEST_F(MsfBuilderTest, TestShrinkStreamWithBlockDecrease) {
+ // Test that shrinking an existing stream to a value large enough that it
+ // causes the need to deallocate new Blocks to the stream correctly updates
+ // the stream's block list.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ EXPECT_NO_ERROR(Msf.addStream(6144));
+ EXPECT_EQ(6144U, Msf.getStreamSize(0));
+ std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(2U, OldStreamBlocks.size());
+
+ EXPECT_NO_ERROR(Msf.setStreamSize(0, 2048));
+ EXPECT_EQ(2048U, Msf.getStreamSize(0));
+ std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);
+ EXPECT_EQ(1U, NewStreamBlocks.size());
+
+ EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);
+}
+
+TEST_F(MsfBuilderTest, TestRejectReusedStreamBlock) {
+ // Test that attempting to add a stream and assigning a block that is already
+ // in use by another stream fails.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ EXPECT_NO_ERROR(Msf.addStream(6144));
+
+ std::vector<uint32_t> Blocks = {2, 3};
+ EXPECT_ERROR(Msf.addStream(6144, Blocks));
+}
+
+TEST_F(MsfBuilderTest, TestBlockCountsWhenAddingStreams) {
+ // Test that when adding multiple streams, the number of used and free Blocks
+ // allocated to the MSF file are as expected.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ // one for the super block, one for the directory block map
+ uint32_t NumUsedBlocks = Msf.getNumUsedBlocks();
+ EXPECT_EQ(msf::getMinimumBlockCount(), NumUsedBlocks);
+ EXPECT_EQ(0U, Msf.getNumFreeBlocks());
+
+ const uint32_t StreamSizes[] = {4000, 6193, 189723};
+ for (int I = 0; I < 3; ++I) {
+ EXPECT_NO_ERROR(Msf.addStream(StreamSizes[I]));
+ NumUsedBlocks += bytesToBlocks(StreamSizes[I], 4096);
+ EXPECT_EQ(NumUsedBlocks, Msf.getNumUsedBlocks());
+ EXPECT_EQ(0U, Msf.getNumFreeBlocks());
+ }
+}
+
+TEST_F(MsfBuilderTest, BuildMsfLayout) {
+ // Test that we can generate an Msf Layout structure from a valid layout
+ // specification.
+ auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ const uint32_t StreamSizes[] = {4000, 6193, 189723};
+ uint32_t ExpectedNumBlocks = msf::getMinimumBlockCount();
+ for (int I = 0; I < 3; ++I) {
+ EXPECT_NO_ERROR(Msf.addStream(StreamSizes[I]));
+ ExpectedNumBlocks += bytesToBlocks(StreamSizes[I], 4096);
+ }
+ ++ExpectedNumBlocks; // The directory itself should use 1 block
+
+ auto ExpectedLayout = Msf.build();
+ EXPECT_EXPECTED(ExpectedLayout);
+ Layout &L = *ExpectedLayout;
+ EXPECT_EQ(4096U, L.SB->BlockSize);
+ EXPECT_EQ(ExpectedNumBlocks, L.SB->NumBlocks);
+
+ EXPECT_EQ(1U, L.DirectoryBlocks.size());
+
+ EXPECT_EQ(3U, L.StreamMap.size());
+ EXPECT_EQ(3U, L.StreamSizes.size());
+ for (int I = 0; I < 3; ++I) {
+ EXPECT_EQ(StreamSizes[I], L.StreamSizes[I]);
+ uint32_t ExpectedNumBlocks = bytesToBlocks(StreamSizes[I], 4096);
+ EXPECT_EQ(ExpectedNumBlocks, L.StreamMap[I].size());
+ }
+}
+
+TEST_F(MsfBuilderTest, UseDirectoryBlockHint) {
+ Expected<MsfBuilder> ExpectedMsf = MsfBuilder::create(
+ Allocator, 4096, msf::getMinimumBlockCount() + 1, false);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ uint32_t B = msf::getFirstUnreservedBlock();
+ EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({B + 1}));
+ EXPECT_NO_ERROR(Msf.addStream(2048, {B + 2}));
+
+ auto ExpectedLayout = Msf.build();
+ EXPECT_EXPECTED(ExpectedLayout);
+ Layout &L = *ExpectedLayout;
+ EXPECT_EQ(msf::getMinimumBlockCount() + 2, L.SB->NumBlocks);
+ EXPECT_EQ(1U, L.DirectoryBlocks.size());
+ EXPECT_EQ(1U, L.StreamMap[0].size());
+
+ EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);
+ EXPECT_EQ(B + 2, L.StreamMap[0].front());
+}
+
+TEST_F(MsfBuilderTest, DirectoryBlockHintInsufficient) {
+ Expected<MsfBuilder> ExpectedMsf =
+ MsfBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+ uint32_t B = msf::getFirstUnreservedBlock();
+ EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({B + 1}));
+
+ uint32_t Size = 4096 * 4096 / 4;
+ EXPECT_NO_ERROR(Msf.addStream(Size));
+
+ auto ExpectedLayout = Msf.build();
+ EXPECT_EXPECTED(ExpectedLayout);
+ Layout &L = *ExpectedLayout;
+ EXPECT_EQ(2U, L.DirectoryBlocks.size());
+ EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);
+}
+
+TEST_F(MsfBuilderTest, DirectoryBlockHintOverestimated) {
+ Expected<MsfBuilder> ExpectedMsf =
+ MsfBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2);
+ EXPECT_EXPECTED(ExpectedMsf);
+ auto &Msf = *ExpectedMsf;
+
+ uint32_t B = msf::getFirstUnreservedBlock();
+ EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({B + 1, B + 2}));
+
+ EXPECT_NO_ERROR(Msf.addStream(2048));
+
+ auto ExpectedLayout = Msf.build();
+ EXPECT_EXPECTED(ExpectedLayout);
+ Layout &L = *ExpectedLayout;
+ EXPECT_EQ(1U, L.DirectoryBlocks.size());
+ EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);
+}
diff --git a/unittests/DebugInfo/PDB/PDBApiTest.cpp b/unittests/DebugInfo/PDB/PDBApiTest.cpp
index ebd3d7bb6b3c..cd0f928a08ab 100644
--- a/unittests/DebugInfo/PDB/PDBApiTest.cpp
+++ b/unittests/DebugInfo/PDB/PDBApiTest.cpp
@@ -50,6 +50,7 @@
#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include "gtest/gtest.h"
using namespace llvm;
+using namespace llvm::pdb;
namespace {
@@ -78,9 +79,35 @@ class MockSession : public IPDBSession {
return nullptr;
}
std::unique_ptr<IPDBEnumLineNumbers>
+ findLineNumbers(const PDBSymbolCompiland &Compiland,
+ const IPDBSourceFile &File) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumLineNumbers>
findLineNumbersByAddress(uint64_t Address, uint32_t Length) const override {
return nullptr;
}
+ std::unique_ptr<IPDBEnumSourceFiles>
+ findSourceFiles(const PDBSymbolCompiland *Compiland, llvm::StringRef Pattern,
+ PDB_NameSearchFlags Flags) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBSourceFile>
+ findOneSourceFile(const PDBSymbolCompiland *Compiland,
+ llvm::StringRef Pattern,
+ PDB_NameSearchFlags Flags) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>>
+ findCompilandsForSourceFile(llvm::StringRef Pattern,
+ PDB_NameSearchFlags Flags) const override {
+ return nullptr;
+ }
+ std::unique_ptr<PDBSymbolCompiland>
+ findOneCompilandForSourceFile(llvm::StringRef Pattern,
+ PDB_NameSearchFlags Flags) const override {
+ return nullptr;
+ }
std::unique_ptr<IPDBEnumSourceFiles> getAllSourceFiles() const override {
return nullptr;
diff --git a/unittests/ExecutionEngine/ExecutionEngineTest.cpp b/unittests/ExecutionEngine/ExecutionEngineTest.cpp
index bb47c4c00304..7cad84130692 100644
--- a/unittests/ExecutionEngine/ExecutionEngineTest.cpp
+++ b/unittests/ExecutionEngine/ExecutionEngineTest.cpp
@@ -28,7 +28,7 @@ private:
protected:
ExecutionEngineTest() {
- auto Owner = make_unique<Module>("<main>", getGlobalContext());
+ auto Owner = make_unique<Module>("<main>", Context);
M = Owner.get();
Engine.reset(EngineBuilder(std::move(Owner)).setErrorStr(&Error).create());
}
@@ -44,13 +44,13 @@ protected:
}
std::string Error;
+ LLVMContext Context;
Module *M; // Owned by ExecutionEngine.
std::unique_ptr<ExecutionEngine> Engine;
};
TEST_F(ExecutionEngineTest, ForwardGlobalMapping) {
- GlobalVariable *G1 =
- NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
+ GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
int32_t Mem1 = 3;
Engine->addGlobalMapping(G1, &Mem1);
EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G1));
@@ -63,8 +63,7 @@ TEST_F(ExecutionEngineTest, ForwardGlobalMapping) {
Engine->updateGlobalMapping(G1, &Mem2);
EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1));
- GlobalVariable *G2 =
- NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
+ GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G2))
<< "The NULL return shouldn't depend on having called"
<< " updateGlobalMapping(..., NULL)";
@@ -76,8 +75,7 @@ TEST_F(ExecutionEngineTest, ForwardGlobalMapping) {
}
TEST_F(ExecutionEngineTest, ReverseGlobalMapping) {
- GlobalVariable *G1 =
- NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
+ GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
int32_t Mem1 = 3;
Engine->addGlobalMapping(G1, &Mem1);
@@ -87,8 +85,7 @@ TEST_F(ExecutionEngineTest, ReverseGlobalMapping) {
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));
- GlobalVariable *G2 =
- NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global2");
+ GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2");
Engine->updateGlobalMapping(G2, &Mem1);
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1));
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));
@@ -104,8 +101,7 @@ TEST_F(ExecutionEngineTest, ReverseGlobalMapping) {
}
TEST_F(ExecutionEngineTest, ClearModuleMappings) {
- GlobalVariable *G1 =
- NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
+ GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
int32_t Mem1 = 3;
Engine->addGlobalMapping(G1, &Mem1);
@@ -115,8 +111,7 @@ TEST_F(ExecutionEngineTest, ClearModuleMappings) {
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
- GlobalVariable *G2 =
- NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global2");
+ GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2");
// After clearing the module mappings, we can assign a new GV to the
// same address.
Engine->addGlobalMapping(G2, &Mem1);
@@ -124,8 +119,7 @@ TEST_F(ExecutionEngineTest, ClearModuleMappings) {
}
TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) {
- GlobalVariable *G1 =
- NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
+ GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
int32_t Mem1 = 3;
Engine->addGlobalMapping(G1, &Mem1);
// Make sure the reverse mapping is enabled.
@@ -136,35 +130,23 @@ TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) {
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
}
-TEST_F(ExecutionEngineTest, LookupWithMangledName) {
- int x;
- llvm::sys::DynamicLibrary::AddSymbol("x", &x);
-
- // Demonstrate that getSymbolAddress accepts mangled names and always strips
- // the leading underscore.
- EXPECT_EQ(reinterpret_cast<uint64_t>(&x),
- RTDyldMemoryManager::getSymbolAddressInProcess("_x"));
-}
-
TEST_F(ExecutionEngineTest, LookupWithMangledAndDemangledSymbol) {
int x;
int _x;
llvm::sys::DynamicLibrary::AddSymbol("x", &x);
llvm::sys::DynamicLibrary::AddSymbol("_x", &_x);
- // Lookup the demangled name first, even if there's a demangled symbol that
- // matches the input already.
+ // RTDyldMemoryManager::getSymbolAddressInProcess expects a mangled symbol,
+ // but DynamicLibrary is a wrapper for dlsym, which expects the unmangled C
+ // symbol name. This test verifies that getSymbolAddressInProcess strips the
+ // leading '_' on Darwin, but not on other platforms.
+#ifdef __APPLE__
EXPECT_EQ(reinterpret_cast<uint64_t>(&x),
RTDyldMemoryManager::getSymbolAddressInProcess("_x"));
-}
-
-TEST_F(ExecutionEngineTest, LookupwithDemangledName) {
- int _x;
- llvm::sys::DynamicLibrary::AddSymbol("_x", &_x);
-
- // But do fallback to looking up a demangled name if there's no ambiguity
+#else
EXPECT_EQ(reinterpret_cast<uint64_t>(&_x),
RTDyldMemoryManager::getSymbolAddressInProcess("_x"));
+#endif
}
}
diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
index c8c244d22ed1..cf63da3a22a5 100644
--- a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
+++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
@@ -285,7 +285,6 @@ protected:
void buildAndRunPasses() {
LLVMPassManagerRef pass = LLVMCreatePassManager();
- LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), pass);
LLVMAddConstantPropagationPass(pass);
LLVMAddInstructionCombiningPass(pass);
LLVMRunPassManager(pass, Module);
@@ -304,8 +303,6 @@ protected:
LLVMPassManagerRef modulePasses =
LLVMCreatePassManager();
- LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), modulePasses);
-
LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder,
functionPasses);
LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses);
diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
index 609ac844c47d..e14201c2d783 100644
--- a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
+++ b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
@@ -282,7 +282,6 @@ protected:
MCJITTestBase()
: TrivialModuleBuilder(HostTriple)
, OptLevel(CodeGenOpt::None)
- , RelocModel(Reloc::Default)
, CodeModel(CodeModel::Default)
, MArch("")
, MM(new SectionMemoryManager)
@@ -322,7 +321,6 @@ protected:
.setErrorStr(&Error)
.setOptLevel(CodeGenOpt::None)
.setCodeModel(CodeModel::JITDefault)
- .setRelocationModel(Reloc::Default)
.setMArch(MArch)
.setMCPU(sys::getHostCPUName())
//.setMAttrs(MAttrs)
@@ -332,7 +330,6 @@ protected:
}
CodeGenOpt::Level OptLevel;
- Reloc::Model RelocModel;
CodeModel::Model CodeModel;
StringRef MArch;
SmallVector<std::string, 1> MAttrs;
diff --git a/unittests/ExecutionEngine/MCJIT/Makefile b/unittests/ExecutionEngine/MCJIT/Makefile
deleted file mode 100644
index 2822b20cdda2..000000000000
--- a/unittests/ExecutionEngine/MCJIT/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-##===- unittests/ExecutionEngine/MCJIT/Makefile ------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-TESTNAME = MCJIT
-LINK_COMPONENTS := core ipo mcjit native support
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
-
-# Permit these tests to use the MCJIT's symbolic lookup.
-LD.Flags += $(RDYNAMIC)
diff --git a/unittests/ExecutionEngine/Makefile b/unittests/ExecutionEngine/Makefile
deleted file mode 100644
index c19f8d688d1b..000000000000
--- a/unittests/ExecutionEngine/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-##===- unittests/ExecutionEngine/Makefile ------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = ExecutionEngine
-LINK_COMPONENTS :=interpreter
-
-include $(LEVEL)/Makefile.config
-
-PARALLEL_DIRS = Orc
-
-ifeq ($(TARGET_HAS_JIT),1)
- PARALLEL_DIRS += MCJIT
-endif
-
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/ExecutionEngine/Orc/CMakeLists.txt b/unittests/ExecutionEngine/Orc/CMakeLists.txt
index 41fef24556b1..68f6d0c28d7c 100644
--- a/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -20,3 +20,5 @@ add_llvm_unittest(OrcJITTests
OrcTestCommon.cpp
RPCUtilsTest.cpp
)
+
+target_link_libraries(OrcJITTests ${PTHREAD_LIB})
diff --git a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp
index a27e649b616f..8140a1ff2493 100644
--- a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp
@@ -18,19 +18,20 @@ namespace {
class DummyCallbackManager : public orc::JITCompileCallbackManager {
public:
- DummyCallbackManager() : JITCompileCallbackManager(0) { }
+ DummyCallbackManager() : JITCompileCallbackManager(0) {}
+
public:
void grow() override { llvm_unreachable("not implemented"); }
};
class DummyStubsManager : public orc::IndirectStubsManager {
public:
- std::error_code createStub(StringRef StubName, TargetAddress InitAddr,
- JITSymbolFlags Flags) override {
+ Error createStub(StringRef StubName, TargetAddress InitAddr,
+ JITSymbolFlags Flags) override {
llvm_unreachable("Not implemented");
}
- std::error_code createStubs(const StubInitsMap &StubInits) override {
+ Error createStubs(const StubInitsMap &StubInits) override {
llvm_unreachable("Not implemented");
}
@@ -42,22 +43,20 @@ public:
llvm_unreachable("Not implemented");
}
- std::error_code updatePointer(StringRef Name,
- TargetAddress NewAddr) override {
+ Error updatePointer(StringRef Name, TargetAddress NewAddr) override {
llvm_unreachable("Not implemented");
}
};
TEST(CompileOnDemandLayerTest, FindSymbol) {
- auto MockBaseLayer =
- createMockBaseLayer<int>(DoNothingAndReturn<int>(0),
- DoNothingAndReturn<void>(),
- [](const std::string &Name, bool) {
- if (Name == "foo")
- return JITSymbol(1, JITSymbolFlags::Exported);
- return JITSymbol(nullptr);
- },
- DoNothingAndReturn<JITSymbol>(nullptr));
+ auto MockBaseLayer = createMockBaseLayer<int>(
+ DoNothingAndReturn<int>(0), DoNothingAndReturn<void>(),
+ [](const std::string &Name, bool) {
+ if (Name == "foo")
+ return JITSymbol(1, JITSymbolFlags::Exported);
+ return JITSymbol(nullptr);
+ },
+ DoNothingAndReturn<JITSymbol>(nullptr));
typedef decltype(MockBaseLayer) MockBaseLayerT;
DummyCallbackManager CallbackMgr;
@@ -68,8 +67,7 @@ TEST(CompileOnDemandLayerTest, FindSymbol) {
auto Sym = COD.findSymbol("foo", true);
- EXPECT_TRUE(!!Sym)
- << "CompileOnDemand::findSymbol should call findSymbol in the base layer.";
+ EXPECT_TRUE(!!Sym) << "CompileOnDemand::findSymbol should call findSymbol in "
+ "the base layer.";
}
-
}
diff --git a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp
index 38b60ea7fcd4..ac847039d9fb 100644
--- a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp
+++ b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp
@@ -17,7 +17,8 @@ using namespace llvm;
namespace {
TEST(IndirectionUtilsTest, MakeStub) {
- ModuleBuilder MB(getGlobalContext(), "x86_64-apple-macosx10.10", "");
+ LLVMContext Context;
+ ModuleBuilder MB(Context, "x86_64-apple-macosx10.10", "");
Function *F = MB.createFunctionDecl<void(DummyStruct, DummyStruct)>("");
SmallVector<AttributeSet, 4> Attrs;
Attrs.push_back(
diff --git a/unittests/ExecutionEngine/Orc/Makefile b/unittests/ExecutionEngine/Orc/Makefile
deleted file mode 100644
index c899728e5078..000000000000
--- a/unittests/ExecutionEngine/Orc/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-##===- unittests/ExecutionEngine/Orc/Makefile --------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-TESTNAME = OrcJIT
-LINK_COMPONENTS := core ipo mcjit orcjit native support
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
-
diff --git a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp
index 59ee01f36010..87928347d88e 100644
--- a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp
@@ -12,6 +12,7 @@
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
+#include "llvm/ExecutionEngine/Orc/NullResolver.h"
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/LLVMContext.h"
@@ -24,19 +25,26 @@ namespace {
class ObjectLinkingLayerExecutionTest : public testing::Test,
public OrcExecutionTest {
+
};
class SectionMemoryManagerWrapper : public SectionMemoryManager {
public:
int FinalizationCount = 0;
- bool finalizeMemory(std::string *ErrMsg = 0) override {
+ int NeedsToReserveAllocationSpaceCount = 0;
+
+ bool needsToReserveAllocationSpace() override {
+ ++NeedsToReserveAllocationSpaceCount;
+ return SectionMemoryManager::needsToReserveAllocationSpace();
+ }
+
+ bool finalizeMemory(std::string *ErrMsg = nullptr) override {
++FinalizationCount;
return SectionMemoryManager::finalizeMemory(ErrMsg);
}
};
TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) {
-
class SectionMemoryManagerWrapper : public SectionMemoryManager {
public:
SectionMemoryManagerWrapper(bool &DebugSeen) : DebugSeen(DebugSeen) {}
@@ -57,9 +65,10 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) {
ObjectLinkingLayer<> ObjLayer;
- auto M = llvm::make_unique<Module>("", getGlobalContext());
+ LLVMContext Context;
+ auto M = llvm::make_unique<Module>("", Context);
M->setTargetTriple("x86_64-unknown-linux-gnu");
- Type *Int32Ty = IntegerType::get(getGlobalContext(), 32);
+ Type *Int32Ty = IntegerType::get(Context, 32);
GlobalVariable *GV =
new GlobalVariable(*M, Int32Ty, false, GlobalValue::ExternalLinkage,
ConstantInt::get(Int32Ty, 42), "foo");
@@ -105,9 +114,7 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) {
}
}
-
TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
-
if (!TM)
return;
@@ -120,15 +127,19 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
// Module 2:
// int bar();
// int foo() { return bar(); }
+ //
+ // Verify that the memory manager is only finalized once (for Module 2).
+ // Failure suggests that finalize is being called on the inner RTDyld
+ // instance (for Module 1) which is unsafe, as it will prevent relocation of
+ // Module 2.
- ModuleBuilder MB1(getGlobalContext(), "", "dummy");
+ ModuleBuilder MB1(Context, "", "dummy");
{
MB1.getModule()->setDataLayout(TM->createDataLayout());
Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("bar");
- BasicBlock *BarEntry = BasicBlock::Create(getGlobalContext(), "entry",
- BarImpl);
+ BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl);
IRBuilder<> Builder(BarEntry);
- IntegerType *Int32Ty = IntegerType::get(getGlobalContext(), 32);
+ IntegerType *Int32Ty = IntegerType::get(Context, 32);
Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42);
Builder.CreateRet(FourtyTwo);
}
@@ -137,13 +148,12 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
std::vector<object::ObjectFile*> Obj1Set;
Obj1Set.push_back(Obj1.getBinary());
- ModuleBuilder MB2(getGlobalContext(), "", "dummy");
+ ModuleBuilder MB2(Context, "", "dummy");
{
MB2.getModule()->setDataLayout(TM->createDataLayout());
Function *BarDecl = MB2.createFunctionDecl<int32_t(void)>("bar");
Function *FooImpl = MB2.createFunctionDecl<int32_t(void)>("foo");
- BasicBlock *FooEntry = BasicBlock::Create(getGlobalContext(), "entry",
- FooImpl);
+ BasicBlock *FooEntry = BasicBlock::Create(Context, "entry", FooImpl);
IRBuilder<> Builder(FooEntry);
Builder.CreateRet(Builder.CreateCall(BarDecl));
}
@@ -155,7 +165,7 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
createLambdaResolver(
[&](const std::string &Name) {
if (auto Sym = ObjLayer.findSymbol(Name, true))
- return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags());
+ return Sym.toRuntimeDyldSymbol();
return RuntimeDyld::SymbolInfo(nullptr);
},
[](const std::string &Name) {
@@ -173,4 +183,65 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
<< "Extra call to finalize";
}
+TEST_F(ObjectLinkingLayerExecutionTest, NoPrematureAllocation) {
+ if (!TM)
+ return;
+
+ ObjectLinkingLayer<> ObjLayer;
+ SimpleCompiler Compile(*TM);
+
+ // Create a pair of unrelated modules:
+ //
+ // Module 1:
+ // int foo() { return 42; }
+ // Module 2:
+ // int bar() { return 7; }
+ //
+ // Both modules will share a memory manager. We want to verify that the
+ // second object is not loaded before the first one is finalized. To do this
+ // in a portable way, we abuse the
+ // RuntimeDyld::MemoryManager::needsToReserveAllocationSpace hook, which is
+ // called once per object before any sections are allocated.
+
+ ModuleBuilder MB1(Context, "", "dummy");
+ {
+ MB1.getModule()->setDataLayout(TM->createDataLayout());
+ Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("foo");
+ BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl);
+ IRBuilder<> Builder(BarEntry);
+ IntegerType *Int32Ty = IntegerType::get(Context, 32);
+ Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42);
+ Builder.CreateRet(FourtyTwo);
+ }
+
+ auto Obj1 = Compile(*MB1.getModule());
+ std::vector<object::ObjectFile*> Obj1Set;
+ Obj1Set.push_back(Obj1.getBinary());
+
+ ModuleBuilder MB2(Context, "", "dummy");
+ {
+ MB2.getModule()->setDataLayout(TM->createDataLayout());
+ Function *BarImpl = MB2.createFunctionDecl<int32_t(void)>("bar");
+ BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl);
+ IRBuilder<> Builder(BarEntry);
+ IntegerType *Int32Ty = IntegerType::get(Context, 32);
+ Value *Seven = ConstantInt::getSigned(Int32Ty, 7);
+ Builder.CreateRet(Seven);
+ }
+ auto Obj2 = Compile(*MB2.getModule());
+ std::vector<object::ObjectFile*> Obj2Set;
+ Obj2Set.push_back(Obj2.getBinary());
+
+ SectionMemoryManagerWrapper SMMW;
+ NullResolver NR;
+ auto H = ObjLayer.addObjectSet(std::move(Obj1Set), &SMMW, &NR);
+ ObjLayer.addObjectSet(std::move(Obj2Set), &SMMW, &NR);
+ ObjLayer.emitAndFinalize(H);
+
+ // Only one call to needsToReserveAllocationSpace should have been made.
+ EXPECT_EQ(SMMW.NeedsToReserveAllocationSpaceCount, 1)
+ << "More than one call to needsToReserveAllocationSpace "
+ "(multiple unrelated objects loaded prior to finalization)";
}
+
+} // end anonymous namespace
diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp
index c88c94f17b1c..e1b1f2f92781 100644
--- a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp
@@ -7,9 +7,14 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
+#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
+#include "llvm/ExecutionEngine/Orc/NullResolver.h"
+#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
+#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
+#include "llvm/Object/ObjectFile.h"
#include "gtest/gtest.h"
using namespace llvm::orc;
@@ -51,13 +56,14 @@ public:
template <typename ObjSetT, typename MemoryManagerPtrT,
typename SymbolResolverPtrT>
- ObjSetHandleT addObjectSet(ObjSetT &Objects, MemoryManagerPtrT MemMgr,
+ ObjSetHandleT addObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr,
SymbolResolverPtrT Resolver) {
EXPECT_EQ(MockManager, *MemMgr) << "MM should pass through";
EXPECT_EQ(MockResolver, *Resolver) << "Resolver should pass through";
size_t I = 0;
for (auto &ObjPtr : Objects) {
- EXPECT_EQ(MockObjects[I++] + 1, *ObjPtr) << "Transform should be applied";
+ EXPECT_EQ(MockObjects[I] + 1, *ObjPtr) << "Transform should be applied";
+ I++;
}
EXPECT_EQ(MockObjects.size(), I) << "Number of objects should match";
LastCalled = "addObjectSet";
@@ -216,13 +222,14 @@ TEST(ObjectTransformLayerTest, Main) {
auto MM = llvm::make_unique<MockMemoryManager>(MockManager);
auto SR = llvm::make_unique<MockSymbolResolver>(MockResolver);
M.expectAddObjectSet(Objs1, MM.get(), SR.get());
- auto H = T1.addObjectSet(Objs1, std::move(MM), std::move(SR));
+ auto H = T1.addObjectSet(std::move(Objs1), std::move(MM), std::move(SR));
M.verifyAddObjectSet(H);
// Test addObjectSet with T2 (mutating, naked pointers)
- llvm::SmallVector<MockObjectFile *, 2> Objs2;
- Objs2.push_back(&MockObject1);
- Objs2.push_back(&MockObject2);
+ llvm::SmallVector<MockObjectFile *, 2> Objs2Vec;
+ Objs2Vec.push_back(&MockObject1);
+ Objs2Vec.push_back(&MockObject2);
+ llvm::MutableArrayRef<MockObjectFile *> Objs2(Objs2Vec);
M.expectAddObjectSet(Objs2, &MockManager, &MockResolver);
H = T2.addObjectSet(Objs2, &MockManager, &MockResolver);
M.verifyAddObjectSet(H);
@@ -271,5 +278,61 @@ TEST(ObjectTransformLayerTest, Main) {
const auto &T1C = T1;
OwnedObj = T1C.getTransform()(std::move(OwnedObj));
EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform";
+
+ volatile bool RunStaticChecks = false;
+ if (!RunStaticChecks)
+ return;
+
+ // Make sure that ObjectTransformLayer implements the object layer concept
+ // correctly by sandwitching one between an ObjectLinkingLayer and an
+ // IRCompileLayer, verifying that it compiles if we have a call to the
+ // IRComileLayer's addModuleSet that should call the transform layer's
+ // addObjectSet, and also calling the other public transform layer methods
+ // directly to make sure the methods they intend to forward to exist on
+ // the ObjectLinkingLayer.
+
+ // We'll need a concrete MemoryManager class.
+ class NullManager : public llvm::RuntimeDyld::MemoryManager {
+ public:
+ uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned,
+ llvm::StringRef) override {
+ return nullptr;
+ }
+ uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef,
+ bool) override {
+ return nullptr;
+ }
+ void registerEHFrames(uint8_t *, uint64_t, size_t) override {}
+ void deregisterEHFrames(uint8_t *, uint64_t, size_t) override {}
+ bool finalizeMemory(std::string *) override { return false; }
+ };
+
+ // Construct the jit layers.
+ ObjectLinkingLayer<> BaseLayer;
+ auto IdentityTransform = [](
+ std::unique_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>>
+ Obj) { return Obj; };
+ ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)>
+ TransformLayer(BaseLayer, IdentityTransform);
+ auto NullCompiler = [](llvm::Module &) {
+ return llvm::object::OwningBinary<llvm::object::ObjectFile>();
+ };
+ IRCompileLayer<decltype(TransformLayer)> CompileLayer(TransformLayer,
+ NullCompiler);
+
+ // Make sure that the calls from IRCompileLayer to ObjectTransformLayer
+ // compile.
+ NullResolver Resolver;
+ NullManager Manager;
+ CompileLayer.addModuleSet(std::vector<llvm::Module *>(), &Manager, &Resolver);
+
+ // Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer
+ // compile.
+ decltype(TransformLayer)::ObjSetHandleT ObjSet;
+ TransformLayer.emitAndFinalize(ObjSet);
+ TransformLayer.findSymbolIn(ObjSet, Name, false);
+ TransformLayer.findSymbol(Name, true);
+ TransformLayer.mapSectionAddress(ObjSet, nullptr, 0);
+ TransformLayer.removeObjectSet(ObjSet);
}
}
diff --git a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
index 776d26970a31..305325b6c6ef 100644
--- a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
+++ b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
@@ -25,11 +25,11 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef)
class OrcCAPIExecutionTest : public testing::Test, public OrcExecutionTest {
protected:
std::unique_ptr<Module> createTestModule(const Triple &TT) {
- ModuleBuilder MB(getGlobalContext(), TT.str(), "");
+ ModuleBuilder MB(Context, TT.str(), "");
Function *TestFunc = MB.createFunctionDecl<int()>("testFunc");
Function *Main = MB.createFunctionDecl<int(int, char*[])>("main");
- Main->getBasicBlockList().push_back(BasicBlock::Create(getGlobalContext()));
+ Main->getBasicBlockList().push_back(BasicBlock::Create(Context));
IRBuilder<> B(&Main->back());
Value* Result = B.CreateCall(TestFunc);
B.CreateRet(Result);
diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/unittests/ExecutionEngine/Orc/OrcTestCommon.h
index f480e0789ae5..fe3da88dc9d1 100644
--- a/unittests/ExecutionEngine/Orc/OrcTestCommon.h
+++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.h
@@ -46,12 +46,15 @@ public:
if (TM) {
// If we found a TargetMachine, check that it's one that Orc supports.
const Triple& TT = TM->getTargetTriple();
- if (TT.getArch() != Triple::x86_64 || !TT.isOSDarwin())
+
+ if ((TT.getArch() != Triple::x86_64 && TT.getArch() != Triple::x86) ||
+ TT.isOSWindows())
TM = nullptr;
}
};
protected:
+ LLVMContext Context;
std::unique_ptr<TargetMachine> TM;
private:
static bool NativeTargetInitialized;
diff --git a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp
index 8215144a514d..7d55641e4ce2 100644
--- a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp
+++ b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp
@@ -17,131 +17,192 @@ using namespace llvm;
using namespace llvm::orc;
using namespace llvm::orc::remote;
-class QueueChannel : public RPCChannel {
+class Queue : public std::queue<char> {
public:
- QueueChannel(std::queue<char> &Queue) : Queue(Queue) {}
+ std::mutex &getLock() { return Lock; }
+
+private:
+ std::mutex Lock;
+};
- std::error_code readBytes(char *Dst, unsigned Size) override {
- while (Size--) {
- *Dst++ = Queue.front();
- Queue.pop();
+class QueueChannel : public RPCChannel {
+public:
+ QueueChannel(Queue &InQueue, Queue &OutQueue)
+ : InQueue(InQueue), OutQueue(OutQueue) {}
+
+ Error readBytes(char *Dst, unsigned Size) override {
+ while (Size != 0) {
+ // If there's nothing to read then yield.
+ while (InQueue.empty())
+ std::this_thread::yield();
+
+ // Lock the channel and read what we can.
+ std::lock_guard<std::mutex> Lock(InQueue.getLock());
+ while (!InQueue.empty() && Size) {
+ *Dst++ = InQueue.front();
+ --Size;
+ InQueue.pop();
+ }
}
- return std::error_code();
+ return Error::success();
}
- std::error_code appendBytes(const char *Src, unsigned Size) override {
+ Error appendBytes(const char *Src, unsigned Size) override {
+ std::lock_guard<std::mutex> Lock(OutQueue.getLock());
while (Size--)
- Queue.push(*Src++);
- return std::error_code();
+ OutQueue.push(*Src++);
+ return Error::success();
}
- std::error_code send() override { return std::error_code(); }
+ Error send() override { return Error::success(); }
private:
- std::queue<char> &Queue;
+ Queue &InQueue;
+ Queue &OutQueue;
};
-class DummyRPC : public testing::Test,
- public RPC<QueueChannel> {
+class DummyRPC : public testing::Test, public RPC<QueueChannel> {
public:
- typedef Procedure<1, bool> Proc1;
- typedef Procedure<2, int8_t,
- uint8_t,
- int16_t,
- uint16_t,
- int32_t,
- uint32_t,
- int64_t,
- uint64_t,
- bool,
- std::string,
- std::vector<int>> AllTheTypes;
+ enum FuncId : uint32_t {
+ VoidBoolId = RPCFunctionIdTraits<FuncId>::FirstValidId,
+ IntIntId,
+ AllTheTypesId
+ };
+
+ typedef Function<VoidBoolId, void(bool)> VoidBool;
+ typedef Function<IntIntId, int32_t(int32_t)> IntInt;
+ typedef Function<AllTheTypesId,
+ void(int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ int64_t, uint64_t, bool, std::string, std::vector<int>)>
+ AllTheTypes;
};
+TEST_F(DummyRPC, TestAsyncVoidBool) {
+ Queue Q1, Q2;
+ QueueChannel C1(Q1, Q2);
+ QueueChannel C2(Q2, Q1);
+
+ // Make an async call.
+ auto ResOrErr = callAsyncWithSeq<VoidBool>(C1, true);
+ EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed";
-TEST_F(DummyRPC, TestBasic) {
- std::queue<char> Queue;
- QueueChannel C(Queue);
+ {
+ // Expect a call to Proc1.
+ auto EC = expect<VoidBool>(C2, [&](bool &B) {
+ EXPECT_EQ(B, true) << "Bool serialization broken";
+ return Error::success();
+ });
+ EXPECT_FALSE(EC) << "Simple expect over queue failed";
+ }
{
- // Make a call to Proc1.
- auto EC = call<Proc1>(C, true);
- EXPECT_FALSE(EC) << "Simple call over queue failed";
+ // Wait for the result.
+ auto EC = waitForResult(C1, ResOrErr->second, handleNone);
+ EXPECT_FALSE(EC) << "Could not read result.";
}
+ // Verify that the function returned ok.
+ auto Val = ResOrErr->first.get();
+ EXPECT_TRUE(Val) << "Remote void function failed to execute.";
+}
+
+TEST_F(DummyRPC, TestAsyncIntInt) {
+ Queue Q1, Q2;
+ QueueChannel C1(Q1, Q2);
+ QueueChannel C2(Q2, Q1);
+
+ // Make an async call.
+ auto ResOrErr = callAsyncWithSeq<IntInt>(C1, 21);
+ EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed";
+
{
// Expect a call to Proc1.
- auto EC = expect<Proc1>(C,
- [&](bool &B) {
- EXPECT_EQ(B, true)
- << "Bool serialization broken";
- return std::error_code();
- });
+ auto EC = expect<IntInt>(C2, [&](int32_t I) -> Expected<int32_t> {
+ EXPECT_EQ(I, 21) << "Bool serialization broken";
+ return 2 * I;
+ });
EXPECT_FALSE(EC) << "Simple expect over queue failed";
}
+
+ {
+ // Wait for the result.
+ auto EC = waitForResult(C1, ResOrErr->second, handleNone);
+ EXPECT_FALSE(EC) << "Could not read result.";
+ }
+
+ // Verify that the function returned ok.
+ auto Val = ResOrErr->first.get();
+ EXPECT_TRUE(!!Val) << "Remote int function failed to execute.";
+ EXPECT_EQ(*Val, 42) << "Remote int function return wrong value.";
}
TEST_F(DummyRPC, TestSerialization) {
- std::queue<char> Queue;
- QueueChannel C(Queue);
+ Queue Q1, Q2;
+ QueueChannel C1(Q1, Q2);
+ QueueChannel C2(Q2, Q1);
+
+ // Make a call to Proc1.
+ std::vector<int> v({42, 7});
+ auto ResOrErr = callAsyncWithSeq<AllTheTypes>(
+ C1, -101, 250, -10000, 10000, -1000000000, 1000000000, -10000000000,
+ 10000000000, true, "foo", v);
+ EXPECT_TRUE(!!ResOrErr) << "Big (serialization test) call over queue failed";
{
- // Make a call to Proc1.
- std::vector<int> v({42, 7});
- auto EC = call<AllTheTypes>(C,
- -101,
- 250,
- -10000,
- 10000,
- -1000000000,
- 1000000000,
- -10000000000,
- 10000000000,
- true,
- "foo",
- v);
+ // Expect a call to Proc1.
+ auto EC = expect<AllTheTypes>(
+ C2, [&](int8_t &s8, uint8_t &u8, int16_t &s16, uint16_t &u16,
+ int32_t &s32, uint32_t &u32, int64_t &s64, uint64_t &u64,
+ bool &b, std::string &s, std::vector<int> &v) {
+
+ EXPECT_EQ(s8, -101) << "int8_t serialization broken";
+ EXPECT_EQ(u8, 250) << "uint8_t serialization broken";
+ EXPECT_EQ(s16, -10000) << "int16_t serialization broken";
+ EXPECT_EQ(u16, 10000) << "uint16_t serialization broken";
+ EXPECT_EQ(s32, -1000000000) << "int32_t serialization broken";
+ EXPECT_EQ(u32, 1000000000ULL) << "uint32_t serialization broken";
+ EXPECT_EQ(s64, -10000000000) << "int64_t serialization broken";
+ EXPECT_EQ(u64, 10000000000ULL) << "uint64_t serialization broken";
+ EXPECT_EQ(b, true) << "bool serialization broken";
+ EXPECT_EQ(s, "foo") << "std::string serialization broken";
+ EXPECT_EQ(v, std::vector<int>({42, 7}))
+ << "std::vector serialization broken";
+ return Error::success();
+ });
EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed";
}
{
- // Expect a call to Proc1.
- auto EC = expect<AllTheTypes>(C,
- [&](int8_t &s8,
- uint8_t &u8,
- int16_t &s16,
- uint16_t &u16,
- int32_t &s32,
- uint32_t &u32,
- int64_t &s64,
- uint64_t &u64,
- bool &b,
- std::string &s,
- std::vector<int> &v) {
-
- EXPECT_EQ(s8, -101)
- << "int8_t serialization broken";
- EXPECT_EQ(u8, 250)
- << "uint8_t serialization broken";
- EXPECT_EQ(s16, -10000)
- << "int16_t serialization broken";
- EXPECT_EQ(u16, 10000)
- << "uint16_t serialization broken";
- EXPECT_EQ(s32, -1000000000)
- << "int32_t serialization broken";
- EXPECT_EQ(u32, 1000000000ULL)
- << "uint32_t serialization broken";
- EXPECT_EQ(s64, -10000000000)
- << "int64_t serialization broken";
- EXPECT_EQ(u64, 10000000000ULL)
- << "uint64_t serialization broken";
- EXPECT_EQ(b, true)
- << "bool serialization broken";
- EXPECT_EQ(s, "foo")
- << "std::string serialization broken";
- EXPECT_EQ(v, std::vector<int>({42, 7}))
- << "std::vector serialization broken";
- return std::error_code();
- });
- EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed";
+ // Wait for the result.
+ auto EC = waitForResult(C1, ResOrErr->second, handleNone);
+ EXPECT_FALSE(EC) << "Could not read result.";
}
+
+ // Verify that the function returned ok.
+ auto Val = ResOrErr->first.get();
+ EXPECT_TRUE(Val) << "Remote void function failed to execute.";
}
+
+// Test the synchronous call API.
+// FIXME: Re-enable once deadlock encountered on S390 has been debugged / fixed,
+// see http://lab.llvm.org:8011/builders/clang-s390x-linux/builds/3459
+// TEST_F(DummyRPC, TestSynchronousCall) {
+// Queue Q1, Q2;
+// QueueChannel C1(Q1, Q2);
+// QueueChannel C2(Q2, Q1);
+//
+// auto ServerResult =
+// std::async(std::launch::async,
+// [&]() {
+// return expect<IntInt>(C2, [&](int32_t V) { return V; });
+// });
+//
+// auto ValOrErr = callST<IntInt>(C1, 42);
+//
+// EXPECT_FALSE(!!ServerResult.get())
+// << "Server returned an error.";
+// EXPECT_TRUE(!!ValOrErr)
+// << "callST returned an error.";
+// EXPECT_EQ(*ValOrErr, 42)
+// << "Incorrect callST<IntInt> result";
+// }
diff --git a/unittests/IR/AttributesTest.cpp b/unittests/IR/AttributesTest.cpp
index ebcb772bc373..9f8013ff181c 100644
--- a/unittests/IR/AttributesTest.cpp
+++ b/unittests/IR/AttributesTest.cpp
@@ -34,6 +34,15 @@ TEST(Attributes, Uniquing) {
TEST(Attributes, Ordering) {
LLVMContext C;
+ Attribute Align4 = Attribute::get(C, Attribute::Alignment, 4);
+ Attribute Align5 = Attribute::get(C, Attribute::Alignment, 5);
+ Attribute Deref4 = Attribute::get(C, Attribute::Dereferenceable, 4);
+ Attribute Deref5 = Attribute::get(C, Attribute::Dereferenceable, 5);
+ EXPECT_TRUE(Align4 < Align5);
+ EXPECT_TRUE(Align4 < Deref4);
+ EXPECT_TRUE(Align4 < Deref5);
+ EXPECT_TRUE(Align5 < Deref4);
+
AttributeSet ASs[] = {
AttributeSet::get(C, 2, Attribute::ZExt),
AttributeSet::get(C, 1, Attribute::SExt)
diff --git a/unittests/IR/CMakeLists.txt b/unittests/IR/CMakeLists.txt
index 5aad8edc9134..2baa4370c70e 100644
--- a/unittests/IR/CMakeLists.txt
+++ b/unittests/IR/CMakeLists.txt
@@ -11,9 +11,12 @@ set(IRSources
ConstantRangeTest.cpp
ConstantsTest.cpp
DebugInfoTest.cpp
+ DebugTypeODRUniquingTest.cpp
DominatorTreeTest.cpp
+ FunctionTest.cpp
IRBuilderTest.cpp
InstructionsTest.cpp
+ IntrinsicsTest.cpp
LegacyPassManagerTest.cpp
MDBuilderTest.cpp
MetadataTest.cpp
diff --git a/unittests/IR/ConstantRangeTest.cpp b/unittests/IR/ConstantRangeTest.cpp
index 1f32eea3e43b..f7a8a82043b9 100644
--- a/unittests/IR/ConstantRangeTest.cpp
+++ b/unittests/IR/ConstantRangeTest.cpp
@@ -450,6 +450,45 @@ TEST_F(ConstantRangeTest, SMax) {
EXPECT_EQ(One.smax(One), One);
}
+TEST_F(ConstantRangeTest, UMin) {
+ EXPECT_EQ(Full.umin(Full), Full);
+ EXPECT_EQ(Full.umin(Empty), Empty);
+ EXPECT_EQ(Full.umin(Some), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
+ EXPECT_EQ(Full.umin(Wrap), Full);
+ EXPECT_EQ(Empty.umin(Empty), Empty);
+ EXPECT_EQ(Empty.umin(Some), Empty);
+ EXPECT_EQ(Empty.umin(Wrap), Empty);
+ EXPECT_EQ(Empty.umin(One), Empty);
+ EXPECT_EQ(Some.umin(Some), Some);
+ EXPECT_EQ(Some.umin(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
+ EXPECT_EQ(Some.umin(One), One);
+ // TODO: ConstantRange is currently over-conservative here.
+ EXPECT_EQ(Wrap.umin(Wrap), Full);
+ EXPECT_EQ(Wrap.umin(One), ConstantRange(APInt(16, 0), APInt(16, 0xb)));
+ EXPECT_EQ(One.umin(One), One);
+}
+
+TEST_F(ConstantRangeTest, SMin) {
+ EXPECT_EQ(Full.smin(Full), Full);
+ EXPECT_EQ(Full.smin(Empty), Empty);
+ EXPECT_EQ(Full.smin(Some), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
+ APInt(16, 0xaaa)));
+ EXPECT_EQ(Full.smin(Wrap), Full);
+ EXPECT_EQ(Empty.smin(Empty), Empty);
+ EXPECT_EQ(Empty.smin(Some), Empty);
+ EXPECT_EQ(Empty.smin(Wrap), Empty);
+ EXPECT_EQ(Empty.smin(One), Empty);
+ EXPECT_EQ(Some.smin(Some), Some);
+ EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
+ APInt(16, 0xaaa)));
+ EXPECT_EQ(Some.smin(One), One);
+ // TODO: ConstantRange is currently over-conservative here.
+ EXPECT_EQ(Wrap.smin(Wrap), Full);
+ EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
+ APInt(16, 0xb)));
+ EXPECT_EQ(One.smin(One), One);
+}
+
TEST_F(ConstantRangeTest, UDiv) {
EXPECT_EQ(Full.udiv(Full), Full);
EXPECT_EQ(Full.udiv(Empty), Empty);
@@ -569,7 +608,7 @@ TEST(ConstantRange, MakeSatisfyingICmpRegion) {
ConstantRange(APInt(8, 4), APInt(8, -128)));
}
-TEST(ConstantRange, MakeOverflowingRegion) {
+TEST(ConstantRange, MakeGuaranteedNoWrapRegion) {
const int IntMin4Bits = 8;
const int IntMax4Bits = 7;
typedef OverflowingBinaryOperator OBO;
@@ -577,17 +616,17 @@ TEST(ConstantRange, MakeOverflowingRegion) {
for (int Const : {0, -1, -2, 1, 2, IntMin4Bits, IntMax4Bits}) {
APInt C(4, Const, true /* = isSigned */);
- auto NUWRegion =
- ConstantRange::makeNoWrapRegion(Instruction::Add, C, OBO::NoUnsignedWrap);
+ auto NUWRegion = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, C, OBO::NoUnsignedWrap);
EXPECT_FALSE(NUWRegion.isEmptySet());
- auto NSWRegion =
- ConstantRange::makeNoWrapRegion(Instruction::Add, C, OBO::NoSignedWrap);
+ auto NSWRegion = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, C, OBO::NoSignedWrap);
EXPECT_FALSE(NSWRegion.isEmptySet());
- auto NoWrapRegion = ConstantRange::makeNoWrapRegion(
+ auto NoWrapRegion = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, C, OBO::NoSignedWrap | OBO::NoUnsignedWrap);
EXPECT_FALSE(NoWrapRegion.isEmptySet());
@@ -618,6 +657,109 @@ TEST(ConstantRange, MakeOverflowingRegion) {
EXPECT_FALSE(Overflow);
}
}
+
+ auto NSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, ConstantRange(32, /* isFullSet = */ true),
+ OBO::NoSignedWrap);
+ EXPECT_TRUE(NSWForAllValues.isSingleElement() &&
+ NSWForAllValues.getSingleElement()->isMinValue());
+
+ auto NUWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, ConstantRange(32, /* isFullSet = */ true),
+ OBO::NoUnsignedWrap);
+ EXPECT_TRUE(NUWForAllValues.isSingleElement() &&
+ NSWForAllValues.getSingleElement()->isMinValue());
+
+ auto NUWAndNSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, ConstantRange(32, /* isFullSet = */ true),
+ OBO::NoUnsignedWrap | OBO::NoSignedWrap);
+ EXPECT_TRUE(NUWAndNSWForAllValues.isSingleElement() &&
+ NSWForAllValues.getSingleElement()->isMinValue());
+
+ ConstantRange OneToFive(APInt(32, 1), APInt(32, 6));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, OneToFive, OBO::NoSignedWrap),
+ ConstantRange(APInt::getSignedMinValue(32),
+ APInt::getSignedMaxValue(32) - 4));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, OneToFive, OBO::NoUnsignedWrap),
+ ConstantRange(APInt::getMinValue(32), APInt::getMinValue(32) - 5));
+ EXPECT_EQ(
+ ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, OneToFive, OBO::NoUnsignedWrap | OBO::NoSignedWrap),
+ ConstantRange(APInt::getMinValue(32), APInt::getSignedMaxValue(32) - 4));
+
+ ConstantRange MinusFiveToMinusTwo(APInt(32, -5), APInt(32, -1));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, MinusFiveToMinusTwo, OBO::NoSignedWrap),
+ ConstantRange(APInt::getSignedMinValue(32) + 5,
+ APInt::getSignedMinValue(32)));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, MinusFiveToMinusTwo, OBO::NoUnsignedWrap),
+ ConstantRange(APInt(32, 0), APInt(32, 2)));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, MinusFiveToMinusTwo,
+ OBO::NoUnsignedWrap | OBO::NoSignedWrap),
+ ConstantRange(APInt(32, 0), APInt(32, 2)));
+
+ ConstantRange MinusOneToOne(APInt(32, -1), APInt(32, 2));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, MinusOneToOne, OBO::NoSignedWrap),
+ ConstantRange(APInt::getSignedMinValue(32) + 1,
+ APInt::getSignedMinValue(32) - 1));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, MinusOneToOne, OBO::NoUnsignedWrap),
+ ConstantRange(APInt(32, 0), APInt(32, 1)));
+ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Add, MinusOneToOne,
+ OBO::NoUnsignedWrap | OBO::NoSignedWrap),
+ ConstantRange(APInt(32, 0), APInt(32, 1)));
+}
+
+TEST(ConstantRange, GetEquivalentICmp) {
+ APInt RHS;
+ CmpInst::Predicate Pred;
+
+ EXPECT_TRUE(ConstantRange(APInt::getMinValue(32), APInt(32, 100))
+ .getEquivalentICmp(Pred, RHS));
+ EXPECT_EQ(Pred, CmpInst::ICMP_ULT);
+ EXPECT_EQ(RHS, APInt(32, 100));
+
+ EXPECT_TRUE(ConstantRange(APInt::getSignedMinValue(32), APInt(32, 100))
+ .getEquivalentICmp(Pred, RHS));
+ EXPECT_EQ(Pred, CmpInst::ICMP_SLT);
+ EXPECT_EQ(RHS, APInt(32, 100));
+
+ EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getMinValue(32))
+ .getEquivalentICmp(Pred, RHS));
+ EXPECT_EQ(Pred, CmpInst::ICMP_UGE);
+ EXPECT_EQ(RHS, APInt(32, 100));
+
+ EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getSignedMinValue(32))
+ .getEquivalentICmp(Pred, RHS));
+ EXPECT_EQ(Pred, CmpInst::ICMP_SGE);
+ EXPECT_EQ(RHS, APInt(32, 100));
+
+ EXPECT_TRUE(
+ ConstantRange(32, /*isFullSet=*/true).getEquivalentICmp(Pred, RHS));
+ EXPECT_EQ(Pred, CmpInst::ICMP_UGE);
+ EXPECT_EQ(RHS, APInt(32, 0));
+
+ EXPECT_TRUE(
+ ConstantRange(32, /*isFullSet=*/false).getEquivalentICmp(Pred, RHS));
+ EXPECT_EQ(Pred, CmpInst::ICMP_ULT);
+ EXPECT_EQ(RHS, APInt(32, 0));
+
+ EXPECT_FALSE(ConstantRange(APInt(32, 100), APInt(32, 200))
+ .getEquivalentICmp(Pred, RHS));
+
+ EXPECT_FALSE(ConstantRange(APInt::getSignedMinValue(32) - APInt(32, 100),
+ APInt::getSignedMinValue(32) + APInt(32, 100))
+ .getEquivalentICmp(Pred, RHS));
+
+ EXPECT_FALSE(ConstantRange(APInt::getMinValue(32) - APInt(32, 100),
+ APInt::getMinValue(32) + APInt(32, 100))
+ .getEquivalentICmp(Pred, RHS));
}
} // anonymous namespace
diff --git a/unittests/IR/ConstantsTest.cpp b/unittests/IR/ConstantsTest.cpp
index 7471584097dd..6959ac85e49d 100644
--- a/unittests/IR/ConstantsTest.cpp
+++ b/unittests/IR/ConstantsTest.cpp
@@ -22,7 +22,8 @@ namespace llvm {
namespace {
TEST(ConstantsTest, Integer_i1) {
- IntegerType* Int1 = IntegerType::get(getGlobalContext(), 1);
+ LLVMContext Context;
+ IntegerType *Int1 = IntegerType::get(Context, 1);
Constant* One = ConstantInt::get(Int1, 1, true);
Constant* Zero = ConstantInt::get(Int1, 0);
Constant* NegOne = ConstantInt::get(Int1, static_cast<uint64_t>(-1), true);
@@ -103,7 +104,8 @@ TEST(ConstantsTest, Integer_i1) {
}
TEST(ConstantsTest, IntSigns) {
- IntegerType* Int8Ty = Type::getInt8Ty(getGlobalContext());
+ LLVMContext Context;
+ IntegerType *Int8Ty = Type::getInt8Ty(Context);
EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, false)->getSExtValue());
EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, true)->getSExtValue());
EXPECT_EQ(100, ConstantInt::getSigned(Int8Ty, 100)->getSExtValue());
@@ -116,16 +118,17 @@ TEST(ConstantsTest, IntSigns) {
}
TEST(ConstantsTest, FP128Test) {
- Type *FP128Ty = Type::getFP128Ty(getGlobalContext());
+ LLVMContext Context;
+ Type *FP128Ty = Type::getFP128Ty(Context);
- IntegerType *Int128Ty = Type::getIntNTy(getGlobalContext(), 128);
+ IntegerType *Int128Ty = Type::getIntNTy(Context, 128);
Constant *Zero128 = Constant::getNullValue(Int128Ty);
Constant *X = ConstantExpr::getUIToFP(Zero128, FP128Ty);
EXPECT_TRUE(isa<ConstantFP>(X));
}
TEST(ConstantsTest, PointerCast) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Type *Int8PtrTy = Type::getInt8PtrTy(C);
Type *Int32PtrTy = Type::getInt32PtrTy(C);
Type *Int64Ty = Type::getInt64Ty(C);
@@ -152,6 +155,27 @@ TEST(ConstantsTest, PointerCast) {
EXPECT_EQ(Constant::getNullValue(Int32PtrVecTy),
ConstantExpr::getPointerCast(
Constant::getNullValue(Int8PtrVecTy), Int32PtrVecTy));
+
+ Type *Int32Ptr1Ty = Type::getInt32PtrTy(C, 1);
+ ConstantInt *K = ConstantInt::get(Type::getInt64Ty(C), 1234);
+
+ // Make sure that addrspacecast of inttoptr is not folded away.
+ EXPECT_NE(K,
+ ConstantExpr::getAddrSpaceCast(
+ ConstantExpr::getIntToPtr(K, Int32PtrTy), Int32Ptr1Ty));
+ EXPECT_NE(K,
+ ConstantExpr::getAddrSpaceCast(
+ ConstantExpr::getIntToPtr(K, Int32Ptr1Ty), Int32PtrTy));
+
+ Constant *NullInt32Ptr0 = Constant::getNullValue(Int32PtrTy);
+ Constant *NullInt32Ptr1 = Constant::getNullValue(Int32Ptr1Ty);
+
+ // Make sure that addrspacecast of null is not folded away.
+ EXPECT_NE(Constant::getNullValue(Int32PtrTy),
+ ConstantExpr::getAddrSpaceCast(NullInt32Ptr0, Int32Ptr1Ty));
+
+ EXPECT_NE(Constant::getNullValue(Int32Ptr1Ty),
+ ConstantExpr::getAddrSpaceCast(NullInt32Ptr1, Int32PtrTy));
}
#define CHECK(x, y) { \
@@ -165,14 +189,15 @@ TEST(ConstantsTest, PointerCast) {
}
TEST(ConstantsTest, AsInstructionsTest) {
- std::unique_ptr<Module> M(new Module("MyModule", getGlobalContext()));
+ LLVMContext Context;
+ std::unique_ptr<Module> M(new Module("MyModule", Context));
- Type *Int64Ty = Type::getInt64Ty(getGlobalContext());
- Type *Int32Ty = Type::getInt32Ty(getGlobalContext());
- Type *Int16Ty = Type::getInt16Ty(getGlobalContext());
- Type *Int1Ty = Type::getInt1Ty(getGlobalContext());
- Type *FloatTy = Type::getFloatTy(getGlobalContext());
- Type *DoubleTy = Type::getDoubleTy(getGlobalContext());
+ Type *Int64Ty = Type::getInt64Ty(Context);
+ Type *Int32Ty = Type::getInt32Ty(Context);
+ Type *Int16Ty = Type::getInt16Ty(Context);
+ Type *Int1Ty = Type::getInt1Ty(Context);
+ Type *FloatTy = Type::getFloatTy(Context);
+ Type *DoubleTy = Type::getDoubleTy(Context);
Constant *Global = M->getOrInsertGlobal("dummy",
PointerType::getUnqual(Int32Ty));
@@ -189,8 +214,7 @@ TEST(ConstantsTest, AsInstructionsTest) {
Constant *One = ConstantInt::get(Int32Ty, 1);
Constant *Two = ConstantInt::get(Int64Ty, 2);
- Constant *Big = ConstantInt::get(getGlobalContext(),
- APInt{256, uint64_t(-1), true});
+ Constant *Big = ConstantInt::get(Context, APInt{256, uint64_t(-1), true});
Constant *Elt = ConstantInt::get(Int16Ty, 2015);
Constant *Undef16 = UndefValue::get(Int16Ty);
Constant *Undef64 = UndefValue::get(Int64Ty);
@@ -278,9 +302,10 @@ TEST(ConstantsTest, AsInstructionsTest) {
#ifdef GTEST_HAS_DEATH_TEST
#ifndef NDEBUG
TEST(ConstantsTest, ReplaceWithConstantTest) {
- std::unique_ptr<Module> M(new Module("MyModule", getGlobalContext()));
+ LLVMContext Context;
+ std::unique_ptr<Module> M(new Module("MyModule", Context));
- Type *Int32Ty = Type::getInt32Ty(getGlobalContext());
+ Type *Int32Ty = Type::getInt32Ty(Context);
Constant *One = ConstantInt::get(Int32Ty, 1);
Constant *Global =
diff --git a/unittests/IR/DebugTypeODRUniquingTest.cpp b/unittests/IR/DebugTypeODRUniquingTest.cpp
new file mode 100644
index 000000000000..2c899d85d1f3
--- /dev/null
+++ b/unittests/IR/DebugTypeODRUniquingTest.cpp
@@ -0,0 +1,156 @@
+//===- DebugTypeODRUniquingTest.cpp - Debug type ODR uniquing tests -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "gtest/gtest.h"
+using namespace llvm;
+
+namespace {
+
+TEST(DebugTypeODRUniquingTest, enableDebugTypeODRUniquing) {
+ LLVMContext Context;
+ EXPECT_FALSE(Context.isODRUniquingDebugTypes());
+ Context.enableDebugTypeODRUniquing();
+ EXPECT_TRUE(Context.isODRUniquingDebugTypes());
+ Context.disableDebugTypeODRUniquing();
+ EXPECT_FALSE(Context.isODRUniquingDebugTypes());
+}
+
+TEST(DebugTypeODRUniquingTest, getODRType) {
+ LLVMContext Context;
+ MDString &UUID = *MDString::get(Context, "string");
+
+ // Without a type map, this should return null.
+ EXPECT_FALSE(DICompositeType::getODRType(
+ Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr));
+
+ // Enable the mapping. There still shouldn't be a type.
+ Context.enableDebugTypeODRUniquing();
+ EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID));
+
+ // Create some ODR-uniqued type.
+ auto &CT = *DICompositeType::getODRType(
+ Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr);
+ EXPECT_EQ(UUID.getString(), CT.getIdentifier());
+
+ // Check that we get it back, even if we change a field.
+ EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID));
+ EXPECT_EQ(
+ &CT, DICompositeType::getODRType(Context, UUID, dwarf::DW_TAG_class_type,
+ nullptr, nullptr, 0, nullptr, nullptr, 0,
+ 0, 0, 0, nullptr, 0, nullptr, nullptr));
+ EXPECT_EQ(&CT, DICompositeType::getODRType(
+ Context, UUID, dwarf::DW_TAG_class_type,
+ MDString::get(Context, "name"), nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr));
+
+ // Check that it's discarded with the type map.
+ Context.disableDebugTypeODRUniquing();
+ EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID));
+
+ // And it shouldn't magically reappear...
+ Context.enableDebugTypeODRUniquing();
+ EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID));
+}
+
+TEST(DebugTypeODRUniquingTest, buildODRType) {
+ LLVMContext Context;
+ Context.enableDebugTypeODRUniquing();
+
+ // Build an ODR type that's a forward decl.
+ MDString &UUID = *MDString::get(Context, "Type");
+ auto &CT = *DICompositeType::buildODRType(
+ Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr);
+ EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID));
+ EXPECT_EQ(dwarf::DW_TAG_class_type, CT.getTag());
+
+ // Update with another forward decl. This should be a no-op.
+ EXPECT_EQ(&CT, DICompositeType::buildODRType(
+ Context, UUID, dwarf::DW_TAG_structure_type, nullptr, nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr));
+ EXPECT_EQ(dwarf::DW_TAG_class_type, CT.getTag());
+
+ // Update with a definition. This time we should see a change.
+ EXPECT_EQ(&CT, DICompositeType::buildODRType(
+ Context, UUID, dwarf::DW_TAG_structure_type, nullptr, nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr));
+ EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag());
+
+ // Further updates should be ignored.
+ EXPECT_EQ(&CT, DICompositeType::buildODRType(
+ Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr));
+ EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag());
+ EXPECT_EQ(&CT, DICompositeType::buildODRType(
+ Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,
+ nullptr, 0, 0, 0, 0, nullptr, 0, nullptr, nullptr));
+ EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag());
+}
+
+TEST(DebugTypeODRUniquingTest, buildODRTypeFields) {
+ LLVMContext Context;
+ Context.enableDebugTypeODRUniquing();
+
+ // Build an ODR type that's a forward decl with no other fields set.
+ MDString &UUID = *MDString::get(Context, "UUID");
+ auto &CT = *DICompositeType::buildODRType(
+ Context, UUID, 0, nullptr, nullptr, 0, nullptr, nullptr, 0, 0, 0,
+ DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr);
+
+// Create macros for running through all the fields except Identifier and Flags.
+#define FOR_EACH_MDFIELD() \
+ DO_FOR_FIELD(Name) \
+ DO_FOR_FIELD(File) \
+ DO_FOR_FIELD(Scope) \
+ DO_FOR_FIELD(BaseType) \
+ DO_FOR_FIELD(Elements) \
+ DO_FOR_FIELD(VTableHolder) \
+ DO_FOR_FIELD(TemplateParams)
+#define FOR_EACH_INLINEFIELD() \
+ DO_FOR_FIELD(Tag) \
+ DO_FOR_FIELD(Line) \
+ DO_FOR_FIELD(SizeInBits) \
+ DO_FOR_FIELD(AlignInBits) \
+ DO_FOR_FIELD(OffsetInBits) \
+ DO_FOR_FIELD(RuntimeLang)
+
+// Create all the fields.
+#define DO_FOR_FIELD(X) auto *X = MDString::get(Context, #X);
+ FOR_EACH_MDFIELD();
+#undef DO_FOR_FIELD
+ unsigned NonZeroInit = 0;
+#define DO_FOR_FIELD(X) auto X = ++NonZeroInit;
+ FOR_EACH_INLINEFIELD();
+#undef DO_FOR_FIELD
+
+ // Replace all the fields with new values that are distinct from each other.
+ EXPECT_EQ(&CT,
+ DICompositeType::buildODRType(
+ Context, UUID, Tag, Name, File, Line, Scope, BaseType,
+ SizeInBits, AlignInBits, OffsetInBits, DINode::FlagArtificial,
+ Elements, RuntimeLang, VTableHolder, TemplateParams));
+
+ // Confirm that all the right fields got updated.
+#define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.getRaw##X());
+ FOR_EACH_MDFIELD();
+#undef DO_FOR_FIELD
+#undef FOR_EACH_MDFIELD
+#define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.get##X());
+ FOR_EACH_INLINEFIELD();
+#undef DO_FOR_FIELD
+#undef FOR_EACH_INLINEFIELD
+ EXPECT_EQ(DINode::FlagArtificial, CT.getFlags());
+ EXPECT_EQ(&UUID, CT.getRawIdentifier());
+}
+
+} // end namespace
diff --git a/unittests/IR/DominatorTreeTest.cpp b/unittests/IR/DominatorTreeTest.cpp
index 3aef4d64cbc2..6c49deb32d94 100644
--- a/unittests/IR/DominatorTreeTest.cpp
+++ b/unittests/IR/DominatorTreeTest.cpp
@@ -29,7 +29,8 @@ namespace llvm {
bool runOnFunction(Function &F) override {
DominatorTree *DT =
&getAnalysis<DominatorTreeWrapperPass>().getDomTree();
- PostDominatorTree *PDT = &getAnalysis<PostDominatorTree>();
+ PostDominatorTree *PDT =
+ &getAnalysis<PostDominatorTreeWrapperPass>().getPostDomTree();
Function::iterator FI = F.begin();
BasicBlock *BB0 = &*FI++;
@@ -206,7 +207,7 @@ namespace llvm {
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<DominatorTreeWrapperPass>();
- AU.addRequired<PostDominatorTree>();
+ AU.addRequired<PostDominatorTreeWrapperPass>();
}
DPass() : FunctionPass(ID) {
initializeDPassPass(*PassRegistry::getPassRegistry());
@@ -214,7 +215,7 @@ namespace llvm {
};
char DPass::ID = 0;
- std::unique_ptr<Module> makeLLVMModule(DPass *P) {
+ std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context, DPass *P) {
const char *ModuleStrig =
"declare i32 @g()\n" \
"define void @f(i32 %x) personality i32 ()* @g {\n" \
@@ -238,14 +239,14 @@ namespace llvm {
" %y9 = phi i32 [0, %bb2], [%y4, %bb1]\n"
" ret void\n" \
"}\n";
- LLVMContext &C = getGlobalContext();
SMDiagnostic Err;
- return parseAssemblyString(ModuleStrig, Err, C);
+ return parseAssemblyString(ModuleStrig, Err, Context);
}
TEST(DominatorTree, Unreachable) {
DPass *P = new DPass();
- std::unique_ptr<Module> M = makeLLVMModule(P);
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, P);
legacy::PassManager Passes;
Passes.add(P);
Passes.run(*M);
@@ -255,5 +256,5 @@ namespace llvm {
INITIALIZE_PASS_BEGIN(DPass, "dpass", "dpass", false, false)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
-INITIALIZE_PASS_DEPENDENCY(PostDominatorTree)
+INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass)
INITIALIZE_PASS_END(DPass, "dpass", "dpass", false, false)
diff --git a/unittests/IR/FunctionTest.cpp b/unittests/IR/FunctionTest.cpp
new file mode 100644
index 000000000000..fb458597c37a
--- /dev/null
+++ b/unittests/IR/FunctionTest.cpp
@@ -0,0 +1,112 @@
+//===- FunctionTest.cpp - Function unit tests -----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/Function.h"
+#include "gtest/gtest.h"
+using namespace llvm;
+
+namespace {
+
+TEST(FunctionTest, hasLazyArguments) {
+ LLVMContext C;
+
+ Type *ArgTypes[] = {Type::getInt8Ty(C), Type::getInt32Ty(C)};
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), ArgTypes, false);
+
+ // Functions start out with lazy arguments.
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+ EXPECT_TRUE(F->hasLazyArguments());
+
+ // Checking for empty or size shouldn't force arguments to be instantiated.
+ EXPECT_FALSE(F->arg_empty());
+ EXPECT_TRUE(F->hasLazyArguments());
+ EXPECT_EQ(2u, F->arg_size());
+ EXPECT_TRUE(F->hasLazyArguments());
+
+ // The argument list should be populated at first access.
+ (void)F->arg_begin();
+ EXPECT_FALSE(F->hasLazyArguments());
+}
+
+TEST(FunctionTest, stealArgumentListFrom) {
+ LLVMContext C;
+
+ Type *ArgTypes[] = {Type::getInt8Ty(C), Type::getInt32Ty(C)};
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), ArgTypes, false);
+ std::unique_ptr<Function> F1(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F1"));
+ std::unique_ptr<Function> F2(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F1"));
+ EXPECT_TRUE(F1->hasLazyArguments());
+ EXPECT_TRUE(F2->hasLazyArguments());
+
+ // Steal arguments before they've been accessed. Nothing should change; both
+ // functions should still have lazy arguments.
+ //
+ // steal(empty); drop (empty)
+ F1->stealArgumentListFrom(*F2);
+ EXPECT_TRUE(F1->hasLazyArguments());
+ EXPECT_TRUE(F2->hasLazyArguments());
+
+ // Save arguments from F1 for later assertions. F1 won't have lazy arguments
+ // anymore.
+ SmallVector<Argument *, 4> Args;
+ for (Argument &A : F1->args())
+ Args.push_back(&A);
+ EXPECT_EQ(2u, Args.size());
+ EXPECT_FALSE(F1->hasLazyArguments());
+
+ // Steal arguments from F1 to F2. F1's arguments should be lazy again.
+ //
+ // steal(real); drop (empty)
+ F2->stealArgumentListFrom(*F1);
+ EXPECT_TRUE(F1->hasLazyArguments());
+ EXPECT_FALSE(F2->hasLazyArguments());
+ unsigned I = 0;
+ for (Argument &A : F2->args()) {
+ EXPECT_EQ(Args[I], &A);
+ I++;
+ }
+ EXPECT_EQ(2u, I);
+
+ // Check that arguments in F1 don't have pointer equality with the saved ones.
+ // This also instantiates F1's arguments.
+ I = 0;
+ for (Argument &A : F1->args()) {
+ EXPECT_NE(Args[I], &A);
+ I++;
+ }
+ EXPECT_EQ(2u, I);
+ EXPECT_FALSE(F1->hasLazyArguments());
+ EXPECT_FALSE(F2->hasLazyArguments());
+
+ // Steal back from F2. F2's arguments should be lazy again.
+ //
+ // steal(real); drop (real)
+ F1->stealArgumentListFrom(*F2);
+ EXPECT_FALSE(F1->hasLazyArguments());
+ EXPECT_TRUE(F2->hasLazyArguments());
+ I = 0;
+ for (Argument &A : F1->args()) {
+ EXPECT_EQ(Args[I], &A);
+ I++;
+ }
+ EXPECT_EQ(2u, I);
+
+ // Steal from F2 a second time. Now both functions should have lazy
+ // arguments.
+ //
+ // steal(empty); drop (real)
+ F1->stealArgumentListFrom(*F2);
+ EXPECT_TRUE(F1->hasLazyArguments());
+ EXPECT_TRUE(F2->hasLazyArguments());
+}
+
+} // end namespace
diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp
index bd0eae0399a4..58fd71b8a357 100644
--- a/unittests/IR/IRBuilderTest.cpp
+++ b/unittests/IR/IRBuilderTest.cpp
@@ -252,7 +252,7 @@ TEST_F(IRBuilderTest, FastMathFlags) {
}
TEST_F(IRBuilderTest, WrapFlags) {
- IRBuilder<true, NoFolder> Builder(BB);
+ IRBuilder<NoFolder> Builder(BB);
// Test instructions.
GlobalVariable *G = new GlobalVariable(*M, Builder.getInt32Ty(), true,
@@ -418,4 +418,18 @@ TEST_F(IRBuilderTest, DebugLoc) {
DIB.finalize();
}
+
+TEST_F(IRBuilderTest, DIImportedEntity) {
+ IRBuilder<> Builder(BB);
+ DIBuilder DIB(*M);
+ auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74, "F.CBL", "/",
+ "llvm-cobol74", true, "", 0);
+ DIB.createImportedDeclaration(CU, nullptr, 1);
+ DIB.createImportedDeclaration(CU, nullptr, 1);
+ DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, 2);
+ DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, 2);
+ DIB.finalize();
+ EXPECT_TRUE(verifyModule(*M));
+ EXPECT_TRUE(CU->getImportedEntities().size() == 2);
+}
}
diff --git a/unittests/IR/InstructionsTest.cpp b/unittests/IR/InstructionsTest.cpp
index 3ca3ad2b6e83..0dac7c1bcfb1 100644
--- a/unittests/IR/InstructionsTest.cpp
+++ b/unittests/IR/InstructionsTest.cpp
@@ -27,7 +27,7 @@ namespace llvm {
namespace {
TEST(InstructionsTest, ReturnInst) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
// test for PR6589
const ReturnInst* r0 = ReturnInst::Create(C);
@@ -103,7 +103,7 @@ TEST_F(ModuleWithFunctionTest, InvokeInst) {
}
TEST(InstructionsTest, BranchInst) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
// Make a BasicBlocks
BasicBlock* bb0 = BasicBlock::Create(C);
@@ -169,7 +169,7 @@ TEST(InstructionsTest, BranchInst) {
}
TEST(InstructionsTest, CastInst) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Type *Int8Ty = Type::getInt8Ty(C);
Type *Int16Ty = Type::getInt16Ty(C);
@@ -281,14 +281,18 @@ TEST(InstructionsTest, CastInst) {
// First form
BasicBlock *BB = BasicBlock::Create(C);
Constant *NullV2I32Ptr = Constant::getNullValue(V2Int32PtrTy);
- CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty, "foo", BB);
+ auto Inst1 = CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty, "foo", BB);
// Second form
- CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty);
+ auto Inst2 = CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty);
+
+ delete Inst2;
+ Inst1->eraseFromParent();
+ delete BB;
}
TEST(InstructionsTest, VectorGep) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
// Type Definitions
Type *I8Ty = IntegerType::get(C, 8);
@@ -391,7 +395,7 @@ TEST(InstructionsTest, VectorGep) {
}
TEST(InstructionsTest, FPMathOperator) {
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
IRBuilder<> Builder(Context);
MDBuilder MDHelper(Context);
Instruction *I = Builder.CreatePHI(Builder.getDoubleTy(), 0);
@@ -406,7 +410,7 @@ TEST(InstructionsTest, FPMathOperator) {
TEST(InstructionsTest, isEliminableCastPair) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Type* Int16Ty = Type::getInt16Ty(C);
Type* Int32Ty = Type::getInt32Ty(C);
@@ -486,7 +490,7 @@ TEST(InstructionsTest, isEliminableCastPair) {
}
TEST(InstructionsTest, CloneCall) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Type *Int32Ty = Type::getInt32Ty(C);
Type *ArgTys[] = {Int32Ty, Int32Ty, Int32Ty};
Type *FnTy = FunctionType::get(Int32Ty, ArgTys, /*isVarArg=*/false);
@@ -518,7 +522,62 @@ TEST(InstructionsTest, CloneCall) {
}
}
-} // end anonymous namespace
-} // end namespace llvm
+TEST(InstructionsTest, AlterCallBundles) {
+ LLVMContext C;
+ Type *Int32Ty = Type::getInt32Ty(C);
+ Type *FnTy = FunctionType::get(Int32Ty, Int32Ty, /*isVarArg=*/false);
+ Value *Callee = Constant::getNullValue(FnTy->getPointerTo());
+ Value *Args[] = {ConstantInt::get(Int32Ty, 42)};
+ OperandBundleDef OldBundle("before", UndefValue::get(Int32Ty));
+ std::unique_ptr<CallInst> Call(
+ CallInst::Create(Callee, Args, OldBundle, "result"));
+ Call->setTailCallKind(CallInst::TailCallKind::TCK_NoTail);
+ AttrBuilder AB;
+ AB.addAttribute(Attribute::Cold);
+ Call->setAttributes(AttributeSet::get(C, AttributeSet::FunctionIndex, AB));
+ Call->setDebugLoc(DebugLoc(MDNode::get(C, None)));
+
+ OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7));
+ std::unique_ptr<CallInst> Clone(CallInst::Create(Call.get(), NewBundle));
+ EXPECT_EQ(Call->getNumArgOperands(), Clone->getNumArgOperands());
+ EXPECT_EQ(Call->getArgOperand(0), Clone->getArgOperand(0));
+ EXPECT_EQ(Call->getCallingConv(), Clone->getCallingConv());
+ EXPECT_EQ(Call->getTailCallKind(), Clone->getTailCallKind());
+ EXPECT_TRUE(Clone->hasFnAttr(Attribute::AttrKind::Cold));
+ EXPECT_EQ(Call->getDebugLoc(), Clone->getDebugLoc());
+ EXPECT_EQ(Clone->getNumOperandBundles(), 1U);
+ EXPECT_TRUE(Clone->getOperandBundle("after").hasValue());
+}
+TEST(InstructionsTest, AlterInvokeBundles) {
+ LLVMContext C;
+ Type *Int32Ty = Type::getInt32Ty(C);
+ Type *FnTy = FunctionType::get(Int32Ty, Int32Ty, /*isVarArg=*/false);
+ Value *Callee = Constant::getNullValue(FnTy->getPointerTo());
+ Value *Args[] = {ConstantInt::get(Int32Ty, 42)};
+ std::unique_ptr<BasicBlock> NormalDest(BasicBlock::Create(C));
+ std::unique_ptr<BasicBlock> UnwindDest(BasicBlock::Create(C));
+ OperandBundleDef OldBundle("before", UndefValue::get(Int32Ty));
+ std::unique_ptr<InvokeInst> Invoke(InvokeInst::Create(
+ Callee, NormalDest.get(), UnwindDest.get(), Args, OldBundle, "result"));
+ AttrBuilder AB;
+ AB.addAttribute(Attribute::Cold);
+ Invoke->setAttributes(AttributeSet::get(C, AttributeSet::FunctionIndex, AB));
+ Invoke->setDebugLoc(DebugLoc(MDNode::get(C, None)));
+
+ OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7));
+ std::unique_ptr<InvokeInst> Clone(
+ InvokeInst::Create(Invoke.get(), NewBundle));
+ EXPECT_EQ(Invoke->getNormalDest(), Clone->getNormalDest());
+ EXPECT_EQ(Invoke->getUnwindDest(), Clone->getUnwindDest());
+ EXPECT_EQ(Invoke->getNumArgOperands(), Clone->getNumArgOperands());
+ EXPECT_EQ(Invoke->getArgOperand(0), Clone->getArgOperand(0));
+ EXPECT_EQ(Invoke->getCallingConv(), Clone->getCallingConv());
+ EXPECT_TRUE(Clone->hasFnAttr(Attribute::AttrKind::Cold));
+ EXPECT_EQ(Invoke->getDebugLoc(), Clone->getDebugLoc());
+ EXPECT_EQ(Clone->getNumOperandBundles(), 1U);
+ EXPECT_TRUE(Clone->getOperandBundle("after").hasValue());
+}
+} // end anonymous namespace
+} // end namespace llvm
diff --git a/unittests/IR/IntrinsicsTest.cpp b/unittests/IR/IntrinsicsTest.cpp
new file mode 100644
index 000000000000..0d12126b1fcf
--- /dev/null
+++ b/unittests/IR/IntrinsicsTest.cpp
@@ -0,0 +1,40 @@
+//===- llvm/unittest/IR/IntrinsicsTest.cpp - ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/IntrinsicInst.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+static const char *const NameTable1[] = {
+ "llvm.foo",
+ "llvm.foo.a",
+ "llvm.foo.b",
+ "llvm.foo.b.a",
+ "llvm.foo.c",
+};
+
+TEST(IntrinNameLookup, Basic) {
+ int I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo");
+ EXPECT_EQ(0, I);
+ I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.f64");
+ EXPECT_EQ(0, I);
+ I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.b");
+ EXPECT_EQ(2, I);
+ I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.b.a");
+ EXPECT_EQ(3, I);
+ I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.c");
+ EXPECT_EQ(4, I);
+ I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.c.f64");
+ EXPECT_EQ(4, I);
+}
+
+} // end namespace
diff --git a/unittests/IR/LegacyPassManagerTest.cpp b/unittests/IR/LegacyPassManagerTest.cpp
index 1f88283dc0ce..9dceb976c937 100644
--- a/unittests/IR/LegacyPassManagerTest.cpp
+++ b/unittests/IR/LegacyPassManagerTest.cpp
@@ -14,7 +14,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/LegacyPassManager.h"
-#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/CallGraphSCCPass.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
@@ -288,7 +287,8 @@ namespace llvm {
char OnTheFlyTest::ID=0;
TEST(PassManager, RunOnce) {
- Module M("test-once", getGlobalContext());
+ LLVMContext Context;
+ Module M("test-once", Context);
struct ModuleNDNM *mNDNM = new ModuleNDNM();
struct ModuleDNM *mDNM = new ModuleDNM();
struct ModuleNDM *mNDM = new ModuleNDM();
@@ -311,7 +311,8 @@ namespace llvm {
}
TEST(PassManager, ReRun) {
- Module M("test-rerun", getGlobalContext());
+ LLVMContext Context;
+ Module M("test-rerun", Context);
struct ModuleNDNM *mNDNM = new ModuleNDNM();
struct ModuleDNM *mDNM = new ModuleDNM();
struct ModuleNDM *mNDM = new ModuleNDM();
@@ -334,11 +335,12 @@ namespace llvm {
EXPECT_EQ(1, mDNM->run);
}
- Module* makeLLVMModule();
+ Module *makeLLVMModule(LLVMContext &Context);
template<typename T>
void MemoryTestHelper(int run) {
- std::unique_ptr<Module> M(makeLLVMModule());
+ LLVMContext Context;
+ std::unique_ptr<Module> M(makeLLVMModule(Context));
T *P = new T();
legacy::PassManager Passes;
Passes.add(P);
@@ -348,7 +350,8 @@ namespace llvm {
template<typename T>
void MemoryTestHelper(int run, int N) {
- Module *M = makeLLVMModule();
+ LLVMContext Context;
+ Module *M = makeLLVMModule(Context);
T *P = new T();
legacy::PassManager Passes;
Passes.add(P);
@@ -383,7 +386,8 @@ namespace llvm {
}
TEST(PassManager, MemoryOnTheFly) {
- Module *M = makeLLVMModule();
+ LLVMContext Context;
+ Module *M = makeLLVMModule(Context);
{
SCOPED_TRACE("Running OnTheFlyTest");
struct OnTheFlyTest *O = new OnTheFlyTest();
@@ -396,9 +400,9 @@ namespace llvm {
delete M;
}
- Module* makeLLVMModule() {
+ Module *makeLLVMModule(LLVMContext &Context) {
// Module Construction
- Module* mod = new Module("test-mem", getGlobalContext());
+ Module *mod = new Module("test-mem", Context);
mod->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-"
"i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-"
"a:0:64-s:64:64-f80:128:128");
@@ -406,18 +410,17 @@ namespace llvm {
// Type Definitions
std::vector<Type*>FuncTy_0_args;
- FunctionType* FuncTy_0 = FunctionType::get(
- /*Result=*/IntegerType::get(getGlobalContext(), 32),
- /*Params=*/FuncTy_0_args,
- /*isVarArg=*/false);
+ FunctionType *FuncTy_0 = FunctionType::get(
+ /*Result=*/IntegerType::get(Context, 32),
+ /*Params=*/FuncTy_0_args,
+ /*isVarArg=*/false);
std::vector<Type*>FuncTy_2_args;
- FuncTy_2_args.push_back(IntegerType::get(getGlobalContext(), 1));
- FunctionType* FuncTy_2 = FunctionType::get(
- /*Result=*/Type::getVoidTy(getGlobalContext()),
- /*Params=*/FuncTy_2_args,
- /*isVarArg=*/false);
-
+ FuncTy_2_args.push_back(IntegerType::get(Context, 1));
+ FunctionType *FuncTy_2 = FunctionType::get(
+ /*Result=*/Type::getVoidTy(Context),
+ /*Params=*/FuncTy_2_args,
+ /*isVarArg=*/false);
// Function Declarations
@@ -465,7 +468,8 @@ namespace llvm {
// Function: test1 (func_test1)
{
- BasicBlock* label_entry = BasicBlock::Create(getGlobalContext(), "entry",func_test1,nullptr);
+ BasicBlock *label_entry =
+ BasicBlock::Create(Context, "entry", func_test1, nullptr);
// Block entry (label_entry)
CallInst* int32_3 = CallInst::Create(func_test2, "", label_entry);
@@ -473,14 +477,14 @@ namespace llvm {
int32_3->setTailCall(false);AttributeSet int32_3_PAL;
int32_3->setAttributes(int32_3_PAL);
- ReturnInst::Create(getGlobalContext(), int32_3, label_entry);
-
+ ReturnInst::Create(Context, int32_3, label_entry);
}
// Function: test2 (func_test2)
{
- BasicBlock* label_entry_5 = BasicBlock::Create(getGlobalContext(), "entry",func_test2,nullptr);
+ BasicBlock *label_entry_5 =
+ BasicBlock::Create(Context, "entry", func_test2, nullptr);
// Block entry (label_entry_5)
CallInst* int32_6 = CallInst::Create(func_test3, "", label_entry_5);
@@ -488,14 +492,14 @@ namespace llvm {
int32_6->setTailCall(false);AttributeSet int32_6_PAL;
int32_6->setAttributes(int32_6_PAL);
- ReturnInst::Create(getGlobalContext(), int32_6, label_entry_5);
-
+ ReturnInst::Create(Context, int32_6, label_entry_5);
}
// Function: test3 (func_test3)
{
- BasicBlock* label_entry_8 = BasicBlock::Create(getGlobalContext(), "entry",func_test3,nullptr);
+ BasicBlock *label_entry_8 =
+ BasicBlock::Create(Context, "entry", func_test3, nullptr);
// Block entry (label_entry_8)
CallInst* int32_9 = CallInst::Create(func_test1, "", label_entry_8);
@@ -503,8 +507,7 @@ namespace llvm {
int32_9->setTailCall(false);AttributeSet int32_9_PAL;
int32_9->setAttributes(int32_9_PAL);
- ReturnInst::Create(getGlobalContext(), int32_9, label_entry_8);
-
+ ReturnInst::Create(Context, int32_9, label_entry_8);
}
// Function: test4 (func_test4)
@@ -513,10 +516,14 @@ namespace llvm {
Value *int1_f = &*args++;
int1_f->setName("f");
- BasicBlock* label_entry_11 = BasicBlock::Create(getGlobalContext(), "entry",func_test4,nullptr);
- BasicBlock* label_bb = BasicBlock::Create(getGlobalContext(), "bb",func_test4,nullptr);
- BasicBlock* label_bb1 = BasicBlock::Create(getGlobalContext(), "bb1",func_test4,nullptr);
- BasicBlock* label_return = BasicBlock::Create(getGlobalContext(), "return",func_test4,nullptr);
+ BasicBlock *label_entry_11 =
+ BasicBlock::Create(Context, "entry", func_test4, nullptr);
+ BasicBlock *label_bb =
+ BasicBlock::Create(Context, "bb", func_test4, nullptr);
+ BasicBlock *label_bb1 =
+ BasicBlock::Create(Context, "bb1", func_test4, nullptr);
+ BasicBlock *label_return =
+ BasicBlock::Create(Context, "return", func_test4, nullptr);
// Block entry (label_entry_11)
BranchInst::Create(label_bb, label_entry_11);
@@ -528,8 +535,7 @@ namespace llvm {
BranchInst::Create(label_bb1, label_return, int1_f, label_bb1);
// Block return (label_return)
- ReturnInst::Create(getGlobalContext(), label_return);
-
+ ReturnInst::Create(Context, label_return);
}
return mod;
}
diff --git a/unittests/IR/Makefile b/unittests/IR/Makefile
deleted file mode 100644
index 45aa8d68082e..000000000000
--- a/unittests/IR/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/IR/Makefile -------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = IR
-LINK_COMPONENTS := core analysis asmparser
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/IR/MetadataTest.cpp b/unittests/IR/MetadataTest.cpp
index a745b235a381..77a2dbaf2dfa 100644
--- a/unittests/IR/MetadataTest.cpp
+++ b/unittests/IR/MetadataTest.cpp
@@ -80,26 +80,29 @@ protected:
MDTuple *getTuple() { return MDTuple::getDistinct(Context, None); }
DISubroutineType *getSubroutineType() {
- return DISubroutineType::getDistinct(Context, 0, getNode(nullptr));
+ return DISubroutineType::getDistinct(Context, 0, 0, getNode(nullptr));
}
DISubprogram *getSubprogram() {
return DISubprogram::getDistinct(Context, nullptr, "", "", nullptr, 0,
- nullptr, false, false, 0, nullptr, 0, 0, 0,
- 0);
+ nullptr, false, false, 0, nullptr,
+ 0, 0, 0, 0, false, nullptr);
}
- DIScopeRef getSubprogramRef() { return getSubprogram()->getRef(); }
DIFile *getFile() {
return DIFile::getDistinct(Context, "file.c", "/path/to/dir");
}
- DITypeRef getBasicType(StringRef Name) {
- return DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type, Name)
- ->getRef();
+ DICompileUnit *getUnit() {
+ return DICompileUnit::getDistinct(Context, 1, getFile(), "clang", false,
+ "-g", 2, "", DICompileUnit::FullDebug,
+ getTuple(), getTuple(), getTuple(),
+ getTuple(), getTuple(), 0);
}
- DITypeRef getDerivedType() {
+ DIType *getBasicType(StringRef Name) {
+ return DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type, Name);
+ }
+ DIType *getDerivedType() {
return DIDerivedType::getDistinct(Context, dwarf::DW_TAG_pointer_type, "",
nullptr, 0, nullptr,
- getBasicType("basictype"), 1, 2, 0, 0)
- ->getRef();
+ getBasicType("basictype"), 1, 2, 0, 0);
}
Constant *getConstant() {
return ConstantInt::get(Type::getInt32Ty(Context), Counter++);
@@ -107,11 +110,10 @@ protected:
ConstantAsMetadata *getConstantAsMetadata() {
return ConstantAsMetadata::get(getConstant());
}
- DITypeRef getCompositeType() {
+ DIType *getCompositeType() {
return DICompositeType::getDistinct(
- Context, dwarf::DW_TAG_structure_type, "", nullptr, 0, nullptr,
- nullptr, 32, 32, 0, 0, nullptr, 0, nullptr, nullptr, "")
- ->getRef();
+ Context, dwarf::DW_TAG_structure_type, "", nullptr, 0, nullptr, nullptr,
+ 32, 32, 0, 0, nullptr, 0, nullptr, nullptr, "");
}
Function *getFunction(StringRef Name) {
return cast<Function>(M.getOrInsertFunction(
@@ -174,8 +176,8 @@ TEST_F(MDNodeTest, Simple) {
MDString *s1 = MDString::get(Context, StringRef(&x[0], 3));
MDString *s2 = MDString::get(Context, StringRef(&y[0], 3));
- ConstantAsMetadata *CI = ConstantAsMetadata::get(
- ConstantInt::get(getGlobalContext(), APInt(8, 0)));
+ ConstantAsMetadata *CI =
+ ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));
std::vector<Metadata *> V;
V.push_back(s1);
@@ -206,8 +208,8 @@ TEST_F(MDNodeTest, Simple) {
}
TEST_F(MDNodeTest, Delete) {
- Constant *C = ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 1);
- Instruction *I = new BitCastInst(C, Type::getInt32Ty(getGlobalContext()));
+ Constant *C = ConstantInt::get(Type::getInt32Ty(Context), 1);
+ Instruction *I = new BitCastInst(C, Type::getInt32Ty(Context));
Metadata *const V = LocalAsMetadata::get(I);
MDNode *n = MDNode::get(Context, V);
@@ -494,20 +496,6 @@ TEST_F(MDNodeTest, isTemporary) {
EXPECT_TRUE(T->isTemporary());
}
-#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
-
-TEST_F(MDNodeTest, deathOnNoReplaceTemporaryRAUW) {
- auto Temp = MDNode::getTemporary(Context, None);
- Temp->setCanReplace(false);
- EXPECT_DEATH(Temp->replaceAllUsesWith(nullptr),
- "Attempted to replace Metadata marked for no replacement");
- Temp->setCanReplace(true);
- // Remove the references to Temp; required for teardown.
- Temp->replaceAllUsesWith(nullptr);
-}
-
-#endif
-
TEST_F(MDNodeTest, getDistinctWithUnresolvedOperands) {
// temporary !{}
auto Temp = MDTuple::getTemporary(Context, None);
@@ -981,14 +969,14 @@ TEST_F(DITypeTest, setFlags) {
Metadata *TypesOps[] = {nullptr};
Metadata *Types = MDTuple::get(Context, TypesOps);
- DIType *D = DISubroutineType::getDistinct(Context, 0u, Types);
+ DIType *D = DISubroutineType::getDistinct(Context, 0u, 0, Types);
EXPECT_EQ(0u, D->getFlags());
D->setFlags(DINode::FlagRValueReference);
EXPECT_EQ(DINode::FlagRValueReference, D->getFlags());
D->setFlags(0u);
EXPECT_EQ(0u, D->getFlags());
- TempDIType T = DISubroutineType::getTemporary(Context, 0u, Types);
+ TempDIType T = DISubroutineType::getTemporary(Context, 0u, 0, Types);
EXPECT_EQ(0u, T->getFlags());
T->setFlags(DINode::FlagRValueReference);
EXPECT_EQ(DINode::FlagRValueReference, T->getFlags());
@@ -1000,8 +988,8 @@ typedef MetadataTest DIDerivedTypeTest;
TEST_F(DIDerivedTypeTest, get) {
DIFile *File = getFile();
- DIScopeRef Scope = getSubprogramRef();
- DITypeRef BaseType = getBasicType("basic");
+ DIScope *Scope = getSubprogram();
+ DIType *BaseType = getBasicType("basic");
MDTuple *ExtraData = getTuple();
auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something",
@@ -1034,7 +1022,7 @@ TEST_F(DIDerivedTypeTest, get) {
"something", File, 2, Scope, BaseType, 2, 3,
4, 5, ExtraData));
EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,
- "something", File, 1, getSubprogramRef(),
+ "something", File, 1, getSubprogram(),
BaseType, 2, 3, 4, 5, ExtraData));
EXPECT_NE(N, DIDerivedType::get(
Context, dwarf::DW_TAG_pointer_type, "something", File, 1,
@@ -1061,8 +1049,8 @@ TEST_F(DIDerivedTypeTest, get) {
TEST_F(DIDerivedTypeTest, getWithLargeValues) {
DIFile *File = getFile();
- DIScopeRef Scope = getSubprogramRef();
- DITypeRef BaseType = getBasicType("basic");
+ DIScope *Scope = getSubprogram();
+ DIType *BaseType = getBasicType("basic");
MDTuple *ExtraData = getTuple();
auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something",
@@ -1080,15 +1068,15 @@ TEST_F(DICompositeTypeTest, get) {
StringRef Name = "some name";
DIFile *File = getFile();
unsigned Line = 1;
- DIScopeRef Scope = getSubprogramRef();
- DITypeRef BaseType = getCompositeType();
+ DIScope *Scope = getSubprogram();
+ DIType *BaseType = getCompositeType();
uint64_t SizeInBits = 2;
uint64_t AlignInBits = 3;
uint64_t OffsetInBits = 4;
unsigned Flags = 5;
MDTuple *Elements = getTuple();
unsigned RuntimeLang = 6;
- DITypeRef VTableHolder = getCompositeType();
+ DIType *VTableHolder = getCompositeType();
MDTuple *TemplateParams = getTuple();
StringRef Identifier = "some id";
@@ -1134,7 +1122,7 @@ TEST_F(DICompositeTypeTest, get) {
OffsetInBits, Flags, Elements, RuntimeLang,
VTableHolder, TemplateParams, Identifier));
EXPECT_NE(N, DICompositeType::get(
- Context, Tag, Name, File, Line, getSubprogramRef(), BaseType,
+ Context, Tag, Name, File, Line, getSubprogram(), BaseType,
SizeInBits, AlignInBits, OffsetInBits, Flags, Elements,
RuntimeLang, VTableHolder, TemplateParams, Identifier));
EXPECT_NE(N, DICompositeType::get(
@@ -1199,15 +1187,15 @@ TEST_F(DICompositeTypeTest, getWithLargeValues) {
StringRef Name = "some name";
DIFile *File = getFile();
unsigned Line = 1;
- DIScopeRef Scope = getSubprogramRef();
- DITypeRef BaseType = getCompositeType();
+ DIScope *Scope = getSubprogram();
+ DIType *BaseType = getCompositeType();
uint64_t SizeInBits = UINT64_MAX;
uint64_t AlignInBits = UINT64_MAX - 1;
uint64_t OffsetInBits = UINT64_MAX - 2;
unsigned Flags = 5;
MDTuple *Elements = getTuple();
unsigned RuntimeLang = 6;
- DITypeRef VTableHolder = getCompositeType();
+ DIType *VTableHolder = getCompositeType();
MDTuple *TemplateParams = getTuple();
StringRef Identifier = "some id";
@@ -1225,8 +1213,8 @@ TEST_F(DICompositeTypeTest, replaceOperands) {
StringRef Name = "some name";
DIFile *File = getFile();
unsigned Line = 1;
- DIScopeRef Scope = getSubprogramRef();
- DITypeRef BaseType = getCompositeType();
+ DIScope *Scope = getSubprogram();
+ DIType *BaseType = getCompositeType();
uint64_t SizeInBits = 2;
uint64_t AlignInBits = 3;
uint64_t OffsetInBits = 4;
@@ -1245,7 +1233,7 @@ TEST_F(DICompositeTypeTest, replaceOperands) {
N->replaceElements(nullptr);
EXPECT_EQ(nullptr, N->getElements().get());
- DITypeRef VTableHolder = getCompositeType();
+ DIType *VTableHolder = getCompositeType();
EXPECT_EQ(nullptr, N->getVTableHolder());
N->replaceVTableHolder(VTableHolder);
EXPECT_EQ(VTableHolder, N->getVTableHolder());
@@ -1266,14 +1254,29 @@ TEST_F(DISubroutineTypeTest, get) {
unsigned Flags = 1;
MDTuple *TypeArray = getTuple();
- auto *N = DISubroutineType::get(Context, Flags, TypeArray);
+ auto *N = DISubroutineType::get(Context, Flags, 0, TypeArray);
EXPECT_EQ(dwarf::DW_TAG_subroutine_type, N->getTag());
EXPECT_EQ(Flags, N->getFlags());
EXPECT_EQ(TypeArray, N->getTypeArray().get());
- EXPECT_EQ(N, DISubroutineType::get(Context, Flags, TypeArray));
-
- EXPECT_NE(N, DISubroutineType::get(Context, Flags + 1, TypeArray));
- EXPECT_NE(N, DISubroutineType::get(Context, Flags, getTuple()));
+ EXPECT_EQ(N, DISubroutineType::get(Context, Flags, 0, TypeArray));
+
+ EXPECT_NE(N, DISubroutineType::get(Context, Flags + 1, 0, TypeArray));
+ EXPECT_NE(N, DISubroutineType::get(Context, Flags, 0, getTuple()));
+
+ // Test the hashing of calling conventions.
+ auto *Fast = DISubroutineType::get(
+ Context, Flags, dwarf::DW_CC_BORLAND_msfastcall, TypeArray);
+ auto *Std = DISubroutineType::get(Context, Flags,
+ dwarf::DW_CC_BORLAND_stdcall, TypeArray);
+ EXPECT_EQ(Fast,
+ DISubroutineType::get(Context, Flags,
+ dwarf::DW_CC_BORLAND_msfastcall, TypeArray));
+ EXPECT_EQ(Std, DISubroutineType::get(
+ Context, Flags, dwarf::DW_CC_BORLAND_stdcall, TypeArray));
+
+ EXPECT_NE(N, Fast);
+ EXPECT_NE(N, Std);
+ EXPECT_NE(Fast, Std);
TempDISubroutineType Temp = N->clone();
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
@@ -1319,10 +1322,9 @@ TEST_F(DICompileUnitTest, get) {
StringRef Flags = "flag after flag";
unsigned RuntimeVersion = 2;
StringRef SplitDebugFilename = "another/file";
- unsigned EmissionKind = 3;
+ auto EmissionKind = DICompileUnit::FullDebug;
MDTuple *EnumTypes = getTuple();
MDTuple *RetainedTypes = getTuple();
- MDTuple *Subprograms = getTuple();
MDTuple *GlobalVariables = getTuple();
MDTuple *ImportedEntities = getTuple();
uint64_t DWOId = 0x10000000c0ffee;
@@ -1330,7 +1332,7 @@ TEST_F(DICompileUnitTest, get) {
auto *N = DICompileUnit::getDistinct(
Context, SourceLanguage, File, Producer, IsOptimized, Flags,
RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes,
- RetainedTypes, Subprograms, GlobalVariables, ImportedEntities, Macros,
+ RetainedTypes, GlobalVariables, ImportedEntities, Macros,
DWOId);
EXPECT_EQ(dwarf::DW_TAG_compile_unit, N->getTag());
@@ -1344,7 +1346,6 @@ TEST_F(DICompileUnitTest, get) {
EXPECT_EQ(EmissionKind, N->getEmissionKind());
EXPECT_EQ(EnumTypes, N->getEnumTypes().get());
EXPECT_EQ(RetainedTypes, N->getRetainedTypes().get());
- EXPECT_EQ(Subprograms, N->getSubprograms().get());
EXPECT_EQ(GlobalVariables, N->getGlobalVariables().get());
EXPECT_EQ(ImportedEntities, N->getImportedEntities().get());
EXPECT_EQ(Macros, N->getMacros().get());
@@ -1362,7 +1363,6 @@ TEST_F(DICompileUnitTest, get) {
EXPECT_EQ(EmissionKind, Temp->getEmissionKind());
EXPECT_EQ(EnumTypes, Temp->getEnumTypes().get());
EXPECT_EQ(RetainedTypes, Temp->getRetainedTypes().get());
- EXPECT_EQ(Subprograms, Temp->getSubprograms().get());
EXPECT_EQ(GlobalVariables, Temp->getGlobalVariables().get());
EXPECT_EQ(ImportedEntities, Temp->getImportedEntities().get());
EXPECT_EQ(Macros, Temp->getMacros().get());
@@ -1382,7 +1382,7 @@ TEST_F(DICompileUnitTest, replaceArrays) {
StringRef Flags = "flag after flag";
unsigned RuntimeVersion = 2;
StringRef SplitDebugFilename = "another/file";
- unsigned EmissionKind = 3;
+ auto EmissionKind = DICompileUnit::FullDebug;
MDTuple *EnumTypes = MDTuple::getDistinct(Context, None);
MDTuple *RetainedTypes = MDTuple::getDistinct(Context, None);
MDTuple *ImportedEntities = MDTuple::getDistinct(Context, None);
@@ -1390,14 +1390,7 @@ TEST_F(DICompileUnitTest, replaceArrays) {
auto *N = DICompileUnit::getDistinct(
Context, SourceLanguage, File, Producer, IsOptimized, Flags,
RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes,
- RetainedTypes, nullptr, nullptr, ImportedEntities, nullptr, DWOId);
-
- auto *Subprograms = MDTuple::getDistinct(Context, None);
- EXPECT_EQ(nullptr, N->getSubprograms().get());
- N->replaceSubprograms(Subprograms);
- EXPECT_EQ(Subprograms, N->getSubprograms().get());
- N->replaceSubprograms(nullptr);
- EXPECT_EQ(nullptr, N->getSubprograms().get());
+ RetainedTypes, nullptr, ImportedEntities, nullptr, DWOId);
auto *GlobalVariables = MDTuple::getDistinct(Context, None);
EXPECT_EQ(nullptr, N->getGlobalVariables().get());
@@ -1417,7 +1410,7 @@ TEST_F(DICompileUnitTest, replaceArrays) {
typedef MetadataTest DISubprogramTest;
TEST_F(DISubprogramTest, get) {
- DIScopeRef Scope = getCompositeType();
+ DIScope *Scope = getCompositeType();
StringRef Name = "name";
StringRef LinkageName = "linkage";
DIFile *File = getFile();
@@ -1426,19 +1419,23 @@ TEST_F(DISubprogramTest, get) {
bool IsLocalToUnit = false;
bool IsDefinition = true;
unsigned ScopeLine = 3;
- DITypeRef ContainingType = getCompositeType();
- unsigned Virtuality = 4;
+ DIType *ContainingType = getCompositeType();
+ unsigned Virtuality = 2;
unsigned VirtualIndex = 5;
+ int ThisAdjustment = -3;
unsigned Flags = 6;
+ unsigned NotFlags = (~Flags) & ((1 << 27) - 1);
bool IsOptimized = false;
MDTuple *TemplateParams = getTuple();
DISubprogram *Declaration = getSubprogram();
MDTuple *Variables = getTuple();
+ DICompileUnit *Unit = getUnit();
- auto *N = DISubprogram::get(
- Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit,
- IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex, Flags,
- IsOptimized, TemplateParams, Declaration, Variables);
+ auto *N = DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
+ Type, IsLocalToUnit, IsDefinition, ScopeLine,
+ ContainingType, Virtuality, VirtualIndex,
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ TemplateParams, Declaration, Variables);
EXPECT_EQ(dwarf::DW_TAG_subprogram, N->getTag());
EXPECT_EQ(Scope, N->getScope());
@@ -1453,102 +1450,110 @@ TEST_F(DISubprogramTest, get) {
EXPECT_EQ(ContainingType, N->getContainingType());
EXPECT_EQ(Virtuality, N->getVirtuality());
EXPECT_EQ(VirtualIndex, N->getVirtualIndex());
+ EXPECT_EQ(ThisAdjustment, N->getThisAdjustment());
EXPECT_EQ(Flags, N->getFlags());
EXPECT_EQ(IsOptimized, N->isOptimized());
+ EXPECT_EQ(Unit, N->getUnit());
EXPECT_EQ(TemplateParams, N->getTemplateParams().get());
EXPECT_EQ(Declaration, N->getDeclaration());
EXPECT_EQ(Variables, N->getVariables().get());
EXPECT_EQ(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
-
- EXPECT_NE(N, DISubprogram::get(Context, getCompositeType(), Name, LinkageName,
- File, Line, Type, IsLocalToUnit, IsDefinition,
- ScopeLine, ContainingType, Virtuality,
- VirtualIndex, Flags, IsOptimized,
- TemplateParams, Declaration, Variables));
- EXPECT_NE(N, DISubprogram::get(Context, Scope, "other", LinkageName, File,
- Line, Type, IsLocalToUnit, IsDefinition,
- ScopeLine, ContainingType, Virtuality,
- VirtualIndex, Flags, IsOptimized,
+ ThisAdjustment, Flags, IsOptimized, Unit,
TemplateParams, Declaration, Variables));
+
+ EXPECT_NE(N, DISubprogram::get(
+ Context, getCompositeType(), Name, LinkageName, File, Line,
+ Type, IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
+ Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
+ Unit, TemplateParams, Declaration, Variables));
+ EXPECT_NE(N, DISubprogram::get(
+ Context, Scope, "other", LinkageName, File, Line, Type,
+ IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
+ Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
+ Unit, TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, "other", File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
- EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, getFile(),
- Line, Type, IsLocalToUnit, IsDefinition,
- ScopeLine, ContainingType, Virtuality,
- VirtualIndex, Flags, IsOptimized,
- TemplateParams, Declaration, Variables));
- EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File,
- Line + 1, Type, IsLocalToUnit, IsDefinition,
- ScopeLine, ContainingType, Virtuality,
- VirtualIndex, Flags, IsOptimized,
- TemplateParams, Declaration, Variables));
- EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
- getSubroutineType(), IsLocalToUnit,
- IsDefinition, ScopeLine, ContainingType,
- Virtuality, VirtualIndex, Flags, IsOptimized,
+ ThisAdjustment, Flags, IsOptimized, Unit,
TemplateParams, Declaration, Variables));
+ EXPECT_NE(N, DISubprogram::get(
+ Context, Scope, Name, LinkageName, getFile(), Line, Type,
+ IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
+ Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
+ Unit, TemplateParams, Declaration, Variables));
+ EXPECT_NE(N, DISubprogram::get(
+ Context, Scope, Name, LinkageName, File, Line + 1, Type,
+ IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
+ Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
+ Unit, TemplateParams, Declaration, Variables));
+ EXPECT_NE(N,
+ DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
+ getSubroutineType(), IsLocalToUnit, IsDefinition,
+ ScopeLine, ContainingType, Virtuality,
+ VirtualIndex, ThisAdjustment, Flags, IsOptimized,
+ Unit, TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, !IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, !IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
- EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
- Type, IsLocalToUnit, IsDefinition,
- ScopeLine + 1, ContainingType, Virtuality,
- VirtualIndex, Flags, IsOptimized,
+ ThisAdjustment, Flags, IsOptimized, Unit,
TemplateParams, Declaration, Variables));
+ EXPECT_NE(N, DISubprogram::get(
+ Context, Scope, Name, LinkageName, File, Line, Type,
+ IsLocalToUnit, IsDefinition, ScopeLine + 1, ContainingType,
+ Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
+ Unit, TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
getCompositeType(), Virtuality, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality + 1, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex + 1,
- Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- ~Flags, IsOptimized, TemplateParams,
- Declaration, Variables));
+ ThisAdjustment, NotFlags, IsOptimized, Unit,
+ TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- Flags, !IsOptimized, TemplateParams,
- Declaration, Variables));
- EXPECT_NE(N,
- DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
- Type, IsLocalToUnit, IsDefinition, ScopeLine,
- ContainingType, Virtuality, VirtualIndex, Flags,
- IsOptimized, getTuple(), Declaration, Variables));
+ ThisAdjustment, Flags, !IsOptimized, Unit,
+ TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- getSubprogram(), Variables));
+ ThisAdjustment, Flags, IsOptimized, nullptr,
+ TemplateParams, Declaration, Variables));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
- Flags, IsOptimized, TemplateParams,
- Declaration, getTuple()));
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ getTuple(), Declaration, Variables));
+ EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
+ Type, IsLocalToUnit, IsDefinition, ScopeLine,
+ ContainingType, Virtuality, VirtualIndex,
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ TemplateParams, getSubprogram(), Variables));
+ EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
+ Type, IsLocalToUnit, IsDefinition, ScopeLine,
+ ContainingType, Virtuality, VirtualIndex,
+ ThisAdjustment, Flags, IsOptimized, Unit,
+ TemplateParams, Declaration, getTuple()));
TempDISubprogram Temp = N->clone();
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
@@ -1697,7 +1702,7 @@ typedef MetadataTest DITemplateTypeParameterTest;
TEST_F(DITemplateTypeParameterTest, get) {
StringRef Name = "template";
- DITypeRef Type = getBasicType("basic");
+ DIType *Type = getBasicType("basic");
auto *N = DITemplateTypeParameter::get(Context, Name, Type);
@@ -1719,7 +1724,7 @@ typedef MetadataTest DITemplateValueParameterTest;
TEST_F(DITemplateValueParameterTest, get) {
unsigned Tag = dwarf::DW_TAG_template_value_parameter;
StringRef Name = "template";
- DITypeRef Type = getBasicType("basic");
+ DIType *Type = getBasicType("basic");
Metadata *Value = getConstantAsMetadata();
auto *N = DITemplateValueParameter::get(Context, Tag, Name, Type, Value);
@@ -1751,7 +1756,7 @@ TEST_F(DIGlobalVariableTest, get) {
StringRef LinkageName = "linkage";
DIFile *File = getFile();
unsigned Line = 5;
- DITypeRef Type = getDerivedType();
+ DIType *Type = getDerivedType();
bool IsLocalToUnit = false;
bool IsDefinition = true;
Constant *Variable = getConstant();
@@ -1824,9 +1829,10 @@ TEST_F(DILocalVariableTest, get) {
StringRef Name = "name";
DIFile *File = getFile();
unsigned Line = 5;
- DITypeRef Type = getDerivedType();
+ DIType *Type = getDerivedType();
unsigned Arg = 6;
unsigned Flags = 7;
+ unsigned NotFlags = (~Flags) & ((1 << 16) - 1);
auto *N =
DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg, Flags);
@@ -1857,7 +1863,7 @@ TEST_F(DILocalVariableTest, get) {
EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type,
Arg + 1, Flags));
EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg,
- ~Flags));
+ NotFlags));
TempDILocalVariable Temp = N->clone();
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
@@ -1943,7 +1949,7 @@ TEST_F(DIObjCPropertyTest, get) {
StringRef GetterName = "getter";
StringRef SetterName = "setter";
unsigned Attributes = 7;
- DITypeRef Type = getBasicType("basic");
+ DIType *Type = getBasicType("basic");
auto *N = DIObjCProperty::get(Context, Name, File, Line, GetterName,
SetterName, Attributes, Type);
@@ -1984,7 +1990,7 @@ typedef MetadataTest DIImportedEntityTest;
TEST_F(DIImportedEntityTest, get) {
unsigned Tag = dwarf::DW_TAG_imported_module;
DIScope *Scope = getSubprogram();
- DINodeRef Entity = getCompositeType();
+ DINode *Entity = getCompositeType();
unsigned Line = 5;
StringRef Name = "name";
@@ -2073,10 +2079,27 @@ TEST_F(ValueAsMetadataTest, UpdatesOnRAUW) {
EXPECT_TRUE(MD->getValue() == GV1.get());
}
+TEST_F(ValueAsMetadataTest, TempTempReplacement) {
+ // Create a constant.
+ ConstantAsMetadata *CI =
+ ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));
+
+ auto Temp1 = MDTuple::getTemporary(Context, None);
+ auto Temp2 = MDTuple::getTemporary(Context, {CI});
+ auto *N = MDTuple::get(Context, {Temp1.get()});
+
+ // Test replacing a temporary node with another temporary node.
+ Temp1->replaceAllUsesWith(Temp2.get());
+ EXPECT_EQ(N->getOperand(0), Temp2.get());
+
+ // Clean up Temp2 for teardown.
+ Temp2->replaceAllUsesWith(nullptr);
+}
+
TEST_F(ValueAsMetadataTest, CollidingDoubleUpdates) {
// Create a constant.
- ConstantAsMetadata *CI = ConstantAsMetadata::get(
- ConstantInt::get(getGlobalContext(), APInt(8, 0)));
+ ConstantAsMetadata *CI =
+ ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));
// Create a temporary to prevent nodes from resolving.
auto Temp = MDTuple::getTemporary(Context, None);
@@ -2238,44 +2261,23 @@ TEST_F(FunctionAttachmentTest, getAll) {
EXPECT_EQ(T2, MDs[3].second);
}
-TEST_F(FunctionAttachmentTest, dropUnknownMetadata) {
- Function *F = getFunction("foo");
-
- MDTuple *T1 = getTuple();
- MDTuple *T2 = getTuple();
- MDTuple *P = getTuple();
- DISubprogram *SP = getSubprogram();
-
- F->setMetadata("other1", T1);
- F->setMetadata(LLVMContext::MD_dbg, SP);
- F->setMetadata("other2", T2);
- F->setMetadata(LLVMContext::MD_prof, P);
-
- unsigned Known[] = {Context.getMDKindID("other2"), LLVMContext::MD_prof};
- F->dropUnknownMetadata(Known);
-
- EXPECT_EQ(T2, F->getMetadata("other2"));
- EXPECT_EQ(P, F->getMetadata(LLVMContext::MD_prof));
- EXPECT_EQ(nullptr, F->getMetadata("other1"));
- EXPECT_EQ(nullptr, F->getMetadata(LLVMContext::MD_dbg));
-
- F->setMetadata("other2", nullptr);
- F->setMetadata(LLVMContext::MD_prof, nullptr);
- EXPECT_FALSE(F->hasMetadata());
-}
-
TEST_F(FunctionAttachmentTest, Verifier) {
Function *F = getFunction("foo");
F->setMetadata("attach", getTuple());
+ F->setIsMaterializable(true);
+
+ // Confirm this is materializable.
+ ASSERT_TRUE(F->isMaterializable());
- // Confirm this has no body.
- ASSERT_TRUE(F->empty());
+ // Materializable functions cannot have metadata attachments.
+ EXPECT_TRUE(verifyFunction(*F));
- // Functions without a body cannot have metadata attachments (they also can't
- // be verified directly, so check that the module fails to verify).
- EXPECT_TRUE(verifyModule(*F->getParent()));
+ // Function declarations can.
+ F->setIsMaterializable(false);
+ EXPECT_FALSE(verifyModule(*F->getParent()));
+ EXPECT_FALSE(verifyFunction(*F));
- // Functions with a body can.
+ // So can definitions.
(void)new UnreachableInst(Context, BasicBlock::Create(Context, "bb", F));
EXPECT_FALSE(verifyModule(*F->getParent()));
EXPECT_FALSE(verifyFunction(*F));
@@ -2301,4 +2303,81 @@ TEST_F(FunctionAttachmentTest, SubprogramAttachment) {
EXPECT_EQ(SP, F->getMetadata(LLVMContext::MD_dbg));
}
+typedef MetadataTest DistinctMDOperandPlaceholderTest;
+TEST_F(DistinctMDOperandPlaceholderTest, getID) {
+ EXPECT_EQ(7u, DistinctMDOperandPlaceholder(7).getID());
+}
+
+TEST_F(DistinctMDOperandPlaceholderTest, replaceUseWith) {
+ // Set up some placeholders.
+ DistinctMDOperandPlaceholder PH0(7);
+ DistinctMDOperandPlaceholder PH1(3);
+ DistinctMDOperandPlaceholder PH2(0);
+ Metadata *Ops[] = {&PH0, &PH1, &PH2};
+ auto *D = MDTuple::getDistinct(Context, Ops);
+ ASSERT_EQ(&PH0, D->getOperand(0));
+ ASSERT_EQ(&PH1, D->getOperand(1));
+ ASSERT_EQ(&PH2, D->getOperand(2));
+
+ // Replace them.
+ auto *N0 = MDTuple::get(Context, None);
+ auto *N1 = MDTuple::get(Context, N0);
+ PH0.replaceUseWith(N0);
+ PH1.replaceUseWith(N1);
+ PH2.replaceUseWith(nullptr);
+ EXPECT_EQ(N0, D->getOperand(0));
+ EXPECT_EQ(N1, D->getOperand(1));
+ EXPECT_EQ(nullptr, D->getOperand(2));
+}
+
+TEST_F(DistinctMDOperandPlaceholderTest, replaceUseWithNoUser) {
+ // There is no user, but we can still call replace.
+ DistinctMDOperandPlaceholder(7).replaceUseWith(MDTuple::get(Context, None));
+}
+
+#ifndef NDEBUG
+#ifdef GTEST_HAS_DEATH_TEST
+TEST_F(DistinctMDOperandPlaceholderTest, MetadataAsValue) {
+ // This shouldn't crash.
+ DistinctMDOperandPlaceholder PH(7);
+ EXPECT_DEATH(MetadataAsValue::get(Context, &PH),
+ "Unexpected callback to owner");
}
+
+TEST_F(DistinctMDOperandPlaceholderTest, UniquedMDNode) {
+ // This shouldn't crash.
+ DistinctMDOperandPlaceholder PH(7);
+ EXPECT_DEATH(MDTuple::get(Context, &PH), "Unexpected callback to owner");
+}
+
+TEST_F(DistinctMDOperandPlaceholderTest, SecondDistinctMDNode) {
+ // This shouldn't crash.
+ DistinctMDOperandPlaceholder PH(7);
+ MDTuple::getDistinct(Context, &PH);
+ EXPECT_DEATH(MDTuple::getDistinct(Context, &PH),
+ "Placeholders can only be used once");
+}
+
+TEST_F(DistinctMDOperandPlaceholderTest, TrackingMDRefAndDistinctMDNode) {
+ // TrackingMDRef doesn't install an owner callback, so it can't be detected
+ // as an invalid use. However, using a placeholder in a TrackingMDRef *and*
+ // a distinct node isn't possible and we should assert.
+ //
+ // (There's no positive test for using TrackingMDRef because it's not a
+ // useful thing to do.)
+ {
+ DistinctMDOperandPlaceholder PH(7);
+ MDTuple::getDistinct(Context, &PH);
+ EXPECT_DEATH(TrackingMDRef Ref(&PH), "Placeholders can only be used once");
+ }
+ {
+ DistinctMDOperandPlaceholder PH(7);
+ TrackingMDRef Ref(&PH);
+ EXPECT_DEATH(MDTuple::getDistinct(Context, &PH),
+ "Placeholders can only be used once");
+ }
+}
+#endif
+#endif
+
+} // end namespace
diff --git a/unittests/IR/PassManagerTest.cpp b/unittests/IR/PassManagerTest.cpp
index 41af0b0bd25c..c2ac863260ae 100644
--- a/unittests/IR/PassManagerTest.cpp
+++ b/unittests/IR/PassManagerTest.cpp
@@ -19,23 +19,17 @@ using namespace llvm;
namespace {
-class TestFunctionAnalysis {
+class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> {
public:
struct Result {
Result(int Count) : InstructionCount(Count) {}
int InstructionCount;
};
- /// \brief Returns an opaque, unique ID for this pass type.
- static void *ID() { return (void *)&PassID; }
-
- /// \brief Returns the name of the analysis.
- static StringRef name() { return "TestFunctionAnalysis"; }
-
TestFunctionAnalysis(int &Runs) : Runs(Runs) {}
/// \brief Run the analysis pass over the function and return a result.
- Result run(Function &F, FunctionAnalysisManager *AM) {
+ Result run(Function &F, FunctionAnalysisManager &AM) {
++Runs;
int Count = 0;
for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI)
@@ -46,7 +40,7 @@ public:
}
private:
- /// \brief Private static data to provide unique ID.
+ friend AnalysisInfoMixin<TestFunctionAnalysis>;
static char PassID;
int &Runs;
@@ -54,20 +48,16 @@ private:
char TestFunctionAnalysis::PassID;
-class TestModuleAnalysis {
+class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> {
public:
struct Result {
Result(int Count) : FunctionCount(Count) {}
int FunctionCount;
};
- static void *ID() { return (void *)&PassID; }
-
- static StringRef name() { return "TestModuleAnalysis"; }
-
TestModuleAnalysis(int &Runs) : Runs(Runs) {}
- Result run(Module &M, ModuleAnalysisManager *AM) {
+ Result run(Module &M, ModuleAnalysisManager &AM) {
++Runs;
int Count = 0;
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
@@ -76,6 +66,7 @@ public:
}
private:
+ friend AnalysisInfoMixin<TestModuleAnalysis>;
static char PassID;
int &Runs;
@@ -83,40 +74,37 @@ private:
char TestModuleAnalysis::PassID;
-struct TestModulePass {
+struct TestModulePass : PassInfoMixin<TestModulePass> {
TestModulePass(int &RunCount) : RunCount(RunCount) {}
- PreservedAnalyses run(Module &M) {
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {
++RunCount;
return PreservedAnalyses::none();
}
- static StringRef name() { return "TestModulePass"; }
-
int &RunCount;
};
-struct TestPreservingModulePass {
- PreservedAnalyses run(Module &M) { return PreservedAnalyses::all(); }
-
- static StringRef name() { return "TestPreservingModulePass"; }
+struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> {
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {
+ return PreservedAnalyses::all();
+ }
};
-struct TestMinPreservingModulePass {
- PreservedAnalyses run(Module &M, ModuleAnalysisManager *AM) {
+struct TestMinPreservingModulePass
+ : PassInfoMixin<TestMinPreservingModulePass> {
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
PreservedAnalyses PA;
// Force running an analysis.
- (void)AM->getResult<TestModuleAnalysis>(M);
+ (void)AM.getResult<TestModuleAnalysis>(M);
PA.preserve<FunctionAnalysisManagerModuleProxy>();
return PA;
}
-
- static StringRef name() { return "TestMinPreservingModulePass"; }
};
-struct TestFunctionPass {
+struct TestFunctionPass : PassInfoMixin<TestFunctionPass> {
TestFunctionPass(int &RunCount, int &AnalyzedInstrCount,
int &AnalyzedFunctionCount,
bool OnlyUseCachedResults = false)
@@ -124,11 +112,11 @@ struct TestFunctionPass {
AnalyzedFunctionCount(AnalyzedFunctionCount),
OnlyUseCachedResults(OnlyUseCachedResults) {}
- PreservedAnalyses run(Function &F, FunctionAnalysisManager *AM) {
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
++RunCount;
const ModuleAnalysisManager &MAM =
- AM->getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager();
+ AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager();
if (TestModuleAnalysis::Result *TMA =
MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()))
AnalyzedFunctionCount += TMA->FunctionCount;
@@ -136,19 +124,17 @@ struct TestFunctionPass {
if (OnlyUseCachedResults) {
// Hack to force the use of the cached interface.
if (TestFunctionAnalysis::Result *AR =
- AM->getCachedResult<TestFunctionAnalysis>(F))
+ AM.getCachedResult<TestFunctionAnalysis>(F))
AnalyzedInstrCount += AR->InstructionCount;
} else {
// Typical path just runs the analysis as needed.
- TestFunctionAnalysis::Result &AR = AM->getResult<TestFunctionAnalysis>(F);
+ TestFunctionAnalysis::Result &AR = AM.getResult<TestFunctionAnalysis>(F);
AnalyzedInstrCount += AR.InstructionCount;
}
return PreservedAnalyses::all();
}
- static StringRef name() { return "TestFunctionPass"; }
-
int &RunCount;
int &AnalyzedInstrCount;
int &AnalyzedFunctionCount;
@@ -157,43 +143,42 @@ struct TestFunctionPass {
// A test function pass that invalidates all function analyses for a function
// with a specific name.
-struct TestInvalidationFunctionPass {
+struct TestInvalidationFunctionPass
+ : PassInfoMixin<TestInvalidationFunctionPass> {
TestInvalidationFunctionPass(StringRef FunctionName) : Name(FunctionName) {}
- PreservedAnalyses run(Function &F) {
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
return F.getName() == Name ? PreservedAnalyses::none()
: PreservedAnalyses::all();
}
- static StringRef name() { return "TestInvalidationFunctionPass"; }
-
StringRef Name;
};
-std::unique_ptr<Module> parseIR(const char *IR) {
- LLVMContext &C = getGlobalContext();
+std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) {
SMDiagnostic Err;
- return parseAssemblyString(IR, Err, C);
+ return parseAssemblyString(IR, Err, Context);
}
class PassManagerTest : public ::testing::Test {
protected:
+ LLVMContext Context;
std::unique_ptr<Module> M;
public:
PassManagerTest()
- : M(parseIR("define void @f() {\n"
- "entry:\n"
- " call void @g()\n"
- " call void @h()\n"
- " ret void\n"
- "}\n"
- "define void @g() {\n"
- " ret void\n"
- "}\n"
- "define void @h() {\n"
- " ret void\n"
- "}\n")) {}
+ : M(parseIR(Context, "define void @f() {\n"
+ "entry:\n"
+ " call void @g()\n"
+ " call void @h()\n"
+ " ret void\n"
+ "}\n"
+ "define void @g() {\n"
+ " ret void\n"
+ "}\n"
+ "define void @h() {\n"
+ " ret void\n"
+ "}\n")) {}
};
TEST_F(PassManagerTest, BasicPreservedAnalyses) {
@@ -232,13 +217,13 @@ TEST_F(PassManagerTest, BasicPreservedAnalyses) {
TEST_F(PassManagerTest, Basic) {
FunctionAnalysisManager FAM;
int FunctionAnalysisRuns = 0;
- FAM.registerPass(TestFunctionAnalysis(FunctionAnalysisRuns));
+ FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });
ModuleAnalysisManager MAM;
int ModuleAnalysisRuns = 0;
- MAM.registerPass(TestModuleAnalysis(ModuleAnalysisRuns));
- MAM.registerPass(FunctionAnalysisManagerModuleProxy(FAM));
- FAM.registerPass(ModuleAnalysisManagerFunctionProxy(MAM));
+ MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });
+ MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
+ FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
ModulePassManager MPM;
@@ -253,8 +238,8 @@ TEST_F(PassManagerTest, Basic) {
{
// Pointless scope to test move assignment.
FunctionPassManager NestedFPM;
- NestedFPM.addPass(TestFunctionPass(FunctionPassRunCount1, AnalyzedInstrCount1,
- AnalyzedFunctionCount1));
+ NestedFPM.addPass(TestFunctionPass(
+ FunctionPassRunCount1, AnalyzedInstrCount1, AnalyzedFunctionCount1));
FPM = std::move(NestedFPM);
}
NestedMPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
@@ -315,7 +300,7 @@ TEST_F(PassManagerTest, Basic) {
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
}
- MPM.run(*M, &MAM);
+ MPM.run(*M, MAM);
// Validate module pass counters.
EXPECT_EQ(1, ModulePassRunCount);
diff --git a/unittests/IR/PatternMatch.cpp b/unittests/IR/PatternMatch.cpp
index f3a27b8d2509..1121d6554db7 100644
--- a/unittests/IR/PatternMatch.cpp
+++ b/unittests/IR/PatternMatch.cpp
@@ -35,7 +35,7 @@ struct PatternMatchTest : ::testing::Test {
std::unique_ptr<Module> M;
Function *F;
BasicBlock *BB;
- IRBuilder<true, NoFolder> IRB;
+ IRBuilder<NoFolder> IRB;
PatternMatchTest()
: M(new Module("PatternMatchTestModule", Ctx)),
diff --git a/unittests/IR/TypeBuilderTest.cpp b/unittests/IR/TypeBuilderTest.cpp
index b7b3e45e35ed..f2dccac001a4 100644
--- a/unittests/IR/TypeBuilderTest.cpp
+++ b/unittests/IR/TypeBuilderTest.cpp
@@ -8,7 +8,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/TypeBuilder.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/LLVMContext.h"
#include "gtest/gtest.h"
@@ -17,141 +16,175 @@ using namespace llvm;
namespace {
TEST(TypeBuilderTest, Void) {
- EXPECT_EQ(Type::getVoidTy(getGlobalContext()), (TypeBuilder<void, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getVoidTy(getGlobalContext()), (TypeBuilder<void, false>::get(getGlobalContext())));
+ LLVMContext Context;
+ EXPECT_EQ(Type::getVoidTy(Context), (TypeBuilder<void, true>::get(Context)));
+ EXPECT_EQ(Type::getVoidTy(Context), (TypeBuilder<void, false>::get(Context)));
// Special cases for C compatibility:
- EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()),
- (TypeBuilder<void*, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()),
- (TypeBuilder<const void*, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()),
- (TypeBuilder<volatile void*, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()),
- (TypeBuilder<const volatile void*, false>::get(
- getGlobalContext())));
+ EXPECT_EQ(Type::getInt8PtrTy(Context),
+ (TypeBuilder<void *, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8PtrTy(Context),
+ (TypeBuilder<const void *, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8PtrTy(Context),
+ (TypeBuilder<volatile void *, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8PtrTy(Context),
+ (TypeBuilder<const volatile void *, false>::get(Context)));
}
TEST(TypeBuilderTest, HostIntegers) {
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), (TypeBuilder<int8_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()), (TypeBuilder<uint8_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt16Ty(getGlobalContext()), (TypeBuilder<int16_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt16Ty(getGlobalContext()), (TypeBuilder<uint16_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (TypeBuilder<int32_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (TypeBuilder<uint32_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt64Ty(getGlobalContext()), (TypeBuilder<int64_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt64Ty(getGlobalContext()), (TypeBuilder<uint64_t, false>::get(getGlobalContext())));
-
- EXPECT_EQ(IntegerType::get(getGlobalContext(), sizeof(size_t) * CHAR_BIT),
- (TypeBuilder<size_t, false>::get(getGlobalContext())));
- EXPECT_EQ(IntegerType::get(getGlobalContext(), sizeof(ptrdiff_t) * CHAR_BIT),
- (TypeBuilder<ptrdiff_t, false>::get(getGlobalContext())));
+ LLVMContext Context;
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<int8_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<uint8_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt16Ty(Context),
+ (TypeBuilder<int16_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt16Ty(Context),
+ (TypeBuilder<uint16_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt32Ty(Context),
+ (TypeBuilder<int32_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt32Ty(Context),
+ (TypeBuilder<uint32_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt64Ty(Context),
+ (TypeBuilder<int64_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt64Ty(Context),
+ (TypeBuilder<uint64_t, false>::get(Context)));
+
+ EXPECT_EQ(IntegerType::get(Context, sizeof(size_t) * CHAR_BIT),
+ (TypeBuilder<size_t, false>::get(Context)));
+ EXPECT_EQ(IntegerType::get(Context, sizeof(ptrdiff_t) * CHAR_BIT),
+ (TypeBuilder<ptrdiff_t, false>::get(Context)));
}
TEST(TypeBuilderTest, CrossCompilableIntegers) {
- EXPECT_EQ(IntegerType::get(getGlobalContext(), 1), (TypeBuilder<types::i<1>, true>::get(getGlobalContext())));
- EXPECT_EQ(IntegerType::get(getGlobalContext(), 1), (TypeBuilder<types::i<1>, false>::get(getGlobalContext())));
- EXPECT_EQ(IntegerType::get(getGlobalContext(), 72), (TypeBuilder<types::i<72>, true>::get(getGlobalContext())));
- EXPECT_EQ(IntegerType::get(getGlobalContext(), 72), (TypeBuilder<types::i<72>, false>::get(getGlobalContext())));
+ LLVMContext Context;
+ EXPECT_EQ(IntegerType::get(Context, 1),
+ (TypeBuilder<types::i<1>, true>::get(Context)));
+ EXPECT_EQ(IntegerType::get(Context, 1),
+ (TypeBuilder<types::i<1>, false>::get(Context)));
+ EXPECT_EQ(IntegerType::get(Context, 72),
+ (TypeBuilder<types::i<72>, true>::get(Context)));
+ EXPECT_EQ(IntegerType::get(Context, 72),
+ (TypeBuilder<types::i<72>, false>::get(Context)));
}
TEST(TypeBuilderTest, Float) {
- EXPECT_EQ(Type::getFloatTy(getGlobalContext()), (TypeBuilder<float, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getDoubleTy(getGlobalContext()), (TypeBuilder<double, false>::get(getGlobalContext())));
+ LLVMContext Context;
+ EXPECT_EQ(Type::getFloatTy(Context),
+ (TypeBuilder<float, false>::get(Context)));
+ EXPECT_EQ(Type::getDoubleTy(Context),
+ (TypeBuilder<double, false>::get(Context)));
// long double isn't supported yet.
- EXPECT_EQ(Type::getFloatTy(getGlobalContext()), (TypeBuilder<types::ieee_float, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getFloatTy(getGlobalContext()), (TypeBuilder<types::ieee_float, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getDoubleTy(getGlobalContext()), (TypeBuilder<types::ieee_double, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getDoubleTy(getGlobalContext()), (TypeBuilder<types::ieee_double, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getX86_FP80Ty(getGlobalContext()), (TypeBuilder<types::x86_fp80, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getX86_FP80Ty(getGlobalContext()), (TypeBuilder<types::x86_fp80, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getFP128Ty(getGlobalContext()), (TypeBuilder<types::fp128, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getFP128Ty(getGlobalContext()), (TypeBuilder<types::fp128, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getPPC_FP128Ty(getGlobalContext()), (TypeBuilder<types::ppc_fp128, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getPPC_FP128Ty(getGlobalContext()), (TypeBuilder<types::ppc_fp128, false>::get(getGlobalContext())));
+ EXPECT_EQ(Type::getFloatTy(Context),
+ (TypeBuilder<types::ieee_float, true>::get(Context)));
+ EXPECT_EQ(Type::getFloatTy(Context),
+ (TypeBuilder<types::ieee_float, false>::get(Context)));
+ EXPECT_EQ(Type::getDoubleTy(Context),
+ (TypeBuilder<types::ieee_double, true>::get(Context)));
+ EXPECT_EQ(Type::getDoubleTy(Context),
+ (TypeBuilder<types::ieee_double, false>::get(Context)));
+ EXPECT_EQ(Type::getX86_FP80Ty(Context),
+ (TypeBuilder<types::x86_fp80, true>::get(Context)));
+ EXPECT_EQ(Type::getX86_FP80Ty(Context),
+ (TypeBuilder<types::x86_fp80, false>::get(Context)));
+ EXPECT_EQ(Type::getFP128Ty(Context),
+ (TypeBuilder<types::fp128, true>::get(Context)));
+ EXPECT_EQ(Type::getFP128Ty(Context),
+ (TypeBuilder<types::fp128, false>::get(Context)));
+ EXPECT_EQ(Type::getPPC_FP128Ty(Context),
+ (TypeBuilder<types::ppc_fp128, true>::get(Context)));
+ EXPECT_EQ(Type::getPPC_FP128Ty(Context),
+ (TypeBuilder<types::ppc_fp128, false>::get(Context)));
}
TEST(TypeBuilderTest, Derived) {
- EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(getGlobalContext())),
- (TypeBuilder<int8_t**, false>::get(getGlobalContext())));
- EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 7),
- (TypeBuilder<int8_t[7], false>::get(getGlobalContext())));
- EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 0),
- (TypeBuilder<int8_t[], false>::get(getGlobalContext())));
-
- EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(getGlobalContext())),
- (TypeBuilder<types::i<8>**, false>::get(getGlobalContext())));
- EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 7),
- (TypeBuilder<types::i<8>[7], false>::get(getGlobalContext())));
- EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 0),
- (TypeBuilder<types::i<8>[], false>::get(getGlobalContext())));
-
- EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(getGlobalContext())),
- (TypeBuilder<types::i<8>**, true>::get(getGlobalContext())));
- EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 7),
- (TypeBuilder<types::i<8>[7], true>::get(getGlobalContext())));
- EXPECT_EQ(ArrayType::get(Type::getInt8Ty(getGlobalContext()), 0),
- (TypeBuilder<types::i<8>[], true>::get(getGlobalContext())));
-
-
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<const int8_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<volatile int8_t, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<const volatile int8_t, false>::get(getGlobalContext())));
-
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<const types::i<8>, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<volatile types::i<8>, false>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<const volatile types::i<8>, false>::get(getGlobalContext())));
-
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<const types::i<8>, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<volatile types::i<8>, true>::get(getGlobalContext())));
- EXPECT_EQ(Type::getInt8Ty(getGlobalContext()),
- (TypeBuilder<const volatile types::i<8>, true>::get(getGlobalContext())));
-
- EXPECT_EQ(Type::getInt8PtrTy(getGlobalContext()),
- (TypeBuilder<const volatile int8_t*const volatile, false>::get(getGlobalContext())));
+ LLVMContext Context;
+ EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(Context)),
+ (TypeBuilder<int8_t **, false>::get(Context)));
+ EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 7),
+ (TypeBuilder<int8_t[7], false>::get(Context)));
+ EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 0),
+ (TypeBuilder<int8_t[], false>::get(Context)));
+
+ EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(Context)),
+ (TypeBuilder<types::i<8> **, false>::get(Context)));
+ EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 7),
+ (TypeBuilder<types::i<8>[7], false>::get(Context)));
+ EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 0),
+ (TypeBuilder<types::i<8>[], false>::get(Context)));
+
+ EXPECT_EQ(PointerType::getUnqual(Type::getInt8PtrTy(Context)),
+ (TypeBuilder<types::i<8> **, true>::get(Context)));
+ EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 7),
+ (TypeBuilder<types::i<8>[7], true>::get(Context)));
+ EXPECT_EQ(ArrayType::get(Type::getInt8Ty(Context), 0),
+ (TypeBuilder<types::i<8>[], true>::get(Context)));
+
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<const int8_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<volatile int8_t, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<const volatile int8_t, false>::get(Context)));
+
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<const types::i<8>, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<volatile types::i<8>, false>::get(Context)));
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<const volatile types::i<8>, false>::get(Context)));
+
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<const types::i<8>, true>::get(Context)));
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<volatile types::i<8>, true>::get(Context)));
+ EXPECT_EQ(Type::getInt8Ty(Context),
+ (TypeBuilder<const volatile types::i<8>, true>::get(Context)));
+
+ EXPECT_EQ(Type::getInt8PtrTy(Context),
+ (TypeBuilder<const volatile int8_t *const volatile, false>::get(
+ Context)));
}
TEST(TypeBuilderTest, Functions) {
+ LLVMContext Context;
std::vector<Type*> params;
- EXPECT_EQ(FunctionType::get(Type::getVoidTy(getGlobalContext()), params, false),
- (TypeBuilder<void(), true>::get(getGlobalContext())));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true),
- (TypeBuilder<int8_t(...), false>::get(getGlobalContext())));
- params.push_back(TypeBuilder<int32_t*, false>::get(getGlobalContext()));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false),
- (TypeBuilder<int8_t(const int32_t*), false>::get(getGlobalContext())));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true),
- (TypeBuilder<int8_t(const int32_t*, ...), false>::get(getGlobalContext())));
- params.push_back(TypeBuilder<char*, false>::get(getGlobalContext()));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false),
- (TypeBuilder<int8_t(int32_t*, void*), false>::get(getGlobalContext())));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true),
- (TypeBuilder<int8_t(int32_t*, char*, ...), false>::get(getGlobalContext())));
- params.push_back(TypeBuilder<char, false>::get(getGlobalContext()));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false),
- (TypeBuilder<int8_t(int32_t*, void*, char), false>::get(getGlobalContext())));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true),
- (TypeBuilder<int8_t(int32_t*, char*, char, ...), false>::get(getGlobalContext())));
- params.push_back(TypeBuilder<char, false>::get(getGlobalContext()));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false),
- (TypeBuilder<int8_t(int32_t*, void*, char, char), false>::get(getGlobalContext())));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true),
- (TypeBuilder<int8_t(int32_t*, char*, char, char, ...),
- false>::get(getGlobalContext())));
- params.push_back(TypeBuilder<char, false>::get(getGlobalContext()));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, false),
- (TypeBuilder<int8_t(int32_t*, void*, char, char, char),
- false>::get(getGlobalContext())));
- EXPECT_EQ(FunctionType::get(Type::getInt8Ty(getGlobalContext()), params, true),
- (TypeBuilder<int8_t(int32_t*, char*, char, char, char, ...),
- false>::get(getGlobalContext())));
+ EXPECT_EQ(FunctionType::get(Type::getVoidTy(Context), params, false),
+ (TypeBuilder<void(), true>::get(Context)));
+ EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true),
+ (TypeBuilder<int8_t(...), false>::get(Context)));
+ params.push_back(TypeBuilder<int32_t *, false>::get(Context));
+ EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, false),
+ (TypeBuilder<int8_t(const int32_t *), false>::get(Context)));
+ EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true),
+ (TypeBuilder<int8_t(const int32_t *, ...), false>::get(Context)));
+ params.push_back(TypeBuilder<char *, false>::get(Context));
+ EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, false),
+ (TypeBuilder<int8_t(int32_t *, void *), false>::get(Context)));
+ EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true),
+ (TypeBuilder<int8_t(int32_t *, char *, ...), false>::get(Context)));
+ params.push_back(TypeBuilder<char, false>::get(Context));
+ EXPECT_EQ(
+ FunctionType::get(Type::getInt8Ty(Context), params, false),
+ (TypeBuilder<int8_t(int32_t *, void *, char), false>::get(Context)));
+ EXPECT_EQ(
+ FunctionType::get(Type::getInt8Ty(Context), params, true),
+ (TypeBuilder<int8_t(int32_t *, char *, char, ...), false>::get(Context)));
+ params.push_back(TypeBuilder<char, false>::get(Context));
+ EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, false),
+ (TypeBuilder<int8_t(int32_t *, void *, char, char), false>::get(
+ Context)));
+ EXPECT_EQ(
+ FunctionType::get(Type::getInt8Ty(Context), params, true),
+ (TypeBuilder<int8_t(int32_t *, char *, char, char, ...), false>::get(
+ Context)));
+ params.push_back(TypeBuilder<char, false>::get(Context));
+ EXPECT_EQ(
+ FunctionType::get(Type::getInt8Ty(Context), params, false),
+ (TypeBuilder<int8_t(int32_t *, void *, char, char, char), false>::get(
+ Context)));
+ EXPECT_EQ(FunctionType::get(Type::getInt8Ty(Context), params, true),
+ (TypeBuilder<int8_t(int32_t *, char *, char, char, char, ...),
+ false>::get(Context)));
}
TEST(TypeBuilderTest, Context) {
@@ -230,24 +263,24 @@ public:
namespace {
TEST(TypeBuilderTest, Extensions) {
+ LLVMContext Context;
EXPECT_EQ(PointerType::getUnqual(StructType::get(
- TypeBuilder<int, false>::get(getGlobalContext()),
- TypeBuilder<int*, false>::get(getGlobalContext()),
- TypeBuilder<void*[], false>::get(getGlobalContext()),
- (void*)nullptr)),
- (TypeBuilder<MyType*, false>::get(getGlobalContext())));
- EXPECT_EQ(PointerType::getUnqual(StructType::get(
- TypeBuilder<types::i<32>, false>::get(getGlobalContext()),
- TypeBuilder<types::i<32>*, false>::get(getGlobalContext()),
- TypeBuilder<types::i<8>*[], false>::get(getGlobalContext()),
- (void*)nullptr)),
- (TypeBuilder<MyPortableType*, false>::get(getGlobalContext())));
- EXPECT_EQ(PointerType::getUnqual(StructType::get(
- TypeBuilder<types::i<32>, false>::get(getGlobalContext()),
- TypeBuilder<types::i<32>*, false>::get(getGlobalContext()),
- TypeBuilder<types::i<8>*[], false>::get(getGlobalContext()),
- (void*)nullptr)),
- (TypeBuilder<MyPortableType*, true>::get(getGlobalContext())));
+ TypeBuilder<int, false>::get(Context),
+ TypeBuilder<int *, false>::get(Context),
+ TypeBuilder<void *[], false>::get(Context), (void *)nullptr)),
+ (TypeBuilder<MyType *, false>::get(Context)));
+ EXPECT_EQ(
+ PointerType::getUnqual(StructType::get(
+ TypeBuilder<types::i<32>, false>::get(Context),
+ TypeBuilder<types::i<32> *, false>::get(Context),
+ TypeBuilder<types::i<8> *[], false>::get(Context), (void *)nullptr)),
+ (TypeBuilder<MyPortableType *, false>::get(Context)));
+ EXPECT_EQ(
+ PointerType::getUnqual(StructType::get(
+ TypeBuilder<types::i<32>, false>::get(Context),
+ TypeBuilder<types::i<32> *, false>::get(Context),
+ TypeBuilder<types::i<8> *[], false>::get(Context), (void *)nullptr)),
+ (TypeBuilder<MyPortableType *, true>::get(Context)));
}
} // anonymous namespace
diff --git a/unittests/IR/UserTest.cpp b/unittests/IR/UserTest.cpp
index 8d488389448a..7d875aa80d48 100644
--- a/unittests/IR/UserTest.cpp
+++ b/unittests/IR/UserTest.cpp
@@ -94,9 +94,9 @@ TEST(UserTest, ValueOpIteration) {
}
TEST(UserTest, PersonalityUser) {
- Module M("", getGlobalContext());
- FunctionType *RetVoidTy =
- FunctionType::get(Type::getVoidTy(getGlobalContext()), false);
+ LLVMContext Context;
+ Module M("", Context);
+ FunctionType *RetVoidTy = FunctionType::get(Type::getVoidTy(Context), false);
Function *PersonalityF = Function::Create(
RetVoidTy, GlobalValue::ExternalLinkage, "PersonalityFn", &M);
Function *TestF =
diff --git a/unittests/IR/ValueHandleTest.cpp b/unittests/IR/ValueHandleTest.cpp
index e1d598bbc58b..59cd9d7ba37a 100644
--- a/unittests/IR/ValueHandleTest.cpp
+++ b/unittests/IR/ValueHandleTest.cpp
@@ -20,13 +20,13 @@ namespace {
class ValueHandle : public testing::Test {
protected:
+ LLVMContext Context;
Constant *ConstantV;
std::unique_ptr<BitCastInst> BitcastV;
- ValueHandle() :
- ConstantV(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0)),
- BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext()))) {
- }
+ ValueHandle()
+ : ConstantV(ConstantInt::get(Type::getInt32Ty(Context), 0)),
+ BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(Context))) {}
};
class ConcreteCallbackVH final : public CallbackVH {
@@ -42,8 +42,8 @@ TEST_F(ValueHandle, WeakVH_BasicOperation) {
// Make sure I can call a method on the underlying Value. It
// doesn't matter which method.
- EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), WVH->getType());
- EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (*WVH).getType());
+ EXPECT_EQ(Type::getInt32Ty(Context), WVH->getType());
+ EXPECT_EQ(Type::getInt32Ty(Context), (*WVH).getType());
}
TEST_F(ValueHandle, WeakVH_Comparisons) {
@@ -197,8 +197,8 @@ TEST_F(ValueHandle, CallbackVH_BasicOperation) {
// Make sure I can call a method on the underlying Value. It
// doesn't matter which method.
- EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), CVH->getType());
- EXPECT_EQ(Type::getInt32Ty(getGlobalContext()), (*CVH).getType());
+ EXPECT_EQ(Type::getInt32Ty(Context), CVH->getType());
+ EXPECT_EQ(Type::getInt32Ty(Context), (*CVH).getType());
}
TEST_F(ValueHandle, CallbackVH_Comparisons) {
@@ -297,15 +297,17 @@ TEST_F(ValueHandle, CallbackVH_DeletionCanRAUW) {
Value *AURWArgument;
LLVMContext *Context;
- RecoveringVH() : DeletedCalls(0), AURWArgument(nullptr),
- Context(&getGlobalContext()) {}
- RecoveringVH(Value *V)
- : CallbackVH(V), DeletedCalls(0), AURWArgument(nullptr),
- Context(&getGlobalContext()) {}
+ RecoveringVH(LLVMContext &TheContext)
+ : DeletedCalls(0), AURWArgument(nullptr), Context(&TheContext) {}
+
+ RecoveringVH(LLVMContext &TheContext, Value *V)
+ : CallbackVH(V), DeletedCalls(0), AURWArgument(nullptr),
+ Context(&TheContext) {}
private:
void deleted() override {
- getValPtr()->replaceAllUsesWith(Constant::getNullValue(Type::getInt32Ty(getGlobalContext())));
+ getValPtr()->replaceAllUsesWith(
+ Constant::getNullValue(Type::getInt32Ty(*Context)));
setValPtr(nullptr);
}
void allUsesReplacedWith(Value *new_value) override {
@@ -318,15 +320,15 @@ TEST_F(ValueHandle, CallbackVH_DeletionCanRAUW) {
// Normally, if a value has uses, deleting it will crash. However, we can use
// a CallbackVH to remove the uses before the check for no uses.
- RecoveringVH RVH;
- RVH = BitcastV.get();
- std::unique_ptr<BinaryOperator> BitcastUser(
- BinaryOperator::CreateAdd(RVH,
- Constant::getNullValue(Type::getInt32Ty(getGlobalContext()))));
+ RecoveringVH RVH(Context);
+ RVH = RecoveringVH(Context, BitcastV.get());
+ std::unique_ptr<BinaryOperator> BitcastUser(BinaryOperator::CreateAdd(
+ RVH, Constant::getNullValue(Type::getInt32Ty(Context))));
EXPECT_EQ(BitcastV.get(), BitcastUser->getOperand(0));
BitcastV.reset(); // Would crash without the ValueHandler.
- EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(getGlobalContext())), RVH.AURWArgument);
- EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(getGlobalContext())),
+ EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(Context)),
+ RVH.AURWArgument);
+ EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(Context)),
BitcastUser->getOperand(0));
}
diff --git a/unittests/IR/ValueMapTest.cpp b/unittests/IR/ValueMapTest.cpp
index 1431a8d87de4..28633b44b11d 100644
--- a/unittests/IR/ValueMapTest.cpp
+++ b/unittests/IR/ValueMapTest.cpp
@@ -22,15 +22,15 @@ namespace {
template<typename T>
class ValueMapTest : public testing::Test {
protected:
+ LLVMContext Context;
Constant *ConstantV;
std::unique_ptr<BitCastInst> BitcastV;
std::unique_ptr<BinaryOperator> AddV;
- ValueMapTest() :
- ConstantV(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0)),
- BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext()))),
- AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) {
- }
+ ValueMapTest()
+ : ConstantV(ConstantInt::get(Type::getInt32Ty(Context), 0)),
+ BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(Context))),
+ AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) {}
};
// Run everything on Value*, a subtype to make sure that casting works as
@@ -292,4 +292,4 @@ TYPED_TEST(ValueMapTest, SurvivesModificationByConfig) {
EXPECT_EQ(0u, VM.count(this->AddV.get()));
}
-}
+} // end namespace
diff --git a/unittests/IR/ValueTest.cpp b/unittests/IR/ValueTest.cpp
index 9cf1306dae67..607b7a1bd2c9 100644
--- a/unittests/IR/ValueTest.cpp
+++ b/unittests/IR/ValueTest.cpp
@@ -45,7 +45,7 @@ TEST(ValueTest, UsedInBasicBlock) {
}
TEST(GlobalTest, CreateAddressSpace) {
- LLVMContext &Ctx = getGlobalContext();
+ LLVMContext Ctx;
std::unique_ptr<Module> M(new Module("TestModule", Ctx));
Type *Int8Ty = Type::getInt8Ty(Ctx);
Type *Int32Ty = Type::getInt32Ty(Ctx);
@@ -92,7 +92,7 @@ TEST(GlobalTest, CreateAddressSpace) {
#ifdef GTEST_HAS_DEATH_TEST
#ifndef NDEBUG
TEST(GlobalTest, AlignDeath) {
- LLVMContext &Ctx = getGlobalContext();
+ LLVMContext Ctx;
std::unique_ptr<Module> M(new Module("TestModule", Ctx));
Type *Int32Ty = Type::getInt32Ty(Ctx);
GlobalVariable *Var =
diff --git a/unittests/IR/VerifierTest.cpp b/unittests/IR/VerifierTest.cpp
index 4e94b4375f92..c33c92a6f7c5 100644
--- a/unittests/IR/VerifierTest.cpp
+++ b/unittests/IR/VerifierTest.cpp
@@ -1,4 +1,4 @@
-//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests ------------===//
+//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests --*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,22 +7,24 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/IR/Verifier.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
#include "gtest/gtest.h"
namespace llvm {
namespace {
TEST(VerifierTest, Branch_i1) {
- LLVMContext &C = getGlobalContext();
+ LLVMContext C;
Module M("M", C);
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy));
@@ -45,7 +47,7 @@ TEST(VerifierTest, Branch_i1) {
}
TEST(VerifierTest, InvalidRetAttribute) {
- LLVMContext &C = getGlobalContext();
+ LLVMContext C;
Module M("M", C);
FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy));
@@ -61,10 +63,10 @@ TEST(VerifierTest, InvalidRetAttribute) {
}
TEST(VerifierTest, CrossModuleRef) {
- LLVMContext &C = getGlobalContext();
+ LLVMContext C;
Module M1("M1", C);
Module M2("M2", C);
- Module M3("M2", C);
+ Module M3("M3", C);
FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
Function *F1 = cast<Function>(M1.getOrInsertFunction("foo1", FTy));
Function *F2 = cast<Function>(M2.getOrInsertFunction("foo2", FTy));
@@ -86,7 +88,21 @@ TEST(VerifierTest, CrossModuleRef) {
std::string Error;
raw_string_ostream ErrorOS(Error);
- EXPECT_FALSE(verifyModule(M2, &ErrorOS));
+ EXPECT_TRUE(verifyModule(M2, &ErrorOS));
+ EXPECT_TRUE(StringRef(ErrorOS.str())
+ .equals("Global is used by function in a different module\n"
+ "i32 ()* @foo2\n"
+ "; ModuleID = 'M2'\n"
+ "i32 ()* @foo3\n"
+ "; ModuleID = 'M3'\n"
+ "Global is referenced in a different module!\n"
+ "i32 ()* @foo2\n"
+ "; ModuleID = 'M2'\n"
+ " %call = call i32 @foo2()\n"
+ "i32 ()* @foo1\n"
+ "; ModuleID = 'M1'\n"));
+
+ Error.clear();
EXPECT_TRUE(verifyModule(M1, &ErrorOS));
EXPECT_TRUE(StringRef(ErrorOS.str()).equals(
"Referencing function in another module!\n"
@@ -105,7 +121,104 @@ TEST(VerifierTest, CrossModuleRef) {
F3->eraseFromParent();
}
+TEST(VerifierTest, CrossModuleMetadataRef) {
+ LLVMContext C;
+ Module M1("M1", C);
+ Module M2("M2", C);
+ GlobalVariable *newGV =
+ new GlobalVariable(M1, Type::getInt8Ty(C), false,
+ GlobalVariable::ExternalLinkage, nullptr,
+ "Some Global");
+
+ DIBuilder dbuilder(M2);
+ auto CU = dbuilder.createCompileUnit(dwarf::DW_LANG_Julia, "test.jl", ".",
+ "unittest", false, "", 0);
+ auto File = dbuilder.createFile("test.jl", ".");
+ auto Ty = dbuilder.createBasicType("Int8", 8, 8, dwarf::DW_ATE_signed);
+ dbuilder.createGlobalVariable(CU, "_SOME_GLOBAL", "_SOME_GLOBAL", File, 1, Ty,
+ false, newGV);
+ dbuilder.finalize();
+ std::string Error;
+ raw_string_ostream ErrorOS(Error);
+ EXPECT_TRUE(verifyModule(M2, &ErrorOS));
+ EXPECT_TRUE(StringRef(ErrorOS.str())
+ .startswith("Referencing global in another module!"));
+}
+TEST(VerifierTest, InvalidVariableLinkage) {
+ LLVMContext C;
+ Module M("M", C);
+ new GlobalVariable(M, Type::getInt8Ty(C), false,
+ GlobalValue::LinkOnceODRLinkage, nullptr, "Some Global");
+ std::string Error;
+ raw_string_ostream ErrorOS(Error);
+ EXPECT_TRUE(verifyModule(M, &ErrorOS));
+ EXPECT_TRUE(
+ StringRef(ErrorOS.str()).startswith("Global is external, but doesn't "
+ "have external or weak linkage!"));
}
+
+TEST(VerifierTest, InvalidFunctionLinkage) {
+ LLVMContext C;
+ Module M("M", C);
+
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
+ Function::Create(FTy, GlobalValue::LinkOnceODRLinkage, "foo", &M);
+ std::string Error;
+ raw_string_ostream ErrorOS(Error);
+ EXPECT_TRUE(verifyModule(M, &ErrorOS));
+ EXPECT_TRUE(
+ StringRef(ErrorOS.str()).startswith("Global is external, but doesn't "
+ "have external or weak linkage!"));
}
+
+#ifndef _MSC_VER
+// FIXME: This test causes an ICE in MSVC 2013.
+TEST(VerifierTest, StripInvalidDebugInfo) {
+ LLVMContext C;
+ Module M("M", C);
+ DIBuilder DIB(M);
+ DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/",
+ "unittest", false, "", 0);
+ DIB.finalize();
+ EXPECT_FALSE(verifyModule(M));
+
+ // Now break it.
+ auto *File = DIB.createFile("not-a-CU.f", ".");
+ NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");
+ NMD->addOperand(File);
+ EXPECT_TRUE(verifyModule(M));
+
+ ModulePassManager MPM(true);
+ MPM.addPass(VerifierPass(false));
+ ModuleAnalysisManager MAM(true);
+ MAM.registerPass([&] { return VerifierAnalysis(); });
+ MPM.run(M, MAM);
+ EXPECT_FALSE(verifyModule(M));
+}
+#endif
+
+TEST(VerifierTest, StripInvalidDebugInfoLegacy) {
+ LLVMContext C;
+ Module M("M", C);
+ DIBuilder DIB(M);
+ DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/",
+ "unittest", false, "", 0);
+ DIB.finalize();
+ EXPECT_FALSE(verifyModule(M));
+
+ // Now break it.
+ auto *File = DIB.createFile("not-a-CU.f", ".");
+ NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");
+ NMD->addOperand(File);
+ EXPECT_TRUE(verifyModule(M));
+
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass(false));
+ Passes.run(M);
+ EXPECT_FALSE(verifyModule(M));
+}
+
+} // end anonymous namespace
+} // end namespace llvm
diff --git a/unittests/IR/WaymarkTest.cpp b/unittests/IR/WaymarkTest.cpp
index a8924efed3f5..4d2671c06c1d 100644
--- a/unittests/IR/WaymarkTest.cpp
+++ b/unittests/IR/WaymarkTest.cpp
@@ -19,16 +19,14 @@
namespace llvm {
namespace {
-Constant *char2constant(char c) {
- return ConstantInt::get(Type::getInt8Ty(getGlobalContext()), c);
-}
-
-
TEST(WaymarkTest, NativeArray) {
+ LLVMContext Context;
static uint8_t tail[22] = "s02s33s30y2y0s1x0syxS";
Value * values[22];
- std::transform(tail, tail + 22, values, char2constant);
- FunctionType *FT = FunctionType::get(Type::getVoidTy(getGlobalContext()), true);
+ std::transform(tail, tail + 22, values, [&](char c) {
+ return ConstantInt::get(Type::getInt8Ty(Context), c);
+ });
+ FunctionType *FT = FunctionType::get(Type::getVoidTy(Context), true);
std::unique_ptr<Function> F(
Function::Create(FT, GlobalValue::ExternalLinkage));
const CallInst *A = CallInst::Create(F.get(), makeArrayRef(values));
diff --git a/unittests/LineEditor/Makefile b/unittests/LineEditor/Makefile
deleted file mode 100644
index 058b6e46eb93..000000000000
--- a/unittests/LineEditor/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/LineEditor/Makefile -----------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = LineEditor
-LINK_COMPONENTS := lineeditor
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/Linker/LinkModulesTest.cpp b/unittests/Linker/LinkModulesTest.cpp
index 322a44f8aafe..92c483278be9 100644
--- a/unittests/Linker/LinkModulesTest.cpp
+++ b/unittests/Linker/LinkModulesTest.cpp
@@ -202,30 +202,6 @@ TEST_F(LinkModuleTest, TypeMerge) {
M1->getNamedGlobal("t2")->getType());
}
-TEST_F(LinkModuleTest, CAPISuccess) {
- std::unique_ptr<Module> DestM(getExternal(Ctx, "foo"));
- std::unique_ptr<Module> SourceM(getExternal(Ctx, "bar"));
- char *errout = nullptr;
- LLVMBool result = LLVMLinkModules(wrap(DestM.get()), wrap(SourceM.get()),
- LLVMLinkerDestroySource, &errout);
- EXPECT_EQ(0, result);
- EXPECT_EQ(nullptr, errout);
- // "bar" is present in destination module
- EXPECT_NE(nullptr, DestM->getFunction("bar"));
-}
-
-TEST_F(LinkModuleTest, CAPIFailure) {
- // Symbol clash between two modules
- std::unique_ptr<Module> DestM(getExternal(Ctx, "foo"));
- std::unique_ptr<Module> SourceM(getExternal(Ctx, "foo"));
- char *errout = nullptr;
- LLVMBool result = LLVMLinkModules(wrap(DestM.get()), wrap(SourceM.get()),
- LLVMLinkerDestroySource, &errout);
- EXPECT_EQ(1, result);
- EXPECT_STREQ("Linking globals named 'foo': symbol multiply defined!", errout);
- LLVMDisposeMessage(errout);
-}
-
TEST_F(LinkModuleTest, NewCAPISuccess) {
std::unique_ptr<Module> DestM(getExternal(Ctx, "foo"));
std::unique_ptr<Module> SourceM(getExternal(Ctx, "bar"));
@@ -330,4 +306,58 @@ TEST_F(LinkModuleTest, MoveDistinctMDs) {
EXPECT_EQ(M3, M4->getOperand(0));
}
+TEST_F(LinkModuleTest, RemangleIntrinsics) {
+ LLVMContext C;
+ SMDiagnostic Err;
+
+ // We load two modules inside the same context C. In both modules there is a
+ // "struct.rtx_def" type. In the module loaded the second (Bar) this type will
+ // be renamed to "struct.rtx_def.0". Check that the intrinsics which have this
+ // type in the signature are properly remangled.
+ const char *FooStr =
+ "%struct.rtx_def = type { i16 }\n"
+ "define void @foo(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"
+ " call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"
+ " ret void\n"
+ "}\n"
+ "declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
+
+ const char *BarStr =
+ "%struct.rtx_def = type { i16 }\n"
+ "define void @bar(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"
+ " call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"
+ " ret void\n"
+ "}\n"
+ "declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
+
+ std::unique_ptr<Module> Foo = parseAssemblyString(FooStr, Err, C);
+ assert(Foo);
+ ASSERT_TRUE(Foo.get());
+ // Foo is loaded first, so the type and the intrinsic have theis original
+ // names.
+ ASSERT_TRUE(Foo->getFunction("llvm.memset.p0struct.rtx_def.i32"));
+ ASSERT_FALSE(Foo->getFunction("llvm.memset.p0struct.rtx_def.0.i32"));
+
+ std::unique_ptr<Module> Bar = parseAssemblyString(BarStr, Err, C);
+ assert(Bar);
+ ASSERT_TRUE(Bar.get());
+ // Bar is loaded after Foo, so the type is renamed to struct.rtx_def.0. Check
+ // that the intrinsic is also renamed.
+ ASSERT_FALSE(Bar->getFunction("llvm.memset.p0struct.rtx_def.i32"));
+ ASSERT_TRUE(Bar->getFunction("llvm.memset.p0struct.rtx_def.0.i32"));
+
+ // Link two modules together.
+ auto Dst = llvm::make_unique<Module>("Linked", C);
+ ASSERT_TRUE(Dst.get());
+ Ctx.setDiagnosticHandler(expectNoDiags);
+ bool Failed = Linker::linkModules(*Foo, std::move(Bar));
+ ASSERT_FALSE(Failed);
+
+ // "struct.rtx_def" from Foo and "struct.rtx_def.0" from Bar are isomorphic
+ // types, so they must be uniquified by linker. Check that they use the same
+ // intrinsic definition.
+ Function *F = Foo->getFunction("llvm.memset.p0struct.rtx_def.i32");
+ ASSERT_EQ(F->getNumUses(), (unsigned)2);
+}
+
} // end anonymous namespace
diff --git a/unittests/Linker/Makefile b/unittests/Linker/Makefile
deleted file mode 100644
index ddbce07d4a4d..000000000000
--- a/unittests/Linker/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Linker/Makefile ---------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = Linker
-LINK_COMPONENTS := core linker asmparser
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/MC/CMakeLists.txt b/unittests/MC/CMakeLists.txt
index f83eaf4779f9..c760c0267a9c 100644
--- a/unittests/MC/CMakeLists.txt
+++ b/unittests/MC/CMakeLists.txt
@@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(MCTests
Disassembler.cpp
+ DwarfLineTables.cpp
StringTableBuilderTest.cpp
- YAMLTest.cpp
+ TargetRegistry.cpp
)
diff --git a/unittests/MC/DwarfLineTables.cpp b/unittests/MC/DwarfLineTables.cpp
new file mode 100644
index 000000000000..4bfb5acea039
--- /dev/null
+++ b/unittests/MC/DwarfLineTables.cpp
@@ -0,0 +1,179 @@
+//===- llvm/unittest/MC/DwarfLineTables.cpp ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Dwarf.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDwarf.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+struct Context {
+ const char *Triple = "x86_64-pc-linux";
+ std::unique_ptr<MCRegisterInfo> MRI;
+ std::unique_ptr<MCAsmInfo> MAI;
+ std::unique_ptr<MCContext> Ctx;
+
+ Context() {
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllDisassemblers();
+
+ // If we didn't build x86, do not run the test.
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(Triple, Error);
+ if (!TheTarget)
+ return;
+
+ MRI.reset(TheTarget->createMCRegInfo(Triple));
+ MAI.reset(TheTarget->createMCAsmInfo(*MRI, Triple));
+ Ctx = llvm::make_unique<MCContext>(MAI.get(), MRI.get(), nullptr);
+ }
+
+ operator bool() { return Ctx.get(); }
+ operator MCContext &() { return *Ctx; };
+};
+
+Context Ctxt;
+}
+
+void verifyEncoding(MCDwarfLineTableParams Params, int LineDelta, int AddrDelta,
+ ArrayRef<uint8_t> ExpectedEncoding) {
+ SmallString<16> Buffer;
+ raw_svector_ostream EncodingOS(Buffer);
+ MCDwarfLineAddr::Encode(Ctxt, Params, LineDelta, AddrDelta, EncodingOS);
+ ArrayRef<uint8_t> Encoding(reinterpret_cast<uint8_t *>(Buffer.data()),
+ Buffer.size());
+ EXPECT_EQ(ExpectedEncoding, Encoding);
+}
+
+TEST(DwarfLineTables, TestDefaultParams) {
+ if (!Ctxt)
+ return;
+
+ MCDwarfLineTableParams Params;
+
+ // Minimal line offset expressible through extended opcode, 0 addr delta
+ const uint8_t Encoding0[] = {13}; // Special opcode Addr += 0, Line += -5
+ verifyEncoding(Params, -5, 0, Encoding0);
+
+ // Maximal line offset expressible through extended opcode,
+ const uint8_t Encoding1[] = {26}; // Special opcode Addr += 0, Line += +8
+ verifyEncoding(Params, 8, 0, Encoding1);
+
+ // Random value in the middle of the special ocode range
+ const uint8_t Encoding2[] = {146}; // Special opcode Addr += 9, Line += 2
+ verifyEncoding(Params, 2, 9, Encoding2);
+
+ // Minimal line offset expressible through extended opcode, max addr delta
+ const uint8_t Encoding3[] = {251}; // Special opcode Addr += 17, Line += -5
+ verifyEncoding(Params, -5, 17, Encoding3);
+
+ // Biggest special opcode
+ const uint8_t Encoding4[] = {255}; // Special opcode Addr += 17, Line += -1
+ verifyEncoding(Params, -1, 17, Encoding4);
+
+ // Line delta outside of the special opcode range, address delta in range
+ const uint8_t Encoding5[] = {dwarf::DW_LNS_advance_line, 9,
+ 158}; // Special opcode Addr += 10, Line += 0
+ verifyEncoding(Params, 9, 10, Encoding5);
+
+ // Address delta outside of the special opcode range, but small
+ // enough to do DW_LNS_const_add_pc + special opcode.
+ const uint8_t Encoding6[] = {dwarf::DW_LNS_const_add_pc, // pc += 17
+ 62}; // Special opcode Addr += 3, Line += 2
+ verifyEncoding(Params, 2, 20, Encoding6);
+
+ // Address delta big enough to require the use of DW_LNS_advance_pc
+ // Line delta in special opcode range
+ const uint8_t Encoding7[] = {dwarf::DW_LNS_advance_pc, 100,
+ 20}; // Special opcode Addr += 0, Line += 2
+ verifyEncoding(Params, 2, 100, Encoding7);
+
+ // No special opcode possible.
+ const uint8_t Encoding8[] = {dwarf::DW_LNS_advance_line, 20,
+ dwarf::DW_LNS_advance_pc, 100,
+ dwarf::DW_LNS_copy};
+ verifyEncoding(Params, 20, 100, Encoding8);
+}
+
+TEST(DwarfLineTables, TestCustomParams) {
+ if (!Ctxt)
+ return;
+
+ // Some tests against the example values given in the standard.
+ MCDwarfLineTableParams Params;
+ Params.DWARF2LineOpcodeBase = 13;
+ Params.DWARF2LineBase = -3;
+ Params.DWARF2LineRange = 12;
+
+ // Minimal line offset expressible through extended opcode, 0 addr delta
+ const uint8_t Encoding0[] = {13}; // Special opcode Addr += 0, Line += -5
+ verifyEncoding(Params, -3, 0, Encoding0);
+
+ // Maximal line offset expressible through extended opcode,
+ const uint8_t Encoding1[] = {24}; // Special opcode Addr += 0, Line += +8
+ verifyEncoding(Params, 8, 0, Encoding1);
+
+ // Random value in the middle of the special ocode range
+ const uint8_t Encoding2[] = {126}; // Special opcode Addr += 9, Line += 2
+ verifyEncoding(Params, 2, 9, Encoding2);
+
+ // Minimal line offset expressible through extended opcode, max addr delta
+ const uint8_t Encoding3[] = {253}; // Special opcode Addr += 20, Line += -3
+ verifyEncoding(Params, -3, 20, Encoding3);
+
+ // Biggest special opcode
+ const uint8_t Encoding4[] = {255}; // Special opcode Addr += 17, Line += -1
+ verifyEncoding(Params, -1, 20, Encoding4);
+
+ // Line delta outside of the special opcode range, address delta in range
+ const uint8_t Encoding5[] = {dwarf::DW_LNS_advance_line, 9,
+ 136}; // Special opcode Addr += 10, Line += 0
+ verifyEncoding(Params, 9, 10, Encoding5);
+
+ // Address delta outside of the special opcode range, but small
+ // enough to do DW_LNS_const_add_pc + special opcode.
+ const uint8_t Encoding6[] = {dwarf::DW_LNS_const_add_pc, // pc += 20
+ 138}; // Special opcode Addr += 10, Line += 2
+ verifyEncoding(Params, 2, 30, Encoding6);
+
+ // Address delta big enough to require the use of DW_LNS_advance_pc
+ // Line delta in special opcode range
+ const uint8_t Encoding7[] = {dwarf::DW_LNS_advance_pc, 100,
+ 18}; // Special opcode Addr += 0, Line += 2
+ verifyEncoding(Params, 2, 100, Encoding7);
+
+ // No special opcode possible.
+ const uint8_t Encoding8[] = {dwarf::DW_LNS_advance_line, 20,
+ dwarf::DW_LNS_advance_pc, 100,
+ dwarf::DW_LNS_copy};
+ verifyEncoding(Params, 20, 100, Encoding8);
+}
+
+TEST(DwarfLineTables, TestCustomParams2) {
+ if (!Ctxt)
+ return;
+
+ // Corner case param values.
+ MCDwarfLineTableParams Params;
+ Params.DWARF2LineOpcodeBase = 13;
+ Params.DWARF2LineBase = 1;
+ Params.DWARF2LineRange = 255;
+
+ const uint8_t Encoding0[] = {dwarf::DW_LNS_advance_line, 248, 1,
+ dwarf::DW_LNS_copy};
+ verifyEncoding(Params, 248, 0, Encoding0);
+}
diff --git a/unittests/MC/Makefile b/unittests/MC/Makefile
deleted file mode 100644
index 3f8d1ef9555c..000000000000
--- a/unittests/MC/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/IR/Makefile -------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = MC
-LINK_COMPONENTS := all-targets MCDisassembler Object
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/MC/StringTableBuilderTest.cpp b/unittests/MC/StringTableBuilderTest.cpp
index 4cc0bda0a03a..f78d3588ffff 100644
--- a/unittests/MC/StringTableBuilderTest.cpp
+++ b/unittests/MC/StringTableBuilderTest.cpp
@@ -68,4 +68,27 @@ TEST(StringTableBuilderTest, BasicWinCOFF) {
EXPECT_EQ(23U, B.getOffset("river horse"));
}
+TEST(StringTableBuilderTest, ELFInOrder) {
+ StringTableBuilder B(StringTableBuilder::ELF);
+ EXPECT_EQ(1U, B.add("foo"));
+ EXPECT_EQ(5U, B.add("bar"));
+ EXPECT_EQ(9U, B.add("foobar"));
+
+ B.finalizeInOrder();
+
+ std::string Expected;
+ Expected += '\x00';
+ Expected += "foo";
+ Expected += '\x00';
+ Expected += "bar";
+ Expected += '\x00';
+ Expected += "foobar";
+ Expected += '\x00';
+
+ EXPECT_EQ(Expected, B.data());
+ EXPECT_EQ(1U, B.getOffset("foo"));
+ EXPECT_EQ(5U, B.getOffset("bar"));
+ EXPECT_EQ(9U, B.getOffset("foobar"));
+}
+
}
diff --git a/unittests/Support/TargetRegistry.cpp b/unittests/MC/TargetRegistry.cpp
index ae89c8b64930..eb46b22f06c1 100644
--- a/unittests/Support/TargetRegistry.cpp
+++ b/unittests/MC/TargetRegistry.cpp
@@ -1,4 +1,4 @@
-//===- unittests/Support/TargetRegistry.cpp - -----------------------------===//
+//===- unittests/MC/TargetRegistry.cpp ------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,6 +7,10 @@
//
//===----------------------------------------------------------------------===//
+// The target registry code lives in Support, but it relies on linking in all
+// LLVM targets. We keep this test with the MC tests, which already do that, to
+// keep the SupportTests target small.
+
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "gtest/gtest.h"
diff --git a/unittests/MI/CMakeLists.txt b/unittests/MI/CMakeLists.txt
new file mode 100644
index 000000000000..595497f3d960
--- /dev/null
+++ b/unittests/MI/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ CodeGen
+ Core
+ MC
+ MIRParser
+ Support
+ Target
+ )
+
+add_llvm_unittest(MITests
+ LiveIntervalTest.cpp
+ )
diff --git a/unittests/MI/LiveIntervalTest.cpp b/unittests/MI/LiveIntervalTest.cpp
new file mode 100644
index 000000000000..e0b3d5529afb
--- /dev/null
+++ b/unittests/MI/LiveIntervalTest.cpp
@@ -0,0 +1,360 @@
+#include "gtest/gtest.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/CodeGen/LiveIntervalAnalysis.h"
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/Target/TargetRegisterInfo.h"
+#include "llvm/IR/LegacyPassManager.h"
+
+using namespace llvm;
+
+namespace llvm {
+ void initializeTestPassPass(PassRegistry &);
+}
+
+namespace {
+
+void initLLVM() {
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+
+ PassRegistry *Registry = PassRegistry::getPassRegistry();
+ initializeCore(*Registry);
+ initializeCodeGen(*Registry);
+}
+
+/// Create a TargetMachine. As we lack a dedicated always available target for
+/// unittests, we go for "x86_64" which should be available in most builds.
+std::unique_ptr<TargetMachine> createTargetMachine() {
+ Triple TargetTriple("x86_64--");
+ std::string Error;
+ const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
+ if (!T)
+ return nullptr;
+
+ TargetOptions Options;
+ return std::unique_ptr<TargetMachine>(
+ T->createTargetMachine("x86_64", "", "", Options, None,
+ CodeModel::Default, CodeGenOpt::Aggressive));
+}
+
+std::unique_ptr<Module> parseMIR(LLVMContext &Context,
+ legacy::PassManagerBase &PM, std::unique_ptr<MIRParser> &MIR,
+ const TargetMachine &TM, StringRef MIRCode, const char *FuncName) {
+ SMDiagnostic Diagnostic;
+ std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);
+ MIR = createMIRParser(std::move(MBuffer), Context);
+ if (!MIR)
+ return nullptr;
+
+ std::unique_ptr<Module> M = MIR->parseLLVMModule();
+ if (!M)
+ return nullptr;
+
+ M->setDataLayout(TM.createDataLayout());
+
+ Function *F = M->getFunction(FuncName);
+ if (!F)
+ return nullptr;
+
+ const LLVMTargetMachine &LLVMTM = static_cast<const LLVMTargetMachine&>(TM);
+ LLVMTM.addMachineModuleInfo(PM);
+ LLVMTM.addMachineFunctionAnalysis(PM, MIR.get());
+
+ return M;
+}
+
+typedef std::function<void(MachineFunction&,LiveIntervals&)> LiveIntervalTest;
+
+struct TestPass : public MachineFunctionPass {
+ static char ID;
+ TestPass() : MachineFunctionPass(ID) {
+ // We should never call this but always use PM.add(new TestPass(...))
+ abort();
+ }
+ TestPass(LiveIntervalTest T) : MachineFunctionPass(ID), T(T) {
+ initializeTestPassPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnMachineFunction(MachineFunction &MF) override {
+ LiveIntervals &LIS = getAnalysis<LiveIntervals>();
+ T(MF, LIS);
+ EXPECT_TRUE(MF.verify(this));
+ return true;
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ AU.addRequired<LiveIntervals>();
+ AU.addPreserved<LiveIntervals>();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+private:
+ LiveIntervalTest T;
+};
+
+/**
+ * Move instruction number \p From in front of instruction number \p To and
+ * update affected liveness intervals with LiveIntervalAnalysis::handleMove().
+ */
+static void testHandleMove(MachineFunction &MF, LiveIntervals &LIS,
+ unsigned From, unsigned To, unsigned BlockNum = 0) {
+ MachineBasicBlock &MBB = *MF.getBlockNumbered(BlockNum);
+
+ unsigned I = 0;
+ MachineInstr *FromInstr = nullptr;
+ MachineInstr *ToInstr = nullptr;
+ for (MachineInstr &MI : MBB) {
+ if (I == From)
+ FromInstr = &MI;
+ if (I == To)
+ ToInstr = &MI;
+ ++I;
+ }
+ assert(FromInstr != nullptr && ToInstr != nullptr);
+
+ MBB.splice(ToInstr->getIterator(), &MBB, FromInstr->getIterator());
+ LIS.handleMove(*FromInstr, true);
+}
+
+static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) {
+ LLVMContext Context;
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ // This test is designed for the X86 backend; stop if it is not available.
+ if (!TM)
+ return;
+
+ legacy::PassManager PM;
+
+ SmallString<160> S;
+ StringRef MIRString = (Twine(
+"---\n"
+"...\n"
+"name: func\n"
+"registers:\n"
+" - { id: 0, class: gr64 }\n"
+"body: |\n"
+" bb.0:\n"
+ ) + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S);
+ std::unique_ptr<MIRParser> MIR;
+ std::unique_ptr<Module> M = parseMIR(Context, PM, MIR, *TM, MIRString,
+ "func");
+
+ PM.add(new TestPass(T));
+
+ PM.run(*M);
+}
+
+} // End of anonymous namespace.
+
+char TestPass::ID = 0;
+INITIALIZE_PASS(TestPass, "testpass", "testpass", false, false)
+
+TEST(LiveIntervalTest, MoveUpDef) {
+ // Value defined.
+ liveIntervalTest(
+" NOOP\n"
+" NOOP\n"
+" early-clobber %0 = IMPLICIT_DEF\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 2, 1);
+ });
+}
+
+TEST(LiveIntervalTest, MoveUpRedef) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 2, 1);
+ });
+}
+
+TEST(LiveIntervalTest, MoveUpEarlyDef) {
+ liveIntervalTest(
+" NOOP\n"
+" NOOP\n"
+" early-clobber %0 = IMPLICIT_DEF\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 2, 1);
+ });
+}
+
+TEST(LiveIntervalTest, MoveUpEarlyRedef) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 2, 1);
+ });
+}
+
+TEST(LiveIntervalTest, MoveUpKill) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" NOOP implicit %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 2, 1);
+ });
+}
+
+TEST(LiveIntervalTest, MoveUpKillFollowing) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" NOOP implicit %0\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 2, 1);
+ });
+}
+
+// TODO: Construct a situation where we have intervals following a hole
+// while still having connected components.
+
+TEST(LiveIntervalTest, MoveDownDef) {
+ // Value defined.
+ liveIntervalTest(
+" NOOP\n"
+" early-clobber %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 1, 2);
+ });
+}
+
+TEST(LiveIntervalTest, MoveDownRedef) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
+" NOOP\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 1, 2);
+ });
+}
+
+TEST(LiveIntervalTest, MoveDownEarlyDef) {
+ liveIntervalTest(
+" NOOP\n"
+" early-clobber %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 1, 2);
+ });
+}
+
+TEST(LiveIntervalTest, MoveDownEarlyRedef) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
+" NOOP\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 1, 2);
+ });
+}
+
+TEST(LiveIntervalTest, MoveDownKill) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP implicit %0\n"
+" NOOP\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 1, 2);
+ });
+}
+
+TEST(LiveIntervalTest, MoveDownKillFollowing) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" NOOP implicit %0\n"
+" RETQ %0\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 1, 2);
+ });
+}
+
+TEST(LiveIntervalTest, MoveUndefUse) {
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP implicit undef %0\n"
+" NOOP implicit %0\n"
+" NOOP\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 1, 3);
+ });
+}
+
+TEST(LiveIntervalTest, MoveUpValNos) {
+ // handleMoveUp() had a bug where it would reuse the value number of the
+ // destination segment, even though we have no guarntee that this valno wasn't
+ // used in other segments.
+ liveIntervalTest(
+" successors: %bb.1, %bb.2\n"
+" %0 = IMPLICIT_DEF\n"
+" JG_1 %bb.2, implicit %eflags\n"
+" JMP_1 %bb.1\n"
+" bb.2:\n"
+" NOOP implicit %0\n"
+" bb.1:\n"
+" successors: %bb.2\n"
+" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
+" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
+" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
+" JMP_1 %bb.2\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 2, 0, 2);
+ });
+}
+
+TEST(LiveIntervalTest, MoveOverUndefUse0) {
+ // findLastUseBefore() used by handleMoveUp() must ignore undef operands.
+ liveIntervalTest(
+" %0 = IMPLICIT_DEF\n"
+" NOOP\n"
+" NOOP implicit undef %0\n"
+" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 3, 1);
+ });
+}
+
+TEST(LiveIntervalTest, MoveOverUndefUse1) {
+ // findLastUseBefore() used by handleMoveUp() must ignore undef operands.
+ liveIntervalTest(
+" %rax = IMPLICIT_DEF\n"
+" NOOP\n"
+" NOOP implicit undef %rax\n"
+" %rax = IMPLICIT_DEF implicit %rax(tied-def 0)\n",
+ [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 3, 1);
+ });
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ initLLVM();
+ return RUN_ALL_TESTS();
+}
diff --git a/unittests/Makefile b/unittests/Makefile
deleted file mode 100644
index bf2ed22efea7..000000000000
--- a/unittests/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-##===- unittests/Makefile ----------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ..
-
-PARALLEL_DIRS = ADT Analysis AsmParser Bitcode CodeGen DebugInfo \
- ExecutionEngine IR LineEditor Linker MC Option ProfileData \
- Support Transforms
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
-
-clean::
- $(Verb) $(RM) -f *Tests
diff --git a/unittests/Makefile.unittest b/unittests/Makefile.unittest
deleted file mode 100644
index a39edc675698..000000000000
--- a/unittests/Makefile.unittest
+++ /dev/null
@@ -1,69 +0,0 @@
-##===- unittests/Makefile.unittest -------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-#
-# This file is included by all of the unit test makefiles.
-#
-##===----------------------------------------------------------------------===##
-
-ifndef MAKEFILE_UNITTEST_NO_INCLUDE_COMMON
-include $(LEVEL)/Makefile.common
-endif
-
-# Clean up out-of-tree stray unittests for Lit not to pick one up.
-.PHONY: cleanup-local
-cleanup-local:
- -$(Verb) $(FIND) $(filter-out $(PARALLEL_DIRS), $(wildcard *)) -type f \
- -path '*/$(BuildMode)/*Tests$(EXEEXT)' \
- -exec rm -f '{}' \;
-
-all:: cleanup-local
-clean:: cleanup-local
-
-# Set up variables for building a unit test.
-ifdef TESTNAME
-
-LLVMUnitTestExe = $(BuildMode)/$(TESTNAME)Tests$(EXEEXT)
-
-# Note that these flags are duplicated when building GoogleTest itself in
-# utils/unittest/googletest/Makefile; ensure that any changes are made to both.
-CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include
-CPP.Flags += $(NO_MISSING_FIELD_INITIALIZERS) $(NO_VARIADIC_MACROS)
-CPP.Flags += -DGTEST_HAS_RTTI=0
-# libstdc++'s TR1 <tuple> header depends on RTTI and uses C++'0x features not
-# supported by Clang, so force googletest to use its own tuple implementation.
-CPP.Flags += -DGTEST_USE_OWN_TR1_TUPLE
-
-# Disable pthreads if LLVM was configured without them.
-ifneq ($(HAVE_PTHREAD), 1)
- CPP.Flags += -DGTEST_HAS_PTHREAD=0
-endif
-
-TESTLIBS = -lgtest -lgtest_main
-
-ifeq ($(ENABLE_SHARED), 1)
- ifneq (,$(RPATH))
- # Add the absolute path to the dynamic library. This is ok because
- # we'll never install unittests.
- LD.Flags += $(RPATH) -Wl,$(SharedLibDir)
- endif
-endif
-
-$(LLVMUnitTestExe): $(ObjectsO) $(ProjLibsPaths) $(LLVMLibsPaths)
- $(Echo) Linking $(BuildMode) unit test $(TESTNAME) $(StripWarnMsg)
- $(Verb) $(Link) -o $@ $(TOOLLINKOPTS) $(ObjectsO) $(ProjLibsOptions) \
- $(TESTLIBS) $(LLVMLibsOptions) $(ExtraLibs) $(TOOLLINKOPTSB) $(LIBS)
- $(Echo) ======= Finished Linking $(BuildMode) Unit test $(TESTNAME) \
- $(StripWarnMsg)
-
-all:: $(LLVMUnitTestExe)
-
-unitcheck:: $(LLVMUnitTestExe)
- $(LLVMUnitTestExe)
-
-endif
diff --git a/unittests/ObjectYAML/CMakeLists.txt b/unittests/ObjectYAML/CMakeLists.txt
new file mode 100644
index 000000000000..baed10b8730f
--- /dev/null
+++ b/unittests/ObjectYAML/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(LLVM_LINK_COMPONENTS
+ ObjectYAML
+ )
+
+add_llvm_unittest(ObjectYAMLTests
+ YAMLTest.cpp
+ )
+
diff --git a/unittests/MC/YAMLTest.cpp b/unittests/ObjectYAML/YAMLTest.cpp
index 09709ad73fc7..606e160d19a5 100644
--- a/unittests/MC/YAMLTest.cpp
+++ b/unittests/ObjectYAML/YAMLTest.cpp
@@ -1,4 +1,4 @@
-//===- llvm/unittest/Object/YAMLTest.cpp - Tests for Object YAML ----------===//
+//===- YAMLTest.cpp - Tests for Object YAML -------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/MC/YAML.h"
+#include "llvm/ObjectYAML/YAML.h"
#include "llvm/Support/YAMLTraits.h"
#include "gtest/gtest.h"
diff --git a/unittests/Option/Makefile b/unittests/Option/Makefile
deleted file mode 100644
index 8c90a83da872..000000000000
--- a/unittests/Option/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-##===- unittests/Option/Makefile ---------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = Option
-LINK_COMPONENTS := option support
-
-BUILT_SOURCES = Opts.inc
-TABLEGEN_INC_FILES_COMMON = 1
-
-include $(LEVEL)/Makefile.config
-
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
-
-$(ObjDir)/Opts.inc.tmp : Opts.td $(LLVM_TBLGEN) $(ObjDir)/.dir
- $(Echo) "Building Driver Option tables with tblgen"
- $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
diff --git a/unittests/Option/OptionParsingTest.cpp b/unittests/Option/OptionParsingTest.cpp
index 5270dc940f96..b0418a71c786 100644
--- a/unittests/Option/OptionParsingTest.cpp
+++ b/unittests/Option/OptionParsingTest.cpp
@@ -79,32 +79,32 @@ TEST(Option, OptionParsing) {
EXPECT_TRUE(AL.hasArg(OPT_G));
// Check the values.
- EXPECT_EQ(AL.getLastArgValue(OPT_B), "hi");
- EXPECT_EQ(AL.getLastArgValue(OPT_C), "bye");
- EXPECT_EQ(AL.getLastArgValue(OPT_D), "adena");
+ EXPECT_EQ("hi", AL.getLastArgValue(OPT_B));
+ EXPECT_EQ("bye", AL.getLastArgValue(OPT_C));
+ EXPECT_EQ("adena", AL.getLastArgValue(OPT_D));
std::vector<std::string> Es = AL.getAllArgValues(OPT_E);
- EXPECT_EQ(Es[0], "apple");
- EXPECT_EQ(Es[1], "bloom");
- EXPECT_EQ(AL.getLastArgValue(OPT_F), "42");
+ EXPECT_EQ("apple", Es[0]);
+ EXPECT_EQ("bloom", Es[1]);
+ EXPECT_EQ("42", AL.getLastArgValue(OPT_F));
std::vector<std::string> Gs = AL.getAllArgValues(OPT_G);
- EXPECT_EQ(Gs[0], "chuu");
- EXPECT_EQ(Gs[1], "2");
+ EXPECT_EQ("chuu", Gs[0]);
+ EXPECT_EQ("2", Gs[1]);
// Check the help text.
std::string Help;
raw_string_ostream RSO(Help);
T.PrintHelp(RSO, "test", "title!");
- EXPECT_NE(Help.find("-A"), std::string::npos);
+ EXPECT_NE(std::string::npos, Help.find("-A"));
// Test aliases.
arg_iterator Cs = AL.filtered_begin(OPT_C);
- ASSERT_NE(Cs, AL.filtered_end());
- EXPECT_EQ(StringRef((*Cs)->getValue()), "desu");
+ ASSERT_NE(AL.filtered_end(), Cs);
+ EXPECT_EQ("desu", StringRef((*Cs)->getValue()));
ArgStringList ASL;
(*Cs)->render(AL, ASL);
- ASSERT_EQ(ASL.size(), 2u);
- EXPECT_EQ(StringRef(ASL[0]), "-C");
- EXPECT_EQ(StringRef(ASL[1]), "desu");
+ ASSERT_EQ(2u, ASL.size());
+ EXPECT_EQ("-C", StringRef(ASL[0]));
+ EXPECT_EQ("desu", StringRef(ASL[1]));
}
TEST(Option, ParseWithFlagExclusions) {
@@ -131,8 +131,8 @@ TEST(Option, ParseWithFlagExclusions) {
AL = T.ParseArgs(NewArgs, MAI, MAC);
EXPECT_TRUE(AL.hasArg(OPT_SLASH_C));
EXPECT_TRUE(AL.hasArg(OPT_C));
- EXPECT_EQ(AL.getLastArgValue(OPT_SLASH_C), "foo");
- EXPECT_EQ(AL.getLastArgValue(OPT_C), "bar");
+ EXPECT_EQ("foo", AL.getLastArgValue(OPT_SLASH_C));
+ EXPECT_EQ("bar", AL.getLastArgValue(OPT_C));
}
TEST(Option, ParseAliasInGroup) {
@@ -151,8 +151,8 @@ TEST(Option, AliasArgs) {
const char *MyArgs[] = { "-J", "-Joo" };
InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
EXPECT_TRUE(AL.hasArg(OPT_B));
- EXPECT_EQ(AL.getAllArgValues(OPT_B)[0], "foo");
- EXPECT_EQ(AL.getAllArgValues(OPT_B)[1], "bar");
+ EXPECT_EQ("foo", AL.getAllArgValues(OPT_B)[0]);
+ EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]);
}
TEST(Option, IgnoreCase) {
@@ -183,7 +183,7 @@ TEST(Option, SlurpEmpty) {
InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
EXPECT_TRUE(AL.hasArg(OPT_A));
EXPECT_TRUE(AL.hasArg(OPT_Slurp));
- EXPECT_EQ(AL.getAllArgValues(OPT_Slurp).size(), 0U);
+ EXPECT_EQ(0U, AL.getAllArgValues(OPT_Slurp).size());
}
TEST(Option, Slurp) {
@@ -196,10 +196,61 @@ TEST(Option, Slurp) {
EXPECT_TRUE(AL.hasArg(OPT_A));
EXPECT_FALSE(AL.hasArg(OPT_B));
EXPECT_TRUE(AL.hasArg(OPT_Slurp));
- EXPECT_EQ(AL.getAllArgValues(OPT_Slurp).size(), 3U);
- EXPECT_EQ(AL.getAllArgValues(OPT_Slurp)[0], "-B");
- EXPECT_EQ(AL.getAllArgValues(OPT_Slurp)[1], "--");
- EXPECT_EQ(AL.getAllArgValues(OPT_Slurp)[2], "foo");
+ EXPECT_EQ(3U, AL.getAllArgValues(OPT_Slurp).size());
+ EXPECT_EQ("-B", AL.getAllArgValues(OPT_Slurp)[0]);
+ EXPECT_EQ("--", AL.getAllArgValues(OPT_Slurp)[1]);
+ EXPECT_EQ("foo", AL.getAllArgValues(OPT_Slurp)[2]);
+}
+
+TEST(Option, SlurpJoinedEmpty) {
+ TestOptTable T;
+ unsigned MAI, MAC;
+
+ const char *MyArgs[] = { "-A", "-slurpjoined" };
+ InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
+ EXPECT_TRUE(AL.hasArg(OPT_A));
+ EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
+ EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 0U);
+}
+
+TEST(Option, SlurpJoinedOneJoined) {
+ TestOptTable T;
+ unsigned MAI, MAC;
+
+ const char *MyArgs[] = { "-A", "-slurpjoinedfoo" };
+ InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
+ EXPECT_TRUE(AL.hasArg(OPT_A));
+ EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
+ EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 1U);
+ EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined)[0], "foo");
+}
+
+TEST(Option, SlurpJoinedAndSeparate) {
+ TestOptTable T;
+ unsigned MAI, MAC;
+
+ const char *MyArgs[] = { "-A", "-slurpjoinedfoo", "bar", "baz" };
+ InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
+ EXPECT_TRUE(AL.hasArg(OPT_A));
+ EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
+ EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size());
+ EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]);
+ EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]);
+ EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]);
+}
+
+TEST(Option, SlurpJoinedButSeparate) {
+ TestOptTable T;
+ unsigned MAI, MAC;
+
+ const char *MyArgs[] = { "-A", "-slurpjoined", "foo", "bar", "baz" };
+ InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
+ EXPECT_TRUE(AL.hasArg(OPT_A));
+ EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
+ EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size());
+ EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]);
+ EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]);
+ EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]);
}
TEST(Option, FlagAliasToJoined) {
@@ -211,6 +262,6 @@ TEST(Option, FlagAliasToJoined) {
InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
EXPECT_EQ(AL.size(), 1U);
EXPECT_TRUE(AL.hasArg(OPT_B));
- EXPECT_EQ(AL.getAllArgValues(OPT_B).size(), 1U);
- EXPECT_EQ(AL.getAllArgValues(OPT_B)[0], "");
+ EXPECT_EQ(1U, AL.getAllArgValues(OPT_B).size());
+ EXPECT_EQ("", AL.getAllArgValues(OPT_B)[0]);
}
diff --git a/unittests/Option/Opts.td b/unittests/Option/Opts.td
index c96774a68e0b..25c98c6f6015 100644
--- a/unittests/Option/Opts.td
+++ b/unittests/Option/Opts.td
@@ -26,3 +26,5 @@ def Joo : Flag<["-"], "Joo">, Alias<B>, AliasArgs<["bar"]>;
def K : Flag<["-"], "K">, Alias<B>;
def Slurp : Option<["-"], "slurp", KIND_REMAINING_ARGS>;
+
+def SlurpJoined : Option<["-"], "slurpjoined", KIND_REMAINING_ARGS_JOINED>;
diff --git a/unittests/ProfileData/CMakeLists.txt b/unittests/ProfileData/CMakeLists.txt
index 011f8c581792..dd39ca7da3ad 100644
--- a/unittests/ProfileData/CMakeLists.txt
+++ b/unittests/ProfileData/CMakeLists.txt
@@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
Core
+ Coverage
ProfileData
Support
)
diff --git a/unittests/ProfileData/CoverageMappingTest.cpp b/unittests/ProfileData/CoverageMappingTest.cpp
index 35b8626c494a..53b40ebae85a 100644
--- a/unittests/ProfileData/CoverageMappingTest.cpp
+++ b/unittests/ProfileData/CoverageMappingTest.cpp
@@ -7,24 +7,24 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ProfileData/CoverageMapping.h"
-#include "llvm/ProfileData/CoverageMappingReader.h"
-#include "llvm/ProfileData/CoverageMappingWriter.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/InstrProfWriter.h"
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
-#include <sstream>
+#include <ostream>
using namespace llvm;
using namespace coverage;
-static ::testing::AssertionResult NoError(std::error_code EC) {
- if (!EC)
+static ::testing::AssertionResult NoError(Error E) {
+ if (!E)
return ::testing::AssertionSuccess();
- return ::testing::AssertionFailure() << "error " << EC.value()
- << ": " << EC.message();
+ return ::testing::AssertionFailure() << "error: " << toString(std::move(E))
+ << "\n";
}
namespace llvm {
@@ -49,41 +49,58 @@ void PrintTo(const CoverageSegment &S, ::std::ostream *os) {
namespace {
-struct OneFunctionCoverageReader : CoverageMappingReader {
+struct OutputFunctionCoverageData {
StringRef Name;
uint64_t Hash;
std::vector<StringRef> Filenames;
- ArrayRef<CounterMappingRegion> Regions;
- bool Done;
-
- OneFunctionCoverageReader(StringRef Name, uint64_t Hash,
- ArrayRef<StringRef> Filenames,
- ArrayRef<CounterMappingRegion> Regions)
- : Name(Name), Hash(Hash), Filenames(Filenames), Regions(Regions),
- Done(false) {}
-
- std::error_code readNextRecord(CoverageMappingRecord &Record) override {
- if (Done)
- return instrprof_error::eof;
- Done = true;
+ std::vector<CounterMappingRegion> Regions;
+ void fillCoverageMappingRecord(CoverageMappingRecord &Record) const {
Record.FunctionName = Name;
Record.FunctionHash = Hash;
Record.Filenames = Filenames;
Record.Expressions = {};
Record.MappingRegions = Regions;
- return instrprof_error::success;
}
};
+struct CoverageMappingReaderMock : CoverageMappingReader {
+ ArrayRef<OutputFunctionCoverageData> Functions;
+
+ CoverageMappingReaderMock(ArrayRef<OutputFunctionCoverageData> Functions)
+ : Functions(Functions) {}
+
+ Error readNextRecord(CoverageMappingRecord &Record) override {
+ if (Functions.empty())
+ return make_error<CoverageMapError>(coveragemap_error::eof);
+
+ Functions.front().fillCoverageMappingRecord(Record);
+ Functions = Functions.slice(1);
+
+ return Error::success();
+ }
+};
+
+struct InputFunctionCoverageData {
+ // Maps the global file index from CoverageMappingTest.Files
+ // to the index of that file within this function. We can't just use
+ // global file indexes here because local indexes have to be dense.
+ // This map is used during serialization to create the virtual file mapping
+ // (from local fileId to global Index) in the head of the per-function
+ // coverage mapping data.
+ SmallDenseMap<unsigned, unsigned> ReverseVirtualFileMapping;
+ std::string Name;
+ uint64_t Hash;
+ std::vector<CounterMappingRegion> Regions;
+
+ InputFunctionCoverageData(std::string Name, uint64_t Hash)
+ : Name(std::move(Name)), Hash(Hash) {}
+};
+
struct CoverageMappingTest : ::testing::Test {
StringMap<unsigned> Files;
- unsigned NextFile;
- std::vector<CounterMappingRegion> InputCMRs;
-
- std::vector<StringRef> OutputFiles;
- std::vector<CounterExpression> OutputExpressions;
- std::vector<CounterMappingRegion> OutputCMRs;
+ std::vector<InputFunctionCoverageData> InputFunctions;
+ std::vector<OutputFunctionCoverageData> OutputFunctions;
InstrProfWriter ProfileWriter;
std::unique_ptr<IndexedInstrProfReader> ProfileReader;
@@ -91,112 +108,242 @@ struct CoverageMappingTest : ::testing::Test {
std::unique_ptr<CoverageMapping> LoadedCoverage;
void SetUp() override {
- NextFile = 0;
+ ProfileWriter.setOutputSparse(false);
}
- unsigned getFile(StringRef Name) {
+ unsigned getGlobalFileIndex(StringRef Name) {
auto R = Files.find(Name);
if (R != Files.end())
return R->second;
- Files[Name] = NextFile;
- return NextFile++;
+ unsigned Index = Files.size();
+ Files.emplace_second(Name, Index);
+ return Index;
+ }
+
+ // Return the file index of file 'Name' for the current function.
+ // Add the file into the global map if necesary.
+ // See also InputFunctionCoverageData::ReverseVirtualFileMapping
+ // for additional comments.
+ unsigned getFileIndexForFunction(StringRef Name) {
+ unsigned GlobalIndex = getGlobalFileIndex(Name);
+ auto &CurrentFunctionFileMapping =
+ InputFunctions.back().ReverseVirtualFileMapping;
+ auto R = CurrentFunctionFileMapping.find(GlobalIndex);
+ if (R != CurrentFunctionFileMapping.end())
+ return R->second;
+ unsigned IndexInFunction = CurrentFunctionFileMapping.size();
+ CurrentFunctionFileMapping.insert(
+ std::make_pair(GlobalIndex, IndexInFunction));
+ return IndexInFunction;
+ }
+
+ void startFunction(StringRef FuncName, uint64_t Hash) {
+ InputFunctions.emplace_back(FuncName.str(), Hash);
}
void addCMR(Counter C, StringRef File, unsigned LS, unsigned CS, unsigned LE,
unsigned CE) {
- InputCMRs.push_back(
- CounterMappingRegion::makeRegion(C, getFile(File), LS, CS, LE, CE));
+ InputFunctions.back().Regions.push_back(CounterMappingRegion::makeRegion(
+ C, getFileIndexForFunction(File), LS, CS, LE, CE));
}
void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS,
unsigned CS, unsigned LE, unsigned CE) {
- InputCMRs.push_back(CounterMappingRegion::makeExpansion(
- getFile(File), getFile(ExpandedFile), LS, CS, LE, CE));
+ InputFunctions.back().Regions.push_back(CounterMappingRegion::makeExpansion(
+ getFileIndexForFunction(File), getFileIndexForFunction(ExpandedFile),
+ LS, CS, LE, CE));
}
- std::string writeCoverageRegions() {
- SmallVector<unsigned, 8> FileIDs;
- for (const auto &E : Files)
- FileIDs.push_back(E.getValue());
+ std::string writeCoverageRegions(InputFunctionCoverageData &Data) {
+ SmallVector<unsigned, 8> FileIDs(Data.ReverseVirtualFileMapping.size());
+ for (const auto &E : Data.ReverseVirtualFileMapping)
+ FileIDs[E.second] = E.first;
std::string Coverage;
llvm::raw_string_ostream OS(Coverage);
- CoverageMappingWriter(FileIDs, None, InputCMRs).write(OS);
+ CoverageMappingWriter(FileIDs, None, Data.Regions).write(OS);
return OS.str();
}
- void readCoverageRegions(std::string Coverage) {
- SmallVector<StringRef, 8> Filenames;
+ void readCoverageRegions(std::string Coverage,
+ OutputFunctionCoverageData &Data) {
+ SmallVector<StringRef, 8> Filenames(Files.size());
for (const auto &E : Files)
- Filenames.push_back(E.getKey());
- RawCoverageMappingReader Reader(Coverage, Filenames, OutputFiles,
- OutputExpressions, OutputCMRs);
+ Filenames[E.getValue()] = E.getKey();
+ std::vector<CounterExpression> Expressions;
+ RawCoverageMappingReader Reader(Coverage, Filenames, Data.Filenames,
+ Expressions, Data.Regions);
ASSERT_TRUE(NoError(Reader.read()));
}
+ void writeAndReadCoverageRegions(bool EmitFilenames = true) {
+ OutputFunctions.resize(InputFunctions.size());
+ for (unsigned I = 0; I < InputFunctions.size(); ++I) {
+ std::string Regions = writeCoverageRegions(InputFunctions[I]);
+ readCoverageRegions(Regions, OutputFunctions[I]);
+ OutputFunctions[I].Name = InputFunctions[I].Name;
+ OutputFunctions[I].Hash = InputFunctions[I].Hash;
+ if (!EmitFilenames)
+ OutputFunctions[I].Filenames.clear();
+ }
+ }
+
void readProfCounts() {
auto Profile = ProfileWriter.writeBuffer();
auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile));
- ASSERT_TRUE(NoError(ReaderOrErr.getError()));
+ ASSERT_TRUE(NoError(ReaderOrErr.takeError()));
ProfileReader = std::move(ReaderOrErr.get());
}
- void loadCoverageMapping(StringRef FuncName, uint64_t Hash) {
- std::string Regions = writeCoverageRegions();
- readCoverageRegions(Regions);
+ void loadCoverageMapping(bool EmitFilenames = true) {
+ readProfCounts();
+ writeAndReadCoverageRegions(EmitFilenames);
- SmallVector<StringRef, 8> Filenames;
- for (const auto &E : Files)
- Filenames.push_back(E.getKey());
- OneFunctionCoverageReader CovReader(FuncName, Hash, Filenames, OutputCMRs);
+ CoverageMappingReaderMock CovReader(OutputFunctions);
auto CoverageOrErr = CoverageMapping::load(CovReader, *ProfileReader);
- ASSERT_TRUE(NoError(CoverageOrErr.getError()));
+ ASSERT_TRUE(NoError(CoverageOrErr.takeError()));
LoadedCoverage = std::move(CoverageOrErr.get());
}
};
-TEST_F(CoverageMappingTest, basic_write_read) {
+struct MaybeSparseCoverageMappingTest
+ : public CoverageMappingTest,
+ public ::testing::WithParamInterface<bool> {
+ void SetUp() {
+ CoverageMappingTest::SetUp();
+ ProfileWriter.setOutputSparse(GetParam());
+ }
+};
+
+TEST_P(MaybeSparseCoverageMappingTest, basic_write_read) {
+ startFunction("func", 0x1234);
addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1);
addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2);
addCMR(Counter::getZero(), "foo", 3, 1, 3, 4);
addCMR(Counter::getCounter(2), "foo", 4, 1, 4, 8);
addCMR(Counter::getCounter(3), "bar", 1, 2, 3, 4);
- std::string Coverage = writeCoverageRegions();
- readCoverageRegions(Coverage);
- size_t N = makeArrayRef(InputCMRs).size();
- ASSERT_EQ(N, OutputCMRs.size());
+ writeAndReadCoverageRegions();
+ ASSERT_EQ(1u, InputFunctions.size());
+ ASSERT_EQ(1u, OutputFunctions.size());
+ InputFunctionCoverageData &Input = InputFunctions.back();
+ OutputFunctionCoverageData &Output = OutputFunctions.back();
+
+ size_t N = makeArrayRef(Input.Regions).size();
+ ASSERT_EQ(N, Output.Regions.size());
for (size_t I = 0; I < N; ++I) {
- ASSERT_EQ(InputCMRs[I].Count, OutputCMRs[I].Count);
- ASSERT_EQ(InputCMRs[I].FileID, OutputCMRs[I].FileID);
- ASSERT_EQ(InputCMRs[I].startLoc(), OutputCMRs[I].startLoc());
- ASSERT_EQ(InputCMRs[I].endLoc(), OutputCMRs[I].endLoc());
- ASSERT_EQ(InputCMRs[I].Kind, OutputCMRs[I].Kind);
+ ASSERT_EQ(Input.Regions[I].Count, Output.Regions[I].Count);
+ ASSERT_EQ(Input.Regions[I].FileID, Output.Regions[I].FileID);
+ ASSERT_EQ(Input.Regions[I].startLoc(), Output.Regions[I].startLoc());
+ ASSERT_EQ(Input.Regions[I].endLoc(), Output.Regions[I].endLoc());
+ ASSERT_EQ(Input.Regions[I].Kind, Output.Regions[I].Kind);
+ }
+}
+
+TEST_P(MaybeSparseCoverageMappingTest,
+ correct_deserialize_for_more_than_two_files) {
+ const char *FileNames[] = {"bar", "baz", "foo"};
+ static const unsigned N = array_lengthof(FileNames);
+
+ startFunction("func", 0x1234);
+ for (unsigned I = 0; I < N; ++I)
+ // Use LineStart to hold the index of the file name
+ // in order to preserve that information during possible sorting of CMRs.
+ addCMR(Counter::getCounter(0), FileNames[I], I, 1, I, 1);
+
+ writeAndReadCoverageRegions();
+ ASSERT_EQ(1u, OutputFunctions.size());
+ OutputFunctionCoverageData &Output = OutputFunctions.back();
+
+ ASSERT_EQ(N, Output.Regions.size());
+ ASSERT_EQ(N, Output.Filenames.size());
+
+ for (unsigned I = 0; I < N; ++I) {
+ ASSERT_GT(N, Output.Regions[I].FileID);
+ ASSERT_GT(N, Output.Regions[I].LineStart);
+ EXPECT_EQ(FileNames[Output.Regions[I].LineStart],
+ Output.Filenames[Output.Regions[I].FileID]);
+ }
+}
+
+TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_more_than_two_files) {
+ InstrProfRecord Record("func", 0x1234, {0});
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+
+ const char *FileNames[] = {"bar", "baz", "foo"};
+ static const unsigned N = array_lengthof(FileNames);
+
+ startFunction("func", 0x1234);
+ for (unsigned I = 0; I < N; ++I)
+ // Use LineStart to hold the index of the file name
+ // in order to preserve that information during possible sorting of CMRs.
+ addCMR(Counter::getCounter(0), FileNames[I], I, 1, I, 1);
+
+ loadCoverageMapping();
+
+ for (unsigned I = 0; I < N; ++I) {
+ CoverageData Data = LoadedCoverage->getCoverageForFile(FileNames[I]);
+ ASSERT_TRUE(!Data.empty());
+ EXPECT_EQ(I, Data.begin()->Line);
}
}
-TEST_F(CoverageMappingTest, expansion_gets_first_counter) {
+TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_several_functions) {
+ InstrProfRecord RecordFunc1("func1", 0x1234, {10});
+ NoError(ProfileWriter.addRecord(std::move(RecordFunc1)));
+ InstrProfRecord RecordFunc2("func2", 0x2345, {20});
+ NoError(ProfileWriter.addRecord(std::move(RecordFunc2)));
+
+ startFunction("func1", 0x1234);
+ addCMR(Counter::getCounter(0), "foo", 1, 1, 5, 5);
+
+ startFunction("func2", 0x2345);
+ addCMR(Counter::getCounter(0), "bar", 2, 2, 6, 6);
+
+ loadCoverageMapping();
+
+ const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
+ EXPECT_EQ(2U, std::distance(FunctionRecords.begin(), FunctionRecords.end()));
+ for (const auto &FunctionRecord : FunctionRecords) {
+ CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
+ std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
+ ASSERT_EQ(2U, Segments.size());
+ if (FunctionRecord.Name == "func1") {
+ EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);
+ EXPECT_EQ(CoverageSegment(5, 5, false), Segments[1]);
+ } else {
+ ASSERT_EQ("func2", FunctionRecord.Name);
+ EXPECT_EQ(CoverageSegment(2, 2, 20, true), Segments[0]);
+ EXPECT_EQ(CoverageSegment(6, 6, false), Segments[1]);
+ }
+ }
+}
+
+TEST_P(MaybeSparseCoverageMappingTest, expansion_gets_first_counter) {
+ startFunction("func", 0x1234);
addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2);
// This starts earlier in "foo", so the expansion should get its counter.
addCMR(Counter::getCounter(2), "foo", 1, 1, 20, 1);
addExpansionCMR("bar", "foo", 3, 3, 3, 3);
- std::string Coverage = writeCoverageRegions();
- readCoverageRegions(Coverage);
- ASSERT_EQ(CounterMappingRegion::ExpansionRegion, OutputCMRs[2].Kind);
- ASSERT_EQ(Counter::getCounter(2), OutputCMRs[2].Count);
- ASSERT_EQ(3U, OutputCMRs[2].LineStart);
+ writeAndReadCoverageRegions();
+ ASSERT_EQ(1u, OutputFunctions.size());
+ OutputFunctionCoverageData &Output = OutputFunctions.back();
+
+ ASSERT_EQ(CounterMappingRegion::ExpansionRegion, Output.Regions[2].Kind);
+ ASSERT_EQ(Counter::getCounter(2), Output.Regions[2].Count);
+ ASSERT_EQ(3U, Output.Regions[2].LineStart);
}
-TEST_F(CoverageMappingTest, basic_coverage_iteration) {
+TEST_P(MaybeSparseCoverageMappingTest, basic_coverage_iteration) {
InstrProfRecord Record("func", 0x1234, {30, 20, 10, 0});
- ProfileWriter.addRecord(std::move(Record));
- readProfCounts();
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+ startFunction("func", 0x1234);
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);
addCMR(Counter::getCounter(2), "file1", 5, 8, 9, 1);
addCMR(Counter::getCounter(3), "file1", 10, 10, 11, 11);
- loadCoverageMapping("func", 0x1234);
+ loadCoverageMapping();
CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
@@ -210,11 +357,10 @@ TEST_F(CoverageMappingTest, basic_coverage_iteration) {
ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]);
}
-TEST_F(CoverageMappingTest, uncovered_function) {
- readProfCounts();
-
+TEST_P(MaybeSparseCoverageMappingTest, uncovered_function) {
+ startFunction("func", 0x1234);
addCMR(Counter::getZero(), "file1", 1, 2, 3, 4);
- loadCoverageMapping("func", 0x1234);
+ loadCoverageMapping();
CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
@@ -223,12 +369,11 @@ TEST_F(CoverageMappingTest, uncovered_function) {
ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]);
}
-TEST_F(CoverageMappingTest, uncovered_function_with_mapping) {
- readProfCounts();
-
+TEST_P(MaybeSparseCoverageMappingTest, uncovered_function_with_mapping) {
+ startFunction("func", 0x1234);
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);
- loadCoverageMapping("func", 0x1234);
+ loadCoverageMapping();
CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
@@ -238,15 +383,15 @@ TEST_F(CoverageMappingTest, uncovered_function_with_mapping) {
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]);
}
-TEST_F(CoverageMappingTest, combine_regions) {
+TEST_P(MaybeSparseCoverageMappingTest, combine_regions) {
InstrProfRecord Record("func", 0x1234, {10, 20, 30});
- ProfileWriter.addRecord(std::move(Record));
- readProfCounts();
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+ startFunction("func", 0x1234);
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4);
addCMR(Counter::getCounter(2), "file1", 3, 3, 4, 4);
- loadCoverageMapping("func", 0x1234);
+ loadCoverageMapping();
CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
@@ -257,16 +402,40 @@ TEST_F(CoverageMappingTest, combine_regions) {
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
}
-TEST_F(CoverageMappingTest, dont_combine_expansions) {
- InstrProfRecord Record("func", 0x1234, {10, 20});
- ProfileWriter.addRecord(std::move(Record));
- readProfCounts();
+TEST_P(MaybeSparseCoverageMappingTest,
+ restore_combined_counter_after_nested_region) {
+ InstrProfRecord Record("func", 0x1234, {10, 20, 40});
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+
+ startFunction("func", 0x1234);
+ addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
+ addCMR(Counter::getCounter(1), "file1", 1, 1, 9, 9);
+ addCMR(Counter::getCounter(2), "file1", 3, 3, 5, 5);
+ loadCoverageMapping();
+
+ CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
+ std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
+ ASSERT_EQ(4U, Segments.size());
+ EXPECT_EQ(CoverageSegment(1, 1, 30, true), Segments[0]);
+ EXPECT_EQ(CoverageSegment(3, 3, 40, true), Segments[1]);
+ EXPECT_EQ(CoverageSegment(5, 5, 30, false), Segments[2]);
+ EXPECT_EQ(CoverageSegment(9, 9, false), Segments[3]);
+}
+
+// If CodeRegions and ExpansionRegions cover the same area,
+// only counts of CodeRegions should be used.
+TEST_P(MaybeSparseCoverageMappingTest, dont_combine_expansions) {
+ InstrProfRecord Record1("func", 0x1234, {10, 20});
+ InstrProfRecord Record2("func", 0x1234, {0, 0});
+ NoError(ProfileWriter.addRecord(std::move(Record1)));
+ NoError(ProfileWriter.addRecord(std::move(Record2)));
+ startFunction("func", 0x1234);
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4);
addCMR(Counter::getCounter(1), "include1", 6, 6, 7, 7);
addExpansionCMR("file1", "include1", 3, 3, 4, 4);
- loadCoverageMapping("func", 0x1234);
+ loadCoverageMapping();
CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
@@ -277,13 +446,51 @@ TEST_F(CoverageMappingTest, dont_combine_expansions) {
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
}
-TEST_F(CoverageMappingTest, strip_filename_prefix) {
- InstrProfRecord Record("file1:func", 0x1234, {10});
- ProfileWriter.addRecord(std::move(Record));
- readProfCounts();
+// If an area is covered only by ExpansionRegions, they should be combinated.
+TEST_P(MaybeSparseCoverageMappingTest, combine_expansions) {
+ InstrProfRecord Record("func", 0x1234, {2, 3, 7});
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+ startFunction("func", 0x1234);
+ addCMR(Counter::getCounter(1), "include1", 1, 1, 1, 10);
+ addCMR(Counter::getCounter(2), "include2", 1, 1, 1, 10);
+ addCMR(Counter::getCounter(0), "file", 1, 1, 5, 5);
+ addExpansionCMR("file", "include1", 3, 1, 3, 5);
+ addExpansionCMR("file", "include2", 3, 1, 3, 5);
+
+ loadCoverageMapping();
+
+ CoverageData Data = LoadedCoverage->getCoverageForFile("file");
+ std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
+ ASSERT_EQ(4U, Segments.size());
+ EXPECT_EQ(CoverageSegment(1, 1, 2, true), Segments[0]);
+ EXPECT_EQ(CoverageSegment(3, 1, 10, true), Segments[1]);
+ EXPECT_EQ(CoverageSegment(3, 5, 2, false), Segments[2]);
+ EXPECT_EQ(CoverageSegment(5, 5, false), Segments[3]);
+}
+
+TEST_P(MaybeSparseCoverageMappingTest, strip_filename_prefix) {
+ InstrProfRecord Record("file1:func", 0x1234, {0});
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+
+ startFunction("file1:func", 0x1234);
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
- loadCoverageMapping("file1:func", 0x1234);
+ loadCoverageMapping();
+
+ std::vector<std::string> Names;
+ for (const auto &Func : LoadedCoverage->getCoveredFunctions())
+ Names.push_back(Func.Name);
+ ASSERT_EQ(1U, Names.size());
+ ASSERT_EQ("func", Names[0]);
+}
+
+TEST_P(MaybeSparseCoverageMappingTest, strip_unknown_filename_prefix) {
+ InstrProfRecord Record("<unknown>:func", 0x1234, {0});
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+
+ startFunction("<unknown>:func", 0x1234);
+ addCMR(Counter::getCounter(0), "", 1, 1, 9, 9);
+ loadCoverageMapping(/*EmitFilenames=*/false);
std::vector<std::string> Names;
for (const auto &Func : LoadedCoverage->getCoveredFunctions())
@@ -292,4 +499,45 @@ TEST_F(CoverageMappingTest, strip_filename_prefix) {
ASSERT_EQ("func", Names[0]);
}
+TEST_P(MaybeSparseCoverageMappingTest, dont_detect_false_instantiations) {
+ InstrProfRecord Record1("foo", 0x1234, {10});
+ InstrProfRecord Record2("bar", 0x2345, {20});
+ NoError(ProfileWriter.addRecord(std::move(Record1)));
+ NoError(ProfileWriter.addRecord(std::move(Record2)));
+
+ startFunction("foo", 0x1234);
+ addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10);
+ addExpansionCMR("main", "expanded", 4, 1, 4, 5);
+
+ startFunction("bar", 0x2345);
+ addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10);
+ addExpansionCMR("main", "expanded", 9, 1, 9, 5);
+
+ loadCoverageMapping();
+
+ std::vector<const FunctionRecord *> Instantiations =
+ LoadedCoverage->getInstantiations("expanded");
+ ASSERT_TRUE(Instantiations.empty());
+}
+
+TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_expanded_file) {
+ InstrProfRecord Record("func", 0x1234, {10});
+ NoError(ProfileWriter.addRecord(std::move(Record)));
+
+ startFunction("func", 0x1234);
+ addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10);
+ addExpansionCMR("main", "expanded", 4, 1, 4, 5);
+
+ loadCoverageMapping();
+
+ CoverageData Data = LoadedCoverage->getCoverageForFile("expanded");
+ std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
+ ASSERT_EQ(2U, Segments.size());
+ EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);
+ EXPECT_EQ(CoverageSegment(1, 10, false), Segments[1]);
+}
+
+INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseCoverageMappingTest,
+ ::testing::Bool());
+
} // end anonymous namespace
diff --git a/unittests/ProfileData/InstrProfTest.cpp b/unittests/ProfileData/InstrProfTest.cpp
index 51f52f2a0771..c13f31251de4 100644
--- a/unittests/ProfileData/InstrProfTest.cpp
+++ b/unittests/ProfileData/InstrProfTest.cpp
@@ -7,28 +7,36 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/InstrProfWriter.h"
#include "llvm/Support/Compression.h"
#include "gtest/gtest.h"
-
#include <cstdarg>
using namespace llvm;
-static ::testing::AssertionResult NoError(std::error_code EC) {
- if (!EC)
+static ::testing::AssertionResult NoError(Error E) {
+ if (!E)
return ::testing::AssertionSuccess();
- return ::testing::AssertionFailure() << "error " << EC.value()
- << ": " << EC.message();
+ return ::testing::AssertionFailure() << "error: " << toString(std::move(E))
+ << "\n";
}
-static ::testing::AssertionResult ErrorEquals(std::error_code Expected,
- std::error_code Found) {
+static ::testing::AssertionResult ErrorEquals(instrprof_error Expected,
+ Error E) {
+ instrprof_error Found;
+ std::string FoundMsg;
+ handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
+ Found = IPE.get();
+ FoundMsg = IPE.message();
+ });
if (Expected == Found)
return ::testing::AssertionSuccess();
- return ::testing::AssertionFailure() << "error " << Found.value()
- << ": " << Found.message();
+ return ::testing::AssertionFailure() << "error: " << FoundMsg << "\n";
}
namespace {
@@ -37,22 +45,33 @@ struct InstrProfTest : ::testing::Test {
InstrProfWriter Writer;
std::unique_ptr<IndexedInstrProfReader> Reader;
+ void SetUp() { Writer.setOutputSparse(false); }
+
void readProfile(std::unique_ptr<MemoryBuffer> Profile) {
auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile));
- ASSERT_TRUE(NoError(ReaderOrErr.getError()));
+ ASSERT_TRUE(NoError(ReaderOrErr.takeError()));
Reader = std::move(ReaderOrErr.get());
}
};
-TEST_F(InstrProfTest, write_and_read_empty_profile) {
+struct SparseInstrProfTest : public InstrProfTest {
+ void SetUp() { Writer.setOutputSparse(true); }
+};
+
+struct MaybeSparseInstrProfTest : public InstrProfTest,
+ public ::testing::WithParamInterface<bool> {
+ void SetUp() { Writer.setOutputSparse(GetParam()); }
+};
+
+TEST_P(MaybeSparseInstrProfTest, write_and_read_empty_profile) {
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
ASSERT_TRUE(Reader->begin() == Reader->end());
}
-TEST_F(InstrProfTest, write_and_read_one_function) {
+TEST_P(MaybeSparseInstrProfTest, write_and_read_one_function) {
InstrProfRecord Record("foo", 0x1234, {1, 2, 3, 4});
- Writer.addRecord(std::move(Record));
+ NoError(Writer.addRecord(std::move(Record)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
@@ -68,38 +87,38 @@ TEST_F(InstrProfTest, write_and_read_one_function) {
ASSERT_TRUE(++I == E);
}
-TEST_F(InstrProfTest, get_instr_prof_record) {
+TEST_P(MaybeSparseInstrProfTest, get_instr_prof_record) {
InstrProfRecord Record1("foo", 0x1234, {1, 2});
InstrProfRecord Record2("foo", 0x1235, {3, 4});
- Writer.addRecord(std::move(Record1));
- Writer.addRecord(std::move(Record2));
+ NoError(Writer.addRecord(std::move(Record1)));
+ NoError(Writer.addRecord(std::move(Record2)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
- ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("foo", 0x1234);
- ASSERT_TRUE(NoError(R.getError()));
- ASSERT_EQ(2U, R.get().Counts.size());
- ASSERT_EQ(1U, R.get().Counts[0]);
- ASSERT_EQ(2U, R.get().Counts[1]);
+ Expected<InstrProfRecord> R = Reader->getInstrProfRecord("foo", 0x1234);
+ ASSERT_TRUE(NoError(R.takeError()));
+ ASSERT_EQ(2U, R->Counts.size());
+ ASSERT_EQ(1U, R->Counts[0]);
+ ASSERT_EQ(2U, R->Counts[1]);
R = Reader->getInstrProfRecord("foo", 0x1235);
- ASSERT_TRUE(NoError(R.getError()));
- ASSERT_EQ(2U, R.get().Counts.size());
- ASSERT_EQ(3U, R.get().Counts[0]);
- ASSERT_EQ(4U, R.get().Counts[1]);
+ ASSERT_TRUE(NoError(R.takeError()));
+ ASSERT_EQ(2U, R->Counts.size());
+ ASSERT_EQ(3U, R->Counts[0]);
+ ASSERT_EQ(4U, R->Counts[1]);
R = Reader->getInstrProfRecord("foo", 0x5678);
- ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, R.getError()));
+ ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, R.takeError()));
R = Reader->getInstrProfRecord("bar", 0x1234);
- ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, R.getError()));
+ ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, R.takeError()));
}
-TEST_F(InstrProfTest, get_function_counts) {
+TEST_P(MaybeSparseInstrProfTest, get_function_counts) {
InstrProfRecord Record1("foo", 0x1234, {1, 2});
InstrProfRecord Record2("foo", 0x1235, {3, 4});
- Writer.addRecord(std::move(Record1));
- Writer.addRecord(std::move(Record2));
+ NoError(Writer.addRecord(std::move(Record1)));
+ NoError(Writer.addRecord(std::move(Record2)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
@@ -114,15 +133,85 @@ TEST_F(InstrProfTest, get_function_counts) {
ASSERT_EQ(3U, Counts[0]);
ASSERT_EQ(4U, Counts[1]);
- std::error_code EC;
- EC = Reader->getFunctionCounts("foo", 0x5678, Counts);
- ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, EC));
+ Error E1 = Reader->getFunctionCounts("foo", 0x5678, Counts);
+ ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, std::move(E1)));
- EC = Reader->getFunctionCounts("bar", 0x1234, Counts);
- ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, EC));
+ Error E2 = Reader->getFunctionCounts("bar", 0x1234, Counts);
+ ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, std::move(E2)));
}
-TEST_F(InstrProfTest, get_icall_data_read_write) {
+// Profile data is copied from general.proftext
+TEST_F(InstrProfTest, get_profile_summary) {
+ InstrProfRecord Record1("func1", 0x1234, {97531});
+ InstrProfRecord Record2("func2", 0x1234, {0, 0});
+ InstrProfRecord Record3("func3", 0x1234,
+ {2305843009213693952, 1152921504606846976,
+ 576460752303423488, 288230376151711744,
+ 144115188075855872, 72057594037927936});
+ InstrProfRecord Record4("func4", 0x1234, {0});
+ NoError(Writer.addRecord(std::move(Record1)));
+ NoError(Writer.addRecord(std::move(Record2)));
+ NoError(Writer.addRecord(std::move(Record3)));
+ NoError(Writer.addRecord(std::move(Record4)));
+ auto Profile = Writer.writeBuffer();
+ readProfile(std::move(Profile));
+
+ auto VerifySummary = [](ProfileSummary &IPS) mutable {
+ ASSERT_EQ(ProfileSummary::PSK_Instr, IPS.getKind());
+ ASSERT_EQ(2305843009213693952U, IPS.getMaxFunctionCount());
+ ASSERT_EQ(2305843009213693952U, IPS.getMaxCount());
+ ASSERT_EQ(10U, IPS.getNumCounts());
+ ASSERT_EQ(4539628424389557499U, IPS.getTotalCount());
+ std::vector<ProfileSummaryEntry> &Details = IPS.getDetailedSummary();
+ uint32_t Cutoff = 800000;
+ auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {
+ return PE.Cutoff == Cutoff;
+ };
+ auto EightyPerc = std::find_if(Details.begin(), Details.end(), Predicate);
+ Cutoff = 900000;
+ auto NinetyPerc = std::find_if(Details.begin(), Details.end(), Predicate);
+ Cutoff = 950000;
+ auto NinetyFivePerc =
+ std::find_if(Details.begin(), Details.end(), Predicate);
+ Cutoff = 990000;
+ auto NinetyNinePerc =
+ std::find_if(Details.begin(), Details.end(), Predicate);
+ ASSERT_EQ(576460752303423488U, EightyPerc->MinCount);
+ ASSERT_EQ(288230376151711744U, NinetyPerc->MinCount);
+ ASSERT_EQ(288230376151711744U, NinetyFivePerc->MinCount);
+ ASSERT_EQ(72057594037927936U, NinetyNinePerc->MinCount);
+ };
+ ProfileSummary &PS = Reader->getSummary();
+ VerifySummary(PS);
+
+ // Test that conversion of summary to and from Metadata works.
+ LLVMContext Context;
+ Metadata *MD = PS.getMD(Context);
+ ASSERT_TRUE(MD);
+ ProfileSummary *PSFromMD = ProfileSummary::getFromMD(MD);
+ ASSERT_TRUE(PSFromMD);
+ VerifySummary(*PSFromMD);
+ delete PSFromMD;
+
+ // Test that summary can be attached to and read back from module.
+ Module M("my_module", Context);
+ M.setProfileSummary(MD);
+ MD = M.getProfileSummary();
+ ASSERT_TRUE(MD);
+ PSFromMD = ProfileSummary::getFromMD(MD);
+ ASSERT_TRUE(PSFromMD);
+ VerifySummary(*PSFromMD);
+ delete PSFromMD;
+}
+
+static const char callee1[] = "callee1";
+static const char callee2[] = "callee2";
+static const char callee3[] = "callee3";
+static const char callee4[] = "callee4";
+static const char callee5[] = "callee5";
+static const char callee6[] = "callee6";
+
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write) {
InstrProfRecord Record1("caller", 0x1234, {1, 2});
InstrProfRecord Record2("callee1", 0x1235, {3, 4});
InstrProfRecord Record3("callee2", 0x1235, {3, 4});
@@ -130,46 +219,142 @@ TEST_F(InstrProfTest, get_icall_data_read_write) {
// 4 value sites.
Record1.reserveSites(IPVK_IndirectCallTarget, 4);
- InstrProfValueData VD0[] = {{(uint64_t) "callee1", 1},
- {(uint64_t) "callee2", 2},
- {(uint64_t) "callee3", 3}};
+ InstrProfValueData VD0[] = {
+ {(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}};
Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr);
// No value profile data at the second site.
Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);
- InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1},
- {(uint64_t) "callee2", 2}};
+ InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}};
Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr);
- InstrProfValueData VD3[] = {{(uint64_t) "callee1", 1}};
+ InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}};
Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr);
- Writer.addRecord(std::move(Record1));
- Writer.addRecord(std::move(Record2));
- Writer.addRecord(std::move(Record3));
- Writer.addRecord(std::move(Record4));
+ NoError(Writer.addRecord(std::move(Record1)));
+ NoError(Writer.addRecord(std::move(Record2)));
+ NoError(Writer.addRecord(std::move(Record3)));
+ NoError(Writer.addRecord(std::move(Record4)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
- ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
- ASSERT_TRUE(NoError(R.getError()));
- ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget));
- ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
- ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
- ASSERT_EQ(2U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
- ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
+ Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
+ ASSERT_TRUE(NoError(R.takeError()));
+ ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget));
+ ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
+ ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
+ ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
+ ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
+ uint64_t TotalC;
std::unique_ptr<InstrProfValueData[]> VD =
- R.get().getValueForSite(IPVK_IndirectCallTarget, 0);
+ R->getValueForSite(IPVK_IndirectCallTarget, 0, &TotalC);
ASSERT_EQ(3U, VD[0].Count);
ASSERT_EQ(2U, VD[1].Count);
ASSERT_EQ(1U, VD[2].Count);
+ ASSERT_EQ(6U, TotalC);
ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3"));
ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2"));
ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));
}
-TEST_F(InstrProfTest, get_icall_data_read_write_with_weight) {
+TEST_P(MaybeSparseInstrProfTest, annotate_vp_data) {
+ InstrProfRecord Record("caller", 0x1234, {1, 2});
+ Record.reserveSites(IPVK_IndirectCallTarget, 1);
+ InstrProfValueData VD0[] = {{1000, 1}, {2000, 2}, {3000, 3}, {5000, 5},
+ {4000, 4}, {6000, 6}};
+ Record.addValueData(IPVK_IndirectCallTarget, 0, VD0, 6, nullptr);
+ NoError(Writer.addRecord(std::move(Record)));
+ auto Profile = Writer.writeBuffer();
+ readProfile(std::move(Profile));
+ Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
+ ASSERT_TRUE(NoError(R.takeError()));
+
+ LLVMContext Ctx;
+ std::unique_ptr<Module> M(new Module("MyModule", Ctx));
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),
+ /*isVarArg=*/false);
+ Function *F =
+ Function::Create(FTy, Function::ExternalLinkage, "caller", M.get());
+ BasicBlock *BB = BasicBlock::Create(Ctx, "", F);
+
+ IRBuilder<> Builder(BB);
+ BasicBlock *TBB = BasicBlock::Create(Ctx, "", F);
+ BasicBlock *FBB = BasicBlock::Create(Ctx, "", F);
+
+ // Use branch instruction to annotate with value profile data for simplicity
+ Instruction *Inst = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB);
+ Instruction *Inst2 = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB);
+ annotateValueSite(*M, *Inst, R.get(), IPVK_IndirectCallTarget, 0);
+
+ InstrProfValueData ValueData[5];
+ uint32_t N;
+ uint64_t T;
+ bool Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5,
+ ValueData, N, T);
+ ASSERT_TRUE(Res);
+ ASSERT_EQ(3U, N);
+ ASSERT_EQ(21U, T);
+ // The result should be sorted already:
+ ASSERT_EQ(6000U, ValueData[0].Value);
+ ASSERT_EQ(6U, ValueData[0].Count);
+ ASSERT_EQ(5000U, ValueData[1].Value);
+ ASSERT_EQ(5U, ValueData[1].Count);
+ ASSERT_EQ(4000U, ValueData[2].Value);
+ ASSERT_EQ(4U, ValueData[2].Count);
+ Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 1, ValueData,
+ N, T);
+ ASSERT_TRUE(Res);
+ ASSERT_EQ(1U, N);
+ ASSERT_EQ(21U, T);
+
+ Res = getValueProfDataFromInst(*Inst2, IPVK_IndirectCallTarget, 5, ValueData,
+ N, T);
+ ASSERT_FALSE(Res);
+
+ // Remove the MD_prof metadata
+ Inst->setMetadata(LLVMContext::MD_prof, 0);
+ // Annotate 5 records this time.
+ annotateValueSite(*M, *Inst, R.get(), IPVK_IndirectCallTarget, 0, 5);
+ Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5,
+ ValueData, N, T);
+ ASSERT_TRUE(Res);
+ ASSERT_EQ(5U, N);
+ ASSERT_EQ(21U, T);
+ ASSERT_EQ(6000U, ValueData[0].Value);
+ ASSERT_EQ(6U, ValueData[0].Count);
+ ASSERT_EQ(5000U, ValueData[1].Value);
+ ASSERT_EQ(5U, ValueData[1].Count);
+ ASSERT_EQ(4000U, ValueData[2].Value);
+ ASSERT_EQ(4U, ValueData[2].Count);
+ ASSERT_EQ(3000U, ValueData[3].Value);
+ ASSERT_EQ(3U, ValueData[3].Count);
+ ASSERT_EQ(2000U, ValueData[4].Value);
+ ASSERT_EQ(2U, ValueData[4].Count);
+
+ // Remove the MD_prof metadata
+ Inst->setMetadata(LLVMContext::MD_prof, 0);
+ // Annotate with 4 records.
+ InstrProfValueData VD0Sorted[] = {{1000, 6}, {2000, 5}, {3000, 4}, {4000, 3},
+ {5000, 2}, {6000, 1}};
+ annotateValueSite(*M, *Inst, makeArrayRef(VD0Sorted).slice(2), 10,
+ IPVK_IndirectCallTarget, 5);
+ Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5,
+ ValueData, N, T);
+ ASSERT_TRUE(Res);
+ ASSERT_EQ(4U, N);
+ ASSERT_EQ(10U, T);
+ ASSERT_EQ(3000U, ValueData[0].Value);
+ ASSERT_EQ(4U, ValueData[0].Count);
+ ASSERT_EQ(4000U, ValueData[1].Value);
+ ASSERT_EQ(3U, ValueData[1].Count);
+ ASSERT_EQ(5000U, ValueData[2].Value);
+ ASSERT_EQ(2U, ValueData[2].Count);
+ ASSERT_EQ(6000U, ValueData[3].Value);
+ ASSERT_EQ(1U, ValueData[3].Count);
+}
+
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_with_weight) {
InstrProfRecord Record1("caller", 0x1234, {1, 2});
InstrProfRecord Record2("callee1", 0x1235, {3, 4});
InstrProfRecord Record3("callee2", 0x1235, {3, 4});
@@ -177,45 +362,45 @@ TEST_F(InstrProfTest, get_icall_data_read_write_with_weight) {
// 4 value sites.
Record1.reserveSites(IPVK_IndirectCallTarget, 4);
- InstrProfValueData VD0[] = {{(uint64_t) "callee1", 1},
- {(uint64_t) "callee2", 2},
- {(uint64_t) "callee3", 3}};
+ InstrProfValueData VD0[] = {
+ {(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}};
Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr);
// No value profile data at the second site.
Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);
- InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1},
- {(uint64_t) "callee2", 2}};
+ InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}};
Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr);
- InstrProfValueData VD3[] = {{(uint64_t) "callee1", 1}};
+ InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}};
Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr);
- Writer.addRecord(std::move(Record1), 10);
- Writer.addRecord(std::move(Record2));
- Writer.addRecord(std::move(Record3));
- Writer.addRecord(std::move(Record4));
+ NoError(Writer.addRecord(std::move(Record1), 10));
+ NoError(Writer.addRecord(std::move(Record2)));
+ NoError(Writer.addRecord(std::move(Record3)));
+ NoError(Writer.addRecord(std::move(Record4)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
- ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
- ASSERT_TRUE(NoError(R.getError()));
- ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget));
- ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
- ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
- ASSERT_EQ(2U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
- ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
+ Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
+ ASSERT_TRUE(NoError(R.takeError()));
+ ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget));
+ ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
+ ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
+ ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
+ ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
+ uint64_t TotalC;
std::unique_ptr<InstrProfValueData[]> VD =
- R.get().getValueForSite(IPVK_IndirectCallTarget, 0);
+ R->getValueForSite(IPVK_IndirectCallTarget, 0, &TotalC);
ASSERT_EQ(30U, VD[0].Count);
ASSERT_EQ(20U, VD[1].Count);
ASSERT_EQ(10U, VD[2].Count);
+ ASSERT_EQ(60U, TotalC);
ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3"));
ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2"));
ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));
}
-TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_big_endian) {
InstrProfRecord Record1("caller", 0x1234, {1, 2});
InstrProfRecord Record2("callee1", 0x1235, {3, 4});
InstrProfRecord Record3("callee2", 0x1235, {3, 4});
@@ -223,22 +408,20 @@ TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) {
// 4 value sites.
Record1.reserveSites(IPVK_IndirectCallTarget, 4);
- InstrProfValueData VD0[] = {{(uint64_t) "callee1", 1},
- {(uint64_t) "callee2", 2},
- {(uint64_t) "callee3", 3}};
+ InstrProfValueData VD0[] = {
+ {(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}};
Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr);
// No value profile data at the second site.
Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);
- InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1},
- {(uint64_t) "callee2", 2}};
+ InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}};
Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr);
- InstrProfValueData VD3[] = {{(uint64_t) "callee1", 1}};
+ InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}};
Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr);
- Writer.addRecord(std::move(Record1));
- Writer.addRecord(std::move(Record2));
- Writer.addRecord(std::move(Record3));
- Writer.addRecord(std::move(Record4));
+ NoError(Writer.addRecord(std::move(Record1)));
+ NoError(Writer.addRecord(std::move(Record2)));
+ NoError(Writer.addRecord(std::move(Record3)));
+ NoError(Writer.addRecord(std::move(Record4)));
// Set big endian output.
Writer.setValueProfDataEndianness(support::big);
@@ -249,16 +432,16 @@ TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) {
// Set big endian input.
Reader->setValueProfDataEndianness(support::big);
- ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
- ASSERT_TRUE(NoError(R.getError()));
- ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget));
- ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
- ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
- ASSERT_EQ(2U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
- ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
+ Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
+ ASSERT_TRUE(NoError(R.takeError()));
+ ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget));
+ ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
+ ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
+ ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
+ ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
std::unique_ptr<InstrProfValueData[]> VD =
- R.get().getValueForSite(IPVK_IndirectCallTarget, 0);
+ R->getValueForSite(IPVK_IndirectCallTarget, 0);
ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3"));
ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2"));
ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));
@@ -267,13 +450,8 @@ TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) {
Writer.setValueProfDataEndianness(support::little);
}
-TEST_F(InstrProfTest, get_icall_data_merge1) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1) {
static const char caller[] = "caller";
- static const char callee1[] = "callee1";
- static const char callee2[] = "callee2";
- static const char callee3[] = "callee3";
- static const char callee4[] = "callee4";
-
InstrProfRecord Record11(caller, 0x1234, {1, 2});
InstrProfRecord Record12(caller, 0x1234, {1, 2});
InstrProfRecord Record2(callee1, 0x1235, {3, 4});
@@ -305,7 +483,7 @@ TEST_F(InstrProfTest, get_icall_data_merge1) {
{uint64_t(callee3), 3}};
Record11.addValueData(IPVK_IndirectCallTarget, 4, VD4, 3, nullptr);
- // A differnt record for the same caller.
+ // A different record for the same caller.
Record12.reserveSites(IPVK_IndirectCallTarget, 5);
InstrProfValueData VD02[] = {{uint64_t(callee2), 5}, {uint64_t(callee3), 3}};
Record12.addValueData(IPVK_IndirectCallTarget, 0, VD02, 2, nullptr);
@@ -324,29 +502,29 @@ TEST_F(InstrProfTest, get_icall_data_merge1) {
{uint64_t(callee3), 3}};
Record12.addValueData(IPVK_IndirectCallTarget, 4, VD42, 3, nullptr);
- Writer.addRecord(std::move(Record11));
+ NoError(Writer.addRecord(std::move(Record11)));
// Merge profile data.
- Writer.addRecord(std::move(Record12));
+ NoError(Writer.addRecord(std::move(Record12)));
- Writer.addRecord(std::move(Record2));
- Writer.addRecord(std::move(Record3));
- Writer.addRecord(std::move(Record4));
- Writer.addRecord(std::move(Record5));
- Writer.addRecord(std::move(Record6));
+ NoError(Writer.addRecord(std::move(Record2)));
+ NoError(Writer.addRecord(std::move(Record3)));
+ NoError(Writer.addRecord(std::move(Record4)));
+ NoError(Writer.addRecord(std::move(Record5)));
+ NoError(Writer.addRecord(std::move(Record6)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
- ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
- ASSERT_TRUE(NoError(R.getError()));
- ASSERT_EQ(5U, R.get().getNumValueSites(IPVK_IndirectCallTarget));
- ASSERT_EQ(4U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
- ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
- ASSERT_EQ(4U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
- ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
- ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 4));
+ Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
+ ASSERT_TRUE(NoError(R.takeError()));
+ ASSERT_EQ(5U, R->getNumValueSites(IPVK_IndirectCallTarget));
+ ASSERT_EQ(4U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
+ ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));
+ ASSERT_EQ(4U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));
+ ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));
+ ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 4));
std::unique_ptr<InstrProfValueData[]> VD =
- R.get().getValueForSite(IPVK_IndirectCallTarget, 0);
+ R->getValueForSite(IPVK_IndirectCallTarget, 0);
ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee2"));
ASSERT_EQ(7U, VD[0].Count);
ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee3"));
@@ -357,7 +535,7 @@ TEST_F(InstrProfTest, get_icall_data_merge1) {
ASSERT_EQ(1U, VD[3].Count);
std::unique_ptr<InstrProfValueData[]> VD_2(
- R.get().getValueForSite(IPVK_IndirectCallTarget, 2));
+ R->getValueForSite(IPVK_IndirectCallTarget, 2));
ASSERT_EQ(StringRef((const char *)VD_2[0].Value, 7), StringRef("callee3"));
ASSERT_EQ(6U, VD_2[0].Count);
ASSERT_EQ(StringRef((const char *)VD_2[1].Value, 7), StringRef("callee4"));
@@ -368,12 +546,12 @@ TEST_F(InstrProfTest, get_icall_data_merge1) {
ASSERT_EQ(1U, VD_2[3].Count);
std::unique_ptr<InstrProfValueData[]> VD_3(
- R.get().getValueForSite(IPVK_IndirectCallTarget, 3));
+ R->getValueForSite(IPVK_IndirectCallTarget, 3));
ASSERT_EQ(StringRef((const char *)VD_3[0].Value, 7), StringRef("callee1"));
ASSERT_EQ(1U, VD_3[0].Count);
std::unique_ptr<InstrProfValueData[]> VD_4(
- R.get().getValueForSite(IPVK_IndirectCallTarget, 4));
+ R->getValueForSite(IPVK_IndirectCallTarget, 4));
ASSERT_EQ(StringRef((const char *)VD_4[0].Value, 7), StringRef("callee3"));
ASSERT_EQ(6U, VD_4[0].Count);
ASSERT_EQ(StringRef((const char *)VD_4[1].Value, 7), StringRef("callee2"));
@@ -382,30 +560,34 @@ TEST_F(InstrProfTest, get_icall_data_merge1) {
ASSERT_EQ(2U, VD_4[2].Count);
}
-TEST_F(InstrProfTest, get_icall_data_merge1_saturation) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1_saturation) {
static const char bar[] = "bar";
const uint64_t Max = std::numeric_limits<uint64_t>::max();
InstrProfRecord Record1("foo", 0x1234, {1});
auto Result1 = Writer.addRecord(std::move(Record1));
- ASSERT_EQ(Result1, instrprof_error::success);
+ ASSERT_EQ(InstrProfError::take(std::move(Result1)),
+ instrprof_error::success);
// Verify counter overflow.
InstrProfRecord Record2("foo", 0x1234, {Max});
auto Result2 = Writer.addRecord(std::move(Record2));
- ASSERT_EQ(Result2, instrprof_error::counter_overflow);
+ ASSERT_EQ(InstrProfError::take(std::move(Result2)),
+ instrprof_error::counter_overflow);
InstrProfRecord Record3(bar, 0x9012, {8});
auto Result3 = Writer.addRecord(std::move(Record3));
- ASSERT_EQ(Result3, instrprof_error::success);
+ ASSERT_EQ(InstrProfError::take(std::move(Result3)),
+ instrprof_error::success);
InstrProfRecord Record4("baz", 0x5678, {3, 4});
Record4.reserveSites(IPVK_IndirectCallTarget, 1);
InstrProfValueData VD4[] = {{uint64_t(bar), 1}};
Record4.addValueData(IPVK_IndirectCallTarget, 0, VD4, 1, nullptr);
auto Result4 = Writer.addRecord(std::move(Record4));
- ASSERT_EQ(Result4, instrprof_error::success);
+ ASSERT_EQ(InstrProfError::take(std::move(Result4)),
+ instrprof_error::success);
// Verify value data counter overflow.
InstrProfRecord Record5("baz", 0x5678, {5, 6});
@@ -413,22 +595,24 @@ TEST_F(InstrProfTest, get_icall_data_merge1_saturation) {
InstrProfValueData VD5[] = {{uint64_t(bar), Max}};
Record5.addValueData(IPVK_IndirectCallTarget, 0, VD5, 1, nullptr);
auto Result5 = Writer.addRecord(std::move(Record5));
- ASSERT_EQ(Result5, instrprof_error::counter_overflow);
+ ASSERT_EQ(InstrProfError::take(std::move(Result5)),
+ instrprof_error::counter_overflow);
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
// Verify saturation of counts.
- ErrorOr<InstrProfRecord> ReadRecord1 =
+ Expected<InstrProfRecord> ReadRecord1 =
Reader->getInstrProfRecord("foo", 0x1234);
- ASSERT_TRUE(NoError(ReadRecord1.getError()));
- ASSERT_EQ(Max, ReadRecord1.get().Counts[0]);
+ ASSERT_TRUE(NoError(ReadRecord1.takeError()));
+ ASSERT_EQ(Max, ReadRecord1->Counts[0]);
- ErrorOr<InstrProfRecord> ReadRecord2 =
+ Expected<InstrProfRecord> ReadRecord2 =
Reader->getInstrProfRecord("baz", 0x5678);
- ASSERT_EQ(1U, ReadRecord2.get().getNumValueSites(IPVK_IndirectCallTarget));
+ ASSERT_TRUE(bool(ReadRecord2));
+ ASSERT_EQ(1U, ReadRecord2->getNumValueSites(IPVK_IndirectCallTarget));
std::unique_ptr<InstrProfValueData[]> VD =
- ReadRecord2.get().getValueForSite(IPVK_IndirectCallTarget, 0);
+ ReadRecord2->getValueForSite(IPVK_IndirectCallTarget, 0);
ASSERT_EQ(StringRef("bar"), StringRef((const char *)VD[0].Value, 3));
ASSERT_EQ(Max, VD[0].Count);
}
@@ -436,7 +620,7 @@ TEST_F(InstrProfTest, get_icall_data_merge1_saturation) {
// This test tests that when there are too many values
// for a given site, the merged results are properly
// truncated.
-TEST_F(InstrProfTest, get_icall_data_merge_site_trunc) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge_site_trunc) {
static const char caller[] = "caller";
InstrProfRecord Record11(caller, 0x1234, {1, 2});
@@ -463,57 +647,56 @@ TEST_F(InstrProfTest, get_icall_data_merge_site_trunc) {
Record12.addValueData(IPVK_IndirectCallTarget, 0, VD1, 255, nullptr);
Record12.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);
- Writer.addRecord(std::move(Record11));
+ NoError(Writer.addRecord(std::move(Record11)));
// Merge profile data.
- Writer.addRecord(std::move(Record12));
+ NoError(Writer.addRecord(std::move(Record12)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
- ErrorOr<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
- ASSERT_TRUE(NoError(R.getError()));
+ Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);
+ ASSERT_TRUE(NoError(R.takeError()));
std::unique_ptr<InstrProfValueData[]> VD(
- R.get().getValueForSite(IPVK_IndirectCallTarget, 0));
- ASSERT_EQ(2U, R.get().getNumValueSites(IPVK_IndirectCallTarget));
- ASSERT_EQ(255U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
+ R->getValueForSite(IPVK_IndirectCallTarget, 0));
+ ASSERT_EQ(2U, R->getNumValueSites(IPVK_IndirectCallTarget));
+ ASSERT_EQ(255U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
for (unsigned I = 0; I < 255; I++) {
ASSERT_EQ(VD[I].Value, 509 - I);
ASSERT_EQ(VD[I].Count, 1509 - I);
}
}
-// Synthesize runtime value profile data.
-ValueProfNode Site1Values[5] = {{{uint64_t("callee1"), 400}, &Site1Values[1]},
- {{uint64_t("callee2"), 1000}, &Site1Values[2]},
- {{uint64_t("callee3"), 500}, &Site1Values[3]},
- {{uint64_t("callee4"), 300}, &Site1Values[4]},
- {{uint64_t("callee5"), 100}, 0}};
-
-ValueProfNode Site2Values[4] = {{{uint64_t("callee5"), 800}, &Site2Values[1]},
- {{uint64_t("callee3"), 1000}, &Site2Values[2]},
- {{uint64_t("callee2"), 2500}, &Site2Values[3]},
- {{uint64_t("callee1"), 1300}, 0}};
-
-ValueProfNode Site3Values[3] = {{{uint64_t("callee6"), 800}, &Site3Values[1]},
- {{uint64_t("callee3"), 1000}, &Site3Values[2]},
- {{uint64_t("callee4"), 5500}, 0}};
-
-ValueProfNode Site4Values[2] = {{{uint64_t("callee2"), 1800}, &Site4Values[1]},
- {{uint64_t("callee3"), 2000}, 0}};
-
-static ValueProfNode *ValueProfNodes[5] = {&Site1Values[0], &Site2Values[0],
- &Site3Values[0], &Site4Values[0], 0};
-static uint16_t NumValueSites[IPVK_Last + 1] = {5};
-TEST_F(InstrProfTest, runtime_value_prof_data_read_write) {
- ValueProfRuntimeRecord RTRecord;
- initializeValueProfRuntimeRecord(&RTRecord, &NumValueSites[0],
- &ValueProfNodes[0]);
+static void addValueProfData(InstrProfRecord &Record) {
+ Record.reserveSites(IPVK_IndirectCallTarget, 5);
+ InstrProfValueData VD0[] = {{uint64_t(callee1), 400},
+ {uint64_t(callee2), 1000},
+ {uint64_t(callee3), 500},
+ {uint64_t(callee4), 300},
+ {uint64_t(callee5), 100}};
+ Record.addValueData(IPVK_IndirectCallTarget, 0, VD0, 5, nullptr);
+ InstrProfValueData VD1[] = {{uint64_t(callee5), 800},
+ {uint64_t(callee3), 1000},
+ {uint64_t(callee2), 2500},
+ {uint64_t(callee1), 1300}};
+ Record.addValueData(IPVK_IndirectCallTarget, 1, VD1, 4, nullptr);
+ InstrProfValueData VD2[] = {{uint64_t(callee6), 800},
+ {uint64_t(callee3), 1000},
+ {uint64_t(callee4), 5500}};
+ Record.addValueData(IPVK_IndirectCallTarget, 2, VD2, 3, nullptr);
+ InstrProfValueData VD3[] = {{uint64_t(callee2), 1800},
+ {uint64_t(callee3), 2000}};
+ Record.addValueData(IPVK_IndirectCallTarget, 3, VD3, 2, nullptr);
+ Record.addValueData(IPVK_IndirectCallTarget, 4, nullptr, 0, nullptr);
+}
- ValueProfData *VPData = serializeValueProfDataFromRT(&RTRecord, nullptr);
+TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write) {
+ InstrProfRecord SrcRecord("caller", 0x1234, {1ULL << 31, 2});
+ addValueProfData(SrcRecord);
+ std::unique_ptr<ValueProfData> VPData =
+ ValueProfData::serializeFrom(SrcRecord);
InstrProfRecord Record("caller", 0x1234, {1ULL << 31, 2});
-
- VPData->deserializeTo(Record, 0);
+ VPData->deserializeTo(Record, nullptr);
// Now read data from Record and sanity check the data
ASSERT_EQ(5U, Record.getNumValueSites(IPVK_IndirectCallTarget));
@@ -569,29 +752,65 @@ TEST_F(InstrProfTest, runtime_value_prof_data_read_write) {
ASSERT_EQ(2000U, VD_3[0].Count);
ASSERT_EQ(StringRef((const char *)VD_3[1].Value, 7), StringRef("callee2"));
ASSERT_EQ(1800U, VD_3[1].Count);
+}
+
+TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write_mapping) {
+
+ InstrProfRecord SrcRecord("caller", 0x1234, {1ULL << 31, 2});
+ addValueProfData(SrcRecord);
+ std::unique_ptr<ValueProfData> VPData =
+ ValueProfData::serializeFrom(SrcRecord);
+
+ InstrProfRecord Record("caller", 0x1234, {1ULL << 31, 2});
+ InstrProfSymtab Symtab;
+ Symtab.mapAddress(uint64_t(callee1), 0x1000ULL);
+ Symtab.mapAddress(uint64_t(callee2), 0x2000ULL);
+ Symtab.mapAddress(uint64_t(callee3), 0x3000ULL);
+ Symtab.mapAddress(uint64_t(callee4), 0x4000ULL);
+ // Missing mapping for callee5
+ Symtab.finalizeSymtab();
+
+ VPData->deserializeTo(Record, &Symtab.getAddrHashMap());
+
+ // Now read data from Record and sanity check the data
+ ASSERT_EQ(5U, Record.getNumValueSites(IPVK_IndirectCallTarget));
+ ASSERT_EQ(5U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 0));
+
+ auto Cmp = [](const InstrProfValueData &VD1, const InstrProfValueData &VD2) {
+ return VD1.Count > VD2.Count;
+ };
+ std::unique_ptr<InstrProfValueData[]> VD_0(
+ Record.getValueForSite(IPVK_IndirectCallTarget, 0));
+ std::sort(&VD_0[0], &VD_0[5], Cmp);
+ ASSERT_EQ(VD_0[0].Value, 0x2000ULL);
+ ASSERT_EQ(1000U, VD_0[0].Count);
+ ASSERT_EQ(VD_0[1].Value, 0x3000ULL);
+ ASSERT_EQ(500U, VD_0[1].Count);
+ ASSERT_EQ(VD_0[2].Value, 0x1000ULL);
+ ASSERT_EQ(400U, VD_0[2].Count);
- finalizeValueProfRuntimeRecord(&RTRecord);
- free(VPData);
+ // callee5 does not have a mapped value -- default to 0.
+ ASSERT_EQ(VD_0[4].Value, 0ULL);
}
-TEST_F(InstrProfTest, get_max_function_count) {
+TEST_P(MaybeSparseInstrProfTest, get_max_function_count) {
InstrProfRecord Record1("foo", 0x1234, {1ULL << 31, 2});
InstrProfRecord Record2("bar", 0, {1ULL << 63});
InstrProfRecord Record3("baz", 0x5678, {0, 0, 0, 0});
- Writer.addRecord(std::move(Record1));
- Writer.addRecord(std::move(Record2));
- Writer.addRecord(std::move(Record3));
+ NoError(Writer.addRecord(std::move(Record1)));
+ NoError(Writer.addRecord(std::move(Record2)));
+ NoError(Writer.addRecord(std::move(Record3)));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount());
}
-TEST_F(InstrProfTest, get_weighted_function_counts) {
+TEST_P(MaybeSparseInstrProfTest, get_weighted_function_counts) {
InstrProfRecord Record1("foo", 0x1234, {1, 2});
InstrProfRecord Record2("foo", 0x1235, {3, 4});
- Writer.addRecord(std::move(Record1), 3);
- Writer.addRecord(std::move(Record2), 5);
+ NoError(Writer.addRecord(std::move(Record1), 3));
+ NoError(Writer.addRecord(std::move(Record2), 5));
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
@@ -607,7 +826,8 @@ TEST_F(InstrProfTest, get_weighted_function_counts) {
ASSERT_EQ(20U, Counts[1]);
}
-TEST_F(InstrProfTest, instr_prof_symtab_test) {
+// Testing symtab creator interface used by indexed profile reader.
+TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_test) {
std::vector<StringRef> FuncNames;
FuncNames.push_back("func1");
FuncNames.push_back("func2");
@@ -630,6 +850,12 @@ TEST_F(InstrProfTest, instr_prof_symtab_test) {
R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar3"));
ASSERT_EQ(StringRef("bar3"), R);
+ // negative tests
+ R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar4"));
+ ASSERT_EQ(StringRef(), R);
+ R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("foo4"));
+ ASSERT_EQ(StringRef(), R);
+
// Now incrementally update the symtab
Symtab.addFuncName("blah_1");
Symtab.addFuncName("blah_2");
@@ -658,16 +884,54 @@ TEST_F(InstrProfTest, instr_prof_symtab_test) {
ASSERT_EQ(StringRef("bar3"), R);
}
-TEST_F(InstrProfTest, instr_prof_symtab_compression_test) {
+// Testing symtab creator interface used by value profile transformer.
+TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_module_test) {
+ LLVMContext Ctx;
+ std::unique_ptr<Module> M = llvm::make_unique<Module>("MyModule.cpp", Ctx);
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),
+ /*isVarArg=*/false);
+ Function::Create(FTy, Function::ExternalLinkage, "Gfoo", M.get());
+ Function::Create(FTy, Function::ExternalLinkage, "Gblah", M.get());
+ Function::Create(FTy, Function::ExternalLinkage, "Gbar", M.get());
+ Function::Create(FTy, Function::InternalLinkage, "Ifoo", M.get());
+ Function::Create(FTy, Function::InternalLinkage, "Iblah", M.get());
+ Function::Create(FTy, Function::InternalLinkage, "Ibar", M.get());
+ Function::Create(FTy, Function::PrivateLinkage, "Pfoo", M.get());
+ Function::Create(FTy, Function::PrivateLinkage, "Pblah", M.get());
+ Function::Create(FTy, Function::PrivateLinkage, "Pbar", M.get());
+ Function::Create(FTy, Function::WeakODRLinkage, "Wfoo", M.get());
+ Function::Create(FTy, Function::WeakODRLinkage, "Wblah", M.get());
+ Function::Create(FTy, Function::WeakODRLinkage, "Wbar", M.get());
+
+ InstrProfSymtab ProfSymtab;
+ ProfSymtab.create(*M);
+
+ StringRef Funcs[] = {"Gfoo", "Gblah", "Gbar", "Ifoo", "Iblah", "Ibar",
+ "Pfoo", "Pblah", "Pbar", "Wfoo", "Wblah", "Wbar"};
+
+ for (unsigned I = 0; I < sizeof(Funcs) / sizeof(*Funcs); I++) {
+ Function *F = M->getFunction(Funcs[I]);
+ ASSERT_TRUE(F != nullptr);
+ std::string PGOName = getPGOFuncName(*F);
+ uint64_t Key = IndexedInstrProf::ComputeHash(PGOName);
+ ASSERT_EQ(StringRef(PGOName),
+ ProfSymtab.getFuncName(Key));
+ ASSERT_EQ(StringRef(Funcs[I]), ProfSymtab.getOrigFuncName(Key));
+ }
+}
+
+// Testing symtab serialization and creator/deserialization interface
+// used by coverage map reader, and raw profile reader.
+TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_compression_test) {
std::vector<std::string> FuncNames1;
std::vector<std::string> FuncNames2;
- for (int I = 0; I < 10 * 1024; I++) {
+ for (int I = 0; I < 3; I++) {
std::string str;
raw_string_ostream OS(str);
OS << "func_" << I;
FuncNames1.push_back(OS.str());
str.clear();
- OS << "fooooooooooooooo_" << I;
+ OS << "f oooooooooooooo_" << I;
FuncNames1.push_back(OS.str());
str.clear();
OS << "BAR_" << I;
@@ -677,21 +941,19 @@ TEST_F(InstrProfTest, instr_prof_symtab_compression_test) {
FuncNames2.push_back(OS.str());
}
- for (int Padding = 0; Padding < 10; Padding++) {
- for (int DoCompression = 0; DoCompression < 2; DoCompression++) {
- // Compressing:
- std::string FuncNameStrings1;
- collectPGOFuncNameStrings(FuncNames1,
- (DoCompression != 0 && zlib::isAvailable()),
- FuncNameStrings1);
-
- // Compressing:
- std::string FuncNameStrings2;
- collectPGOFuncNameStrings(FuncNames2,
- (DoCompression != 0 && zlib::isAvailable()),
- FuncNameStrings2);
-
- // Join with paddings:
+ for (bool DoCompression : {false, true}) {
+ // Compressing:
+ std::string FuncNameStrings1;
+ NoError(collectPGOFuncNameStrings(
+ FuncNames1, (DoCompression && zlib::isAvailable()), FuncNameStrings1));
+
+ // Compressing:
+ std::string FuncNameStrings2;
+ NoError(collectPGOFuncNameStrings(
+ FuncNames2, (DoCompression && zlib::isAvailable()), FuncNameStrings2));
+
+ for (int Padding = 0; Padding < 2; Padding++) {
+ // Join with paddings :
std::string FuncNameStrings = FuncNameStrings1;
for (int P = 0; P < Padding; P++) {
FuncNameStrings.push_back('\0');
@@ -700,23 +962,15 @@ TEST_F(InstrProfTest, instr_prof_symtab_compression_test) {
// Now decompress:
InstrProfSymtab Symtab;
- Symtab.create(StringRef(FuncNameStrings));
+ NoError(Symtab.create(StringRef(FuncNameStrings)));
// Now do the checks:
// First sampling some data points:
StringRef R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[0]));
ASSERT_EQ(StringRef("func_0"), R);
R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[1]));
- ASSERT_EQ(StringRef("fooooooooooooooo_0"), R);
- R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[998]));
- ASSERT_EQ(StringRef("func_499"), R);
- R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[999]));
- ASSERT_EQ(StringRef("fooooooooooooooo_499"), R);
- R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames2[100]));
- ASSERT_EQ(StringRef("BAR_50"), R);
- R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames2[101]));
- ASSERT_EQ(StringRef("BlahblahBlahblahBar_50"), R);
- for (int I = 0; I < 10 * 1024; I++) {
+ ASSERT_EQ(StringRef("f oooooooooooooo_0"), R);
+ for (int I = 0; I < 3; I++) {
std::string N[4];
N[0] = FuncNames1[2 * I];
N[1] = FuncNames1[2 * I + 1];
@@ -731,4 +985,22 @@ TEST_F(InstrProfTest, instr_prof_symtab_compression_test) {
}
}
+TEST_F(SparseInstrProfTest, preserve_no_records) {
+ InstrProfRecord Record1("foo", 0x1234, {0});
+ InstrProfRecord Record2("bar", 0x4321, {0, 0});
+ InstrProfRecord Record3("bar", 0x4321, {0, 0, 0});
+
+ NoError(Writer.addRecord(std::move(Record1)));
+ NoError(Writer.addRecord(std::move(Record2)));
+ NoError(Writer.addRecord(std::move(Record3)));
+ auto Profile = Writer.writeBuffer();
+ readProfile(std::move(Profile));
+
+ auto I = Reader->begin(), E = Reader->end();
+ ASSERT_TRUE(I == E);
+}
+
+INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseInstrProfTest,
+ ::testing::Bool());
+
} // end anonymous namespace
diff --git a/unittests/ProfileData/Makefile b/unittests/ProfileData/Makefile
deleted file mode 100644
index d017c15c00ad..000000000000
--- a/unittests/ProfileData/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/ProfileData/Makefile ----------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = ProfileData
-LINK_COMPONENTS := ProfileData Core Support
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/ProfileData/SampleProfTest.cpp b/unittests/ProfileData/SampleProfTest.cpp
index cc3c2f5306e4..175852b93fb2 100644
--- a/unittests/ProfileData/SampleProfTest.cpp
+++ b/unittests/ProfileData/SampleProfTest.cpp
@@ -1,5 +1,4 @@
-//===- unittest/ProfileData/SampleProfTest.cpp -------------------*- C++
-//-*-===//
+//===- unittest/ProfileData/SampleProfTest.cpp ------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -8,11 +7,27 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/ProfileData/ProfileCommon.h"
+#include "llvm/ProfileData/SampleProf.h"
#include "llvm/ProfileData/SampleProfReader.h"
#include "llvm/ProfileData/SampleProfWriter.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
-
-#include <cstdarg>
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
using namespace llvm;
using namespace sampleprof;
@@ -28,6 +43,7 @@ namespace {
struct SampleProfTest : ::testing::Test {
std::string Data;
+ LLVMContext Context;
std::unique_ptr<raw_ostream> OS;
std::unique_ptr<SampleProfileWriter> Writer;
std::unique_ptr<SampleProfileReader> Reader;
@@ -42,7 +58,7 @@ struct SampleProfTest : ::testing::Test {
}
void readProfile(std::unique_ptr<MemoryBuffer> &Profile) {
- auto ReaderOrErr = SampleProfileReader::create(Profile, getGlobalContext());
+ auto ReaderOrErr = SampleProfileReader::create(Profile, Context);
ASSERT_TRUE(NoError(ReaderOrErr.getError()));
Reader = std::move(ReaderOrErr.get());
}
@@ -52,12 +68,18 @@ struct SampleProfTest : ::testing::Test {
StringRef FooName("_Z3fooi");
FunctionSamples FooSamples;
+ FooSamples.setName(FooName);
FooSamples.addTotalSamples(7711);
FooSamples.addHeadSamples(610);
FooSamples.addBodySamples(1, 0, 610);
+ FooSamples.addBodySamples(2, 0, 600);
+ FooSamples.addBodySamples(4, 0, 60000);
+ FooSamples.addBodySamples(8, 0, 60351);
+ FooSamples.addBodySamples(10, 0, 605);
StringRef BarName("_Z3bari");
FunctionSamples BarSamples;
+ BarSamples.setName(BarName);
BarSamples.addTotalSamples(20301);
BarSamples.addHeadSamples(1437);
BarSamples.addBodySamples(1, 0, 1437);
@@ -88,6 +110,55 @@ struct SampleProfTest : ::testing::Test {
FunctionSamples &ReadBarSamples = ReadProfiles[BarName];
ASSERT_EQ(20301u, ReadBarSamples.getTotalSamples());
ASSERT_EQ(1437u, ReadBarSamples.getHeadSamples());
+
+ auto VerifySummary = [](ProfileSummary &Summary) mutable {
+ ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());
+ ASSERT_EQ(123603u, Summary.getTotalCount());
+ ASSERT_EQ(6u, Summary.getNumCounts());
+ ASSERT_EQ(2u, Summary.getNumFunctions());
+ ASSERT_EQ(1437u, Summary.getMaxFunctionCount());
+ ASSERT_EQ(60351u, Summary.getMaxCount());
+
+ uint32_t Cutoff = 800000;
+ auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {
+ return PE.Cutoff == Cutoff;
+ };
+ std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary();
+ auto EightyPerc = std::find_if(Details.begin(), Details.end(), Predicate);
+ Cutoff = 900000;
+ auto NinetyPerc = std::find_if(Details.begin(), Details.end(), Predicate);
+ Cutoff = 950000;
+ auto NinetyFivePerc =
+ std::find_if(Details.begin(), Details.end(), Predicate);
+ Cutoff = 990000;
+ auto NinetyNinePerc =
+ std::find_if(Details.begin(), Details.end(), Predicate);
+ ASSERT_EQ(60000u, EightyPerc->MinCount);
+ ASSERT_EQ(60000u, NinetyPerc->MinCount);
+ ASSERT_EQ(60000u, NinetyFivePerc->MinCount);
+ ASSERT_EQ(610u, NinetyNinePerc->MinCount);
+ };
+
+ ProfileSummary &Summary = Reader->getSummary();
+ VerifySummary(Summary);
+
+ // Test that conversion of summary to and from Metadata works.
+ Metadata *MD = Summary.getMD(Context);
+ ASSERT_TRUE(MD);
+ ProfileSummary *PS = ProfileSummary::getFromMD(MD);
+ ASSERT_TRUE(PS);
+ VerifySummary(*PS);
+ delete PS;
+
+ // Test that summary can be attached to and read back from module.
+ Module M("my_module", Context);
+ M.setProfileSummary(MD);
+ MD = M.getProfileSummary();
+ ASSERT_TRUE(MD);
+ PS = ProfileSummary::getFromMD(MD);
+ ASSERT_TRUE(PS);
+ VerifySummary(*PS);
+ delete PS;
}
};
diff --git a/unittests/Support/AlignOfTest.cpp b/unittests/Support/AlignOfTest.cpp
index be208f7d28ea..74b03f0e7dfd 100644
--- a/unittests/Support/AlignOfTest.cpp
+++ b/unittests/Support/AlignOfTest.cpp
@@ -1,4 +1,4 @@
-//=== - llvm/unittest/Support/AlignOfTest.cpp - Alignment utility tests ----===//
+//=== - llvm/unittest/Support/AlignOfTest.cpp - Alignment utility tests ---===//
//
// The LLVM Compiler Infrastructure
//
@@ -97,7 +97,7 @@ struct Abstract1 {
};
struct Abstract2 : Abstract1 {
- virtual ~Abstract2() {}
+ ~Abstract2() override = default;
double d;
};
@@ -354,4 +354,4 @@ TEST(AlignOfTest, BasicAlignedArray) {
EXPECT_EQ(2u, sizeof(AlignedCharArray<2, 2>));
EXPECT_EQ(16u, sizeof(AlignedCharArray<2, 16>));
}
-}
+} // end anonymous namespace
diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt
index 3ab98d58d5f0..9a4a14450911 100644
--- a/unittests/Support/CMakeLists.txt
+++ b/unittests/Support/CMakeLists.txt
@@ -1,5 +1,4 @@
set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
Support
)
@@ -17,6 +16,7 @@ add_llvm_unittest(SupportTests
DwarfTest.cpp
EndianStreamTest.cpp
EndianTest.cpp
+ ErrorTest.cpp
ErrorOrTest.cpp
FileOutputBufferTest.cpp
IteratorTest.cpp
@@ -36,14 +36,15 @@ add_llvm_unittest(SupportTests
ScaledNumberTest.cpp
SourceMgrTest.cpp
SpecialCaseListTest.cpp
- StreamingMemoryObject.cpp
+ StreamingMemoryObjectTest.cpp
StringPool.cpp
SwapByteOrderTest.cpp
- TargetRegistry.cpp
+ TargetParserTest.cpp
ThreadLocalTest.cpp
ThreadPool.cpp
TimerTest.cpp
TimeValueTest.cpp
+ TypeNameTest.cpp
TrailingObjectsTest.cpp
UnicodeTest.cpp
YAMLIOTest.cpp
@@ -51,9 +52,8 @@ add_llvm_unittest(SupportTests
formatted_raw_ostream_test.cpp
raw_ostream_test.cpp
raw_pwrite_stream_test.cpp
+ raw_sha1_ostream_test.cpp
)
# ManagedStatic.cpp uses <pthread>.
-if(LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD)
- target_link_libraries(SupportTests pthread)
-endif()
+target_link_libraries(SupportTests ${PTHREAD_LIB})
diff --git a/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp
index eac669f467b7..9b24c1e40bca 100644
--- a/unittests/Support/CommandLineTest.cpp
+++ b/unittests/Support/CommandLineTest.cpp
@@ -67,6 +67,22 @@ public:
: Base(M0, M1, M2, M3) {}
~StackOption() override { this->removeArgument(); }
+
+ template <class DT> StackOption<T> &operator=(const DT &V) {
+ this->setValue(V);
+ return *this;
+ }
+};
+
+class StackSubCommand : public cl::SubCommand {
+public:
+ StackSubCommand(const char *const Name,
+ const char *const Description = nullptr)
+ : SubCommand(Name, Description) {}
+
+ StackSubCommand() : SubCommand() {}
+
+ ~StackSubCommand() { unregisterSubCommand(); }
};
@@ -78,7 +94,8 @@ TEST(CommandLineTest, ModifyExisitingOption) {
const char ArgString[] = "new-test-option";
const char ValueString[] = "Integer";
- StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
+ StringMap<cl::Option *> &Map =
+ cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_TRUE(Map.count("test-option") == 1) <<
"Could not find option in map.";
@@ -165,11 +182,12 @@ void testCommandLineTokenizer(ParserFunction *parse, const char *Input,
}
TEST(CommandLineTest, TokenizeGNUCommandLine) {
- const char *Input = "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' "
- "foo\"bar\"baz C:\\src\\foo.cpp \"C:\\src\\foo.cpp\"";
- const char *const Output[] = { "foo bar", "foo bar", "foo bar", "foo\\bar",
- "foobarbaz", "C:\\src\\foo.cpp",
- "C:\\src\\foo.cpp" };
+ const char *Input =
+ "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' -DFOO=bar\\(\\) "
+ "foo\"bar\"baz C:\\\\src\\\\foo.cpp \"C:\\src\\foo.cpp\"";
+ const char *const Output[] = {
+ "foo bar", "foo bar", "foo bar", "foo\\bar",
+ "-DFOO=bar()", "foobarbaz", "C:\\src\\foo.cpp", "C:srcfoo.cpp"};
testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output,
array_lengthof(Output));
}
@@ -236,7 +254,8 @@ TEST(CommandLineTest, HideUnrelatedOptions) {
ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
<< "Hid extra option that should be visable.";
- StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
+ StringMap<cl::Option *> &Map =
+ cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
<< "Hid default option that should be visable.";
}
@@ -260,9 +279,201 @@ TEST(CommandLineTest, HideUnrelatedOptionsMulti) {
ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())
<< "Hid extra option that should be visable.";
- StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
+ StringMap<cl::Option *> &Map =
+ cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
<< "Hid default option that should be visable.";
}
+TEST(CommandLineTest, SetValueInSubcategories) {
+ cl::ResetCommandLineParser();
+
+ StackSubCommand SC1("sc1", "First subcommand");
+ StackSubCommand SC2("sc2", "Second subcommand");
+
+ StackOption<bool> TopLevelOpt("top-level", cl::init(false));
+ StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
+ StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
+
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_FALSE(SC1Opt);
+ EXPECT_FALSE(SC2Opt);
+ const char *args[] = {"prog", "-top-level"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+ EXPECT_TRUE(TopLevelOpt);
+ EXPECT_FALSE(SC1Opt);
+ EXPECT_FALSE(SC2Opt);
+
+ TopLevelOpt = false;
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_FALSE(SC1Opt);
+ EXPECT_FALSE(SC2Opt);
+ const char *args2[] = {"prog", "sc1", "-sc1"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_TRUE(SC1Opt);
+ EXPECT_FALSE(SC2Opt);
+
+ SC1Opt = false;
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_FALSE(SC1Opt);
+ EXPECT_FALSE(SC2Opt);
+ const char *args3[] = {"prog", "sc2", "-sc2"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_FALSE(SC1Opt);
+ EXPECT_TRUE(SC2Opt);
+}
+
+TEST(CommandLineTest, LookupFailsInWrongSubCommand) {
+ cl::ResetCommandLineParser();
+
+ StackSubCommand SC1("sc1", "First subcommand");
+ StackSubCommand SC2("sc2", "Second subcommand");
+
+ StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
+ StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
+
+ const char *args[] = {"prog", "sc1", "-sc2"};
+ EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
+}
+
+TEST(CommandLineTest, AddToAllSubCommands) {
+ cl::ResetCommandLineParser();
+
+ StackSubCommand SC1("sc1", "First subcommand");
+ StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands),
+ cl::init(false));
+ StackSubCommand SC2("sc2", "Second subcommand");
+
+ const char *args[] = {"prog", "-everywhere"};
+ const char *args2[] = {"prog", "sc1", "-everywhere"};
+ const char *args3[] = {"prog", "sc2", "-everywhere"};
+
+ EXPECT_FALSE(AllOpt);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+ EXPECT_TRUE(AllOpt);
+
+ AllOpt = false;
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(AllOpt);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
+ EXPECT_TRUE(AllOpt);
+
+ AllOpt = false;
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(AllOpt);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
+ EXPECT_TRUE(AllOpt);
+}
+
+TEST(CommandLineTest, ReparseCommandLineOptions) {
+ cl::ResetCommandLineParser();
+
+ StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand),
+ cl::init(false));
+
+ const char *args[] = {"prog", "-top-level"};
+
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+ EXPECT_TRUE(TopLevelOpt);
+
+ TopLevelOpt = false;
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+ EXPECT_TRUE(TopLevelOpt);
+}
+
+TEST(CommandLineTest, RemoveFromRegularSubCommand) {
+ cl::ResetCommandLineParser();
+
+ StackSubCommand SC("sc", "Subcommand");
+ StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false));
+ StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false));
+
+ const char *args[] = {"prog", "sc", "-remove-option"};
+
+ EXPECT_FALSE(RemoveOption);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, nullptr, true));
+ EXPECT_TRUE(RemoveOption);
+
+ RemoveOption.removeArgument();
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
+}
+
+TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {
+ cl::ResetCommandLineParser();
+
+ StackOption<bool> TopLevelRemove(
+ "top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false));
+ StackOption<bool> TopLevelKeep(
+ "top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false));
+
+ const char *args[] = {"prog", "-top-level-remove"};
+
+ EXPECT_FALSE(TopLevelRemove);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+ EXPECT_TRUE(TopLevelRemove);
+
+ TopLevelRemove.removeArgument();
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+}
+
+TEST(CommandLineTest, RemoveFromAllSubCommands) {
+ cl::ResetCommandLineParser();
+
+ StackSubCommand SC1("sc1", "First Subcommand");
+ StackSubCommand SC2("sc2", "Second Subcommand");
+ StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands),
+ cl::init(false));
+ StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands),
+ cl::init(false));
+
+ const char *args0[] = {"prog", "-remove-option"};
+ const char *args1[] = {"prog", "sc1", "-remove-option"};
+ const char *args2[] = {"prog", "sc2", "-remove-option"};
+
+ // It should work for all subcommands including the top-level.
+ EXPECT_FALSE(RemoveOption);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
+ EXPECT_TRUE(RemoveOption);
+
+ RemoveOption = false;
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(RemoveOption);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
+ EXPECT_TRUE(RemoveOption);
+
+ RemoveOption = false;
+
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(RemoveOption);
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
+ EXPECT_TRUE(RemoveOption);
+
+ RemoveOption.removeArgument();
+
+ // It should not work for any subcommands including the top-level.
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
+ cl::ResetAllOptionOccurrences();
+ EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
+}
+
} // anonymous namespace
diff --git a/unittests/Support/ConvertUTFTest.cpp b/unittests/Support/ConvertUTFTest.cpp
index d436fc022896..0af09e98a217 100644
--- a/unittests/Support/ConvertUTFTest.cpp
+++ b/unittests/Support/ConvertUTFTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/ConvertUTF.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Format.h"
#include "gtest/gtest.h"
#include <string>
@@ -59,7 +60,7 @@ TEST(ConvertUTFTest, OddLengthInput) {
TEST(ConvertUTFTest, Empty) {
std::string Result;
- bool Success = convertUTF16ToUTF8String(None, Result);
+ bool Success = convertUTF16ToUTF8String(llvm::ArrayRef<char>(None), Result);
EXPECT_TRUE(Success);
EXPECT_TRUE(Result.empty());
}
@@ -80,6 +81,41 @@ TEST(ConvertUTFTest, HasUTF16BOM) {
EXPECT_FALSE(HasBOM);
}
+TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) {
+ // Src is the look of disapproval.
+ static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
+ ArrayRef<UTF16> SrcRef = makeArrayRef((const UTF16 *)Src, 4);
+ std::string Result;
+ bool Success = convertUTF16ToUTF8String(SrcRef, Result);
+ EXPECT_TRUE(Success);
+ std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST(ConvertUTFTest, ConvertUTF8toWide) {
+ // Src is the look of disapproval.
+ static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0";
+ std::wstring Result;
+ bool Success = ConvertUTF8toWide((const char*)Src, Result);
+ EXPECT_TRUE(Success);
+ std::wstring Expected(L"\x0ca0_\x0ca0");
+ EXPECT_EQ(Expected, Result);
+ Result.clear();
+ Success = ConvertUTF8toWide(StringRef(Src, 7), Result);
+ EXPECT_TRUE(Success);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST(ConvertUTFTest, convertWideToUTF8) {
+ // Src is the look of disapproval.
+ static const wchar_t Src[] = L"\x0ca0_\x0ca0";
+ std::string Result;
+ bool Success = convertWideToUTF8(Src, Result);
+ EXPECT_TRUE(Success);
+ std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
+ EXPECT_EQ(Expected, Result);
+}
+
struct ConvertUTFResultContainer {
ConversionResult ErrorCode;
std::vector<unsigned> UnicodeScalars;
diff --git a/unittests/Support/EndianStreamTest.cpp b/unittests/Support/EndianStreamTest.cpp
index 6a69be55f926..ea6c308c560a 100644
--- a/unittests/Support/EndianStreamTest.cpp
+++ b/unittests/Support/EndianStreamTest.cpp
@@ -153,5 +153,56 @@ TEST(EndianStream, WriteDoubleBE) {
EXPECT_EQ(static_cast<uint8_t>(data[7]), 0x20);
}
+TEST(EndianStream, WriteArrayLE) {
+ SmallString<16> Data;
+
+ {
+ raw_svector_ostream OS(Data);
+ endian::Writer<little> LE(OS);
+ LE.write<uint16_t>({0x1234, 0x5678});
+ }
+
+ EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x34);
+ EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0x12);
+ EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x78);
+ EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x56);
+}
+
+TEST(EndianStream, WriteVectorLE) {
+ SmallString<16> Data;
+
+ {
+ raw_svector_ostream OS(Data);
+ endian::Writer<little> LE(OS);
+ std::vector<uint16_t> Vec{0x1234, 0x5678};
+ LE.write<uint16_t>(Vec);
+ }
+
+ EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x34);
+ EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0x12);
+ EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x78);
+ EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x56);
+}
+
+TEST(EndianStream, WriteFloatArrayLE) {
+ SmallString<16> Data;
+
+ {
+ raw_svector_ostream OS(Data);
+ endian::Writer<little> LE(OS);
+ LE.write<float>({12345.0f, 12346.0f});
+ }
+
+ EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x00);
+ EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0xE4);
+ EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x40);
+ EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x46);
+
+ EXPECT_EQ(static_cast<uint8_t>(Data[4]), 0x00);
+ EXPECT_EQ(static_cast<uint8_t>(Data[5]), 0xE8);
+ EXPECT_EQ(static_cast<uint8_t>(Data[6]), 0x40);
+ EXPECT_EQ(static_cast<uint8_t>(Data[7]), 0x46);
+}
+
} // end anon namespace
diff --git a/unittests/Support/ErrorOrTest.cpp b/unittests/Support/ErrorOrTest.cpp
index 73d0e3f2bb71..87dcab7b181e 100644
--- a/unittests/Support/ErrorOrTest.cpp
+++ b/unittests/Support/ErrorOrTest.cpp
@@ -16,7 +16,7 @@ using namespace llvm;
namespace {
-ErrorOr<int> t1() {return 1;}
+ErrorOr<int> t1() { return 1; }
ErrorOr<int> t2() { return errc::invalid_argument; }
TEST(ErrorOr, SimpleValue) {
@@ -71,6 +71,46 @@ TEST(ErrorOr, Comparison) {
EXPECT_EQ(x, errc::no_such_file_or_directory);
}
+TEST(ErrorOr, ImplicitConversion) {
+ ErrorOr<std::string> x("string literal");
+ EXPECT_TRUE(!!x);
+}
+
+TEST(ErrorOr, ImplicitConversionCausesMove) {
+ struct Source {};
+ struct Destination {
+ Destination(const Source&) {}
+ Destination(Source&&) = delete;
+ };
+ Source s;
+ ErrorOr<Destination> x = s;
+ EXPECT_TRUE(!!x);
+}
+
+TEST(ErrorOr, ImplicitConversionNoAmbiguity) {
+ struct CastsToErrorCode {
+ CastsToErrorCode() = default;
+ CastsToErrorCode(std::error_code) {}
+ operator std::error_code() { return errc::invalid_argument; }
+ } casts_to_error_code;
+ ErrorOr<CastsToErrorCode> x1(casts_to_error_code);
+ ErrorOr<CastsToErrorCode> x2 = casts_to_error_code;
+ ErrorOr<CastsToErrorCode> x3 = {casts_to_error_code};
+ ErrorOr<CastsToErrorCode> x4{casts_to_error_code};
+ ErrorOr<CastsToErrorCode> x5(errc::no_such_file_or_directory);
+ ErrorOr<CastsToErrorCode> x6 = errc::no_such_file_or_directory;
+ ErrorOr<CastsToErrorCode> x7 = {errc::no_such_file_or_directory};
+ ErrorOr<CastsToErrorCode> x8{errc::no_such_file_or_directory};
+ EXPECT_TRUE(!!x1);
+ EXPECT_TRUE(!!x2);
+ EXPECT_TRUE(!!x3);
+ EXPECT_TRUE(!!x4);
+ EXPECT_FALSE(x5);
+ EXPECT_FALSE(x6);
+ EXPECT_FALSE(x7);
+ EXPECT_FALSE(x8);
+}
+
// ErrorOr<int*> x(nullptr);
// ErrorOr<std::unique_ptr<int>> y = x; // invalid conversion
static_assert(
diff --git a/unittests/Support/ErrorTest.cpp b/unittests/Support/ErrorTest.cpp
new file mode 100644
index 000000000000..205092683744
--- /dev/null
+++ b/unittests/Support/ErrorTest.cpp
@@ -0,0 +1,627 @@
+//===----- unittests/ErrorTest.cpp - Error.h tests ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Error.h"
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+using namespace llvm;
+
+namespace {
+
+// Custom error class with a default base class and some random 'info' attached.
+class CustomError : public ErrorInfo<CustomError> {
+public:
+ // Create an error with some info attached.
+ CustomError(int Info) : Info(Info) {}
+
+ // Get the info attached to this error.
+ int getInfo() const { return Info; }
+
+ // Log this error to a stream.
+ void log(raw_ostream &OS) const override {
+ OS << "CustomError { " << getInfo() << "}";
+ }
+
+ std::error_code convertToErrorCode() const override {
+ llvm_unreachable("CustomError doesn't support ECError conversion");
+ }
+
+ // Used by ErrorInfo::classID.
+ static char ID;
+
+protected:
+ // This error is subclassed below, but we can't use inheriting constructors
+ // yet, so we can't propagate the constructors through ErrorInfo. Instead
+ // we have to have a default constructor and have the subclass initialize all
+ // fields.
+ CustomError() : Info(0) {}
+
+ int Info;
+};
+
+char CustomError::ID = 0;
+
+// Custom error class with a custom base class and some additional random
+// 'info'.
+class CustomSubError : public ErrorInfo<CustomSubError, CustomError> {
+public:
+ // Create a sub-error with some info attached.
+ CustomSubError(int Info, int ExtraInfo) : ExtraInfo(ExtraInfo) {
+ this->Info = Info;
+ }
+
+ // Get the extra info attached to this error.
+ int getExtraInfo() const { return ExtraInfo; }
+
+ // Log this error to a stream.
+ void log(raw_ostream &OS) const override {
+ OS << "CustomSubError { " << getInfo() << ", " << getExtraInfo() << "}";
+ }
+
+ std::error_code convertToErrorCode() const override {
+ llvm_unreachable("CustomSubError doesn't support ECError conversion");
+ }
+
+ // Used by ErrorInfo::classID.
+ static char ID;
+
+protected:
+ int ExtraInfo;
+};
+
+char CustomSubError::ID = 0;
+
+static Error handleCustomError(const CustomError &CE) { return Error(); }
+
+static void handleCustomErrorVoid(const CustomError &CE) {}
+
+static Error handleCustomErrorUP(std::unique_ptr<CustomError> CE) {
+ return Error();
+}
+
+static void handleCustomErrorUPVoid(std::unique_ptr<CustomError> CE) {}
+
+// Test that success values implicitly convert to false, and don't cause crashes
+// once they've been implicitly converted.
+TEST(Error, CheckedSuccess) {
+ Error E;
+ EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'";
+}
+
+// Test that unchecked succes values cause an abort.
+#ifndef NDEBUG
+TEST(Error, UncheckedSuccess) {
+ EXPECT_DEATH({ Error E; }, "Program aborted due to an unhandled Error:")
+ << "Unchecked Error Succes value did not cause abort()";
+}
+#endif
+
+// ErrorAsOutParameter tester.
+void errAsOutParamHelper(Error &Err) {
+ ErrorAsOutParameter ErrAsOutParam(Err);
+ // Verify that checked flag is raised - assignment should not crash.
+ Err = Error::success();
+ // Raise the checked bit manually - caller should still have to test the
+ // error.
+ (void)!!Err;
+}
+
+// Test that ErrorAsOutParameter sets the checked flag on construction.
+TEST(Error, ErrorAsOutParameterChecked) {
+ Error E;
+ errAsOutParamHelper(E);
+ (void)!!E;
+}
+
+// Test that ErrorAsOutParameter clears the checked flag on destruction.
+#ifndef NDEBUG
+TEST(Error, ErrorAsOutParameterUnchecked) {
+ EXPECT_DEATH({ Error E; errAsOutParamHelper(E); },
+ "Program aborted due to an unhandled Error:")
+ << "ErrorAsOutParameter did not clear the checked flag on destruction.";
+}
+#endif
+
+// Check that we abort on unhandled failure cases. (Force conversion to bool
+// to make sure that we don't accidentally treat checked errors as handled).
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, UncheckedError) {
+ auto DropUnhandledError = []() {
+ Error E = make_error<CustomError>(42);
+ (void)!E;
+ };
+ EXPECT_DEATH(DropUnhandledError(),
+ "Program aborted due to an unhandled Error:")
+ << "Unhandled Error failure value did not cause abort()";
+}
+#endif
+
+// Check 'Error::isA<T>' method handling.
+TEST(Error, IsAHandling) {
+ // Check 'isA' handling.
+ Error E = make_error<CustomError>(1);
+ Error F = make_error<CustomSubError>(1, 2);
+ Error G = Error::success();
+
+ EXPECT_TRUE(E.isA<CustomError>());
+ EXPECT_FALSE(E.isA<CustomSubError>());
+ EXPECT_TRUE(F.isA<CustomError>());
+ EXPECT_TRUE(F.isA<CustomSubError>());
+ EXPECT_FALSE(G.isA<CustomError>());
+
+ consumeError(std::move(E));
+ consumeError(std::move(F));
+ consumeError(std::move(G));
+}
+
+// Check that we can handle a custom error.
+TEST(Error, HandleCustomError) {
+ int CaughtErrorInfo = 0;
+ handleAllErrors(make_error<CustomError>(42), [&](const CustomError &CE) {
+ CaughtErrorInfo = CE.getInfo();
+ });
+
+ EXPECT_TRUE(CaughtErrorInfo == 42) << "Wrong result from CustomError handler";
+}
+
+// Check that handler type deduction also works for handlers
+// of the following types:
+// void (const Err&)
+// Error (const Err&) mutable
+// void (const Err&) mutable
+// Error (Err&)
+// void (Err&)
+// Error (Err&) mutable
+// void (Err&) mutable
+// Error (unique_ptr<Err>)
+// void (unique_ptr<Err>)
+// Error (unique_ptr<Err>) mutable
+// void (unique_ptr<Err>) mutable
+TEST(Error, HandlerTypeDeduction) {
+
+ handleAllErrors(make_error<CustomError>(42), [](const CustomError &CE) {});
+
+ handleAllErrors(
+ make_error<CustomError>(42),
+ [](const CustomError &CE) mutable { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](const CustomError &CE) mutable {});
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](CustomError &CE) { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) {});
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](CustomError &CE) mutable { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) mutable {});
+
+ handleAllErrors(
+ make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) {});
+
+ handleAllErrors(
+ make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) mutable { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) mutable {});
+
+ // Check that named handlers of type 'Error (const Err&)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomError);
+
+ // Check that named handlers of type 'void (const Err&)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomErrorVoid);
+
+ // Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomErrorUP);
+
+ // Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomErrorUPVoid);
+}
+
+// Test that we can handle errors with custom base classes.
+TEST(Error, HandleCustomErrorWithCustomBaseClass) {
+ int CaughtErrorInfo = 0;
+ int CaughtErrorExtraInfo = 0;
+ handleAllErrors(make_error<CustomSubError>(42, 7),
+ [&](const CustomSubError &SE) {
+ CaughtErrorInfo = SE.getInfo();
+ CaughtErrorExtraInfo = SE.getExtraInfo();
+ });
+
+ EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7)
+ << "Wrong result from CustomSubError handler";
+}
+
+// Check that we trigger only the first handler that applies.
+TEST(Error, FirstHandlerOnly) {
+ int DummyInfo = 0;
+ int CaughtErrorInfo = 0;
+ int CaughtErrorExtraInfo = 0;
+
+ handleAllErrors(make_error<CustomSubError>(42, 7),
+ [&](const CustomSubError &SE) {
+ CaughtErrorInfo = SE.getInfo();
+ CaughtErrorExtraInfo = SE.getExtraInfo();
+ },
+ [&](const CustomError &CE) { DummyInfo = CE.getInfo(); });
+
+ EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7 &&
+ DummyInfo == 0)
+ << "Activated the wrong Error handler(s)";
+}
+
+// Check that general handlers shadow specific ones.
+TEST(Error, HandlerShadowing) {
+ int CaughtErrorInfo = 0;
+ int DummyInfo = 0;
+ int DummyExtraInfo = 0;
+
+ handleAllErrors(
+ make_error<CustomSubError>(42, 7),
+ [&](const CustomError &CE) { CaughtErrorInfo = CE.getInfo(); },
+ [&](const CustomSubError &SE) {
+ DummyInfo = SE.getInfo();
+ DummyExtraInfo = SE.getExtraInfo();
+ });
+
+ EXPECT_TRUE(CaughtErrorInfo == 42 && DummyInfo == 0 && DummyExtraInfo == 0)
+ << "General Error handler did not shadow specific handler";
+}
+
+// Test joinErrors.
+TEST(Error, CheckJoinErrors) {
+ int CustomErrorInfo1 = 0;
+ int CustomErrorInfo2 = 0;
+ int CustomErrorExtraInfo = 0;
+ Error E =
+ joinErrors(make_error<CustomError>(7), make_error<CustomSubError>(42, 7));
+
+ handleAllErrors(std::move(E),
+ [&](const CustomSubError &SE) {
+ CustomErrorInfo2 = SE.getInfo();
+ CustomErrorExtraInfo = SE.getExtraInfo();
+ },
+ [&](const CustomError &CE) {
+ // Assert that the CustomError instance above is handled
+ // before the
+ // CustomSubError - joinErrors should preserve error
+ // ordering.
+ EXPECT_EQ(CustomErrorInfo2, 0)
+ << "CustomErrorInfo2 should be 0 here. "
+ "joinErrors failed to preserve ordering.\n";
+ CustomErrorInfo1 = CE.getInfo();
+ });
+
+ EXPECT_TRUE(CustomErrorInfo1 == 7 && CustomErrorInfo2 == 42 &&
+ CustomErrorExtraInfo == 7)
+ << "Failed handling compound Error.";
+
+ // Test appending a single item to a list.
+ {
+ int Sum = 0;
+ handleAllErrors(
+ joinErrors(
+ joinErrors(make_error<CustomError>(7),
+ make_error<CustomError>(7)),
+ make_error<CustomError>(7)),
+ [&](const CustomError &CE) {
+ Sum += CE.getInfo();
+ });
+ EXPECT_EQ(Sum, 21) << "Failed to correctly append error to error list.";
+ }
+
+ // Test prepending a single item to a list.
+ {
+ int Sum = 0;
+ handleAllErrors(
+ joinErrors(
+ make_error<CustomError>(7),
+ joinErrors(make_error<CustomError>(7),
+ make_error<CustomError>(7))),
+ [&](const CustomError &CE) {
+ Sum += CE.getInfo();
+ });
+ EXPECT_EQ(Sum, 21) << "Failed to correctly prepend error to error list.";
+ }
+
+ // Test concatenating two error lists.
+ {
+ int Sum = 0;
+ handleAllErrors(
+ joinErrors(
+ joinErrors(
+ make_error<CustomError>(7),
+ make_error<CustomError>(7)),
+ joinErrors(
+ make_error<CustomError>(7),
+ make_error<CustomError>(7))),
+ [&](const CustomError &CE) {
+ Sum += CE.getInfo();
+ });
+ EXPECT_EQ(Sum, 28) << "Failed to correctly concatenate erorr lists.";
+ }
+}
+
+// Test that we can consume success values.
+TEST(Error, ConsumeSuccess) {
+ Error E;
+ consumeError(std::move(E));
+}
+
+TEST(Error, ConsumeError) {
+ Error E = make_error<CustomError>(7);
+ consumeError(std::move(E));
+}
+
+// Test that handleAllUnhandledErrors crashes if an error is not caught.
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, FailureToHandle) {
+ auto FailToHandle = []() {
+ handleAllErrors(make_error<CustomError>(7), [&](const CustomSubError &SE) {
+ errs() << "This should never be called";
+ exit(1);
+ });
+ };
+
+ EXPECT_DEATH(FailToHandle(), "Program aborted due to an unhandled Error:")
+ << "Unhandled Error in handleAllErrors call did not cause an "
+ "abort()";
+}
+#endif
+
+// Test that handleAllUnhandledErrors crashes if an error is returned from a
+// handler.
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, FailureFromHandler) {
+ auto ReturnErrorFromHandler = []() {
+ handleAllErrors(make_error<CustomError>(7),
+ [&](std::unique_ptr<CustomSubError> SE) {
+ return Error(std::move(SE));
+ });
+ };
+
+ EXPECT_DEATH(ReturnErrorFromHandler(),
+ "Program aborted due to an unhandled Error:")
+ << " Error returned from handler in handleAllErrors call did not "
+ "cause abort()";
+}
+#endif
+
+// Test that we can return values from handleErrors.
+TEST(Error, CatchErrorFromHandler) {
+ int ErrorInfo = 0;
+
+ Error E = handleErrors(
+ make_error<CustomError>(7),
+ [&](std::unique_ptr<CustomError> CE) { return Error(std::move(CE)); });
+
+ handleAllErrors(std::move(E),
+ [&](const CustomError &CE) { ErrorInfo = CE.getInfo(); });
+
+ EXPECT_EQ(ErrorInfo, 7)
+ << "Failed to handle Error returned from handleErrors.";
+}
+
+TEST(Error, StringError) {
+ std::string Msg;
+ raw_string_ostream S(Msg);
+ logAllUnhandledErrors(make_error<StringError>("foo" + Twine(42),
+ inconvertibleErrorCode()),
+ S, "");
+ EXPECT_EQ(S.str(), "foo42\n") << "Unexpected StringError log result";
+
+ auto EC =
+ errorToErrorCode(make_error<StringError>("", errc::invalid_argument));
+ EXPECT_EQ(EC, errc::invalid_argument)
+ << "Failed to convert StringError to error_code.";
+}
+
+// Test that the ExitOnError utility works as expected.
+TEST(Error, ExitOnError) {
+ ExitOnError ExitOnErr;
+ ExitOnErr.setBanner("Error in tool:");
+ ExitOnErr.setExitCodeMapper([](const Error &E) {
+ if (E.isA<CustomSubError>())
+ return 2;
+ return 1;
+ });
+
+ // Make sure we don't bail on success.
+ ExitOnErr(Error::success());
+ EXPECT_EQ(ExitOnErr(Expected<int>(7)), 7)
+ << "exitOnError returned an invalid value for Expected";
+
+ int A = 7;
+ int &B = ExitOnErr(Expected<int&>(A));
+ EXPECT_EQ(&A, &B) << "ExitOnError failed to propagate reference";
+
+ // Exit tests.
+ EXPECT_EXIT(ExitOnErr(make_error<CustomError>(7)),
+ ::testing::ExitedWithCode(1), "Error in tool:")
+ << "exitOnError returned an unexpected error result";
+
+ EXPECT_EXIT(ExitOnErr(Expected<int>(make_error<CustomSubError>(0, 0))),
+ ::testing::ExitedWithCode(2), "Error in tool:")
+ << "exitOnError returned an unexpected error result";
+}
+
+// Test Checked Expected<T> in success mode.
+TEST(Error, CheckedExpectedInSuccessMode) {
+ Expected<int> A = 7;
+ EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'";
+ // Access is safe in second test, since we checked the error in the first.
+ EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value";
+}
+
+// Test Expected with reference type.
+TEST(Error, ExpectedWithReferenceType) {
+ int A = 7;
+ Expected<int&> B = A;
+ // 'Check' B.
+ (void)!!B;
+ int &C = *B;
+ EXPECT_EQ(&A, &C) << "Expected failed to propagate reference";
+}
+
+// Test Unchecked Expected<T> in success mode.
+// We expect this to blow up the same way Error would.
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, UncheckedExpectedInSuccessModeDestruction) {
+ EXPECT_DEATH({ Expected<int> A = 7; },
+ "Expected<T> must be checked before access or destruction.")
+ << "Unchecekd Expected<T> success value did not cause an abort().";
+}
+#endif
+
+// Test Unchecked Expected<T> in success mode.
+// We expect this to blow up the same way Error would.
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, UncheckedExpectedInSuccessModeAccess) {
+ EXPECT_DEATH({ Expected<int> A = 7; *A; },
+ "Expected<T> must be checked before access or destruction.")
+ << "Unchecekd Expected<T> success value did not cause an abort().";
+}
+#endif
+
+// Test Unchecked Expected<T> in success mode.
+// We expect this to blow up the same way Error would.
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, UncheckedExpectedInSuccessModeAssignment) {
+ EXPECT_DEATH({ Expected<int> A = 7; A = 7; },
+ "Expected<T> must be checked before access or destruction.")
+ << "Unchecekd Expected<T> success value did not cause an abort().";
+}
+#endif
+
+// Test Expected<T> in failure mode.
+TEST(Error, ExpectedInFailureMode) {
+ Expected<int> A = make_error<CustomError>(42);
+ EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'";
+ Error E = A.takeError();
+ EXPECT_TRUE(E.isA<CustomError>()) << "Incorrect Expected error value";
+ consumeError(std::move(E));
+}
+
+// Check that an Expected instance with an error value doesn't allow access to
+// operator*.
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, AccessExpectedInFailureMode) {
+ Expected<int> A = make_error<CustomError>(42);
+ EXPECT_DEATH(*A, "Expected<T> must be checked before access or destruction.")
+ << "Incorrect Expected error value";
+ consumeError(A.takeError());
+}
+#endif
+
+// Check that an Expected instance with an error triggers an abort if
+// unhandled.
+// Test runs in debug mode only.
+#ifndef NDEBUG
+TEST(Error, UnhandledExpectedInFailureMode) {
+ EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); },
+ "Expected<T> must be checked before access or destruction.")
+ << "Unchecked Expected<T> failure value did not cause an abort()";
+}
+#endif
+
+// Test covariance of Expected.
+TEST(Error, ExpectedCovariance) {
+ class B {};
+ class D : public B {};
+
+ Expected<B *> A1(Expected<D *>(nullptr));
+ // Check A1 by converting to bool before assigning to it.
+ (void)!!A1;
+ A1 = Expected<D *>(nullptr);
+ // Check A1 again before destruction.
+ (void)!!A1;
+
+ Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr));
+ // Check A2 by converting to bool before assigning to it.
+ (void)!!A2;
+ A2 = Expected<std::unique_ptr<D>>(nullptr);
+ // Check A2 again before destruction.
+ (void)!!A2;
+}
+
+TEST(Error, ErrorCodeConversions) {
+ // Round-trip a success value to check that it converts correctly.
+ EXPECT_EQ(errorToErrorCode(errorCodeToError(std::error_code())),
+ std::error_code())
+ << "std::error_code() should round-trip via Error conversions";
+
+ // Round-trip an error value to check that it converts correctly.
+ EXPECT_EQ(errorToErrorCode(errorCodeToError(errc::invalid_argument)),
+ errc::invalid_argument)
+ << "std::error_code error value should round-trip via Error "
+ "conversions";
+
+ // Round-trip a success value through ErrorOr/Expected to check that it
+ // converts correctly.
+ {
+ auto Orig = ErrorOr<int>(42);
+ auto RoundTripped =
+ expectedToErrorOr(errorOrToExpected(ErrorOr<int>(42)));
+ EXPECT_EQ(*Orig, *RoundTripped)
+ << "ErrorOr<T> success value should round-trip via Expected<T> "
+ "conversions.";
+ }
+
+ // Round-trip a failure value through ErrorOr/Expected to check that it
+ // converts correctly.
+ {
+ auto Orig = ErrorOr<int>(errc::invalid_argument);
+ auto RoundTripped =
+ expectedToErrorOr(
+ errorOrToExpected(ErrorOr<int>(errc::invalid_argument)));
+ EXPECT_EQ(Orig.getError(), RoundTripped.getError())
+ << "ErrorOr<T> failure value should round-trip via Expected<T> "
+ "conversions.";
+ }
+}
+
+// Test that error messages work.
+TEST(Error, ErrorMessage) {
+ EXPECT_EQ(toString(Error::success()).compare(""), 0);
+
+ Error E1 = make_error<CustomError>(0);
+ EXPECT_EQ(toString(std::move(E1)).compare("CustomError { 0}"), 0);
+
+ Error E2 = make_error<CustomError>(0);
+ handleAllErrors(std::move(E2), [](const CustomError &CE) {
+ EXPECT_EQ(CE.message().compare("CustomError { 0}"), 0);
+ });
+
+ Error E3 = joinErrors(make_error<CustomError>(0), make_error<CustomError>(1));
+ EXPECT_EQ(toString(std::move(E3))
+ .compare("CustomError { 0}\n"
+ "CustomError { 1}"),
+ 0);
+}
+
+} // end anon namespace
diff --git a/unittests/Support/Makefile b/unittests/Support/Makefile
deleted file mode 100644
index 21657f12e3dc..000000000000
--- a/unittests/Support/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Support/Makefile --------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-TESTNAME = Support
-LINK_COMPONENTS := all-targets core support
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/Support/MathExtrasTest.cpp b/unittests/Support/MathExtrasTest.cpp
index 97309f8d31f5..d373030881ec 100644
--- a/unittests/Support/MathExtrasTest.cpp
+++ b/unittests/Support/MathExtrasTest.cpp
@@ -106,6 +106,40 @@ TEST(MathExtras, findLastSet) {
EXPECT_EQ(5u, findLastSet(NZ64));
}
+TEST(MathExtras, isIntN) {
+ EXPECT_TRUE(isIntN(16, 32767));
+ EXPECT_FALSE(isIntN(16, 32768));
+}
+
+TEST(MathExtras, isUIntN) {
+ EXPECT_TRUE(isUIntN(16, 65535));
+ EXPECT_FALSE(isUIntN(16, 65536));
+ EXPECT_TRUE(isUIntN(1, 0));
+ EXPECT_TRUE(isUIntN(6, 63));
+}
+
+TEST(MathExtras, maxIntN) {
+ EXPECT_EQ(32767, maxIntN(16));
+ EXPECT_EQ(2147483647, maxIntN(32));
+ EXPECT_EQ(std::numeric_limits<int32_t>::max(), maxIntN(32));
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(), maxIntN(64));
+}
+
+TEST(MathExtras, minIntN) {
+ EXPECT_EQ(-32768LL, minIntN(16));
+ EXPECT_EQ(-64LL, minIntN(7));
+ EXPECT_EQ(std::numeric_limits<int32_t>::min(), minIntN(32));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(), minIntN(64));
+}
+
+TEST(MathExtras, maxUIntN) {
+ EXPECT_EQ(0xffffULL, maxUIntN(16));
+ EXPECT_EQ(0xffffffffULL, maxUIntN(32));
+ EXPECT_EQ(0xffffffffffffffffULL, maxUIntN(64));
+ EXPECT_EQ(1ULL, maxUIntN(1));
+ EXPECT_EQ(0x0fULL, maxUIntN(4));
+}
+
TEST(MathExtras, reverseBits) {
uint8_t NZ8 = 42;
uint16_t NZ16 = 42;
@@ -163,7 +197,7 @@ TEST(MathExtras, FloatBits) {
TEST(MathExtras, DoubleBits) {
static const double kValue = 87987234.983498;
- EXPECT_FLOAT_EQ(kValue, BitsToDouble(DoubleToBits(kValue)));
+ EXPECT_DOUBLE_EQ(kValue, BitsToDouble(DoubleToBits(kValue)));
}
TEST(MathExtras, MinAlign) {
@@ -179,15 +213,15 @@ TEST(MathExtras, NextPowerOf2) {
EXPECT_EQ(256u, NextPowerOf2(128));
}
-TEST(MathExtras, RoundUpToAlignment) {
- EXPECT_EQ(8u, RoundUpToAlignment(5, 8));
- EXPECT_EQ(24u, RoundUpToAlignment(17, 8));
- EXPECT_EQ(0u, RoundUpToAlignment(~0LL, 8));
+TEST(MathExtras, alignTo) {
+ EXPECT_EQ(8u, alignTo(5, 8));
+ EXPECT_EQ(24u, alignTo(17, 8));
+ EXPECT_EQ(0u, alignTo(~0LL, 8));
- EXPECT_EQ(7u, RoundUpToAlignment(5, 8, 7));
- EXPECT_EQ(17u, RoundUpToAlignment(17, 8, 1));
- EXPECT_EQ(3u, RoundUpToAlignment(~0LL, 8, 3));
- EXPECT_EQ(552u, RoundUpToAlignment(321, 255, 42));
+ EXPECT_EQ(7u, alignTo(5, 8, 7));
+ EXPECT_EQ(17u, alignTo(17, 8, 1));
+ EXPECT_EQ(3u, alignTo(~0LL, 8, 3));
+ EXPECT_EQ(552u, alignTo(321, 255, 42));
}
template<typename T>
@@ -358,4 +392,42 @@ TEST(MathExtras, SaturatingMultiplyAdd) {
SaturatingMultiplyAddTestHelper<uint64_t>();
}
+TEST(MathExtras, IsShiftedUInt) {
+ EXPECT_TRUE((isShiftedUInt<1, 0>(0)));
+ EXPECT_TRUE((isShiftedUInt<1, 0>(1)));
+ EXPECT_FALSE((isShiftedUInt<1, 0>(2)));
+ EXPECT_FALSE((isShiftedUInt<1, 0>(3)));
+ EXPECT_FALSE((isShiftedUInt<1, 0>(0x8000000000000000)));
+ EXPECT_TRUE((isShiftedUInt<1, 63>(0x8000000000000000)));
+ EXPECT_TRUE((isShiftedUInt<2, 62>(0xC000000000000000)));
+ EXPECT_FALSE((isShiftedUInt<2, 62>(0xE000000000000000)));
+
+ // 0x201 is ten bits long and has a 1 in the MSB and LSB.
+ EXPECT_TRUE((isShiftedUInt<10, 5>(uint64_t(0x201) << 5)));
+ EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 4)));
+ EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 6)));
}
+
+TEST(MathExtras, IsShiftedInt) {
+ EXPECT_TRUE((isShiftedInt<1, 0>(0)));
+ EXPECT_TRUE((isShiftedInt<1, 0>(-1)));
+ EXPECT_FALSE((isShiftedInt<1, 0>(2)));
+ EXPECT_FALSE((isShiftedInt<1, 0>(3)));
+ EXPECT_FALSE((isShiftedInt<1, 0>(0x8000000000000000)));
+ EXPECT_TRUE((isShiftedInt<1, 63>(0x8000000000000000)));
+ EXPECT_TRUE((isShiftedInt<2, 62>(0xC000000000000000)));
+ EXPECT_FALSE((isShiftedInt<2, 62>(0xE000000000000000)));
+
+ // 0x201 is ten bits long and has a 1 in the MSB and LSB.
+ EXPECT_TRUE((isShiftedInt<11, 5>(int64_t(0x201) << 5)));
+ EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 3)));
+ EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 6)));
+ EXPECT_TRUE((isShiftedInt<11, 5>(-(int64_t(0x201) << 5))));
+ EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 3))));
+ EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 6))));
+
+ EXPECT_TRUE((isShiftedInt<6, 10>(-(int64_t(1) << 15))));
+ EXPECT_FALSE((isShiftedInt<6, 10>(int64_t(1) << 15)));
+}
+
+} // namespace
diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp
index 3f626f87888a..1a6ffa50e983 100644
--- a/unittests/Support/Path.cpp
+++ b/unittests/Support/Path.cpp
@@ -17,6 +17,7 @@
#include "gtest/gtest.h"
#ifdef LLVM_ON_WIN32
+#include "llvm/ADT/ArrayRef.h"
#include <windows.h>
#include <winerror.h>
#endif
@@ -416,7 +417,7 @@ TEST(SupportDeathTest, TempDirectoryOnWindows) {
SmallString<270> Expected{"C:\\Temp\\AB\\123456789"};
while (Expected.size() < 260)
Expected.append("\\DirNameWith19Charss");
- ASSERT_EQ(260, Expected.size());
+ ASSERT_EQ(260U, Expected.size());
EXPECT_TEMP_DIR(_putenv_s("TMP", Expected.c_str()), Expected.c_str());
}
#endif
@@ -715,6 +716,20 @@ TEST_F(FileSystemTest, DirectoryIteration) {
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0/za1"));
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0"));
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive"));
+
+ // Test recursive_directory_iterator level()
+ ASSERT_NO_ERROR(
+ fs::create_directories(Twine(TestDirectory) + "/reclevel/a/b/c"));
+ fs::recursive_directory_iterator I(Twine(TestDirectory) + "/reclevel", ec), E;
+ for (int l = 0; I != E; I.increment(ec), ++l) {
+ ASSERT_NO_ERROR(ec);
+ EXPECT_EQ(I.level(), l);
+ }
+ EXPECT_EQ(I, E);
+ ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b/c"));
+ ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b"));
+ ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a"));
+ ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel"));
}
const char archive[] = "!<arch>\x0A";
@@ -725,22 +740,30 @@ const char coff_bigobj[] = "\x00\x00\xff\xff\x00\x02......"
const char coff_import_library[] = "\x00\x00\xff\xff....";
const char elf_relocatable[] = { 0x7f, 'E', 'L', 'F', 1, 2, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1 };
-const char macho_universal_binary[] = "\xca\xfe\xba\xbe...\0x00";
-const char macho_object[] = "\xfe\xed\xfa\xce..........\x00\x01";
-const char macho_executable[] = "\xfe\xed\xfa\xce..........\x00\x02";
+const char macho_universal_binary[] = "\xca\xfe\xba\xbe...\x00";
+const char macho_object[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x01............";
+const char macho_executable[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x02............";
const char macho_fixed_virtual_memory_shared_lib[] =
- "\xfe\xed\xfa\xce..........\x00\x03";
-const char macho_core[] = "\xfe\xed\xfa\xce..........\x00\x04";
-const char macho_preload_executable[] = "\xfe\xed\xfa\xce..........\x00\x05";
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x03............";
+const char macho_core[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x04............";
+const char macho_preload_executable[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x05............";
const char macho_dynamically_linked_shared_lib[] =
- "\xfe\xed\xfa\xce..........\x00\x06";
-const char macho_dynamic_linker[] = "\xfe\xed\xfa\xce..........\x00\x07";
-const char macho_bundle[] = "\xfe\xed\xfa\xce..........\x00\x08";
-const char macho_dsym_companion[] = "\xfe\xed\xfa\xce..........\x00\x0a";
-const char macho_kext_bundle[] = "\xfe\xed\xfa\xce..........\x00\x0b";
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x06............";
+const char macho_dynamic_linker[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x07............";
+const char macho_bundle[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x08............";
+const char macho_dsym_companion[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x0a............";
+const char macho_kext_bundle[] =
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x0b............";
const char windows_resource[] = "\x00\x00\x00\x00\x020\x00\x00\x00\xff";
const char macho_dynamically_linked_shared_lib_stub[] =
- "\xfe\xed\xfa\xce..........\x00\x09";
+ "\xfe\xed\xfa\xce........\x00\x00\x00\x09............";
TEST_F(FileSystemTest, Magic) {
struct type {
@@ -947,4 +970,145 @@ TEST(Support, RemoveDots) {
EXPECT_EQ("c", Path1);
#endif
}
+
+TEST(Support, ReplacePathPrefix) {
+ SmallString<64> Path1("/foo");
+ SmallString<64> Path2("/old/foo");
+ SmallString<64> OldPrefix("/old");
+ SmallString<64> NewPrefix("/new");
+ SmallString<64> NewPrefix2("/longernew");
+ SmallString<64> EmptyPrefix("");
+
+ SmallString<64> Path = Path1;
+ path::replace_path_prefix(Path, OldPrefix, NewPrefix);
+ EXPECT_EQ(Path, "/foo");
+ Path = Path2;
+ path::replace_path_prefix(Path, OldPrefix, NewPrefix);
+ EXPECT_EQ(Path, "/new/foo");
+ Path = Path2;
+ path::replace_path_prefix(Path, OldPrefix, NewPrefix2);
+ EXPECT_EQ(Path, "/longernew/foo");
+ Path = Path1;
+ path::replace_path_prefix(Path, EmptyPrefix, NewPrefix);
+ EXPECT_EQ(Path, "/new/foo");
+ Path = Path2;
+ path::replace_path_prefix(Path, OldPrefix, EmptyPrefix);
+ EXPECT_EQ(Path, "/foo");
+}
+
+TEST_F(FileSystemTest, PathFromFD) {
+ // Create a temp file.
+ int FileDescriptor;
+ SmallString<64> TempPath;
+ ASSERT_NO_ERROR(
+ fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
+
+ // Make sure it exists.
+ ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
+
+ // Try to get the path from the file descriptor
+ SmallString<64> ResultPath;
+ std::error_code ErrorCode =
+ fs::getPathFromOpenFD(FileDescriptor, ResultPath);
+
+ // If we succeeded, check that the paths are the same (modulo case):
+ if (!ErrorCode) {
+ // The paths returned by createTemporaryFile and getPathFromOpenFD
+ // should reference the same file on disk.
+ fs::UniqueID D1, D2;
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
+ ASSERT_EQ(D1, D2);
+ }
+
+ ::close(FileDescriptor);
+}
+
+TEST_F(FileSystemTest, PathFromFDWin32) {
+ // Create a temp file.
+ int FileDescriptor;
+ SmallString<64> TempPath;
+ ASSERT_NO_ERROR(
+ fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
+
+ // Make sure it exists.
+ ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
+
+ SmallVector<char, 8> ResultPath;
+ std::error_code ErrorCode =
+ fs::getPathFromOpenFD(FileDescriptor, ResultPath);
+
+ if (!ErrorCode) {
+ // Now that we know how much space is required for the path, create a path
+ // buffer with exactly enough space (sans null terminator, which should not
+ // be present), and call getPathFromOpenFD again to ensure that the API
+ // properly handles exactly-sized buffers.
+ SmallVector<char, 8> ExactSizedPath(ResultPath.size());
+ ErrorCode = fs::getPathFromOpenFD(FileDescriptor, ExactSizedPath);
+ ResultPath = ExactSizedPath;
+ }
+
+ if (!ErrorCode) {
+ fs::UniqueID D1, D2;
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
+ ASSERT_EQ(D1, D2);
+ }
+ ::close(FileDescriptor);
+}
+
+TEST_F(FileSystemTest, PathFromFDUnicode) {
+ // Create a temp file.
+ int FileDescriptor;
+ SmallString<64> TempPath;
+
+ // Test Unicode: "<temp directory>/(pi)r^2<temp rand chars>.aleth.0"
+ ASSERT_NO_ERROR(
+ fs::createTemporaryFile("\xCF\x80r\xC2\xB2",
+ "\xE2\x84\xB5.0", FileDescriptor, TempPath));
+
+ // Make sure it exists.
+ ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
+
+ SmallVector<char, 8> ResultPath;
+ std::error_code ErrorCode =
+ fs::getPathFromOpenFD(FileDescriptor, ResultPath);
+
+ if (!ErrorCode) {
+ fs::UniqueID D1, D2;
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
+ ASSERT_EQ(D1, D2);
+ }
+ ::close(FileDescriptor);
+}
+
+TEST_F(FileSystemTest, OpenFileForRead) {
+ // Create a temp file.
+ int FileDescriptor;
+ SmallString<64> TempPath;
+ ASSERT_NO_ERROR(
+ fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
+
+ // Make sure it exists.
+ ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
+
+ // Open the file for read
+ int FileDescriptor2;
+ SmallString<64> ResultPath;
+ ASSERT_NO_ERROR(
+ fs::openFileForRead(Twine(TempPath), FileDescriptor2, &ResultPath))
+
+ // If we succeeded, check that the paths are the same (modulo case):
+ if (!ResultPath.empty()) {
+ // The paths returned by createTemporaryFile and getPathFromOpenFD
+ // should reference the same file on disk.
+ fs::UniqueID D1, D2;
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
+ ASSERT_EQ(D1, D2);
+ }
+
+ ::close(FileDescriptor);
+}
} // anonymous namespace
diff --git a/unittests/Support/ProgramTest.cpp b/unittests/Support/ProgramTest.cpp
index 47a3dbb5fb1f..886ead8305bc 100644
--- a/unittests/Support/ProgramTest.cpp
+++ b/unittests/Support/ProgramTest.cpp
@@ -223,7 +223,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) {
ProcessInfo PI1 = ExecuteNoWait(Executable, argv, getEnviron(), nullptr, 0,
&Error, &ExecutionFailed);
ASSERT_FALSE(ExecutionFailed) << Error;
- ASSERT_NE(PI1.Pid, 0) << "Invalid process id";
+ ASSERT_NE(PI1.Pid, ProcessInfo::InvalidPid) << "Invalid process id";
unsigned LoopCount = 0;
@@ -231,7 +231,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) {
// LoopCount should only be incremented once.
while (true) {
++LoopCount;
- ProcessInfo WaitResult = Wait(PI1, 0, true, &Error);
+ ProcessInfo WaitResult = llvm::sys::Wait(PI1, 0, true, &Error);
ASSERT_TRUE(Error.empty());
if (WaitResult.Pid == PI1.Pid)
break;
@@ -242,13 +242,13 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) {
ProcessInfo PI2 = ExecuteNoWait(Executable, argv, getEnviron(), nullptr, 0,
&Error, &ExecutionFailed);
ASSERT_FALSE(ExecutionFailed) << Error;
- ASSERT_NE(PI2.Pid, 0) << "Invalid process id";
+ ASSERT_NE(PI2.Pid, ProcessInfo::InvalidPid) << "Invalid process id";
// Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this
// cse, LoopCount should be greater than 1 (more than one increment occurs).
while (true) {
++LoopCount;
- ProcessInfo WaitResult = Wait(PI2, 0, false, &Error);
+ ProcessInfo WaitResult = llvm::sys::Wait(PI2, 0, false, &Error);
ASSERT_TRUE(Error.empty());
if (WaitResult.Pid == PI2.Pid)
break;
@@ -304,7 +304,7 @@ TEST(ProgramTest, TestExecuteNegative) {
bool ExecutionFailed;
ProcessInfo PI = ExecuteNoWait(Executable, argv, nullptr, nullptr, 0,
&Error, &ExecutionFailed);
- ASSERT_EQ(PI.Pid, 0)
+ ASSERT_EQ(PI.Pid, ProcessInfo::InvalidPid)
<< "On error ExecuteNoWait should return an invalid ProcessInfo";
ASSERT_TRUE(ExecutionFailed);
ASSERT_FALSE(Error.empty());
diff --git a/unittests/Support/StreamingMemoryObject.cpp b/unittests/Support/StreamingMemoryObject.cpp
deleted file mode 100644
index e86aa9cae51e..000000000000
--- a/unittests/Support/StreamingMemoryObject.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===- llvm/unittest/Support/StreamingMemoryObject.cpp - unit tests -------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/Support/StreamingMemoryObject.h"
-#include "gtest/gtest.h"
-#include <string.h>
-
-using namespace llvm;
-
-namespace {
-class NullDataStreamer : public DataStreamer {
- size_t GetBytes(unsigned char *buf, size_t len) override {
- memset(buf, 0, len);
- return len;
- }
-};
-}
-
-TEST(StreamingMemoryObject, Test) {
- auto DS = make_unique<NullDataStreamer>();
- StreamingMemoryObject O(std::move(DS));
- EXPECT_TRUE(O.isValidAddress(32 * 1024));
-}
-
-TEST(StreamingMemoryObject, TestSetKnownObjectSize) {
- auto DS = make_unique<NullDataStreamer>();
- StreamingMemoryObject O(std::move(DS));
- uint8_t Buf[32];
- EXPECT_EQ((uint64_t) 16, O.readBytes(Buf, 16, 0));
- O.setKnownObjectSize(24);
- EXPECT_EQ((uint64_t) 8, O.readBytes(Buf, 16, 16));
-}
diff --git a/unittests/Support/StreamingMemoryObjectTest.cpp b/unittests/Support/StreamingMemoryObjectTest.cpp
new file mode 100644
index 000000000000..836dfa9084f5
--- /dev/null
+++ b/unittests/Support/StreamingMemoryObjectTest.cpp
@@ -0,0 +1,68 @@
+//===- unittests/Support/StreamingMemoryObjectTest.cpp --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/StreamingMemoryObject.h"
+#include "gtest/gtest.h"
+#include <string.h>
+
+using namespace llvm;
+
+namespace {
+
+class NullDataStreamer : public DataStreamer {
+ size_t GetBytes(unsigned char *Buffer, size_t Length) override {
+ memset(Buffer, 0, Length);
+ return Length;
+ }
+};
+
+class BufferStreamer : public DataStreamer {
+ StringRef Buffer;
+
+public:
+ BufferStreamer(StringRef Buffer) : Buffer(Buffer) {}
+ size_t GetBytes(unsigned char *OutBuffer, size_t Length) override {
+ if (Length >= Buffer.size())
+ Length = Buffer.size();
+
+ std::copy(Buffer.begin(), Buffer.begin() + Length, OutBuffer);
+ Buffer = Buffer.drop_front(Length);
+ return Length;
+ }
+};
+
+TEST(StreamingMemoryObjectTest, isValidAddress) {
+ auto DS = make_unique<NullDataStreamer>();
+ StreamingMemoryObject O(std::move(DS));
+ EXPECT_TRUE(O.isValidAddress(32 * 1024));
+}
+
+TEST(StreamingMemoryObjectTest, setKnownObjectSize) {
+ auto DS = make_unique<NullDataStreamer>();
+ StreamingMemoryObject O(std::move(DS));
+ uint8_t Buf[32];
+ EXPECT_EQ(16u, O.readBytes(Buf, 16, 0));
+ O.setKnownObjectSize(24);
+ EXPECT_EQ(8u, O.readBytes(Buf, 16, 16));
+}
+
+TEST(StreamingMemoryObjectTest, getPointer) {
+ uint8_t InputBuffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+ StreamingMemoryObject O(make_unique<BufferStreamer>(StringRef(
+ reinterpret_cast<const char *>(InputBuffer), sizeof(InputBuffer))));
+
+ EXPECT_TRUE(std::equal(InputBuffer + 1, InputBuffer + 2, O.getPointer(1, 2)));
+ EXPECT_TRUE(std::equal(InputBuffer + 3, InputBuffer + 7, O.getPointer(3, 4)));
+ EXPECT_TRUE(std::equal(InputBuffer + 4, InputBuffer + 8, O.getPointer(4, 5)));
+ EXPECT_TRUE(std::equal(InputBuffer, InputBuffer + 8, O.getPointer(0, 20)));
+}
+
+} // end namespace
diff --git a/unittests/Support/TargetParserTest.cpp b/unittests/Support/TargetParserTest.cpp
new file mode 100644
index 000000000000..21994f27b73c
--- /dev/null
+++ b/unittests/Support/TargetParserTest.cpp
@@ -0,0 +1,92 @@
+//===----------- TargetParser.cpp - Target Parser -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+#include "llvm/Support/TargetParser.h"
+
+using namespace llvm;
+
+namespace {
+static const unsigned kAArch64ArchKinds[] = {
+#define AARCH64_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, \
+ ARCH_BASE_EXT) \
+ llvm::ARM::ID,
+#include "llvm/Support/AArch64TargetParser.def"
+#undef AARCH64_ARCH
+};
+
+template <typename T, size_t N>
+bool contains(const T (&array)[N], const T element) {
+ return std::find(std::begin(array), std::end(array), element) !=
+ std::end(array);
+}
+
+TEST(TargetParserTest, ARMArchName) {
+ for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0);
+ AK <= ARM::ArchKind::AK_LAST;
+ AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1))
+ EXPECT_TRUE(AK == ARM::AK_LAST ? ARM::getArchName(AK).empty()
+ : !ARM::getArchName(AK).empty());
+}
+
+TEST(TargetParserTest, ARMCPUAttr) {
+ for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0);
+ AK <= ARM::ArchKind::AK_LAST;
+ AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1))
+ EXPECT_TRUE((AK == ARM::AK_INVALID || AK == ARM::AK_LAST)
+ ? ARM::getCPUAttr(AK).empty()
+ : !ARM::getCPUAttr(AK).empty());
+}
+
+TEST(TargetParserTest, ARMSubArch) {
+ for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0);
+ AK <= ARM::ArchKind::AK_LAST;
+ AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1))
+ EXPECT_TRUE((AK == ARM::AK_INVALID || AK == ARM::AK_IWMMXT ||
+ AK == ARM::AK_IWMMXT2 || AK == ARM::AK_LAST)
+ ? ARM::getSubArch(AK).empty()
+ : !ARM::getSubArch(AK).empty());
+}
+
+TEST(TargetParserTest, ARMFPUName) {
+ for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0);
+ FK <= ARM::FPUKind::FK_LAST;
+ FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1))
+ EXPECT_TRUE(FK == ARM::FK_LAST ? ARM::getFPUName(FK).empty()
+ : !ARM::getFPUName(FK).empty());
+}
+
+TEST(TargetParserTest, AArch64ArchName) {
+ for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0);
+ AK <= ARM::ArchKind::AK_LAST;
+ AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1))
+ EXPECT_TRUE(contains(kAArch64ArchKinds, static_cast<unsigned>(AK))
+ ? !AArch64::getArchName(AK).empty()
+ : AArch64::getArchName(AK).empty());
+}
+
+TEST(TargetParserTest, AArch64CPUAttr) {
+ for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0);
+ AK <= ARM::ArchKind::AK_LAST;
+ AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1))
+ EXPECT_TRUE(contains(kAArch64ArchKinds, static_cast<unsigned>(AK))
+ ? !AArch64::getCPUAttr(AK).empty()
+ : AArch64::getCPUAttr(AK).empty());
+}
+
+TEST(TargetParserTest, AArch64SubArch) {
+ for (ARM::ArchKind AK = static_cast<ARM::ArchKind>(0);
+ AK <= ARM::ArchKind::AK_LAST;
+ AK = static_cast<ARM::ArchKind>(static_cast<unsigned>(AK) + 1))
+ EXPECT_TRUE(contains(kAArch64ArchKinds, static_cast<unsigned>(AK))
+ ? !AArch64::getSubArch(AK).empty()
+ : AArch64::getSubArch(AK).empty());
+}
+}
+
diff --git a/unittests/Support/ThreadPool.cpp b/unittests/Support/ThreadPool.cpp
index 0f36c383d494..69a24bc5444c 100644
--- a/unittests/Support/ThreadPool.cpp
+++ b/unittests/Support/ThreadPool.cpp
@@ -135,7 +135,7 @@ TEST_F(ThreadPoolTest, Async) {
TEST_F(ThreadPoolTest, GetFuture) {
CHECK_UNSUPPORTED();
- ThreadPool Pool;
+ ThreadPool Pool{2};
std::atomic_int i{0};
Pool.async([this, &i] {
waitForMainThread();
diff --git a/unittests/Support/TrailingObjectsTest.cpp b/unittests/Support/TrailingObjectsTest.cpp
index 866ff1e6e88d..a1d3e7b3c864 100644
--- a/unittests/Support/TrailingObjectsTest.cpp
+++ b/unittests/Support/TrailingObjectsTest.cpp
@@ -34,6 +34,7 @@ public:
void *Mem = ::operator new(totalSizeToAlloc<short>(NumShorts));
return new (Mem) Class1(ShortArray, NumShorts);
}
+ void operator delete(void *p) { ::operator delete(p); }
short get(unsigned Num) const { return getTrailingObjects<short>()[Num]; }
@@ -78,6 +79,7 @@ public:
*C->getTrailingObjects<double>() = D;
return C;
}
+ void operator delete(void *p) { ::operator delete(p); }
short getShort() const {
if (!HasShort)
@@ -118,8 +120,8 @@ TEST(TrailingObjects, TwoArg) {
Class2 *C1 = Class2::create(4);
Class2 *C2 = Class2::create(0, 4.2);
- EXPECT_EQ(sizeof(Class2), llvm::RoundUpToAlignment(sizeof(bool) * 2,
- llvm::alignOf<double>()));
+ EXPECT_EQ(sizeof(Class2),
+ llvm::alignTo(sizeof(bool) * 2, llvm::alignOf<double>()));
EXPECT_EQ(llvm::alignOf<Class2>(), llvm::alignOf<double>());
EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(1, 0)),
@@ -162,8 +164,7 @@ class Class3 final : public TrailingObjects<Class3, double, short, bool> {
TEST(TrailingObjects, ThreeArg) {
EXPECT_EQ((Class3::additionalSizeToAlloc<double, short, bool>(1, 1, 3)),
sizeof(double) + sizeof(short) + 3 * sizeof(bool));
- EXPECT_EQ(sizeof(Class3),
- llvm::RoundUpToAlignment(1, llvm::alignOf<double>()));
+ EXPECT_EQ(sizeof(Class3), llvm::alignTo(1, llvm::alignOf<double>()));
std::unique_ptr<char[]> P(new char[1000]);
Class3 *C = reinterpret_cast<Class3 *>(P.get());
EXPECT_EQ(C->getTrailingObjects<double>(), reinterpret_cast<double *>(C + 1));
@@ -183,8 +184,8 @@ class Class4 final : public TrailingObjects<Class4, char, long> {
TEST(TrailingObjects, Realignment) {
EXPECT_EQ((Class4::additionalSizeToAlloc<char, long>(1, 1)),
- llvm::RoundUpToAlignment(sizeof(long) + 1, llvm::alignOf<long>()));
- EXPECT_EQ(sizeof(Class4), llvm::RoundUpToAlignment(1, llvm::alignOf<long>()));
+ llvm::alignTo(sizeof(long) + 1, llvm::alignOf<long>()));
+ EXPECT_EQ(sizeof(Class4), llvm::alignTo(1, llvm::alignOf<long>()));
std::unique_ptr<char[]> P(new char[1000]);
Class4 *C = reinterpret_cast<Class4 *>(P.get());
EXPECT_EQ(C->getTrailingObjects<char>(), reinterpret_cast<char *>(C + 1));
diff --git a/unittests/Support/TypeNameTest.cpp b/unittests/Support/TypeNameTest.cpp
new file mode 100644
index 000000000000..63381d46f02f
--- /dev/null
+++ b/unittests/Support/TypeNameTest.cpp
@@ -0,0 +1,49 @@
+//===- TypeNameTest.cpp ---------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/TypeName.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+namespace N1 {
+struct S1 {};
+class C1 {};
+union U1 {};
+}
+
+TEST(TypeNameTest, Names) {
+ struct S2 {};
+
+ StringRef S1Name = getTypeName<N1::S1>();
+ StringRef C1Name = getTypeName<N1::C1>();
+ StringRef U1Name = getTypeName<N1::U1>();
+ StringRef S2Name = getTypeName<S2>();
+
+#if defined(__clang__) || defined(__GNUC__) || defined(__INTEL_COMPILER) || \
+ defined(_MSC_VER)
+ EXPECT_TRUE(S1Name.endswith("::N1::S1")) << S1Name.str();
+ EXPECT_TRUE(C1Name.endswith("::N1::C1")) << C1Name.str();
+ EXPECT_TRUE(U1Name.endswith("::N1::U1")) << U1Name.str();
+#ifdef __clang__
+ EXPECT_TRUE(S2Name.endswith("S2")) << S2Name.str();
+#else
+ EXPECT_TRUE(S2Name.endswith("::S2")) << S2Name.str();
+#endif
+#else
+ EXPECT_EQ("UNKNOWN_TYPE", S1Name);
+ EXPECT_EQ("UNKNOWN_TYPE", C1Name);
+ EXPECT_EQ("UNKNOWN_TYPE", U1Name);
+ EXPECT_EQ("UNKNOWN_TYPE", S2Name);
+#endif
+}
+
+} // end anonymous namespace
diff --git a/unittests/Support/YAMLIOTest.cpp b/unittests/Support/YAMLIOTest.cpp
index e7affa1698dc..5f35a802caa5 100644
--- a/unittests/Support/YAMLIOTest.cpp
+++ b/unittests/Support/YAMLIOTest.cpp
@@ -7,9 +7,9 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/Endian.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/YAMLTraits.h"
#include "gtest/gtest.h"
@@ -389,6 +389,111 @@ TEST(YAMLIO, TestReadWriteBuiltInTypes) {
}
}
+//===----------------------------------------------------------------------===//
+// Test endian-aware types
+//===----------------------------------------------------------------------===//
+
+struct EndianTypes {
+ typedef llvm::support::detail::packed_endian_specific_integral<
+ float, llvm::support::little, llvm::support::unaligned>
+ ulittle_float;
+ typedef llvm::support::detail::packed_endian_specific_integral<
+ double, llvm::support::little, llvm::support::unaligned>
+ ulittle_double;
+
+ llvm::support::ulittle64_t u64;
+ llvm::support::ulittle32_t u32;
+ llvm::support::ulittle16_t u16;
+ llvm::support::little64_t s64;
+ llvm::support::little32_t s32;
+ llvm::support::little16_t s16;
+ ulittle_float f;
+ ulittle_double d;
+};
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<EndianTypes> {
+ static void mapping(IO &io, EndianTypes &et) {
+ io.mapRequired("u64", et.u64);
+ io.mapRequired("u32", et.u32);
+ io.mapRequired("u16", et.u16);
+ io.mapRequired("s64", et.s64);
+ io.mapRequired("s32", et.s32);
+ io.mapRequired("s16", et.s16);
+ io.mapRequired("f", et.f);
+ io.mapRequired("d", et.d);
+ }
+};
+}
+}
+
+//
+// Test the reading of all endian scalar conversions
+//
+TEST(YAMLIO, TestReadEndianTypes) {
+ EndianTypes map;
+ Input yin("---\n"
+ "u64: 5000000000\n"
+ "u32: 4000000000\n"
+ "u16: 65000\n"
+ "s64: -5000000000\n"
+ "s32: -2000000000\n"
+ "s16: -32000\n"
+ "f: 3.25\n"
+ "d: -2.8625\n"
+ "...\n");
+ yin >> map;
+
+ EXPECT_FALSE(yin.error());
+ EXPECT_EQ(map.u64, 5000000000ULL);
+ EXPECT_EQ(map.u32, 4000000000U);
+ EXPECT_EQ(map.u16, 65000);
+ EXPECT_EQ(map.s64, -5000000000LL);
+ EXPECT_EQ(map.s32, -2000000000L);
+ EXPECT_EQ(map.s16, -32000);
+ EXPECT_EQ(map.f, 3.25f);
+ EXPECT_EQ(map.d, -2.8625);
+}
+
+//
+// Test writing then reading back all endian-aware scalar types
+//
+TEST(YAMLIO, TestReadWriteEndianTypes) {
+ std::string intermediate;
+ {
+ EndianTypes map;
+ map.u64 = 6000000000ULL;
+ map.u32 = 3000000000U;
+ map.u16 = 50000;
+ map.s64 = -6000000000LL;
+ map.s32 = -2000000000;
+ map.s16 = -32000;
+ map.f = 3.25f;
+ map.d = -2.8625;
+
+ llvm::raw_string_ostream ostr(intermediate);
+ Output yout(ostr);
+ yout << map;
+ }
+
+ {
+ Input yin(intermediate);
+ EndianTypes map;
+ yin >> map;
+
+ EXPECT_FALSE(yin.error());
+ EXPECT_EQ(map.u64, 6000000000ULL);
+ EXPECT_EQ(map.u32, 3000000000U);
+ EXPECT_EQ(map.u16, 50000);
+ EXPECT_EQ(map.s64, -6000000000LL);
+ EXPECT_EQ(map.s32, -2000000000L);
+ EXPECT_EQ(map.s16, -32000);
+ EXPECT_EQ(map.f, 3.25f);
+ EXPECT_EQ(map.d, -2.8625);
+ }
+}
+
struct StringTypes {
llvm::StringRef str1;
llvm::StringRef str2;
diff --git a/unittests/Support/YAMLParserTest.cpp b/unittests/Support/YAMLParserTest.cpp
index 41ad649699ca..3f12a53fd9c5 100644
--- a/unittests/Support/YAMLParserTest.cpp
+++ b/unittests/Support/YAMLParserTest.cpp
@@ -7,7 +7,6 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/MemoryBuffer.h"
diff --git a/unittests/Support/raw_ostream_test.cpp b/unittests/Support/raw_ostream_test.cpp
index ff986025b2cc..ed6ddabe4634 100644
--- a/unittests/Support/raw_ostream_test.cpp
+++ b/unittests/Support/raw_ostream_test.cpp
@@ -70,8 +70,8 @@ TEST(raw_ostreamTest, Types_Buffered) {
// void*
EXPECT_EQ("0x0", printToString((void*) nullptr));
- EXPECT_EQ("0xbeef", printToString((void*) 0xbeef));
- EXPECT_EQ("0xdeadbeef", printToString((void*) 0xdeadbeef));
+ EXPECT_EQ("0xbeef", printToString((void*) 0xbeefLL));
+ EXPECT_EQ("0xdeadbeef", printToString((void*) 0xdeadbeefLL));
// Min and max.
EXPECT_EQ("18446744073709551615", printToString(UINT64_MAX));
@@ -101,8 +101,8 @@ TEST(raw_ostreamTest, Types_Unbuffered) {
// void*
EXPECT_EQ("0x0", printToStringUnbuffered((void*) nullptr));
- EXPECT_EQ("0xbeef", printToStringUnbuffered((void*) 0xbeef));
- EXPECT_EQ("0xdeadbeef", printToStringUnbuffered((void*) 0xdeadbeef));
+ EXPECT_EQ("0xbeef", printToStringUnbuffered((void*) 0xbeefLL));
+ EXPECT_EQ("0xdeadbeef", printToStringUnbuffered((void*) 0xdeadbeefLL));
// Min and max.
EXPECT_EQ("18446744073709551615", printToStringUnbuffered(UINT64_MAX));
diff --git a/unittests/Support/raw_sha1_ostream_test.cpp b/unittests/Support/raw_sha1_ostream_test.cpp
new file mode 100644
index 000000000000..db2a3e9ab643
--- /dev/null
+++ b/unittests/Support/raw_sha1_ostream_test.cpp
@@ -0,0 +1,71 @@
+//===- llvm/unittest/Support/raw_ostream_test.cpp - raw_ostream tests -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_sha1_ostream.h"
+
+#include <string>
+
+using namespace llvm;
+
+static std::string toHex(StringRef Input) {
+ static const char *const LUT = "0123456789ABCDEF";
+ size_t Length = Input.size();
+
+ std::string Output;
+ Output.reserve(2 * Length);
+ for (size_t i = 0; i < Length; ++i) {
+ const unsigned char c = Input[i];
+ Output.push_back(LUT[c >> 4]);
+ Output.push_back(LUT[c & 15]);
+ }
+ return Output;
+}
+
+TEST(raw_sha1_ostreamTest, Basic) {
+ llvm::raw_sha1_ostream Sha1Stream;
+ Sha1Stream << "Hello World!";
+ auto Hash = toHex(Sha1Stream.sha1());
+
+ ASSERT_EQ("2EF7BDE608CE5404E97D5F042F95F89F1C232871", Hash);
+}
+
+// Check that getting the intermediate hash in the middle of the stream does
+// not invalidate the final result.
+TEST(raw_sha1_ostreamTest, Intermediate) {
+ llvm::raw_sha1_ostream Sha1Stream;
+ Sha1Stream << "Hello";
+ auto Hash = toHex(Sha1Stream.sha1());
+
+ ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash);
+ Sha1Stream << " World!";
+ Hash = toHex(Sha1Stream.sha1());
+
+ // Compute the non-split hash separately as a reference.
+ llvm::raw_sha1_ostream NonSplitSha1Stream;
+ NonSplitSha1Stream << "Hello World!";
+ auto NonSplitHash = toHex(NonSplitSha1Stream.sha1());
+
+ ASSERT_EQ(NonSplitHash, Hash);
+}
+
+TEST(raw_sha1_ostreamTest, Reset) {
+ llvm::raw_sha1_ostream Sha1Stream;
+ Sha1Stream << "Hello";
+ auto Hash = toHex(Sha1Stream.sha1());
+
+ ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash);
+
+ Sha1Stream.resetHash();
+ Sha1Stream << " World!";
+ Hash = toHex(Sha1Stream.sha1());
+
+ ASSERT_EQ("7447F2A5A42185C8CF91E632789C431830B59067", Hash);
+}
diff --git a/unittests/Transforms/IPO/CMakeLists.txt b/unittests/Transforms/IPO/CMakeLists.txt
index 58b71b2bce03..ee33a5fcd1b3 100644
--- a/unittests/Transforms/IPO/CMakeLists.txt
+++ b/unittests/Transforms/IPO/CMakeLists.txt
@@ -5,5 +5,6 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_unittest(IPOTests
- LowerBitSets.cpp
+ LowerTypeTests.cpp
+ WholeProgramDevirt.cpp
)
diff --git a/unittests/Transforms/IPO/LowerBitSets.cpp b/unittests/Transforms/IPO/LowerTypeTests.cpp
index 49a42cd20d7a..66c9de6bd662 100644
--- a/unittests/Transforms/IPO/LowerBitSets.cpp
+++ b/unittests/Transforms/IPO/LowerTypeTests.cpp
@@ -1,4 +1,4 @@
-//===- LowerBitSets.cpp - Unit tests for bitset lowering ------------------===//
+//===- LowerTypeTests.cpp - Unit tests for type test lowering -------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,12 +7,13 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/Transforms/IPO/LowerBitSets.h"
+#include "llvm/Transforms/IPO/LowerTypeTests.h"
#include "gtest/gtest.h"
using namespace llvm;
+using namespace lowertypetests;
-TEST(LowerBitSets, BitSetBuilder) {
+TEST(LowerTypeTests, BitSetBuilder) {
struct {
std::vector<uint64_t> Offsets;
std::set<uint64_t> Bits;
@@ -79,7 +80,7 @@ TEST(LowerBitSets, BitSetBuilder) {
}
}
-TEST(LowerBitSets, GlobalLayoutBuilder) {
+TEST(LowerTypeTests, GlobalLayoutBuilder) {
struct {
uint64_t NumObjects;
std::vector<std::set<uint64_t>> Fragments;
@@ -106,7 +107,7 @@ TEST(LowerBitSets, GlobalLayoutBuilder) {
}
}
-TEST(LowerBitSets, ByteArrayBuilder) {
+TEST(LowerTypeTests, ByteArrayBuilder) {
struct BABAlloc {
std::set<uint64_t> Bits;
uint64_t BitSize;
diff --git a/unittests/Transforms/IPO/Makefile b/unittests/Transforms/IPO/Makefile
deleted file mode 100644
index f807879c2b5f..000000000000
--- a/unittests/Transforms/IPO/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Transforms/IPO/Makefile -------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-TESTNAME = IPO
-LINK_COMPONENTS := IPO
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/Transforms/IPO/WholeProgramDevirt.cpp b/unittests/Transforms/IPO/WholeProgramDevirt.cpp
new file mode 100644
index 000000000000..7e7a6bf45964
--- /dev/null
+++ b/unittests/Transforms/IPO/WholeProgramDevirt.cpp
@@ -0,0 +1,165 @@
+//===- WholeProgramDevirt.cpp - Unit tests for whole-program devirt -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/IPO/WholeProgramDevirt.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace wholeprogramdevirt;
+
+TEST(WholeProgramDevirt, findLowestOffset) {
+ VTableBits VT1;
+ VT1.ObjectSize = 8;
+ VT1.Before.BytesUsed = {1 << 0};
+ VT1.After.BytesUsed = {1 << 1};
+
+ VTableBits VT2;
+ VT2.ObjectSize = 8;
+ VT2.Before.BytesUsed = {1 << 1};
+ VT2.After.BytesUsed = {1 << 0};
+
+ TypeMemberInfo TM1{&VT1, 0};
+ TypeMemberInfo TM2{&VT2, 0};
+ VirtualCallTarget Targets[] = {
+ {&TM1, /*IsBigEndian=*/false},
+ {&TM2, /*IsBigEndian=*/false},
+ };
+
+ EXPECT_EQ(2ull, findLowestOffset(Targets, /*IsAfter=*/false, 1));
+ EXPECT_EQ(66ull, findLowestOffset(Targets, /*IsAfter=*/true, 1));
+
+ EXPECT_EQ(8ull, findLowestOffset(Targets, /*IsAfter=*/false, 8));
+ EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/true, 8));
+
+ TM1.Offset = 4;
+ EXPECT_EQ(33ull, findLowestOffset(Targets, /*IsAfter=*/false, 1));
+ EXPECT_EQ(65ull, findLowestOffset(Targets, /*IsAfter=*/true, 1));
+
+ EXPECT_EQ(40ull, findLowestOffset(Targets, /*IsAfter=*/false, 8));
+ EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/true, 8));
+
+ TM1.Offset = 8;
+ TM2.Offset = 8;
+ EXPECT_EQ(66ull, findLowestOffset(Targets, /*IsAfter=*/false, 1));
+ EXPECT_EQ(2ull, findLowestOffset(Targets, /*IsAfter=*/true, 1));
+
+ EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/false, 8));
+ EXPECT_EQ(8ull, findLowestOffset(Targets, /*IsAfter=*/true, 8));
+
+ VT1.After.BytesUsed = {0xff, 0, 0, 0, 0xff};
+ VT2.After.BytesUsed = {0xff, 1, 0, 0, 0};
+ EXPECT_EQ(16ull, findLowestOffset(Targets, /*IsAfter=*/true, 16));
+ EXPECT_EQ(40ull, findLowestOffset(Targets, /*IsAfter=*/true, 32));
+}
+
+TEST(WholeProgramDevirt, setReturnValues) {
+ VTableBits VT1;
+ VT1.ObjectSize = 8;
+
+ VTableBits VT2;
+ VT2.ObjectSize = 8;
+
+ TypeMemberInfo TM1{&VT1, 0};
+ TypeMemberInfo TM2{&VT2, 0};
+ VirtualCallTarget Targets[] = {
+ {&TM1, /*IsBigEndian=*/false},
+ {&TM2, /*IsBigEndian=*/false},
+ };
+
+ TM1.Offset = 4;
+ TM2.Offset = 4;
+
+ int64_t OffsetByte;
+ uint64_t OffsetBit;
+
+ Targets[0].RetVal = 1;
+ Targets[1].RetVal = 0;
+ setBeforeReturnValues(Targets, 32, 1, OffsetByte, OffsetBit);
+ EXPECT_EQ(-5ll, OffsetByte);
+ EXPECT_EQ(0ull, OffsetBit);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.BytesUsed);
+ EXPECT_EQ(std::vector<uint8_t>{0}, VT2.Before.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT2.Before.BytesUsed);
+
+ Targets[0].RetVal = 0;
+ Targets[1].RetVal = 1;
+ setBeforeReturnValues(Targets, 39, 1, OffsetByte, OffsetBit);
+ EXPECT_EQ(-5ll, OffsetByte);
+ EXPECT_EQ(7ull, OffsetBit);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{0x81}, VT1.Before.BytesUsed);
+ EXPECT_EQ(std::vector<uint8_t>{0x80}, VT2.Before.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{0x81}, VT2.Before.BytesUsed);
+
+ Targets[0].RetVal = 12;
+ Targets[1].RetVal = 34;
+ setBeforeReturnValues(Targets, 40, 8, OffsetByte, OffsetBit);
+ EXPECT_EQ(-6ll, OffsetByte);
+ EXPECT_EQ(0ull, OffsetBit);
+ EXPECT_EQ((std::vector<uint8_t>{1, 12}), VT1.Before.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT1.Before.BytesUsed);
+ EXPECT_EQ((std::vector<uint8_t>{0x80, 34}), VT2.Before.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT2.Before.BytesUsed);
+
+ Targets[0].RetVal = 56;
+ Targets[1].RetVal = 78;
+ setBeforeReturnValues(Targets, 48, 16, OffsetByte, OffsetBit);
+ EXPECT_EQ(-8ll, OffsetByte);
+ EXPECT_EQ(0ull, OffsetBit);
+ EXPECT_EQ((std::vector<uint8_t>{1, 12, 0, 56}), VT1.Before.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),
+ VT1.Before.BytesUsed);
+ EXPECT_EQ((std::vector<uint8_t>{0x80, 34, 0, 78}), VT2.Before.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),
+ VT2.Before.BytesUsed);
+
+ Targets[0].RetVal = 1;
+ Targets[1].RetVal = 0;
+ setAfterReturnValues(Targets, 32, 1, OffsetByte, OffsetBit);
+ EXPECT_EQ(4ll, OffsetByte);
+ EXPECT_EQ(0ull, OffsetBit);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.BytesUsed);
+ EXPECT_EQ(std::vector<uint8_t>{0}, VT2.After.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT2.After.BytesUsed);
+
+ Targets[0].RetVal = 0;
+ Targets[1].RetVal = 1;
+ setAfterReturnValues(Targets, 39, 1, OffsetByte, OffsetBit);
+ EXPECT_EQ(4ll, OffsetByte);
+ EXPECT_EQ(7ull, OffsetBit);
+ EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{0x81}, VT1.After.BytesUsed);
+ EXPECT_EQ(std::vector<uint8_t>{0x80}, VT2.After.Bytes);
+ EXPECT_EQ(std::vector<uint8_t>{0x81}, VT2.After.BytesUsed);
+
+ Targets[0].RetVal = 12;
+ Targets[1].RetVal = 34;
+ setAfterReturnValues(Targets, 40, 8, OffsetByte, OffsetBit);
+ EXPECT_EQ(5ll, OffsetByte);
+ EXPECT_EQ(0ull, OffsetBit);
+ EXPECT_EQ((std::vector<uint8_t>{1, 12}), VT1.After.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT1.After.BytesUsed);
+ EXPECT_EQ((std::vector<uint8_t>{0x80, 34}), VT2.After.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT2.After.BytesUsed);
+
+ Targets[0].RetVal = 56;
+ Targets[1].RetVal = 78;
+ setAfterReturnValues(Targets, 48, 16, OffsetByte, OffsetBit);
+ EXPECT_EQ(6ll, OffsetByte);
+ EXPECT_EQ(0ull, OffsetBit);
+ EXPECT_EQ((std::vector<uint8_t>{1, 12, 56, 0}), VT1.After.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),
+ VT1.After.BytesUsed);
+ EXPECT_EQ((std::vector<uint8_t>{0x80, 34, 78, 0}), VT2.After.Bytes);
+ EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),
+ VT2.After.BytesUsed);
+}
diff --git a/unittests/Transforms/Makefile b/unittests/Transforms/Makefile
deleted file mode 100644
index 3a2cdfc2c74a..000000000000
--- a/unittests/Transforms/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- unittests/Transforms/Makefile -----------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../..
-
-PARALLEL_DIRS = IPO Utils
-
-include $(LEVEL)/Makefile.common
-
-clean::
- $(Verb) $(RM) -f *Tests
diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt
index 517ff99ea46b..657d151048e8 100644
--- a/unittests/Transforms/Utils/CMakeLists.txt
+++ b/unittests/Transforms/Utils/CMakeLists.txt
@@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS
+ Analysis
Core
Support
TransformUtils
@@ -9,5 +10,6 @@ add_llvm_unittest(UtilsTests
Cloning.cpp
IntegerDivision.cpp
Local.cpp
+ MemorySSA.cpp
ValueMapperTest.cpp
)
diff --git a/unittests/Transforms/Utils/Cloning.cpp b/unittests/Transforms/Utils/Cloning.cpp
index 25e322ee5a8e..f53e0a95e94d 100644
--- a/unittests/Transforms/Utils/Cloning.cpp
+++ b/unittests/Transforms/Utils/Cloning.cpp
@@ -8,7 +8,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/Cloning.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/Argument.h"
@@ -275,8 +274,7 @@ protected:
void CreateNewFunc() {
ValueToValueMapTy VMap;
- NewFunc = CloneFunction(OldFunc, VMap, true, nullptr);
- M->getFunctionList().push_back(NewFunc);
+ NewFunc = CloneFunction(OldFunc, VMap, nullptr);
}
void SetupFinder() {
@@ -302,31 +300,13 @@ TEST_F(CloneFunc, Subprogram) {
EXPECT_FALSE(verifyModule(*M));
unsigned SubprogramCount = Finder->subprogram_count();
- EXPECT_EQ(2U, SubprogramCount);
+ EXPECT_EQ(1U, SubprogramCount);
auto Iter = Finder->subprograms().begin();
- auto *Sub1 = cast<DISubprogram>(*Iter);
- Iter++;
- auto *Sub2 = cast<DISubprogram>(*Iter);
+ auto *Sub = cast<DISubprogram>(*Iter);
- EXPECT_TRUE(
- (Sub1 == OldFunc->getSubprogram() && Sub2 == NewFunc->getSubprogram()) ||
- (Sub1 == NewFunc->getSubprogram() && Sub2 == OldFunc->getSubprogram()));
-}
-
-// Test that the new subprogram entry was not added to the CU which doesn't
-// contain the old subprogram entry.
-TEST_F(CloneFunc, SubprogramInRightCU) {
- EXPECT_FALSE(verifyModule(*M));
-
- EXPECT_EQ(2U, Finder->compile_unit_count());
-
- auto Iter = Finder->compile_units().begin();
- auto *CU1 = cast<DICompileUnit>(*Iter);
- Iter++;
- auto *CU2 = cast<DICompileUnit>(*Iter);
- EXPECT_TRUE(CU1->getSubprograms().size() == 0 ||
- CU2->getSubprograms().size() == 0);
+ EXPECT_TRUE(Sub == OldFunc->getSubprogram());
+ EXPECT_TRUE(Sub == NewFunc->getSubprogram());
}
// Test that instructions in the old function still belong to it in the
@@ -423,6 +403,7 @@ protected:
void SetupModule() { OldM = new Module("", C); }
void CreateOldModule() {
+ DIBuilder DBuilder(*OldM);
IRBuilder<> IBuilder(C);
auto *FuncType = FunctionType::get(Type::getVoidTy(C), false);
@@ -431,9 +412,25 @@ protected:
auto *F =
Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", OldM);
F->setPersonalityFn(PersFn);
+
+ // Create debug info
+ auto *File = DBuilder.createFile("filename.c", "/file/dir/");
+ DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None);
+ DISubroutineType *DFuncType = DBuilder.createSubroutineType(ParamTypes);
+ auto *CU =
+ DBuilder.createCompileUnit(dwarf::DW_LANG_C99, "filename.c",
+ "/file/dir", "CloneModule", false, "", 0);
+ // Function DI
+ auto *Subprogram = DBuilder.createFunction(CU, "f", "f", File, 4, DFuncType,
+ true, true, 3, 0, false);
+ F->setSubprogram(Subprogram);
+
auto *Entry = BasicBlock::Create(C, "", F);
IBuilder.SetInsertPoint(Entry);
IBuilder.CreateRetVoid();
+
+ // Finalize the debug info
+ DBuilder.finalize();
}
void CreateNewModule() { NewM = llvm::CloneModule(OldM).release(); }
@@ -447,4 +444,18 @@ TEST_F(CloneModule, Verify) {
EXPECT_FALSE(verifyModule(*NewM));
}
+TEST_F(CloneModule, OldModuleUnchanged) {
+ DebugInfoFinder Finder;
+ Finder.processModule(*OldM);
+ EXPECT_EQ(1U, Finder.subprogram_count());
+}
+
+TEST_F(CloneModule, Subprogram) {
+ Function *NewF = NewM->getFunction("f");
+ DISubprogram *SP = NewF->getSubprogram();
+ EXPECT_TRUE(SP != nullptr);
+ EXPECT_EQ(SP->getName(), "f");
+ EXPECT_EQ(SP->getFile()->getFilename(), "filename.c");
+ EXPECT_EQ(SP->getLine(), (unsigned)4);
+}
}
diff --git a/unittests/Transforms/Utils/IntegerDivision.cpp b/unittests/Transforms/Utils/IntegerDivision.cpp
index 4cda2b4e5892..b6b1b1665ab1 100644
--- a/unittests/Transforms/Utils/IntegerDivision.cpp
+++ b/unittests/Transforms/Utils/IntegerDivision.cpp
@@ -21,7 +21,7 @@ namespace {
TEST(IntegerDivision, SDiv) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -51,7 +51,7 @@ TEST(IntegerDivision, SDiv) {
}
TEST(IntegerDivision, UDiv) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -81,7 +81,7 @@ TEST(IntegerDivision, UDiv) {
}
TEST(IntegerDivision, SRem) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
@@ -111,7 +111,7 @@ TEST(IntegerDivision, SRem) {
}
TEST(IntegerDivision, URem) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
@@ -142,7 +142,7 @@ TEST(IntegerDivision, URem) {
TEST(IntegerDivision, SDiv64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -172,7 +172,7 @@ TEST(IntegerDivision, SDiv64) {
}
TEST(IntegerDivision, UDiv64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -202,7 +202,7 @@ TEST(IntegerDivision, UDiv64) {
}
TEST(IntegerDivision, SRem64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
@@ -232,7 +232,7 @@ TEST(IntegerDivision, SRem64) {
}
TEST(IntegerDivision, URem64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
diff --git a/unittests/Transforms/Utils/Local.cpp b/unittests/Transforms/Utils/Local.cpp
index 2ff560475551..5164bdbb2a4e 100644
--- a/unittests/Transforms/Utils/Local.cpp
+++ b/unittests/Transforms/Utils/Local.cpp
@@ -17,7 +17,7 @@
using namespace llvm;
TEST(Local, RecursivelyDeleteDeadPHINodes) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
IRBuilder<> builder(C);
@@ -60,7 +60,7 @@ TEST(Local, RecursivelyDeleteDeadPHINodes) {
}
TEST(Local, RemoveDuplicatePHINodes) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
IRBuilder<> B(C);
std::unique_ptr<Function> F(
diff --git a/unittests/Transforms/Utils/Makefile b/unittests/Transforms/Utils/Makefile
deleted file mode 100644
index e6c2a2c133aa..000000000000
--- a/unittests/Transforms/Utils/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Transforms/Utils/Makefile -----------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-TESTNAME = Utils
-LINK_COMPONENTS := TransformUtils
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/Transforms/Utils/MemorySSA.cpp b/unittests/Transforms/Utils/MemorySSA.cpp
new file mode 100644
index 000000000000..c21121f78705
--- /dev/null
+++ b/unittests/Transforms/Utils/MemorySSA.cpp
@@ -0,0 +1,310 @@
+//===- MemorySSA.cpp - Unit tests for MemorySSA ---------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/Transforms/Utils/MemorySSA.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/BasicAliasAnalysis.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+const static char DLString[] = "e-i64:64-f80:128-n8:16:32:64-S128";
+
+/// There's a lot of common setup between these tests. This fixture helps reduce
+/// that. Tests should mock up a function, store it in F, and then call
+/// setupAnalyses().
+class MemorySSATest : public testing::Test {
+protected:
+ // N.B. Many of these members depend on each other (e.g. the Module depends on
+ // the Context, etc.). So, order matters here (and in TestAnalyses).
+ LLVMContext C;
+ Module M;
+ IRBuilder<> B;
+ DataLayout DL;
+ TargetLibraryInfoImpl TLII;
+ TargetLibraryInfo TLI;
+ Function *F;
+
+ // Things that we need to build after the function is created.
+ struct TestAnalyses {
+ DominatorTree DT;
+ AssumptionCache AC;
+ AAResults AA;
+ BasicAAResult BAA;
+ MemorySSA MSSA;
+ MemorySSAWalker *Walker;
+
+ TestAnalyses(MemorySSATest &Test)
+ : DT(*Test.F), AC(*Test.F), AA(Test.TLI),
+ BAA(Test.DL, Test.TLI, AC, &DT), MSSA(*Test.F, &AA, &DT) {
+ AA.addAAResult(BAA);
+ Walker = MSSA.getWalker();
+ }
+ };
+
+ std::unique_ptr<TestAnalyses> Analyses;
+
+ void setupAnalyses() {
+ assert(F);
+ Analyses.reset(new TestAnalyses(*this));
+ }
+
+public:
+ MemorySSATest()
+ : M("MemorySSATest", C), B(C), DL(DLString), TLI(TLII), F(nullptr) {}
+};
+
+TEST_F(MemorySSATest, CreateALoadAndPhi) {
+ // We create a diamond where there is a store on one side, and then after
+ // running memory ssa, create a load after the merge point, and use it to test
+ // updating by creating an access for the load and a memoryphi.
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ BasicBlock *Entry(BasicBlock::Create(C, "", F));
+ BasicBlock *Left(BasicBlock::Create(C, "", F));
+ BasicBlock *Right(BasicBlock::Create(C, "", F));
+ BasicBlock *Merge(BasicBlock::Create(C, "", F));
+ B.SetInsertPoint(Entry);
+ B.CreateCondBr(B.getTrue(), Left, Right);
+ B.SetInsertPoint(Left);
+ Argument *PointerArg = &*F->arg_begin();
+ StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg);
+ BranchInst::Create(Merge, Left);
+ BranchInst::Create(Merge, Right);
+
+ setupAnalyses();
+ MemorySSA &MSSA = Analyses->MSSA;
+ // Add the load
+ B.SetInsertPoint(Merge);
+ LoadInst *LoadInst = B.CreateLoad(PointerArg);
+ // Should be no phi to start
+ EXPECT_EQ(MSSA.getMemoryAccess(Merge), nullptr);
+
+ // Create the phi
+ MemoryPhi *MP = MSSA.createMemoryPhi(Merge);
+ MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst));
+ MP->addIncoming(StoreAccess, Left);
+ MP->addIncoming(MSSA.getLiveOnEntryDef(), Right);
+
+ // Create the load memory acccess
+ MemoryUse *LoadAccess = cast<MemoryUse>(
+ MSSA.createMemoryAccessInBB(LoadInst, MP, Merge, MemorySSA::Beginning));
+ MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();
+ EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, RemoveAPhi) {
+ // We create a diamond where there is a store on one side, and then a load
+ // after the merge point. This enables us to test a bunch of different
+ // removal cases.
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ BasicBlock *Entry(BasicBlock::Create(C, "", F));
+ BasicBlock *Left(BasicBlock::Create(C, "", F));
+ BasicBlock *Right(BasicBlock::Create(C, "", F));
+ BasicBlock *Merge(BasicBlock::Create(C, "", F));
+ B.SetInsertPoint(Entry);
+ B.CreateCondBr(B.getTrue(), Left, Right);
+ B.SetInsertPoint(Left);
+ Argument *PointerArg = &*F->arg_begin();
+ StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg);
+ BranchInst::Create(Merge, Left);
+ BranchInst::Create(Merge, Right);
+ B.SetInsertPoint(Merge);
+ LoadInst *LoadInst = B.CreateLoad(PointerArg);
+
+ setupAnalyses();
+ MemorySSA &MSSA = Analyses->MSSA;
+ // Before, the load will be a use of a phi<store, liveonentry>.
+ MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst));
+ MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst));
+ MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();
+ EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));
+ // Kill the store
+ MSSA.removeMemoryAccess(StoreAccess);
+ MemoryPhi *MP = cast<MemoryPhi>(DefiningAccess);
+ // Verify the phi ended up as liveonentry, liveonentry
+ for (auto &Op : MP->incoming_values())
+ EXPECT_TRUE(MSSA.isLiveOnEntryDef(cast<MemoryAccess>(Op.get())));
+ // Replace the phi uses with the live on entry def
+ MP->replaceAllUsesWith(MSSA.getLiveOnEntryDef());
+ // Verify the load is now defined by liveOnEntryDef
+ EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess()));
+ // Remove the PHI
+ MSSA.removeMemoryAccess(MP);
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, RemoveMemoryAccess) {
+ // We create a diamond where there is a store on one side, and then a load
+ // after the merge point. This enables us to test a bunch of different
+ // removal cases.
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ BasicBlock *Entry(BasicBlock::Create(C, "", F));
+ BasicBlock *Left(BasicBlock::Create(C, "", F));
+ BasicBlock *Right(BasicBlock::Create(C, "", F));
+ BasicBlock *Merge(BasicBlock::Create(C, "", F));
+ B.SetInsertPoint(Entry);
+ B.CreateCondBr(B.getTrue(), Left, Right);
+ B.SetInsertPoint(Left);
+ Argument *PointerArg = &*F->arg_begin();
+ StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg);
+ BranchInst::Create(Merge, Left);
+ BranchInst::Create(Merge, Right);
+ B.SetInsertPoint(Merge);
+ LoadInst *LoadInst = B.CreateLoad(PointerArg);
+
+ setupAnalyses();
+ MemorySSA &MSSA = Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ // Before, the load will be a use of a phi<store, liveonentry>. It should be
+ // the same after.
+ MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst));
+ MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst));
+ MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();
+ EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));
+ // The load is currently clobbered by one of the phi arguments, so the walker
+ // should determine the clobbering access as the phi.
+ EXPECT_EQ(DefiningAccess, Walker->getClobberingMemoryAccess(LoadInst));
+ MSSA.removeMemoryAccess(StoreAccess);
+ MSSA.verifyMemorySSA();
+ // After the removeaccess, let's see if we got the right accesses
+ // The load should still point to the phi ...
+ EXPECT_EQ(DefiningAccess, LoadAccess->getDefiningAccess());
+ // but we should now get live on entry for the clobbering definition of the
+ // load, since it will walk past the phi node since every argument is the
+ // same.
+ EXPECT_TRUE(
+ MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst)));
+
+ // The phi should now be a two entry phi with two live on entry defs.
+ for (const auto &Op : DefiningAccess->operands()) {
+ MemoryAccess *Operand = cast<MemoryAccess>(&*Op);
+ EXPECT_TRUE(MSSA.isLiveOnEntryDef(Operand));
+ }
+
+ // Now we try to remove the single valued phi
+ MSSA.removeMemoryAccess(DefiningAccess);
+ MSSA.verifyMemorySSA();
+ // Now the load should be a load of live on entry.
+ EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess()));
+}
+
+// We had a bug with caching where the walker would report MemoryDef#3's clobber
+// (below) was MemoryDef#1.
+//
+// define void @F(i8*) {
+// %A = alloca i8, i8 1
+// ; 1 = MemoryDef(liveOnEntry)
+// store i8 0, i8* %A
+// ; 2 = MemoryDef(1)
+// store i8 1, i8* %A
+// ; 3 = MemoryDef(2)
+// store i8 2, i8* %A
+// }
+TEST_F(MemorySSATest, TestTripleStore) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");
+ StoreInst *S1 = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);
+ StoreInst *S2 = B.CreateStore(ConstantInt::get(Int8, 1), Alloca);
+ StoreInst *S3 = B.CreateStore(ConstantInt::get(Int8, 2), Alloca);
+
+ setupAnalyses();
+ MemorySSA &MSSA = Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ unsigned I = 0;
+ for (StoreInst *V : {S1, S2, S3}) {
+ // Everything should be clobbered by its defining access
+ MemoryAccess *DefiningAccess =
+ cast<MemoryUseOrDef>(MSSA.getMemoryAccess(V))->getDefiningAccess();
+ MemoryAccess *WalkerClobber = Walker->getClobberingMemoryAccess(V);
+ EXPECT_EQ(DefiningAccess, WalkerClobber)
+ << "Store " << I << " doesn't have the correct clobbering access";
+ // EXPECT_EQ expands such that if we increment I above, it won't get
+ // incremented except when we try to print the error message.
+ ++I;
+ }
+}
+
+// ...And fixing the above bug made it obvious that, when walking, MemorySSA's
+// walker was caching the initial node it walked. This was fine (albeit
+// mostly redundant) unless the initial node being walked is a clobber for the
+// query. In that case, we'd cache that the node clobbered itself.
+TEST_F(MemorySSATest, TestStoreAndLoad) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");
+ Instruction *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);
+ Instruction *LI = B.CreateLoad(Alloca);
+
+ setupAnalyses();
+ MemorySSA &MSSA = Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LI);
+ EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SI));
+ EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SI)));
+}
+
+// Another bug (related to the above two fixes): It was noted that, given the
+// following code:
+// ; 1 = MemoryDef(liveOnEntry)
+// store i8 0, i8* %1
+//
+// ...A query to getClobberingMemoryAccess(MemoryAccess*, MemoryLocation) would
+// hand back the store (correctly). A later call to
+// getClobberingMemoryAccess(const Instruction*) would also hand back the store
+// (incorrectly; it should return liveOnEntry).
+//
+// This test checks that repeated calls to either function returns what they're
+// meant to.
+TEST_F(MemorySSATest, TestStoreDoubleQuery) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");
+ StoreInst *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);
+
+ setupAnalyses();
+ MemorySSA &MSSA = Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ MemoryAccess *StoreAccess = MSSA.getMemoryAccess(SI);
+ MemoryLocation StoreLoc = MemoryLocation::get(SI);
+ MemoryAccess *Clobber =
+ Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc);
+ MemoryAccess *LiveOnEntry = Walker->getClobberingMemoryAccess(SI);
+
+ EXPECT_EQ(Clobber, StoreAccess);
+ EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry));
+
+ // Try again (with entries in the cache already) for good measure...
+ Clobber = Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc);
+ LiveOnEntry = Walker->getClobberingMemoryAccess(SI);
+ EXPECT_EQ(Clobber, StoreAccess);
+ EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry));
+}
diff --git a/unittests/Transforms/Utils/ValueMapperTest.cpp b/unittests/Transforms/Utils/ValueMapperTest.cpp
index 9dbe4dbc56de..34b62bb930d9 100644
--- a/unittests/Transforms/Utils/ValueMapperTest.cpp
+++ b/unittests/Transforms/Utils/ValueMapperTest.cpp
@@ -7,6 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
@@ -16,31 +19,117 @@ using namespace llvm;
namespace {
-TEST(ValueMapperTest, MapMetadataUnresolved) {
+TEST(ValueMapperTest, mapMDNode) {
+ LLVMContext Context;
+ auto *U = MDTuple::get(Context, None);
+
+ // The node should be unchanged.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(U, ValueMapper(VM).mapMDNode(*U));
+}
+
+TEST(ValueMapperTest, mapMDNodeCycle) {
+ LLVMContext Context;
+ MDNode *U0;
+ MDNode *U1;
+ {
+ Metadata *Ops[] = {nullptr};
+ auto T = MDTuple::getTemporary(Context, Ops);
+ Ops[0] = T.get();
+ U0 = MDTuple::get(Context, Ops);
+ T->replaceOperandWith(0, U0);
+ U1 = MDNode::replaceWithUniqued(std::move(T));
+ U0->resolveCycles();
+ }
+
+ EXPECT_TRUE(U0->isResolved());
+ EXPECT_TRUE(U0->isUniqued());
+ EXPECT_TRUE(U1->isResolved());
+ EXPECT_TRUE(U1->isUniqued());
+ EXPECT_EQ(U1, U0->getOperand(0));
+ EXPECT_EQ(U0, U1->getOperand(0));
+
+ // Cycles shouldn't be duplicated.
+ {
+ ValueToValueMapTy VM;
+ EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0));
+ EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1));
+ }
+
+ // Check the other order.
+ {
+ ValueToValueMapTy VM;
+ EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1));
+ EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0));
+ }
+}
+
+TEST(ValueMapperTest, mapMDNodeDuplicatedCycle) {
+ LLVMContext Context;
+ auto *PtrTy = Type::getInt8Ty(Context)->getPointerTo();
+ std::unique_ptr<GlobalVariable> G0 = llvm::make_unique<GlobalVariable>(
+ PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G0");
+ std::unique_ptr<GlobalVariable> G1 = llvm::make_unique<GlobalVariable>(
+ PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G1");
+
+ // Create a cycle that references G0.
+ MDNode *N0; // !0 = !{!1}
+ MDNode *N1; // !1 = !{!0, i8* @G0}
+ {
+ auto T0 = MDTuple::getTemporary(Context, nullptr);
+ Metadata *Ops1[] = {T0.get(), ConstantAsMetadata::get(G0.get())};
+ N1 = MDTuple::get(Context, Ops1);
+ T0->replaceOperandWith(0, N1);
+ N0 = MDNode::replaceWithUniqued(std::move(T0));
+ }
+
+ // Resolve N0 and N1.
+ ASSERT_FALSE(N0->isResolved());
+ ASSERT_FALSE(N1->isResolved());
+ N0->resolveCycles();
+ ASSERT_TRUE(N0->isResolved());
+ ASSERT_TRUE(N1->isResolved());
+
+ // Seed the value map to map G0 to G1 and map the nodes. The output should
+ // have new nodes that reference G1 (instead of G0).
+ ValueToValueMapTy VM;
+ VM[G0.get()] = G1.get();
+ MDNode *MappedN0 = ValueMapper(VM).mapMDNode(*N0);
+ MDNode *MappedN1 = ValueMapper(VM).mapMDNode(*N1);
+ EXPECT_NE(N0, MappedN0);
+ EXPECT_NE(N1, MappedN1);
+ EXPECT_EQ(ConstantAsMetadata::get(G1.get()), MappedN1->getOperand(1));
+
+ // Check that the output nodes are resolved.
+ EXPECT_TRUE(MappedN0->isResolved());
+ EXPECT_TRUE(MappedN1->isResolved());
+}
+
+TEST(ValueMapperTest, mapMDNodeUnresolved) {
LLVMContext Context;
TempMDTuple T = MDTuple::getTemporary(Context, None);
ValueToValueMapTy VM;
- EXPECT_EQ(T.get(), MapMetadata(T.get(), VM, RF_NoModuleLevelChanges));
+ EXPECT_EQ(T.get(), ValueMapper(VM, RF_NoModuleLevelChanges).mapMDNode(*T));
}
-TEST(ValueMapperTest, MapMetadataDistinct) {
+TEST(ValueMapperTest, mapMDNodeDistinct) {
LLVMContext Context;
auto *D = MDTuple::getDistinct(Context, None);
{
// The node should be cloned.
ValueToValueMapTy VM;
- EXPECT_NE(D, MapMetadata(D, VM, RF_None));
+ EXPECT_NE(D, ValueMapper(VM).mapMDNode(*D));
}
{
// The node should be moved.
ValueToValueMapTy VM;
- EXPECT_EQ(D, MapMetadata(D, VM, RF_MoveDistinctMDs));
+ EXPECT_EQ(D, ValueMapper(VM, RF_MoveDistinctMDs).mapMDNode(*D));
}
}
-TEST(ValueMapperTest, MapMetadataDistinctOperands) {
+TEST(ValueMapperTest, mapMDNodeDistinctOperands) {
LLVMContext Context;
Metadata *Old = MDTuple::getDistinct(Context, None);
auto *D = MDTuple::getDistinct(Context, Old);
@@ -51,8 +140,211 @@ TEST(ValueMapperTest, MapMetadataDistinctOperands) {
VM.MD()[Old].reset(New);
// Make sure operands are updated.
- EXPECT_EQ(D, MapMetadata(D, VM, RF_MoveDistinctMDs));
+ EXPECT_EQ(D, ValueMapper(VM, RF_MoveDistinctMDs).mapMDNode(*D));
EXPECT_EQ(New, D->getOperand(0));
}
+TEST(ValueMapperTest, mapMDNodeSeeded) {
+ LLVMContext Context;
+ auto *D = MDTuple::getDistinct(Context, None);
+
+ // The node should be moved.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(None, VM.getMappedMD(D));
+
+ VM.MD().insert(std::make_pair(D, TrackingMDRef(D)));
+ EXPECT_EQ(D, *VM.getMappedMD(D));
+ EXPECT_EQ(D, ValueMapper(VM).mapMDNode(*D));
+}
+
+TEST(ValueMapperTest, mapMDNodeSeededWithNull) {
+ LLVMContext Context;
+ auto *D = MDTuple::getDistinct(Context, None);
+
+ // The node should be moved.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(None, VM.getMappedMD(D));
+
+ VM.MD().insert(std::make_pair(D, TrackingMDRef()));
+ EXPECT_EQ(nullptr, *VM.getMappedMD(D));
+ EXPECT_EQ(nullptr, ValueMapper(VM).mapMDNode(*D));
}
+
+TEST(ValueMapperTest, mapMetadataNullMapGlobalWithIgnoreMissingLocals) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+
+ ValueToValueMapTy VM;
+ RemapFlags Flags = RF_IgnoreMissingLocals | RF_NullMapMissingGlobalValues;
+ EXPECT_EQ(nullptr, ValueMapper(VM, Flags).mapValue(*F));
+}
+
+TEST(ValueMapperTest, mapMetadataMDString) {
+ LLVMContext C;
+ auto *S1 = MDString::get(C, "S1");
+ ValueToValueMapTy VM;
+
+ // Make sure S1 maps to itself, but isn't memoized.
+ EXPECT_EQ(S1, ValueMapper(VM).mapMetadata(*S1));
+ EXPECT_EQ(None, VM.getMappedMD(S1));
+
+ // We still expect VM.MD() to be respected.
+ auto *S2 = MDString::get(C, "S2");
+ VM.MD()[S1].reset(S2);
+ EXPECT_EQ(S2, ValueMapper(VM).mapMetadata(*S1));
+}
+
+TEST(ValueMapperTest, mapMetadataGetMappedMD) {
+ LLVMContext C;
+ auto *N0 = MDTuple::get(C, None);
+ auto *N1 = MDTuple::get(C, N0);
+
+ // Make sure hasMD and getMappedMD work correctly.
+ ValueToValueMapTy VM;
+ EXPECT_FALSE(VM.hasMD());
+ EXPECT_EQ(N0, ValueMapper(VM).mapMetadata(*N0));
+ EXPECT_EQ(N1, ValueMapper(VM).mapMetadata(*N1));
+ EXPECT_TRUE(VM.hasMD());
+ ASSERT_NE(None, VM.getMappedMD(N0));
+ ASSERT_NE(None, VM.getMappedMD(N1));
+ EXPECT_EQ(N0, *VM.getMappedMD(N0));
+ EXPECT_EQ(N1, *VM.getMappedMD(N1));
+}
+
+TEST(ValueMapperTest, mapMetadataNoModuleLevelChanges) {
+ LLVMContext C;
+ auto *N0 = MDTuple::get(C, None);
+ auto *N1 = MDTuple::get(C, N0);
+
+ // Nothing should be memoized when RF_NoModuleLevelChanges.
+ ValueToValueMapTy VM;
+ EXPECT_FALSE(VM.hasMD());
+ EXPECT_EQ(N0, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N0));
+ EXPECT_EQ(N1, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N1));
+ EXPECT_FALSE(VM.hasMD());
+ EXPECT_EQ(None, VM.getMappedMD(N0));
+ EXPECT_EQ(None, VM.getMappedMD(N1));
+}
+
+TEST(ValueMapperTest, mapMetadataConstantAsMetadata) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+
+ auto *CAM = ConstantAsMetadata::get(F.get());
+ {
+ // ConstantAsMetadata shouldn't be memoized.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(CAM, ValueMapper(VM).mapMetadata(*CAM));
+ EXPECT_FALSE(VM.MD().count(CAM));
+ EXPECT_EQ(CAM, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM));
+ EXPECT_FALSE(VM.MD().count(CAM));
+
+ // But it should respect a mapping that gets seeded.
+ auto *N = MDTuple::get(C, None);
+ VM.MD()[CAM].reset(N);
+ EXPECT_EQ(N, ValueMapper(VM).mapMetadata(*CAM));
+ EXPECT_EQ(N, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM));
+ }
+
+ std::unique_ptr<Function> F2(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F2"));
+ ValueToValueMapTy VM;
+ VM[F.get()] = F2.get();
+ auto *F2MD = ValueMapper(VM).mapMetadata(*CAM);
+ EXPECT_FALSE(VM.MD().count(CAM));
+ EXPECT_TRUE(F2MD);
+ EXPECT_EQ(F2.get(), cast<ConstantAsMetadata>(F2MD)->getValue());
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+#ifndef NDEBUG
+TEST(ValueMapperTest, mapMetadataLocalAsMetadata) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+ Argument &A = *F->arg_begin();
+
+ // mapMetadata doesn't support LocalAsMetadata. The only valid container for
+ // LocalAsMetadata is a MetadataAsValue instance, so use it directly.
+ auto *LAM = LocalAsMetadata::get(&A);
+ ValueToValueMapTy VM;
+ EXPECT_DEATH(ValueMapper(VM).mapMetadata(*LAM), "Unexpected local metadata");
+ EXPECT_DEATH(ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*LAM),
+ "Unexpected local metadata");
+}
+#endif
+#endif
+
+TEST(ValueMapperTest, mapValueLocalAsMetadata) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+ Argument &A = *F->arg_begin();
+
+ auto *LAM = LocalAsMetadata::get(&A);
+ auto *MAV = MetadataAsValue::get(C, LAM);
+
+ // The principled answer to a LocalAsMetadata of an unmapped SSA value would
+ // be to return nullptr (regardless of RF_IgnoreMissingLocals).
+ //
+ // However, algorithms that use RemapInstruction assume that each instruction
+ // only references SSA values from previous instructions. Arguments of
+ // such as "metadata i32 %x" don't currently successfully maintain that
+ // property. To keep RemapInstruction from crashing we need a non-null
+ // return here, but we also shouldn't reference the unmapped local. Use
+ // "metadata !{}".
+ auto *N0 = MDTuple::get(C, None);
+ auto *N0AV = MetadataAsValue::get(C, N0);
+ ValueToValueMapTy VM;
+ EXPECT_EQ(N0AV, ValueMapper(VM).mapValue(*MAV));
+ EXPECT_EQ(nullptr, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));
+ EXPECT_FALSE(VM.count(MAV));
+ EXPECT_FALSE(VM.count(&A));
+ EXPECT_EQ(None, VM.getMappedMD(LAM));
+
+ VM[MAV] = MAV;
+ EXPECT_EQ(MAV, ValueMapper(VM).mapValue(*MAV));
+ EXPECT_EQ(MAV, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));
+ EXPECT_TRUE(VM.count(MAV));
+ EXPECT_FALSE(VM.count(&A));
+
+ VM[MAV] = &A;
+ EXPECT_EQ(&A, ValueMapper(VM).mapValue(*MAV));
+ EXPECT_EQ(&A, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));
+ EXPECT_TRUE(VM.count(MAV));
+ EXPECT_FALSE(VM.count(&A));
+}
+
+TEST(ValueMapperTest, mapValueLocalAsMetadataToConstant) {
+ LLVMContext Context;
+ auto *Int8 = Type::getInt8Ty(Context);
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), Int8, false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+
+ // Map a local value to a constant.
+ Argument &A = *F->arg_begin();
+ Constant &C = *ConstantInt::get(Int8, 42);
+ ValueToValueMapTy VM;
+ VM[&A] = &C;
+
+ // Look up the metadata-as-value wrapper. Don't crash.
+ auto *MDA = MetadataAsValue::get(Context, ValueAsMetadata::get(&A));
+ auto *MDC = MetadataAsValue::get(Context, ValueAsMetadata::get(&C));
+ EXPECT_TRUE(isa<LocalAsMetadata>(MDA->getMetadata()));
+ EXPECT_TRUE(isa<ConstantAsMetadata>(MDC->getMetadata()));
+ EXPECT_EQ(&C, ValueMapper(VM).mapValue(A));
+ EXPECT_EQ(MDC, ValueMapper(VM).mapValue(*MDA));
+}
+
+} // end namespace