summaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
commiteb11fae6d08f479c0799db45860a98af528fa6e7 (patch)
tree44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /unittests
parentb8a2042aa938069e862750553db0e4d82d25822c (diff)
Notes
Diffstat (limited to 'unittests')
-rw-r--r--unittests/ADT/APFloatTest.cpp7
-rw-r--r--unittests/ADT/APIntTest.cpp138
-rw-r--r--unittests/ADT/AnyTest.cpp175
-rw-r--r--unittests/ADT/ArrayRefTest.cpp8
-rw-r--r--unittests/ADT/CMakeLists.txt10
-rw-r--r--unittests/ADT/DenseMapTest.cpp16
-rw-r--r--unittests/ADT/DenseSetTest.cpp2
-rw-r--r--unittests/ADT/FunctionExtrasTest.cpp228
-rw-r--r--unittests/ADT/IteratorTest.cpp63
-rw-r--r--unittests/ADT/MapVectorTest.cpp39
-rw-r--r--unittests/ADT/OptionalTest.cpp20
-rw-r--r--unittests/ADT/STLExtrasTest.cpp4
-rw-r--r--unittests/ADT/ScopeExitTest.cpp17
-rw-r--r--unittests/ADT/SmallPtrSetTest.cpp6
-rw-r--r--unittests/ADT/SmallSetTest.cpp149
-rw-r--r--unittests/ADT/SparseBitVectorTest.cpp2
-rw-r--r--unittests/ADT/StatisticTest.cpp160
-rw-r--r--unittests/ADT/StringExtrasTest.cpp25
-rw-r--r--unittests/ADT/StringMapTest.cpp44
-rw-r--r--unittests/ADT/StringRefTest.cpp23
-rw-r--r--unittests/ADT/StringSwitchTest.cpp6
-rw-r--r--unittests/ADT/TripleTest.cpp47
-rw-r--r--unittests/Analysis/AliasAnalysisTest.cpp2
-rw-r--r--unittests/Analysis/CMakeLists.txt1
-rw-r--r--unittests/Analysis/LazyCallGraphTest.cpp22
-rw-r--r--unittests/Analysis/MemorySSA.cpp292
-rw-r--r--unittests/Analysis/PhiValuesTest.cpp208
-rw-r--r--unittests/Analysis/ScalarEvolutionTest.cpp102
-rw-r--r--unittests/Analysis/TargetLibraryInfoTest.cpp21
-rw-r--r--unittests/Analysis/ValueLatticeTest.cpp147
-rw-r--r--unittests/BinaryFormat/CMakeLists.txt1
-rw-r--r--unittests/BinaryFormat/DwarfTest.cpp64
-rw-r--r--unittests/BinaryFormat/MachOTest.cpp47
-rw-r--r--unittests/BinaryFormat/TestFileMagic.cpp3
-rw-r--r--unittests/Bitcode/BitReaderTest.cpp2
-rw-r--r--unittests/CMakeLists.txt4
-rw-r--r--unittests/CodeGen/CMakeLists.txt6
-rw-r--r--unittests/CodeGen/GlobalISel/CMakeLists.txt9
-rw-r--r--unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp167
-rw-r--r--unittests/CodeGen/GlobalISel/PatternMatchTest.cpp493
-rw-r--r--unittests/CodeGen/MachineInstrTest.cpp29
-rw-r--r--unittests/CodeGen/MachineOperandTest.cpp16
-rw-r--r--unittests/CodeGen/ScalableVectorMVTsTest.cpp2
-rw-r--r--unittests/DebugInfo/CodeView/CMakeLists.txt6
-rw-r--r--unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp23
-rw-r--r--unittests/DebugInfo/DWARF/CMakeLists.txt8
-rw-r--r--unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp110
-rw-r--r--unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp667
-rw-r--r--unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp53
-rw-r--r--unittests/DebugInfo/DWARF/DwarfGenerator.cpp341
-rw-r--r--unittests/DebugInfo/DWARF/DwarfGenerator.h95
-rw-r--r--unittests/DebugInfo/DWARF/DwarfUtils.cpp43
-rw-r--r--unittests/DebugInfo/DWARF/DwarfUtils.h29
-rw-r--r--unittests/DebugInfo/MSF/CMakeLists.txt6
-rw-r--r--unittests/DebugInfo/MSF/MSFBuilderTest.cpp21
-rw-r--r--unittests/DebugInfo/MSF/MSFCommonTest.cpp45
-rw-r--r--unittests/DebugInfo/PDB/CMakeLists.txt6
-rw-r--r--unittests/DebugInfo/PDB/HashTableTest.cpp188
-rw-r--r--unittests/DebugInfo/PDB/PDBApiTest.cpp79
-rw-r--r--unittests/DebugInfo/PDB/StringTableBuilderTest.cpp48
-rw-r--r--unittests/Demangle/CMakeLists.txt7
-rw-r--r--unittests/Demangle/PartialDemangleTest.cpp149
-rw-r--r--unittests/ExecutionEngine/MCJIT/CMakeLists.txt1
-rw-r--r--unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h4
-rw-r--r--unittests/ExecutionEngine/Orc/CMakeLists.txt2
-rw-r--r--unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp25
-rw-r--r--unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp744
-rw-r--r--unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp10
-rw-r--r--unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp183
-rw-r--r--unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp126
-rw-r--r--unittests/ExecutionEngine/Orc/OrcCAPITest.cpp60
-rw-r--r--unittests/ExecutionEngine/Orc/OrcTestCommon.cpp5
-rw-r--r--unittests/ExecutionEngine/Orc/OrcTestCommon.h57
-rw-r--r--unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp127
-rw-r--r--unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp34
-rw-r--r--unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp6
-rw-r--r--unittests/FuzzMutate/RandomIRBuilderTest.cpp30
-rw-r--r--unittests/FuzzMutate/StrategiesTest.cpp47
-rw-r--r--unittests/IR/AttributesTest.cpp78
-rw-r--r--unittests/IR/BasicBlockTest.cpp57
-rw-r--r--unittests/IR/CFGBuilder.cpp12
-rw-r--r--unittests/IR/CMakeLists.txt15
-rw-r--r--unittests/IR/ConstantRangeTest.cpp99
-rw-r--r--unittests/IR/ConstantsTest.cpp3
-rw-r--r--unittests/IR/DebugTypeODRUniquingTest.cpp22
-rw-r--r--unittests/IR/DeferredDominanceTest.cpp344
-rw-r--r--unittests/IR/DomTreeUpdaterTest.cpp701
-rw-r--r--unittests/IR/DominatorTreeBatchUpdatesTest.cpp128
-rw-r--r--unittests/IR/DominatorTreeTest.cpp179
-rw-r--r--unittests/IR/IRBuilderTest.cpp77
-rw-r--r--unittests/IR/InstructionsTest.cpp131
-rw-r--r--unittests/IR/LegacyPassManagerTest.cpp62
-rw-r--r--unittests/IR/ManglerTest.cpp140
-rw-r--r--unittests/IR/MetadataTest.cpp187
-rw-r--r--unittests/IR/PassBuilderCallbacksTest.cpp4
-rw-r--r--unittests/IR/PassManagerTest.cpp2
-rw-r--r--unittests/IR/PatternMatch.cpp194
-rw-r--r--unittests/IR/ValueMapTest.cpp2
-rw-r--r--unittests/IR/ValueTest.cpp15
-rw-r--r--unittests/Linker/CMakeLists.txt6
-rw-r--r--unittests/MC/Disassembler.cpp45
-rw-r--r--unittests/MI/LiveIntervalTest.cpp37
-rw-r--r--unittests/Option/OptionParsingTest.cpp45
-rw-r--r--unittests/Option/Opts.td8
-rw-r--r--unittests/Passes/CMakeLists.txt29
-rw-r--r--unittests/Passes/PluginsTest.cpp61
-rw-r--r--unittests/Passes/TestPlugin.cpp39
-rw-r--r--unittests/Passes/TestPlugin.h2
-rw-r--r--unittests/ProfileData/CoverageMappingTest.cpp19
-rw-r--r--unittests/ProfileData/InstrProfTest.cpp15
-rw-r--r--unittests/ProfileData/SampleProfTest.cpp31
-rw-r--r--unittests/Support/AllocatorTest.cpp2
-rw-r--r--unittests/Support/CMakeLists.txt7
-rw-r--r--unittests/Support/CheckedArithmeticTest.cpp84
-rw-r--r--unittests/Support/CommandLineTest.cpp236
-rw-r--r--unittests/Support/CrashRecoveryTest.cpp4
-rw-r--r--unittests/Support/DJBTest.cpp96
-rw-r--r--unittests/Support/DebugCounterTest.cpp42
-rw-r--r--unittests/Support/DynamicLibrary/CMakeLists.txt17
-rw-r--r--unittests/Support/DynamicLibrary/DynamicLibraryTest.cpp8
-rw-r--r--unittests/Support/DynamicLibrary/ExportedFuncs.cpp (renamed from unittests/Support/DynamicLibrary/ExportedFuncs.cxx)2
-rw-r--r--unittests/Support/DynamicLibrary/PipSqueak.cpp (renamed from unittests/Support/DynamicLibrary/PipSqueak.cxx)4
-rw-r--r--unittests/Support/EndianStreamTest.cpp22
-rw-r--r--unittests/Support/ErrnoTest.cpp3
-rw-r--r--unittests/Support/ErrorOrTest.cpp4
-rw-r--r--unittests/Support/ErrorTest.cpp85
-rw-r--r--unittests/Support/FileOutputBufferTest.cpp50
-rw-r--r--unittests/Support/FormatVariadicTest.cpp80
-rw-r--r--unittests/Support/Host.cpp4
-rw-r--r--unittests/Support/JSONTest.cpp387
-rw-r--r--unittests/Support/LockFileManagerTest.cpp2
-rw-r--r--unittests/Support/MD5Test.cpp2
-rw-r--r--unittests/Support/ManagedStatic.cpp6
-rw-r--r--unittests/Support/MemoryBufferTest.cpp33
-rw-r--r--unittests/Support/ParallelTest.cpp2
-rw-r--r--unittests/Support/Path.cpp578
-rw-r--r--unittests/Support/ProcessTest.cpp4
-rw-r--r--unittests/Support/ProgramTest.cpp75
-rw-r--r--unittests/Support/ReplaceFileTest.cpp2
-rw-r--r--unittests/Support/SourceMgrTest.cpp314
-rw-r--r--unittests/Support/TargetParserTest.cpp86
-rw-r--r--unittests/Support/TaskQueueTest.cpp108
-rw-r--r--unittests/Support/TimerTest.cpp4
-rw-r--r--unittests/Support/TypeTraitsTest.cpp77
-rw-r--r--unittests/Support/VersionTupleTest.cpp50
-rw-r--r--unittests/Support/YAMLIOTest.cpp89
-rw-r--r--unittests/Support/raw_pwrite_stream_test.cpp3
-rw-r--r--unittests/Target/WebAssembly/CMakeLists.txt18
-rw-r--r--unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp549
-rw-r--r--unittests/Transforms/CMakeLists.txt1
-rw-r--r--unittests/Transforms/Scalar/LoopPassManagerTest.cpp12
-rw-r--r--unittests/Transforms/Utils/BasicBlockUtils.cpp52
-rw-r--r--unittests/Transforms/Utils/CMakeLists.txt2
-rw-r--r--unittests/Transforms/Utils/Cloning.cpp100
-rw-r--r--unittests/Transforms/Utils/Local.cpp504
-rw-r--r--unittests/Transforms/Utils/SSAUpdaterBulk.cpp195
-rw-r--r--unittests/Transforms/Vectorize/CMakeLists.txt11
-rw-r--r--unittests/Transforms/Vectorize/VPlanHCFGTest.cpp158
-rw-r--r--unittests/Transforms/Vectorize/VPlanTest.cpp64
-rw-r--r--unittests/Transforms/Vectorize/VPlanTestBase.h61
-rw-r--r--unittests/XRay/CMakeLists.txt6
-rw-r--r--unittests/tools/CMakeLists.txt8
-rw-r--r--unittests/tools/llvm-cfi-verify/CMakeLists.txt3
-rw-r--r--unittests/tools/llvm-cfi-verify/FileAnalysis.cpp303
-rw-r--r--unittests/tools/llvm-exegesis/AArch64/CMakeLists.txt21
-rw-r--r--unittests/tools/llvm-exegesis/AArch64/TargetTest.cpp53
-rw-r--r--unittests/tools/llvm-exegesis/ARM/AssemblerTest.cpp48
-rw-r--r--unittests/tools/llvm-exegesis/ARM/CMakeLists.txt19
-rw-r--r--unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp134
-rw-r--r--unittests/tools/llvm-exegesis/CMakeLists.txt28
-rw-r--r--unittests/tools/llvm-exegesis/ClusteringTest.cpp100
-rw-r--r--unittests/tools/llvm-exegesis/Common/AssemblerUtils.h87
-rw-r--r--unittests/tools/llvm-exegesis/PerfHelperTest.cpp47
-rw-r--r--unittests/tools/llvm-exegesis/X86/AnalysisTest.cpp102
-rw-r--r--unittests/tools/llvm-exegesis/X86/AssemblerTest.cpp57
-rw-r--r--unittests/tools/llvm-exegesis/X86/CMakeLists.txt25
-rw-r--r--unittests/tools/llvm-exegesis/X86/RegisterAliasingTest.cpp91
-rw-r--r--unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp285
-rw-r--r--unittests/tools/llvm-exegesis/X86/TargetTest.cpp170
179 files changed, 13692 insertions, 1195 deletions
diff --git a/unittests/ADT/APFloatTest.cpp b/unittests/ADT/APFloatTest.cpp
index 8b88c123b197..1212b45fb575 100644
--- a/unittests/ADT/APFloatTest.cpp
+++ b/unittests/ADT/APFloatTest.cpp
@@ -983,6 +983,13 @@ TEST(APFloatTest, toString) {
ASSERT_EQ("8.73183400000000010e+02", convertToString(873.1834, 0, 0, false));
ASSERT_EQ("1.79769313486231570e+308",
convertToString(1.7976931348623157E+308, 0, 0, false));
+
+ {
+ SmallString<64> Str;
+ APFloat UnnormalZero(APFloat::x87DoubleExtended(), APInt(80, {0, 1}));
+ UnnormalZero.toString(Str);
+ ASSERT_EQ("NaN", Str);
+ }
}
TEST(APFloatTest, toInteger) {
diff --git a/unittests/ADT/APIntTest.cpp b/unittests/ADT/APIntTest.cpp
index 05fad386064c..48f91195e446 100644
--- a/unittests/ADT/APIntTest.cpp
+++ b/unittests/ADT/APIntTest.cpp
@@ -1060,6 +1060,38 @@ TEST(APIntTest, divremuint) {
APInt{1024, 1});
}
+TEST(APIntTest, divrem_simple) {
+ // Test simple cases.
+ APInt A(65, 2), B(65, 2);
+ APInt Q, R;
+
+ // X / X
+ APInt::sdivrem(A, B, Q, R);
+ EXPECT_EQ(Q, APInt(65, 1));
+ EXPECT_EQ(R, APInt(65, 0));
+ APInt::udivrem(A, B, Q, R);
+ EXPECT_EQ(Q, APInt(65, 1));
+ EXPECT_EQ(R, APInt(65, 0));
+
+ // 0 / X
+ APInt O(65, 0);
+ APInt::sdivrem(O, B, Q, R);
+ EXPECT_EQ(Q, APInt(65, 0));
+ EXPECT_EQ(R, APInt(65, 0));
+ APInt::udivrem(O, B, Q, R);
+ EXPECT_EQ(Q, APInt(65, 0));
+ EXPECT_EQ(R, APInt(65, 0));
+
+ // X / 1
+ APInt I(65, 1);
+ APInt::sdivrem(A, I, Q, R);
+ EXPECT_EQ(Q, A);
+ EXPECT_EQ(R, APInt(65, 0));
+ APInt::udivrem(A, I, Q, R);
+ EXPECT_EQ(Q, A);
+ EXPECT_EQ(R, APInt(65, 0));
+}
+
TEST(APIntTest, fromString) {
EXPECT_EQ(APInt(32, 0), APInt(32, "0", 2));
EXPECT_EQ(APInt(32, 1), APInt(32, "1", 2));
@@ -1659,6 +1691,38 @@ TEST(APIntTest, isShiftedMask) {
}
}
+// Test that self-move works, but only when we're using MSVC.
+#if defined(_MSC_VER)
+#if defined(__clang__)
+// Disable the pragma warning from versions of Clang without -Wself-move
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+// Disable the warning that triggers on exactly what is being tested.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-move"
+#endif
+TEST(APIntTest, SelfMoveAssignment) {
+ APInt X(32, 0xdeadbeef);
+ X = std::move(X);
+ EXPECT_EQ(32u, X.getBitWidth());
+ EXPECT_EQ(0xdeadbeefULL, X.getLimitedValue());
+
+ uint64_t Bits[] = {0xdeadbeefdeadbeefULL, 0xdeadbeefdeadbeefULL};
+ APInt Y(128, Bits);
+ Y = std::move(Y);
+ EXPECT_EQ(128u, Y.getBitWidth());
+ EXPECT_EQ(~0ULL, Y.getLimitedValue());
+ const uint64_t *Raw = Y.getRawData();
+ EXPECT_EQ(2u, Y.getNumWords());
+ EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[0]);
+ EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[1]);
+}
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#pragma clang diagnostic pop
+#endif
+#endif // _MSC_VER
+
TEST(APIntTest, reverseBits) {
EXPECT_EQ(1, APInt(1, 1).reverseBits());
EXPECT_EQ(0, APInt(1, 0).reverseBits());
@@ -1768,6 +1832,13 @@ TEST(APIntTest, extractBits) {
i257.extractBits(128, 1).getSExtValue());
EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFFF80007Full),
i257.extractBits(129, 1).getSExtValue());
+
+ EXPECT_EQ(APInt(48, 0),
+ APInt(144, "281474976710655", 10).extractBits(48, 48));
+ EXPECT_EQ(APInt(48, 0x0000ffffffffffffull),
+ APInt(144, "281474976710655", 10).extractBits(48, 0));
+ EXPECT_EQ(APInt(48, 0x00007fffffffffffull),
+ APInt(144, "281474976710655", 10).extractBits(48, 1));
}
TEST(APIntTest, getLowBitsSet) {
@@ -2219,4 +2290,71 @@ TEST(APIntTest, multiply) {
EXPECT_EQ(64U, i96.countTrailingZeros());
}
+TEST(APIntTest, RoundingUDiv) {
+ for (uint64_t Ai = 1; Ai <= 255; Ai++) {
+ APInt A(8, Ai);
+ APInt Zero(8, 0);
+ EXPECT_EQ(0, APIntOps::RoundingUDiv(Zero, A, APInt::Rounding::UP));
+ EXPECT_EQ(0, APIntOps::RoundingUDiv(Zero, A, APInt::Rounding::DOWN));
+ EXPECT_EQ(0, APIntOps::RoundingUDiv(Zero, A, APInt::Rounding::TOWARD_ZERO));
+
+ for (uint64_t Bi = 1; Bi <= 255; Bi++) {
+ APInt B(8, Bi);
+ {
+ APInt Quo = APIntOps::RoundingUDiv(A, B, APInt::Rounding::UP);
+ auto Prod = Quo.zext(16) * B.zext(16);
+ EXPECT_TRUE(Prod.uge(Ai));
+ if (Prod.ugt(Ai)) {
+ EXPECT_TRUE(((Quo - 1).zext(16) * B.zext(16)).ult(Ai));
+ }
+ }
+ {
+ APInt Quo = A.udiv(B);
+ EXPECT_EQ(Quo, APIntOps::RoundingUDiv(A, B, APInt::Rounding::TOWARD_ZERO));
+ EXPECT_EQ(Quo, APIntOps::RoundingUDiv(A, B, APInt::Rounding::DOWN));
+ }
+ }
+ }
+}
+
+TEST(APIntTest, RoundingSDiv) {
+ for (int64_t Ai = -128; Ai <= 127; Ai++) {
+ APInt A(8, Ai);
+
+ if (Ai != 0) {
+ APInt Zero(8, 0);
+ EXPECT_EQ(0, APIntOps::RoundingSDiv(Zero, A, APInt::Rounding::UP));
+ EXPECT_EQ(0, APIntOps::RoundingSDiv(Zero, A, APInt::Rounding::DOWN));
+ EXPECT_EQ(0, APIntOps::RoundingSDiv(Zero, A, APInt::Rounding::TOWARD_ZERO));
+ }
+
+ for (uint64_t Bi = -128; Bi <= 127; Bi++) {
+ if (Bi == 0)
+ continue;
+
+ APInt B(8, Bi);
+ {
+ APInt Quo = APIntOps::RoundingSDiv(A, B, APInt::Rounding::UP);
+ auto Prod = Quo.sext(16) * B.sext(16);
+ EXPECT_TRUE(Prod.uge(A));
+ if (Prod.ugt(A)) {
+ EXPECT_TRUE(((Quo - 1).sext(16) * B.sext(16)).ult(A));
+ }
+ }
+ {
+ APInt Quo = APIntOps::RoundingSDiv(A, B, APInt::Rounding::DOWN);
+ auto Prod = Quo.sext(16) * B.sext(16);
+ EXPECT_TRUE(Prod.ule(A));
+ if (Prod.ult(A)) {
+ EXPECT_TRUE(((Quo + 1).sext(16) * B.sext(16)).ugt(A));
+ }
+ }
+ {
+ APInt Quo = A.sdiv(B);
+ EXPECT_EQ(Quo, APIntOps::RoundingSDiv(A, B, APInt::Rounding::TOWARD_ZERO));
+ }
+ }
+ }
+}
+
} // end anonymous namespace
diff --git a/unittests/ADT/AnyTest.cpp b/unittests/ADT/AnyTest.cpp
new file mode 100644
index 000000000000..658f6b6ce3b7
--- /dev/null
+++ b/unittests/ADT/AnyTest.cpp
@@ -0,0 +1,175 @@
+//===- llvm/unittest/Support/AnyTest.cpp - Any 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/Any.h"
+#include "gtest/gtest.h"
+#include <cstdlib>
+
+using namespace llvm;
+
+namespace {
+
+// Make sure we can construct, copy-construct, move-construct, and assign Any's.
+TEST(AnyTest, ConstructionAndAssignment) {
+ llvm::Any A;
+ llvm::Any B{7};
+ llvm::Any C{8};
+ llvm::Any D{"hello"};
+ llvm::Any E{3.7};
+
+ // An empty Any is not anything.
+ EXPECT_FALSE(A.hasValue());
+ EXPECT_FALSE(any_isa<int>(A));
+
+ // An int is an int but not something else.
+ EXPECT_TRUE(B.hasValue());
+ EXPECT_TRUE(any_isa<int>(B));
+ EXPECT_FALSE(any_isa<float>(B));
+
+ EXPECT_TRUE(C.hasValue());
+ EXPECT_TRUE(any_isa<int>(C));
+
+ // A const char * is a const char * but not an int.
+ EXPECT_TRUE(D.hasValue());
+ EXPECT_TRUE(any_isa<const char *>(D));
+ EXPECT_FALSE(any_isa<int>(D));
+
+ // A double is a double but not a float.
+ EXPECT_TRUE(E.hasValue());
+ EXPECT_TRUE(any_isa<double>(E));
+ EXPECT_FALSE(any_isa<float>(E));
+
+ // After copy constructing from an int, the new item and old item are both
+ // ints.
+ llvm::Any F(B);
+ EXPECT_TRUE(B.hasValue());
+ EXPECT_TRUE(F.hasValue());
+ EXPECT_TRUE(any_isa<int>(F));
+ EXPECT_TRUE(any_isa<int>(B));
+
+ // After move constructing from an int, the new item is an int and the old one
+ // isn't.
+ llvm::Any G(std::move(C));
+ EXPECT_FALSE(C.hasValue());
+ EXPECT_TRUE(G.hasValue());
+ EXPECT_TRUE(any_isa<int>(G));
+ EXPECT_FALSE(any_isa<int>(C));
+
+ // After copy-assigning from an int, the new item and old item are both ints.
+ A = F;
+ EXPECT_TRUE(A.hasValue());
+ EXPECT_TRUE(F.hasValue());
+ EXPECT_TRUE(any_isa<int>(A));
+ EXPECT_TRUE(any_isa<int>(F));
+
+ // After move-assigning from an int, the new item and old item are both ints.
+ B = std::move(G);
+ EXPECT_TRUE(B.hasValue());
+ EXPECT_FALSE(G.hasValue());
+ EXPECT_TRUE(any_isa<int>(B));
+ EXPECT_FALSE(any_isa<int>(G));
+}
+
+TEST(AnyTest, GoodAnyCast) {
+ llvm::Any A;
+ llvm::Any B{7};
+ llvm::Any C{8};
+ llvm::Any D{"hello"};
+ llvm::Any E{'x'};
+
+ // Check each value twice to make sure it isn't damaged by the cast.
+ EXPECT_EQ(7, llvm::any_cast<int>(B));
+ EXPECT_EQ(7, llvm::any_cast<int>(B));
+
+ EXPECT_STREQ("hello", llvm::any_cast<const char *>(D));
+ EXPECT_STREQ("hello", llvm::any_cast<const char *>(D));
+
+ EXPECT_EQ('x', llvm::any_cast<char>(E));
+ EXPECT_EQ('x', llvm::any_cast<char>(E));
+
+ llvm::Any F(B);
+ EXPECT_EQ(7, llvm::any_cast<int>(F));
+ EXPECT_EQ(7, llvm::any_cast<int>(F));
+
+ llvm::Any G(std::move(C));
+ EXPECT_EQ(8, llvm::any_cast<int>(G));
+ EXPECT_EQ(8, llvm::any_cast<int>(G));
+
+ A = F;
+ EXPECT_EQ(7, llvm::any_cast<int>(A));
+ EXPECT_EQ(7, llvm::any_cast<int>(A));
+
+ E = std::move(G);
+ EXPECT_EQ(8, llvm::any_cast<int>(E));
+ EXPECT_EQ(8, llvm::any_cast<int>(E));
+
+ // Make sure we can any_cast from an rvalue and that it's properly destroyed
+ // in the process.
+ EXPECT_EQ(8, llvm::any_cast<int>(std::move(E)));
+ EXPECT_TRUE(E.hasValue());
+
+ // Make sure moving from pointers gives back pointers, and that we can modify
+ // the underlying value through those pointers.
+ EXPECT_EQ(7, *llvm::any_cast<int>(&A));
+ int *N = llvm::any_cast<int>(&A);
+ *N = 42;
+ EXPECT_EQ(42, llvm::any_cast<int>(A));
+
+ // Make sure that we can any_cast to a reference and this is considered a good
+ // cast, resulting in an lvalue which can be modified.
+ llvm::any_cast<int &>(A) = 43;
+ EXPECT_EQ(43, llvm::any_cast<int>(A));
+}
+
+TEST(AnyTest, CopiesAndMoves) {
+ struct TestType {
+ TestType() = default;
+ TestType(const TestType &Other)
+ : Copies(Other.Copies + 1), Moves(Other.Moves) {}
+ TestType(TestType &&Other) : Copies(Other.Copies), Moves(Other.Moves + 1) {}
+ int Copies = 0;
+ int Moves = 0;
+ };
+
+ // One move to get TestType into the Any, and one move on the cast.
+ TestType T1 = llvm::any_cast<TestType>(Any{TestType()});
+ EXPECT_EQ(0, T1.Copies);
+ EXPECT_EQ(2, T1.Moves);
+
+ // One move to get TestType into the Any, and one copy on the cast.
+ Any A{TestType()};
+ TestType T2 = llvm::any_cast<TestType>(A);
+ EXPECT_EQ(1, T2.Copies);
+ EXPECT_EQ(1, T2.Moves);
+
+ // One move to get TestType into the Any, and one move on the cast.
+ TestType T3 = llvm::any_cast<TestType>(std::move(A));
+ EXPECT_EQ(0, T3.Copies);
+ EXPECT_EQ(2, T3.Moves);
+}
+
+TEST(AnyTest, BadAnyCast) {
+ llvm::Any A;
+ llvm::Any B{7};
+ llvm::Any C{"hello"};
+ llvm::Any D{'x'};
+
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
+ EXPECT_DEATH(llvm::any_cast<int>(A), "");
+
+ EXPECT_DEATH(llvm::any_cast<float>(B), "");
+ EXPECT_DEATH(llvm::any_cast<int *>(B), "");
+
+ EXPECT_DEATH(llvm::any_cast<std::string>(C), "");
+
+ EXPECT_DEATH(llvm::any_cast<unsigned char>(D), "");
+#endif
+}
+
+} // anonymous namespace
diff --git a/unittests/ADT/ArrayRefTest.cpp b/unittests/ADT/ArrayRefTest.cpp
index 4694ff112cb5..e01d212f218b 100644
--- a/unittests/ADT/ArrayRefTest.cpp
+++ b/unittests/ADT/ArrayRefTest.cpp
@@ -39,16 +39,16 @@ static_assert(
// std::is_assignable and actually writing such an assignment.
#if !defined(_MSC_VER)
static_assert(
- !std::is_assignable<ArrayRef<int *>, int *>::value,
+ !std::is_assignable<ArrayRef<int *>&, int *>::value,
"Assigning from single prvalue element");
static_assert(
- !std::is_assignable<ArrayRef<int *>, int * &&>::value,
+ !std::is_assignable<ArrayRef<int *>&, int * &&>::value,
"Assigning from single xvalue element");
static_assert(
- std::is_assignable<ArrayRef<int *>, int * &>::value,
+ std::is_assignable<ArrayRef<int *>&, int * &>::value,
"Assigning from single lvalue element");
static_assert(
- !std::is_assignable<ArrayRef<int *>, std::initializer_list<int *>>::value,
+ !std::is_assignable<ArrayRef<int *>&, std::initializer_list<int *>>::value,
"Assigning from an initializer list");
#endif
diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt
index c0d511000f61..19657da8bcee 100644
--- a/unittests/ADT/CMakeLists.txt
+++ b/unittests/ADT/CMakeLists.txt
@@ -2,7 +2,8 @@ set(LLVM_LINK_COMPONENTS
Support
)
-set(ADTSources
+add_llvm_unittest(ADTTests
+ AnyTest.cpp
APFloatTest.cpp
APIntTest.cpp
APSIntTest.cpp
@@ -18,6 +19,7 @@ set(ADTSources
DepthFirstIteratorTest.cpp
EquivalenceClassesTest.cpp
FoldingSet.cpp
+ FunctionExtrasTest.cpp
FunctionRefTest.cpp
HashingTest.cpp
IListBaseTest.cpp
@@ -51,11 +53,13 @@ set(ADTSources
SetVectorTest.cpp
SimpleIListTest.cpp
SmallPtrSetTest.cpp
+ SmallSetTest.cpp
SmallStringTest.cpp
SmallVectorTest.cpp
SparseBitVectorTest.cpp
SparseMultiSetTest.cpp
SparseSetTest.cpp
+ StatisticTest.cpp
StringExtrasTest.cpp
StringMapTest.cpp
StringRefTest.cpp
@@ -64,10 +68,6 @@ set(ADTSources
TripleTest.cpp
TwineTest.cpp
VariadicFunctionTest.cpp
- )
-
-add_llvm_unittest(ADTTests
- ${ADTSources}
)
add_dependencies(ADTTests intrinsics_gen)
diff --git a/unittests/ADT/DenseMapTest.cpp b/unittests/ADT/DenseMapTest.cpp
index cb5ba6875eaa..87f22f6f403e 100644
--- a/unittests/ADT/DenseMapTest.cpp
+++ b/unittests/ADT/DenseMapTest.cpp
@@ -30,7 +30,7 @@ uint32_t *getTestValue(int i, uint32_t **) {
return &dummy_arr1[i];
}
-/// \brief A test class that tries to check that construction and destruction
+/// A test class that tries to check that construction and destruction
/// occur correctly.
class CtorTester {
static std::set<CtorTester *> Constructed;
@@ -247,7 +247,7 @@ TYPED_TEST(DenseMapTest, AssignmentTest) {
EXPECT_EQ(this->getValue(), copyMap[this->getKey()]);
// test self-assignment.
- copyMap = copyMap;
+ copyMap = static_cast<TypeParam &>(copyMap);
EXPECT_EQ(1u, copyMap.size());
EXPECT_EQ(this->getValue(), copyMap[this->getKey()]);
}
@@ -262,7 +262,7 @@ TYPED_TEST(DenseMapTest, AssignmentTestNotSmall) {
EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);
// test self-assignment.
- copyMap = copyMap;
+ copyMap = static_cast<TypeParam &>(copyMap);
EXPECT_EQ(5u, copyMap.size());
for (int Key = 0; Key < 5; ++Key)
EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);
@@ -384,7 +384,7 @@ TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) {
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
+ // Check that no copy occurred
EXPECT_EQ(0, CountCopyAndMove::Copy);
// Adding one extra element should grow the map
@@ -397,7 +397,7 @@ TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) {
// 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
+ // Check that no copy occurred
EXPECT_EQ(0, CountCopyAndMove::Copy);
}
@@ -422,7 +422,7 @@ TEST(DenseMapCustomTest, InitialSizeTest) {
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
+ // Check that no copy occurred
EXPECT_EQ(0, CountCopyAndMove::Copy);
}
}
@@ -438,7 +438,7 @@ TEST(DenseMapCustomTest, InitFromIterator) {
CountCopyAndMove::Move = 0;
CountCopyAndMove::Copy = 0;
DenseMap<int, CountCopyAndMove> Map(Values.begin(), Values.end());
- // Check that no move occured
+ // Check that no move occurred
EXPECT_EQ(0, CountCopyAndMove::Move);
// Check that copy was called the expected number of times
EXPECT_EQ(Count, CountCopyAndMove::Copy);
@@ -466,7 +466,7 @@ TEST(DenseMapCustomTest, ReserveTest) {
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
+ // Check that no copy occurred
EXPECT_EQ(0, CountCopyAndMove::Copy);
}
}
diff --git a/unittests/ADT/DenseSetTest.cpp b/unittests/ADT/DenseSetTest.cpp
index a09537a3e990..03738e46be4a 100644
--- a/unittests/ADT/DenseSetTest.cpp
+++ b/unittests/ADT/DenseSetTest.cpp
@@ -181,7 +181,7 @@ TEST(DenseSetCustomTest, ReserveTest) {
EXPECT_EQ(MemorySize, Set.getMemorySize());
// Check that move was called the expected number of times
EXPECT_EQ(Size, CountCopyAndMove::Move);
- // Check that no copy occured
+ // Check that no copy occurred
EXPECT_EQ(0, CountCopyAndMove::Copy);
}
}
diff --git a/unittests/ADT/FunctionExtrasTest.cpp b/unittests/ADT/FunctionExtrasTest.cpp
new file mode 100644
index 000000000000..d85962e0f773
--- /dev/null
+++ b/unittests/ADT/FunctionExtrasTest.cpp
@@ -0,0 +1,228 @@
+//===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/FunctionExtras.h"
+#include "gtest/gtest.h"
+
+#include <memory>
+
+using namespace llvm;
+
+namespace {
+
+TEST(UniqueFunctionTest, Basic) {
+ unique_function<int(int, int)> Sum = [](int A, int B) { return A + B; };
+ EXPECT_EQ(Sum(1, 2), 3);
+
+ unique_function<int(int, int)> Sum2 = std::move(Sum);
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ unique_function<int(int, int)> Sum3 = [](int A, int B) { return A + B; };
+ Sum2 = std::move(Sum3);
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ Sum2 = unique_function<int(int, int)>([](int A, int B) { return A + B; });
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ // Explicit self-move test.
+ *&Sum2 = std::move(Sum2);
+ EXPECT_EQ(Sum2(1, 2), 3);
+
+ Sum2 = unique_function<int(int, int)>();
+ EXPECT_FALSE(Sum2);
+
+ // Make sure we can forward through l-value reference parameters.
+ unique_function<void(int &)> Inc = [](int &X) { ++X; };
+ int X = 42;
+ Inc(X);
+ EXPECT_EQ(X, 43);
+
+ // Make sure we can forward through r-value reference parameters with
+ // move-only types.
+ unique_function<int(std::unique_ptr<int> &&)> ReadAndDeallocByRef =
+ [](std::unique_ptr<int> &&Ptr) {
+ int V = *Ptr;
+ Ptr.reset();
+ return V;
+ };
+ std::unique_ptr<int> Ptr{new int(13)};
+ EXPECT_EQ(ReadAndDeallocByRef(std::move(Ptr)), 13);
+ EXPECT_FALSE((bool)Ptr);
+
+ // Make sure we can pass a move-only temporary as opposed to a local variable.
+ EXPECT_EQ(ReadAndDeallocByRef(std::unique_ptr<int>(new int(42))), 42);
+
+ // Make sure we can pass a move-only type by-value.
+ unique_function<int(std::unique_ptr<int>)> ReadAndDeallocByVal =
+ [](std::unique_ptr<int> Ptr) {
+ int V = *Ptr;
+ Ptr.reset();
+ return V;
+ };
+ Ptr.reset(new int(13));
+ EXPECT_EQ(ReadAndDeallocByVal(std::move(Ptr)), 13);
+ EXPECT_FALSE((bool)Ptr);
+
+ EXPECT_EQ(ReadAndDeallocByVal(std::unique_ptr<int>(new int(42))), 42);
+}
+
+TEST(UniqueFunctionTest, Captures) {
+ long A = 1, B = 2, C = 3, D = 4, E = 5;
+
+ unique_function<long()> Tmp;
+
+ unique_function<long()> C1 = [A]() { return A; };
+ EXPECT_EQ(C1(), 1);
+ Tmp = std::move(C1);
+ EXPECT_EQ(Tmp(), 1);
+
+ unique_function<long()> C2 = [A, B]() { return A + B; };
+ EXPECT_EQ(C2(), 3);
+ Tmp = std::move(C2);
+ EXPECT_EQ(Tmp(), 3);
+
+ unique_function<long()> C3 = [A, B, C]() { return A + B + C; };
+ EXPECT_EQ(C3(), 6);
+ Tmp = std::move(C3);
+ EXPECT_EQ(Tmp(), 6);
+
+ unique_function<long()> C4 = [A, B, C, D]() { return A + B + C + D; };
+ EXPECT_EQ(C4(), 10);
+ Tmp = std::move(C4);
+ EXPECT_EQ(Tmp(), 10);
+
+ unique_function<long()> C5 = [A, B, C, D, E]() { return A + B + C + D + E; };
+ EXPECT_EQ(C5(), 15);
+ Tmp = std::move(C5);
+ EXPECT_EQ(Tmp(), 15);
+}
+
+TEST(UniqueFunctionTest, MoveOnly) {
+ struct SmallCallable {
+ std::unique_ptr<int> A{new int(1)};
+
+ int operator()(int B) { return *A + B; }
+ };
+ unique_function<int(int)> Small = SmallCallable();
+ EXPECT_EQ(Small(2), 3);
+ unique_function<int(int)> Small2 = std::move(Small);
+ EXPECT_EQ(Small2(2), 3);
+
+ struct LargeCallable {
+ std::unique_ptr<int> A{new int(1)};
+ std::unique_ptr<int> B{new int(2)};
+ std::unique_ptr<int> C{new int(3)};
+ std::unique_ptr<int> D{new int(4)};
+ std::unique_ptr<int> E{new int(5)};
+
+ int operator()() { return *A + *B + *C + *D + *E; }
+ };
+ unique_function<int()> Large = LargeCallable();
+ EXPECT_EQ(Large(), 15);
+ unique_function<int()> Large2 = std::move(Large);
+ EXPECT_EQ(Large2(), 15);
+}
+
+TEST(UniqueFunctionTest, CountForwardingCopies) {
+ struct CopyCounter {
+ int &CopyCount;
+
+ CopyCounter(int &CopyCount) : CopyCount(CopyCount) {}
+ CopyCounter(const CopyCounter &Arg) : CopyCount(Arg.CopyCount) {
+ ++CopyCount;
+ }
+ };
+
+ unique_function<void(CopyCounter)> ByValF = [](CopyCounter) {};
+ int CopyCount = 0;
+ ByValF(CopyCounter(CopyCount));
+ EXPECT_EQ(1, CopyCount);
+
+ CopyCount = 0;
+ {
+ CopyCounter Counter{CopyCount};
+ ByValF(Counter);
+ }
+ EXPECT_EQ(2, CopyCount);
+
+ // Check that we don't generate a copy at all when we can bind a reference all
+ // the way down, even if that reference could *in theory* allow copies.
+ unique_function<void(const CopyCounter &)> ByRefF = [](const CopyCounter &) {
+ };
+ CopyCount = 0;
+ ByRefF(CopyCounter(CopyCount));
+ EXPECT_EQ(0, CopyCount);
+
+ CopyCount = 0;
+ {
+ CopyCounter Counter{CopyCount};
+ ByRefF(Counter);
+ }
+ EXPECT_EQ(0, CopyCount);
+
+ // If we use a reference, we can make a stronger guarantee that *no* copy
+ // occurs.
+ struct Uncopyable {
+ Uncopyable() = default;
+ Uncopyable(const Uncopyable &) = delete;
+ };
+ unique_function<void(const Uncopyable &)> UncopyableF =
+ [](const Uncopyable &) {};
+ UncopyableF(Uncopyable());
+ Uncopyable X;
+ UncopyableF(X);
+}
+
+TEST(UniqueFunctionTest, CountForwardingMoves) {
+ struct MoveCounter {
+ int &MoveCount;
+
+ MoveCounter(int &MoveCount) : MoveCount(MoveCount) {}
+ MoveCounter(MoveCounter &&Arg) : MoveCount(Arg.MoveCount) { ++MoveCount; }
+ };
+
+ unique_function<void(MoveCounter)> ByValF = [](MoveCounter) {};
+ int MoveCount = 0;
+ ByValF(MoveCounter(MoveCount));
+ EXPECT_EQ(1, MoveCount);
+
+ MoveCount = 0;
+ {
+ MoveCounter Counter{MoveCount};
+ ByValF(std::move(Counter));
+ }
+ EXPECT_EQ(2, MoveCount);
+
+ // Check that when we use an r-value reference we get no spurious copies.
+ unique_function<void(MoveCounter &&)> ByRefF = [](MoveCounter &&) {};
+ MoveCount = 0;
+ ByRefF(MoveCounter(MoveCount));
+ EXPECT_EQ(0, MoveCount);
+
+ MoveCount = 0;
+ {
+ MoveCounter Counter{MoveCount};
+ ByRefF(std::move(Counter));
+ }
+ EXPECT_EQ(0, MoveCount);
+
+ // If we use an r-value reference we can in fact make a stronger guarantee
+ // with an unmovable type.
+ struct Unmovable {
+ Unmovable() = default;
+ Unmovable(Unmovable &&) = delete;
+ };
+ unique_function<void(const Unmovable &)> UnmovableF = [](const Unmovable &) {
+ };
+ UnmovableF(Unmovable());
+ Unmovable X;
+ UnmovableF(X);
+}
+
+} // anonymous namespace
diff --git a/unittests/ADT/IteratorTest.cpp b/unittests/ADT/IteratorTest.cpp
index c95ce8061847..50c3b01bbc74 100644
--- a/unittests/ADT/IteratorTest.cpp
+++ b/unittests/ADT/IteratorTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/iterator.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "gtest/gtest.h"
@@ -127,6 +128,20 @@ TEST(PointeeIteratorTest, Range) {
EXPECT_EQ(A[I++], II);
}
+TEST(PointeeIteratorTest, PointeeType) {
+ struct S {
+ int X;
+ bool operator==(const S &RHS) const { return X == RHS.X; };
+ };
+ S A[] = {S{0}, S{1}};
+ SmallVector<S *, 2> V{&A[0], &A[1]};
+
+ pointee_iterator<SmallVectorImpl<S *>::const_iterator, const S> I = V.begin();
+ for (int j = 0; j < 2; ++j, ++I) {
+ EXPECT_EQ(*V[j], *I);
+ }
+}
+
TEST(FilterIteratorTest, Lambda) {
auto IsOdd = [](int N) { return N % 2 == 1; };
int A[] = {0, 1, 2, 3, 4, 5, 6};
@@ -196,6 +211,33 @@ TEST(FilterIteratorTest, InputIterator) {
EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
}
+TEST(FilterIteratorTest, ReverseFilterRange) {
+ auto IsOdd = [](int N) { return N % 2 == 1; };
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+
+ // Check basic reversal.
+ auto Range = reverse(make_filter_range(A, IsOdd));
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{5, 3, 1}), Actual);
+
+ // Check that the reverse of the reverse is the original.
+ auto Range2 = reverse(reverse(make_filter_range(A, IsOdd)));
+ SmallVector<int, 3> Actual2(Range2.begin(), Range2.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual2);
+
+ // Check empty ranges.
+ auto Range3 = reverse(make_filter_range(ArrayRef<int>(), IsOdd));
+ SmallVector<int, 0> Actual3(Range3.begin(), Range3.end());
+ EXPECT_EQ((SmallVector<int, 0>{}), Actual3);
+
+ // Check that we don't skip the first element, provided it isn't filtered
+ // away.
+ auto IsEven = [](int N) { return N % 2 == 0; };
+ auto Range4 = reverse(make_filter_range(A, IsEven));
+ SmallVector<int, 4> Actual4(Range4.begin(), Range4.end());
+ EXPECT_EQ((SmallVector<int, 4>{6, 4, 2, 0}), Actual4);
+}
+
TEST(PointerIterator, Basic) {
int A[] = {1, 2, 3, 4};
pointer_iterator<int *> Begin(std::begin(A)), End(std::end(A));
@@ -337,4 +379,25 @@ TEST(ZipIteratorTest, Reverse) {
EXPECT_TRUE(all_of(ascending, [](unsigned n) { return (n & 0x01) == 0; }));
}
+TEST(RangeTest, Distance) {
+ std::vector<int> v1;
+ std::vector<int> v2{1, 2, 3};
+
+ EXPECT_EQ(std::distance(v1.begin(), v1.end()), size(v1));
+ EXPECT_EQ(std::distance(v2.begin(), v2.end()), size(v2));
+}
+
+TEST(IteratorRangeTest, DropBegin) {
+ SmallVector<int, 5> vec{0, 1, 2, 3, 4};
+
+ for (int n = 0; n < 5; ++n) {
+ int i = n;
+ for (auto &v : drop_begin(vec, n)) {
+ EXPECT_EQ(v, i);
+ i += 1;
+ }
+ EXPECT_EQ(i, 5);
+ }
+}
+
} // anonymous namespace
diff --git a/unittests/ADT/MapVectorTest.cpp b/unittests/ADT/MapVectorTest.cpp
index bd6602b030f6..16e9b5a74f42 100644
--- a/unittests/ADT/MapVectorTest.cpp
+++ b/unittests/ADT/MapVectorTest.cpp
@@ -157,6 +157,45 @@ TEST(MapVectorTest, NonCopyable) {
ASSERT_EQ(*MV.find(2)->second, 2);
}
+template <class IntType> struct MapVectorMappedTypeTest : ::testing::Test {
+ using int_type = IntType;
+};
+
+using MapIntTypes = ::testing::Types<int, long, long long, unsigned,
+ unsigned long, unsigned long long>;
+TYPED_TEST_CASE(MapVectorMappedTypeTest, MapIntTypes);
+
+TYPED_TEST(MapVectorMappedTypeTest, DifferentDenseMap) {
+ // Test that using a map with a mapped type other than 'unsigned' compiles
+ // and works.
+ using IntType = typename TestFixture::int_type;
+ using MapVectorType = MapVector<int, int, DenseMap<int, IntType>>;
+
+ MapVectorType MV;
+ std::pair<typename MapVectorType::iterator, bool> R;
+
+ R = MV.insert(std::make_pair(1, 2));
+ ASSERT_EQ(R.first, MV.begin());
+ EXPECT_EQ(R.first->first, 1);
+ EXPECT_EQ(R.first->second, 2);
+ EXPECT_TRUE(R.second);
+
+ const std::pair<int, int> Elem(1, 3);
+ R = MV.insert(Elem);
+ ASSERT_EQ(R.first, MV.begin());
+ EXPECT_EQ(R.first->first, 1);
+ EXPECT_EQ(R.first->second, 2);
+ EXPECT_FALSE(R.second);
+
+ int& value = MV[4];
+ EXPECT_EQ(value, 0);
+ value = 5;
+
+ EXPECT_EQ(MV.size(), 2u);
+ EXPECT_EQ(MV[1], 2);
+ EXPECT_EQ(MV[4], 5);
+}
+
TEST(SmallMapVectorSmallTest, insert_pop) {
SmallMapVector<int, int, 32> MV;
std::pair<SmallMapVector<int, int, 32>::iterator, bool> R;
diff --git a/unittests/ADT/OptionalTest.cpp b/unittests/ADT/OptionalTest.cpp
index 46d4fe0780c3..2e09c5340fa3 100644
--- a/unittests/ADT/OptionalTest.cpp
+++ b/unittests/ADT/OptionalTest.cpp
@@ -268,12 +268,12 @@ TEST_F(OptionalTest, MoveOnlyMoveConstruction) {
Optional<MoveOnly> A(MoveOnly(3));
MoveOnly::ResetCounts();
Optional<MoveOnly> B(std::move(A));
- EXPECT_FALSE((bool)A);
+ EXPECT_TRUE((bool)A);
EXPECT_TRUE((bool)B);
EXPECT_EQ(3, B->val);
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
- EXPECT_EQ(1u, MoveOnly::Destructions);
+ EXPECT_EQ(0u, MoveOnly::Destructions);
}
TEST_F(OptionalTest, MoveOnlyAssignment) {
@@ -292,12 +292,12 @@ TEST_F(OptionalTest, MoveOnlyInitializingAssignment) {
Optional<MoveOnly> B;
MoveOnly::ResetCounts();
B = std::move(A);
- EXPECT_FALSE((bool)A);
+ EXPECT_TRUE((bool)A);
EXPECT_TRUE((bool)B);
EXPECT_EQ(3, B->val);
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
- EXPECT_EQ(1u, MoveOnly::Destructions);
+ EXPECT_EQ(0u, MoveOnly::Destructions);
}
TEST_F(OptionalTest, MoveOnlyNullingAssignment) {
@@ -317,12 +317,12 @@ TEST_F(OptionalTest, MoveOnlyAssigningAssignment) {
Optional<MoveOnly> B(MoveOnly(4));
MoveOnly::ResetCounts();
B = std::move(A);
- EXPECT_FALSE((bool)A);
+ EXPECT_TRUE((bool)A);
EXPECT_TRUE((bool)B);
EXPECT_EQ(3, B->val);
EXPECT_EQ(0u, MoveOnly::MoveConstructions);
EXPECT_EQ(1u, MoveOnly::MoveAssignments);
- EXPECT_EQ(1u, MoveOnly::Destructions);
+ EXPECT_EQ(0u, MoveOnly::Destructions);
}
struct Immovable {
@@ -518,5 +518,13 @@ TEST_F(OptionalTest, OperatorGreaterEqual) {
CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess);
}
+#if __has_feature(is_trivially_copyable) && defined(_LIBCPP_VERSION)
+static_assert(std::is_trivially_copyable<Optional<int>>::value,
+ "Should be trivially copyable");
+static_assert(
+ !std::is_trivially_copyable<Optional<NonDefaultConstructible>>::value,
+ "Shouldn't be trivially copyable");
+#endif
+
} // end anonymous namespace
diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp
index 89e876eb4de9..d744a3cd521a 100644
--- a/unittests/ADT/STLExtrasTest.cpp
+++ b/unittests/ADT/STLExtrasTest.cpp
@@ -302,8 +302,8 @@ TEST(STLExtrasTest, PartitionAdaptor) {
ASSERT_EQ(V.begin() + 4, I);
// Sort the two halves as partition may have messed with the order.
- std::sort(V.begin(), I);
- std::sort(I, V.end());
+ llvm::sort(V.begin(), I);
+ llvm::sort(I, V.end());
EXPECT_EQ(2, V[0]);
EXPECT_EQ(4, V[1]);
diff --git a/unittests/ADT/ScopeExitTest.cpp b/unittests/ADT/ScopeExitTest.cpp
index 301942c30bbc..ab11dff8ce48 100644
--- a/unittests/ADT/ScopeExitTest.cpp
+++ b/unittests/ADT/ScopeExitTest.cpp
@@ -29,4 +29,21 @@ TEST(ScopeExitTest, Basic) {
EXPECT_TRUE(Called);
}
+TEST(ScopeExitTest, Release) {
+ int Count = 0;
+ auto Increment = [&] { ++Count; };
+ {
+ auto G = make_scope_exit(Increment);
+ auto H = std::move(G);
+ auto I = std::move(G);
+ EXPECT_EQ(0, Count);
+ }
+ EXPECT_EQ(1, Count);
+ {
+ auto G = make_scope_exit(Increment);
+ G.release();
+ }
+ EXPECT_EQ(1, Count);
+}
+
} // end anonymous namespace
diff --git a/unittests/ADT/SmallPtrSetTest.cpp b/unittests/ADT/SmallPtrSetTest.cpp
index 0070d1cbae1b..76f9cf7fb9af 100644
--- a/unittests/ADT/SmallPtrSetTest.cpp
+++ b/unittests/ADT/SmallPtrSetTest.cpp
@@ -28,7 +28,7 @@ TEST(SmallPtrSetTest, Assignment) {
(s2 = s1).insert(&buf[2]);
// Self assign as well.
- (s2 = s2).insert(&buf[3]);
+ (s2 = static_cast<SmallPtrSet<int *, 4> &>(s2)).insert(&buf[3]);
s1 = s2;
EXPECT_EQ(4U, s1.size());
@@ -56,7 +56,7 @@ TEST(SmallPtrSetTest, GrowthTest) {
SmallPtrSet<int *, 4> s;
typedef SmallPtrSet<int *, 4>::iterator iter;
-
+
s.insert(&buf[0]);
s.insert(&buf[1]);
s.insert(&buf[2]);
@@ -299,7 +299,7 @@ TEST(SmallPtrSetTest, dereferenceAndIterate) {
// Sort. We should hit the first element just once and the final element N
// times.
- std::sort(std::begin(Found), std::end(Found));
+ llvm::sort(std::begin(Found), std::end(Found));
for (auto F = std::begin(Found), E = std::end(Found); F != E; ++F)
EXPECT_EQ(F - Found + 1, *F);
}
diff --git a/unittests/ADT/SmallSetTest.cpp b/unittests/ADT/SmallSetTest.cpp
new file mode 100644
index 000000000000..d78a72b38f8b
--- /dev/null
+++ b/unittests/ADT/SmallSetTest.cpp
@@ -0,0 +1,149 @@
+//===- llvm/unittest/ADT/SmallSetTest.cpp ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// SmallSet unit tests.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallSet.h"
+#include "gtest/gtest.h"
+#include <string>
+
+using namespace llvm;
+
+TEST(SmallSetTest, Insert) {
+
+ SmallSet<int, 4> s1;
+
+ for (int i = 0; i < 4; i++)
+ s1.insert(i);
+
+ for (int i = 0; i < 4; i++)
+ s1.insert(i);
+
+ EXPECT_EQ(4u, s1.size());
+
+ for (int i = 0; i < 4; i++)
+ EXPECT_EQ(1u, s1.count(i));
+
+ EXPECT_EQ(0u, s1.count(4));
+}
+
+TEST(SmallSetTest, Grow) {
+ SmallSet<int, 4> s1;
+
+ for (int i = 0; i < 8; i++)
+ s1.insert(i);
+
+ EXPECT_EQ(8u, s1.size());
+
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(1u, s1.count(i));
+
+ EXPECT_EQ(0u, s1.count(8));
+}
+
+TEST(SmallSetTest, Erase) {
+ SmallSet<int, 4> s1;
+
+ for (int i = 0; i < 8; i++)
+ s1.insert(i);
+
+ EXPECT_EQ(8u, s1.size());
+
+ // Remove elements one by one and check if all other elements are still there.
+ for (int i = 0; i < 8; i++) {
+ EXPECT_EQ(1u, s1.count(i));
+ EXPECT_TRUE(s1.erase(i));
+ EXPECT_EQ(0u, s1.count(i));
+ EXPECT_EQ(8u - i - 1, s1.size());
+ for (int j = i + 1; j < 8; j++)
+ EXPECT_EQ(1u, s1.count(j));
+ }
+
+ EXPECT_EQ(0u, s1.count(8));
+}
+
+TEST(SmallSetTest, IteratorInt) {
+ SmallSet<int, 4> s1;
+
+ // Test the 'small' case.
+ for (int i = 0; i < 3; i++)
+ s1.insert(i);
+
+ std::vector<int> V(s1.begin(), s1.end());
+ // Make sure the elements are in the expected order.
+ std::sort(V.begin(), V.end());
+ for (int i = 0; i < 3; i++)
+ EXPECT_EQ(i, V[i]);
+
+ // Test the 'big' case by adding a few more elements to switch to std::set
+ // internally.
+ for (int i = 3; i < 6; i++)
+ s1.insert(i);
+
+ V.assign(s1.begin(), s1.end());
+ // Make sure the elements are in the expected order.
+ std::sort(V.begin(), V.end());
+ for (int i = 0; i < 6; i++)
+ EXPECT_EQ(i, V[i]);
+}
+
+TEST(SmallSetTest, IteratorString) {
+ // Test SmallSetIterator for SmallSet with a type with non-trivial
+ // ctors/dtors.
+ SmallSet<std::string, 2> s1;
+
+ s1.insert("str 1");
+ s1.insert("str 2");
+ s1.insert("str 1");
+
+ std::vector<std::string> V(s1.begin(), s1.end());
+ std::sort(V.begin(), V.end());
+ EXPECT_EQ(2u, s1.size());
+ EXPECT_EQ("str 1", V[0]);
+ EXPECT_EQ("str 2", V[1]);
+
+ s1.insert("str 4");
+ s1.insert("str 0");
+ s1.insert("str 4");
+
+ V.assign(s1.begin(), s1.end());
+ // Make sure the elements are in the expected order.
+ std::sort(V.begin(), V.end());
+ EXPECT_EQ(4u, s1.size());
+ EXPECT_EQ("str 0", V[0]);
+ EXPECT_EQ("str 1", V[1]);
+ EXPECT_EQ("str 2", V[2]);
+ EXPECT_EQ("str 4", V[3]);
+}
+
+TEST(SmallSetTest, IteratorIncMoveCopy) {
+ // Test SmallSetIterator for SmallSet with a type with non-trivial
+ // ctors/dtors.
+ SmallSet<std::string, 2> s1;
+
+ s1.insert("str 1");
+ s1.insert("str 2");
+
+ auto Iter = s1.begin();
+ EXPECT_EQ("str 1", *Iter);
+ ++Iter;
+ EXPECT_EQ("str 2", *Iter);
+
+ s1.insert("str 4");
+ s1.insert("str 0");
+ auto Iter2 = s1.begin();
+ Iter = std::move(Iter2);
+ EXPECT_EQ("str 0", *Iter);
+
+ auto Iter3 = s1.end();
+ Iter3 = Iter2;
+ EXPECT_EQ(Iter3, Iter2);
+}
diff --git a/unittests/ADT/SparseBitVectorTest.cpp b/unittests/ADT/SparseBitVectorTest.cpp
index 6cd4de35bca7..9d6f4f1665d5 100644
--- a/unittests/ADT/SparseBitVectorTest.cpp
+++ b/unittests/ADT/SparseBitVectorTest.cpp
@@ -68,7 +68,7 @@ TEST(SparseBitVectorTest, SelfAssignment) {
Vec.set(23);
Vec.set(234);
- Vec = Vec;
+ Vec = static_cast<SparseBitVector<> &>(Vec);
EXPECT_TRUE(Vec.test(23));
EXPECT_TRUE(Vec.test(234));
diff --git a/unittests/ADT/StatisticTest.cpp b/unittests/ADT/StatisticTest.cpp
new file mode 100644
index 000000000000..17a5c7fc204a
--- /dev/null
+++ b/unittests/ADT/StatisticTest.cpp
@@ -0,0 +1,160 @@
+//===- llvm/unittest/ADT/StatisticTest.cpp - Statistic 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/Statistic.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+using namespace llvm;
+
+using OptionalStatistic = Optional<std::pair<StringRef, unsigned>>;
+
+namespace {
+#define DEBUG_TYPE "unittest"
+STATISTIC(Counter, "Counts things");
+STATISTIC(Counter2, "Counts other things");
+
+#if LLVM_ENABLE_STATS
+static void
+extractCounters(const std::vector<std::pair<StringRef, unsigned>> &Range,
+ OptionalStatistic &S1, OptionalStatistic &S2) {
+ for (const auto &S : Range) {
+ if (S.first == "Counter")
+ S1 = S;
+ if (S.first == "Counter2")
+ S2 = S;
+ }
+}
+#endif
+
+TEST(StatisticTest, Count) {
+ EnableStatistics();
+
+ Counter = 0;
+ EXPECT_EQ(Counter, 0u);
+ Counter++;
+ Counter++;
+#if LLVM_ENABLE_STATS
+ EXPECT_EQ(Counter, 2u);
+#else
+ EXPECT_EQ(Counter, 0u);
+#endif
+}
+
+TEST(StatisticTest, Assign) {
+ EnableStatistics();
+
+ Counter = 2;
+#if LLVM_ENABLE_STATS
+ EXPECT_EQ(Counter, 2u);
+#else
+ EXPECT_EQ(Counter, 0u);
+#endif
+}
+
+TEST(StatisticTest, API) {
+ EnableStatistics();
+
+ Counter = 0;
+ EXPECT_EQ(Counter, 0u);
+ Counter++;
+ Counter++;
+#if LLVM_ENABLE_STATS
+ EXPECT_EQ(Counter, 2u);
+#else
+ EXPECT_EQ(Counter, 0u);
+#endif
+
+#if LLVM_ENABLE_STATS
+ {
+ const auto Range1 = GetStatistics();
+ EXPECT_NE(Range1.begin(), Range1.end());
+ EXPECT_EQ(Range1.begin() + 1, Range1.end());
+
+ OptionalStatistic S1;
+ OptionalStatistic S2;
+ extractCounters(Range1, S1, S2);
+
+ EXPECT_EQ(S1.hasValue(), true);
+ EXPECT_EQ(S2.hasValue(), false);
+ }
+
+ // Counter2 will be registered when it's first touched.
+ Counter2++;
+
+ {
+ const auto Range = GetStatistics();
+ EXPECT_NE(Range.begin(), Range.end());
+ EXPECT_EQ(Range.begin() + 2, Range.end());
+
+ OptionalStatistic S1;
+ OptionalStatistic S2;
+ extractCounters(Range, S1, S2);
+
+ EXPECT_EQ(S1.hasValue(), true);
+ EXPECT_EQ(S2.hasValue(), true);
+
+ EXPECT_EQ(S1->first, "Counter");
+ EXPECT_EQ(S1->second, 2u);
+
+ EXPECT_EQ(S2->first, "Counter2");
+ EXPECT_EQ(S2->second, 1u);
+ }
+#else
+ Counter2++;
+ auto &Range = GetStatistics();
+ EXPECT_EQ(Range.begin(), Range.end());
+#endif
+
+#if LLVM_ENABLE_STATS
+ // Check that resetting the statistics works correctly.
+ // It should empty the list and zero the counters.
+ ResetStatistics();
+ {
+ auto &Range = GetStatistics();
+ EXPECT_EQ(Range.begin(), Range.end());
+ EXPECT_EQ(Counter, 0u);
+ EXPECT_EQ(Counter2, 0u);
+ OptionalStatistic S1;
+ OptionalStatistic S2;
+ extractCounters(Range, S1, S2);
+ EXPECT_EQ(S1.hasValue(), false);
+ EXPECT_EQ(S2.hasValue(), false);
+ }
+
+ // Now check that they successfully re-register and count.
+ Counter++;
+ Counter2++;
+
+ {
+ auto &Range = GetStatistics();
+ EXPECT_EQ(Range.begin() + 2, Range.end());
+ EXPECT_EQ(Counter, 1u);
+ EXPECT_EQ(Counter2, 1u);
+
+ OptionalStatistic S1;
+ OptionalStatistic S2;
+ extractCounters(Range, S1, S2);
+
+ EXPECT_EQ(S1.hasValue(), true);
+ EXPECT_EQ(S2.hasValue(), true);
+
+ EXPECT_EQ(S1->first, "Counter");
+ EXPECT_EQ(S1->second, 1u);
+
+ EXPECT_EQ(S2->first, "Counter2");
+ EXPECT_EQ(S2->second, 1u);
+ }
+#else
+ // No need to test the output ResetStatistics(), there's nothing to reset so
+ // we can't tell if it failed anyway.
+ ResetStatistics();
+#endif
+}
+
+} // end anonymous namespace
diff --git a/unittests/ADT/StringExtrasTest.cpp b/unittests/ADT/StringExtrasTest.cpp
index 7bcdaf62fe5a..1df200553a61 100644
--- a/unittests/ADT/StringExtrasTest.cpp
+++ b/unittests/ADT/StringExtrasTest.cpp
@@ -13,6 +13,17 @@
using namespace llvm;
+TEST(StringExtrasTest, isPrint) {
+ EXPECT_FALSE(isPrint('\0'));
+ EXPECT_FALSE(isPrint('\t'));
+ EXPECT_TRUE(isPrint('0'));
+ EXPECT_TRUE(isPrint('a'));
+ EXPECT_TRUE(isPrint('A'));
+ EXPECT_TRUE(isPrint(' '));
+ EXPECT_TRUE(isPrint('~'));
+ EXPECT_TRUE(isPrint('?'));
+}
+
TEST(StringExtrasTest, Join) {
std::vector<std::string> Items;
EXPECT_EQ("", join(Items.begin(), Items.end(), " <sep> "));
@@ -92,3 +103,17 @@ TEST(StringExtrasTest, printLowerCase) {
printLowerCase("ABCdefg01234.,&!~`'}\"", OS);
EXPECT_EQ("abcdefg01234.,&!~`'}\"", OS.str());
}
+
+TEST(StringExtrasTest, printEscapedString) {
+ std::string str;
+ raw_string_ostream OS(str);
+ printEscapedString("ABCdef123&<>\\\"'\t", OS);
+ EXPECT_EQ("ABCdef123&<>\\5C\\22'\\09", OS.str());
+}
+
+TEST(StringExtrasTest, printHTMLEscaped) {
+ std::string str;
+ raw_string_ostream OS(str);
+ printHTMLEscaped("ABCdef123&<>\"'", OS);
+ EXPECT_EQ("ABCdef123&amp;&lt;&gt;&quot;&apos;", OS.str());
+}
diff --git a/unittests/ADT/StringMapTest.cpp b/unittests/ADT/StringMapTest.cpp
index b5c63695ff35..1f5c4f031642 100644
--- a/unittests/ADT/StringMapTest.cpp
+++ b/unittests/ADT/StringMapTest.cpp
@@ -12,6 +12,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/DataTypes.h"
#include "gtest/gtest.h"
+#include <limits>
#include <tuple>
using namespace llvm;
@@ -278,7 +279,7 @@ TEST_F(StringMapTest, IterMapKeys) {
Map["D"] = 3;
auto Keys = to_vector<4>(Map.keys());
- std::sort(Keys.begin(), Keys.end());
+ llvm::sort(Keys.begin(), Keys.end());
SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"};
EXPECT_EQ(Expected, Keys);
@@ -292,7 +293,7 @@ TEST_F(StringMapTest, IterSetKeys) {
Set.insert("D");
auto Keys = to_vector<4>(Set.keys());
- std::sort(Keys.begin(), Keys.end());
+ llvm::sort(Keys.begin(), Keys.end());
SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"};
EXPECT_EQ(Expected, Keys);
@@ -492,4 +493,43 @@ TEST(StringMapCustomTest, EmplaceTest) {
EXPECT_EQ(42, Map["abcd"].Data);
}
+// Test that StringMapEntryBase can handle size_t wide sizes.
+TEST(StringMapCustomTest, StringMapEntryBaseSize) {
+ size_t LargeValue;
+
+ // Test that the entry can represent max-unsigned.
+ if (sizeof(size_t) <= sizeof(unsigned))
+ LargeValue = std::numeric_limits<unsigned>::max();
+ else
+ LargeValue = std::numeric_limits<unsigned>::max() + 1ULL;
+ StringMapEntryBase LargeBase(LargeValue);
+ EXPECT_EQ(LargeValue, LargeBase.getKeyLength());
+
+ // Test that the entry can hold at least max size_t.
+ LargeValue = std::numeric_limits<size_t>::max();
+ StringMapEntryBase LargerBase(LargeValue);
+ LargeValue = std::numeric_limits<size_t>::max();
+ EXPECT_EQ(LargeValue, LargerBase.getKeyLength());
+}
+
+// Test that StringMapEntry can handle size_t wide sizes.
+TEST(StringMapCustomTest, StringMapEntrySize) {
+ size_t LargeValue;
+
+ // Test that the entry can represent max-unsigned.
+ if (sizeof(size_t) <= sizeof(unsigned))
+ LargeValue = std::numeric_limits<unsigned>::max();
+ else
+ LargeValue = std::numeric_limits<unsigned>::max() + 1ULL;
+ StringMapEntry<int> LargeEntry(LargeValue);
+ StringRef Key = LargeEntry.getKey();
+ EXPECT_EQ(LargeValue, Key.size());
+
+ // Test that the entry can hold at least max size_t.
+ LargeValue = std::numeric_limits<size_t>::max();
+ StringMapEntry<int> LargerEntry(LargeValue);
+ Key = LargerEntry.getKey();
+ EXPECT_EQ(LargeValue, Key.size());
+}
+
} // end anonymous namespace
diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp
index 0e0b5957f025..4087d6c99a93 100644
--- a/unittests/ADT/StringRefTest.cpp
+++ b/unittests/ADT/StringRefTest.cpp
@@ -40,22 +40,22 @@ std::ostream &operator<<(std::ostream &OS,
// std::is_assignable and actually writing such an assignment.
#if !defined(_MSC_VER)
static_assert(
- !std::is_assignable<StringRef, std::string>::value,
+ !std::is_assignable<StringRef&, std::string>::value,
"Assigning from prvalue std::string");
static_assert(
- !std::is_assignable<StringRef, std::string &&>::value,
+ !std::is_assignable<StringRef&, std::string &&>::value,
"Assigning from xvalue std::string");
static_assert(
- std::is_assignable<StringRef, std::string &>::value,
+ std::is_assignable<StringRef&, std::string &>::value,
"Assigning from lvalue std::string");
static_assert(
- std::is_assignable<StringRef, const char *>::value,
+ std::is_assignable<StringRef&, const char *>::value,
"Assigning from prvalue C string");
static_assert(
- std::is_assignable<StringRef, const char * &&>::value,
+ std::is_assignable<StringRef&, const char * &&>::value,
"Assigning from xvalue C string");
static_assert(
- std::is_assignable<StringRef, const char * &>::value,
+ std::is_assignable<StringRef&, const char * &>::value,
"Assigning from lvalue C string");
#endif
@@ -181,6 +181,17 @@ TEST(StringRefTest, Split) {
Str.rsplit('l'));
EXPECT_EQ(std::make_pair(StringRef("hell"), StringRef("")),
Str.rsplit('o'));
+
+ EXPECT_EQ(std::make_pair(StringRef("he"), StringRef("o")),
+ Str.rsplit("ll"));
+ EXPECT_EQ(std::make_pair(StringRef(""), StringRef("ello")),
+ Str.rsplit("h"));
+ EXPECT_EQ(std::make_pair(StringRef("hell"), StringRef("")),
+ Str.rsplit("o"));
+ EXPECT_EQ(std::make_pair(StringRef("hello"), StringRef("")),
+ Str.rsplit("::"));
+ EXPECT_EQ(std::make_pair(StringRef("hel"), StringRef("o")),
+ Str.rsplit("l"));
}
TEST(StringRefTest, Split2) {
diff --git a/unittests/ADT/StringSwitchTest.cpp b/unittests/ADT/StringSwitchTest.cpp
index 1a51629ca574..62d3a319e7a4 100644
--- a/unittests/ADT/StringSwitchTest.cpp
+++ b/unittests/ADT/StringSwitchTest.cpp
@@ -158,7 +158,8 @@ TEST(StringSwitchTest, Cases) {
auto Translate = [](StringRef S) {
return llvm::StringSwitch<OSType>(S)
- .Cases("wind\0ws", "win32", "winnt", OSType::Windows)
+ .Cases(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt",
+ OSType::Windows)
.Cases("linux", "unix", "*nix", "posix", OSType::Linux)
.Default(OSType::Unknown);
};
@@ -184,7 +185,8 @@ TEST(StringSwitchTest, CasesLower) {
auto Translate = [](StringRef S) {
return llvm::StringSwitch<OSType>(S)
- .CasesLower("wind\0ws", "win32", "winnt", OSType::Windows)
+ .CasesLower(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt",
+ OSType::Windows)
.CasesLower("linux", "unix", "*nix", "posix", OSType::Linux)
.Default(OSType::Unknown);
};
diff --git a/unittests/ADT/TripleTest.cpp b/unittests/ADT/TripleTest.cpp
index ed4a88067b15..b6b470d34e2b 100644
--- a/unittests/ADT/TripleTest.cpp
+++ b/unittests/ADT/TripleTest.cpp
@@ -117,12 +117,29 @@ TEST(TripleTest, ParsedIDs) {
EXPECT_EQ(Triple::CNK, T.getOS());
EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+ T = Triple("ppc-bgp-linux");
+ EXPECT_EQ(Triple::ppc, T.getArch());
+ EXPECT_EQ(Triple::BGP, T.getVendor());
+ EXPECT_EQ(Triple::Linux, T.getOS());
+ EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+
+ T = Triple("ppc32-bgp-linux");
+ EXPECT_EQ(Triple::ppc, T.getArch());
+ EXPECT_EQ(Triple::BGP, T.getVendor());
+ EXPECT_EQ(Triple::Linux, T.getOS());
+ EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+
T = Triple("powerpc64-bgq-linux");
EXPECT_EQ(Triple::ppc64, T.getArch());
EXPECT_EQ(Triple::BGQ, T.getVendor());
EXPECT_EQ(Triple::Linux, T.getOS());
EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+ T = Triple("ppc64-bgq-linux");
+ EXPECT_EQ(Triple::ppc64, T.getArch());
+ EXPECT_EQ(Triple::BGQ, T.getVendor());
+ EXPECT_EQ(Triple::Linux, T.getOS());
+
T = Triple("powerpc-ibm-aix");
EXPECT_EQ(Triple::ppc, T.getArch());
EXPECT_EQ(Triple::IBM, T.getVendor());
@@ -266,12 +283,6 @@ TEST(TripleTest, ParsedIDs) {
EXPECT_EQ(Triple::AMDHSA, T.getOS());
EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
- T = Triple("amdgcn-amd-amdhsa-opencl");
- EXPECT_EQ(Triple::amdgcn, T.getArch());
- EXPECT_EQ(Triple::AMD, T.getVendor());
- EXPECT_EQ(Triple::AMDHSA, T.getOS());
- EXPECT_EQ(Triple::OpenCL, T.getEnvironment());
-
T = Triple("amdgcn-amd-amdpal");
EXPECT_EQ(Triple::amdgcn, T.getArch());
EXPECT_EQ(Triple::AMD, T.getVendor());
@@ -350,6 +361,18 @@ TEST(TripleTest, ParsedIDs) {
EXPECT_EQ(Triple::Linux, T.getOS());
EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());
+ T = Triple("arm-oe-linux-gnueabi");
+ EXPECT_EQ(Triple::arm, T.getArch());
+ EXPECT_EQ(Triple::OpenEmbedded, T.getVendor());
+ EXPECT_EQ(Triple::Linux, T.getOS());
+ EXPECT_EQ(Triple::GNUEABI, T.getEnvironment());
+
+ T = Triple("aarch64-oe-linux");
+ EXPECT_EQ(Triple::aarch64, T.getArch());
+ EXPECT_EQ(Triple::OpenEmbedded, T.getVendor());
+ EXPECT_EQ(Triple::Linux, T.getOS());
+ EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
+
T = Triple("huh");
EXPECT_EQ(Triple::UnknownArch, T.getArch());
}
@@ -523,9 +546,6 @@ TEST(TripleTest, MutateName) {
EXPECT_EQ(Triple::PC, T.getVendor());
EXPECT_EQ(Triple::Darwin, T.getOS());
EXPECT_EQ("i386-pc-darwin", T.getTriple());
-
- T.setEnvironmentName("amdopencl");
- EXPECT_EQ(Triple::AMDOpenCL, T.getEnvironment());
}
TEST(TripleTest, BitWidthPredicates) {
@@ -1027,8 +1047,13 @@ TEST(TripleTest, FileFormat) {
EXPECT_EQ(Triple::ELF, Triple("i686-pc-windows-msvc-elf").getObjectFormat());
EXPECT_EQ(Triple::ELF, Triple("i686-pc-cygwin-elf").getObjectFormat());
- EXPECT_EQ(Triple::Wasm, Triple("wasm32-unknown-unknown-wasm").getObjectFormat());
- EXPECT_EQ(Triple::Wasm, Triple("wasm64-unknown-unknown-wasm").getObjectFormat());
+ EXPECT_EQ(Triple::Wasm, Triple("wasm32-unknown-unknown").getObjectFormat());
+ EXPECT_EQ(Triple::Wasm, Triple("wasm64-unknown-unknown").getObjectFormat());
+
+ EXPECT_EQ(Triple::Wasm,
+ Triple("wasm32-unknown-unknown-wasm").getObjectFormat());
+ EXPECT_EQ(Triple::Wasm,
+ Triple("wasm64-unknown-unknown-wasm").getObjectFormat());
Triple MSVCNormalized(Triple::normalize("i686-pc-windows-msvc-elf"));
EXPECT_EQ(Triple::ELF, MSVCNormalized.getObjectFormat());
diff --git a/unittests/Analysis/AliasAnalysisTest.cpp b/unittests/Analysis/AliasAnalysisTest.cpp
index f1294eb5b7e6..0f0d44f6c780 100644
--- a/unittests/Analysis/AliasAnalysisTest.cpp
+++ b/unittests/Analysis/AliasAnalysisTest.cpp
@@ -156,7 +156,7 @@ protected:
// Build the various AA results and register them.
AC.reset(new AssumptionCache(F));
- BAR.reset(new BasicAAResult(M.getDataLayout(), TLI, *AC));
+ BAR.reset(new BasicAAResult(M.getDataLayout(), F, TLI, *AC));
AAR->addAAResult(*BAR);
return *AAR;
diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt
index 65f2aeda4180..f6ff29064be3 100644
--- a/unittests/Analysis/CMakeLists.txt
+++ b/unittests/Analysis/CMakeLists.txt
@@ -20,6 +20,7 @@ add_llvm_unittest(AnalysisTests
MemoryBuiltinsTest.cpp
MemorySSA.cpp
OrderedBasicBlockTest.cpp
+ PhiValuesTest.cpp
ProfileSummaryInfoTest.cpp
ScalarEvolutionTest.cpp
SparsePropagation.cpp
diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp
index cb8eb3756059..6279ebb9b5ae 100644
--- a/unittests/Analysis/LazyCallGraphTest.cpp
+++ b/unittests/Analysis/LazyCallGraphTest.cpp
@@ -264,7 +264,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
for (LazyCallGraph::Edge &E : A1.populate())
Nodes.push_back(E.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ("a2", Nodes[0]);
EXPECT_EQ("b2", Nodes[1]);
EXPECT_EQ("c3", Nodes[2]);
@@ -279,7 +279,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
for (LazyCallGraph::Edge &E : B1.populate())
Nodes.push_back(E.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ("b2", Nodes[0]);
EXPECT_EQ("d3", Nodes[1]);
Nodes.clear();
@@ -293,7 +293,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
for (LazyCallGraph::Edge &E : C1.populate())
Nodes.push_back(E.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ("c2", Nodes[0]);
EXPECT_EQ("d2", Nodes[1]);
Nodes.clear();
@@ -323,7 +323,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
ASSERT_EQ(1, D.size());
for (LazyCallGraph::Node &N : *D.begin())
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("d1", Nodes[0]);
EXPECT_EQ("d2", Nodes[1]);
@@ -339,7 +339,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
ASSERT_EQ(1, C.size());
for (LazyCallGraph::Node &N : *C.begin())
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("c1", Nodes[0]);
EXPECT_EQ("c2", Nodes[1]);
@@ -355,7 +355,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
ASSERT_EQ(1, B.size());
for (LazyCallGraph::Node &N : *B.begin())
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("b1", Nodes[0]);
EXPECT_EQ("b2", Nodes[1]);
@@ -373,7 +373,7 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
ASSERT_EQ(1, A.size());
for (LazyCallGraph::Node &N : *A.begin())
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("a1", Nodes[0]);
EXPECT_EQ("a2", Nodes[1]);
@@ -477,7 +477,7 @@ TEST(LazyCallGraphTest, InnerSCCFormation) {
LazyCallGraph::SCC &D = *J++;
for (LazyCallGraph::Node &N : D)
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("d1", Nodes[0]);
EXPECT_EQ("d2", Nodes[1]);
@@ -487,7 +487,7 @@ TEST(LazyCallGraphTest, InnerSCCFormation) {
LazyCallGraph::SCC &B = *J++;
for (LazyCallGraph::Node &N : B)
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("b1", Nodes[0]);
EXPECT_EQ("b2", Nodes[1]);
@@ -497,7 +497,7 @@ TEST(LazyCallGraphTest, InnerSCCFormation) {
LazyCallGraph::SCC &C = *J++;
for (LazyCallGraph::Node &N : C)
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("c1", Nodes[0]);
EXPECT_EQ("c2", Nodes[1]);
@@ -507,7 +507,7 @@ TEST(LazyCallGraphTest, InnerSCCFormation) {
LazyCallGraph::SCC &A = *J++;
for (LazyCallGraph::Node &N : A)
Nodes.push_back(N.getFunction().getName());
- std::sort(Nodes.begin(), Nodes.end());
+ llvm::sort(Nodes.begin(), Nodes.end());
EXPECT_EQ(3u, Nodes.size());
EXPECT_EQ("a1", Nodes[0]);
EXPECT_EQ("a2", Nodes[1]);
diff --git a/unittests/Analysis/MemorySSA.cpp b/unittests/Analysis/MemorySSA.cpp
index affa0e71820c..1a1675faca16 100644
--- a/unittests/Analysis/MemorySSA.cpp
+++ b/unittests/Analysis/MemorySSA.cpp
@@ -50,7 +50,7 @@ protected:
TestAnalyses(MemorySSATest &Test)
: DT(*Test.F), AC(*Test.F), AA(Test.TLI),
- BAA(Test.DL, Test.TLI, AC, &DT) {
+ BAA(Test.DL, *Test.F, Test.TLI, AC, &DT) {
AA.addAAResult(BAA);
MSSA = make_unique<MemorySSA>(*Test.F, &AA, &DT);
Walker = MSSA->getWalker();
@@ -909,3 +909,293 @@ TEST_F(MemorySSATest, Irreducible) {
Updater.insertUse(LoadAccess);
MSSA.verifyMemorySSA();
}
+
+TEST_F(MemorySSATest, MoveToBeforeLiveOnEntryInvalidatesCache) {
+ // Create:
+ // %1 = alloca i8
+ // ; 1 = MemoryDef(liveOnEntry)
+ // store i8 0, i8* %1
+ // ; 2 = MemoryDef(1)
+ // store i8 0, i8* %1
+ //
+ // ...And be sure that MSSA's caching doesn't give us `1` for the clobber of
+ // `2` after `1` is removed.
+ IRBuilder<> B(C);
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ BasicBlock *Entry = BasicBlock::Create(C, "if", F);
+ B.SetInsertPoint(Entry);
+
+ Value *A = B.CreateAlloca(B.getInt8Ty());
+ StoreInst *StoreA = B.CreateStore(B.getInt8(0), A);
+ StoreInst *StoreB = B.CreateStore(B.getInt8(0), A);
+
+ setupAnalyses();
+
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ auto *DefA = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA));
+ auto *DefB = cast<MemoryDef>(MSSA.getMemoryAccess(StoreB));
+
+ MemoryAccess *BClobber = MSSA.getWalker()->getClobberingMemoryAccess(DefB);
+ ASSERT_EQ(DefA, BClobber);
+
+ MemorySSAUpdater(&MSSA).removeMemoryAccess(DefA);
+ StoreA->eraseFromParent();
+
+ EXPECT_EQ(DefB->getDefiningAccess(), MSSA.getLiveOnEntryDef());
+
+ EXPECT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(DefB),
+ MSSA.getLiveOnEntryDef())
+ << "(DefA = " << DefA << ")";
+}
+
+TEST_F(MemorySSATest, RemovingDefInvalidatesCache) {
+ // Create:
+ // %x = alloca i8
+ // %y = alloca i8
+ // ; 1 = MemoryDef(liveOnEntry)
+ // store i8 0, i8* %x
+ // ; 2 = MemoryDef(1)
+ // store i8 0, i8* %y
+ // ; 3 = MemoryDef(2)
+ // store i8 0, i8* %x
+ //
+ // And be sure that MSSA's caching handles the removal of def `1`
+ // appropriately.
+ IRBuilder<> B(C);
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ BasicBlock *Entry = BasicBlock::Create(C, "if", F);
+ B.SetInsertPoint(Entry);
+
+ Value *X = B.CreateAlloca(B.getInt8Ty());
+ Value *Y = B.CreateAlloca(B.getInt8Ty());
+ StoreInst *StoreX1 = B.CreateStore(B.getInt8(0), X);
+ StoreInst *StoreY = B.CreateStore(B.getInt8(0), Y);
+ StoreInst *StoreX2 = B.CreateStore(B.getInt8(0), X);
+
+ setupAnalyses();
+
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ auto *DefX1 = cast<MemoryDef>(MSSA.getMemoryAccess(StoreX1));
+ auto *DefY = cast<MemoryDef>(MSSA.getMemoryAccess(StoreY));
+ auto *DefX2 = cast<MemoryDef>(MSSA.getMemoryAccess(StoreX2));
+
+ EXPECT_EQ(DefX2->getDefiningAccess(), DefY);
+ MemoryAccess *X2Clobber = MSSA.getWalker()->getClobberingMemoryAccess(DefX2);
+ ASSERT_EQ(DefX1, X2Clobber);
+
+ MemorySSAUpdater(&MSSA).removeMemoryAccess(DefX1);
+ StoreX1->eraseFromParent();
+
+ EXPECT_EQ(DefX2->getDefiningAccess(), DefY);
+ EXPECT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(DefX2),
+ MSSA.getLiveOnEntryDef())
+ << "(DefX1 = " << DefX1 << ")";
+}
+
+// Test Must alias for optimized uses
+TEST_F(MemorySSATest, TestLoadMustAlias) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ Value *AllocaA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");
+ Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");
+
+ B.CreateStore(ConstantInt::get(Int8, 1), AllocaB);
+ // Check load from LOE
+ LoadInst *LA1 = B.CreateLoad(AllocaA, "");
+ // Check load alias cached for second load
+ LoadInst *LA2 = B.CreateLoad(AllocaA, "");
+
+ B.CreateStore(ConstantInt::get(Int8, 1), AllocaA);
+ // Check load from store/def
+ LoadInst *LA3 = B.CreateLoad(AllocaA, "");
+ // Check load alias cached for second load
+ LoadInst *LA4 = B.CreateLoad(AllocaA, "");
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ unsigned I = 0;
+ for (LoadInst *V : {LA1, LA2}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), None)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+ for (LoadInst *V : {LA3, LA4}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), MustAlias)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
+
+// Test Must alias for optimized defs.
+TEST_F(MemorySSATest, TestStoreMustAlias) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ Value *AllocaA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");
+ Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");
+ StoreInst *SA1 = B.CreateStore(ConstantInt::get(Int8, 1), AllocaA);
+ StoreInst *SB1 = B.CreateStore(ConstantInt::get(Int8, 1), AllocaB);
+ StoreInst *SA2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaA);
+ StoreInst *SB2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaB);
+ StoreInst *SA3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaA);
+ StoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaB);
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ unsigned I = 0;
+ for (StoreInst *V : {SA1, SB1, SA2, SB2, SA3, SB3}) {
+ MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemDef->isOptimized(), false)
+ << "Store " << I << " is optimized from the start?";
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MayAlias)
+ << "Store " << I
+ << " has correct alias information before being optimized?";
+ if (V == SA1)
+ Walker->getClobberingMemoryAccess(V);
+ else {
+ MemoryAccess *Def = MemDef->getDefiningAccess();
+ MemoryAccess *Clob = Walker->getClobberingMemoryAccess(V);
+ EXPECT_NE(Def, Clob) << "Store " << I
+ << " has Defining Access equal to Clobbering Access";
+ }
+ EXPECT_EQ(MemDef->isOptimized(), true)
+ << "Store " << I << " was not optimized";
+ if (I == 0 || I == 1)
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), None)
+ << "Store " << I << " doesn't have the correct alias information";
+ else
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MustAlias)
+ << "Store " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
+
+// Test May alias for optimized uses.
+TEST_F(MemorySSATest, TestLoadMayAlias) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(),
+ {B.getInt8PtrTy(), B.getInt8PtrTy()},
+ false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ auto *ArgIt = F->arg_begin();
+ Argument *PointerA = &*ArgIt;
+ Argument *PointerB = &*(++ArgIt);
+ B.CreateStore(ConstantInt::get(Int8, 1), PointerB);
+ LoadInst *LA1 = B.CreateLoad(PointerA, "");
+ B.CreateStore(ConstantInt::get(Int8, 0), PointerA);
+ LoadInst *LB1 = B.CreateLoad(PointerB, "");
+ B.CreateStore(ConstantInt::get(Int8, 0), PointerA);
+ LoadInst *LA2 = B.CreateLoad(PointerA, "");
+ B.CreateStore(ConstantInt::get(Int8, 0), PointerB);
+ LoadInst *LB2 = B.CreateLoad(PointerB, "");
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ unsigned I = 0;
+ for (LoadInst *V : {LA1, LB1}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), MayAlias)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+ for (LoadInst *V : {LA2, LB2}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), MustAlias)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
+
+// Test May alias for optimized defs.
+TEST_F(MemorySSATest, TestStoreMayAlias) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(),
+ {B.getInt8PtrTy(), B.getInt8PtrTy()},
+ false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ auto *ArgIt = F->arg_begin();
+ Argument *PointerA = &*ArgIt;
+ Argument *PointerB = &*(++ArgIt);
+ Value *AllocaC = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "C");
+ // Store into arg1, must alias because it's LOE => must
+ StoreInst *SA1 = B.CreateStore(ConstantInt::get(Int8, 0), PointerA);
+ // Store into arg2, may alias store to arg1 => may
+ StoreInst *SB1 = B.CreateStore(ConstantInt::get(Int8, 1), PointerB);
+ // Store into aloca, no alias with args, so must alias LOE => must
+ StoreInst *SC1 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaC);
+ // Store into arg1, may alias store to arg2 => may
+ StoreInst *SA2 = B.CreateStore(ConstantInt::get(Int8, 3), PointerA);
+ // Store into arg2, may alias store to arg1 => may
+ StoreInst *SB2 = B.CreateStore(ConstantInt::get(Int8, 4), PointerB);
+ // Store into aloca, no alias with args, so must alias SC1 => must
+ StoreInst *SC2 = B.CreateStore(ConstantInt::get(Int8, 5), AllocaC);
+ // Store into arg2, must alias store to arg2 => must
+ StoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 6), PointerB);
+ std::initializer_list<StoreInst *> Sts = {SA1, SB1, SC1, SA2, SB2, SC2, SB3};
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ unsigned I = 0;
+ for (StoreInst *V : Sts) {
+ MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemDef->isOptimized(), false)
+ << "Store " << I << " is optimized from the start?";
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MayAlias)
+ << "Store " << I
+ << " has correct alias information before being optimized?";
+ ++I;
+ }
+
+ for (StoreInst *V : Sts)
+ Walker->getClobberingMemoryAccess(V);
+
+ I = 0;
+ for (StoreInst *V : Sts) {
+ MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemDef->isOptimized(), true)
+ << "Store " << I << " was not optimized";
+ if (I == 1 || I == 3 || I == 4)
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MayAlias)
+ << "Store " << I << " doesn't have the correct alias information";
+ else if (I == 0 || I == 2)
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), None)
+ << "Store " << I << " doesn't have the correct alias information";
+ else
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MustAlias)
+ << "Store " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
diff --git a/unittests/Analysis/PhiValuesTest.cpp b/unittests/Analysis/PhiValuesTest.cpp
new file mode 100644
index 000000000000..303b0df21f52
--- /dev/null
+++ b/unittests/Analysis/PhiValuesTest.cpp
@@ -0,0 +1,208 @@
+//===- PhiValuesTest.cpp - PhiValues 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/PhiValues.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(PhiValuesTest, SimplePhi) {
+ LLVMContext C;
+ Module M("PhiValuesTest", C);
+
+ Type *VoidTy = Type::getVoidTy(C);
+ Type *I1Ty = Type::getInt1Ty(C);
+ Type *I32Ty = Type::getInt32Ty(C);
+ Type *I32PtrTy = Type::getInt32PtrTy(C);
+
+ // Create a function with phis that do not have other phis as incoming values
+ Function *F = cast<Function>(M.getOrInsertFunction("f", FunctionType::get(VoidTy, false)));
+
+ BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
+ BasicBlock *If = BasicBlock::Create(C, "if", F);
+ BasicBlock *Else = BasicBlock::Create(C, "else", F);
+ BasicBlock *Then = BasicBlock::Create(C, "then", F);
+ BranchInst::Create(If, Else, UndefValue::get(I1Ty), Entry);
+ BranchInst::Create(Then, If);
+ BranchInst::Create(Then, Else);
+
+ Value *Val1 = new LoadInst(UndefValue::get(I32PtrTy), "val1", Entry);
+ Value *Val2 = new LoadInst(UndefValue::get(I32PtrTy), "val2", Entry);
+ Value *Val3 = new LoadInst(UndefValue::get(I32PtrTy), "val3", Entry);
+ Value *Val4 = new LoadInst(UndefValue::get(I32PtrTy), "val4", Entry);
+
+ PHINode *Phi1 = PHINode::Create(I32Ty, 2, "phi1", Then);
+ Phi1->addIncoming(Val1, If);
+ Phi1->addIncoming(Val2, Else);
+ PHINode *Phi2 = PHINode::Create(I32Ty, 2, "phi2", Then);
+ Phi2->addIncoming(Val1, If);
+ Phi2->addIncoming(Val3, Else);
+
+ PhiValues PV(*F);
+ PhiValues::ValueSet Vals;
+
+ // Check that simple usage works
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val2));
+ Vals = PV.getValuesForPhi(Phi2);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val3));
+
+ // Check that values are updated when one value is replaced with another
+ Val1->replaceAllUsesWith(Val4);
+ PV.invalidateValue(Val1);
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val4));
+ EXPECT_TRUE(Vals.count(Val2));
+ Vals = PV.getValuesForPhi(Phi2);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val4));
+ EXPECT_TRUE(Vals.count(Val3));
+
+ // Check that setting in incoming value directly updates the values
+ Phi1->setIncomingValue(0, Val1);
+ PV.invalidateValue(Phi1);
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val2));
+}
+
+TEST(PhiValuesTest, DependentPhi) {
+ LLVMContext C;
+ Module M("PhiValuesTest", C);
+
+ Type *VoidTy = Type::getVoidTy(C);
+ Type *I1Ty = Type::getInt1Ty(C);
+ Type *I32Ty = Type::getInt32Ty(C);
+ Type *I32PtrTy = Type::getInt32PtrTy(C);
+
+ // Create a function with a phi that has another phi as an incoming value
+ Function *F = cast<Function>(M.getOrInsertFunction("f", FunctionType::get(VoidTy, false)));
+
+ BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
+ BasicBlock *If1 = BasicBlock::Create(C, "if1", F);
+ BasicBlock *Else1 = BasicBlock::Create(C, "else1", F);
+ BasicBlock *Then = BasicBlock::Create(C, "then", F);
+ BasicBlock *If2 = BasicBlock::Create(C, "if2", F);
+ BasicBlock *Else2 = BasicBlock::Create(C, "else2", F);
+ BasicBlock *End = BasicBlock::Create(C, "then", F);
+ BranchInst::Create(If1, Else1, UndefValue::get(I1Ty), Entry);
+ BranchInst::Create(Then, If1);
+ BranchInst::Create(Then, Else1);
+ BranchInst::Create(If2, Else2, UndefValue::get(I1Ty), Then);
+ BranchInst::Create(End, If2);
+ BranchInst::Create(End, Else2);
+
+ Value *Val1 = new LoadInst(UndefValue::get(I32PtrTy), "val1", Entry);
+ Value *Val2 = new LoadInst(UndefValue::get(I32PtrTy), "val2", Entry);
+ Value *Val3 = new LoadInst(UndefValue::get(I32PtrTy), "val3", Entry);
+ Value *Val4 = new LoadInst(UndefValue::get(I32PtrTy), "val4", Entry);
+
+ PHINode *Phi1 = PHINode::Create(I32Ty, 2, "phi1", Then);
+ Phi1->addIncoming(Val1, If1);
+ Phi1->addIncoming(Val2, Else1);
+ PHINode *Phi2 = PHINode::Create(I32Ty, 2, "phi2", Then);
+ Phi2->addIncoming(Val2, If1);
+ Phi2->addIncoming(Val3, Else1);
+ PHINode *Phi3 = PHINode::Create(I32Ty, 2, "phi3", End);
+ Phi3->addIncoming(Phi1, If2);
+ Phi3->addIncoming(Val3, Else2);
+
+ PhiValues PV(*F);
+ PhiValues::ValueSet Vals;
+
+ // Check that simple usage works
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val2));
+ Vals = PV.getValuesForPhi(Phi2);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val2));
+ EXPECT_TRUE(Vals.count(Val3));
+ Vals = PV.getValuesForPhi(Phi3);
+ EXPECT_EQ(Vals.size(), 3u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val2));
+ EXPECT_TRUE(Vals.count(Val3));
+
+ // Check that changing an incoming value in the dependent phi changes the depending phi
+ Phi1->setIncomingValue(0, Val4);
+ PV.invalidateValue(Phi1);
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val4));
+ EXPECT_TRUE(Vals.count(Val2));
+ Vals = PV.getValuesForPhi(Phi2);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val2));
+ EXPECT_TRUE(Vals.count(Val3));
+ Vals = PV.getValuesForPhi(Phi3);
+ EXPECT_EQ(Vals.size(), 3u);
+ EXPECT_TRUE(Vals.count(Val4));
+ EXPECT_TRUE(Vals.count(Val2));
+ EXPECT_TRUE(Vals.count(Val3));
+
+ // Check that replacing an incoming phi with a value works
+ Phi3->setIncomingValue(0, Val1);
+ PV.invalidateValue(Phi3);
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val4));
+ EXPECT_TRUE(Vals.count(Val2));
+ Vals = PV.getValuesForPhi(Phi2);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val2));
+ EXPECT_TRUE(Vals.count(Val3));
+ Vals = PV.getValuesForPhi(Phi3);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val3));
+
+ // Check that adding a phi as an incoming value works
+ Phi3->setIncomingValue(1, Phi2);
+ PV.invalidateValue(Phi3);
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val4));
+ EXPECT_TRUE(Vals.count(Val2));
+ Vals = PV.getValuesForPhi(Phi2);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val2));
+ EXPECT_TRUE(Vals.count(Val3));
+ Vals = PV.getValuesForPhi(Phi3);
+ EXPECT_EQ(Vals.size(), 3u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val2));
+ EXPECT_TRUE(Vals.count(Val3));
+
+ // Check that replacing an incoming phi then deleting it works
+ Phi3->setIncomingValue(1, Val2);
+ PV.invalidateValue(Phi2);
+ Phi2->eraseFromParent();
+ PV.invalidateValue(Phi3);
+ Vals = PV.getValuesForPhi(Phi1);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val4));
+ EXPECT_TRUE(Vals.count(Val2));
+ Vals = PV.getValuesForPhi(Phi3);
+ EXPECT_EQ(Vals.size(), 2u);
+ EXPECT_TRUE(Vals.count(Val1));
+ EXPECT_TRUE(Vals.count(Val2));
+}
diff --git a/unittests/Analysis/ScalarEvolutionTest.cpp b/unittests/Analysis/ScalarEvolutionTest.cpp
index dffb68ac94f4..98fc44e4923d 100644
--- a/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1288,5 +1288,107 @@ TEST_F(ScalarEvolutionsTest, SCEVExpanderNSW) {
EXPECT_FALSE(I->hasNoSignedWrap());
}
+// Check that SCEV does not save the SCEV -> V
+// mapping of SCEV differ from V in NUW flag.
+TEST_F(ScalarEvolutionsTest, SCEVCacheNUW) {
+ /*
+ * Create the following code:
+ * func(i64 %a)
+ * entry:
+ * %s1 = add i64 %a, -1
+ * %s2 = add nuw i64 %a, -1
+ * br label %exit
+ * exit:
+ * ret %s
+ */
+
+ // Create a module.
+ Module M("SCEVCacheNUW", Context);
+
+ Type *T_int64 = Type::getInt64Ty(Context);
+
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(Context), { T_int64 }, false);
+ Function *F = cast<Function>(M.getOrInsertFunction("func", FTy));
+ Argument *Arg = &*F->arg_begin();
+ ConstantInt *C = ConstantInt::get(Context, APInt(64, -1));
+
+ BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);
+ BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);
+
+ IRBuilder<> Builder(Entry);
+ auto *S1 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));
+ auto *S2 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));
+ S2->setHasNoUnsignedWrap(true);
+ Builder.CreateBr(Exit);
+
+ Builder.SetInsertPoint(Exit);
+ auto *R = cast<Instruction>(Builder.CreateRetVoid());
+
+ ScalarEvolution SE = buildSE(*F);
+ // Get S2 first to move it to cache.
+ const SCEV *SC2 = SE.getSCEV(S2);
+ EXPECT_TRUE(isa<SCEVAddExpr>(SC2));
+ // Now get S1.
+ const SCEV *SC1 = SE.getSCEV(S1);
+ EXPECT_TRUE(isa<SCEVAddExpr>(SC1));
+ // Expand for S1, it should use S1 not S2 in spite S2
+ // first in the cache.
+ SCEVExpander Exp(SE, M.getDataLayout(), "expander");
+ auto *I = cast<Instruction>(Exp.expandCodeFor(SC1, nullptr, R));
+ EXPECT_FALSE(I->hasNoUnsignedWrap());
+}
+
+// Check that SCEV does not save the SCEV -> V
+// mapping of SCEV differ from V in NSW flag.
+TEST_F(ScalarEvolutionsTest, SCEVCacheNSW) {
+ /*
+ * Create the following code:
+ * func(i64 %a)
+ * entry:
+ * %s1 = add i64 %a, -1
+ * %s2 = add nsw i64 %a, -1
+ * br label %exit
+ * exit:
+ * ret %s
+ */
+
+ // Create a module.
+ Module M("SCEVCacheNUW", Context);
+
+ Type *T_int64 = Type::getInt64Ty(Context);
+
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(Context), { T_int64 }, false);
+ Function *F = cast<Function>(M.getOrInsertFunction("func", FTy));
+ Argument *Arg = &*F->arg_begin();
+ ConstantInt *C = ConstantInt::get(Context, APInt(64, -1));
+
+ BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);
+ BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);
+
+ IRBuilder<> Builder(Entry);
+ auto *S1 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));
+ auto *S2 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));
+ S2->setHasNoSignedWrap(true);
+ Builder.CreateBr(Exit);
+
+ Builder.SetInsertPoint(Exit);
+ auto *R = cast<Instruction>(Builder.CreateRetVoid());
+
+ ScalarEvolution SE = buildSE(*F);
+ // Get S2 first to move it to cache.
+ const SCEV *SC2 = SE.getSCEV(S2);
+ EXPECT_TRUE(isa<SCEVAddExpr>(SC2));
+ // Now get S1.
+ const SCEV *SC1 = SE.getSCEV(S1);
+ EXPECT_TRUE(isa<SCEVAddExpr>(SC1));
+ // Expand for S1, it should use S1 not S2 in spite S2
+ // first in the cache.
+ SCEVExpander Exp(SE, M.getDataLayout(), "expander");
+ auto *I = cast<Instruction>(Exp.expandCodeFor(SC1, nullptr, R));
+ EXPECT_FALSE(I->hasNoSignedWrap());
+}
+
} // end anonymous namespace
} // end namespace llvm
diff --git a/unittests/Analysis/TargetLibraryInfoTest.cpp b/unittests/Analysis/TargetLibraryInfoTest.cpp
index ef558a434c7d..ec0f89a7e500 100644
--- a/unittests/Analysis/TargetLibraryInfoTest.cpp
+++ b/unittests/Analysis/TargetLibraryInfoTest.cpp
@@ -161,8 +161,10 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare i32 @ffsl(i64)\n"
"declare i32 @ffsll(i64)\n"
"declare i32 @fgetc(%struct*)\n"
+ "declare i32 @fgetc_unlocked(%struct*)\n"
"declare i32 @fgetpos(%struct*, i64*)\n"
"declare i8* @fgets(i8*, i32, %struct*)\n"
+ "declare i8* @fgets_unlocked(i8*, i32, %struct*)\n"
"declare i32 @fileno(%struct*)\n"
"declare void @flockfile(%struct*)\n"
"declare double @floor(double)\n"
@@ -182,7 +184,9 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare x86_fp80 @fmodl(x86_fp80, x86_fp80)\n"
"declare i32 @fprintf(%struct*, i8*, ...)\n"
"declare i32 @fputc(i32, %struct*)\n"
+ "declare i32 @fputc_unlocked(i32, %struct*)\n"
"declare i64 @fread(i8*, i64, i64, %struct*)\n"
+ "declare i64 @fread_unlocked(i8*, i64, i64, %struct*)\n"
"declare void @free(i8*)\n"
"declare double @frexp(double, i32*)\n"
"declare float @frexpf(float, i32*)\n"
@@ -199,6 +203,7 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare i32 @getc(%struct*)\n"
"declare i32 @getc_unlocked(%struct*)\n"
"declare i32 @getchar()\n"
+ "declare i32 @getchar_unlocked()\n"
"declare i8* @getenv(i8*)\n"
"declare i32 @getitimer(i32, %struct*)\n"
"declare i32 @getlogin_r(i8*, i64)\n"
@@ -250,7 +255,9 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare x86_fp80 @powl(x86_fp80, x86_fp80)\n"
"declare i32 @printf(i8*, ...)\n"
"declare i32 @putc(i32, %struct*)\n"
+ "declare i32 @putc_unlocked(i32, %struct*)\n"
"declare i32 @putchar(i32)\n"
+ "declare i32 @putchar_unlocked(i32)\n"
"declare i32 @puts(i8*)\n"
"declare void @qsort(i8*, i64, i64, i32 (i8*, i8*)*)\n"
"declare i64 @readlink(i8*, i8*, i64)\n"
@@ -347,8 +354,10 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare %struct* @fdopen(i32, i8*)\n"
"declare %struct* @fopen(i8*, i8*)\n"
"declare i32 @fputs(i8*, %struct*)\n"
+ "declare i32 @fputs_unlocked(i8*, %struct*)\n"
"declare i32 @fstat(i32, %struct*)\n"
"declare i64 @fwrite(i8*, i64, i64, %struct*)\n"
+ "declare i64 @fwrite_unlocked(i8*, i64, i64, %struct*)\n"
"declare i32 @lchown(i8*, i32, i32)\n"
"declare i32 @lstat(i8*, %struct*)\n"
"declare i64 @mktime(%struct*)\n"
@@ -386,20 +395,32 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare void @_ZdaPv(i8*)\n"
"declare void @_ZdaPvRKSt9nothrow_t(i8*, %struct*)\n"
+ "declare void @_ZdaPvSt11align_val_t(i8*, i64)\n"
+ "declare void @_ZdaPvSt11align_val_tRKSt9nothrow_t(i8*, i64, %struct*)\n"
"declare void @_ZdaPvj(i8*, i32)\n"
"declare void @_ZdaPvm(i8*, i64)\n"
"declare void @_ZdlPv(i8*)\n"
"declare void @_ZdlPvRKSt9nothrow_t(i8*, %struct*)\n"
+ "declare void @_ZdlPvSt11align_val_t(i8*, i64)\n"
+ "declare void @_ZdlPvSt11align_val_tRKSt9nothrow_t(i8*, i64, %struct*)\n"
"declare void @_ZdlPvj(i8*, i32)\n"
"declare void @_ZdlPvm(i8*, i64)\n"
"declare i8* @_Znaj(i32)\n"
"declare i8* @_ZnajRKSt9nothrow_t(i32, %struct*)\n"
+ "declare i8* @_ZnajSt11align_val_t(i32, i32)\n"
+ "declare i8* @_ZnajSt11align_val_tRKSt9nothrow_t(i32, i32, %struct*)\n"
"declare i8* @_Znam(i64)\n"
"declare i8* @_ZnamRKSt9nothrow_t(i64, %struct*)\n"
+ "declare i8* @_ZnamSt11align_val_t(i64, i64)\n"
+ "declare i8* @_ZnamSt11align_val_tRKSt9nothrow_t(i64, i64, %struct*)\n"
"declare i8* @_Znwj(i32)\n"
"declare i8* @_ZnwjRKSt9nothrow_t(i32, %struct*)\n"
+ "declare i8* @_ZnwjSt11align_val_t(i32, i32)\n"
+ "declare i8* @_ZnwjSt11align_val_tRKSt9nothrow_t(i32, i32, %struct*)\n"
"declare i8* @_Znwm(i64)\n"
"declare i8* @_ZnwmRKSt9nothrow_t(i64, %struct*)\n"
+ "declare i8* @_ZnwmSt11align_val_t(i64, i64)\n"
+ "declare i8* @_ZnwmSt11align_val_tRKSt9nothrow_t(i64, i64, %struct*)\n"
"declare void @\"??3@YAXPEAX@Z\"(i8*)\n"
"declare void @\"??3@YAXPEAXAEBUnothrow_t@std@@@Z\"(i8*, %struct*)\n"
diff --git a/unittests/Analysis/ValueLatticeTest.cpp b/unittests/Analysis/ValueLatticeTest.cpp
index 3e4102564144..b0b379760a53 100644
--- a/unittests/Analysis/ValueLatticeTest.cpp
+++ b/unittests/Analysis/ValueLatticeTest.cpp
@@ -50,20 +50,26 @@ TEST_F(ValueLatticeTest, MergeIn) {
// Merge to lattice values with equal integer constant.
auto LV1 = ValueLatticeElement::get(C1);
- LV1.mergeIn(ValueLatticeElement::get(C1), M.getDataLayout());
+ EXPECT_FALSE(LV1.mergeIn(ValueLatticeElement::get(C1), M.getDataLayout()));
EXPECT_TRUE(LV1.isConstantRange());
EXPECT_EQ(LV1.asConstantInteger().getValue().getLimitedValue(), 1U);
// Merge LV1 with different integer constant.
- LV1.mergeIn(ValueLatticeElement::get(ConstantInt::get(I32Ty, 99)),
- M.getDataLayout());
+ EXPECT_TRUE(LV1.mergeIn(ValueLatticeElement::get(ConstantInt::get(I32Ty, 99)),
+ M.getDataLayout()));
+ EXPECT_TRUE(LV1.isConstantRange());
+ EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 1U);
+ EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 100U);
+
+ // Merge constant range with same constant range.
+ EXPECT_FALSE(LV1.mergeIn(LV1, M.getDataLayout()));
EXPECT_TRUE(LV1.isConstantRange());
EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 1U);
EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 100U);
// Merge LV1 in undefined value.
ValueLatticeElement LV2;
- LV2.mergeIn(LV1, M.getDataLayout());
+ EXPECT_TRUE(LV2.mergeIn(LV1, M.getDataLayout()));
EXPECT_TRUE(LV1.isConstantRange());
EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 1U);
EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 100U);
@@ -71,77 +77,110 @@ TEST_F(ValueLatticeTest, MergeIn) {
EXPECT_EQ(LV2.getConstantRange().getLower().getLimitedValue(), 1U);
EXPECT_EQ(LV2.getConstantRange().getUpper().getLimitedValue(), 100U);
- // Merge with overdefined.
- LV1.mergeIn(ValueLatticeElement::getOverdefined(), M.getDataLayout());
+ // Merge LV1 with overdefined.
+ EXPECT_TRUE(
+ LV1.mergeIn(ValueLatticeElement::getOverdefined(), M.getDataLayout()));
+ EXPECT_TRUE(LV1.isOverdefined());
+
+ // Merge overdefined with overdefined.
+ EXPECT_FALSE(
+ LV1.mergeIn(ValueLatticeElement::getOverdefined(), M.getDataLayout()));
EXPECT_TRUE(LV1.isOverdefined());
}
-TEST_F(ValueLatticeTest, satisfiesPredicateIntegers) {
- auto I32Ty = IntegerType::get(Context, 32);
+TEST_F(ValueLatticeTest, getCompareIntegers) {
+ auto *I32Ty = IntegerType::get(Context, 32);
+ auto *I1Ty = IntegerType::get(Context, 1);
auto *C1 = ConstantInt::get(I32Ty, 1);
auto LV1 = ValueLatticeElement::get(C1);
- // Check satisfiesPredicate for equal integer constants.
- EXPECT_TRUE(LV1.satisfiesPredicate(CmpInst::ICMP_EQ, LV1));
- EXPECT_TRUE(LV1.satisfiesPredicate(CmpInst::ICMP_SGE, LV1));
- EXPECT_TRUE(LV1.satisfiesPredicate(CmpInst::ICMP_SLE, LV1));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::ICMP_NE, LV1));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::ICMP_SLT, LV1));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::ICMP_SGT, LV1));
+ // Check getCompare for equal integer constants.
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_EQ, I1Ty, LV1)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGE, I1Ty, LV1)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLE, I1Ty, LV1)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_NE, I1Ty, LV1)->isZeroValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLT, I1Ty, LV1)->isZeroValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGT, I1Ty, LV1)->isZeroValue());
auto LV2 =
ValueLatticeElement::getRange({APInt(32, 10, true), APInt(32, 20, true)});
- // Check satisfiesPredicate with distinct integer ranges.
- EXPECT_TRUE(LV1.satisfiesPredicate(CmpInst::ICMP_SLT, LV2));
- EXPECT_TRUE(LV1.satisfiesPredicate(CmpInst::ICMP_SLE, LV2));
- EXPECT_TRUE(LV1.satisfiesPredicate(CmpInst::ICMP_NE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::ICMP_EQ, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::ICMP_SGE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::ICMP_SGT, LV2));
+ // Check getCompare with distinct integer ranges.
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLT, I1Ty, LV2)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLE, I1Ty, LV2)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_NE, I1Ty, LV2)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_EQ, I1Ty, LV2)->isZeroValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGE, I1Ty, LV2)->isZeroValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGT, I1Ty, LV2)->isZeroValue());
auto LV3 =
ValueLatticeElement::getRange({APInt(32, 15, true), APInt(32, 19, true)});
- // Check satisfiesPredicate with a subset integer ranges.
- EXPECT_FALSE(LV2.satisfiesPredicate(CmpInst::ICMP_SLT, LV3));
- EXPECT_FALSE(LV2.satisfiesPredicate(CmpInst::ICMP_SLE, LV3));
- EXPECT_FALSE(LV2.satisfiesPredicate(CmpInst::ICMP_NE, LV3));
- EXPECT_FALSE(LV2.satisfiesPredicate(CmpInst::ICMP_EQ, LV3));
- EXPECT_FALSE(LV2.satisfiesPredicate(CmpInst::ICMP_SGE, LV3));
- EXPECT_FALSE(LV2.satisfiesPredicate(CmpInst::ICMP_SGT, LV3));
+ // Check getCompare with a subset integer ranges.
+ EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SLT, I1Ty, LV3), nullptr);
+ EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SLE, I1Ty, LV3), nullptr);
+ EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_NE, I1Ty, LV3), nullptr);
+ EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_EQ, I1Ty, LV3), nullptr);
+ EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SGE, I1Ty, LV3), nullptr);
+ EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SGT, I1Ty, LV3), nullptr);
auto LV4 =
ValueLatticeElement::getRange({APInt(32, 15, true), APInt(32, 25, true)});
- // Check satisfiesPredicate with overlapping integer ranges.
- EXPECT_FALSE(LV3.satisfiesPredicate(CmpInst::ICMP_SLT, LV4));
- EXPECT_FALSE(LV3.satisfiesPredicate(CmpInst::ICMP_SLE, LV4));
- EXPECT_FALSE(LV3.satisfiesPredicate(CmpInst::ICMP_NE, LV4));
- EXPECT_FALSE(LV3.satisfiesPredicate(CmpInst::ICMP_EQ, LV4));
- EXPECT_FALSE(LV3.satisfiesPredicate(CmpInst::ICMP_SGE, LV4));
- EXPECT_FALSE(LV3.satisfiesPredicate(CmpInst::ICMP_SGT, LV4));
+ // Check getCompare with overlapping integer ranges.
+ EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SLT, I1Ty, LV4), nullptr);
+ EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SLE, I1Ty, LV4), nullptr);
+ EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_NE, I1Ty, LV4), nullptr);
+ EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_EQ, I1Ty, LV4), nullptr);
+ EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SGE, I1Ty, LV4), nullptr);
+ EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SGT, I1Ty, LV4), nullptr);
}
-TEST_F(ValueLatticeTest, satisfiesPredicateFloat) {
- auto FloatTy = IntegerType::getFloatTy(Context);
+TEST_F(ValueLatticeTest, getCompareFloat) {
+ auto *FloatTy = IntegerType::getFloatTy(Context);
+ auto *I1Ty = IntegerType::get(Context, 1);
auto *C1 = ConstantFP::get(FloatTy, 1.0);
auto LV1 = ValueLatticeElement::get(C1);
auto LV2 = ValueLatticeElement::get(C1);
- // Check satisfiesPredicate for equal floating point constants.
- EXPECT_TRUE(LV1.satisfiesPredicate(CmpInst::FCMP_OEQ, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OGE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OLE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_ONE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OLT, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OGT, LV2));
-
- LV1.mergeIn(ValueLatticeElement::get(ConstantFP::get(FloatTy, 2.2)),
- M.getDataLayout());
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OEQ, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OGE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OLE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_ONE, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OLT, LV2));
- EXPECT_FALSE(LV1.satisfiesPredicate(CmpInst::FCMP_OGT, LV2));
+ // Check getCompare for equal floating point constants.
+ EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OEQ, I1Ty, LV2)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OGE, I1Ty, LV2)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OLE, I1Ty, LV2)->isOneValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_ONE, I1Ty, LV2)->isZeroValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OLT, I1Ty, LV2)->isZeroValue());
+ EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OGT, I1Ty, LV2)->isZeroValue());
+
+ EXPECT_TRUE(
+ LV1.mergeIn(ValueLatticeElement::get(ConstantFP::get(FloatTy, 2.2)),
+ M.getDataLayout()));
+ EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OEQ, I1Ty, LV2), nullptr);
+ EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OGE, I1Ty, LV2), nullptr);
+ EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OLE, I1Ty, LV2), nullptr);
+ EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_ONE, I1Ty, LV2), nullptr);
+ EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OLT, I1Ty, LV2), nullptr);
+ EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OGT, I1Ty, LV2), nullptr);
+}
+
+TEST_F(ValueLatticeTest, getCompareUndef) {
+ auto *I32Ty = IntegerType::get(Context, 32);
+ auto *I1Ty = IntegerType::get(Context, 1);
+
+ auto LV1 = ValueLatticeElement::get(UndefValue::get(I32Ty));
+ auto LV2 =
+ ValueLatticeElement::getRange({APInt(32, 10, true), APInt(32, 20, true)});
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SLT, I1Ty, LV2)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SLE, I1Ty, LV2)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_NE, I1Ty, LV2)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_EQ, I1Ty, LV2)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SGE, I1Ty, LV2)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SGT, I1Ty, LV2)));
+
+ auto *FloatTy = IntegerType::getFloatTy(Context);
+ auto LV3 = ValueLatticeElement::get(ConstantFP::get(FloatTy, 1.0));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OEQ, I1Ty, LV3)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OGE, I1Ty, LV3)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OLE, I1Ty, LV3)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_ONE, I1Ty, LV3)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OLT, I1Ty, LV3)));
+ EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OGT, I1Ty, LV3)));
}
} // end anonymous namespace
diff --git a/unittests/BinaryFormat/CMakeLists.txt b/unittests/BinaryFormat/CMakeLists.txt
index 631936795b6c..95c672e35be8 100644
--- a/unittests/BinaryFormat/CMakeLists.txt
+++ b/unittests/BinaryFormat/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(BinaryFormatTests
DwarfTest.cpp
+ MachOTest.cpp
TestFileMagic.cpp
)
diff --git a/unittests/BinaryFormat/DwarfTest.cpp b/unittests/BinaryFormat/DwarfTest.cpp
index f24e029beef2..08a731f6174d 100644
--- a/unittests/BinaryFormat/DwarfTest.cpp
+++ b/unittests/BinaryFormat/DwarfTest.cpp
@@ -9,6 +9,7 @@
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatVariadic.h"
#include "gtest/gtest.h"
using namespace llvm;
@@ -139,4 +140,67 @@ TEST(DwarfTest, getVirtuality) {
EXPECT_EQ(DW_VIRTUALITY_invalid, getVirtuality("something else"));
}
+TEST(DwarfTest, FixedFormSizes) {
+ Optional<uint8_t> RefSize;
+ Optional<uint8_t> AddrSize;
+
+ // Test 32 bit DWARF version 2 with 4 byte addresses.
+ FormParams Params_2_4_32 = {2, 4, DWARF32};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_4_32);
+ AddrSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_4_32);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_TRUE(AddrSize.hasValue());
+ EXPECT_EQ(*RefSize, *AddrSize);
+
+ // Test 32 bit DWARF version 2 with 8 byte addresses.
+ FormParams Params_2_8_32 = {2, 8, DWARF32};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_8_32);
+ AddrSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_8_32);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_TRUE(AddrSize.hasValue());
+ EXPECT_EQ(*RefSize, *AddrSize);
+
+ // DW_FORM_ref_addr is 4 bytes in DWARF 32 in DWARF version 3 and beyond.
+ FormParams Params_3_4_32 = {3, 4, DWARF32};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_3_4_32);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_EQ(*RefSize, 4);
+
+ FormParams Params_4_4_32 = {4, 4, DWARF32};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_4_4_32);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_EQ(*RefSize, 4);
+
+ FormParams Params_5_4_32 = {5, 4, DWARF32};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_5_4_32);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_EQ(*RefSize, 4);
+
+ // DW_FORM_ref_addr is 8 bytes in DWARF 64 in DWARF version 3 and beyond.
+ FormParams Params_3_8_64 = {3, 8, DWARF64};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_3_8_64);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_EQ(*RefSize, 8);
+
+ FormParams Params_4_8_64 = {4, 8, DWARF64};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_4_8_64);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_EQ(*RefSize, 8);
+
+ FormParams Params_5_8_64 = {5, 8, DWARF64};
+ RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_5_8_64);
+ EXPECT_TRUE(RefSize.hasValue());
+ EXPECT_EQ(*RefSize, 8);
+}
+
+TEST(DwarfTest, format_provider) {
+ EXPECT_EQ("DW_AT_name", formatv("{0}", DW_AT_name).str());
+ EXPECT_EQ("DW_AT_unknown_3fff", formatv("{0}", DW_AT_hi_user).str());
+ EXPECT_EQ("DW_FORM_addr", formatv("{0}", DW_FORM_addr).str());
+ EXPECT_EQ("DW_FORM_unknown_1f00", formatv("{0}", DW_FORM_lo_user).str());
+ EXPECT_EQ("DW_IDX_compile_unit", formatv("{0}", DW_IDX_compile_unit).str());
+ EXPECT_EQ("DW_IDX_unknown_3fff", formatv("{0}", DW_IDX_hi_user).str());
+ EXPECT_EQ("DW_TAG_compile_unit", formatv("{0}", DW_TAG_compile_unit).str());
+ EXPECT_EQ("DW_TAG_unknown_ffff", formatv("{0}", DW_TAG_hi_user).str());
+}
} // end namespace
diff --git a/unittests/BinaryFormat/MachOTest.cpp b/unittests/BinaryFormat/MachOTest.cpp
new file mode 100644
index 000000000000..da8e9e8c6901
--- /dev/null
+++ b/unittests/BinaryFormat/MachOTest.cpp
@@ -0,0 +1,47 @@
+//===- unittest/BinaryFormat/MachOTest.cpp - MachO support tests ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/MachO.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+
+TEST(MachOTest, UnalignedLC) {
+ unsigned char Valid32BitMachO[] = {
+ 0xCE, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00,
+ 0x85, 0x80, 0x21, 0x01, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x50, 0x41, 0x47, 0x45, 0x5A, 0x45, 0x52, 0x4F, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x4C, 0x49,
+ 0x4E, 0x4B, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+ 0x8C, 0x0B, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ mach_header *Header =
+ reinterpret_cast<mach_header *>(Valid32BitMachO);
+ if (!sys::IsLittleEndianHost)
+ swapStruct(*Header);
+ ASSERT_EQ(Header->magic, MH_MAGIC);
+ unsigned char *Current = Valid32BitMachO + sizeof(mach_header);
+ unsigned char *BufferEnd =
+ Valid32BitMachO + sizeof(mach_header) + Header->sizeofcmds;
+ while (Current < BufferEnd) {
+ macho_load_command *LC =
+ reinterpret_cast<macho_load_command *>(Current);
+ if (!sys::IsLittleEndianHost)
+ swapStruct(LC->load_command_data);
+ ASSERT_EQ(LC->load_command_data.cmd, LC_SEGMENT);
+ Current += LC->load_command_data.cmdsize;
+ }
+}
diff --git a/unittests/BinaryFormat/TestFileMagic.cpp b/unittests/BinaryFormat/TestFileMagic.cpp
index ca4ca9a27281..5f132ba4144d 100644
--- a/unittests/BinaryFormat/TestFileMagic.cpp
+++ b/unittests/BinaryFormat/TestFileMagic.cpp
@@ -81,6 +81,8 @@ const char windows_resource[] =
const char macho_dynamically_linked_shared_lib_stub[] =
"\xfe\xed\xfa\xce........\x00\x00\x00\x09............";
const char ms_dos_stub_broken[] = "\x4d\x5a\x20\x20";
+const char pdb[] = "Microsoft C/C++ MSF 7.00\r\n\x1a"
+ "DS\x00\x00\x00";
TEST_F(MagicTest, Magic) {
struct type {
@@ -110,6 +112,7 @@ TEST_F(MagicTest, Magic) {
DEFINE(macho_dsym_companion),
DEFINE(macho_kext_bundle),
DEFINE(windows_resource),
+ DEFINE(pdb),
{"ms_dos_stub_broken", ms_dos_stub_broken, sizeof(ms_dos_stub_broken),
file_magic::unknown},
#undef DEFINE
diff --git a/unittests/Bitcode/BitReaderTest.cpp b/unittests/Bitcode/BitReaderTest.cpp
index 5efb7faf508d..3b4600718b53 100644
--- a/unittests/Bitcode/BitReaderTest.cpp
+++ b/unittests/Bitcode/BitReaderTest.cpp
@@ -44,7 +44,7 @@ std::unique_ptr<Module> parseAssembly(LLVMContext &Context,
static void writeModuleToBuffer(std::unique_ptr<Module> Mod,
SmallVectorImpl<char> &Buffer) {
raw_svector_ostream OS(Buffer);
- WriteBitcodeToFile(Mod.get(), OS);
+ WriteBitcodeToFile(*Mod, OS);
}
static std::unique_ptr<Module> getLazyModuleFromAssembly(LLVMContext &Context,
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index 94aca0566256..f9166509b4ef 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -8,9 +8,11 @@ endfunction()
add_subdirectory(ADT)
add_subdirectory(Analysis)
add_subdirectory(AsmParser)
+add_subdirectory(BinaryFormat)
add_subdirectory(Bitcode)
add_subdirectory(CodeGen)
add_subdirectory(DebugInfo)
+add_subdirectory(Demangle)
add_subdirectory(ExecutionEngine)
add_subdirectory(FuzzMutate)
add_subdirectory(IR)
@@ -19,9 +21,9 @@ add_subdirectory(Linker)
add_subdirectory(MC)
add_subdirectory(MI)
add_subdirectory(Object)
-add_subdirectory(BinaryFormat)
add_subdirectory(ObjectYAML)
add_subdirectory(Option)
+add_subdirectory(Passes)
add_subdirectory(ProfileData)
add_subdirectory(Support)
add_subdirectory(Target)
diff --git a/unittests/CodeGen/CMakeLists.txt b/unittests/CodeGen/CMakeLists.txt
index 05b72ce05b7b..3a9112740e0a 100644
--- a/unittests/CodeGen/CMakeLists.txt
+++ b/unittests/CodeGen/CMakeLists.txt
@@ -8,7 +8,7 @@ set(LLVM_LINK_COMPONENTS
Target
)
-set(CodeGenSources
+add_llvm_unittest(CodeGenTests
DIEHashTest.cpp
LowLevelTypeTest.cpp
MachineInstrBundleIteratorTest.cpp
@@ -17,8 +17,4 @@ set(CodeGenSources
ScalableVectorMVTsTest.cpp
)
-add_llvm_unittest(CodeGenTests
- ${CodeGenSources}
- )
-
add_subdirectory(GlobalISel)
diff --git a/unittests/CodeGen/GlobalISel/CMakeLists.txt b/unittests/CodeGen/GlobalISel/CMakeLists.txt
index 075bb44bc330..85f5dcd6d040 100644
--- a/unittests/CodeGen/GlobalISel/CMakeLists.txt
+++ b/unittests/CodeGen/GlobalISel/CMakeLists.txt
@@ -1,8 +1,15 @@
set(LLVM_LINK_COMPONENTS
- GlobalISel
+ ${LLVM_TARGETS_TO_BUILD}
CodeGen
+ Core
+ GlobalISel
+ MC
+ MIRParser
+ Support
+ Target
)
add_llvm_unittest(GlobalISelTests
LegalizerInfoTest.cpp
+ PatternMatchTest.cpp
)
diff --git a/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp b/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp
index ca7b47e87eda..42371e63da9d 100644
--- a/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp
+++ b/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp
@@ -12,22 +12,24 @@
#include "gtest/gtest.h"
using namespace llvm;
+using namespace LegalizeActions;
// Define a couple of pretty printers to help debugging when things go wrong.
namespace llvm {
std::ostream &
-operator<<(std::ostream &OS, const llvm::LegalizerInfo::LegalizeAction Act) {
+operator<<(std::ostream &OS, const LegalizeAction Act) {
switch (Act) {
- case LegalizerInfo::Lower: OS << "Lower"; break;
- case LegalizerInfo::Legal: OS << "Legal"; break;
- case LegalizerInfo::NarrowScalar: OS << "NarrowScalar"; break;
- case LegalizerInfo::WidenScalar: OS << "WidenScalar"; break;
- case LegalizerInfo::FewerElements: OS << "FewerElements"; break;
- case LegalizerInfo::MoreElements: OS << "MoreElements"; break;
- case LegalizerInfo::Libcall: OS << "Libcall"; break;
- case LegalizerInfo::Custom: OS << "Custom"; break;
- case LegalizerInfo::Unsupported: OS << "Unsupported"; break;
- case LegalizerInfo::NotFound: OS << "NotFound";
+ case Lower: OS << "Lower"; break;
+ case Legal: OS << "Legal"; break;
+ case NarrowScalar: OS << "NarrowScalar"; break;
+ case WidenScalar: OS << "WidenScalar"; break;
+ case FewerElements: OS << "FewerElements"; break;
+ case MoreElements: OS << "MoreElements"; break;
+ case Libcall: OS << "Libcall"; break;
+ case Custom: OS << "Custom"; break;
+ case Unsupported: OS << "Unsupported"; break;
+ case NotFound: OS << "NotFound"; break;
+ case UseLegacyRules: OS << "UseLegacyRules"; break;
}
return OS;
}
@@ -51,7 +53,7 @@ TEST(LegalizerInfoTest, ScalarRISC) {
// Typical RISCy set of operations based on AArch64.
for (unsigned Op : {G_ADD, G_SUB}) {
for (unsigned Size : {32, 64})
- L.setAction({Op, 0, LLT::scalar(Size)}, LegalizerInfo::Legal);
+ L.setAction({Op, 0, LLT::scalar(Size)}, Legal);
L.setLegalizeScalarToDifferentSizeStrategy(
Op, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest);
}
@@ -60,29 +62,29 @@ TEST(LegalizerInfoTest, ScalarRISC) {
for (unsigned opcode : {G_ADD, G_SUB}) {
// Check we infer the correct types and actually do what we're told.
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(8)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(16)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(32)}),
- std::make_pair(LegalizerInfo::Legal, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(64)}),
- std::make_pair(LegalizerInfo::Legal, LLT::scalar(64)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(8)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(16)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(32)}}),
+ LegalizeActionStep(Legal, 0, LLT{}));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(64)}}),
+ LegalizeActionStep(Legal, 0, LLT{}));
// Make sure the default for over-sized types applies.
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(128)}),
- std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(128)}}),
+ LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64)));
// Make sure we also handle unusual sizes
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(1)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(31)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(33)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(64)));
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(63)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(64)));
- ASSERT_EQ(L.getAction({opcode, LLT::scalar(65)}),
- std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(1)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(31)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(33)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(64)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(63)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(64)));
+ ASSERT_EQ(L.getAction({opcode, {LLT::scalar(65)}}),
+ LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64)));
}
}
@@ -90,35 +92,35 @@ TEST(LegalizerInfoTest, VectorRISC) {
using namespace TargetOpcode;
LegalizerInfo L;
// Typical RISCy set of operations based on ARM.
- L.setAction({G_ADD, LLT::vector(8, 8)}, LegalizerInfo::Legal);
- L.setAction({G_ADD, LLT::vector(16, 8)}, LegalizerInfo::Legal);
- L.setAction({G_ADD, LLT::vector(4, 16)}, LegalizerInfo::Legal);
- L.setAction({G_ADD, LLT::vector(8, 16)}, LegalizerInfo::Legal);
- L.setAction({G_ADD, LLT::vector(2, 32)}, LegalizerInfo::Legal);
- L.setAction({G_ADD, LLT::vector(4, 32)}, LegalizerInfo::Legal);
+ L.setAction({G_ADD, LLT::vector(8, 8)}, Legal);
+ L.setAction({G_ADD, LLT::vector(16, 8)}, Legal);
+ L.setAction({G_ADD, LLT::vector(4, 16)}, Legal);
+ L.setAction({G_ADD, LLT::vector(8, 16)}, Legal);
+ L.setAction({G_ADD, LLT::vector(2, 32)}, Legal);
+ L.setAction({G_ADD, LLT::vector(4, 32)}, Legal);
L.setLegalizeVectorElementToDifferentSizeStrategy(
G_ADD, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
- L.setAction({G_ADD, 0, LLT::scalar(32)}, LegalizerInfo::Legal);
+ L.setAction({G_ADD, 0, LLT::scalar(32)}, Legal);
L.computeTables();
// Check we infer the correct types and actually do what we're told for some
// simple cases.
- ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 8)}),
- std::make_pair(LegalizerInfo::Legal, LLT::vector(8, 8)));
- ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 7)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::vector(8, 8)));
- ASSERT_EQ(L.getAction({G_ADD, LLT::vector(2, 8)}),
- std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8)));
- ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 32)}),
- std::make_pair(LegalizerInfo::FewerElements, LLT::vector(4, 32)));
+ ASSERT_EQ(L.getAction({G_ADD, {LLT::vector(8, 8)}}),
+ LegalizeActionStep(Legal, 0, LLT{}));
+ ASSERT_EQ(L.getAction({G_ADD, {LLT::vector(8, 7)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::vector(8, 8)));
+ ASSERT_EQ(L.getAction({G_ADD, {LLT::vector(2, 8)}}),
+ LegalizeActionStep(MoreElements, 0, LLT::vector(8, 8)));
+ ASSERT_EQ(L.getAction({G_ADD, {LLT::vector(8, 32)}}),
+ LegalizeActionStep(FewerElements, 0, LLT::vector(4, 32)));
// Check a few non-power-of-2 sizes:
- ASSERT_EQ(L.getAction({G_ADD, LLT::vector(3, 3)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::vector(3, 8)));
- ASSERT_EQ(L.getAction({G_ADD, LLT::vector(3, 8)}),
- std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8)));
+ ASSERT_EQ(L.getAction({G_ADD, {LLT::vector(3, 3)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::vector(3, 8)));
+ ASSERT_EQ(L.getAction({G_ADD, {LLT::vector(3, 8)}}),
+ LegalizeActionStep(MoreElements, 0, LLT::vector(8, 8)));
}
TEST(LegalizerInfoTest, MultipleTypes) {
@@ -128,8 +130,8 @@ TEST(LegalizerInfoTest, MultipleTypes) {
LLT s64 = LLT::scalar(64);
// Typical RISCy set of operations based on AArch64.
- L.setAction({G_PTRTOINT, 0, s64}, LegalizerInfo::Legal);
- L.setAction({G_PTRTOINT, 1, p0}, LegalizerInfo::Legal);
+ L.setAction({G_PTRTOINT, 0, s64}, Legal);
+ L.setAction({G_PTRTOINT, 1, p0}, Legal);
L.setLegalizeScalarToDifferentSizeStrategy(
G_PTRTOINT, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest);
@@ -137,15 +139,16 @@ TEST(LegalizerInfoTest, MultipleTypes) {
L.computeTables();
// Check we infer the correct types and actually do what we're told.
- ASSERT_EQ(L.getAction({G_PTRTOINT, 0, s64}),
- std::make_pair(LegalizerInfo::Legal, s64));
- ASSERT_EQ(L.getAction({G_PTRTOINT, 1, p0}),
- std::make_pair(LegalizerInfo::Legal, p0));
+ ASSERT_EQ(L.getAction({G_PTRTOINT, {s64, p0}}),
+ LegalizeActionStep(Legal, 0, LLT{}));
+
// Make sure we also handle unusual sizes
- ASSERT_EQ(L.getAction({G_PTRTOINT, 0, LLT::scalar(65)}),
- std::make_pair(LegalizerInfo::NarrowScalar, s64));
- ASSERT_EQ(L.getAction({G_PTRTOINT, 1, LLT::pointer(0, 32)}),
- std::make_pair(LegalizerInfo::Unsupported, LLT::pointer(0, 32)));
+ ASSERT_EQ(
+ L.getAction({G_PTRTOINT, {LLT::scalar(65), s64}}),
+ LegalizeActionStep(NarrowScalar, 0, s64));
+ ASSERT_EQ(
+ L.getAction({G_PTRTOINT, {s64, LLT::pointer(0, 32)}}),
+ LegalizeActionStep(Unsupported, 1, LLT::pointer(0, 32)));
}
TEST(LegalizerInfoTest, MultipleSteps) {
@@ -156,22 +159,22 @@ TEST(LegalizerInfoTest, MultipleSteps) {
L.setLegalizeScalarToDifferentSizeStrategy(
G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
- L.setAction({G_UREM, 0, s32}, LegalizerInfo::Lower);
- L.setAction({G_UREM, 0, s64}, LegalizerInfo::Lower);
+ L.setAction({G_UREM, 0, s32}, Lower);
+ L.setAction({G_UREM, 0, s64}, Lower);
L.computeTables();
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(16)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(32)}),
- std::make_pair(LegalizerInfo::Lower, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(16)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(32)}}),
+ LegalizeActionStep(Lower, 0, LLT::scalar(32)));
}
TEST(LegalizerInfoTest, SizeChangeStrategy) {
using namespace TargetOpcode;
LegalizerInfo L;
for (unsigned Size : {1, 8, 16, 32})
- L.setAction({G_UREM, 0, LLT::scalar(Size)}, LegalizerInfo::Legal);
+ L.setAction({G_UREM, 0, LLT::scalar(Size)}, Legal);
L.setLegalizeScalarToDifferentSizeStrategy(
G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
@@ -179,20 +182,20 @@ TEST(LegalizerInfoTest, SizeChangeStrategy) {
// Check we infer the correct types and actually do what we're told.
for (unsigned Size : {1, 8, 16, 32}) {
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(Size)}),
- std::make_pair(LegalizerInfo::Legal, LLT::scalar(Size)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(Size)}}),
+ LegalizeActionStep(Legal, 0, LLT{}));
}
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(2)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(8)));
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(7)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(8)));
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(9)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(16)));
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(17)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(31)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(33)}),
- std::make_pair(LegalizerInfo::Unsupported, LLT::scalar(33)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(2)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(8)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(7)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(8)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(9)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(16)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(17)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(31)}}),
+ LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({G_UREM, {LLT::scalar(33)}}),
+ LegalizeActionStep(Unsupported, 0, LLT::scalar(33)));
}
}
diff --git a/unittests/CodeGen/GlobalISel/PatternMatchTest.cpp b/unittests/CodeGen/GlobalISel/PatternMatchTest.cpp
new file mode 100644
index 000000000000..8f17b1991df5
--- /dev/null
+++ b/unittests/CodeGen/GlobalISel/PatternMatchTest.cpp
@@ -0,0 +1,493 @@
+//===- PatternMatchTest.cpp -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/CodeGen/GlobalISel/ConstantFoldingMIRBuilder.h"
+#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/GlobalISel/Utils.h"
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/TargetFrameLowering.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.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 "gtest/gtest.h"
+
+using namespace llvm;
+using namespace MIPatternMatch;
+
+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 "AArch64".
+std::unique_ptr<TargetMachine> createTargetMachine() {
+ Triple TargetTriple("aarch64--");
+ std::string Error;
+ const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
+ if (!T)
+ return nullptr;
+
+ TargetOptions Options;
+ return std::unique_ptr<TargetMachine>(T->createTargetMachine(
+ "AArch64", "", "", Options, None, None, CodeGenOpt::Aggressive));
+}
+
+std::unique_ptr<Module> parseMIR(LLVMContext &Context,
+ std::unique_ptr<MIRParser> &MIR,
+ const TargetMachine &TM, StringRef MIRCode,
+ const char *FuncName, MachineModuleInfo &MMI) {
+ 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->parseIRModule();
+ if (!M)
+ return nullptr;
+
+ M->setDataLayout(TM.createDataLayout());
+
+ if (MIR->parseMachineFunctions(*M, MMI))
+ return nullptr;
+
+ return M;
+}
+
+std::pair<std::unique_ptr<Module>, std::unique_ptr<MachineModuleInfo>>
+createDummyModule(LLVMContext &Context, const TargetMachine &TM,
+ StringRef MIRFunc) {
+ SmallString<512> S;
+ StringRef MIRString = (Twine(R"MIR(
+---
+...
+name: func
+registers:
+ - { id: 0, class: _ }
+ - { id: 1, class: _ }
+ - { id: 2, class: _ }
+ - { id: 3, class: _ }
+body: |
+ bb.1:
+ %0(s64) = COPY $x0
+ %1(s64) = COPY $x1
+ %2(s64) = COPY $x2
+)MIR") + Twine(MIRFunc) + Twine("...\n"))
+ .toNullTerminatedStringRef(S);
+ std::unique_ptr<MIRParser> MIR;
+ auto MMI = make_unique<MachineModuleInfo>(&TM);
+ std::unique_ptr<Module> M =
+ parseMIR(Context, MIR, TM, MIRString, "func", *MMI);
+ return make_pair(std::move(M), std::move(MMI));
+}
+
+static MachineFunction *getMFFromMMI(const Module *M,
+ const MachineModuleInfo *MMI) {
+ Function *F = M->getFunction("func");
+ auto *MF = MMI->getMachineFunction(*F);
+ return MF;
+}
+
+static void collectCopies(SmallVectorImpl<unsigned> &Copies,
+ MachineFunction *MF) {
+ for (auto &MBB : *MF)
+ for (MachineInstr &MI : MBB) {
+ if (MI.getOpcode() == TargetOpcode::COPY)
+ Copies.push_back(MI.getOperand(0).getReg());
+ }
+}
+
+TEST(PatternMatchInstr, MatchIntConstant) {
+ LLVMContext Context;
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ if (!TM)
+ return;
+ auto ModuleMMIPair = createDummyModule(Context, *TM, "");
+ MachineFunction *MF =
+ getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get());
+ SmallVector<unsigned, 4> Copies;
+ collectCopies(Copies, MF);
+ MachineBasicBlock *EntryMBB = &*MF->begin();
+ MachineIRBuilder B(*MF);
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+ B.setInsertPt(*EntryMBB, EntryMBB->end());
+ auto MIBCst = B.buildConstant(LLT::scalar(64), 42);
+ int64_t Cst;
+ bool match = mi_match(MIBCst->getOperand(0).getReg(), MRI, m_ICst(Cst));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Cst, 42);
+}
+
+TEST(PatternMatchInstr, MatchBinaryOp) {
+ LLVMContext Context;
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ if (!TM)
+ return;
+ auto ModuleMMIPair = createDummyModule(Context, *TM, "");
+ MachineFunction *MF =
+ getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get());
+ SmallVector<unsigned, 4> Copies;
+ collectCopies(Copies, MF);
+ MachineBasicBlock *EntryMBB = &*MF->begin();
+ MachineIRBuilder B(*MF);
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+ B.setInsertPt(*EntryMBB, EntryMBB->end());
+ LLT s64 = LLT::scalar(64);
+ auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);
+ // Test case for no bind.
+ bool match =
+ mi_match(MIBAdd->getOperand(0).getReg(), MRI, m_GAdd(m_Reg(), m_Reg()));
+ ASSERT_TRUE(match);
+ unsigned Src0, Src1, Src2;
+ match = mi_match(MIBAdd->getOperand(0).getReg(), MRI,
+ m_GAdd(m_Reg(Src0), m_Reg(Src1)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+ ASSERT_EQ(Src1, Copies[1]);
+
+ // Build MUL(ADD %0, %1), %2
+ auto MIBMul = B.buildMul(s64, MIBAdd, Copies[2]);
+
+ // Try to match MUL.
+ match = mi_match(MIBMul->getOperand(0).getReg(), MRI,
+ m_GMul(m_Reg(Src0), m_Reg(Src1)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, MIBAdd->getOperand(0).getReg());
+ ASSERT_EQ(Src1, Copies[2]);
+
+ // Try to match MUL(ADD)
+ match = mi_match(MIBMul->getOperand(0).getReg(), MRI,
+ m_GMul(m_GAdd(m_Reg(Src0), m_Reg(Src1)), m_Reg(Src2)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+ ASSERT_EQ(Src1, Copies[1]);
+ ASSERT_EQ(Src2, Copies[2]);
+
+ // Test Commutativity.
+ auto MIBMul2 = B.buildMul(s64, Copies[0], B.buildConstant(s64, 42));
+ // Try to match MUL(Cst, Reg) on src of MUL(Reg, Cst) to validate
+ // commutativity.
+ int64_t Cst;
+ match = mi_match(MIBMul2->getOperand(0).getReg(), MRI,
+ m_GMul(m_ICst(Cst), m_Reg(Src0)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Cst, 42);
+ ASSERT_EQ(Src0, Copies[0]);
+
+ // Make sure commutative doesn't work with something like SUB.
+ auto MIBSub = B.buildSub(s64, Copies[0], B.buildConstant(s64, 42));
+ match = mi_match(MIBSub->getOperand(0).getReg(), MRI,
+ m_GSub(m_ICst(Cst), m_Reg(Src0)));
+ ASSERT_FALSE(match);
+
+ auto MIBFMul = B.buildInstr(TargetOpcode::G_FMUL, s64, Copies[0],
+ B.buildConstant(s64, 42));
+ // Match and test commutativity for FMUL.
+ match = mi_match(MIBFMul->getOperand(0).getReg(), MRI,
+ m_GFMul(m_ICst(Cst), m_Reg(Src0)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Cst, 42);
+ ASSERT_EQ(Src0, Copies[0]);
+
+ // FSUB
+ auto MIBFSub = B.buildInstr(TargetOpcode::G_FSUB, s64, Copies[0],
+ B.buildConstant(s64, 42));
+ match = mi_match(MIBFSub->getOperand(0).getReg(), MRI,
+ m_GFSub(m_Reg(Src0), m_Reg()));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+
+ // Build AND %0, %1
+ auto MIBAnd = B.buildAnd(s64, Copies[0], Copies[1]);
+ // Try to match AND.
+ match = mi_match(MIBAnd->getOperand(0).getReg(), MRI,
+ m_GAnd(m_Reg(Src0), m_Reg(Src1)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+ ASSERT_EQ(Src1, Copies[1]);
+
+ // Build OR %0, %1
+ auto MIBOr = B.buildOr(s64, Copies[0], Copies[1]);
+ // Try to match OR.
+ match = mi_match(MIBOr->getOperand(0).getReg(), MRI,
+ m_GOr(m_Reg(Src0), m_Reg(Src1)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+ ASSERT_EQ(Src1, Copies[1]);
+
+ // Try to use the FoldableInstructionsBuilder to build binary ops.
+ ConstantFoldingMIRBuilder CFB(B.getState());
+ LLT s32 = LLT::scalar(32);
+ auto MIBCAdd =
+ CFB.buildAdd(s32, CFB.buildConstant(s32, 0), CFB.buildConstant(s32, 1));
+ // This should be a constant now.
+ match = mi_match(MIBCAdd->getOperand(0).getReg(), MRI, m_ICst(Cst));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Cst, 1);
+ auto MIBCAdd1 =
+ CFB.buildInstr(TargetOpcode::G_ADD, s32, CFB.buildConstant(s32, 0),
+ CFB.buildConstant(s32, 1));
+ // This should be a constant now.
+ match = mi_match(MIBCAdd1->getOperand(0).getReg(), MRI, m_ICst(Cst));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Cst, 1);
+
+ // Try one of the other constructors of MachineIRBuilder to make sure it's
+ // compatible.
+ ConstantFoldingMIRBuilder CFB1(*MF);
+ CFB1.setInsertPt(*EntryMBB, EntryMBB->end());
+ auto MIBCSub =
+ CFB1.buildInstr(TargetOpcode::G_SUB, s32, CFB1.buildConstant(s32, 1),
+ CFB1.buildConstant(s32, 1));
+ // This should be a constant now.
+ match = mi_match(MIBCSub->getOperand(0).getReg(), MRI, m_ICst(Cst));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Cst, 0);
+}
+
+TEST(PatternMatchInstr, MatchFPUnaryOp) {
+ LLVMContext Context;
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ if (!TM)
+ return;
+ auto ModuleMMIPair = createDummyModule(Context, *TM, "");
+ MachineFunction *MF =
+ getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get());
+ SmallVector<unsigned, 4> Copies;
+ collectCopies(Copies, MF);
+ MachineBasicBlock *EntryMBB = &*MF->begin();
+ MachineIRBuilder B(*MF);
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+ B.setInsertPt(*EntryMBB, EntryMBB->end());
+
+ // Truncate s64 to s32.
+ LLT s32 = LLT::scalar(32);
+ auto Copy0s32 = B.buildFPTrunc(s32, Copies[0]);
+
+ // Match G_FABS.
+ auto MIBFabs = B.buildInstr(TargetOpcode::G_FABS, s32, Copy0s32);
+ bool match = mi_match(MIBFabs->getOperand(0).getReg(), MRI, m_GFabs(m_Reg()));
+ ASSERT_TRUE(match);
+
+ unsigned Src;
+ auto MIBFNeg = B.buildInstr(TargetOpcode::G_FNEG, s32, Copy0s32);
+ match = mi_match(MIBFNeg->getOperand(0).getReg(), MRI, m_GFNeg(m_Reg(Src)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src, Copy0s32->getOperand(0).getReg());
+
+ match = mi_match(MIBFabs->getOperand(0).getReg(), MRI, m_GFabs(m_Reg(Src)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src, Copy0s32->getOperand(0).getReg());
+
+ // Build and match FConstant.
+ auto MIBFCst = B.buildFConstant(s32, .5);
+ const ConstantFP *TmpFP{};
+ match = mi_match(MIBFCst->getOperand(0).getReg(), MRI, m_GFCst(TmpFP));
+ ASSERT_TRUE(match);
+ ASSERT_TRUE(TmpFP);
+ APFloat APF((float).5);
+ auto *CFP = ConstantFP::get(Context, APF);
+ ASSERT_EQ(CFP, TmpFP);
+
+ // Build double float.
+ LLT s64 = LLT::scalar(64);
+ auto MIBFCst64 = B.buildFConstant(s64, .5);
+ const ConstantFP *TmpFP64{};
+ match = mi_match(MIBFCst64->getOperand(0).getReg(), MRI, m_GFCst(TmpFP64));
+ ASSERT_TRUE(match);
+ ASSERT_TRUE(TmpFP64);
+ APFloat APF64(.5);
+ auto CFP64 = ConstantFP::get(Context, APF64);
+ ASSERT_EQ(CFP64, TmpFP64);
+ ASSERT_NE(TmpFP64, TmpFP);
+
+ // Build half float.
+ LLT s16 = LLT::scalar(16);
+ auto MIBFCst16 = B.buildFConstant(s16, .5);
+ const ConstantFP *TmpFP16{};
+ match = mi_match(MIBFCst16->getOperand(0).getReg(), MRI, m_GFCst(TmpFP16));
+ ASSERT_TRUE(match);
+ ASSERT_TRUE(TmpFP16);
+ bool Ignored;
+ APFloat APF16(.5);
+ APF16.convert(APFloat::IEEEhalf(), APFloat::rmNearestTiesToEven, &Ignored);
+ auto CFP16 = ConstantFP::get(Context, APF16);
+ ASSERT_EQ(TmpFP16, CFP16);
+ ASSERT_NE(TmpFP16, TmpFP);
+}
+
+TEST(PatternMatchInstr, MatchExtendsTrunc) {
+ LLVMContext Context;
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ if (!TM)
+ return;
+ auto ModuleMMIPair = createDummyModule(Context, *TM, "");
+ MachineFunction *MF =
+ getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get());
+ SmallVector<unsigned, 4> Copies;
+ collectCopies(Copies, MF);
+ MachineBasicBlock *EntryMBB = &*MF->begin();
+ MachineIRBuilder B(*MF);
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+ B.setInsertPt(*EntryMBB, EntryMBB->end());
+ LLT s64 = LLT::scalar(64);
+ LLT s32 = LLT::scalar(32);
+
+ auto MIBTrunc = B.buildTrunc(s32, Copies[0]);
+ auto MIBAExt = B.buildAnyExt(s64, MIBTrunc);
+ auto MIBZExt = B.buildZExt(s64, MIBTrunc);
+ auto MIBSExt = B.buildSExt(s64, MIBTrunc);
+ unsigned Src0;
+ bool match =
+ mi_match(MIBTrunc->getOperand(0).getReg(), MRI, m_GTrunc(m_Reg(Src0)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+ match =
+ mi_match(MIBAExt->getOperand(0).getReg(), MRI, m_GAnyExt(m_Reg(Src0)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, MIBTrunc->getOperand(0).getReg());
+
+ match = mi_match(MIBSExt->getOperand(0).getReg(), MRI, m_GSExt(m_Reg(Src0)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, MIBTrunc->getOperand(0).getReg());
+
+ match = mi_match(MIBZExt->getOperand(0).getReg(), MRI, m_GZExt(m_Reg(Src0)));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, MIBTrunc->getOperand(0).getReg());
+
+ // Match ext(trunc src)
+ match = mi_match(MIBAExt->getOperand(0).getReg(), MRI,
+ m_GAnyExt(m_GTrunc(m_Reg(Src0))));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+
+ match = mi_match(MIBSExt->getOperand(0).getReg(), MRI,
+ m_GSExt(m_GTrunc(m_Reg(Src0))));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+
+ match = mi_match(MIBZExt->getOperand(0).getReg(), MRI,
+ m_GZExt(m_GTrunc(m_Reg(Src0))));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+}
+
+TEST(PatternMatchInstr, MatchSpecificType) {
+ LLVMContext Context;
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ if (!TM)
+ return;
+ auto ModuleMMIPair = createDummyModule(Context, *TM, "");
+ MachineFunction *MF =
+ getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get());
+ SmallVector<unsigned, 4> Copies;
+ collectCopies(Copies, MF);
+ MachineBasicBlock *EntryMBB = &*MF->begin();
+ MachineIRBuilder B(*MF);
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+ B.setInsertPt(*EntryMBB, EntryMBB->end());
+
+ // Try to match a 64bit add.
+ LLT s64 = LLT::scalar(64);
+ LLT s32 = LLT::scalar(32);
+ auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);
+ ASSERT_FALSE(mi_match(MIBAdd->getOperand(0).getReg(), MRI,
+ m_GAdd(m_SpecificType(s32), m_Reg())));
+ ASSERT_TRUE(mi_match(MIBAdd->getOperand(0).getReg(), MRI,
+ m_GAdd(m_SpecificType(s64), m_Reg())));
+
+ // Try to match the destination type of a bitcast.
+ LLT v2s32 = LLT::vector(2, 32);
+ auto MIBCast = B.buildCast(v2s32, Copies[0]);
+ ASSERT_TRUE(
+ mi_match(MIBCast->getOperand(0).getReg(), MRI, m_GBitcast(m_Reg())));
+ ASSERT_TRUE(
+ mi_match(MIBCast->getOperand(0).getReg(), MRI, m_SpecificType(v2s32)));
+ ASSERT_TRUE(
+ mi_match(MIBCast->getOperand(1).getReg(), MRI, m_SpecificType(s64)));
+
+ // Build a PTRToInt and INTTOPTR and match and test them.
+ LLT PtrTy = LLT::pointer(0, 64);
+ auto MIBIntToPtr = B.buildCast(PtrTy, Copies[0]);
+ auto MIBPtrToInt = B.buildCast(s64, MIBIntToPtr);
+ unsigned Src0;
+
+ // match the ptrtoint(inttoptr reg)
+ bool match = mi_match(MIBPtrToInt->getOperand(0).getReg(), MRI,
+ m_GPtrToInt(m_GIntToPtr(m_Reg(Src0))));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+}
+
+TEST(PatternMatchInstr, MatchCombinators) {
+ LLVMContext Context;
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ if (!TM)
+ return;
+ auto ModuleMMIPair = createDummyModule(Context, *TM, "");
+ MachineFunction *MF =
+ getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get());
+ SmallVector<unsigned, 4> Copies;
+ collectCopies(Copies, MF);
+ MachineBasicBlock *EntryMBB = &*MF->begin();
+ MachineIRBuilder B(*MF);
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+ B.setInsertPt(*EntryMBB, EntryMBB->end());
+ LLT s64 = LLT::scalar(64);
+ LLT s32 = LLT::scalar(32);
+ auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);
+ unsigned Src0, Src1;
+ bool match =
+ mi_match(MIBAdd->getOperand(0).getReg(), MRI,
+ m_all_of(m_SpecificType(s64), m_GAdd(m_Reg(Src0), m_Reg(Src1))));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+ ASSERT_EQ(Src1, Copies[1]);
+ // Check for s32 (which should fail).
+ match =
+ mi_match(MIBAdd->getOperand(0).getReg(), MRI,
+ m_all_of(m_SpecificType(s32), m_GAdd(m_Reg(Src0), m_Reg(Src1))));
+ ASSERT_FALSE(match);
+ match =
+ mi_match(MIBAdd->getOperand(0).getReg(), MRI,
+ m_any_of(m_SpecificType(s32), m_GAdd(m_Reg(Src0), m_Reg(Src1))));
+ ASSERT_TRUE(match);
+ ASSERT_EQ(Src0, Copies[0]);
+ ASSERT_EQ(Src1, Copies[1]);
+
+ // Match a case where none of the predicates hold true.
+ match = mi_match(
+ MIBAdd->getOperand(0).getReg(), MRI,
+ m_any_of(m_SpecificType(LLT::scalar(16)), m_GSub(m_Reg(), m_Reg())));
+ ASSERT_FALSE(match);
+}
+} // namespace
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ initLLVM();
+ return RUN_ALL_TESTS();
+}
diff --git a/unittests/CodeGen/MachineInstrTest.cpp b/unittests/CodeGen/MachineInstrTest.cpp
index aca640ebcf35..6883d3ed63e9 100644
--- a/unittests/CodeGen/MachineInstrTest.cpp
+++ b/unittests/CodeGen/MachineInstrTest.cpp
@@ -14,6 +14,8 @@
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/ModuleSlotTracker.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
@@ -244,4 +246,31 @@ TEST(MachineInstrExpressionTraitTest, IsEqualAgreesWithGetHashValue) {
checkHashAndIsEqualMatch(VD2PU, VD2PD);
}
+
+TEST(MachineInstrPrintingTest, DebugLocPrinting) {
+ auto MF = createMachineFunction();
+
+ MCOperandInfo OpInfo{0, 0, MCOI::OPERAND_REGISTER, 0};
+ MCInstrDesc MCID = {0, 1, 1, 0, 0, 0,
+ 0, nullptr, nullptr, &OpInfo, 0, nullptr};
+
+ LLVMContext Ctx;
+ DIFile *DIF = DIFile::getDistinct(Ctx, "filename", "");
+ DISubprogram *DIS = DISubprogram::getDistinct(
+ Ctx, nullptr, "", "", DIF, 0, nullptr, false, false, 0, nullptr, 0, 0, 0,
+ DINode::FlagZero, false, nullptr);
+ DILocation *DIL = DILocation::get(Ctx, 1, 5, DIS);
+ DebugLoc DL(DIL);
+ MachineInstr *MI = MF->CreateMachineInstr(MCID, DL);
+ MI->addOperand(*MF, MachineOperand::CreateReg(0, /*isDef*/ true));
+
+ std::string str;
+ raw_string_ostream OS(str);
+ MI->print(OS);
+ ASSERT_TRUE(
+ StringRef(OS.str()).startswith("$noreg = UNKNOWN debug-location "));
+ ASSERT_TRUE(
+ StringRef(OS.str()).endswith("filename:1:5"));
+}
+
} // end namespace
diff --git a/unittests/CodeGen/MachineOperandTest.cpp b/unittests/CodeGen/MachineOperandTest.cpp
index 78a20b836486..53ec5aed8189 100644
--- a/unittests/CodeGen/MachineOperandTest.cpp
+++ b/unittests/CodeGen/MachineOperandTest.cpp
@@ -80,7 +80,7 @@ TEST(MachineOperandTest, PrintSubReg) {
std::string str;
raw_string_ostream OS(str);
MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);
- ASSERT_TRUE(OS.str() == "%physreg1.subreg5");
+ ASSERT_TRUE(OS.str() == "$physreg1.subreg5");
}
TEST(MachineOperandTest, PrintCImm) {
@@ -118,8 +118,7 @@ TEST(MachineOperandTest, PrintSubRegIndex) {
// TRI and IntrinsicInfo we can print the operand as a subreg index.
std::string str;
raw_string_ostream OS(str);
- ModuleSlotTracker DummyMST(nullptr);
- MachineOperand::printSubregIdx(OS, MO.getImm(), nullptr);
+ MachineOperand::printSubRegIdx(OS, MO.getImm(), nullptr);
ASSERT_TRUE(OS.str() == "%subreg.3");
}
@@ -215,7 +214,7 @@ TEST(MachineOperandTest, PrintExternalSymbol) {
{
raw_string_ostream OS(str);
MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);
- ASSERT_TRUE(OS.str() == "$foo");
+ ASSERT_TRUE(OS.str() == "&foo");
}
str.clear();
@@ -225,7 +224,7 @@ TEST(MachineOperandTest, PrintExternalSymbol) {
{
raw_string_ostream OS(str);
MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);
- ASSERT_TRUE(OS.str() == "$foo + 12");
+ ASSERT_TRUE(OS.str() == "&foo + 12");
}
str.clear();
@@ -235,7 +234,7 @@ TEST(MachineOperandTest, PrintExternalSymbol) {
{
raw_string_ostream OS(str);
MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);
- ASSERT_TRUE(OS.str() == "$foo - 12");
+ ASSERT_TRUE(OS.str() == "&foo - 12");
}
}
@@ -296,7 +295,7 @@ TEST(MachineOperandTest, PrintMetadata) {
LLVMContext Ctx;
Module M("MachineOperandMDNodeTest", Ctx);
NamedMDNode *MD = M.getOrInsertNamedMetadata("namedmd");
- ModuleSlotTracker DummyMST(&M);
+ ModuleSlotTracker MST(&M);
Metadata *MDS = MDString::get(Ctx, "foo");
MDNode *Node = MDNode::get(Ctx, MDS);
MD->addOperand(Node);
@@ -312,7 +311,8 @@ TEST(MachineOperandTest, PrintMetadata) {
std::string str;
// Print a MachineOperand containing a metadata node.
raw_string_ostream OS(str);
- MO.print(OS, DummyMST, LLT{}, false, false, 0, /*TRI=*/nullptr,
+ MO.print(OS, MST, LLT{}, /*PrintDef=*/false, /*IsStandalone=*/false,
+ /*ShouldPrintRegisterTies=*/false, 0, /*TRI=*/nullptr,
/*IntrinsicInfo=*/nullptr);
ASSERT_TRUE(OS.str() == "!0");
}
diff --git a/unittests/CodeGen/ScalableVectorMVTsTest.cpp b/unittests/CodeGen/ScalableVectorMVTsTest.cpp
index 0071823f2cc9..20b3fb1b9f48 100644
--- a/unittests/CodeGen/ScalableVectorMVTsTest.cpp
+++ b/unittests/CodeGen/ScalableVectorMVTsTest.cpp
@@ -7,9 +7,9 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/CodeGen/MachineValueType.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/Support/MachineValueType.h"
#include "gtest/gtest.h"
using namespace llvm;
diff --git a/unittests/DebugInfo/CodeView/CMakeLists.txt b/unittests/DebugInfo/CodeView/CMakeLists.txt
index d06ccfaba72a..70a7b8af1447 100644
--- a/unittests/DebugInfo/CodeView/CMakeLists.txt
+++ b/unittests/DebugInfo/CodeView/CMakeLists.txt
@@ -2,14 +2,10 @@ set(LLVM_LINK_COMPONENTS
DebugInfoCodeView
)
-set(DebugInfoCodeViewSources
+add_llvm_unittest(DebugInfoCodeViewTests
RandomAccessVisitorTest.cpp
TypeHashingTest.cpp
TypeIndexDiscoveryTest.cpp
)
-add_llvm_unittest(DebugInfoCodeViewTests
- ${DebugInfoCodeViewSources}
- )
-
target_link_libraries(DebugInfoCodeViewTests PRIVATE LLVMTestingSupport)
diff --git a/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp b/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp
index c51b9e723f04..60ca56b0d143 100644
--- a/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp
+++ b/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp
@@ -555,7 +555,7 @@ TEST_F(TypeIndexIteratorTest, DataSym) {
TEST_F(TypeIndexIteratorTest, RegisterSym) {
RegisterSym Reg(SymbolRecordKind::RegisterSym);
Reg.Index = TypeIndex::UInt32();
- Reg.Register = RegisterId::EAX;
+ Reg.Register = RegisterId::CVRegEAX;
Reg.Name = "Target";
writeSymbolRecords(Reg);
checkTypeReferences(0, Reg.Index);
@@ -580,3 +580,24 @@ TEST_F(TypeIndexIteratorTest, CallerSym) {
checkTypeReferences(2, TypeIndex(7), TypeIndex(8), TypeIndex(9));
}
+TEST_F(TypeIndexIteratorTest, Precomp) {
+ PrecompRecord P(TypeRecordKind::Precomp);
+ P.StartTypeIndex = TypeIndex::FirstNonSimpleIndex;
+ P.TypesCount = 100;
+ P.Signature = 0x12345678;
+ P.PrecompFilePath = "C:/precomp.obj";
+
+ EndPrecompRecord EP(TypeRecordKind::EndPrecomp);
+ EP.Signature = P.Signature;
+
+ writeTypeRecords(P, EP);
+ checkTypeReferences(0);
+}
+
+// This is a test for getEncodedIntegerLength()
+TEST_F(TypeIndexIteratorTest, VariableSizeIntegers) {
+ BaseClassRecord BaseClass1(MemberAccess::Public, TypeIndex(47), (uint64_t)-1);
+ BaseClassRecord BaseClass2(MemberAccess::Public, TypeIndex(48), 1);
+ writeFieldList(BaseClass1, BaseClass2);
+ checkTypeReferences(0, TypeIndex(47), TypeIndex(48));
+} \ No newline at end of file
diff --git a/unittests/DebugInfo/DWARF/CMakeLists.txt b/unittests/DebugInfo/DWARF/CMakeLists.txt
index f490097a21a7..d97e573ea38f 100644
--- a/unittests/DebugInfo/DWARF/CMakeLists.txt
+++ b/unittests/DebugInfo/DWARF/CMakeLists.txt
@@ -8,14 +8,12 @@ set(LLVM_LINK_COMPONENTS
Support
)
-set(DebugInfoSources
+add_llvm_unittest(DebugInfoDWARFTests
DwarfGenerator.cpp
+ DwarfUtils.cpp
DWARFDebugInfoTest.cpp
+ DWARFDebugLineTest.cpp
DWARFFormValueTest.cpp
)
-add_llvm_unittest(DebugInfoDWARFTests
- ${DebugInfoSources}
- )
-
target_link_libraries(DebugInfoDWARFTests PRIVATE LLVMTestingSupport)
diff --git a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
index cb7bf82d86f6..442dea3c52f7 100644
--- a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
+++ b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
@@ -1,4 +1,4 @@
-//===- llvm/unittest/DebugInfo/DWARFFormValueTest.cpp ---------------------===//
+//===- llvm/unittest/DebugInfo/DWARFDebugInfoTest.cpp ---------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "DwarfGenerator.h"
+#include "DwarfUtils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
@@ -15,7 +16,6 @@
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinter.h"
-#include "llvm/Config/llvm-config.h"
#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
@@ -36,36 +36,10 @@
using namespace llvm;
using namespace dwarf;
+using namespace utils;
namespace {
-void initLLVMIfNeeded() {
- static bool gInitialized = false;
- if (!gInitialized) {
- gInitialized = true;
- InitializeAllTargets();
- InitializeAllTargetMCs();
- InitializeAllAsmPrinters();
- InitializeAllAsmParsers();
- }
-}
-
-Triple getHostTripleForAddrSize(uint8_t AddrSize) {
- Triple PT(Triple::normalize(LLVM_HOST_TRIPLE));
-
- if (AddrSize == 8 && PT.isArch32Bit())
- return PT.get64BitArchVariant();
- if (AddrSize == 4 && PT.isArch64Bit())
- return PT.get32BitArchVariant();
- return PT;
-}
-
-static bool isConfigurationSupported(Triple &T) {
- initLLVMIfNeeded();
- std::string Err;
- return TargetRegistry::lookupTarget(T.getTriple(), Err);
-}
-
template <uint16_t Version, class AddrType, class RefAddrType>
void TestAllForms() {
Triple Triple = getHostTripleForAddrSize(sizeof(AddrType));
@@ -93,12 +67,21 @@ void TestAllForms() {
const uint32_t Dwarf32Values[] = {1, 2, 3, 4, 5, 6, 7, 8};
const char *StringValue = "Hello";
const char *StrpValue = "World";
+ const char *StrxValue = "Indexed";
+ const char *Strx1Value = "Indexed1";
+ const char *Strx2Value = "Indexed2";
+ const char *Strx3Value = "Indexed3";
+ const char *Strx4Value = "Indexed4";
auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);
ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());
dwarfgen::Generator *DG = ExpectedDG.get().get();
dwarfgen::CompileUnit &CU = DG->addCompileUnit();
dwarfgen::DIE CUDie = CU.getUnitDIE();
+
+ if (Version >= 5)
+ CUDie.addStrOffsetsBaseAttribute();
+
uint16_t Attr = DW_AT_lo_user;
//----------------------------------------------------------------------
@@ -148,6 +131,19 @@ void TestAllForms() {
const auto Attr_DW_FORM_string = static_cast<dwarf::Attribute>(Attr++);
CUDie.addAttribute(Attr_DW_FORM_string, DW_FORM_string, StringValue);
+ const auto Attr_DW_FORM_strx = static_cast<dwarf::Attribute>(Attr++);
+ const auto Attr_DW_FORM_strx1 = static_cast<dwarf::Attribute>(Attr++);
+ const auto Attr_DW_FORM_strx2 = static_cast<dwarf::Attribute>(Attr++);
+ const auto Attr_DW_FORM_strx3 = static_cast<dwarf::Attribute>(Attr++);
+ const auto Attr_DW_FORM_strx4 = static_cast<dwarf::Attribute>(Attr++);
+ if (Version >= 5) {
+ CUDie.addAttribute(Attr_DW_FORM_strx, DW_FORM_strx, StrxValue);
+ CUDie.addAttribute(Attr_DW_FORM_strx1, DW_FORM_strx1, Strx1Value);
+ CUDie.addAttribute(Attr_DW_FORM_strx2, DW_FORM_strx2, Strx2Value);
+ CUDie.addAttribute(Attr_DW_FORM_strx3, DW_FORM_strx3, Strx3Value);
+ CUDie.addAttribute(Attr_DW_FORM_strx4, DW_FORM_strx4, Strx4Value);
+ }
+
const auto Attr_DW_FORM_strp = static_cast<dwarf::Attribute>(Attr++);
CUDie.addAttribute(Attr_DW_FORM_strp, DW_FORM_strp, StrpValue);
@@ -307,11 +303,33 @@ void TestAllForms() {
//----------------------------------------------------------------------
auto ExtractedStringValue = toString(DieDG.find(Attr_DW_FORM_string));
EXPECT_TRUE((bool)ExtractedStringValue);
- EXPECT_TRUE(strcmp(StringValue, *ExtractedStringValue) == 0);
+ EXPECT_STREQ(StringValue, *ExtractedStringValue);
+
+ if (Version >= 5) {
+ auto ExtractedStrxValue = toString(DieDG.find(Attr_DW_FORM_strx));
+ EXPECT_TRUE((bool)ExtractedStrxValue);
+ EXPECT_STREQ(StrxValue, *ExtractedStrxValue);
+
+ auto ExtractedStrx1Value = toString(DieDG.find(Attr_DW_FORM_strx1));
+ EXPECT_TRUE((bool)ExtractedStrx1Value);
+ EXPECT_STREQ(Strx1Value, *ExtractedStrx1Value);
+
+ auto ExtractedStrx2Value = toString(DieDG.find(Attr_DW_FORM_strx2));
+ EXPECT_TRUE((bool)ExtractedStrx2Value);
+ EXPECT_STREQ(Strx2Value, *ExtractedStrx2Value);
+
+ auto ExtractedStrx3Value = toString(DieDG.find(Attr_DW_FORM_strx3));
+ EXPECT_TRUE((bool)ExtractedStrx3Value);
+ EXPECT_STREQ(Strx3Value, *ExtractedStrx3Value);
+
+ auto ExtractedStrx4Value = toString(DieDG.find(Attr_DW_FORM_strx4));
+ EXPECT_TRUE((bool)ExtractedStrx4Value);
+ EXPECT_STREQ(Strx4Value, *ExtractedStrx4Value);
+ }
auto ExtractedStrpValue = toString(DieDG.find(Attr_DW_FORM_strp));
EXPECT_TRUE((bool)ExtractedStrpValue);
- EXPECT_TRUE(strcmp(StrpValue, *ExtractedStrpValue) == 0);
+ EXPECT_STREQ(StrpValue, *ExtractedStrpValue);
//----------------------------------------------------------------------
// Test reference forms
@@ -516,6 +534,11 @@ template <uint16_t Version, class AddrType> void TestChildren() {
EXPECT_TRUE(!NullDieDG.getSibling().isValid());
EXPECT_TRUE(!NullDieDG.getFirstChild().isValid());
}
+
+ // Verify the previous sibling of our subprogram is our integer base type.
+ IntDieDG = NullDieDG.getPreviousSibling();
+ EXPECT_TRUE(IntDieDG.isValid());
+ EXPECT_EQ(IntDieDG.getTag(), DW_TAG_base_type);
}
TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Children) {
@@ -1098,6 +1121,27 @@ TEST(DWARFDebugInfo, TestRelations) {
// Make sure the parent of all the children of the B are the B.
EXPECT_EQ(C1.getParent(), C);
EXPECT_EQ(C2.getParent(), C);
+
+ // Make sure bidirectional iterator works as expected.
+ auto Begin = A.begin();
+ auto End = A.end();
+ auto It = A.begin();
+
+ EXPECT_EQ(It, Begin);
+ EXPECT_EQ(*It, B);
+ ++It;
+ EXPECT_EQ(*It, C);
+ ++It;
+ EXPECT_EQ(*It, D);
+ ++It;
+ EXPECT_EQ(It, End);
+ --It;
+ EXPECT_EQ(*It, D);
+ --It;
+ EXPECT_EQ(*It, C);
+ --It;
+ EXPECT_EQ(*It, B);
+ EXPECT_EQ(It, Begin);
}
TEST(DWARFDebugInfo, TestDWARFDie) {
@@ -1191,7 +1235,7 @@ TEST(DWARFDebugInfo, TestEmptyChildren) {
" Attributes:\n"
"debug_info:\n"
" - Length:\n"
- " TotalLength: 9\n"
+ " TotalLength: 0\n"
" Version: 4\n"
" AbbrOffset: 0\n"
" AddrSize: 8\n"
@@ -1201,7 +1245,7 @@ TEST(DWARFDebugInfo, TestEmptyChildren) {
" - AbbrCode: 0x00000000\n"
" Values:\n";
- auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
+ auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata), true);
ASSERT_TRUE((bool)ErrOrSections);
std::unique_ptr<DWARFContext> DwarfContext =
DWARFContext::create(*ErrOrSections, 8);
diff --git a/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
new file mode 100644
index 000000000000..99ed118e539b
--- /dev/null
+++ b/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
@@ -0,0 +1,667 @@
+//===- DWARFDebugLineTest.cpp ---------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DwarfGenerator.h"
+#include "DwarfUtils.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace dwarf;
+using namespace dwarfgen;
+using namespace object;
+using namespace utils;
+using namespace testing;
+
+namespace {
+struct CommonFixture {
+ CommonFixture()
+ : LineData("", true, 0), Recoverable(Error::success()),
+ RecordRecoverable(std::bind(&CommonFixture::recordRecoverable, this,
+ std::placeholders::_1)),
+ Unrecoverable(Error::success()),
+ RecordUnrecoverable(std::bind(&CommonFixture::recordUnrecoverable, this,
+ std::placeholders::_1)){};
+
+ ~CommonFixture() {
+ EXPECT_FALSE(Recoverable);
+ EXPECT_FALSE(Unrecoverable);
+ }
+
+ bool setupGenerator(uint16_t Version = 4) {
+ Triple T = getHostTripleForAddrSize(8);
+ if (!isConfigurationSupported(T))
+ return false;
+ auto ExpectedGenerator = Generator::create(T, Version);
+ if (ExpectedGenerator)
+ Gen.reset(ExpectedGenerator->release());
+ return true;
+ }
+
+ void generate() {
+ Context = createContext();
+ assert(Context != nullptr && "test state is not valid");
+ const DWARFObject &Obj = Context->getDWARFObj();
+ LineData = DWARFDataExtractor(Obj, Obj.getLineSection(),
+ sys::IsLittleEndianHost, 8);
+ }
+
+ std::unique_ptr<DWARFContext> createContext() {
+ if (!Gen)
+ return nullptr;
+ StringRef FileBytes = Gen->generate();
+ MemoryBufferRef FileBuffer(FileBytes, "dwarf");
+ auto Obj = object::ObjectFile::createObjectFile(FileBuffer);
+ if (Obj)
+ return DWARFContext::create(**Obj);
+ return nullptr;
+ }
+
+ DWARFDebugLine::SectionParser setupParser() {
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ LineTable &LT2 = Gen->addLineTable(DWARF64);
+ LT2.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x11223344, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xbb);
+ LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ generate();
+
+ return DWARFDebugLine::SectionParser(LineData, *Context, CUs, TUs);
+ }
+
+ void recordRecoverable(Error Err) {
+ Recoverable = joinErrors(std::move(Recoverable), std::move(Err));
+ }
+ void recordUnrecoverable(Error Err) {
+ Unrecoverable = joinErrors(std::move(Unrecoverable), std::move(Err));
+ }
+
+ void checkError(ArrayRef<StringRef> ExpectedMsgs, Error Err) {
+ ASSERT_TRUE(Err.operator bool());
+ size_t WhichMsg = 0;
+ Error Remaining =
+ handleErrors(std::move(Err), [&](const ErrorInfoBase &Actual) {
+ ASSERT_LT(WhichMsg, ExpectedMsgs.size());
+ // Use .str(), because googletest doesn't visualise a StringRef
+ // properly.
+ EXPECT_EQ(Actual.message(), ExpectedMsgs[WhichMsg++].str());
+ });
+ EXPECT_EQ(WhichMsg, ExpectedMsgs.size());
+ EXPECT_FALSE(Remaining);
+ }
+
+ void checkError(StringRef ExpectedMsg, Error Err) {
+ checkError(ArrayRef<StringRef>{ExpectedMsg}, std::move(Err));
+ }
+
+ void checkGetOrParseLineTableEmitsError(StringRef ExpectedMsg,
+ uint64_t Offset = 0) {
+ auto ExpectedLineTable = Line.getOrParseLineTable(
+ LineData, Offset, *Context, nullptr, RecordRecoverable);
+ EXPECT_FALSE(ExpectedLineTable);
+ EXPECT_FALSE(Recoverable);
+
+ checkError(ExpectedMsg, ExpectedLineTable.takeError());
+ }
+
+ std::unique_ptr<Generator> Gen;
+ std::unique_ptr<DWARFContext> Context;
+ DWARFDataExtractor LineData;
+ DWARFDebugLine Line;
+ Error Recoverable;
+ std::function<void(Error)> RecordRecoverable;
+ Error Unrecoverable;
+ std::function<void(Error)> RecordUnrecoverable;
+
+ SmallVector<std::unique_ptr<DWARFCompileUnit>, 2> CUs;
+ std::deque<DWARFUnitSection<DWARFTypeUnit>> TUs;
+};
+
+// Fixtures must derive from "Test", but parameterised fixtures from
+// "TestWithParam". It does not seem possible to inherit from both, so we share
+// the common state in a separate class, inherited by the two fixture classes.
+struct DebugLineBasicFixture : public Test, public CommonFixture {};
+
+struct DebugLineParameterisedFixture
+ : public TestWithParam<std::pair<uint16_t, DwarfFormat>>,
+ public CommonFixture {
+ void SetUp() { std::tie(Version, Format) = GetParam(); }
+
+ uint16_t Version;
+ DwarfFormat Format;
+};
+
+void checkDefaultPrologue(uint16_t Version, DwarfFormat Format,
+ DWARFDebugLine::Prologue Prologue,
+ uint64_t BodyLength) {
+ // Check version specific fields and values.
+ uint64_t UnitLength;
+ uint64_t PrologueLength;
+ switch (Version) {
+ case 4:
+ PrologueLength = 36;
+ UnitLength = PrologueLength + 2;
+ EXPECT_EQ(Prologue.MaxOpsPerInst, 1u);
+ break;
+ case 2:
+ case 3:
+ PrologueLength = 35;
+ UnitLength = PrologueLength + 2;
+ break;
+ case 5:
+ PrologueLength = 39;
+ UnitLength = PrologueLength + 4;
+ EXPECT_EQ(Prologue.getAddressSize(), 8u);
+ EXPECT_EQ(Prologue.SegSelectorSize, 0u);
+ break;
+ default:
+ llvm_unreachable("unsupported DWARF version");
+ }
+ UnitLength += BodyLength + (Format == DWARF32 ? 4 : 8);
+
+ EXPECT_EQ(Prologue.TotalLength, UnitLength);
+ EXPECT_EQ(Prologue.PrologueLength, PrologueLength);
+ EXPECT_EQ(Prologue.MinInstLength, 1u);
+ EXPECT_EQ(Prologue.DefaultIsStmt, 1u);
+ EXPECT_EQ(Prologue.LineBase, -5);
+ EXPECT_EQ(Prologue.LineRange, 14u);
+ EXPECT_EQ(Prologue.OpcodeBase, 13u);
+ std::vector<uint8_t> ExpectedLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};
+ EXPECT_EQ(Prologue.StandardOpcodeLengths, ExpectedLengths);
+ ASSERT_EQ(Prologue.IncludeDirectories.size(), 1u);
+ ASSERT_EQ(Prologue.IncludeDirectories[0].getForm(), DW_FORM_string);
+ EXPECT_STREQ(*Prologue.IncludeDirectories[0].getAsCString(), "a dir");
+ ASSERT_EQ(Prologue.FileNames.size(), 1u);
+ ASSERT_EQ(Prologue.FileNames[0].Name.getForm(), DW_FORM_string);
+ EXPECT_STREQ(*Prologue.FileNames[0].Name.getAsCString(), "a file");
+}
+
+TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) {
+ if (!setupGenerator())
+ return;
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "offset 0x00000000 is not a valid debug line section offset", 0);
+ // Repeat to show that an error is reported each time.
+ checkGetOrParseLineTableEmitsError(
+ "offset 0x00000000 is not a valid debug line section offset", 0);
+ // Show that an error is reported for later offsets too.
+ checkGetOrParseLineTableEmitsError(
+ "offset 0x00000001 is not a valid debug line section offset", 1);
+}
+
+TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({{0, LineTable::Byte}});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "offset 0x00000001 is not a valid debug line section offset", 1);
+}
+
+TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) {
+ if (!setupGenerator(Version))
+ return;
+
+ SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
+ (Format == DWARF64 ? "DWARF64" : "DWARF32"));
+
+ LineTable &LT = Gen->addLineTable(Format);
+ LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ LineTable &LT2 = Gen->addLineTable(Format);
+ LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xbb);
+ LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+ LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xcc);
+ LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ generate();
+
+ auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
+ nullptr, RecordRecoverable);
+ ASSERT_TRUE(ExpectedLineTable.operator bool());
+ EXPECT_FALSE(Recoverable);
+ const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;
+ checkDefaultPrologue(Version, Format, Expected->Prologue, 16);
+ EXPECT_EQ(Expected->Sequences.size(), 1u);
+
+ uint64_t SecondOffset =
+ Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;
+ Recoverable = Error::success();
+ auto ExpectedLineTable2 = Line.getOrParseLineTable(
+ LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
+ ASSERT_TRUE(ExpectedLineTable2.operator bool());
+ EXPECT_FALSE(Recoverable);
+ const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;
+ checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);
+ EXPECT_EQ(Expected2->Sequences.size(), 2u);
+
+ EXPECT_NE(Expected, Expected2);
+
+ // Check that if the same offset is requested, the exact same pointer is
+ // returned.
+ Recoverable = Error::success();
+ auto ExpectedLineTable3 = Line.getOrParseLineTable(
+ LineData, 0, *Context, nullptr, RecordRecoverable);
+ ASSERT_TRUE(ExpectedLineTable3.operator bool());
+ EXPECT_FALSE(Recoverable);
+ EXPECT_EQ(Expected, *ExpectedLineTable3);
+
+ Recoverable = Error::success();
+ auto ExpectedLineTable4 = Line.getOrParseLineTable(
+ LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
+ ASSERT_TRUE(ExpectedLineTable4.operator bool());
+ EXPECT_FALSE(Recoverable);
+ EXPECT_EQ(Expected2, *ExpectedLineTable4);
+
+ // TODO: Add tests that show that the body of the programs have been read
+ // correctly.
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForReservedLength) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "parsing line table prologue at offset 0x00000000 unsupported reserved "
+ "unit length found of value 0xffffff00");
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForLowVersion) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue(
+ {{LineTable::Half, LineTable::Long}, {1, LineTable::Half}});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError("parsing line table prologue at offset "
+ "0x00000000 found unsupported version "
+ "0x01");
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {
+ if (!setupGenerator(5))
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({
+ {19, LineTable::Long}, // unit length
+ {5, LineTable::Half}, // version
+ {8, LineTable::Byte}, // addr size
+ {0, LineTable::Byte}, // segment selector size
+ {11, LineTable::Long}, // prologue length
+ {1, LineTable::Byte}, // min instruction length
+ {1, LineTable::Byte}, // max ops per instruction
+ {1, LineTable::Byte}, // default is_stmt
+ {0, LineTable::Byte}, // line base
+ {14, LineTable::Byte}, // line range
+ {2, LineTable::Byte}, // opcode base (small to reduce the amount of
+ // setup required).
+ {0, LineTable::Byte}, // standard opcode lengths
+ {0, LineTable::Byte}, // directory entry format count (should not be
+ // zero).
+ {0, LineTable::ULEB}, // directories count
+ {0, LineTable::Byte}, // file name entry format count
+ {0, LineTable::ULEB} // file name entry count
+ });
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "parsing line table prologue at 0x00000000 found an invalid directory or "
+ "file table description at 0x00000014");
+}
+
+TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {
+ if (!setupGenerator(Version))
+ return;
+
+ SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
+ (Format == DWARF64 ? "DWARF64" : "DWARF32"));
+
+ LineTable &LT = Gen->addLineTable(Format);
+ DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
+ ++Prologue.PrologueLength;
+ LT.setPrologue(Prologue);
+
+ generate();
+
+ uint64_t ExpectedEnd =
+ Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();
+ checkGetOrParseLineTableEmitsError(
+ (Twine("parsing line table prologue at 0x00000000 should have ended at "
+ "0x000000") +
+ Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
+ Twine::utohexstr(ExpectedEnd - 1))
+ .str());
+}
+
+TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {
+ if (!setupGenerator(Version))
+ return;
+
+ SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
+ (Format == DWARF64 ? "DWARF64" : "DWARF32"));
+
+ LineTable &LT = Gen->addLineTable(Format);
+ DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
+ // FIXME: Ideally, we'd test for 1 less than expected, but the code does not
+ // currently fail if missing only the terminator of a v2-4 file table.
+ if (Version < 5)
+ Prologue.PrologueLength -= 2;
+ else
+ Prologue.PrologueLength -= 1;
+ LT.setPrologue(Prologue);
+
+ generate();
+
+ uint64_t ExpectedEnd =
+ Prologue.TotalLength - 1 + Prologue.sizeofTotalLength();
+ if (Version < 5)
+ --ExpectedEnd;
+ checkGetOrParseLineTableEmitsError(
+ (Twine("parsing line table prologue at 0x00000000 should have ended at "
+ "0x000000") +
+ Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
+ Twine::utohexstr(ExpectedEnd + 1))
+ .str());
+}
+
+INSTANTIATE_TEST_CASE_P(
+ LineTableTestParams, DebugLineParameterisedFixture,
+ Values(std::make_pair(
+ 2, DWARF32), // Test lower-bound of v2-3 fields and DWARF32.
+ std::make_pair(3, DWARF32), // Test upper-bound of v2-3 fields.
+ std::make_pair(4, DWARF64), // Test v4 fields and DWARF64.
+ std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)), );
+
+TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ // The Length should be 1 for an end sequence opcode.
+ LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError("unexpected line op length at offset "
+ "0x00000030 expected 0x02 found 0x01");
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ // The line data extractor expects size 8 (Quad) addresses.
+ LT.addExtendedOpcode(5, DW_LNE_set_address, {{0x11223344, LineTable::Long}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "mismatching address size at offset 0x00000030 expected 0x08 found 0x04");
+}
+
+TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x1122334455667788, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+ LT.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x99aabbccddeeff00, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xbb);
+ LT.addByte(0xcc);
+
+ generate();
+
+ auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
+ nullptr, RecordRecoverable);
+ checkError("last sequence in debug line table is not terminated!",
+ std::move(Recoverable));
+ ASSERT_TRUE(ExpectedLineTable.operator bool());
+ EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6u);
+ // The unterminated sequence is not added to the sequence list.
+ EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
+}
+
+TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) {
+ if (!setupGenerator())
+ return;
+
+ DWARFDebugLine::SectionParser Parser = setupParser();
+
+ EXPECT_EQ(Parser.getOffset(), 0u);
+ ASSERT_FALSE(Parser.done());
+
+ DWARFDebugLine::LineTable Parsed =
+ Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
+ checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16);
+ EXPECT_EQ(Parsed.Sequences.size(), 1u);
+ EXPECT_EQ(Parser.getOffset(), 62u);
+ ASSERT_FALSE(Parser.done());
+
+ DWARFDebugLine::LineTable Parsed2 =
+ Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
+ checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16);
+ EXPECT_EQ(Parsed2.Sequences.size(), 1u);
+ EXPECT_EQ(Parser.getOffset(), 136u);
+ EXPECT_TRUE(Parser.done());
+
+ EXPECT_FALSE(Recoverable);
+ EXPECT_FALSE(Unrecoverable);
+}
+
+TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {
+ if (!setupGenerator())
+ return;
+
+ DWARFDebugLine::SectionParser Parser = setupParser();
+
+ EXPECT_EQ(Parser.getOffset(), 0u);
+ ASSERT_FALSE(Parser.done());
+
+ Parser.skip(RecordUnrecoverable);
+ EXPECT_EQ(Parser.getOffset(), 62u);
+ ASSERT_FALSE(Parser.done());
+
+ Parser.skip(RecordUnrecoverable);
+ EXPECT_EQ(Parser.getOffset(), 136u);
+ EXPECT_TRUE(Parser.done());
+
+ EXPECT_FALSE(Unrecoverable);
+}
+
+TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) {
+ if (!setupGenerator())
+ return;
+
+ generate();
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+
+ EXPECT_TRUE(Parser.done());
+}
+
+TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenParsing) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
+ Gen->addLineTable();
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
+
+ EXPECT_EQ(Parser.getOffset(), 4u);
+ EXPECT_TRUE(Parser.done());
+ EXPECT_FALSE(Recoverable);
+
+ checkError("parsing line table prologue at offset 0x00000000 unsupported "
+ "reserved unit length found of value 0xffffff00",
+ std::move(Unrecoverable));
+}
+
+TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenSkipping) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
+ Gen->addLineTable();
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.skip(RecordUnrecoverable);
+
+ EXPECT_EQ(Parser.getOffset(), 4u);
+ EXPECT_TRUE(Parser.done());
+
+ checkError("parsing line table prologue at offset 0x00000000 unsupported "
+ "reserved unit length found of value 0xffffff00",
+ std::move(Unrecoverable));
+}
+
+TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
+ LineTable &LT2 = Gen->addLineTable(DWARF32);
+ LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
+ ASSERT_FALSE(Parser.done());
+ Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
+
+ EXPECT_TRUE(Parser.done());
+ EXPECT_FALSE(Recoverable);
+
+ checkError({"parsing line table prologue at offset 0x00000000 found "
+ "unsupported version 0x00",
+ "parsing line table prologue at offset 0x00000006 found "
+ "unsupported version 0x01"},
+ std::move(Unrecoverable));
+}
+
+TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {});
+ LineTable &LT2 = Gen->addLineTable(DWARF32);
+ LT2.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x1234567890abcdef, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xbb);
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
+ EXPECT_FALSE(Recoverable);
+ ASSERT_FALSE(Parser.done());
+ checkError(
+ "unexpected line op length at offset 0x00000030 expected 0x42 found 0x01",
+ std::move(Unrecoverable));
+
+ // Reset the error state so that it does not confuse the next set of checks.
+ Unrecoverable = Error::success();
+ Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
+
+ EXPECT_TRUE(Parser.done());
+ checkError("last sequence in debug line table is not terminated!",
+ std::move(Recoverable));
+ EXPECT_FALSE(Unrecoverable);
+}
+
+TEST_F(DebugLineBasicFixture,
+ ParserReportsPrologueErrorsInEachTableWhenSkipping) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
+ LineTable &LT2 = Gen->addLineTable(DWARF32);
+ LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.skip(RecordUnrecoverable);
+ ASSERT_FALSE(Parser.done());
+ Parser.skip(RecordUnrecoverable);
+
+ EXPECT_TRUE(Parser.done());
+
+ checkError({"parsing line table prologue at offset 0x00000000 found "
+ "unsupported version 0x00",
+ "parsing line table prologue at offset 0x00000006 found "
+ "unsupported version 0x01"},
+ std::move(Unrecoverable));
+}
+
+TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.skip(RecordUnrecoverable);
+
+ EXPECT_TRUE(Parser.done());
+ EXPECT_FALSE(Unrecoverable);
+}
+
+} // end anonymous namespace
diff --git a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp
index c552623a7866..582b58062c45 100644
--- a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp
+++ b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp
@@ -20,59 +20,6 @@ using namespace dwarf;
namespace {
-TEST(DWARFFormValue, FixedFormSizes) {
- Optional<uint8_t> RefSize;
- Optional<uint8_t> AddrSize;
-
- // Test 32 bit DWARF version 2 with 4 byte addresses.
- DWARFFormParams Params_2_4_32 = {2, 4, DWARF32};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_4_32);
- AddrSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_4_32);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_TRUE(AddrSize.hasValue());
- EXPECT_EQ(*RefSize, *AddrSize);
-
- // Test 32 bit DWARF version 2 with 8 byte addresses.
- DWARFFormParams Params_2_8_32 = {2, 8, DWARF32};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_8_32);
- AddrSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_8_32);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_TRUE(AddrSize.hasValue());
- EXPECT_EQ(*RefSize, *AddrSize);
-
- // DW_FORM_ref_addr is 4 bytes in DWARF 32 in DWARF version 3 and beyond.
- DWARFFormParams Params_3_4_32 = {3, 4, DWARF32};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_3_4_32);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_EQ(*RefSize, 4);
-
- DWARFFormParams Params_4_4_32 = {4, 4, DWARF32};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_4_4_32);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_EQ(*RefSize, 4);
-
- DWARFFormParams Params_5_4_32 = {5, 4, DWARF32};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_5_4_32);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_EQ(*RefSize, 4);
-
- // DW_FORM_ref_addr is 8 bytes in DWARF 64 in DWARF version 3 and beyond.
- DWARFFormParams Params_3_8_64 = {3, 8, DWARF64};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_3_8_64);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_EQ(*RefSize, 8);
-
- DWARFFormParams Params_4_8_64 = {4, 8, DWARF64};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_4_8_64);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_EQ(*RefSize, 8);
-
- DWARFFormParams Params_5_8_64 = {5, 8, DWARF64};
- RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_5_8_64);
- EXPECT_TRUE(RefSize.hasValue());
- EXPECT_EQ(*RefSize, 8);
-}
-
bool isFormClass(dwarf::Form Form, DWARFFormValue::FormClass FC) {
return DWARFFormValue(Form).isFormClass(FC);
}
diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp
index 3aa52a0d5b8f..adc400c2fd0e 100644
--- a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp
+++ b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp
@@ -22,13 +22,15 @@
#include "llvm/MC/MCDwarf.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/MC/MCTargetOptionsCommandFlags.def"
+#include "llvm/MC/MCTargetOptionsCommandFlags.inc"
#include "llvm/PassAnalysisSupport.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
@@ -52,17 +54,36 @@ void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, uint64_t U) {
DIEInteger(U));
}
+void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, const MCExpr &Expr) {
+ auto &DG = CU->getGenerator();
+ Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
+ DIEExpr(&Expr));
+}
+
void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form,
StringRef String) {
auto &DG = CU->getGenerator();
- if (Form == DW_FORM_string) {
+ switch (Form) {
+ case DW_FORM_string:
Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
new (DG.getAllocator())
DIEInlineString(String, DG.getAllocator()));
- } else {
+ break;
+
+ case DW_FORM_strp:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_strx:
+ case DW_FORM_strx1:
+ case DW_FORM_strx2:
+ case DW_FORM_strx3:
+ case DW_FORM_strx4:
Die->addValue(
DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
DIEString(DG.getStringPool().getEntry(*DG.getAsmPrinter(), String)));
+ break;
+
+ default:
+ llvm_unreachable("Unhandled form!");
}
}
@@ -95,6 +116,24 @@ void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form) {
DIEInteger(1));
}
+void dwarfgen::DIE::addStrOffsetsBaseAttribute() {
+ auto &DG = CU->getGenerator();
+ auto &MC = *DG.getMCContext();
+ AsmPrinter *Asm = DG.getAsmPrinter();
+
+ const MCSymbol *SectionStart =
+ Asm->getObjFileLowering().getDwarfStrOffSection()->getBeginSymbol();
+
+ const MCExpr *Expr =
+ MCSymbolRefExpr::create(DG.getStringOffsetsStartSym(), MC);
+
+ if (!Asm->MAI->doesDwarfUseRelocationsAcrossSections())
+ Expr = MCBinaryExpr::createSub(
+ Expr, MCSymbolRefExpr::create(SectionStart, MC), MC);
+
+ addAttribute(dwarf::DW_AT_str_offsets_base, DW_FORM_sec_offset, *Expr);
+}
+
dwarfgen::DIE dwarfgen::DIE::addChild(dwarf::Tag Tag) {
auto &DG = CU->getGenerator();
return dwarfgen::DIE(CU,
@@ -106,6 +145,229 @@ dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() {
}
//===----------------------------------------------------------------------===//
+/// dwarfgen::LineTable implementation.
+//===----------------------------------------------------------------------===//
+DWARFDebugLine::Prologue dwarfgen::LineTable::createBasicPrologue() const {
+ DWARFDebugLine::Prologue P;
+ switch (Version) {
+ case 2:
+ case 3:
+ P.TotalLength = 41;
+ P.PrologueLength = 35;
+ break;
+ case 4:
+ P.TotalLength = 42;
+ P.PrologueLength = 36;
+ break;
+ case 5:
+ P.TotalLength = 47;
+ P.PrologueLength = 39;
+ P.FormParams.AddrSize = AddrSize;
+ break;
+ default:
+ llvm_unreachable("unsupported version");
+ }
+ if (Format == DWARF64) {
+ P.TotalLength += 4;
+ P.FormParams.Format = DWARF64;
+ }
+ P.FormParams.Version = Version;
+ P.MinInstLength = 1;
+ P.MaxOpsPerInst = 1;
+ P.DefaultIsStmt = 1;
+ P.LineBase = -5;
+ P.LineRange = 14;
+ P.OpcodeBase = 13;
+ P.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};
+ P.IncludeDirectories.push_back(DWARFFormValue(DW_FORM_string));
+ P.IncludeDirectories.back().setPValue("a dir");
+ P.FileNames.push_back(DWARFDebugLine::FileNameEntry());
+ P.FileNames.back().Name.setPValue("a file");
+ P.FileNames.back().Name.setForm(DW_FORM_string);
+ return P;
+}
+
+void dwarfgen::LineTable::setPrologue(DWARFDebugLine::Prologue NewPrologue) {
+ Prologue = NewPrologue;
+ CustomPrologue.clear();
+}
+
+void dwarfgen::LineTable::setCustomPrologue(
+ ArrayRef<ValueAndLength> NewPrologue) {
+ Prologue.reset();
+ CustomPrologue = NewPrologue;
+}
+
+void dwarfgen::LineTable::addByte(uint8_t Value) {
+ Contents.push_back({Value, Byte});
+}
+
+void dwarfgen::LineTable::addStandardOpcode(uint8_t Opcode,
+ ArrayRef<ValueAndLength> Operands) {
+ Contents.push_back({Opcode, Byte});
+ Contents.insert(Contents.end(), Operands.begin(), Operands.end());
+}
+
+void dwarfgen::LineTable::addExtendedOpcode(uint64_t Length, uint8_t Opcode,
+ ArrayRef<ValueAndLength> Operands) {
+ Contents.push_back({0, Byte});
+ Contents.push_back({Length, ULEB});
+ Contents.push_back({Opcode, Byte});
+ Contents.insert(Contents.end(), Operands.begin(), Operands.end());
+}
+
+void dwarfgen::LineTable::generate(MCContext &MC, AsmPrinter &Asm) const {
+ MC.setDwarfVersion(Version);
+
+ MCSymbol *EndSymbol = nullptr;
+ if (!CustomPrologue.empty()) {
+ writeData(CustomPrologue, Asm);
+ } else if (!Prologue) {
+ EndSymbol = writeDefaultPrologue(Asm);
+ } else {
+ writePrologue(Asm);
+ }
+
+ writeData(Contents, Asm);
+ if (EndSymbol != nullptr)
+ Asm.OutStreamer->EmitLabel(EndSymbol);
+}
+
+void dwarfgen::LineTable::writeData(ArrayRef<ValueAndLength> Data,
+ AsmPrinter &Asm) const {
+ for (auto Entry : Data) {
+ switch (Entry.Length) {
+ case Byte:
+ case Half:
+ case Long:
+ case Quad:
+ Asm.OutStreamer->EmitIntValue(Entry.Value, Entry.Length);
+ continue;
+ case ULEB:
+ Asm.EmitULEB128(Entry.Value);
+ continue;
+ case SLEB:
+ Asm.EmitSLEB128(Entry.Value);
+ continue;
+ }
+ llvm_unreachable("unsupported ValueAndLength Length value");
+ }
+}
+
+MCSymbol *dwarfgen::LineTable::writeDefaultPrologue(AsmPrinter &Asm) const {
+ MCSymbol *UnitStart = Asm.createTempSymbol("line_unit_start");
+ MCSymbol *UnitEnd = Asm.createTempSymbol("line_unit_end");
+ if (Format == DwarfFormat::DWARF64) {
+ Asm.emitInt32(0xffffffff);
+ Asm.EmitLabelDifference(UnitEnd, UnitStart, 8);
+ } else {
+ Asm.EmitLabelDifference(UnitEnd, UnitStart, 4);
+ }
+ Asm.OutStreamer->EmitLabel(UnitStart);
+ Asm.emitInt16(Version);
+ if (Version == 5) {
+ Asm.emitInt8(AddrSize);
+ Asm.emitInt8(SegSize);
+ }
+
+ MCSymbol *PrologueStart = Asm.createTempSymbol("line_prologue_start");
+ MCSymbol *PrologueEnd = Asm.createTempSymbol("line_prologue_end");
+ Asm.EmitLabelDifference(PrologueEnd, PrologueStart,
+ Format == DwarfFormat::DWARF64 ? 8 : 4);
+ Asm.OutStreamer->EmitLabel(PrologueStart);
+
+ DWARFDebugLine::Prologue DefaultPrologue = createBasicPrologue();
+ writeProloguePayload(DefaultPrologue, Asm);
+ Asm.OutStreamer->EmitLabel(PrologueEnd);
+ return UnitEnd;
+}
+
+void dwarfgen::LineTable::writePrologue(AsmPrinter &Asm) const {
+ if (Format == DwarfFormat::DWARF64) {
+ Asm.emitInt32(0xffffffff);
+ Asm.emitInt64(Prologue->TotalLength);
+ } else {
+ Asm.emitInt32(Prologue->TotalLength);
+ }
+ Asm.emitInt16(Prologue->getVersion());
+ if (Version == 5) {
+ Asm.emitInt8(Prologue->getAddressSize());
+ Asm.emitInt8(Prologue->SegSelectorSize);
+ }
+ if (Format == DwarfFormat::DWARF64)
+ Asm.emitInt64(Prologue->PrologueLength);
+ else
+ Asm.emitInt32(Prologue->PrologueLength);
+
+ writeProloguePayload(*Prologue, Asm);
+}
+
+static void writeCString(StringRef Str, AsmPrinter &Asm) {
+ Asm.OutStreamer->EmitBytes(Str);
+ Asm.emitInt8(0);
+}
+
+static void writeV2IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,
+ AsmPrinter &Asm) {
+ for (auto Include : Prologue.IncludeDirectories) {
+ assert(Include.getAsCString() && "expected a string form for include dir");
+ writeCString(*Include.getAsCString(), Asm);
+ }
+ Asm.emitInt8(0);
+
+ for (auto File : Prologue.FileNames) {
+ assert(File.Name.getAsCString() && "expected a string form for file name");
+ writeCString(*File.Name.getAsCString(), Asm);
+ Asm.EmitULEB128(File.DirIdx);
+ Asm.EmitULEB128(File.ModTime);
+ Asm.EmitULEB128(File.Length);
+ }
+ Asm.emitInt8(0);
+}
+
+static void writeV5IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,
+ AsmPrinter &Asm) {
+ Asm.emitInt8(1); // directory_entry_format_count.
+ // TODO: Add support for other content descriptions - we currently only
+ // support a single DW_LNCT_path/DW_FORM_string.
+ Asm.EmitULEB128(DW_LNCT_path);
+ Asm.EmitULEB128(DW_FORM_string);
+ Asm.EmitULEB128(Prologue.IncludeDirectories.size());
+ for (auto Include : Prologue.IncludeDirectories) {
+ assert(Include.getAsCString() && "expected a string form for include dir");
+ writeCString(*Include.getAsCString(), Asm);
+ }
+
+ Asm.emitInt8(1); // file_name_entry_format_count.
+ Asm.EmitULEB128(DW_LNCT_path);
+ Asm.EmitULEB128(DW_FORM_string);
+ Asm.EmitULEB128(Prologue.FileNames.size());
+ for (auto File : Prologue.FileNames) {
+ assert(File.Name.getAsCString() && "expected a string form for file name");
+ writeCString(*File.Name.getAsCString(), Asm);
+ }
+}
+
+void dwarfgen::LineTable::writeProloguePayload(
+ const DWARFDebugLine::Prologue &Prologue, AsmPrinter &Asm) const {
+ Asm.emitInt8(Prologue.MinInstLength);
+ if (Version >= 4)
+ Asm.emitInt8(Prologue.MaxOpsPerInst);
+ Asm.emitInt8(Prologue.DefaultIsStmt);
+ Asm.emitInt8(Prologue.LineBase);
+ Asm.emitInt8(Prologue.LineRange);
+ Asm.emitInt8(Prologue.OpcodeBase);
+ for (auto Length : Prologue.StandardOpcodeLengths) {
+ Asm.emitInt8(Length);
+ }
+
+ if (Version < 5)
+ writeV2IncludeAndFileTable(Prologue, Asm);
+ else
+ writeV5IncludeAndFileTable(Prologue, Asm);
+}
+
+//===----------------------------------------------------------------------===//
/// dwarfgen::Generator implementation.
//===----------------------------------------------------------------------===//
@@ -148,12 +410,13 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {
return make_error<StringError>("no asm info for target " + TripleName,
inconvertibleErrorCode());
- MOFI.reset(new MCObjectFileInfo);
- MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get()));
- MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, *MC);
+ MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ if (!MSTI)
+ return make_error<StringError>("no subtarget info for target " + TripleName,
+ inconvertibleErrorCode());
MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
- MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "", MCOptions);
+ MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions);
if (!MAB)
return make_error<StringError>("no asm backend for target " + TripleName,
inconvertibleErrorCode());
@@ -164,11 +427,16 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {
TripleName,
inconvertibleErrorCode());
- MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
- if (!MSTI)
- return make_error<StringError>("no subtarget info for target " + TripleName,
+ TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(),
+ None));
+ if (!TM)
+ return make_error<StringError>("no target machine for target " + TripleName,
inconvertibleErrorCode());
+ TLOF = TM->getObjFileLowering();
+ MC.reset(new MCContext(MAI.get(), MRI.get(), TLOF));
+ TLOF->Initialize(*MC, *TM);
+
MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC);
if (!MCE)
return make_error<StringError>("no code emitter for target " + TripleName,
@@ -177,22 +445,17 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {
Stream = make_unique<raw_svector_ostream>(FileBytes);
MS = TheTarget->createMCObjectStreamer(
- TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB), *Stream,
- std::unique_ptr<MCCodeEmitter>(MCE), *MSTI, MCOptions.MCRelaxAll,
- MCOptions.MCIncrementalLinkerCompatible,
+ TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB),
+ MAB->createObjectWriter(*Stream), std::unique_ptr<MCCodeEmitter>(MCE),
+ *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ false);
if (!MS)
return make_error<StringError>("no object streamer for target " +
TripleName,
inconvertibleErrorCode());
- // Finally create the AsmPrinter we'll use to emit the DIEs.
- TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(),
- None));
- if (!TM)
- return make_error<StringError>("no target machine for target " + TripleName,
- inconvertibleErrorCode());
+ // Finally create the AsmPrinter we'll use to emit the DIEs.
Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS)));
if (!Asm)
return make_error<StringError>("no asm printer for target " + TripleName,
@@ -203,6 +466,7 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {
Asm->setDwarfVersion(Version);
StringPool = llvm::make_unique<DwarfStringPool>(Allocator, *Asm, StringRef());
+ StringOffsetsStartSym = Asm->createTempSymbol("str_offsets_base");
return Error::success();
}
@@ -223,27 +487,36 @@ StringRef dwarfgen::Generator::generate() {
SecOffset += CUOffset;
CU->setLength(CUOffset - 4);
}
- Abbreviations.Emit(Asm.get(), MOFI->getDwarfAbbrevSection());
- StringPool->emit(*Asm, MOFI->getDwarfStrSection());
- MS->SwitchSection(MOFI->getDwarfInfoSection());
+ Abbreviations.Emit(Asm.get(), TLOF->getDwarfAbbrevSection());
+
+ StringPool->emitStringOffsetsTableHeader(*Asm, TLOF->getDwarfStrOffSection(),
+ StringOffsetsStartSym);
+ StringPool->emit(*Asm, TLOF->getDwarfStrSection(),
+ TLOF->getDwarfStrOffSection());
+
+ MS->SwitchSection(TLOF->getDwarfInfoSection());
for (auto &CU : CompileUnits) {
uint16_t Version = CU->getVersion();
auto Length = CU->getLength();
MC->setDwarfVersion(Version);
assert(Length != -1U);
- Asm->EmitInt32(Length);
- Asm->EmitInt16(Version);
+ Asm->emitInt32(Length);
+ Asm->emitInt16(Version);
if (Version <= 4) {
- Asm->EmitInt32(0);
- Asm->EmitInt8(CU->getAddressSize());
+ Asm->emitInt32(0);
+ Asm->emitInt8(CU->getAddressSize());
} else {
- Asm->EmitInt8(dwarf::DW_UT_compile);
- Asm->EmitInt8(CU->getAddressSize());
- Asm->EmitInt32(0);
+ Asm->emitInt8(dwarf::DW_UT_compile);
+ Asm->emitInt8(CU->getAddressSize());
+ Asm->emitInt32(0);
}
Asm->emitDwarfDIE(*CU->getUnitDIE().Die);
}
+ MS->SwitchSection(TLOF->getDwarfLineSection());
+ for (auto &LT : LineTables)
+ LT->generate(*MC, *Asm);
+
MS->Finish();
if (FileBytes.empty())
return StringRef();
@@ -263,7 +536,13 @@ bool dwarfgen::Generator::saveFile(StringRef Path) {
}
dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() {
- CompileUnits.push_back(std::unique_ptr<CompileUnit>(
- new CompileUnit(*this, Version, Asm->getPointerSize())));
+ CompileUnits.push_back(
+ make_unique<CompileUnit>(*this, Version, Asm->getPointerSize()));
return *CompileUnits.back();
}
+
+dwarfgen::LineTable &dwarfgen::Generator::addLineTable(DwarfFormat Format) {
+ LineTables.push_back(
+ make_unique<LineTable>(Version, Format, Asm->getPointerSize()));
+ return *LineTables.back();
+}
diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.h b/unittests/DebugInfo/DWARF/DwarfGenerator.h
index dd7e8709638d..40ecaa98d053 100644
--- a/unittests/DebugInfo/DWARF/DwarfGenerator.h
+++ b/unittests/DebugInfo/DWARF/DwarfGenerator.h
@@ -36,11 +36,11 @@ class MCCodeEmitter;
class MCContext;
struct MCDwarfLineTableParams;
class MCInstrInfo;
-class MCObjectFileInfo;
class MCRegisterInfo;
class MCStreamer;
class MCSubtargetInfo;
class raw_fd_ostream;
+class TargetLoweringObjectFile;
class TargetMachine;
class Triple;
@@ -89,6 +89,14 @@ public:
/// \param U the unsigned integer to encode.
void addAttribute(uint16_t Attr, dwarf::Form Form, uint64_t U);
+ /// Add an attribute value to be encoded as a DIEExpr
+ ///
+ /// \param Attr a dwarf::Attribute enumeration value or any uint16_t that
+ /// represents a user defined DWARF attribute.
+ /// \param Form the dwarf::Form to use when encoding the attribute.
+ /// \param Expr the MC expression used to compute the value.
+ void addAttribute(uint16_t Attr, dwarf::Form Form, const MCExpr &Expr);
+
/// Add an attribute value to be encoded as a DIEString or DIEInlinedString.
///
/// \param Attr a dwarf::Attribute enumeration value or any uint16_t that
@@ -123,6 +131,9 @@ public:
/// \param S the size in bytes of the data pointed to by P .
void addAttribute(uint16_t Attr, dwarf::Form Form, const void *P, size_t S);
+ /// Add a DW_AT_str_offsets_base attribute to this DIE.
+ void addStrOffsetsBaseAttribute();
+
/// Add a new child to this DIE object.
///
/// \param Tag the dwarf::Tag to assing to the llvm::DIE object.
@@ -153,6 +164,72 @@ public:
void setLength(uint64_t Length) { DU.setLength(Length); }
};
+/// A DWARF line unit-like class used to generate DWARF line units.
+///
+/// Instances of this class are created by instances of the Generator class.
+class LineTable {
+public:
+ enum ValueLength { Byte = 1, Half = 2, Long = 4, Quad = 8, ULEB, SLEB };
+
+ struct ValueAndLength {
+ uint64_t Value;
+ ValueLength Length;
+ };
+
+ LineTable(uint16_t Version, dwarf::DwarfFormat Format, uint8_t AddrSize,
+ uint8_t SegSize = 0)
+ : Version(Version), Format(Format), AddrSize(AddrSize), SegSize(SegSize) {
+ assert(Version >= 2 && Version <= 5 && "unsupported version");
+ }
+
+ // Create a Prologue suitable to pass to setPrologue, with a single file and
+ // include directory entry.
+ DWARFDebugLine::Prologue createBasicPrologue() const;
+
+ // Set or replace the current prologue with the specified prologue. If no
+ // prologue is set, a default one will be used when generating.
+ void setPrologue(DWARFDebugLine::Prologue NewPrologue);
+ // Used to write an arbitrary payload instead of the standard prologue. This
+ // is useful if you wish to test handling of corrupt .debug_line sections.
+ void setCustomPrologue(ArrayRef<ValueAndLength> NewPrologue);
+
+ // Add a byte to the program, with the given value. This can be used to
+ // specify a special opcode, or to add arbitrary contents to the section.
+ void addByte(uint8_t Value);
+ // Add a standard opcode to the program. The opcode and operands do not have
+ // to be valid.
+ void addStandardOpcode(uint8_t Opcode, ArrayRef<ValueAndLength> Operands);
+ // Add an extended opcode to the program with the specified length, opcode,
+ // and operands. These values do not have to be valid.
+ void addExtendedOpcode(uint64_t Length, uint8_t Opcode,
+ ArrayRef<ValueAndLength> Operands);
+
+ // Write the contents of the LineUnit to the current section in the generator.
+ void generate(MCContext &MC, AsmPrinter &Asm) const;
+
+private:
+ void writeData(ArrayRef<ValueAndLength> Data, AsmPrinter &Asm) const;
+ MCSymbol *writeDefaultPrologue(AsmPrinter &Asm) const;
+ void writePrologue(AsmPrinter &Asm) const;
+
+ void writeProloguePayload(const DWARFDebugLine::Prologue &Prologue,
+ AsmPrinter &Asm) const;
+
+ llvm::Optional<DWARFDebugLine::Prologue> Prologue;
+ std::vector<ValueAndLength> CustomPrologue;
+ std::vector<ValueAndLength> Contents;
+
+ // The Version field is used for determining how to write the Prologue, if a
+ // non-custom prologue is used. The version value actually written, will be
+ // that specified in the Prologue, if a custom prologue has been passed in.
+ // Otherwise, it will be this value.
+ uint16_t Version;
+
+ dwarf::DwarfFormat Format;
+ uint8_t AddrSize;
+ uint8_t SegSize;
+};
+
/// A DWARF generator.
///
/// Generate DWARF for unit tests by creating any instance of this class and
@@ -161,7 +238,6 @@ public:
class Generator {
std::unique_ptr<MCRegisterInfo> MRI;
std::unique_ptr<MCAsmInfo> MAI;
- std::unique_ptr<MCObjectFileInfo> MOFI;
std::unique_ptr<MCContext> MC;
MCAsmBackend *MAB; // Owned by MCStreamer
std::unique_ptr<MCInstrInfo> MII;
@@ -169,12 +245,16 @@ class Generator {
MCCodeEmitter *MCE; // Owned by MCStreamer
MCStreamer *MS; // Owned by AsmPrinter
std::unique_ptr<TargetMachine> TM;
+ TargetLoweringObjectFile *TLOF; // Owned by TargetMachine;
std::unique_ptr<AsmPrinter> Asm;
BumpPtrAllocator Allocator;
std::unique_ptr<DwarfStringPool> StringPool; // Entries owned by Allocator.
std::vector<std::unique_ptr<CompileUnit>> CompileUnits;
+ std::vector<std::unique_ptr<LineTable>> LineTables;
DIEAbbrevSet Abbreviations;
+ MCSymbol *StringOffsetsStartSym;
+
SmallString<4096> FileBytes;
/// The stream we use to generate the DWARF into as an ELF file.
std::unique_ptr<raw_svector_ostream> Stream;
@@ -210,14 +290,23 @@ public:
///
/// \returns a dwarfgen::CompileUnit that can be used to retrieve the compile
/// unit dwarfgen::DIE that can be used to add attributes and add child DIE
- /// objedts to.
+ /// objects to.
dwarfgen::CompileUnit &addCompileUnit();
+ /// Add a line table unit to be generated.
+ /// \param DwarfFormat the DWARF format to use (DWARF32 or DWARF64).
+ ///
+ /// \returns a dwarfgen::LineTable that can be used to customise the contents
+ /// of the line table.
+ LineTable &
+ addLineTable(dwarf::DwarfFormat DwarfFormat = dwarf::DwarfFormat::DWARF32);
+
BumpPtrAllocator &getAllocator() { return Allocator; }
AsmPrinter *getAsmPrinter() const { return Asm.get(); }
MCContext *getMCContext() const { return MC.get(); }
DIEAbbrevSet &getAbbrevSet() { return Abbreviations; }
DwarfStringPool &getStringPool() { return *StringPool; }
+ MCSymbol *getStringOffsetsStartSym() const { return StringOffsetsStartSym; }
/// Save the generated DWARF file to disk.
///
diff --git a/unittests/DebugInfo/DWARF/DwarfUtils.cpp b/unittests/DebugInfo/DWARF/DwarfUtils.cpp
new file mode 100644
index 000000000000..ef43c13db4eb
--- /dev/null
+++ b/unittests/DebugInfo/DWARF/DwarfUtils.cpp
@@ -0,0 +1,43 @@
+//===--- unittests/DebugInfo/DWARF/DwarfUtils.cpp ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DwarfUtils.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+using namespace llvm;
+
+static void initLLVMIfNeeded() {
+ static bool gInitialized = false;
+ if (!gInitialized) {
+ gInitialized = true;
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+ }
+}
+
+Triple llvm::dwarf::utils::getHostTripleForAddrSize(uint8_t AddrSize) {
+ Triple T(Triple::normalize(LLVM_HOST_TRIPLE));
+
+ if (AddrSize == 8 && T.isArch32Bit())
+ return T.get64BitArchVariant();
+ if (AddrSize == 4 && T.isArch64Bit())
+ return T.get32BitArchVariant();
+ return T;
+}
+
+bool llvm::dwarf::utils::isConfigurationSupported(Triple &T) {
+ initLLVMIfNeeded();
+ std::string Err;
+ return TargetRegistry::lookupTarget(T.getTriple(), Err);
+}
diff --git a/unittests/DebugInfo/DWARF/DwarfUtils.h b/unittests/DebugInfo/DWARF/DwarfUtils.h
new file mode 100644
index 000000000000..3d8045679d54
--- /dev/null
+++ b/unittests/DebugInfo/DWARF/DwarfUtils.h
@@ -0,0 +1,29 @@
+//===--- unittests/DebugInfo/DWARF/DwarfUtils.h -----------------*- 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_DEBUG_INFO_DWARF_DWARFUTILS_H
+#define LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H
+
+#include <cstdint>
+
+namespace llvm {
+
+class Triple;
+
+namespace dwarf {
+namespace utils {
+
+Triple getHostTripleForAddrSize(uint8_t AddrSize);
+bool isConfigurationSupported(Triple &T);
+
+} // end namespace utils
+} // end namespace dwarf
+} // end namespace llvm
+
+#endif // LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H
diff --git a/unittests/DebugInfo/MSF/CMakeLists.txt b/unittests/DebugInfo/MSF/CMakeLists.txt
index 20f3b2ab3dcd..0e48ab8f2f6b 100644
--- a/unittests/DebugInfo/MSF/CMakeLists.txt
+++ b/unittests/DebugInfo/MSF/CMakeLists.txt
@@ -2,14 +2,10 @@ set(LLVM_LINK_COMPONENTS
DebugInfoMSF
)
-set(DebugInfoMSFSources
+add_llvm_unittest(DebugInfoMSFTests
MappedBlockStreamTest.cpp
MSFBuilderTest.cpp
MSFCommonTest.cpp
)
-add_llvm_unittest(DebugInfoMSFTests
- ${DebugInfoMSFSources}
- )
-
target_link_libraries(DebugInfoMSFTests PRIVATE LLVMTestingSupport)
diff --git a/unittests/DebugInfo/MSF/MSFBuilderTest.cpp b/unittests/DebugInfo/MSF/MSFBuilderTest.cpp
index a91ac8d443fe..16247951804c 100644
--- a/unittests/DebugInfo/MSF/MSFBuilderTest.cpp
+++ b/unittests/DebugInfo/MSF/MSFBuilderTest.cpp
@@ -112,7 +112,7 @@ TEST_F(MSFBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
auto &Msf = *ExpectedMsf;
- auto ExpectedL1 = Msf.build();
+ auto ExpectedL1 = Msf.generateLayout();
EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());
MSFLayout &L1 = *ExpectedL1;
@@ -129,7 +129,7 @@ TEST_F(MSFBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {
auto Blocks = Msf2.getStreamBlocks(0);
EXPECT_EQ(1U, Blocks.size());
- auto ExpectedL2 = Msf2.build();
+ auto ExpectedL2 = Msf2.generateLayout();
EXPECT_THAT_EXPECTED(ExpectedL2, Succeeded());
MSFLayout &L2 = *ExpectedL2;
auto NewDirBlocks = L2.DirectoryBlocks;
@@ -149,7 +149,7 @@ TEST_F(MSFBuilderTest, TestAddStreamWithDirectoryBlockIncrease) {
EXPECT_THAT_EXPECTED(Msf.addStream(4096 * 4096 / sizeof(uint32_t)),
Succeeded());
- auto ExpectedL1 = Msf.build();
+ auto ExpectedL1 = Msf.generateLayout();
EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());
MSFLayout &L1 = *ExpectedL1;
auto DirBlocks = L1.DirectoryBlocks;
@@ -289,7 +289,7 @@ TEST_F(MSFBuilderTest, BuildMsfLayout) {
}
++ExpectedNumBlocks; // The directory itself should use 1 block
- auto ExpectedLayout = Msf.build();
+ auto ExpectedLayout = Msf.generateLayout();
EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());
MSFLayout &L = *ExpectedLayout;
EXPECT_EQ(4096U, L.SB->BlockSize);
@@ -316,7 +316,7 @@ TEST_F(MSFBuilderTest, UseDirectoryBlockHint) {
EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1}), Succeeded());
EXPECT_THAT_EXPECTED(Msf.addStream(2048, {B + 2}), Succeeded());
- auto ExpectedLayout = Msf.build();
+ auto ExpectedLayout = Msf.generateLayout();
EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());
MSFLayout &L = *ExpectedLayout;
EXPECT_EQ(msf::getMinimumBlockCount() + 2, L.SB->NumBlocks);
@@ -338,7 +338,7 @@ TEST_F(MSFBuilderTest, DirectoryBlockHintInsufficient) {
uint32_t Size = 4096 * 4096 / 4;
EXPECT_THAT_EXPECTED(Msf.addStream(Size), Succeeded());
- auto ExpectedLayout = Msf.build();
+ auto ExpectedLayout = Msf.generateLayout();
EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());
MSFLayout &L = *ExpectedLayout;
EXPECT_EQ(2U, L.DirectoryBlocks.size());
@@ -356,7 +356,7 @@ TEST_F(MSFBuilderTest, DirectoryBlockHintOverestimated) {
ASSERT_THAT_EXPECTED(Msf.addStream(2048), Succeeded());
- auto ExpectedLayout = Msf.build();
+ auto ExpectedLayout = Msf.generateLayout();
ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());
MSFLayout &L = *ExpectedLayout;
EXPECT_EQ(1U, L.DirectoryBlocks.size());
@@ -376,7 +376,7 @@ TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) {
Expected<uint32_t> SN = Msf.addStream(StreamSize);
ASSERT_THAT_EXPECTED(SN, Succeeded());
- auto ExpectedLayout = Msf.build();
+ auto ExpectedLayout = Msf.generateLayout();
ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());
MSFLayout &L = *ExpectedLayout;
auto BlocksRef = L.StreamMap[*SN];
@@ -384,10 +384,9 @@ TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) {
EXPECT_EQ(StreamSize, L.StreamSizes[*SN]);
for (uint32_t I = 0; I <= 3; ++I) {
- // Pages from the regular FPM are allocated, while pages from the alt fpm
- // are free.
+ // Pages from both FPMs are always allocated.
+ EXPECT_FALSE(L.FreePageMap.test(2 + I * 4096));
EXPECT_FALSE(L.FreePageMap.test(1 + I * 4096));
- EXPECT_TRUE(L.FreePageMap.test(2 + I * 4096));
}
for (uint32_t I = 1; I <= 3; ++I) {
diff --git a/unittests/DebugInfo/MSF/MSFCommonTest.cpp b/unittests/DebugInfo/MSF/MSFCommonTest.cpp
index 144f5b113fb5..ee9ac75d15ce 100644
--- a/unittests/DebugInfo/MSF/MSFCommonTest.cpp
+++ b/unittests/DebugInfo/MSF/MSFCommonTest.cpp
@@ -46,12 +46,47 @@ TEST(MSFCommonTest, FpmIntervals) {
EXPECT_EQ(1u, getNumFpmIntervals(L, true));
SB.NumBlocks = SB.BlockSize;
EXPECT_EQ(1u, getNumFpmIntervals(L, true));
- SB.NumBlocks = SB.BlockSize + 1;
- EXPECT_EQ(2u, getNumFpmIntervals(L, true));
SB.NumBlocks = SB.BlockSize * 8;
EXPECT_EQ(8u, getNumFpmIntervals(L, true));
- SB.NumBlocks = SB.BlockSize * 8 + 1;
- EXPECT_EQ(9u, getNumFpmIntervals(L, true));
+
+ // The FPM is going to look like this:
+ // | 0 | 1 | 2 | ... | 4096 | 4097 | 4098 | ... |
+ // | SB | FPM0 | FPM1 | Data | Data | FPM0 | FPM1 | ... |
+ //
+ // So when there are up to 4097 blocks (last index 4096), the final blocks
+ // are data blocks. When there are 4098 blocks (last index 4097), there is
+ // one terminating FPM block, and when there are 4099 blocks, there are two
+ // terminating FPM blocks. Make sure all these cases are handled.
+
+ // With 4096 or 4097 blocks, the last block is a data block so we only have
+ // 1 interval.
+ for (uint32_t I : {4096, 4097}) {
+ // 1 FPM0 interval
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 1));
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 1));
+
+ // 1 FPM1 interval
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 2));
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 2));
+ }
+
+ // With 4098 blocks, the last block belongs to FPM0 so we should have 2 FPM0
+ // intervals.
+ EXPECT_EQ(2U, getNumFpmIntervals(4096, 4098, true, 1));
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 1));
+
+ // And 1 FPM1 interval.
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, true, 2));
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 2));
+
+ // With 4099 blocks, the last block belongs to FPM1 so we should have 2
+ // FPM0 intervals.
+ EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 1));
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 1));
+
+ // And 2 FPM1 intervals.
+ EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 2));
+ EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 2));
}
TEST(MSFCommonTest, FpmStreamLayout) {
@@ -95,7 +130,7 @@ TEST(MSFCommonTest, FpmStreamLayout) {
// 2. When we are including unused FPM data, there should be one FPM block
// at every BlockSize interval in the file, even if entire FPM blocks are
// unused.
- SB.NumBlocks = SB.BlockSize * 8 + 1;
+ SB.NumBlocks = SB.BlockSize * 8 + 3;
SL = getFpmStreamLayout(L, true, false);
EXPECT_EQ(SB.BlockSize * 9, SL.Length);
EXPECT_EQ(9u, SL.Blocks.size());
diff --git a/unittests/DebugInfo/PDB/CMakeLists.txt b/unittests/DebugInfo/PDB/CMakeLists.txt
index b19ee2cf43a5..5410e5f895d4 100644
--- a/unittests/DebugInfo/PDB/CMakeLists.txt
+++ b/unittests/DebugInfo/PDB/CMakeLists.txt
@@ -4,14 +4,10 @@ set(LLVM_LINK_COMPONENTS
DebugInfoPDB
)
-set(DebugInfoPDBSources
+add_llvm_unittest(DebugInfoPDBTests
HashTableTest.cpp
StringTableBuilderTest.cpp
PDBApiTest.cpp
)
-add_llvm_unittest(DebugInfoPDBTests
- ${DebugInfoPDBSources}
- )
-
target_link_libraries(DebugInfoPDBTests PRIVATE LLVMTestingSupport)
diff --git a/unittests/DebugInfo/PDB/HashTableTest.cpp b/unittests/DebugInfo/PDB/HashTableTest.cpp
index f1968e55e86f..301b215badf5 100644
--- a/unittests/DebugInfo/PDB/HashTableTest.cpp
+++ b/unittests/DebugInfo/PDB/HashTableTest.cpp
@@ -8,9 +8,14 @@
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/PDB/Native/HashTable.h"
+
+#include "llvm/DebugInfo/PDB/Native/Hash.h"
+#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
+#include "llvm/Support/Allocator.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/BinaryStreamReader.h"
#include "llvm/Support/BinaryStreamWriter.h"
+#include "llvm/Support/StringSaver.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
@@ -22,7 +27,8 @@ using namespace llvm::pdb;
using namespace llvm::support;
namespace {
-class HashTableInternals : public HashTable {
+
+class HashTableInternals : public HashTable<uint32_t> {
public:
using HashTable::Buckets;
using HashTable::Present;
@@ -31,18 +37,18 @@ public:
}
TEST(HashTableTest, TestSimple) {
- HashTable Table;
+ HashTableInternals Table;
EXPECT_EQ(0u, Table.size());
EXPECT_GT(Table.capacity(), 0u);
- Table.set(3, 7);
+ Table.set_as(3u, 7);
EXPECT_EQ(1u, Table.size());
- ASSERT_NE(Table.end(), Table.find(3));
- EXPECT_EQ(7u, Table.get(3));
+ ASSERT_NE(Table.end(), Table.find_as(3u));
+ EXPECT_EQ(7u, Table.get(3u));
}
TEST(HashTableTest, TestCollision) {
- HashTable Table;
+ HashTableInternals Table;
EXPECT_EQ(0u, Table.size());
EXPECT_GT(Table.capacity(), 0u);
@@ -52,39 +58,33 @@ TEST(HashTableTest, TestCollision) {
uint32_t N1 = Table.capacity() + 1;
uint32_t N2 = 2 * N1;
- Table.set(N1, 7);
- Table.set(N2, 12);
+ Table.set_as(N1, 7);
+ Table.set_as(N2, 12);
EXPECT_EQ(2u, Table.size());
- ASSERT_NE(Table.end(), Table.find(N1));
- ASSERT_NE(Table.end(), Table.find(N2));
+ ASSERT_NE(Table.end(), Table.find_as(N1));
+ ASSERT_NE(Table.end(), Table.find_as(N2));
EXPECT_EQ(7u, Table.get(N1));
EXPECT_EQ(12u, Table.get(N2));
}
TEST(HashTableTest, TestRemove) {
- HashTable Table;
+ HashTableInternals Table;
EXPECT_EQ(0u, Table.size());
EXPECT_GT(Table.capacity(), 0u);
- Table.set(1, 2);
- Table.set(3, 4);
+ Table.set_as(1u, 2);
+ Table.set_as(3u, 4);
EXPECT_EQ(2u, Table.size());
- ASSERT_NE(Table.end(), Table.find(1));
- ASSERT_NE(Table.end(), Table.find(3));
-
- EXPECT_EQ(2u, Table.get(1));
- EXPECT_EQ(4u, Table.get(3));
+ ASSERT_NE(Table.end(), Table.find_as(1u));
+ ASSERT_NE(Table.end(), Table.find_as(3u));
- Table.remove(1u);
- EXPECT_EQ(1u, Table.size());
- EXPECT_EQ(Table.end(), Table.find(1));
- ASSERT_NE(Table.end(), Table.find(3));
- EXPECT_EQ(4u, Table.get(3));
+ EXPECT_EQ(2u, Table.get(1u));
+ EXPECT_EQ(4u, Table.get(3u));
}
TEST(HashTableTest, TestCollisionAfterMultipleProbes) {
- HashTable Table;
+ HashTableInternals Table;
EXPECT_EQ(0u, Table.size());
EXPECT_GT(Table.capacity(), 0u);
@@ -95,31 +95,17 @@ TEST(HashTableTest, TestCollisionAfterMultipleProbes) {
uint32_t N2 = N1 + 1;
uint32_t N3 = 2 * N1;
- Table.set(N1, 7);
- Table.set(N2, 11);
- Table.set(N3, 13);
+ Table.set_as(N1, 7);
+ Table.set_as(N2, 11);
+ Table.set_as(N3, 13);
EXPECT_EQ(3u, Table.size());
- ASSERT_NE(Table.end(), Table.find(N1));
- ASSERT_NE(Table.end(), Table.find(N2));
- ASSERT_NE(Table.end(), Table.find(N3));
+ ASSERT_NE(Table.end(), Table.find_as(N1));
+ ASSERT_NE(Table.end(), Table.find_as(N2));
+ ASSERT_NE(Table.end(), Table.find_as(N3));
EXPECT_EQ(7u, Table.get(N1));
EXPECT_EQ(11u, Table.get(N2));
EXPECT_EQ(13u, Table.get(N3));
-
- // Remove the one that had been filled in the middle, then insert another one
- // with a collision. It should fill the newly emptied slot.
- Table.remove(N2);
- uint32_t N4 = N1 * 3;
- Table.set(N4, 17);
- EXPECT_EQ(3u, Table.size());
- ASSERT_NE(Table.end(), Table.find(N1));
- ASSERT_NE(Table.end(), Table.find(N3));
- ASSERT_NE(Table.end(), Table.find(N4));
-
- EXPECT_EQ(7u, Table.get(N1));
- EXPECT_EQ(13u, Table.get(N3));
- EXPECT_EQ(17u, Table.get(N4));
}
TEST(HashTableTest, Grow) {
@@ -127,15 +113,15 @@ TEST(HashTableTest, Grow) {
// guaranteed to trigger a grow. Then verify that the size is the same, the
// capacity is larger, and all the original items are still in the table.
- HashTable Table;
+ HashTableInternals Table;
uint32_t OldCapacity = Table.capacity();
for (uint32_t I = 0; I < OldCapacity; ++I) {
- Table.set(OldCapacity + I * 2 + 1, I * 2 + 3);
+ Table.set_as(OldCapacity + I * 2 + 1, I * 2 + 3);
}
EXPECT_EQ(OldCapacity, Table.size());
EXPECT_GT(Table.capacity(), OldCapacity);
for (uint32_t I = 0; I < OldCapacity; ++I) {
- ASSERT_NE(Table.end(), Table.find(OldCapacity + I * 2 + 1));
+ ASSERT_NE(Table.end(), Table.find_as(OldCapacity + I * 2 + 1));
EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1));
}
}
@@ -144,7 +130,7 @@ TEST(HashTableTest, Serialization) {
HashTableInternals Table;
uint32_t Cap = Table.capacity();
for (uint32_t I = 0; I < Cap; ++I) {
- Table.set(Cap + I * 2 + 1, I * 2 + 3);
+ Table.set_as(Cap + I * 2 + 1, I * 2 + 3);
}
std::vector<uint8_t> Buffer(Table.calculateSerializedLength());
@@ -166,3 +152,109 @@ TEST(HashTableTest, Serialization) {
EXPECT_EQ(Table.Present, Table2.Present);
EXPECT_EQ(Table.Deleted, Table2.Deleted);
}
+
+TEST(HashTableTest, NamedStreamMap) {
+ std::vector<StringRef> Streams = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven"};
+ StringMap<uint32_t> ExpectedIndices;
+ for (uint32_t I = 0; I < Streams.size(); ++I)
+ ExpectedIndices[Streams[I]] = I + 1;
+
+ // To verify the hash table actually works, we want to verify that insertion
+ // order doesn't matter. So try inserting in every possible order of 7 items.
+ do {
+ NamedStreamMap NSM;
+ for (StringRef S : Streams)
+ NSM.set(S, ExpectedIndices[S]);
+
+ EXPECT_EQ(Streams.size(), NSM.size());
+
+ uint32_t N;
+ EXPECT_TRUE(NSM.get("One", N));
+ EXPECT_EQ(1U, N);
+
+ EXPECT_TRUE(NSM.get("Two", N));
+ EXPECT_EQ(2U, N);
+
+ EXPECT_TRUE(NSM.get("Three", N));
+ EXPECT_EQ(3U, N);
+
+ EXPECT_TRUE(NSM.get("Four", N));
+ EXPECT_EQ(4U, N);
+
+ EXPECT_TRUE(NSM.get("Five", N));
+ EXPECT_EQ(5U, N);
+
+ EXPECT_TRUE(NSM.get("Six", N));
+ EXPECT_EQ(6U, N);
+
+ EXPECT_TRUE(NSM.get("Seven", N));
+ EXPECT_EQ(7U, N);
+ } while (std::next_permutation(Streams.begin(), Streams.end()));
+}
+
+namespace {
+struct FooBar {
+ uint32_t X;
+ uint32_t Y;
+};
+
+} // namespace
+
+namespace llvm {
+namespace pdb {
+template <> struct PdbHashTraits<FooBar> {
+ std::vector<char> Buffer;
+
+ PdbHashTraits() { Buffer.push_back(0); }
+
+ uint32_t hashLookupKey(StringRef S) const {
+ return llvm::pdb::hashStringV1(S);
+ }
+
+ StringRef storageKeyToLookupKey(uint32_t N) const {
+ if (N >= Buffer.size())
+ return StringRef();
+
+ return StringRef(Buffer.data() + N);
+ }
+
+ uint32_t lookupKeyToStorageKey(StringRef S) {
+ uint32_t N = Buffer.size();
+ Buffer.insert(Buffer.end(), S.begin(), S.end());
+ Buffer.push_back('\0');
+ return N;
+ }
+};
+} // namespace pdb
+} // namespace llvm
+
+TEST(HashTableTest, NonTrivialValueType) {
+ HashTable<FooBar> Table;
+ uint32_t Cap = Table.capacity();
+ for (uint32_t I = 0; I < Cap; ++I) {
+ FooBar F;
+ F.X = I;
+ F.Y = I + 1;
+ Table.set_as(utostr(I), F);
+ }
+
+ std::vector<uint8_t> Buffer(Table.calculateSerializedLength());
+ MutableBinaryByteStream Stream(Buffer, little);
+ BinaryStreamWriter Writer(Stream);
+ EXPECT_THAT_ERROR(Table.commit(Writer), Succeeded());
+ // We should have written precisely the number of bytes we calculated earlier.
+ EXPECT_EQ(Buffer.size(), Writer.getOffset());
+
+ HashTable<FooBar> Table2;
+ BinaryStreamReader Reader(Stream);
+ EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());
+ // We should have read precisely the number of bytes we calculated earlier.
+ EXPECT_EQ(Buffer.size(), Reader.getOffset());
+
+ EXPECT_EQ(Table.size(), Table2.size());
+ EXPECT_EQ(Table.capacity(), Table2.capacity());
+ // EXPECT_EQ(Table.Buckets, Table2.Buckets);
+ // EXPECT_EQ(Table.Present, Table2.Present);
+ // EXPECT_EQ(Table.Deleted, Table2.Deleted);
+}
diff --git a/unittests/DebugInfo/PDB/PDBApiTest.cpp b/unittests/DebugInfo/PDB/PDBApiTest.cpp
index e998acf009ec..41b679825f17 100644
--- a/unittests/DebugInfo/PDB/PDBApiTest.cpp
+++ b/unittests/DebugInfo/PDB/PDBApiTest.cpp
@@ -11,7 +11,10 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
+#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
+#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
#include "llvm/DebugInfo/PDB/IPDBTable.h"
@@ -63,7 +66,7 @@ namespace {
class MockSession : public IPDBSession {
uint64_t getLoadAddress() const override { return 0; }
- void setLoadAddress(uint64_t Address) override {}
+ bool setLoadAddress(uint64_t Address) override { return false; }
std::unique_ptr<PDBSymbolExe> getGlobalScope() override { return nullptr; }
std::unique_ptr<PDBSymbol> getSymbolById(uint32_t SymbolId) const override {
return nullptr;
@@ -72,11 +75,27 @@ class MockSession : public IPDBSession {
getSourceFileById(uint32_t SymbolId) const override {
return nullptr;
}
-
+ bool addressForVA(uint64_t VA, uint32_t &Section,
+ uint32_t &Offset) const override {
+ return false;
+ }
+ bool addressForRVA(uint32_t RVA, uint32_t &Section,
+ uint32_t &Offset) const override {
+ return false;
+ }
std::unique_ptr<PDBSymbol>
findSymbolByAddress(uint64_t Address, PDB_SymType Type) const override {
return nullptr;
}
+ std::unique_ptr<PDBSymbol> findSymbolByRVA(uint32_t RVA,
+ PDB_SymType Type) const override {
+ return nullptr;
+ }
+ std::unique_ptr<PDBSymbol>
+ findSymbolBySectOffset(uint32_t Sect, uint32_t Offset,
+ PDB_SymType Type) const override {
+ return nullptr;
+ }
std::unique_ptr<IPDBEnumLineNumbers>
findLineNumbers(const PDBSymbolCompiland &Compiland,
const IPDBSourceFile &File) const override {
@@ -86,6 +105,15 @@ class MockSession : public IPDBSession {
findLineNumbersByAddress(uint64_t Address, uint32_t Length) const override {
return nullptr;
}
+ std::unique_ptr<IPDBEnumLineNumbers>
+ findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumLineNumbers>
+ findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset,
+ uint32_t Length) const override {
+ return nullptr;
+ }
std::unique_ptr<IPDBEnumSourceFiles>
findSourceFiles(const PDBSymbolCompiland *Compiland, llvm::StringRef Pattern,
PDB_NameSearchFlags Flags) const override {
@@ -123,6 +151,14 @@ class MockSession : public IPDBSession {
std::unique_ptr<IPDBEnumTables> getEnumTables() const override {
return nullptr;
}
+
+ std::unique_ptr<IPDBEnumInjectedSources> getInjectedSources() const override {
+ return nullptr;
+ }
+
+ std::unique_ptr<IPDBEnumSectionContribs> getSectionContribs() const override {
+ return nullptr;
+ }
};
class MockRawSymbol : public IPDBRawSymbol {
@@ -142,14 +178,48 @@ public:
return nullptr;
}
std::unique_ptr<IPDBEnumSymbols>
+ findChildrenByAddr(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags,
+ uint32_t Section, uint32_t Offset) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumSymbols>
+ findChildrenByVA(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags,
+ uint64_t VA) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumSymbols>
findChildrenByRVA(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags,
uint32_t RVA) const override {
return nullptr;
}
std::unique_ptr<IPDBEnumSymbols>
+ findInlineFramesByAddr(uint32_t Section, uint32_t Offset) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumSymbols>
findInlineFramesByRVA(uint32_t RVA) const override {
return nullptr;
}
+ std::unique_ptr<IPDBEnumSymbols>
+ findInlineFramesByVA(uint64_t VA) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumLineNumbers> findInlineeLines() const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumLineNumbers>
+ findInlineeLinesByAddr(uint32_t Section, uint32_t Offset,
+ uint32_t Length) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumLineNumbers>
+ findInlineeLinesByRVA(uint32_t RVA, uint32_t Length) const override {
+ return nullptr;
+ }
+ std::unique_ptr<IPDBEnumLineNumbers>
+ findInlineeLinesByVA(uint64_t VA, uint32_t Length) const override {
+ return nullptr;
+ }
void getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) const override {}
void getFrontEndVersion(VersionInfo &Version) const override {}
@@ -161,6 +231,10 @@ public:
return {};
}
+ std::unique_ptr<IPDBLineNumber> getSrcLineOnTypeDefn() const override {
+ return nullptr;
+ }
+
MOCK_SYMBOL_ACCESSOR(getAccess)
MOCK_SYMBOL_ACCESSOR(getAddressOffset)
MOCK_SYMBOL_ACCESSOR(getAddressSection)
@@ -428,5 +502,4 @@ TEST_F(PDBApiTest, Dyncast) {
VerifyUnknownDyncasts();
}
-
} // end anonymous namespace
diff --git a/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp b/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp
index 0efc2c6411b8..bf08ab20923b 100644
--- a/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp
+++ b/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp
@@ -27,10 +27,29 @@ class StringTableBuilderTest : public ::testing::Test {};
TEST_F(StringTableBuilderTest, Simple) {
// Create /names table contents.
PDBStringTableBuilder Builder;
- EXPECT_EQ(1U, Builder.insert("foo"));
- EXPECT_EQ(5U, Builder.insert("bar"));
- EXPECT_EQ(1U, Builder.insert("foo"));
- EXPECT_EQ(9U, Builder.insert("baz"));
+
+ // This test case is carefully constructed to ensure that at least one
+ // string gets bucketed into slot 0, *and* to ensure that at least one
+ // has a hash collision at the end of the bucket list so it has to
+ // wrap around.
+ uint32_t FooID = Builder.insert("foo");
+ uint32_t BarID = Builder.insert("bar");
+ uint32_t BazID = Builder.insert("baz");
+ uint32_t BuzzID = Builder.insert("buzz");
+ uint32_t BazzID = Builder.insert("bazz");
+ uint32_t BarrID = Builder.insert("barr");
+
+ // Re-inserting the same item should return the same id.
+ EXPECT_EQ(FooID, Builder.insert("foo"));
+ EXPECT_EQ(BarID, Builder.insert("bar"));
+ EXPECT_EQ(BazID, Builder.insert("baz"));
+ EXPECT_EQ(BuzzID, Builder.insert("buzz"));
+ EXPECT_EQ(BazzID, Builder.insert("bazz"));
+ EXPECT_EQ(BarrID, Builder.insert("barr"));
+
+ // Each ID should be distinct.
+ std::set<uint32_t> Distinct{FooID, BarID, BazID, BuzzID, BazzID, BarrID};
+ EXPECT_EQ(6U, Distinct.size());
std::vector<uint8_t> Buffer(Builder.calculateSerializedSize());
MutableBinaryByteStream OutStream(Buffer, little);
@@ -43,13 +62,20 @@ TEST_F(StringTableBuilderTest, Simple) {
PDBStringTable Table;
EXPECT_THAT_ERROR(Table.reload(Reader), Succeeded());
- EXPECT_EQ(3U, Table.getNameCount());
+ EXPECT_EQ(6U, Table.getNameCount());
EXPECT_EQ(1U, Table.getHashVersion());
- EXPECT_THAT_EXPECTED(Table.getStringForID(1), HasValue("foo"));
- EXPECT_THAT_EXPECTED(Table.getStringForID(5), HasValue("bar"));
- EXPECT_THAT_EXPECTED(Table.getStringForID(9), HasValue("baz"));
- EXPECT_THAT_EXPECTED(Table.getIDForString("foo"), HasValue(1U));
- EXPECT_THAT_EXPECTED(Table.getIDForString("bar"), HasValue(5U));
- EXPECT_THAT_EXPECTED(Table.getIDForString("baz"), HasValue(9U));
+ EXPECT_THAT_EXPECTED(Table.getStringForID(FooID), HasValue("foo"));
+ EXPECT_THAT_EXPECTED(Table.getStringForID(BarID), HasValue("bar"));
+ EXPECT_THAT_EXPECTED(Table.getStringForID(BazID), HasValue("baz"));
+ EXPECT_THAT_EXPECTED(Table.getStringForID(BuzzID), HasValue("buzz"));
+ EXPECT_THAT_EXPECTED(Table.getStringForID(BazzID), HasValue("bazz"));
+ EXPECT_THAT_EXPECTED(Table.getStringForID(BarrID), HasValue("barr"));
+
+ EXPECT_THAT_EXPECTED(Table.getIDForString("foo"), HasValue(FooID));
+ EXPECT_THAT_EXPECTED(Table.getIDForString("bar"), HasValue(BarID));
+ EXPECT_THAT_EXPECTED(Table.getIDForString("baz"), HasValue(BazID));
+ EXPECT_THAT_EXPECTED(Table.getIDForString("buzz"), HasValue(BuzzID));
+ EXPECT_THAT_EXPECTED(Table.getIDForString("bazz"), HasValue(BazzID));
+ EXPECT_THAT_EXPECTED(Table.getIDForString("barr"), HasValue(BarrID));
}
diff --git a/unittests/Demangle/CMakeLists.txt b/unittests/Demangle/CMakeLists.txt
new file mode 100644
index 000000000000..3633d3212eb0
--- /dev/null
+++ b/unittests/Demangle/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(LLVM_LINK_COMPONENTS
+ Demangle
+)
+
+add_llvm_unittest(DemangleTests
+ PartialDemangleTest.cpp
+)
diff --git a/unittests/Demangle/PartialDemangleTest.cpp b/unittests/Demangle/PartialDemangleTest.cpp
new file mode 100644
index 000000000000..49d50169e5c7
--- /dev/null
+++ b/unittests/Demangle/PartialDemangleTest.cpp
@@ -0,0 +1,149 @@
+//===----------------------- PartialDemangleTest.cpp ----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstdlib>
+#include "llvm/Demangle/Demangle.h"
+#include "gtest/gtest.h"
+
+struct ChoppedName {
+ const char *Mangled;
+ const char *ContextName, *BaseName, *ReturnType, *Params;
+};
+
+static ChoppedName NamesToTest[] = {
+ {"_Z1fv", "", "f", "", "()"},
+ {"_ZN1a1b1cIiiiEEvm", "a::b", "c", "void", "(unsigned long)"},
+ {"_ZZ5OuterIiEivEN5Inner12inner_memberEv",
+ "int Outer<int>()::Inner", "inner_member", "", "()"},
+ {"_Z1fIiEPFvvEv", "", "f", "void (*)()", "()"},
+ {"_ZN1S1fIiEEvv", "S", "f", "void", "()"},
+
+ // Call operator for a lambda in f().
+ {"_ZZ1fvENK3$_0clEi", "f()::$_0", "operator()", "", "(int)"},
+
+ // A call operator for a lambda in a lambda in f().
+ {"_ZZZ1fvENK3$_0clEvENKUlvE_clEv",
+ "f()::$_0::operator()() const::'lambda'()", "operator()", "", "()"},
+
+ {"_ZZN1S1fEiiEd0_NKUlvE_clEv",
+ "S::f(int, int)::'lambda'()", "operator()", "", "()"},
+
+ {"_ZN1Scv7MuncherIJDpPT_EEIJFivEA_iEEEv",
+ "S", "operator Muncher<int (*)(), int (*) []>", "", "()"},
+
+ // Attributes.
+ {"_ZN5test4IdE1fEUa9enable_ifIXeqfL0p_Li1EEXeqfL0p0_Li2EEEi",
+ "test4<double>", "f", "", "(int)"},
+ {"_ZN1SC2B8ctor_tagEv", "S", "S", "", "()"},
+ {"_ZN1S1fB4MERPIiEEvv", "S", "f", "void", "()"},
+
+ {"_ZNSsC1EmcRKSaIcE",
+ "std::basic_string<char, std::char_traits<char>, std::allocator<char> >",
+ "basic_string", "", "(unsigned long, char, std::allocator<char> const&)"},
+ {"_ZNSsixEm", "std::string", "operator[]", "", "(unsigned long)"},
+ {"_ZSt17__throw_bad_allocv", "std", "__throw_bad_alloc", "", "()"},
+
+ {"_ZN1AI1BEC2Ev", "A<B>", "A", "", "()"},
+ {"_ZN1AI1BED2Ev", "A<B>", "~A", "", "()"},
+ {"_ZN1AI1BECI24BaseEi", "A<B>", "A", "", "(int)"},
+ {"_ZNKR1AI1BE1fIiEEiv", "A<B>", "f", "int", "()"},
+
+ {"_ZN1SIJicfEE3mfnIJjcdEEEvicfDpT_", "S<int, char, float>",
+ "mfn", "void", "(int, char, float, unsigned int, char, double)"},
+};
+
+TEST(PartialDemangleTest, TestNameChopping) {
+ size_t Size = 1;
+ char *Buf = static_cast<char *>(std::malloc(Size));
+
+ llvm::ItaniumPartialDemangler D;
+
+ for (ChoppedName &N : NamesToTest) {
+ EXPECT_FALSE(D.partialDemangle(N.Mangled));
+ EXPECT_TRUE(D.isFunction());
+ EXPECT_FALSE(D.isData());
+ EXPECT_FALSE(D.isSpecialName());
+
+ Buf = D.getFunctionDeclContextName(Buf, &Size);
+ EXPECT_STREQ(Buf, N.ContextName);
+
+ Buf = D.getFunctionBaseName(Buf, &Size);
+ EXPECT_STREQ(Buf, N.BaseName);
+
+ Buf = D.getFunctionReturnType(Buf, &Size);
+ EXPECT_STREQ(Buf, N.ReturnType);
+
+ Buf = D.getFunctionParameters(Buf, &Size);
+ EXPECT_STREQ(Buf, N.Params);
+ }
+
+ std::free(Buf);
+}
+
+TEST(PartialDemangleTest, TestNameMeta) {
+ llvm::ItaniumPartialDemangler Demangler;
+
+ EXPECT_FALSE(Demangler.partialDemangle("_ZNK1f1gEv"));
+ EXPECT_TRUE(Demangler.isFunction());
+ EXPECT_TRUE(Demangler.hasFunctionQualifiers());
+ EXPECT_FALSE(Demangler.isSpecialName());
+ EXPECT_FALSE(Demangler.isData());
+
+ EXPECT_FALSE(Demangler.partialDemangle("_Z1fv"));
+ EXPECT_FALSE(Demangler.hasFunctionQualifiers());
+
+ EXPECT_FALSE(Demangler.partialDemangle("_ZTV1S"));
+ EXPECT_TRUE(Demangler.isSpecialName());
+ EXPECT_FALSE(Demangler.isData());
+ EXPECT_FALSE(Demangler.isFunction());
+
+ EXPECT_FALSE(Demangler.partialDemangle("_ZN1aDC1a1b1cEE"));
+ EXPECT_FALSE(Demangler.isFunction());
+ EXPECT_FALSE(Demangler.isSpecialName());
+ EXPECT_TRUE(Demangler.isData());
+}
+
+TEST(PartialDemanglerTest, TestCtorOrDtor) {
+ static const char *Pos[] = {
+ "_ZN1AC1Ev", // A::A()
+ "_ZN1AC1IiEET_", // A::A<int>(int)
+ "_ZN1AD2Ev", // A::~A()
+ "_ZN1BIiEC1IcEET_", // B<int>::B<char>(char)
+ "_ZN1AC1B1TEv", // A::A[abi:T]()
+ "_ZNSt1AD2Ev", // std::A::~A()
+ "_ZN2ns1AD1Ev", // ns::A::~A()
+ };
+ static const char *Neg[] = {
+ "_Z1fv",
+ "_ZN1A1gIiEEvT_", // void A::g<int>(int)
+ };
+
+ llvm::ItaniumPartialDemangler D;
+ for (const char *N : Pos) {
+ EXPECT_FALSE(D.partialDemangle(N));
+ EXPECT_TRUE(D.isCtorOrDtor());
+ }
+ for (const char *N : Neg) {
+ EXPECT_FALSE(D.partialDemangle(N));
+ EXPECT_FALSE(D.isCtorOrDtor());
+ }
+}
+
+TEST(PartialDemanglerTest, TestMisc) {
+ llvm::ItaniumPartialDemangler D1, D2;
+
+ EXPECT_FALSE(D1.partialDemangle("_Z1fv"));
+ EXPECT_FALSE(D2.partialDemangle("_Z1g"));
+ std::swap(D1, D2);
+ EXPECT_FALSE(D1.isFunction());
+ EXPECT_TRUE(D2.isFunction());
+
+ EXPECT_TRUE(D1.partialDemangle("Not a mangled name!"));
+
+}
diff --git a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt
index e29787f8f426..b5f8a14c41c1 100644
--- a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt
+++ b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
Core
ExecutionEngine
IPO
+ InstCombine
MC
MCJIT
RuntimeDyld
diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h
index a3d3d1fffdbe..d3ebb95abc3a 100644
--- a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h
+++ b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h
@@ -46,10 +46,10 @@ protected:
// fail to initialize the AssumptionCacheTracker.
initializeAssumptionCacheTrackerPass(*PassRegistry::getPassRegistry());
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
// On Windows, generate ELF objects by specifying "-elf" in triple
HostTriple += "-elf";
-#endif // LLVM_ON_WIN32
+#endif // _WIN32
HostTriple = Triple::normalize(HostTriple);
}
diff --git a/unittests/ExecutionEngine/Orc/CMakeLists.txt b/unittests/ExecutionEngine/Orc/CMakeLists.txt
index dd0281d0b73d..6dbff7c592a4 100644
--- a/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -11,9 +11,11 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(OrcJITTests
CompileOnDemandLayerTest.cpp
+ CoreAPIsTest.cpp
IndirectionUtilsTest.cpp
GlobalMappingLayerTest.cpp
LazyEmittingLayerTest.cpp
+ LegacyAPIInteropTest.cpp
ObjectTransformLayerTest.cpp
OrcCAPITest.cpp
OrcTestCommon.cpp
diff --git a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp
index 61ce310e6311..a1f864bae01f 100644
--- a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp
@@ -18,7 +18,8 @@ namespace {
class DummyCallbackManager : public orc::JITCompileCallbackManager {
public:
- DummyCallbackManager() : JITCompileCallbackManager(0) {}
+ DummyCallbackManager(ExecutionSession &ES)
+ : JITCompileCallbackManager(ES, 0) {}
public:
Error grow() override { llvm_unreachable("not implemented"); }
@@ -35,11 +36,11 @@ public:
llvm_unreachable("Not implemented");
}
- JITSymbol findStub(StringRef Name, bool ExportedStubsOnly) override {
+ JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override {
llvm_unreachable("Not implemented");
}
- JITSymbol findPointer(StringRef Name) override {
+ JITEvaluatedSymbol findPointer(StringRef Name) override {
llvm_unreachable("Not implemented");
}
@@ -57,11 +58,23 @@ TEST(CompileOnDemandLayerTest, FindSymbol) {
return JITSymbol(nullptr);
};
- DummyCallbackManager CallbackMgr;
+
+ ExecutionSession ES(std::make_shared<SymbolStringPool>());
+ DummyCallbackManager CallbackMgr(ES);
+
+ auto GetResolver =
+ [](orc::VModuleKey) -> std::shared_ptr<llvm::orc::SymbolResolver> {
+ llvm_unreachable("Should never be called");
+ };
+
+ auto SetResolver = [](orc::VModuleKey, std::shared_ptr<orc::SymbolResolver>) {
+ llvm_unreachable("Should never be called");
+ };
llvm::orc::CompileOnDemandLayer<decltype(TestBaseLayer)> COD(
- TestBaseLayer, [](Function &F) { return std::set<Function *>{&F}; },
- CallbackMgr, [] { return llvm::make_unique<DummyStubsManager>(); }, true);
+ ES, TestBaseLayer, GetResolver, SetResolver,
+ [](Function &F) { return std::set<Function *>{&F}; }, CallbackMgr,
+ [] { return llvm::make_unique<DummyStubsManager>(); }, true);
auto Sym = COD.findSymbol("foo", true);
diff --git a/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
new file mode 100644
index 000000000000..baa1e3b5e8cb
--- /dev/null
+++ b/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
@@ -0,0 +1,744 @@
+//===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OrcTestCommon.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/OrcError.h"
+
+#include <set>
+#include <thread>
+
+using namespace llvm;
+using namespace llvm::orc;
+
+class CoreAPIsStandardTest : public CoreAPIsBasedStandardTest {};
+
+namespace {
+
+class SimpleMaterializationUnit : public MaterializationUnit {
+public:
+ using MaterializeFunction =
+ std::function<void(MaterializationResponsibility)>;
+ using DiscardFunction = std::function<void(const VSO &, SymbolStringPtr)>;
+ using DestructorFunction = std::function<void()>;
+
+ SimpleMaterializationUnit(
+ SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize,
+ DiscardFunction Discard = DiscardFunction(),
+ DestructorFunction Destructor = DestructorFunction())
+ : MaterializationUnit(std::move(SymbolFlags)),
+ Materialize(std::move(Materialize)), Discard(std::move(Discard)),
+ Destructor(std::move(Destructor)) {}
+
+ ~SimpleMaterializationUnit() override {
+ if (Destructor)
+ Destructor();
+ }
+
+ void materialize(MaterializationResponsibility R) override {
+ Materialize(std::move(R));
+ }
+
+ void discard(const VSO &V, SymbolStringPtr Name) override {
+ if (Discard)
+ Discard(V, std::move(Name));
+ else
+ llvm_unreachable("Discard not supported");
+ }
+
+private:
+ MaterializeFunction Materialize;
+ DiscardFunction Discard;
+ DestructorFunction Destructor;
+};
+
+TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {
+ bool OnResolutionRun = false;
+ bool OnReadyRun = false;
+
+ auto OnResolution = [&](Expected<SymbolMap> Result) {
+ EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
+ auto &Resolved = *Result;
+ auto I = Resolved.find(Foo);
+ EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition";
+ EXPECT_EQ(I->second.getAddress(), FooAddr)
+ << "Resolution returned incorrect result";
+ OnResolutionRun = true;
+ };
+ auto OnReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ OnReadyRun = true;
+ };
+
+ std::shared_ptr<MaterializationResponsibility> FooMR;
+
+ cantFail(V.define(llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
+ [&](MaterializationResponsibility R) {
+ FooMR = std::make_shared<MaterializationResponsibility>(std::move(R));
+ })));
+
+ ES.lookup({&V}, {Foo}, OnResolution, OnReady, NoDependenciesToRegister);
+
+ EXPECT_FALSE(OnResolutionRun) << "Should not have been resolved yet";
+ EXPECT_FALSE(OnReadyRun) << "Should not have been marked ready yet";
+
+ FooMR->resolve({{Foo, FooSym}});
+
+ EXPECT_TRUE(OnResolutionRun) << "Should have been resolved";
+ EXPECT_FALSE(OnReadyRun) << "Should not have been marked ready yet";
+
+ FooMR->finalize();
+
+ EXPECT_TRUE(OnReadyRun) << "Should have been marked ready";
+}
+
+TEST_F(CoreAPIsStandardTest, ExecutionSessionFailQuery) {
+ bool OnResolutionRun = false;
+ bool OnReadyRun = false;
+
+ auto OnResolution = [&](Expected<SymbolMap> Result) {
+ EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success";
+ auto Msg = toString(Result.takeError());
+ EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result";
+ OnResolutionRun = true;
+ };
+ auto OnReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ OnReadyRun = true;
+ };
+
+ AsynchronousSymbolQuery Q(SymbolNameSet({Foo}), OnResolution, OnReady);
+
+ ES.legacyFailQuery(Q,
+ make_error<StringError>("xyz", inconvertibleErrorCode()));
+
+ EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
+ EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
+}
+
+TEST_F(CoreAPIsStandardTest, EmptyLookup) {
+ bool OnResolvedRun = false;
+ bool OnReadyRun = false;
+
+ auto OnResolution = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
+ OnResolvedRun = true;
+ };
+
+ auto OnReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ OnReadyRun = true;
+ };
+
+ ES.lookup({&V}, {}, OnResolution, OnReady, NoDependenciesToRegister);
+
+ EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run for empty query";
+ EXPECT_TRUE(OnReadyRun) << "OnReady was not run for empty query";
+}
+
+TEST_F(CoreAPIsStandardTest, ChainedVSOLookup) {
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+
+ auto &V2 = ES.createVSO("V2");
+
+ bool OnResolvedRun = false;
+ bool OnReadyRun = false;
+
+ auto Q = std::make_shared<AsynchronousSymbolQuery>(
+ SymbolNameSet({Foo}),
+ [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
+ OnResolvedRun = true;
+ },
+ [&](Error Err) {
+ cantFail(std::move(Err));
+ OnReadyRun = true;
+ });
+
+ V2.legacyLookup(Q, V.legacyLookup(Q, {Foo}));
+
+ EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run for empty query";
+ EXPECT_TRUE(OnReadyRun) << "OnReady was not run for empty query";
+}
+
+TEST_F(CoreAPIsStandardTest, LookupFlagsTest) {
+ // Test that lookupFlags works on a predefined symbol, and does not trigger
+ // materialization of a lazy symbol. Make the lazy symbol weak to test that
+ // the weak flag is propagated correctly.
+
+ BarSym.setFlags(static_cast<JITSymbolFlags::FlagNames>(
+ JITSymbolFlags::Exported | JITSymbolFlags::Weak));
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
+ [](MaterializationResponsibility R) {
+ llvm_unreachable("Symbol materialized on flags lookup");
+ });
+
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+ cantFail(V.define(std::move(MU)));
+
+ SymbolNameSet Names({Foo, Bar, Baz});
+
+ auto SymbolFlags = V.lookupFlags(Names);
+
+ EXPECT_EQ(SymbolFlags.size(), 2U)
+ << "Returned symbol flags contains unexpected results";
+ EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo";
+ EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags())
+ << "Incorrect flags returned for Foo";
+ EXPECT_EQ(SymbolFlags.count(Bar), 1U)
+ << "Missing lookupFlags result for Bar";
+ EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags())
+ << "Incorrect flags returned for Bar";
+}
+
+TEST_F(CoreAPIsStandardTest, TestBasicAliases) {
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));
+ cantFail(V.define(symbolAliases({{Baz, {Foo, JITSymbolFlags::Exported}},
+ {Qux, {Bar, JITSymbolFlags::Weak}}})));
+ cantFail(V.define(absoluteSymbols({{Qux, QuxSym}})));
+
+ auto Result = lookup({&V}, {Baz, Qux});
+ EXPECT_TRUE(!!Result) << "Unexpected lookup failure";
+ EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\"";
+ EXPECT_EQ(Result->count(Qux), 1U) << "No result for \"qux\"";
+ EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress())
+ << "\"Baz\"'s address should match \"Foo\"'s";
+ EXPECT_EQ((*Result)[Qux].getAddress(), QuxSym.getAddress())
+ << "The \"Qux\" alias should have been overriden";
+}
+
+TEST_F(CoreAPIsStandardTest, TestChainedAliases) {
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+ cantFail(V.define(symbolAliases(
+ {{Baz, {Bar, BazSym.getFlags()}}, {Bar, {Foo, BarSym.getFlags()}}})));
+
+ auto Result = lookup({&V}, {Bar, Baz});
+ EXPECT_TRUE(!!Result) << "Unexpected lookup failure";
+ EXPECT_EQ(Result->count(Bar), 1U) << "No result for \"bar\"";
+ EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\"";
+ EXPECT_EQ((*Result)[Bar].getAddress(), FooSym.getAddress())
+ << "\"Bar\"'s address should match \"Foo\"'s";
+ EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress())
+ << "\"Baz\"'s address should match \"Foo\"'s";
+}
+
+TEST_F(CoreAPIsStandardTest, TestBasicReExports) {
+ // Test that the basic use case of re-exporting a single symbol from another
+ // VSO works.
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+
+ auto &V2 = ES.createVSO("V2");
+
+ cantFail(V2.define(reexports(V, {{Bar, {Foo, BarSym.getFlags()}}})));
+
+ auto Result = cantFail(lookup({&V2}, Bar));
+ EXPECT_EQ(Result.getAddress(), FooSym.getAddress())
+ << "Re-export Bar for symbol Foo should match FooSym's address";
+}
+
+TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) {
+ // Test that re-exports do not materialize symbols that have not been queried
+ // for.
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+
+ bool BarMaterialized = false;
+ auto BarMU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
+ [&](MaterializationResponsibility R) {
+ BarMaterialized = true;
+ R.resolve({{Bar, BarSym}});
+ R.finalize();
+ });
+
+ cantFail(V.define(BarMU));
+
+ auto &V2 = ES.createVSO("V2");
+
+ cantFail(V2.define(reexports(
+ V, {{Baz, {Foo, BazSym.getFlags()}}, {Qux, {Bar, QuxSym.getFlags()}}})));
+
+ auto Result = cantFail(lookup({&V2}, Baz));
+ EXPECT_EQ(Result.getAddress(), FooSym.getAddress())
+ << "Re-export Baz for symbol Foo should match FooSym's address";
+
+ EXPECT_FALSE(BarMaterialized) << "Bar should not have been materialized";
+}
+
+TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) {
+ Optional<MaterializationResponsibility> FooR;
+ auto FooMU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
+ [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
+
+ cantFail(V.define(FooMU));
+
+ bool FooReady = false;
+ auto OnResolution = [](Expected<SymbolMap> R) { cantFail(std::move(R)); };
+ auto OnReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ FooReady = true;
+ };
+
+ ES.lookup({&V}, {Foo}, std::move(OnResolution), std::move(OnReady),
+ NoDependenciesToRegister);
+
+ FooR->resolve({{Foo, FooSym}});
+ FooR->finalize();
+
+ EXPECT_TRUE(FooReady)
+ << "Self-dependency prevented symbol from being marked ready";
+}
+
+TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneVSO) {
+ // Test that a circular symbol dependency between three symbols in a VSO does
+ // not prevent any symbol from becoming 'ready' once all symbols are
+ // finalized.
+
+ // Create three MaterializationResponsibility objects: one for each of Foo,
+ // Bar and Baz. These are optional because MaterializationResponsibility
+ // does not have a default constructor).
+ Optional<MaterializationResponsibility> FooR;
+ Optional<MaterializationResponsibility> BarR;
+ Optional<MaterializationResponsibility> BazR;
+
+ // Create a MaterializationUnit for each symbol that moves the
+ // MaterializationResponsibility into one of the locals above.
+ auto FooMU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
+ [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
+
+ auto BarMU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
+ [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
+
+ auto BazMU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Baz, BazSym.getFlags()}}),
+ [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); });
+
+ // Define the symbols.
+ cantFail(V.define(FooMU));
+ cantFail(V.define(BarMU));
+ cantFail(V.define(BazMU));
+
+ // Query each of the symbols to trigger materialization.
+ bool FooResolved = false;
+ bool FooReady = false;
+
+ auto OnFooResolution = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
+ FooResolved = true;
+ };
+
+ auto OnFooReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ FooReady = true;
+ };
+
+ // Issue a lookup for Foo. Use NoDependenciesToRegister: We're going to add
+ // the dependencies manually below.
+ ES.lookup({&V}, {Foo}, std::move(OnFooResolution), std::move(OnFooReady),
+ NoDependenciesToRegister);
+
+ bool BarResolved = false;
+ bool BarReady = false;
+ auto OnBarResolution = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
+ BarResolved = true;
+ };
+
+ auto OnBarReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ BarReady = true;
+ };
+
+ ES.lookup({&V}, {Bar}, std::move(OnBarResolution), std::move(OnBarReady),
+ NoDependenciesToRegister);
+
+ bool BazResolved = false;
+ bool BazReady = false;
+
+ auto OnBazResolution = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
+ BazResolved = true;
+ };
+
+ auto OnBazReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ BazReady = true;
+ };
+
+ ES.lookup({&V}, {Baz}, std::move(OnBazResolution), std::move(OnBazReady),
+ NoDependenciesToRegister);
+
+ // Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo.
+ FooR->addDependenciesForAll({{&V, SymbolNameSet({Bar})}});
+ BarR->addDependenciesForAll({{&V, SymbolNameSet({Baz})}});
+ BazR->addDependenciesForAll({{&V, SymbolNameSet({Foo})}});
+
+ // Add self-dependencies for good measure. This tests that the implementation
+ // of addDependencies filters these out.
+ FooR->addDependenciesForAll({{&V, SymbolNameSet({Foo})}});
+ BarR->addDependenciesForAll({{&V, SymbolNameSet({Bar})}});
+ BazR->addDependenciesForAll({{&V, SymbolNameSet({Baz})}});
+
+ // Check that nothing has been resolved yet.
+ EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet";
+ EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet";
+ EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet";
+
+ // Resolve the symbols (but do not finalized them).
+ FooR->resolve({{Foo, FooSym}});
+ BarR->resolve({{Bar, BarSym}});
+ BazR->resolve({{Baz, BazSym}});
+
+ // Verify that the symbols have been resolved, but are not ready yet.
+ EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now";
+ EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now";
+ EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now";
+
+ EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet";
+ EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet";
+ EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet";
+
+ // Finalize two of the symbols.
+ FooR->finalize();
+ BarR->finalize();
+
+ // Verify that nothing is ready until the circular dependence is resolved.
+ EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready";
+ EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready";
+ EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready";
+
+ // Finalize the last symbol.
+ BazR->finalize();
+
+ // Verify that everything becomes ready once the circular dependence resolved.
+ EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now";
+ EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now";
+ EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now";
+}
+
+TEST_F(CoreAPIsStandardTest, DropMaterializerWhenEmpty) {
+ bool DestructorRun = false;
+
+ JITSymbolFlags WeakExported(JITSymbolFlags::Exported);
+ WeakExported |= JITSymbolFlags::Weak;
+
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, WeakExported}, {Bar, WeakExported}}),
+ [](MaterializationResponsibility R) {
+ llvm_unreachable("Unexpected call to materialize");
+ },
+ [&](const VSO &V, SymbolStringPtr Name) {
+ EXPECT_TRUE(Name == Foo || Name == Bar)
+ << "Discard of unexpected symbol?";
+ },
+ [&]() { DestructorRun = true; });
+
+ cantFail(V.define(MU));
+
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+
+ EXPECT_FALSE(DestructorRun)
+ << "MaterializationUnit should not have been destroyed yet";
+
+ cantFail(V.define(absoluteSymbols({{Bar, BarSym}})));
+
+ EXPECT_TRUE(DestructorRun)
+ << "MaterializationUnit should have been destroyed";
+}
+
+TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) {
+ bool FooMaterialized = false;
+ bool BarDiscarded = false;
+
+ JITSymbolFlags WeakExported(JITSymbolFlags::Exported);
+ WeakExported |= JITSymbolFlags::Weak;
+
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}),
+ [&](MaterializationResponsibility R) {
+ assert(BarDiscarded && "Bar should have been discarded by this point");
+ R.resolve(SymbolMap({{Foo, FooSym}}));
+ R.finalize();
+ FooMaterialized = true;
+ },
+ [&](const VSO &V, SymbolStringPtr Name) {
+ EXPECT_EQ(Name, Bar) << "Expected Name to be Bar";
+ BarDiscarded = true;
+ });
+
+ cantFail(V.define(MU));
+ cantFail(V.define(absoluteSymbols({{Bar, BarSym}})));
+
+ SymbolNameSet Names({Foo});
+
+ bool OnResolutionRun = false;
+ bool OnReadyRun = false;
+
+ auto OnResolution = [&](Expected<SymbolMap> Result) {
+ EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
+ auto I = Result->find(Foo);
+ EXPECT_NE(I, Result->end()) << "Could not find symbol definition";
+ EXPECT_EQ(I->second.getAddress(), FooSym.getAddress())
+ << "Resolution returned incorrect result";
+ OnResolutionRun = true;
+ };
+
+ auto OnReady = [&](Error Err) {
+ cantFail(std::move(Err));
+ OnReadyRun = true;
+ };
+
+ ES.lookup({&V}, Names, std::move(OnResolution), std::move(OnReady),
+ NoDependenciesToRegister);
+
+ EXPECT_TRUE(FooMaterialized) << "Foo was not materialized";
+ EXPECT_TRUE(BarDiscarded) << "Bar was not discarded";
+ EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
+ EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
+}
+
+TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) {
+ bool ExpectNoMoreMaterialization = false;
+ ES.setDispatchMaterialization(
+ [&](VSO &V, std::unique_ptr<MaterializationUnit> MU) {
+ if (ExpectNoMoreMaterialization)
+ ADD_FAILURE() << "Unexpected materialization";
+ MU->doMaterialize(V);
+ });
+
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
+ [&](MaterializationResponsibility R) {
+ cantFail(
+ R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}})));
+ R.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}}));
+ R.finalize();
+ });
+
+ cantFail(V.define(MU));
+ cantFail(lookup({&V}, Foo));
+
+ // Assert that materialization is complete by now.
+ ExpectNoMoreMaterialization = true;
+
+ // Look up bar to verify that no further materialization happens.
+ auto BarResult = cantFail(lookup({&V}, Bar));
+ EXPECT_EQ(BarResult.getAddress(), BarSym.getAddress())
+ << "Expected Bar == BarSym";
+}
+
+TEST_F(CoreAPIsStandardTest, FallbackDefinitionGeneratorTest) {
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+
+ V.setFallbackDefinitionGenerator([&](VSO &W, const SymbolNameSet &Names) {
+ cantFail(W.define(absoluteSymbols({{Bar, BarSym}})));
+ return SymbolNameSet({Bar});
+ });
+
+ auto Result = cantFail(lookup({&V}, {Foo, Bar}));
+
+ EXPECT_EQ(Result.count(Bar), 1U) << "Expected to find fallback def for 'bar'";
+ EXPECT_EQ(Result[Bar].getAddress(), BarSym.getAddress())
+ << "Expected fallback def for Bar to be equal to BarSym";
+}
+
+TEST_F(CoreAPIsStandardTest, FailResolution) {
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap(
+ {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}),
+ [&](MaterializationResponsibility R) { R.failMaterialization(); });
+
+ cantFail(V.define(MU));
+
+ SymbolNameSet Names({Foo, Bar});
+ auto Result = lookup({&V}, Names);
+
+ EXPECT_FALSE(!!Result) << "Expected failure";
+ if (!Result) {
+ handleAllErrors(Result.takeError(),
+ [&](FailedToMaterialize &F) {
+ EXPECT_EQ(F.getSymbols(), Names)
+ << "Expected to fail on symbols in Names";
+ },
+ [](ErrorInfoBase &EIB) {
+ std::string ErrMsg;
+ {
+ raw_string_ostream ErrOut(ErrMsg);
+ EIB.log(ErrOut);
+ }
+ ADD_FAILURE()
+ << "Expected a FailedToResolve error. Got:\n"
+ << ErrMsg;
+ });
+ }
+}
+
+TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) {
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
+ [&](MaterializationResponsibility R) {
+ R.resolve({{Foo, FooSym}});
+ R.finalize();
+ });
+
+ cantFail(V.define(MU));
+
+ auto FooLookupResult = cantFail(lookup({&V}, Foo));
+
+ EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
+ << "lookup returned an incorrect address";
+ EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
+ << "lookup returned incorrect flags";
+}
+
+TEST_F(CoreAPIsStandardTest, TestLookupWithThreadedMaterialization) {
+#if LLVM_ENABLE_THREADS
+
+ std::thread MaterializationThread;
+ ES.setDispatchMaterialization(
+ [&](VSO &V, std::unique_ptr<MaterializationUnit> MU) {
+ auto SharedMU = std::shared_ptr<MaterializationUnit>(std::move(MU));
+ MaterializationThread =
+ std::thread([SharedMU, &V]() { SharedMU->doMaterialize(V); });
+ });
+
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+
+ auto FooLookupResult = cantFail(lookup({&V}, Foo));
+
+ EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
+ << "lookup returned an incorrect address";
+ EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
+ << "lookup returned incorrect flags";
+ MaterializationThread.join();
+#endif
+}
+
+TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) {
+ // Test that GetRequestedSymbols returns the set of symbols that currently
+ // have pending queries, and test that MaterializationResponsibility's
+ // replace method can be used to return definitions to the VSO in a new
+ // MaterializationUnit.
+ SymbolNameSet Names({Foo, Bar});
+
+ bool FooMaterialized = false;
+ bool BarMaterialized = false;
+
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
+ [&](MaterializationResponsibility R) {
+ auto Requested = R.getRequestedSymbols();
+ EXPECT_EQ(Requested.size(), 1U) << "Expected one symbol requested";
+ EXPECT_EQ(*Requested.begin(), Foo) << "Expected \"Foo\" requested";
+
+ auto NewMU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
+ [&](MaterializationResponsibility R2) {
+ R2.resolve(SymbolMap({{Bar, BarSym}}));
+ R2.finalize();
+ BarMaterialized = true;
+ });
+
+ R.replace(std::move(NewMU));
+
+ R.resolve(SymbolMap({{Foo, FooSym}}));
+ R.finalize();
+
+ FooMaterialized = true;
+ });
+
+ cantFail(V.define(MU));
+
+ EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet";
+ EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet";
+
+ auto FooSymResult = cantFail(lookup({&V}, Foo));
+ EXPECT_EQ(FooSymResult.getAddress(), FooSym.getAddress())
+ << "Address mismatch for Foo";
+
+ EXPECT_TRUE(FooMaterialized) << "Foo should be materialized now";
+ EXPECT_FALSE(BarMaterialized) << "Bar still should not be materialized";
+
+ auto BarSymResult = cantFail(lookup({&V}, Bar));
+ EXPECT_EQ(BarSymResult.getAddress(), BarSym.getAddress())
+ << "Address mismatch for Bar";
+ EXPECT_TRUE(BarMaterialized) << "Bar should be materialized now";
+}
+
+TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) {
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
+ [&](MaterializationResponsibility R) {
+ auto R2 = R.delegate({Bar});
+
+ R.resolve({{Foo, FooSym}});
+ R.finalize();
+ R2.resolve({{Bar, BarSym}});
+ R2.finalize();
+ });
+
+ cantFail(V.define(MU));
+
+ auto Result = lookup({&V}, {Foo, Bar});
+
+ EXPECT_TRUE(!!Result) << "Result should be a success value";
+ EXPECT_EQ(Result->count(Foo), 1U) << "\"Foo\" entry missing";
+ EXPECT_EQ(Result->count(Bar), 1U) << "\"Bar\" entry missing";
+ EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress())
+ << "Address mismatch for \"Foo\"";
+ EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress())
+ << "Address mismatch for \"Bar\"";
+}
+
+TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) {
+ // Confirm that once a weak definition is selected for materialization it is
+ // treated as strong.
+ JITSymbolFlags WeakExported = JITSymbolFlags::Exported;
+ WeakExported &= JITSymbolFlags::Weak;
+
+ std::unique_ptr<MaterializationResponsibility> FooResponsibility;
+ auto MU = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
+ [&](MaterializationResponsibility R) {
+ FooResponsibility =
+ llvm::make_unique<MaterializationResponsibility>(std::move(R));
+ });
+
+ cantFail(V.define(MU));
+ auto OnResolution = [](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
+ };
+
+ auto OnReady = [](Error Err) { cantFail(std::move(Err)); };
+
+ ES.lookup({&V}, {Foo}, std::move(OnResolution), std::move(OnReady),
+ NoDependenciesToRegister);
+
+ auto MU2 = llvm::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
+ [](MaterializationResponsibility R) {
+ llvm_unreachable("This unit should never be materialized");
+ });
+
+ auto Err = V.define(MU2);
+ EXPECT_TRUE(!!Err) << "Expected failure value";
+ EXPECT_TRUE(Err.isA<DuplicateDefinition>())
+ << "Expected a duplicate definition error";
+ consumeError(std::move(Err));
+
+ FooResponsibility->resolve(SymbolMap({{Foo, FooSym}}));
+ FooResponsibility->finalize();
+}
+
+} // namespace
diff --git a/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp b/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp
index 0dba66d47535..3801fa918db7 100644
--- a/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp
@@ -15,11 +15,8 @@ namespace {
struct MockBaseLayer {
typedef int ModuleHandleT;
- ModuleHandleT addModule(
- std::shared_ptr<llvm::Module>,
- std::unique_ptr<llvm::RuntimeDyld::MemoryManager> MemMgr,
- std::unique_ptr<llvm::JITSymbolResolver> Resolver) {
- EXPECT_FALSE(MemMgr);
+ ModuleHandleT addModule(llvm::orc::VModuleKey,
+ std::shared_ptr<llvm::Module>) {
return 42;
}
};
@@ -27,7 +24,8 @@ struct MockBaseLayer {
TEST(LazyEmittingLayerTest, Empty) {
MockBaseLayer M;
llvm::orc::LazyEmittingLayer<MockBaseLayer> L(M);
- cantFail(L.addModule(std::unique_ptr<llvm::Module>(), nullptr));
+ cantFail(
+ L.addModule(llvm::orc::VModuleKey(), std::unique_ptr<llvm::Module>()));
}
}
diff --git a/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp b/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp
new file mode 100644
index 000000000000..746ae1dca490
--- /dev/null
+++ b/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp
@@ -0,0 +1,183 @@
+//===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OrcTestCommon.h"
+#include "llvm/ExecutionEngine/Orc/Legacy.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+
+class LegacyAPIsStandardTest : public CoreAPIsBasedStandardTest {};
+
+namespace {
+
+TEST_F(LegacyAPIsStandardTest, TestLambdaSymbolResolver) {
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));
+
+ auto Resolver = createSymbolResolver(
+ [&](const SymbolNameSet &Symbols) { return V.lookupFlags(Symbols); },
+ [&](std::shared_ptr<AsynchronousSymbolQuery> Q, SymbolNameSet Symbols) {
+ return V.legacyLookup(std::move(Q), Symbols);
+ });
+
+ SymbolNameSet Symbols({Foo, Bar, Baz});
+
+ SymbolFlagsMap SymbolFlags = Resolver->lookupFlags(Symbols);
+
+ EXPECT_EQ(SymbolFlags.size(), 2U)
+ << "lookupFlags returned the wrong number of results";
+ EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for foo";
+ EXPECT_EQ(SymbolFlags.count(Bar), 1U) << "Missing lookupFlags result for bar";
+ EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags())
+ << "Incorrect lookupFlags result for Foo";
+ EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags())
+ << "Incorrect lookupFlags result for Bar";
+
+ bool OnResolvedRun = false;
+
+ auto OnResolved = [&](Expected<SymbolMap> Result) {
+ OnResolvedRun = true;
+ EXPECT_TRUE(!!Result) << "Unexpected error";
+ EXPECT_EQ(Result->size(), 2U) << "Unexpected number of resolved symbols";
+ EXPECT_EQ(Result->count(Foo), 1U) << "Missing lookup result for foo";
+ EXPECT_EQ(Result->count(Bar), 1U) << "Missing lookup result for bar";
+ EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress())
+ << "Incorrect address for foo";
+ EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress())
+ << "Incorrect address for bar";
+ };
+ auto OnReady = [&](Error Err) {
+ EXPECT_FALSE(!!Err) << "Finalization should never fail in this test";
+ };
+
+ auto Q = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Foo, Bar}),
+ OnResolved, OnReady);
+ auto Unresolved = Resolver->lookup(std::move(Q), Symbols);
+
+ EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol";
+ EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to not be resolved";
+ EXPECT_TRUE(OnResolvedRun) << "OnResolved was never run";
+}
+
+TEST(LegacyAPIInteropTest, QueryAgainstVSO) {
+
+ ExecutionSession ES(std::make_shared<SymbolStringPool>());
+ auto Foo = ES.getSymbolStringPool().intern("foo");
+
+ auto &V = ES.createVSO("V");
+ JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported);
+ cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
+
+ auto LookupFlags = [&](const SymbolNameSet &Names) {
+ return V.lookupFlags(Names);
+ };
+
+ auto Lookup = [&](std::shared_ptr<AsynchronousSymbolQuery> Query,
+ SymbolNameSet Symbols) {
+ return V.legacyLookup(std::move(Query), Symbols);
+ };
+
+ auto UnderlyingResolver =
+ createSymbolResolver(std::move(LookupFlags), std::move(Lookup));
+ JITSymbolResolverAdapter Resolver(ES, *UnderlyingResolver, nullptr);
+
+ JITSymbolResolver::LookupSet Names{StringRef("foo")};
+
+ auto LFR = Resolver.lookupFlags(Names);
+ EXPECT_TRUE(!!LFR) << "lookupFlags failed";
+ EXPECT_EQ(LFR->size(), 1U)
+ << "lookupFlags returned the wrong number of results";
+ EXPECT_EQ(LFR->count(*Foo), 1U)
+ << "lookupFlags did not contain a result for 'foo'";
+ EXPECT_EQ((*LFR)[*Foo], FooSym.getFlags())
+ << "lookupFlags contained the wrong result for 'foo'";
+
+ auto LR = Resolver.lookup(Names);
+ EXPECT_TRUE(!!LR) << "lookup failed";
+ EXPECT_EQ(LR->size(), 1U) << "lookup returned the wrong number of results";
+ EXPECT_EQ(LR->count(*Foo), 1U) << "lookup did not contain a result for 'foo'";
+ EXPECT_EQ((*LR)[*Foo].getFlags(), FooSym.getFlags())
+ << "lookup returned the wrong result for flags of 'foo'";
+ EXPECT_EQ((*LR)[*Foo].getAddress(), FooSym.getAddress())
+ << "lookup returned the wrong result for address of 'foo'";
+}
+
+TEST(LegacyAPIInteropTset, LegacyLookupHelpersFn) {
+ constexpr JITTargetAddress FooAddr = 0xdeadbeef;
+ JITSymbolFlags FooFlags = JITSymbolFlags::Exported;
+
+ bool BarMaterialized = false;
+ constexpr JITTargetAddress BarAddr = 0xcafef00d;
+ JITSymbolFlags BarFlags = static_cast<JITSymbolFlags::FlagNames>(
+ JITSymbolFlags::Exported | JITSymbolFlags::Weak);
+
+ auto LegacyLookup = [&](const std::string &Name) -> JITSymbol {
+ if (Name == "foo")
+ return {FooAddr, FooFlags};
+
+ if (Name == "bar") {
+ auto BarMaterializer = [&]() -> Expected<JITTargetAddress> {
+ BarMaterialized = true;
+ return BarAddr;
+ };
+
+ return {BarMaterializer, BarFlags};
+ }
+
+ return nullptr;
+ };
+
+ ExecutionSession ES;
+ auto Foo = ES.getSymbolStringPool().intern("foo");
+ auto Bar = ES.getSymbolStringPool().intern("bar");
+ auto Baz = ES.getSymbolStringPool().intern("baz");
+
+ SymbolNameSet Symbols({Foo, Bar, Baz});
+
+ auto SymbolFlags = lookupFlagsWithLegacyFn(Symbols, LegacyLookup);
+
+ EXPECT_TRUE(!!SymbolFlags) << "Expected lookupFlagsWithLegacyFn to succeed";
+ EXPECT_EQ(SymbolFlags->size(), 2U) << "Wrong number of flags returned";
+ EXPECT_EQ(SymbolFlags->count(Foo), 1U) << "Flags for foo missing";
+ EXPECT_EQ(SymbolFlags->count(Bar), 1U) << "Flags for foo missing";
+ EXPECT_EQ((*SymbolFlags)[Foo], FooFlags) << "Wrong flags for foo";
+ EXPECT_EQ((*SymbolFlags)[Bar], BarFlags) << "Wrong flags for foo";
+ EXPECT_FALSE(BarMaterialized)
+ << "lookupFlags should not have materialized bar";
+
+ bool OnResolvedRun = false;
+ bool OnReadyRun = false;
+ auto OnResolved = [&](Expected<SymbolMap> Result) {
+ OnResolvedRun = true;
+ EXPECT_TRUE(!!Result) << "lookuWithLegacy failed to resolve";
+
+ EXPECT_EQ(Result->size(), 2U) << "Wrong number of symbols resolved";
+ EXPECT_EQ(Result->count(Foo), 1U) << "Result for foo missing";
+ EXPECT_EQ(Result->count(Bar), 1U) << "Result for bar missing";
+ EXPECT_EQ((*Result)[Foo].getAddress(), FooAddr) << "Wrong address for foo";
+ EXPECT_EQ((*Result)[Foo].getFlags(), FooFlags) << "Wrong flags for foo";
+ EXPECT_EQ((*Result)[Bar].getAddress(), BarAddr) << "Wrong address for bar";
+ EXPECT_EQ((*Result)[Bar].getFlags(), BarFlags) << "Wrong flags for bar";
+ };
+ auto OnReady = [&](Error Err) {
+ EXPECT_FALSE(!!Err) << "Finalization unexpectedly failed";
+ OnReadyRun = true;
+ };
+
+ AsynchronousSymbolQuery Q({Foo, Bar}, OnResolved, OnReady);
+ auto Unresolved = lookupWithLegacyFn(ES, Q, Symbols, LegacyLookup);
+
+ EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run";
+ EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
+ EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol";
+ EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to be unresolved";
+}
+
+} // namespace
diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp
index 7cd6443b5d4a..6ad3c19ada95 100644
--- a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp
@@ -14,6 +14,7 @@
#include "llvm/ExecutionEngine/Orc/NullResolver.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
+#include "llvm/IR/Module.h"
#include "llvm/Object/ObjectFile.h"
#include "gtest/gtest.h"
@@ -44,42 +45,32 @@ struct AllocatingTransform {
// transform layer called the base layer and forwarded any return value.
class MockBaseLayer {
public:
- typedef int ObjHandleT;
-
MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); }
- template <typename ObjPtrT>
- llvm::Expected<ObjHandleT>
- addObject(ObjPtrT Obj,
- std::shared_ptr<llvm::JITSymbolResolver> Resolver) {
- EXPECT_EQ(MockResolver, Resolver) << "Resolver should pass through";
+ template <typename ObjPtrT> llvm::Error addObject(VModuleKey K, ObjPtrT Obj) {
+ EXPECT_EQ(MockKey, K) << "Key should pass through";
EXPECT_EQ(MockObject + 1, *Obj) << "Transform should be applied";
LastCalled = "addObject";
- MockObjHandle = 111;
- return MockObjHandle;
+ return llvm::Error::success();
}
- template <typename ObjPtrT>
- void expectAddObject(ObjPtrT Obj,
- std::shared_ptr<llvm::JITSymbolResolver> Resolver) {
- MockResolver = Resolver;
+ template <typename ObjPtrT> void expectAddObject(VModuleKey K, ObjPtrT Obj) {
+ MockKey = K;
MockObject = *Obj;
}
-
- void verifyAddObject(ObjHandleT Returned) {
+ void verifyAddObject() {
EXPECT_EQ("addObject", LastCalled);
- EXPECT_EQ(MockObjHandle, Returned) << "Return should pass through";
resetExpectations();
}
- llvm::Error removeObject(ObjHandleT H) {
- EXPECT_EQ(MockObjHandle, H);
+ llvm::Error removeObject(VModuleKey K) {
+ EXPECT_EQ(MockKey, K);
LastCalled = "removeObject";
return llvm::Error::success();
}
- void expectRemoveObject(ObjHandleT H) { MockObjHandle = H; }
+ void expectRemoveObject(VModuleKey K) { MockKey = K; }
void verifyRemoveObject() {
EXPECT_EQ("removeObject", LastCalled);
resetExpectations();
@@ -105,18 +96,18 @@ public:
resetExpectations();
}
- llvm::JITSymbol findSymbolIn(ObjHandleT H, const std::string &Name,
+ llvm::JITSymbol findSymbolIn(VModuleKey K, const std::string &Name,
bool ExportedSymbolsOnly) {
- EXPECT_EQ(MockObjHandle, H) << "Handle should pass through";
+ EXPECT_EQ(MockKey, K) << "VModuleKey should pass through";
EXPECT_EQ(MockName, Name) << "Name should pass through";
EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
LastCalled = "findSymbolIn";
MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
return llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
}
- void expectFindSymbolIn(ObjHandleT H, const std::string &Name,
+ void expectFindSymbolIn(VModuleKey K, const std::string &Name,
bool ExportedSymbolsOnly) {
- MockObjHandle = H;
+ MockKey = K;
MockName = Name;
MockBool = ExportedSymbolsOnly;
}
@@ -128,29 +119,29 @@ public:
resetExpectations();
}
- llvm::Error emitAndFinalize(ObjHandleT H) {
- EXPECT_EQ(MockObjHandle, H) << "Handle should pass through";
+ llvm::Error emitAndFinalize(VModuleKey K) {
+ EXPECT_EQ(MockKey, K) << "VModuleKey should pass through";
LastCalled = "emitAndFinalize";
return llvm::Error::success();
}
- void expectEmitAndFinalize(ObjHandleT H) { MockObjHandle = H; }
+ void expectEmitAndFinalize(VModuleKey K) { MockKey = K; }
void verifyEmitAndFinalize() {
EXPECT_EQ("emitAndFinalize", LastCalled);
resetExpectations();
}
- void mapSectionAddress(ObjHandleT H, const void *LocalAddress,
+ void mapSectionAddress(VModuleKey K, const void *LocalAddress,
llvm::JITTargetAddress TargetAddr) {
- EXPECT_EQ(MockObjHandle, H);
+ EXPECT_EQ(MockKey, K);
EXPECT_EQ(MockLocalAddress, LocalAddress);
EXPECT_EQ(MockTargetAddress, TargetAddr);
LastCalled = "mapSectionAddress";
}
- void expectMapSectionAddress(ObjHandleT H, const void *LocalAddress,
+ void expectMapSectionAddress(VModuleKey K, const void *LocalAddress,
llvm::JITTargetAddress TargetAddr) {
- MockObjHandle = H;
+ MockKey = K;
MockLocalAddress = LocalAddress;
MockTargetAddress = TargetAddr;
}
@@ -162,9 +153,8 @@ public:
private:
// Backing fields for remembering parameter/return values
std::string LastCalled;
- std::shared_ptr<llvm::JITSymbolResolver> MockResolver;
+ VModuleKey MockKey;
MockObjectFile MockObject;
- ObjHandleT MockObjHandle;
std::string MockName;
bool MockBool;
llvm::JITSymbol MockSymbol;
@@ -175,9 +165,8 @@ private:
// Clear remembered parameters between calls
void resetExpectations() {
LastCalled = "nothing";
- MockResolver = nullptr;
+ MockKey = 0;
MockObject = 0;
- MockObjHandle = 0;
MockName = "bogus";
MockSymbol = llvm::JITSymbol(nullptr);
MockLocalAddress = nullptr;
@@ -190,6 +179,8 @@ private:
TEST(ObjectTransformLayerTest, Main) {
MockBaseLayer M;
+ ExecutionSession ES(std::make_shared<SymbolStringPool>());
+
// Create one object transform layer using a transform (as a functor)
// that allocates new objects, and deals in unique pointers.
ObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(M);
@@ -205,22 +196,23 @@ TEST(ObjectTransformLayerTest, Main) {
});
// Test addObject with T1 (allocating)
+ auto K1 = ES.allocateVModule();
auto Obj1 = std::make_shared<MockObjectFile>(211);
- auto SR = std::make_shared<NullResolver>();
- M.expectAddObject(Obj1, SR);
- auto H = cantFail(T1.addObject(std::move(Obj1), SR));
- M.verifyAddObject(H);
+ M.expectAddObject(K1, Obj1);
+ cantFail(T1.addObject(K1, std::move(Obj1)));
+ M.verifyAddObject();
// Test addObjectSet with T2 (mutating)
+ auto K2 = ES.allocateVModule();
auto Obj2 = std::make_shared<MockObjectFile>(222);
- M.expectAddObject(Obj2, SR);
- H = cantFail(T2.addObject(Obj2, SR));
- M.verifyAddObject(H);
+ M.expectAddObject(K2, Obj2);
+ cantFail(T2.addObject(K2, Obj2));
+ M.verifyAddObject();
EXPECT_EQ(223, *Obj2) << "Expected mutation";
// Test removeObjectSet
- M.expectRemoveObject(H);
- cantFail(T1.removeObject(H));
+ M.expectRemoveObject(K2);
+ cantFail(T1.removeObject(K2));
M.verifyRemoveObject();
// Test findSymbol
@@ -233,20 +225,20 @@ TEST(ObjectTransformLayerTest, Main) {
// Test findSymbolIn
Name = "bar";
ExportedOnly = false;
- M.expectFindSymbolIn(H, Name, ExportedOnly);
- llvm::JITSymbol Sym2 = T1.findSymbolIn(H, Name, ExportedOnly);
+ M.expectFindSymbolIn(K1, Name, ExportedOnly);
+ llvm::JITSymbol Sym2 = T1.findSymbolIn(K1, Name, ExportedOnly);
M.verifyFindSymbolIn(std::move(Sym2));
// Test emitAndFinalize
- M.expectEmitAndFinalize(H);
- cantFail(T2.emitAndFinalize(H));
+ M.expectEmitAndFinalize(K1);
+ cantFail(T2.emitAndFinalize(K1));
M.verifyEmitAndFinalize();
// Test mapSectionAddress
char Buffer[24];
llvm::JITTargetAddress MockAddress = 255;
- M.expectMapSectionAddress(H, Buffer, MockAddress);
- T1.mapSectionAddress(H, Buffer, MockAddress);
+ M.expectMapSectionAddress(K1, Buffer, MockAddress);
+ T1.mapSectionAddress(K1, Buffer, MockAddress);
M.verifyMapSectionAddress();
// Verify transform getter (non-const)
@@ -290,37 +282,35 @@ TEST(ObjectTransformLayerTest, Main) {
};
// Construct the jit layers.
- RTDyldObjectLinkingLayer BaseLayer(
- []() {
- return std::make_shared<llvm::SectionMemoryManager>();
- });
-
- auto IdentityTransform =
- [](std::shared_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>>
- Obj) {
- return Obj;
- };
+ RTDyldObjectLinkingLayer BaseLayer(ES, [](VModuleKey) {
+ return RTDyldObjectLinkingLayer::Resources{
+ std::make_shared<llvm::SectionMemoryManager>(),
+ std::make_shared<NullResolver>()};
+ });
+
+ auto IdentityTransform = [](std::unique_ptr<llvm::MemoryBuffer> Obj) {
+ return Obj;
+ };
ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)>
TransformLayer(BaseLayer, IdentityTransform);
auto NullCompiler = [](llvm::Module &) {
- return llvm::object::OwningBinary<llvm::object::ObjectFile>(nullptr,
- nullptr);
+ return std::unique_ptr<llvm::MemoryBuffer>(nullptr);
};
IRCompileLayer<decltype(TransformLayer), decltype(NullCompiler)>
CompileLayer(TransformLayer, NullCompiler);
// Make sure that the calls from IRCompileLayer to ObjectTransformLayer
// compile.
- auto Resolver = std::make_shared<NullResolver>();
- cantFail(CompileLayer.addModule(std::shared_ptr<llvm::Module>(), Resolver));
+ cantFail(CompileLayer.addModule(ES.allocateVModule(),
+ std::unique_ptr<llvm::Module>()));
// Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer
// compile.
- decltype(TransformLayer)::ObjHandleT H2;
- cantFail(TransformLayer.emitAndFinalize(H2));
- TransformLayer.findSymbolIn(H2, Name, false);
+ VModuleKey DummyKey = ES.allocateVModule();
+ cantFail(TransformLayer.emitAndFinalize(DummyKey));
+ TransformLayer.findSymbolIn(DummyKey, Name, false);
TransformLayer.findSymbol(Name, true);
- TransformLayer.mapSectionAddress(H2, nullptr, 0);
- cantFail(TransformLayer.removeObject(H2));
+ TransformLayer.mapSectionAddress(DummyKey, nullptr, 0);
+ cantFail(TransformLayer.removeObject(DummyKey));
}
}
diff --git a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
index d9448d47667f..b288b6bab2c9 100644
--- a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
+++ b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
@@ -38,13 +38,11 @@ protected:
return MB.takeModule();
}
- std::shared_ptr<object::OwningBinary<object::ObjectFile>>
- createTestObject() {
+ std::unique_ptr<MemoryBuffer> createTestObject() {
orc::SimpleCompiler IRCompiler(*TM);
auto M = createTestModule(TM->getTargetTriple());
M->setDataLayout(TM->createDataLayout());
- return std::make_shared<object::OwningBinary<object::ObjectFile>>(
- IRCompiler(*M));
+ return IRCompiler(*M);
}
typedef int (*MainFnTy)();
@@ -75,9 +73,8 @@ protected:
CompileContext *CCtx = static_cast<CompileContext*>(Ctx);
auto *ET = CCtx->APIExecTest;
CCtx->M = ET->createTestModule(ET->TM->getTargetTriple());
- LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(CCtx->M.release()));
- LLVMOrcAddEagerlyCompiledIR(JITStack, &CCtx->H, SM, myResolver, nullptr);
- LLVMOrcDisposeSharedModuleRef(SM);
+ LLVMOrcAddEagerlyCompiledIR(JITStack, &CCtx->H, wrap(CCtx->M.release()),
+ myResolver, nullptr);
CCtx->Compiled = true;
LLVMOrcTargetAddress MainAddr;
LLVMOrcGetSymbolAddress(JITStack, &MainAddr, "main");
@@ -89,7 +86,7 @@ protected:
char *OrcCAPIExecutionTest::testFuncName = nullptr;
TEST_F(OrcCAPIExecutionTest, TestEagerIRCompilation) {
- if (!TM)
+ if (!SupportsJIT)
return;
LLVMOrcJITStackRef JIT =
@@ -99,16 +96,28 @@ TEST_F(OrcCAPIExecutionTest, TestEagerIRCompilation) {
LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc");
- LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(M.release()));
LLVMOrcModuleHandle H;
- LLVMOrcAddEagerlyCompiledIR(JIT, &H, SM, myResolver, nullptr);
- LLVMOrcDisposeSharedModuleRef(SM);
- LLVMOrcTargetAddress MainAddr;
- LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main");
- MainFnTy MainFn = (MainFnTy)MainAddr;
- int Result = MainFn();
- EXPECT_EQ(Result, 42)
- << "Eagerly JIT'd code did not return expected result";
+ LLVMOrcAddEagerlyCompiledIR(JIT, &H, wrap(M.release()), myResolver, nullptr);
+
+ // get symbol address searching the entire stack
+ {
+ LLVMOrcTargetAddress MainAddr;
+ LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main");
+ MainFnTy MainFn = (MainFnTy)MainAddr;
+ int Result = MainFn();
+ EXPECT_EQ(Result, 42)
+ << "Eagerly JIT'd code did not return expected result";
+ }
+
+ // and then just searching a single handle
+ {
+ LLVMOrcTargetAddress MainAddr;
+ LLVMOrcGetSymbolAddressIn(JIT, &MainAddr, H, "main");
+ MainFnTy MainFn = (MainFnTy)MainAddr;
+ int Result = MainFn();
+ EXPECT_EQ(Result, 42)
+ << "Eagerly JIT'd code did not return expected result";
+ }
LLVMOrcRemoveModule(JIT, H);
@@ -117,7 +126,7 @@ TEST_F(OrcCAPIExecutionTest, TestEagerIRCompilation) {
}
TEST_F(OrcCAPIExecutionTest, TestLazyIRCompilation) {
- if (!TM)
+ if (!SupportsIndirection)
return;
LLVMOrcJITStackRef JIT =
@@ -127,10 +136,8 @@ TEST_F(OrcCAPIExecutionTest, TestLazyIRCompilation) {
LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc");
- LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(M.release()));
LLVMOrcModuleHandle H;
- LLVMOrcAddLazilyCompiledIR(JIT, &H, SM, myResolver, nullptr);
- LLVMOrcDisposeSharedModuleRef(SM);
+ LLVMOrcAddLazilyCompiledIR(JIT, &H, wrap(M.release()), myResolver, nullptr);
LLVMOrcTargetAddress MainAddr;
LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main");
MainFnTy MainFn = (MainFnTy)MainAddr;
@@ -145,15 +152,10 @@ TEST_F(OrcCAPIExecutionTest, TestLazyIRCompilation) {
}
TEST_F(OrcCAPIExecutionTest, TestAddObjectFile) {
- if (!TM)
+ if (!SupportsJIT)
return;
- std::unique_ptr<MemoryBuffer> ObjBuffer;
- {
- auto OwningObj = createTestObject();
- auto ObjAndBuffer = OwningObj->takeBinary();
- ObjBuffer = std::move(ObjAndBuffer.second);
- }
+ auto ObjBuffer = createTestObject();
LLVMOrcJITStackRef JIT =
LLVMOrcCreateInstance(wrap(TM.get()));
@@ -175,7 +177,7 @@ TEST_F(OrcCAPIExecutionTest, TestAddObjectFile) {
}
TEST_F(OrcCAPIExecutionTest, TestDirectCallbacksAPI) {
- if (!TM)
+ if (!SupportsIndirection)
return;
LLVMOrcJITStackRef JIT =
diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp b/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp
index ccd2fc0fb189..a12b1876e29c 100644
--- a/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp
+++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp
@@ -15,6 +15,11 @@
using namespace llvm;
+const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::FooAddr;
+const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::BarAddr;
+const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::BazAddr;
+const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::QuxAddr;
+
bool OrcNativeTarget::NativeTargetInitialized = false;
ModuleBuilder::ModuleBuilder(LLVMContext &Context, StringRef Triple,
diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/unittests/ExecutionEngine/Orc/OrcTestCommon.h
index 28a5f08ee439..c6caaf07db0e 100644
--- a/unittests/ExecutionEngine/Orc/OrcTestCommon.h
+++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.h
@@ -17,17 +17,59 @@
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/TypeBuilder.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
+#include "gtest/gtest.h"
+
#include <memory>
namespace llvm {
+namespace orc {
+// CoreAPIsStandardTest that saves a bunch of boilerplate by providing the
+// following:
+//
+// (1) ES -- An ExecutionSession
+// (2) Foo, Bar, Baz, Qux -- SymbolStringPtrs for strings "foo", "bar", "baz",
+// and "qux" respectively.
+// (3) FooAddr, BarAddr, BazAddr, QuxAddr -- Dummy addresses. Guaranteed
+// distinct and non-null.
+// (4) FooSym, BarSym, BazSym, QuxSym -- JITEvaluatedSymbols with FooAddr,
+// BarAddr, BazAddr, and QuxAddr respectively. All with default strong,
+// linkage and non-hidden visibility.
+// (5) V -- A VSO associated with ES.
+class CoreAPIsBasedStandardTest : public testing::Test {
+public:
+protected:
+ ExecutionSession ES;
+ VSO &V = ES.createVSO("V");
+ SymbolStringPtr Foo = ES.getSymbolStringPool().intern("foo");
+ SymbolStringPtr Bar = ES.getSymbolStringPool().intern("bar");
+ SymbolStringPtr Baz = ES.getSymbolStringPool().intern("baz");
+ SymbolStringPtr Qux = ES.getSymbolStringPool().intern("qux");
+ static const JITTargetAddress FooAddr = 1U;
+ static const JITTargetAddress BarAddr = 2U;
+ static const JITTargetAddress BazAddr = 3U;
+ static const JITTargetAddress QuxAddr = 4U;
+ JITEvaluatedSymbol FooSym =
+ JITEvaluatedSymbol(FooAddr, JITSymbolFlags::Exported);
+ JITEvaluatedSymbol BarSym =
+ JITEvaluatedSymbol(BarAddr, JITSymbolFlags::Exported);
+ JITEvaluatedSymbol BazSym =
+ JITEvaluatedSymbol(BazAddr, JITSymbolFlags::Exported);
+ JITEvaluatedSymbol QuxSym =
+ JITEvaluatedSymbol(QuxAddr, JITSymbolFlags::Exported);
+};
+
+} // end namespace orc
+
class OrcNativeTarget {
public:
static void initialize() {
@@ -59,15 +101,26 @@ public:
// If we found a TargetMachine, check that it's one that Orc supports.
const Triple& TT = TM->getTargetTriple();
+ // Bail out for windows platforms. We do not support these yet.
if ((TT.getArch() != Triple::x86_64 && TT.getArch() != Triple::x86) ||
- TT.isOSWindows())
- TM = nullptr;
+ TT.isOSWindows())
+ return;
+
+ // Target can JIT?
+ SupportsJIT = TM->getTarget().hasJIT();
+ // Use ability to create callback manager to detect whether Orc
+ // has indirection support on this platform. This way the test
+ // and Orc code do not get out of sync.
+ SupportsIndirection = !!orc::createLocalCompileCallbackManager(TT, ES, 0);
}
};
protected:
+ orc::ExecutionSession ES;
LLVMContext Context;
std::unique_ptr<TargetMachine> TM;
+ bool SupportsJIT = false;
+ bool SupportsIndirection = false;
};
class ModuleBuilder {
diff --git a/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp b/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp
index ed7b327124d7..420631c36ad2 100644
--- a/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp
@@ -12,6 +12,7 @@
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
+#include "llvm/ExecutionEngine/Orc/Legacy.h"
#include "llvm/ExecutionEngine/Orc/NullResolver.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/Constants.h"
@@ -66,7 +67,12 @@ TEST(RTDyldObjectLinkingLayerTest, TestSetProcessAllSections) {
bool DebugSectionSeen = false;
auto MM = std::make_shared<MemoryManagerWrapper>(DebugSectionSeen);
- RTDyldObjectLinkingLayer ObjLayer([&MM]() { return MM; });
+ ExecutionSession ES(std::make_shared<SymbolStringPool>());
+
+ RTDyldObjectLinkingLayer ObjLayer(ES, [&MM](VModuleKey) {
+ return RTDyldObjectLinkingLayer::Resources{
+ MM, std::make_shared<NullResolver>()};
+ });
LLVMContext Context;
auto M = llvm::make_unique<Module>("", Context);
@@ -88,46 +94,48 @@ TEST(RTDyldObjectLinkingLayerTest, TestSetProcessAllSections) {
if (!TM)
return;
- auto Obj =
- std::make_shared<object::OwningBinary<object::ObjectFile>>(
- SimpleCompiler(*TM)(*M));
-
- auto Resolver =
- createLambdaResolver(
- [](const std::string &Name) {
- return JITSymbol(nullptr);
- },
- [](const std::string &Name) {
- return JITSymbol(nullptr);
- });
+ auto Obj = SimpleCompiler(*TM)(*M);
{
// Test with ProcessAllSections = false (the default).
- auto H = cantFail(ObjLayer.addObject(Obj, Resolver));
- cantFail(ObjLayer.emitAndFinalize(H));
+ auto K = ES.allocateVModule();
+ cantFail(ObjLayer.addObject(
+ K, MemoryBuffer::getMemBufferCopy(Obj->getBuffer())));
+ cantFail(ObjLayer.emitAndFinalize(K));
EXPECT_EQ(DebugSectionSeen, false)
<< "Unexpected debug info section";
- cantFail(ObjLayer.removeObject(H));
+ cantFail(ObjLayer.removeObject(K));
}
{
// Test with ProcessAllSections = true.
ObjLayer.setProcessAllSections(true);
- auto H = cantFail(ObjLayer.addObject(Obj, Resolver));
- cantFail(ObjLayer.emitAndFinalize(H));
+ auto K = ES.allocateVModule();
+ cantFail(ObjLayer.addObject(K, std::move(Obj)));
+ cantFail(ObjLayer.emitAndFinalize(K));
EXPECT_EQ(DebugSectionSeen, true)
<< "Expected debug info section not seen";
- cantFail(ObjLayer.removeObject(H));
+ cantFail(ObjLayer.removeObject(K));
}
}
TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
- if (!TM)
+ if (!SupportsJIT)
return;
+ ExecutionSession ES(std::make_shared<SymbolStringPool>());
+
auto MM = std::make_shared<SectionMemoryManagerWrapper>();
- RTDyldObjectLinkingLayer ObjLayer([&MM]() { return MM; });
+ std::map<orc::VModuleKey, std::shared_ptr<orc::SymbolResolver>> Resolvers;
+
+ RTDyldObjectLinkingLayer ObjLayer(ES, [&](VModuleKey K) {
+ auto I = Resolvers.find(K);
+ assert(I != Resolvers.end() && "Missing resolver");
+ auto R = std::move(I->second);
+ Resolvers.erase(I);
+ return RTDyldObjectLinkingLayer::Resources{MM, std::move(R)};
+ });
SimpleCompiler Compile(*TM);
// Create a pair of modules that will trigger recursive finalization:
@@ -153,9 +161,7 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
Builder.CreateRet(FourtyTwo);
}
- auto Obj1 =
- std::make_shared<object::OwningBinary<object::ObjectFile>>(
- Compile(*MB1.getModule()));
+ auto Obj1 = Compile(*MB1.getModule());
ModuleBuilder MB2(Context, "", "dummy");
{
@@ -166,25 +172,29 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
IRBuilder<> Builder(FooEntry);
Builder.CreateRet(Builder.CreateCall(BarDecl));
}
- auto Obj2 =
- std::make_shared<object::OwningBinary<object::ObjectFile>>(
- Compile(*MB2.getModule()));
-
- auto Resolver =
- createLambdaResolver(
- [&](const std::string &Name) {
- if (auto Sym = ObjLayer.findSymbol(Name, true))
- return Sym;
- return JITSymbol(nullptr);
+ auto Obj2 = Compile(*MB2.getModule());
+
+ auto K1 = ES.allocateVModule();
+ Resolvers[K1] = std::make_shared<NullResolver>();
+ cantFail(ObjLayer.addObject(K1, std::move(Obj1)));
+
+ auto K2 = ES.allocateVModule();
+ auto LegacyLookup = [&](const std::string &Name) {
+ return ObjLayer.findSymbol(Name, true);
+ };
+
+ Resolvers[K2] = createSymbolResolver(
+ [&](const SymbolNameSet &Symbols) {
+ return cantFail(lookupFlagsWithLegacyFn(Symbols, LegacyLookup));
},
- [](const std::string &Name) {
- return JITSymbol(nullptr);
+ [&](std::shared_ptr<AsynchronousSymbolQuery> Query,
+ const SymbolNameSet &Symbols) {
+ return lookupWithLegacyFn(ES, *Query, Symbols, LegacyLookup);
});
- cantFail(ObjLayer.addObject(std::move(Obj1), Resolver));
- auto H = cantFail(ObjLayer.addObject(std::move(Obj2), Resolver));
- cantFail(ObjLayer.emitAndFinalize(H));
- cantFail(ObjLayer.removeObject(H));
+ cantFail(ObjLayer.addObject(K2, std::move(Obj2)));
+ cantFail(ObjLayer.emitAndFinalize(K2));
+ cantFail(ObjLayer.removeObject(K2));
// Finalization of module 2 should trigger finalization of module 1.
// Verify that finalize on SMMW is only called once.
@@ -193,12 +203,17 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) {
}
TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) {
- if (!TM)
+ if (!SupportsJIT)
return;
+ ExecutionSession ES(std::make_shared<SymbolStringPool>());
+
auto MM = std::make_shared<SectionMemoryManagerWrapper>();
- RTDyldObjectLinkingLayer ObjLayer([&MM]() { return MM; });
+ RTDyldObjectLinkingLayer ObjLayer(ES, [&MM](VModuleKey K) {
+ return RTDyldObjectLinkingLayer::Resources{
+ MM, std::make_shared<NullResolver>()};
+ });
SimpleCompiler Compile(*TM);
// Create a pair of unrelated modules:
@@ -225,9 +240,7 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) {
Builder.CreateRet(FourtyTwo);
}
- auto Obj1 =
- std::make_shared<object::OwningBinary<object::ObjectFile>>(
- Compile(*MB1.getModule()));
+ auto Obj1 = Compile(*MB1.getModule());
ModuleBuilder MB2(Context, "", "dummy");
{
@@ -239,15 +252,13 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) {
Value *Seven = ConstantInt::getSigned(Int32Ty, 7);
Builder.CreateRet(Seven);
}
- auto Obj2 =
- std::make_shared<object::OwningBinary<object::ObjectFile>>(
- Compile(*MB2.getModule()));
+ auto Obj2 = Compile(*MB2.getModule());
- auto NR = std::make_shared<NullResolver>();
- auto H = cantFail(ObjLayer.addObject(std::move(Obj1), NR));
- cantFail(ObjLayer.addObject(std::move(Obj2), NR));
- cantFail(ObjLayer.emitAndFinalize(H));
- cantFail(ObjLayer.removeObject(H));
+ auto K = ES.allocateVModule();
+ cantFail(ObjLayer.addObject(K, std::move(Obj1)));
+ cantFail(ObjLayer.addObject(ES.allocateVModule(), std::move(Obj2)));
+ cantFail(ObjLayer.emitAndFinalize(K));
+ cantFail(ObjLayer.removeObject(K));
// Only one call to needsToReserveAllocationSpace should have been made.
EXPECT_EQ(MM->NeedsToReserveAllocationSpaceCount, 1)
@@ -256,10 +267,14 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) {
}
TEST_F(RTDyldObjectLinkingLayerExecutionTest, TestNotifyLoadedSignature) {
+ ExecutionSession ES(std::make_shared<SymbolStringPool>());
RTDyldObjectLinkingLayer ObjLayer(
- []() { return nullptr; },
- [](RTDyldObjectLinkingLayer::ObjHandleT,
- const RTDyldObjectLinkingLayer::ObjectPtr &obj,
+ ES,
+ [](VModuleKey) {
+ return RTDyldObjectLinkingLayer::Resources{
+ nullptr, std::make_shared<NullResolver>()};
+ },
+ [](VModuleKey, const object::ObjectFile &obj,
const RuntimeDyld::LoadedObjectInfo &info) {});
}
diff --git a/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp b/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp
index 819c5f8eb34b..ae408a06f84d 100644
--- a/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp
+++ b/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp
@@ -24,8 +24,7 @@ public:
using ObjHandleT = uint64_t;
- using ObjectPtr =
- std::shared_ptr<object::OwningBinary<object::ObjectFile>>;
+ using ObjectPtr = std::unique_ptr<MemoryBuffer>;
using LookupFn = std::function<JITSymbol(StringRef, bool)>;
using SymbolLookupTable = std::map<ObjHandleT, LookupFn>;
@@ -43,7 +42,7 @@ public:
Expected<ObjHandleT> addObject(ObjectPtr Obj,
std::shared_ptr<JITSymbolResolver> Resolver) {
- return AddObject(Obj, SymTab);
+ return AddObject(std::move(Obj), SymTab);
}
Error removeObject(ObjHandleT H) {
@@ -102,8 +101,7 @@ MockObjectLayer::ObjectPtr createTestObject() {
B.CreateRet(ConstantInt::getSigned(Type::getInt32Ty(Ctx), 42));
SimpleCompiler IRCompiler(*TM);
- return std::make_shared<object::OwningBinary<object::ObjectFile>>(
- IRCompiler(*MB.getModule()));
+ return IRCompiler(*MB.getModule());
}
TEST(RemoteObjectLayer, AddObject) {
@@ -121,7 +119,7 @@ TEST(RemoteObjectLayer, AddObject) {
// Copy the bytes out of the test object: the copy will be used to verify
// that the original is correctly transmitted over RPC to the mock layer.
- StringRef ObjBytes = TestObject->getBinary()->getData();
+ StringRef ObjBytes = TestObject->getBuffer();
std::vector<char> ObjContents(ObjBytes.size());
std::copy(ObjBytes.begin(), ObjBytes.end(), ObjContents.begin());
@@ -134,7 +132,7 @@ TEST(RemoteObjectLayer, AddObject) {
MockObjectLayer::SymbolLookupTable &SymTab) {
// Check that the received object file content matches the original.
- StringRef RPCObjContents = Obj->getBinary()->getData();
+ StringRef RPCObjContents = Obj->getBuffer();
EXPECT_EQ(RPCObjContents.size(), ObjContents.size())
<< "RPC'd object file has incorrect size";
EXPECT_TRUE(std::equal(RPCObjContents.begin(), RPCObjContents.end(),
@@ -159,7 +157,7 @@ TEST(RemoteObjectLayer, AddObject) {
});
cantFail(Client.addObject(std::move(TestObject),
- std::make_shared<NullResolver>()));
+ std::make_shared<NullLegacyResolver>()));
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
@@ -205,8 +203,8 @@ TEST(RemoteObjectLayer, AddObjectFailure) {
cantFail(ServerEP.handleOne());
});
- auto HandleOrErr =
- Client.addObject(std::move(TestObject), std::make_shared<NullResolver>());
+ auto HandleOrErr = Client.addObject(std::move(TestObject),
+ std::make_shared<NullLegacyResolver>());
EXPECT_FALSE(HandleOrErr) << "Expected error from addObject";
@@ -258,8 +256,8 @@ TEST(RemoteObjectLayer, RemoveObject) {
cantFail(ServerEP.handleOne());
});
- auto H = cantFail(Client.addObject(std::move(TestObject),
- std::make_shared<NullResolver>()));
+ auto H = cantFail(Client.addObject(std::move(TestObject),
+ std::make_shared<NullLegacyResolver>()));
cantFail(Client.removeObject(H));
@@ -309,8 +307,8 @@ TEST(RemoteObjectLayer, RemoveObjectFailure) {
cantFail(ServerEP.handleOne());
});
- auto H = cantFail(Client.addObject(std::move(TestObject),
- std::make_shared<NullResolver>()));
+ auto H = cantFail(Client.addObject(std::move(TestObject),
+ std::make_shared<NullLegacyResolver>()));
auto Err = Client.removeObject(H);
EXPECT_TRUE(!!Err) << "Expected error from removeObject";
@@ -374,7 +372,7 @@ TEST(RemoteObjectLayer, FindSymbol) {
});
cantFail(Client.addObject(std::move(TestObject),
- std::make_shared<NullResolver>()));
+ std::make_shared<NullLegacyResolver>()));
// Check that we can find and materialize a valid symbol.
auto Sym1 = Client.findSymbol("foobar", true);
@@ -463,7 +461,7 @@ TEST(RemoteObjectLayer, FindSymbolIn) {
});
auto H = cantFail(Client.addObject(std::move(TestObject),
- std::make_shared<NullResolver>()));
+ std::make_shared<NullLegacyResolver>()));
auto Sym1 = Client.findSymbolIn(H, "foobar", true);
@@ -523,7 +521,7 @@ TEST(RemoteObjectLayer, EmitAndFinalize) {
});
auto H = cantFail(Client.addObject(std::move(TestObject),
- std::make_shared<NullResolver>()));
+ std::make_shared<NullLegacyResolver>()));
auto Err = Client.emitAndFinalize(H);
EXPECT_FALSE(!!Err) << "emitAndFinalize should work";
@@ -573,7 +571,7 @@ TEST(RemoteObjectLayer, EmitAndFinalizeFailure) {
});
auto H = cantFail(Client.addObject(std::move(TestObject),
- std::make_shared<NullResolver>()));
+ std::make_shared<NullLegacyResolver>()));
auto Err = Client.emitAndFinalize(H);
EXPECT_TRUE(!!Err) << "emitAndFinalize should work";
diff --git a/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp b/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp
index 79929e332182..861a9661223a 100644
--- a/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp
+++ b/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp
@@ -34,6 +34,12 @@ TEST(SymbolStringPool, UniquingAndComparisons) {
(void)(P1 < P3);
}
+TEST(SymbolStringPool, Dereference) {
+ SymbolStringPool SP;
+ auto Foo = SP.intern("foo");
+ EXPECT_EQ(*Foo, "foo") << "Equality on dereferenced string failed";
+}
+
TEST(SymbolStringPool, ClearDeadEntries) {
SymbolStringPool SP;
{
diff --git a/unittests/FuzzMutate/RandomIRBuilderTest.cpp b/unittests/FuzzMutate/RandomIRBuilderTest.cpp
index b60da6cbf9fc..d6ad07d00538 100644
--- a/unittests/FuzzMutate/RandomIRBuilderTest.cpp
+++ b/unittests/FuzzMutate/RandomIRBuilderTest.cpp
@@ -266,4 +266,34 @@ TEST(RandomIRBuilderTest, FirstClassTypes) {
}
}
+TEST(RandomIRBuilderTest, SwiftError) {
+ // Check that we never pick swifterror value as a source for operation
+ // other than load, store and call.
+
+ LLVMContext Ctx;
+ const char *SourceCode = "declare void @use(i8** swifterror %err)"
+ "define void @test() {\n"
+ "entry:\n"
+ " %err = alloca swifterror i8*, align 8\n"
+ " call void @use(i8** swifterror %err)\n"
+ " ret void\n"
+ "}";
+ auto M = parseAssembly(SourceCode, Ctx);
+
+ std::vector<Type *> Types = {Type::getInt8Ty(Ctx)};
+ RandomIRBuilder IB(Seed, Types);
+
+ // Get first basic block of the test function
+ Function &F = *M->getFunction("test");
+ BasicBlock &BB = *F.begin();
+ Instruction *Alloca = &*BB.begin();
+
+ fuzzerop::OpDescriptor Descr = fuzzerop::gepDescriptor(1);
+
+ for (int i = 0; i < 10; ++i) {
+ Value *V = IB.findOrCreateSource(BB, {Alloca}, {}, Descr.SourcePreds[0]);
+ ASSERT_FALSE(isa<AllocaInst>(V));
+ }
+}
+
}
diff --git a/unittests/FuzzMutate/StrategiesTest.cpp b/unittests/FuzzMutate/StrategiesTest.cpp
index 4fcd45692714..320a4e950f3d 100644
--- a/unittests/FuzzMutate/StrategiesTest.cpp
+++ b/unittests/FuzzMutate/StrategiesTest.cpp
@@ -64,6 +64,18 @@ std::unique_ptr<Module> parseAssembly(
return M;
}
+void IterateOnSource(StringRef Source, IRMutator &Mutator) {
+ LLVMContext Ctx;
+
+ for (int i = 0; i < 10; ++i) {
+ auto M = parseAssembly(Source.data(), Ctx);
+ ASSERT_TRUE(M && !verifyModule(*M, &errs()));
+
+ Mutator.mutateModule(*M, Seed, Source.size(), Source.size() + 100);
+ EXPECT_TRUE(!verifyModule(*M, &errs()));
+ }
+}
+
TEST(InjectorIRStrategyTest, EmptyModule) {
// Test that we can inject into empty module
@@ -81,7 +93,6 @@ TEST(InjectorIRStrategyTest, EmptyModule) {
TEST(InstDeleterIRStrategyTest, EmptyFunction) {
// Test that we don't crash even if we can't remove from one of the functions.
- LLVMContext Ctx;
StringRef Source = ""
"define <8 x i32> @func1() {\n"
"ret <8 x i32> undef\n"
@@ -96,15 +107,33 @@ TEST(InstDeleterIRStrategyTest, EmptyFunction) {
auto Mutator = createDeleterMutator();
ASSERT_TRUE(Mutator);
- // We need to choose 'func1' in order for the crash to appear.
- // Loop 10 times and assume we are lucky.
- for (int i = 0; i < 10; ++i) {
- auto M = parseAssembly(Source.data(), Ctx);
- ASSERT_TRUE(M && !verifyModule(*M, &errs()));
+ IterateOnSource(Source, *Mutator);
+}
- Mutator->mutateModule(*M, Seed, Source.size(), Source.size() + 100);
- EXPECT_TRUE(!verifyModule(*M, &errs()));
- }
+TEST(InstDeleterIRStrategyTest, PhiNodes) {
+ // Test that inst deleter works correctly with the phi nodes.
+
+ LLVMContext Ctx;
+ StringRef Source = "\n\
+ define i32 @earlyreturncrash(i32 %x) {\n\
+ entry:\n\
+ switch i32 %x, label %sw.epilog [\n\
+ i32 1, label %sw.bb1\n\
+ ]\n\
+ \n\
+ sw.bb1:\n\
+ br label %sw.epilog\n\
+ \n\
+ sw.epilog:\n\
+ %a.0 = phi i32 [ 7, %entry ], [ 9, %sw.bb1 ]\n\
+ %b.0 = phi i32 [ 10, %entry ], [ 4, %sw.bb1 ]\n\
+ ret i32 %a.0\n\
+ }";
+
+ auto Mutator = createDeleterMutator();
+ ASSERT_TRUE(Mutator);
+
+ IterateOnSource(Source, *Mutator);
}
}
diff --git a/unittests/IR/AttributesTest.cpp b/unittests/IR/AttributesTest.cpp
index ab018d845382..dd88cc359343 100644
--- a/unittests/IR/AttributesTest.cpp
+++ b/unittests/IR/AttributesTest.cpp
@@ -63,6 +63,76 @@ TEST(Attributes, AddAttributes) {
EXPECT_TRUE(AL.hasFnAttribute(Attribute::NoReturn));
}
+TEST(Attributes, RemoveAlign) {
+ LLVMContext C;
+
+ Attribute AlignAttr = Attribute::getWithAlignment(C, 8);
+ Attribute StackAlignAttr = Attribute::getWithStackAlignment(C, 32);
+ AttrBuilder B_align_readonly;
+ B_align_readonly.addAttribute(AlignAttr);
+ B_align_readonly.addAttribute(Attribute::ReadOnly);
+ AttrBuilder B_align;
+ B_align.addAttribute(AlignAttr);
+ AttrBuilder B_stackalign_optnone;
+ B_stackalign_optnone.addAttribute(StackAlignAttr);
+ B_stackalign_optnone.addAttribute(Attribute::OptimizeNone);
+ AttrBuilder B_stackalign;
+ B_stackalign.addAttribute(StackAlignAttr);
+
+ AttributeSet AS = AttributeSet::get(C, B_align_readonly);
+ EXPECT_TRUE(AS.getAlignment() == 8);
+ EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly));
+ AS = AS.removeAttribute(C, Attribute::Alignment);
+ EXPECT_FALSE(AS.hasAttribute(Attribute::Alignment));
+ EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly));
+ AS = AttributeSet::get(C, B_align_readonly);
+ AS = AS.removeAttributes(C, B_align);
+ EXPECT_TRUE(AS.getAlignment() == 0);
+ EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly));
+
+ AttributeList AL;
+ AL = AL.addParamAttributes(C, 0, B_align_readonly);
+ AL = AL.addAttributes(C, 0, B_stackalign_optnone);
+ EXPECT_TRUE(AL.hasAttributes(0));
+ EXPECT_TRUE(AL.hasAttribute(0, Attribute::StackAlignment));
+ EXPECT_TRUE(AL.hasAttribute(0, Attribute::OptimizeNone));
+ EXPECT_TRUE(AL.getStackAlignment(0) == 32);
+ EXPECT_TRUE(AL.hasParamAttrs(0));
+ EXPECT_TRUE(AL.hasParamAttr(0, Attribute::Alignment));
+ EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly));
+ EXPECT_TRUE(AL.getParamAlignment(0) == 8);
+
+ AL = AL.removeParamAttribute(C, 0, Attribute::Alignment);
+ EXPECT_FALSE(AL.hasParamAttr(0, Attribute::Alignment));
+ EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly));
+ EXPECT_TRUE(AL.hasAttribute(0, Attribute::StackAlignment));
+ EXPECT_TRUE(AL.hasAttribute(0, Attribute::OptimizeNone));
+ EXPECT_TRUE(AL.getStackAlignment(0) == 32);
+
+ AL = AL.removeAttribute(C, 0, Attribute::StackAlignment);
+ EXPECT_FALSE(AL.hasParamAttr(0, Attribute::Alignment));
+ EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly));
+ EXPECT_FALSE(AL.hasAttribute(0, Attribute::StackAlignment));
+ EXPECT_TRUE(AL.hasAttribute(0, Attribute::OptimizeNone));
+
+ AttributeList AL2;
+ AL2 = AL2.addParamAttributes(C, 0, B_align_readonly);
+ AL2 = AL2.addAttributes(C, 0, B_stackalign_optnone);
+
+ AL2 = AL2.removeParamAttributes(C, 0, B_align);
+ EXPECT_FALSE(AL2.hasParamAttr(0, Attribute::Alignment));
+ EXPECT_TRUE(AL2.hasParamAttr(0, Attribute::ReadOnly));
+ EXPECT_TRUE(AL2.hasAttribute(0, Attribute::StackAlignment));
+ EXPECT_TRUE(AL2.hasAttribute(0, Attribute::OptimizeNone));
+ EXPECT_TRUE(AL2.getStackAlignment(0) == 32);
+
+ AL2 = AL2.removeAttributes(C, 0, B_stackalign);
+ EXPECT_FALSE(AL2.hasParamAttr(0, Attribute::Alignment));
+ EXPECT_TRUE(AL2.hasParamAttr(0, Attribute::ReadOnly));
+ EXPECT_FALSE(AL2.hasAttribute(0, Attribute::StackAlignment));
+ EXPECT_TRUE(AL2.hasAttribute(0, Attribute::OptimizeNone));
+}
+
TEST(Attributes, AddMatchingAlignAttr) {
LLVMContext C;
AttributeList AL;
@@ -89,4 +159,12 @@ TEST(Attributes, EmptyGet) {
EXPECT_TRUE(AL.isEmpty());
}
+TEST(Attributes, OverflowGet) {
+ LLVMContext C;
+ std::pair<unsigned, Attribute> Attrs[] = { { AttributeList::ReturnIndex, Attribute::get(C, Attribute::SExt) },
+ { AttributeList::FunctionIndex, Attribute::get(C, Attribute::ReadOnly) } };
+ AttributeList AL = AttributeList::get(C, Attrs);
+ EXPECT_EQ(2U, AL.getNumAttrSets());
+}
+
} // end anonymous namespace
diff --git a/unittests/IR/BasicBlockTest.cpp b/unittests/IR/BasicBlockTest.cpp
index f1777e35b82c..07ed997f6381 100644
--- a/unittests/IR/BasicBlockTest.cpp
+++ b/unittests/IR/BasicBlockTest.cpp
@@ -33,6 +33,12 @@ TEST(BasicBlockTest, PhiRange) {
std::unique_ptr<BasicBlock> BB2(BasicBlock::Create(Context));
BranchInst::Create(BB.get(), BB2.get());
+ // Make sure this doesn't crash if there are no phis.
+ for (auto &PN : BB->phis()) {
+ (void)PN;
+ EXPECT_TRUE(false) << "empty block should have no phis";
+ }
+
// Make it a cycle.
auto *BI = BranchInst::Create(BB.get(), BB.get());
@@ -63,6 +69,15 @@ TEST(BasicBlockTest, PhiRange) {
CI = BB->phis().begin();
EXPECT_NE(CI, BB->phis().end());
+ // Test that filtering iterators work with basic blocks.
+ auto isPhi = [](Instruction &I) { return isa<PHINode>(&I); };
+ auto Phis = make_filter_range(*BB, isPhi);
+ auto ReversedPhis = reverse(make_filter_range(*BB, isPhi));
+ EXPECT_EQ(std::distance(Phis.begin(), Phis.end()), 3);
+ EXPECT_EQ(&*Phis.begin(), P1);
+ EXPECT_EQ(std::distance(ReversedPhis.begin(), ReversedPhis.end()), 3);
+ EXPECT_EQ(&*ReversedPhis.begin(), P3);
+
// And iterate a const range.
for (const auto &PN : const_cast<const BasicBlock *>(BB.get())->phis()) {
EXPECT_EQ(BB.get(), PN.getIncomingBlock(0));
@@ -71,5 +86,47 @@ TEST(BasicBlockTest, PhiRange) {
}
}
+#define CHECK_ITERATORS(Range1, Range2) \
+ EXPECT_EQ(std::distance(Range1.begin(), Range1.end()), \
+ std::distance(Range2.begin(), Range2.end())); \
+ for (auto Pair : zip(Range1, Range2)) \
+ EXPECT_EQ(&std::get<0>(Pair), std::get<1>(Pair));
+
+TEST(BasicBlockTest, TestInstructionsWithoutDebug) {
+ LLVMContext Ctx;
+
+ Module *M = new Module("MyModule", Ctx);
+ Type *ArgTy1[] = {Type::getInt32PtrTy(Ctx)};
+ FunctionType *FT = FunctionType::get(Type::getVoidTy(Ctx), ArgTy1, false);
+ Argument *V = new Argument(Type::getInt32Ty(Ctx));
+ Function *F = Function::Create(FT, Function::ExternalLinkage, "", M);
+
+ Value *DbgAddr = Intrinsic::getDeclaration(M, Intrinsic::dbg_addr);
+ Value *DbgDeclare =
+ Intrinsic::getDeclaration(M, Intrinsic::dbg_declare);
+ Value *DbgValue = Intrinsic::getDeclaration(M, Intrinsic::dbg_value);
+ Value *DIV = MetadataAsValue::get(Ctx, (Metadata *)nullptr);
+ SmallVector<Value *, 3> Args = {DIV, DIV, DIV};
+
+ BasicBlock *BB1 = BasicBlock::Create(Ctx, "", F);
+ const BasicBlock *BBConst = BB1;
+ IRBuilder<> Builder1(BB1);
+
+ AllocaInst *Var = Builder1.CreateAlloca(Builder1.getInt8Ty());
+ Builder1.CreateCall(DbgValue, Args);
+ Instruction *AddInst = cast<Instruction>(Builder1.CreateAdd(V, V));
+ Instruction *MulInst = cast<Instruction>(Builder1.CreateMul(AddInst, V));
+ Builder1.CreateCall(DbgDeclare, Args);
+ Instruction *SubInst = cast<Instruction>(Builder1.CreateSub(MulInst, V));
+ Builder1.CreateCall(DbgAddr, Args);
+
+ SmallVector<Instruction *, 4> Exp = {Var, AddInst, MulInst, SubInst};
+ CHECK_ITERATORS(BB1->instructionsWithoutDebug(), Exp);
+ CHECK_ITERATORS(BBConst->instructionsWithoutDebug(), Exp);
+
+ delete M;
+ delete V;
+}
+
} // End anonymous namespace.
} // End llvm namespace.
diff --git a/unittests/IR/CFGBuilder.cpp b/unittests/IR/CFGBuilder.cpp
index 50494ab5c7ca..0f9fb8b18cc1 100644
--- a/unittests/IR/CFGBuilder.cpp
+++ b/unittests/IR/CFGBuilder.cpp
@@ -36,9 +36,9 @@ CFGBuilder::CFGBuilder(Function *F, const std::vector<Arc> &InitialArcs,
}
static void ConnectBlocks(BasicBlock *From, BasicBlock *To) {
- DEBUG(dbgs() << "Creating BB arc " << From->getName() << " -> "
- << To->getName() << "\n";
- dbgs().flush());
+ LLVM_DEBUG(dbgs() << "Creating BB arc " << From->getName() << " -> "
+ << To->getName() << "\n";
+ dbgs().flush());
auto *IntTy = IntegerType::get(From->getContext(), 32);
if (isa<UnreachableInst>(From->getTerminator()))
@@ -57,9 +57,9 @@ static void ConnectBlocks(BasicBlock *From, BasicBlock *To) {
}
static void DisconnectBlocks(BasicBlock *From, BasicBlock *To) {
- DEBUG(dbgs() << "Deleting BB arc " << From->getName() << " -> "
- << To->getName() << "\n";
- dbgs().flush());
+ LLVM_DEBUG(dbgs() << "Deleting BB arc " << From->getName() << " -> "
+ << To->getName() << "\n";
+ dbgs().flush());
SwitchInst *SI = cast<SwitchInst>(From->getTerminator());
if (SI->getNumCases() == 0) {
diff --git a/unittests/IR/CMakeLists.txt b/unittests/IR/CMakeLists.txt
index 83f9dfd31765..58ebf159c3e1 100644
--- a/unittests/IR/CMakeLists.txt
+++ b/unittests/IR/CMakeLists.txt
@@ -6,7 +6,7 @@ set(LLVM_LINK_COMPONENTS
Passes
)
-set(IRSources
+add_llvm_unittest(IRTests
AsmWriterTest.cpp
AttributesTest.cpp
BasicBlockTest.cpp
@@ -15,8 +15,10 @@ set(IRSources
ConstantsTest.cpp
DebugInfoTest.cpp
DebugTypeODRUniquingTest.cpp
+ DeferredDominanceTest.cpp
DominatorTreeTest.cpp
DominatorTreeBatchUpdatesTest.cpp
+ DomTreeUpdaterTest.cpp
FunctionTest.cpp
PassBuilderCallbacksTest.cpp
IRBuilderTest.cpp
@@ -24,6 +26,7 @@ set(IRSources
IntrinsicsTest.cpp
LegacyPassManagerTest.cpp
MDBuilderTest.cpp
+ ManglerTest.cpp
MetadataTest.cpp
ModuleTest.cpp
PassManagerTest.cpp
@@ -38,13 +41,3 @@ set(IRSources
VerifierTest.cpp
WaymarkTest.cpp
)
-
-# HACK: Declare a couple of source files as optionally compiled to satisfy the
-# missing-file-checker in LLVM's weird CMake build.
-set(LLVM_OPTIONAL_SOURCES
- ValueMapTest.cpp
- )
-
-add_llvm_unittest(IRTests
- ${IRSources}
- )
diff --git a/unittests/IR/ConstantRangeTest.cpp b/unittests/IR/ConstantRangeTest.cpp
index 351256d49932..c0a30f1b4d94 100644
--- a/unittests/IR/ConstantRangeTest.cpp
+++ b/unittests/IR/ConstantRangeTest.cpp
@@ -1021,4 +1021,103 @@ TEST(ConstantRange, GetEquivalentICmp) {
EXPECT_EQ(RHS, APInt(32, -1));
}
+TEST(ConstantRange, MakeGuaranteedNoWrapRegionMulUnsignedSingleValue) {
+ typedef OverflowingBinaryOperator OBO;
+
+ for (uint64_t I = std::numeric_limits<uint8_t>::min();
+ I <= std::numeric_limits<uint8_t>::max(); I++) {
+ auto Range = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Mul, ConstantRange(APInt(8, I), APInt(8, I + 1)),
+ OBO::NoUnsignedWrap);
+
+ for (uint64_t V = std::numeric_limits<uint8_t>::min();
+ V <= std::numeric_limits<uint8_t>::max(); V++) {
+ bool Overflow;
+ (void)APInt(8, I).umul_ov(APInt(8, V), Overflow);
+ EXPECT_EQ(!Overflow, Range.contains(APInt(8, V)));
+ }
+ }
+}
+
+TEST(ConstantRange, MakeGuaranteedNoWrapRegionMulSignedSingleValue) {
+ typedef OverflowingBinaryOperator OBO;
+
+ for (int64_t I = std::numeric_limits<int8_t>::min();
+ I <= std::numeric_limits<int8_t>::max(); I++) {
+ auto Range = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Mul,
+ ConstantRange(APInt(8, I, /*isSigned=*/true),
+ APInt(8, I + 1, /*isSigned=*/true)),
+ OBO::NoSignedWrap);
+
+ for (int64_t V = std::numeric_limits<int8_t>::min();
+ V <= std::numeric_limits<int8_t>::max(); V++) {
+ bool Overflow;
+ (void)APInt(8, I, /*isSigned=*/true)
+ .smul_ov(APInt(8, V, /*isSigned=*/true), Overflow);
+ EXPECT_EQ(!Overflow, Range.contains(APInt(8, V, /*isSigned=*/true)));
+ }
+ }
+}
+
+TEST(ConstantRange, MakeGuaranteedNoWrapRegionMulUnsignedAndSignedSingleValue) {
+ typedef OverflowingBinaryOperator OBO;
+
+ for (uint64_t I = std::numeric_limits<uint8_t>::min();
+ I <= std::numeric_limits<uint8_t>::max(); I++) {
+ auto Range = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Mul, ConstantRange(APInt(8, I), APInt(8, I + 1)),
+ OBO::NoUnsignedWrap | OBO::NoSignedWrap);
+
+ for (uint64_t V = std::numeric_limits<uint8_t>::min();
+ V <= std::numeric_limits<uint8_t>::max(); V++) {
+ bool UOverflow;
+ (void)APInt(8, I).umul_ov(APInt(8, V), UOverflow);
+ bool SOverflow;
+ (void)APInt(8, I).smul_ov(APInt(8, V), SOverflow);
+ EXPECT_EQ(!(UOverflow || SOverflow), Range.contains(APInt(8, V)));
+ }
+ }
+}
+
+TEST(ConstantRange, MakeGuaranteedNoWrapRegionMulUnsignedRange) {
+ typedef OverflowingBinaryOperator OBO;
+
+ for (uint64_t Lo = std::numeric_limits<uint8_t>::min();
+ Lo <= std::numeric_limits<uint8_t>::max(); Lo++) {
+ for (uint64_t Hi = Lo; Hi <= std::numeric_limits<uint8_t>::max(); Hi++) {
+ EXPECT_EQ(
+ ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Mul, ConstantRange(APInt(8, Lo), APInt(8, Hi + 1)),
+ OBO::NoUnsignedWrap),
+ ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Mul, ConstantRange(APInt(8, Hi), APInt(8, Hi + 1)),
+ OBO::NoUnsignedWrap));
+ }
+ }
+}
+
+TEST(ConstantRange, MakeGuaranteedNoWrapRegionMulSignedRange) {
+ typedef OverflowingBinaryOperator OBO;
+
+ int Lo = -12, Hi = 16;
+ auto Range = ConstantRange::makeGuaranteedNoWrapRegion(
+ Instruction::Mul,
+ ConstantRange(APInt(8, Lo, /*isSigned=*/true),
+ APInt(8, Hi + 1, /*isSigned=*/true)),
+ OBO::NoSignedWrap);
+
+ for (int64_t V = std::numeric_limits<int8_t>::min();
+ V <= std::numeric_limits<int8_t>::max(); V++) {
+ bool AnyOverflow = false;
+ for (int64_t I = Lo; I <= Hi; I++) {
+ bool Overflow;
+ (void)APInt(8, I, /*isSigned=*/true)
+ .smul_ov(APInt(8, V, /*isSigned=*/true), Overflow);
+ AnyOverflow |= Overflow;
+ }
+ EXPECT_EQ(!AnyOverflow, Range.contains(APInt(8, V, /*isSigned=*/true)));
+ }
+}
+
} // anonymous namespace
diff --git a/unittests/IR/ConstantsTest.cpp b/unittests/IR/ConstantsTest.cpp
index ccffa50bf133..cf67e55b32a2 100644
--- a/unittests/IR/ConstantsTest.cpp
+++ b/unittests/IR/ConstantsTest.cpp
@@ -473,8 +473,7 @@ TEST(ConstantsTest, BitcastToGEP) {
GlobalValue::ExternalLinkage, nullptr);
auto *PtrTy = PointerType::get(i32, 0);
auto *C = ConstantExpr::getBitCast(G, PtrTy);
- ASSERT_EQ(dyn_cast<ConstantExpr>(C)->getOpcode(),
- Instruction::BitCast);
+ ASSERT_EQ(cast<ConstantExpr>(C)->getOpcode(), Instruction::BitCast);
}
} // end anonymous namespace
diff --git a/unittests/IR/DebugTypeODRUniquingTest.cpp b/unittests/IR/DebugTypeODRUniquingTest.cpp
index 7eb08e24b408..af77091221c3 100644
--- a/unittests/IR/DebugTypeODRUniquingTest.cpp
+++ b/unittests/IR/DebugTypeODRUniquingTest.cpp
@@ -30,7 +30,7 @@ TEST(DebugTypeODRUniquingTest, getODRType) {
// Without a type map, this should return null.
EXPECT_FALSE(DICompositeType::getODRType(
Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,
- nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr));
+ nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, nullptr));
// Enable the mapping. There still shouldn't be a type.
Context.enableDebugTypeODRUniquing();
@@ -39,7 +39,7 @@ TEST(DebugTypeODRUniquingTest, getODRType) {
// Create some ODR-uniqued type.
auto &CT = *DICompositeType::getODRType(
Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,
- nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr);
+ nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, nullptr);
EXPECT_EQ(UUID.getString(), CT.getIdentifier());
// Check that we get it back, even if we change a field.
@@ -47,12 +47,12 @@ TEST(DebugTypeODRUniquingTest, getODRType) {
EXPECT_EQ(&CT, DICompositeType::getODRType(
Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr,
0, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0,
- nullptr, nullptr));
+ nullptr, nullptr, nullptr));
EXPECT_EQ(&CT,
DICompositeType::getODRType(
Context, UUID, dwarf::DW_TAG_class_type,
MDString::get(Context, "name"), nullptr, 0, nullptr, nullptr, 0,
- 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr));
+ 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, nullptr));
// Check that it's discarded with the type map.
Context.disableDebugTypeODRUniquing();
@@ -71,32 +71,32 @@ TEST(DebugTypeODRUniquingTest, buildODRType) {
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);
+ nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, 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));
+ nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, 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, DINode::FlagZero,
- nullptr, 0, nullptr, nullptr));
+ nullptr, 0, nullptr, 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));
+ nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, 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, DINode::FlagZero, nullptr, 0,
- nullptr, nullptr));
+ nullptr, nullptr, nullptr));
EXPECT_EQ(dwarf::DW_TAG_structure_type, CT.getTag());
}
@@ -108,7 +108,7 @@ TEST(DebugTypeODRUniquingTest, buildODRTypeFields) {
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);
+ DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr, nullptr);
// Create macros for running through all the fields except Identifier and Flags.
#define FOR_EACH_MDFIELD() \
@@ -141,7 +141,7 @@ TEST(DebugTypeODRUniquingTest, buildODRTypeFields) {
DICompositeType::buildODRType(
Context, UUID, Tag, Name, File, Line, Scope, BaseType,
SizeInBits, AlignInBits, OffsetInBits, DINode::FlagArtificial,
- Elements, RuntimeLang, VTableHolder, TemplateParams));
+ Elements, RuntimeLang, VTableHolder, TemplateParams, nullptr));
// Confirm that all the right fields got updated.
#define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.getRaw##X());
diff --git a/unittests/IR/DeferredDominanceTest.cpp b/unittests/IR/DeferredDominanceTest.cpp
new file mode 100644
index 000000000000..96156f89a744
--- /dev/null
+++ b/unittests/IR/DeferredDominanceTest.cpp
@@ -0,0 +1,344 @@
+//===- llvm/unittests/IR/DeferredDominanceTest.cpp - DDT 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/Constants.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,
+ StringRef ModuleStr) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(ModuleStr, Err, Context);
+ assert(M && "Bad LLVM IR?");
+ return M;
+}
+
+TEST(DeferredDominance, BasicOperations) {
+ StringRef FuncName = "f";
+ StringRef ModuleString =
+ "define i32 @f(i32 %i, i32 *%p) {\n"
+ " bb0:\n"
+ " store i32 %i, i32 *%p\n"
+ " switch i32 %i, label %bb1 [\n"
+ " i32 0, label %bb2\n"
+ " i32 1, label %bb2\n"
+ " i32 2, label %bb3\n"
+ " ]\n"
+ " bb1:\n"
+ " ret i32 1\n"
+ " bb2:\n"
+ " ret i32 2\n"
+ " bb3:\n"
+ " ret i32 3\n"
+ "}\n";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+ ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
+
+ // Make the DDT.
+ DominatorTree DT(*F);
+ DeferredDominance DDT(DT);
+ ASSERT_TRUE(DDT.flush().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+ BasicBlock *BB2 = &*FI++;
+ BasicBlock *BB3 = &*FI++;
+
+ // Test discards of invalid self-domination updates. These use the single
+ // short-hand interface but are still queued inside DDT.
+ DDT.deleteEdge(BB0, BB0);
+ DDT.insertEdge(BB1, BB1);
+
+ // Delete edge bb0 -> bb3 and push the update twice to verify duplicate
+ // entries are discarded.
+ std::vector<DominatorTree::UpdateType> Updates;
+ Updates.reserve(4);
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+
+ // Unnecessary Insert: no edge bb1 -> bb2 after change to bb0.
+ Updates.push_back({DominatorTree::Insert, BB1, BB2});
+ // Unnecessary Delete: edge exists bb0 -> bb1 after change to bb0.
+ Updates.push_back({DominatorTree::Delete, BB0, BB1});
+
+ // CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+
+ // Deletion of a BasicBlock is an immediate event. We remove all uses to the
+ // contained Instructions and change the Terminator to "unreachable" when
+ // queued for deletion. Its parent is still F until DDT.flush() is called. We
+ // don't defer this action because it can cause problems for other transforms
+ // or analysis as it's part of the actual CFG. We only defer updates to the
+ // DominatorTree. This code will crash if it is placed before the
+ // BranchInst::Create() call above.
+ ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_FALSE(DDT.pendingDeletedBB(BB3));
+ DDT.deleteBB(BB3);
+ EXPECT_TRUE(DDT.pendingDeletedBB(BB3));
+ ASSERT_TRUE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_EQ(BB3->getParent(), F);
+
+ // Verify. Updates to DDT must be applied *after* all changes to the CFG
+ // (including block deletion).
+ DDT.applyUpdates(Updates);
+ ASSERT_TRUE(DDT.flush().verify());
+}
+
+TEST(DeferredDominance, PairedUpdate) {
+ StringRef FuncName = "f";
+ StringRef ModuleString =
+ "define i32 @f(i32 %i, i32 *%p) {\n"
+ " bb0:\n"
+ " store i32 %i, i32 *%p\n"
+ " switch i32 %i, label %bb1 [\n"
+ " i32 0, label %bb2\n"
+ " i32 1, label %bb2\n"
+ " ]\n"
+ " bb1:\n"
+ " ret i32 1\n"
+ " bb2:\n"
+ " ret i32 2\n"
+ "}\n";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+ ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
+
+ // Make the DDT.
+ DominatorTree DT(*F);
+ DeferredDominance DDT(DT);
+ ASSERT_TRUE(DDT.flush().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+ BasicBlock *BB2 = &*FI++;
+
+ // CFG Change: only edge from bb0 is bb0 -> bb1.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
+
+ // Must be done after the CFG change. The applyUpdate() routine analyzes the
+ // current state of the CFG.
+ DDT.deleteEdge(BB0, BB2);
+
+ // CFG Change: bb0 now has bb0 -> bb1 and bb0 -> bb2.
+ // With this change no dominance has been altered from the original IR. DT
+ // doesn't care if the type of TerminatorInstruction changed, only if the
+ // unique edges have.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+
+ // Must be done after the CFG change. The applyUpdate() routine analyzes the
+ // current state of the CFG. This DDT update pairs with the previous one and
+ // is cancelled out before ever applying updates to DT.
+ DDT.insertEdge(BB0, BB2);
+
+ // Test the empty DeletedBB list.
+ EXPECT_FALSE(DDT.pendingDeletedBB(BB0));
+ EXPECT_FALSE(DDT.pendingDeletedBB(BB1));
+ EXPECT_FALSE(DDT.pendingDeletedBB(BB2));
+
+ // The DT has no changes, this flush() simply returns a reference to the
+ // internal DT calculated at the beginning of this test.
+ ASSERT_TRUE(DDT.flush().verify());
+}
+
+TEST(DeferredDominance, ReplaceEntryBB) {
+ StringRef FuncName = "f";
+ StringRef ModuleString =
+ "define i32 @f() {\n"
+ "bb0:\n"
+ " br label %bb1\n"
+ " bb1:\n"
+ " ret i32 1\n"
+ "}\n";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+ ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
+
+ // Make the DDT.
+ DominatorTree DT(*F);
+ DeferredDominance DDT(DT);
+ ASSERT_TRUE(DDT.flush().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+
+ // Add a block as the new function entry BB. We also link it to BB0.
+ BasicBlock *NewEntry =
+ BasicBlock::Create(F->getContext(), "new_entry", F, BB0);
+ BranchInst::Create(BB0, NewEntry);
+ EXPECT_EQ(F->begin()->getName(), NewEntry->getName());
+ EXPECT_TRUE(&F->getEntryBlock() == NewEntry);
+
+ // Insert the new edge between new_eentry -> bb0. Without this the
+ // recalculate() call below will not actually recalculate the DT as there
+ // are no changes pending and no blocks deleted.
+ DDT.insertEdge(NewEntry, BB0);
+
+ // Changing the Entry BB requires a full recalulation.
+ DDT.recalculate(*F);
+ ASSERT_TRUE(DDT.flush().verify());
+
+ // CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1.
+ EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u);
+ NewEntry->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, NewEntry);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
+
+ // Update the DDT. At this point bb0 now has no predecessors but is still a
+ // Child of F.
+ DDT.applyUpdates({{DominatorTree::Delete, NewEntry, BB0},
+ {DominatorTree::Insert, NewEntry, BB1}});
+ ASSERT_TRUE(DDT.flush().verify());
+
+ // Now remove bb0 from F.
+ ASSERT_FALSE(isa<UnreachableInst>(BB0->getTerminator()));
+ EXPECT_FALSE(DDT.pendingDeletedBB(BB0));
+ DDT.deleteBB(BB0);
+ EXPECT_TRUE(DDT.pendingDeletedBB(BB0));
+ ASSERT_TRUE(isa<UnreachableInst>(BB0->getTerminator()));
+ EXPECT_EQ(BB0->getParent(), F);
+
+ // Perform a full recalculation of the DDT. It is not necessary here but we
+ // do this to test the case when there are no pending DT updates but there are
+ // pending deleted BBs.
+ DDT.recalculate(*F);
+ ASSERT_TRUE(DDT.flush().verify());
+}
+
+TEST(DeferredDominance, InheritedPreds) {
+ StringRef FuncName = "f";
+ StringRef ModuleString =
+ "define i32 @f(i32 %i, i32 *%p) {\n"
+ " bb0:\n"
+ " store i32 %i, i32 *%p\n"
+ " switch i32 %i, label %bb1 [\n"
+ " i32 2, label %bb2\n"
+ " i32 3, label %bb3\n"
+ " ]\n"
+ " bb1:\n"
+ " br label %bb3\n"
+ " bb2:\n"
+ " br label %bb3\n"
+ " bb3:\n"
+ " ret i32 3\n"
+ "}\n";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+ ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
+
+ // Make the DDT.
+ DominatorTree DT(*F);
+ DeferredDominance DDT(DT);
+ ASSERT_TRUE(DDT.flush().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+ BasicBlock *BB2 = &*FI++;
+ BasicBlock *BB3 = &*FI++;
+
+ // There are several CFG locations where we have:
+ //
+ // pred1..predN
+ // | |
+ // +> curr <+ converted into: pred1..predN curr
+ // | | |
+ // v +> succ <+
+ // succ
+ //
+ // There is a specific shape of this we have to be careful of:
+ //
+ // pred1..predN
+ // || |
+ // |+> curr <+ converted into: pred1..predN curr
+ // | | | |
+ // | v +> succ <+
+ // +-> succ
+ //
+ // While the final CFG form is functionally identical the updates to
+ // DDT are not. In the first case we must have DDT.insertEdge(Pred1, Succ)
+ // while in the latter case we must *NOT* have DDT.insertEdge(Pred1, Succ).
+
+ // CFG Change: bb0 now only has bb0 -> bb1 and bb0 -> bb3. We are preparing to
+ // remove bb2.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, BB3, ConstantInt::getTrue(F->getContext()), BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+
+ // Remove bb2 from F. This has to happen before the call to applyUpdates() for
+ // DDT to detect there is no longer an edge between bb2 -> bb3. The deleteBB()
+ // method converts bb2's TI into "unreachable".
+ ASSERT_FALSE(isa<UnreachableInst>(BB2->getTerminator()));
+ EXPECT_FALSE(DDT.pendingDeletedBB(BB2));
+ DDT.deleteBB(BB2);
+ EXPECT_TRUE(DDT.pendingDeletedBB(BB2));
+ ASSERT_TRUE(isa<UnreachableInst>(BB2->getTerminator()));
+ EXPECT_EQ(BB2->getParent(), F);
+
+ // Queue up the DDT updates.
+ std::vector<DominatorTree::UpdateType> Updates;
+ Updates.reserve(4);
+ Updates.push_back({DominatorTree::Delete, BB0, BB2});
+ Updates.push_back({DominatorTree::Delete, BB2, BB3});
+
+ // Handle the specific shape case next.
+ // CFG Change: bb0 now only branches to bb3. We are preparing to remove bb1.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB3, BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
+
+ // Remove bb1 from F. This has to happen before the call to applyUpdates() for
+ // DDT to detect there is no longer an edge between bb1 -> bb3. The deleteBB()
+ // method converts bb1's TI into "unreachable".
+ ASSERT_FALSE(isa<UnreachableInst>(BB1->getTerminator()));
+ EXPECT_FALSE(DDT.pendingDeletedBB(BB1));
+ DDT.deleteBB(BB1);
+ EXPECT_TRUE(DDT.pendingDeletedBB(BB1));
+ ASSERT_TRUE(isa<UnreachableInst>(BB1->getTerminator()));
+ EXPECT_EQ(BB1->getParent(), F);
+
+ // Update the DDT. In this case we don't call DDT.insertEdge(BB0, BB3) because
+ // the edge previously existed at the start of this test when DT was first
+ // created.
+ Updates.push_back({DominatorTree::Delete, BB0, BB1});
+ Updates.push_back({DominatorTree::Delete, BB1, BB3});
+
+ // Verify everything.
+ DDT.applyUpdates(Updates);
+ ASSERT_TRUE(DDT.flush().verify());
+}
diff --git a/unittests/IR/DomTreeUpdaterTest.cpp b/unittests/IR/DomTreeUpdaterTest.cpp
new file mode 100644
index 000000000000..03b6b529fd8a
--- /dev/null
+++ b/unittests/IR/DomTreeUpdaterTest.cpp
@@ -0,0 +1,701 @@
+//==- llvm/unittests/IR/DomTreeUpdaterTest.cpp - DomTreeUpdater 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/DomTreeUpdater.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+
+using namespace llvm;
+
+static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,
+ StringRef ModuleStr) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(ModuleStr, Err, Context);
+ assert(M && "Bad LLVM IR?");
+ return M;
+}
+
+TEST(DomTreeUpdater, EagerUpdateBasicOperations) {
+ StringRef FuncName = "f";
+ StringRef ModuleString = R"(
+ define i32 @f(i32 %i, i32 *%p) {
+ bb0:
+ store i32 %i, i32 *%p
+ switch i32 %i, label %bb1 [
+ i32 1, label %bb2
+ i32 2, label %bb3
+ ]
+ bb1:
+ ret i32 1
+ bb2:
+ ret i32 2
+ bb3:
+ ret i32 3
+ })";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+
+ // Make the DomTreeUpdater.
+ DominatorTree DT(*F);
+ PostDominatorTree PDT(*F);
+ DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);
+
+ ASSERT_TRUE(DTU.hasDomTree());
+ ASSERT_TRUE(DTU.hasPostDomTree());
+ ASSERT_TRUE(DTU.isEager());
+ ASSERT_FALSE(DTU.isLazy());
+ ASSERT_TRUE(DTU.getDomTree().verify());
+ ASSERT_TRUE(DTU.getPostDomTree().verify());
+ ASSERT_FALSE(DTU.hasPendingUpdates());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+ BasicBlock *BB2 = &*FI++;
+ BasicBlock *BB3 = &*FI++;
+ SwitchInst *SI = dyn_cast<SwitchInst>(BB0->getTerminator());
+ ASSERT_NE(SI, nullptr) << "Couldn't get SwitchInst.";
+
+ DTU.insertEdgeRelaxed(BB0, BB0);
+ DTU.deleteEdgeRelaxed(BB0, BB0);
+
+ // Delete edge bb0 -> bb3 and push the update twice to verify duplicate
+ // entries are discarded.
+ std::vector<DominatorTree::UpdateType> Updates;
+ Updates.reserve(4);
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+
+ // Invalid Insert: no edge bb1 -> bb2 after change to bb0.
+ Updates.push_back({DominatorTree::Insert, BB1, BB2});
+ // Invalid Delete: edge exists bb0 -> bb1 after change to bb0.
+ Updates.push_back({DominatorTree::Delete, BB0, BB1});
+
+ // CFG Change: remove edge bb0 -> bb3.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);
+ BB3->removePredecessor(BB0);
+ for (auto i = SI->case_begin(), e = SI->case_end(); i != e; ++i) {
+ if (i->getCaseSuccessor() == BB3) {
+ SI->removeCase(i);
+ break;
+ }
+ }
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+ // Deletion of a BasicBlock is an immediate event. We remove all uses to the
+ // contained Instructions and change the Terminator to "unreachable" when
+ // queued for deletion.
+ ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));
+ DTU.applyUpdates(Updates, /*ForceRemoveDuplicates*/ true);
+ ASSERT_FALSE(DTU.hasPendingUpdates());
+
+ // Invalid Insert: no edge bb1 -> bb2 after change to bb0.
+ DTU.insertEdgeRelaxed(BB1, BB2);
+ // Invalid Delete: edge exists bb0 -> bb1 after change to bb0.
+ DTU.deleteEdgeRelaxed(BB0, BB1);
+
+ // DTU working with Eager UpdateStrategy does not need to flush.
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+
+ // Test callback utils.
+ ASSERT_EQ(BB3->getParent(), F);
+ DTU.callbackDeleteBB(BB3,
+ [&F](BasicBlock *BB) { ASSERT_NE(BB->getParent(), F); });
+
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+ ASSERT_FALSE(DTU.hasPendingUpdates());
+
+ // Unnecessary flush() test
+ DTU.flush();
+ EXPECT_TRUE(DT.verify());
+ EXPECT_TRUE(PDT.verify());
+
+ // Remove all case branch to BB2 to test Eager recalculation.
+ // Code section from llvm::ConstantFoldTerminator
+ for (auto i = SI->case_begin(), e = SI->case_end(); i != e;) {
+ if (i->getCaseSuccessor() == BB2) {
+ // Remove this entry.
+ BB2->removePredecessor(BB0);
+ i = SI->removeCase(i);
+ e = SI->case_end();
+ } else
+ ++i;
+ }
+ ASSERT_FALSE(DT.verify());
+ ASSERT_FALSE(PDT.verify());
+ DTU.recalculate(*F);
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+}
+
+TEST(DomTreeUpdater, EagerUpdateReplaceEntryBB) {
+ StringRef FuncName = "f";
+ StringRef ModuleString = R"(
+ define i32 @f() {
+ bb0:
+ br label %bb1
+ bb1:
+ ret i32 1
+ }
+ )";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+
+ // Make the DTU.
+ DominatorTree DT(*F);
+ PostDominatorTree PDT(*F);
+ DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);
+ ASSERT_TRUE(DTU.hasDomTree());
+ ASSERT_TRUE(DTU.hasPostDomTree());
+ ASSERT_TRUE(DTU.isEager());
+ ASSERT_FALSE(DTU.isLazy());
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+
+ // Add a block as the new function entry BB. We also link it to BB0.
+ BasicBlock *NewEntry =
+ BasicBlock::Create(F->getContext(), "new_entry", F, BB0);
+ BranchInst::Create(BB0, NewEntry);
+ EXPECT_EQ(F->begin()->getName(), NewEntry->getName());
+ EXPECT_TRUE(&F->getEntryBlock() == NewEntry);
+
+ DTU.insertEdgeRelaxed(NewEntry, BB0);
+
+ // Changing the Entry BB requires a full recalculation of DomTree.
+ DTU.recalculate(*F);
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+
+ // CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1.
+ EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u);
+ NewEntry->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, NewEntry);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
+
+ // Update the DTU. At this point bb0 now has no predecessors but is still a
+ // Child of F.
+ DTU.applyUpdates({{DominatorTree::Delete, NewEntry, BB0},
+ {DominatorTree::Insert, NewEntry, BB1}});
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+
+ // Now remove bb0 from F.
+ ASSERT_FALSE(isa<UnreachableInst>(BB0->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB0));
+ DTU.deleteBB(BB0);
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+}
+
+TEST(DomTreeUpdater, LazyUpdateDTBasicOperations) {
+ StringRef FuncName = "f";
+ StringRef ModuleString = R"(
+ define i32 @f(i32 %i, i32 *%p) {
+ bb0:
+ store i32 %i, i32 *%p
+ switch i32 %i, label %bb1 [
+ i32 0, label %bb2
+ i32 1, label %bb2
+ i32 2, label %bb3
+ ]
+ bb1:
+ ret i32 1
+ bb2:
+ ret i32 2
+ bb3:
+ ret i32 3
+ }
+ )";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+
+ // Make the DTU.
+ DominatorTree DT(*F);
+ PostDominatorTree *PDT = nullptr;
+ DomTreeUpdater DTU(&DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
+ ASSERT_TRUE(DTU.hasDomTree());
+ ASSERT_FALSE(DTU.hasPostDomTree());
+ ASSERT_FALSE(DTU.isEager());
+ ASSERT_TRUE(DTU.isLazy());
+ ASSERT_TRUE(DTU.getDomTree().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+ BasicBlock *BB2 = &*FI++;
+ BasicBlock *BB3 = &*FI++;
+
+ // Test discards of self-domination update.
+ DTU.deleteEdge(BB0, BB0);
+ ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());
+
+ // Delete edge bb0 -> bb3 and push the update twice to verify duplicate
+ // entries are discarded.
+ std::vector<DominatorTree::UpdateType> Updates;
+ Updates.reserve(4);
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+
+ // Invalid Insert: no edge bb1 -> bb2 after change to bb0.
+ Updates.push_back({DominatorTree::Insert, BB1, BB2});
+ // Invalid Delete: edge exists bb0 -> bb1 after change to bb0.
+ Updates.push_back({DominatorTree::Delete, BB0, BB1});
+
+ // CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+
+ // Verify. Updates to DTU must be applied *after* all changes to the CFG
+ // (including block deletion).
+ DTU.applyUpdates(Updates);
+ ASSERT_TRUE(DTU.getDomTree().verify());
+
+ // Deletion of a BasicBlock is an immediate event. We remove all uses to the
+ // contained Instructions and change the Terminator to "unreachable" when
+ // queued for deletion. Its parent is still F until all the pending updates
+ // are applied to all trees held by the DomTreeUpdater (DomTree/PostDomTree).
+ // We don't defer this action because it can cause problems for other
+ // transforms or analysis as it's part of the actual CFG. We only defer
+ // updates to the DominatorTrees. This code will crash if it is placed before
+ // the BranchInst::Create() call above. After a deletion of a BasicBlock. Only
+ // an explicit flush event can trigger the flushing of deleteBBs. Because some
+ // passes using Lazy UpdateStrategy rely on this behavior.
+
+ ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));
+ EXPECT_FALSE(DTU.hasPendingDeletedBB());
+ DTU.deleteBB(BB3);
+ EXPECT_TRUE(DTU.isBBPendingDeletion(BB3));
+ EXPECT_TRUE(DTU.hasPendingDeletedBB());
+ ASSERT_TRUE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_EQ(BB3->getParent(), F);
+ DTU.recalculate(*F);
+ EXPECT_FALSE(DTU.hasPendingDeletedBB());
+}
+
+TEST(DomTreeUpdater, LazyUpdateDTInheritedPreds) {
+ StringRef FuncName = "f";
+ StringRef ModuleString = R"(
+ define i32 @f(i32 %i, i32 *%p) {
+ bb0:
+ store i32 %i, i32 *%p
+ switch i32 %i, label %bb1 [
+ i32 2, label %bb2
+ i32 3, label %bb3
+ ]
+ bb1:
+ br label %bb3
+ bb2:
+ br label %bb3
+ bb3:
+ ret i32 3
+ }
+ )";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+
+ // Make the DTU.
+ DominatorTree DT(*F);
+ PostDominatorTree *PDT = nullptr;
+ DomTreeUpdater DTU(&DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
+ ASSERT_TRUE(DTU.hasDomTree());
+ ASSERT_FALSE(DTU.hasPostDomTree());
+ ASSERT_FALSE(DTU.isEager());
+ ASSERT_TRUE(DTU.isLazy());
+ ASSERT_TRUE(DTU.getDomTree().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+ BasicBlock *BB2 = &*FI++;
+ BasicBlock *BB3 = &*FI++;
+
+ // There are several CFG locations where we have:
+ //
+ // pred1..predN
+ // | |
+ // +> curr <+ converted into: pred1..predN curr
+ // | | |
+ // v +> succ <+
+ // succ
+ //
+ // There is a specific shape of this we have to be careful of:
+ //
+ // pred1..predN
+ // || |
+ // |+> curr <+ converted into: pred1..predN curr
+ // | | | |
+ // | v +> succ <+
+ // +-> succ
+ //
+ // While the final CFG form is functionally identical the updates to
+ // DTU are not. In the first case we must have DTU.insertEdge(Pred1, Succ)
+ // while in the latter case we must *NOT* have DTU.insertEdge(Pred1, Succ).
+
+ // CFG Change: bb0 now only has bb0 -> bb1 and bb0 -> bb3. We are preparing to
+ // remove bb2.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, BB3, ConstantInt::getTrue(F->getContext()), BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+
+ // Test callback utils.
+ std::vector<BasicBlock *> BasicBlocks;
+ BasicBlocks.push_back(BB1);
+ BasicBlocks.push_back(BB2);
+ auto Eraser = [&](BasicBlock *BB) {
+ BasicBlocks.erase(
+ std::remove_if(BasicBlocks.begin(), BasicBlocks.end(),
+ [&](const BasicBlock *i) { return i == BB; }),
+ BasicBlocks.end());
+ };
+ ASSERT_EQ(BasicBlocks.size(), static_cast<size_t>(2));
+ // Remove bb2 from F. This has to happen before the call to applyUpdates() for
+ // DTU to detect there is no longer an edge between bb2 -> bb3. The deleteBB()
+ // method converts bb2's TI into "unreachable".
+ ASSERT_FALSE(isa<UnreachableInst>(BB2->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB2));
+ DTU.callbackDeleteBB(BB2, Eraser);
+ EXPECT_TRUE(DTU.isBBPendingDeletion(BB2));
+ ASSERT_TRUE(isa<UnreachableInst>(BB2->getTerminator()));
+ EXPECT_EQ(BB2->getParent(), F);
+
+ // Queue up the DTU updates.
+ std::vector<DominatorTree::UpdateType> Updates;
+ Updates.reserve(4);
+ Updates.push_back({DominatorTree::Delete, BB0, BB2});
+ Updates.push_back({DominatorTree::Delete, BB2, BB3});
+
+ // Handle the specific shape case next.
+ // CFG Change: bb0 now only branches to bb3. We are preparing to remove bb1.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB3, BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
+
+ // Remove bb1 from F. This has to happen before the call to applyUpdates() for
+ // DTU to detect there is no longer an edge between bb1 -> bb3. The deleteBB()
+ // method converts bb1's TI into "unreachable".
+ ASSERT_FALSE(isa<UnreachableInst>(BB1->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB1));
+ DTU.callbackDeleteBB(BB1, Eraser);
+ EXPECT_TRUE(DTU.isBBPendingDeletion(BB1));
+ ASSERT_TRUE(isa<UnreachableInst>(BB1->getTerminator()));
+ EXPECT_EQ(BB1->getParent(), F);
+
+ // Update the DTU. In this case we don't call DTU.insertEdge(BB0, BB3) because
+ // the edge previously existed at the start of this test when DT was first
+ // created.
+ Updates.push_back({DominatorTree::Delete, BB0, BB1});
+ Updates.push_back({DominatorTree::Delete, BB1, BB3});
+
+ // Verify everything.
+ DTU.applyUpdates(Updates);
+ ASSERT_EQ(BasicBlocks.size(), static_cast<size_t>(2));
+ DTU.flush();
+ ASSERT_EQ(BasicBlocks.size(), static_cast<size_t>(0));
+ ASSERT_TRUE(DT.verify());
+}
+
+TEST(DomTreeUpdater, LazyUpdateBasicOperations) {
+ StringRef FuncName = "f";
+ StringRef ModuleString = R"(
+ define i32 @f(i32 %i, i32 *%p) {
+ bb0:
+ store i32 %i, i32 *%p
+ switch i32 %i, label %bb1 [
+ i32 0, label %bb2
+ i32 1, label %bb2
+ i32 2, label %bb3
+ ]
+ bb1:
+ ret i32 1
+ bb2:
+ ret i32 2
+ bb3:
+ ret i32 3
+ }
+ )";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+
+ // Make the DTU.
+ DominatorTree DT(*F);
+ PostDominatorTree PDT(*F);
+ DomTreeUpdater DTU(&DT, &PDT, DomTreeUpdater::UpdateStrategy::Lazy);
+ ASSERT_TRUE(DTU.hasDomTree());
+ ASSERT_TRUE(DTU.hasPostDomTree());
+ ASSERT_FALSE(DTU.isEager());
+ ASSERT_TRUE(DTU.isLazy());
+ ASSERT_TRUE(DTU.getDomTree().verify());
+ ASSERT_TRUE(DTU.getPostDomTree().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+ BasicBlock *BB2 = &*FI++;
+ BasicBlock *BB3 = &*FI++;
+ // Test discards of self-domination update.
+ DTU.deleteEdge(BB0, BB0);
+
+ // Delete edge bb0 -> bb3 and push the update twice to verify duplicate
+ // entries are discarded.
+ std::vector<DominatorTree::UpdateType> Updates;
+ Updates.reserve(4);
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+
+ // Unnecessary Insert: no edge bb1 -> bb2 after change to bb0.
+ Updates.push_back({DominatorTree::Insert, BB1, BB2});
+ // Unnecessary Delete: edge exists bb0 -> bb1 after change to bb0.
+ Updates.push_back({DominatorTree::Delete, BB0, BB1});
+
+ // CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);
+ BB0->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
+
+ // Deletion of a BasicBlock is an immediate event. We remove all uses to the
+ // contained Instructions and change the Terminator to "unreachable" when
+ // queued for deletion. Its parent is still F until DTU.flushDomTree is
+ // called. We don't defer this action because it can cause problems for other
+ // transforms or analysis as it's part of the actual CFG. We only defer
+ // updates to the DominatorTree. This code will crash if it is placed before
+ // the BranchInst::Create() call above.
+ bool CallbackFlag = false;
+ ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));
+ DTU.callbackDeleteBB(BB3, [&](BasicBlock *) { CallbackFlag = true; });
+ EXPECT_TRUE(DTU.isBBPendingDeletion(BB3));
+ ASSERT_TRUE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_EQ(BB3->getParent(), F);
+
+ // Verify. Updates to DTU must be applied *after* all changes to the CFG
+ // (including block deletion).
+ DTU.applyUpdates(Updates);
+ ASSERT_TRUE(DTU.getDomTree().verify());
+ ASSERT_TRUE(DTU.hasPendingUpdates());
+ ASSERT_TRUE(DTU.hasPendingPostDomTreeUpdates());
+ ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());
+ ASSERT_TRUE(DTU.hasPendingDeletedBB());
+ ASSERT_TRUE(DTU.getPostDomTree().verify());
+ ASSERT_FALSE(DTU.hasPendingUpdates());
+ ASSERT_FALSE(DTU.hasPendingPostDomTreeUpdates());
+ ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());
+ ASSERT_FALSE(DTU.hasPendingDeletedBB());
+ ASSERT_EQ(CallbackFlag, true);
+}
+
+TEST(DomTreeUpdater, LazyUpdateReplaceEntryBB) {
+ StringRef FuncName = "f";
+ StringRef ModuleString = R"(
+ define i32 @f() {
+ bb0:
+ br label %bb1
+ bb1:
+ ret i32 1
+ }
+ )";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+
+ // Make the DTU.
+ DominatorTree DT(*F);
+ PostDominatorTree PDT(*F);
+ DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
+ ASSERT_TRUE(DTU.hasDomTree());
+ ASSERT_TRUE(DTU.hasPostDomTree());
+ ASSERT_FALSE(DTU.isEager());
+ ASSERT_TRUE(DTU.isLazy());
+ ASSERT_TRUE(DTU.getDomTree().verify());
+ ASSERT_TRUE(DTU.getPostDomTree().verify());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ BasicBlock *BB1 = &*FI++;
+
+ // Add a block as the new function entry BB. We also link it to BB0.
+ BasicBlock *NewEntry =
+ BasicBlock::Create(F->getContext(), "new_entry", F, BB0);
+ BranchInst::Create(BB0, NewEntry);
+ EXPECT_EQ(F->begin()->getName(), NewEntry->getName());
+ EXPECT_TRUE(&F->getEntryBlock() == NewEntry);
+
+ // Insert the new edge between new_entry -> bb0. Without this the
+ // recalculate() call below will not actually recalculate the DT as there
+ // are no changes pending and no blocks deleted.
+ DTU.insertEdge(NewEntry, BB0);
+
+ // Changing the Entry BB requires a full recalculation.
+ DTU.recalculate(*F);
+ ASSERT_TRUE(DTU.getDomTree().verify());
+ ASSERT_TRUE(DTU.getPostDomTree().verify());
+
+ // CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1.
+ EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u);
+ NewEntry->getTerminator()->eraseFromParent();
+ BranchInst::Create(BB1, NewEntry);
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
+
+ // Update the DTU. At this point bb0 now has no predecessors but is still a
+ // Child of F.
+ DTU.applyUpdates({{DominatorTree::Delete, NewEntry, BB0},
+ {DominatorTree::Insert, NewEntry, BB1}});
+ DTU.flush();
+ ASSERT_TRUE(DT.verify());
+ ASSERT_TRUE(PDT.verify());
+
+ // Now remove bb0 from F.
+ ASSERT_FALSE(isa<UnreachableInst>(BB0->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB0));
+ DTU.deleteBB(BB0);
+ EXPECT_TRUE(DTU.isBBPendingDeletion(BB0));
+ ASSERT_TRUE(isa<UnreachableInst>(BB0->getTerminator()));
+ EXPECT_EQ(BB0->getParent(), F);
+
+ // Perform a full recalculation of the DTU. It is not necessary here but we
+ // do this to test the case when there are no pending DT updates but there are
+ // pending deleted BBs.
+ ASSERT_TRUE(DTU.hasPendingDeletedBB());
+ DTU.recalculate(*F);
+ ASSERT_FALSE(DTU.hasPendingDeletedBB());
+}
+
+TEST(DomTreeUpdater, LazyUpdateStepTest) {
+ // This test focus on testing a DTU holding both trees applying multiple
+ // updates and DT/PDT not flushed together.
+ StringRef FuncName = "f";
+ StringRef ModuleString = R"(
+ define i32 @f(i32 %i, i32 *%p) {
+ bb0:
+ store i32 %i, i32 *%p
+ switch i32 %i, label %bb1 [
+ i32 0, label %bb1
+ i32 1, label %bb2
+ i32 2, label %bb3
+ i32 3, label %bb1
+ ]
+ bb1:
+ ret i32 1
+ bb2:
+ ret i32 2
+ bb3:
+ ret i32 3
+ }
+ )";
+ // Make the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+ Function *F = M->getFunction(FuncName);
+
+ // Make the DomTreeUpdater.
+ DominatorTree DT(*F);
+ PostDominatorTree PDT(*F);
+ DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
+
+ ASSERT_TRUE(DTU.hasDomTree());
+ ASSERT_TRUE(DTU.hasPostDomTree());
+ ASSERT_FALSE(DTU.isEager());
+ ASSERT_TRUE(DTU.isLazy());
+ ASSERT_TRUE(DTU.getDomTree().verify());
+ ASSERT_TRUE(DTU.getPostDomTree().verify());
+ ASSERT_FALSE(DTU.hasPendingUpdates());
+
+ Function::iterator FI = F->begin();
+ BasicBlock *BB0 = &*FI++;
+ FI++;
+ BasicBlock *BB2 = &*FI++;
+ BasicBlock *BB3 = &*FI++;
+ SwitchInst *SI = dyn_cast<SwitchInst>(BB0->getTerminator());
+ ASSERT_NE(SI, nullptr) << "Couldn't get SwitchInst.";
+
+ // Delete edge bb0 -> bb3 and push the update twice to verify duplicate
+ // entries are discarded.
+ std::vector<DominatorTree::UpdateType> Updates;
+ Updates.reserve(1);
+ Updates.push_back({DominatorTree::Delete, BB0, BB3});
+
+ // CFG Change: remove edge bb0 -> bb3.
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 5u);
+ BB3->removePredecessor(BB0);
+ for (auto i = SI->case_begin(), e = SI->case_end(); i != e; ++i) {
+ if (i->getCaseIndex() == 2) {
+ SI->removeCase(i);
+ break;
+ }
+ }
+ EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);
+ // Deletion of a BasicBlock is an immediate event. We remove all uses to the
+ // contained Instructions and change the Terminator to "unreachable" when
+ // queued for deletion.
+ ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));
+ EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));
+ DTU.applyUpdates(Updates);
+
+ // Only flush DomTree.
+ ASSERT_TRUE(DTU.getDomTree().verify());
+ ASSERT_TRUE(DTU.hasPendingPostDomTreeUpdates());
+ ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());
+
+ ASSERT_EQ(BB3->getParent(), F);
+ DTU.deleteBB(BB3);
+
+ Updates.clear();
+
+ // Remove all case branch to BB2 to test Eager recalculation.
+ // Code section from llvm::ConstantFoldTerminator
+ for (auto i = SI->case_begin(), e = SI->case_end(); i != e;) {
+ if (i->getCaseSuccessor() == BB2) {
+ // Remove this entry.
+ BB2->removePredecessor(BB0);
+ i = SI->removeCase(i);
+ e = SI->case_end();
+ Updates.push_back({DominatorTree::Delete, BB0, BB2});
+ } else
+ ++i;
+ }
+
+ DTU.applyUpdates(Updates);
+ // flush PostDomTree
+ ASSERT_TRUE(DTU.getPostDomTree().verify());
+ ASSERT_FALSE(DTU.hasPendingPostDomTreeUpdates());
+ ASSERT_TRUE(DTU.hasPendingDomTreeUpdates());
+ // flush both trees
+ DTU.flush();
+ ASSERT_TRUE(DT.verify());
+}
diff --git a/unittests/IR/DominatorTreeBatchUpdatesTest.cpp b/unittests/IR/DominatorTreeBatchUpdatesTest.cpp
index 4ad1f69030c1..85308685e5dd 100644
--- a/unittests/IR/DominatorTreeBatchUpdatesTest.cpp
+++ b/unittests/IR/DominatorTreeBatchUpdatesTest.cpp
@@ -22,13 +22,10 @@ namespace {
const auto CFGInsert = CFGBuilder::ActionKind::Insert;
const auto CFGDelete = CFGBuilder::ActionKind::Delete;
-struct PostDomTree : PostDomTreeBase<BasicBlock> {
- PostDomTree(Function &F) { recalculate(F); }
-};
using DomUpdate = DominatorTree::UpdateType;
static_assert(
- std::is_same<DomUpdate, PostDomTree::UpdateType>::value,
+ std::is_same<DomUpdate, PostDominatorTree::UpdateType>::value,
"Trees differing only in IsPostDom should have the same update types");
using DomSNCA = DomTreeBuilder::SemiNCAInfo<DomTreeBuilder::BBDomTree>;
using PostDomSNCA = DomTreeBuilder::SemiNCAInfo<DomTreeBuilder::BBPostDomTree>;
@@ -62,9 +59,9 @@ TEST(DominatorTreeBatchUpdates, LegalizeDomUpdates) {
{Insert, B, D}, {Delete, C, D}, {Delete, A, B}};
SmallVector<DomUpdate, 4> Legalized;
DomSNCA::LegalizeUpdates(Updates, Legalized);
- DEBUG(dbgs() << "Legalized updates:\t");
- DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
- DEBUG(dbgs() << "\n");
+ LLVM_DEBUG(dbgs() << "Legalized updates:\t");
+ LLVM_DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
+ LLVM_DEBUG(dbgs() << "\n");
EXPECT_EQ(Legalized.size(), 3UL);
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, B, C}), Legalized.end());
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, B, D}), Legalized.end());
@@ -85,9 +82,9 @@ TEST(DominatorTreeBatchUpdates, LegalizePostDomUpdates) {
{Insert, B, D}, {Delete, C, D}, {Delete, A, B}};
SmallVector<DomUpdate, 4> Legalized;
PostDomSNCA::LegalizeUpdates(Updates, Legalized);
- DEBUG(dbgs() << "Legalized postdom updates:\t");
- DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
- DEBUG(dbgs() << "\n");
+ LLVM_DEBUG(dbgs() << "Legalized postdom updates:\t");
+ LLVM_DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
+ LLVM_DEBUG(dbgs() << "\n");
EXPECT_EQ(Legalized.size(), 3UL);
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, C, B}), Legalized.end());
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, D, B}), Legalized.end());
@@ -100,8 +97,8 @@ TEST(DominatorTreeBatchUpdates, SingleInsertion) {
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
- EXPECT_TRUE(DT.verify());
+ PostDominatorTree PDT(*Holder.F);
+ EXPECT_TRUE(PDT.verify());
BasicBlock *B = Builder.getOrAddBlock("B");
BasicBlock *C = Builder.getOrAddBlock("C");
@@ -122,8 +119,8 @@ TEST(DominatorTreeBatchUpdates, SingleDeletion) {
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
- EXPECT_TRUE(DT.verify());
+ PostDominatorTree PDT(*Holder.F);
+ EXPECT_TRUE(PDT.verify());
BasicBlock *B = Builder.getOrAddBlock("B");
BasicBlock *C = Builder.getOrAddBlock("C");
@@ -148,7 +145,7 @@ TEST(DominatorTreeBatchUpdates, FewInsertion) {
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
BasicBlock *B = Builder.getOrAddBlock("B");
@@ -181,7 +178,7 @@ TEST(DominatorTreeBatchUpdates, FewDeletions) {
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
auto Updates = ToDomUpdates(Builder, CFGUpdates);
@@ -212,7 +209,7 @@ TEST(DominatorTreeBatchUpdates, InsertDelete) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
while (B.applyUpdate())
@@ -245,7 +242,7 @@ TEST(DominatorTreeBatchUpdates, InsertDeleteExhaustive) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
while (B.applyUpdate())
@@ -258,3 +255,98 @@ TEST(DominatorTreeBatchUpdates, InsertDeleteExhaustive) {
EXPECT_TRUE(PDT.verify());
}
}
+
+// These are some odd flowgraphs, usually generated from csmith cases,
+// which are difficult on post dom trees.
+TEST(DominatorTreeBatchUpdates, InfiniteLoop) {
+ std::vector<CFGBuilder::Arc> Arcs = {
+ {"1", "2"},
+ {"2", "3"},
+ {"3", "6"}, {"3", "5"},
+ {"4", "5"},
+ {"5", "2"},
+ {"6", "3"}, {"6", "4"}};
+
+ // SplitBlock on 3 -> 5
+ std::vector<CFGBuilder::Update> Updates = {
+ {CFGInsert, {"N", "5"}}, {CFGInsert, {"3", "N"}}, {CFGDelete, {"3", "5"}}};
+
+ CFGHolder Holder;
+ CFGBuilder B(Holder.F, Arcs, Updates);
+ DominatorTree DT(*Holder.F);
+ EXPECT_TRUE(DT.verify());
+ PostDominatorTree PDT(*Holder.F);
+ EXPECT_TRUE(PDT.verify());
+
+ while (B.applyUpdate())
+ ;
+
+ auto DomUpdates = ToDomUpdates(B, Updates);
+ DT.applyUpdates(DomUpdates);
+ EXPECT_TRUE(DT.verify());
+ PDT.applyUpdates(DomUpdates);
+ EXPECT_TRUE(PDT.verify());
+}
+
+TEST(DominatorTreeBatchUpdates, DeadBlocks) {
+ std::vector<CFGBuilder::Arc> Arcs = {
+ {"1", "2"},
+ {"2", "3"},
+ {"3", "4"}, {"3", "7"},
+ {"4", "4"},
+ {"5", "6"}, {"5", "7"},
+ {"6", "7"},
+ {"7", "2"}, {"7", "8"}};
+
+ // Remove dead 5 and 7,
+ // plus SplitBlock on 7 -> 8
+ std::vector<CFGBuilder::Update> Updates = {
+ {CFGDelete, {"6", "7"}}, {CFGDelete, {"5", "7"}}, {CFGDelete, {"5", "6"}},
+ {CFGInsert, {"N", "8"}}, {CFGInsert, {"7", "N"}}, {CFGDelete, {"7", "8"}}};
+
+ CFGHolder Holder;
+ CFGBuilder B(Holder.F, Arcs, Updates);
+ DominatorTree DT(*Holder.F);
+ EXPECT_TRUE(DT.verify());
+ PostDominatorTree PDT(*Holder.F);
+ EXPECT_TRUE(PDT.verify());
+
+ while (B.applyUpdate())
+ ;
+
+ auto DomUpdates = ToDomUpdates(B, Updates);
+ DT.applyUpdates(DomUpdates);
+ EXPECT_TRUE(DT.verify());
+ PDT.applyUpdates(DomUpdates);
+ EXPECT_TRUE(PDT.verify());
+}
+
+TEST(DominatorTreeBatchUpdates, InfiniteLoop2) {
+ std::vector<CFGBuilder::Arc> Arcs = {
+ {"1", "2"},
+ {"2", "6"}, {"2", "3"},
+ {"3", "4"},
+ {"4", "5"}, {"4", "6"},
+ {"5", "4"},
+ {"6", "2"}};
+
+ // SplitBlock on 4 -> 6
+ std::vector<CFGBuilder::Update> Updates = {
+ {CFGInsert, {"N", "6"}}, {CFGInsert, {"4", "N"}}, {CFGDelete, {"4", "6"}}};
+
+ CFGHolder Holder;
+ CFGBuilder B(Holder.F, Arcs, Updates);
+ DominatorTree DT(*Holder.F);
+ EXPECT_TRUE(DT.verify());
+ PostDominatorTree PDT(*Holder.F);
+ EXPECT_TRUE(PDT.verify());
+
+ while (B.applyUpdate())
+ ;
+
+ auto DomUpdates = ToDomUpdates(B, Updates);
+ DT.applyUpdates(DomUpdates);
+ EXPECT_TRUE(DT.verify());
+ PDT.applyUpdates(DomUpdates);
+ EXPECT_TRUE(PDT.verify());
+}
diff --git a/unittests/IR/DominatorTreeTest.cpp b/unittests/IR/DominatorTreeTest.cpp
index bf5aced49289..cf81623d0d17 100644
--- a/unittests/IR/DominatorTreeTest.cpp
+++ b/unittests/IR/DominatorTreeTest.cpp
@@ -9,6 +9,7 @@
#include <random>
#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/IteratedDominanceFrontier.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
@@ -21,19 +22,17 @@
using namespace llvm;
-struct PostDomTree : PostDomTreeBase<BasicBlock> {
- PostDomTree(Function &F) { recalculate(F); }
-};
/// Build the dominator tree for the function and run the Test.
static void runWithDomTree(
Module &M, StringRef FuncName,
- function_ref<void(Function &F, DominatorTree *DT, PostDomTree *PDT)> Test) {
+ function_ref<void(Function &F, DominatorTree *DT, PostDominatorTree *PDT)>
+ Test) {
auto *F = M.getFunction(FuncName);
ASSERT_NE(F, nullptr) << "Could not find " << FuncName;
// Compute the dominator tree for the function.
DominatorTree DT(*F);
- PostDomTree PDT(*F);
+ PostDominatorTree PDT(*F);
Test(*F, &DT, &PDT);
}
@@ -75,7 +74,7 @@ TEST(DominatorTree, Unreachable) {
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
runWithDomTree(
- *M, "f", [&](Function &F, DominatorTree *DT, PostDomTree *PDT) {
+ *M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {
Function::iterator FI = F.begin();
BasicBlock *BB0 = &*FI++;
@@ -264,14 +263,14 @@ TEST(DominatorTree, Unreachable) {
EXPECT_EQ(DT->getNode(BB4)->getLevel(), 1U);
// Change root node
- DT->verifyDomTree();
+ EXPECT_TRUE(DT->verify());
BasicBlock *NewEntry =
BasicBlock::Create(F.getContext(), "new_entry", &F, BB0);
BranchInst::Create(BB0, NewEntry);
EXPECT_EQ(F.begin()->getName(), NewEntry->getName());
EXPECT_TRUE(&F.getEntryBlock() == NewEntry);
DT->setNewRoot(NewEntry);
- DT->verifyDomTree();
+ EXPECT_TRUE(DT->verify());
});
}
@@ -295,7 +294,7 @@ TEST(DominatorTree, NonUniqueEdges) {
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
runWithDomTree(
- *M, "f", [&](Function &F, DominatorTree *DT, PostDomTree *PDT) {
+ *M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {
Function::iterator FI = F.begin();
BasicBlock *BB0 = &*FI++;
@@ -359,7 +358,7 @@ TEST(DominatorTree, NonUniqueEdges) {
// unreachable Exit
//
// Both the blocks that end with ret and with unreachable become trivial
-// PostDomTree roots, as they have no successors.
+// PostDominatorTree roots, as they have no successors.
//
TEST(DominatorTree, DeletingEdgesIntroducesUnreachables) {
StringRef ModuleString =
@@ -379,7 +378,7 @@ TEST(DominatorTree, DeletingEdgesIntroducesUnreachables) {
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
runWithDomTree(
- *M, "f", [&](Function &F, DominatorTree *DT, PostDomTree *PDT) {
+ *M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {
Function::iterator FI = F.begin();
FI++;
@@ -406,7 +405,7 @@ TEST(DominatorTree, DeletingEdgesIntroducesUnreachables) {
DominatorTree NDT(F);
EXPECT_EQ(DT->compare(NDT), 0);
- PostDomTree NPDT(F);
+ PostDominatorTree NPDT(F);
EXPECT_EQ(PDT->compare(NPDT), 0);
});
}
@@ -473,7 +472,7 @@ TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop) {
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
runWithDomTree(
- *M, "f", [&](Function &F, DominatorTree *DT, PostDomTree *PDT) {
+ *M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {
Function::iterator FI = F.begin();
FI++;
@@ -498,7 +497,7 @@ TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop) {
DominatorTree NDT(F);
EXPECT_EQ(DT->compare(NDT), 0);
- PostDomTree NPDT(F);
+ PostDominatorTree NPDT(F);
EXPECT_EQ(PDT->compare(NPDT), 0);
});
}
@@ -562,7 +561,7 @@ TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop2) {
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
runWithDomTree(
- *M, "f", [&](Function &F, DominatorTree *DT, PostDomTree *PDT) {
+ *M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {
Function::iterator FI = F.begin();
FI++;
@@ -593,11 +592,80 @@ TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop2) {
DominatorTree NDT(F);
EXPECT_EQ(DT->compare(NDT), 0);
- PostDomTree NPDT(F);
+ PostDominatorTree NPDT(F);
EXPECT_EQ(PDT->compare(NPDT), 0);
});
}
+// Verify that the IDF returns blocks in a deterministic way.
+//
+// Test case:
+//
+// CFG
+//
+// (A)
+// / \
+// / \
+// (B) (C)
+// |\ /|
+// | X |
+// |/ \|
+// (D) (E)
+//
+// IDF for block B is {D, E}, and the order of blocks in this list is defined by
+// their 1) level in dom-tree and 2) DFSIn number if the level is the same.
+//
+TEST(DominatorTree, IDFDeterminismTest) {
+ StringRef ModuleString =
+ "define void @f() {\n"
+ "A:\n"
+ " br i1 undef, label %B, label %C\n"
+ "B:\n"
+ " br i1 undef, label %D, label %E\n"
+ "C:\n"
+ " br i1 undef, label %D, label %E\n"
+ "D:\n"
+ " ret void\n"
+ "E:\n"
+ " ret void\n"
+ "}\n";
+
+ // Parse the module.
+ LLVMContext Context;
+ std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
+
+ runWithDomTree(
+ *M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {
+ Function::iterator FI = F.begin();
+
+ BasicBlock *A = &*FI++;
+ BasicBlock *B = &*FI++;
+ BasicBlock *C = &*FI++;
+ BasicBlock *D = &*FI++;
+ BasicBlock *E = &*FI++;
+ (void)C;
+
+ DT->updateDFSNumbers();
+ ForwardIDFCalculator IDF(*DT);
+ SmallPtrSet<BasicBlock *, 1> DefBlocks;
+ DefBlocks.insert(B);
+ IDF.setDefiningBlocks(DefBlocks);
+
+ SmallVector<BasicBlock *, 32> IDFBlocks;
+ SmallPtrSet<BasicBlock *, 32> LiveInBlocks;
+ IDF.resetLiveInBlocks();
+ IDF.calculate(IDFBlocks);
+
+
+ EXPECT_EQ(IDFBlocks.size(), 2UL);
+ EXPECT_EQ(DT->getNode(A)->getDFSNumIn(), 0UL);
+ EXPECT_EQ(IDFBlocks[0], D);
+ EXPECT_EQ(IDFBlocks[1], E);
+ EXPECT_TRUE(DT->getNode(IDFBlocks[0])->getDFSNumIn() <
+ DT->getNode(IDFBlocks[1])->getDFSNumIn());
+ });
+}
+
namespace {
const auto Insert = CFGBuilder::ActionKind::Insert;
const auto Delete = CFGBuilder::ActionKind::Delete;
@@ -621,7 +689,7 @@ TEST(DominatorTree, InsertReachable) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -647,7 +715,7 @@ TEST(DominatorTree, InsertReachable2) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate = B.applyUpdate();
@@ -675,7 +743,7 @@ TEST(DominatorTree, InsertUnreachable) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -696,7 +764,7 @@ TEST(DominatorTree, InsertFromUnreachable) {
std::vector<CFGBuilder::Update> Updates = {{Insert, {"3", "5"}}};
CFGBuilder B(Holder.F, Arcs, Updates);
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate = B.applyUpdate();
@@ -708,7 +776,9 @@ TEST(DominatorTree, InsertFromUnreachable) {
PDT.insertEdge(From, To);
EXPECT_TRUE(PDT.verify());
EXPECT_TRUE(PDT.getRoots().size() == 2);
- EXPECT_NE(PDT.getNode(B.getOrAddBlock("5")), nullptr);
+ // Make sure we can use a const pointer with getNode.
+ const BasicBlock *BB5 = B.getOrAddBlock("5");
+ EXPECT_NE(PDT.getNode(BB5), nullptr);
}
TEST(DominatorTree, InsertMixed) {
@@ -724,7 +794,7 @@ TEST(DominatorTree, InsertMixed) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -754,7 +824,7 @@ TEST(DominatorTree, InsertPermut) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -781,7 +851,7 @@ TEST(DominatorTree, DeleteReachable) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -807,7 +877,7 @@ TEST(DominatorTree, DeleteUnreachable) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -822,36 +892,6 @@ TEST(DominatorTree, DeleteUnreachable) {
}
}
-TEST(DominatorTree, DeletionsInSubtrees) {
- CFGHolder Holder;
- std::vector<CFGBuilder::Arc> Arcs = {{"0", "1"}, {"1", "2"}, {"1", "3"},
- {"1", "6"}, {"3", "4"}, {"2", "5"},
- {"5", "2"}};
-
- // It is possible to perform multiple deletions and inform the
- // DominatorTree about them at the same time, if the all of the
- // deletions happen in different subtrees.
- std::vector<CFGBuilder::Update> Updates = {{Delete, {"1", "2"}},
- {Delete, {"1", "3"}}};
- CFGBuilder B(Holder.F, Arcs, Updates);
- DominatorTree DT(*Holder.F);
- EXPECT_TRUE(DT.verify());
-
- Optional<CFGBuilder::Update> LastUpdate;
- while ((LastUpdate = B.applyUpdate()))
- ;
-
- DT.deleteEdge(B.getOrAddBlock("1"), B.getOrAddBlock("2"));
- DT.deleteEdge(B.getOrAddBlock("1"), B.getOrAddBlock("3"));
-
- EXPECT_TRUE(DT.verify());
- EXPECT_EQ(DT.getNode(B.getOrAddBlock("2")), nullptr);
- EXPECT_EQ(DT.getNode(B.getOrAddBlock("3")), nullptr);
- EXPECT_EQ(DT.getNode(B.getOrAddBlock("4")), nullptr);
- EXPECT_EQ(DT.getNode(B.getOrAddBlock("5")), nullptr);
- EXPECT_NE(DT.getNode(B.getOrAddBlock("6")), nullptr);
-}
-
TEST(DominatorTree, InsertDelete) {
std::vector<CFGBuilder::Arc> Arcs = {
{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},
@@ -867,7 +907,7 @@ TEST(DominatorTree, InsertDelete) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -905,7 +945,7 @@ TEST(DominatorTree, InsertDeleteExhaustive) {
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
- PostDomTree PDT(*Holder.F);
+ PostDominatorTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());
Optional<CFGBuilder::Update> LastUpdate;
@@ -925,3 +965,28 @@ TEST(DominatorTree, InsertDeleteExhaustive) {
}
}
}
+
+TEST(DominatorTree, InsertIntoIrreducible) {
+ std::vector<CFGBuilder::Arc> Arcs = {
+ {"0", "1"},
+ {"1", "27"}, {"1", "7"},
+ {"10", "18"},
+ {"13", "10"},
+ {"18", "13"}, {"18", "23"},
+ {"23", "13"}, {"23", "24"},
+ {"24", "1"}, {"24", "18"},
+ {"27", "24"}};
+
+ CFGHolder Holder;
+ CFGBuilder B(Holder.F, Arcs, {{Insert, {"7", "23"}}});
+ DominatorTree DT(*Holder.F);
+ EXPECT_TRUE(DT.verify());
+
+ B.applyUpdate();
+ BasicBlock *From = B.getOrAddBlock("7");
+ BasicBlock *To = B.getOrAddBlock("23");
+ DT.insertEdge(From, To);
+
+ EXPECT_TRUE(DT.verify());
+}
+
diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp
index bb74756d81a9..720967cb136d 100644
--- a/unittests/IR/IRBuilderTest.cpp
+++ b/unittests/IR/IRBuilderTest.cpp
@@ -48,6 +48,27 @@ protected:
GlobalVariable *GV;
};
+TEST_F(IRBuilderTest, Intrinsics) {
+ IRBuilder<> Builder(BB);
+ Value *V;
+ CallInst *Call;
+ IntrinsicInst *II;
+
+ V = Builder.CreateLoad(GV);
+
+ Call = Builder.CreateMinNum(V, V);
+ II = cast<IntrinsicInst>(Call);
+ EXPECT_EQ(II->getIntrinsicID(), Intrinsic::minnum);
+
+ Call = Builder.CreateMaxNum(V, V);
+ II = cast<IntrinsicInst>(Call);
+ EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maxnum);
+
+ Call = Builder.CreateIntrinsic(Intrinsic::readcyclecounter);
+ II = cast<IntrinsicInst>(Call);
+ EXPECT_EQ(II->getIntrinsicID(), Intrinsic::readcyclecounter);
+}
+
TEST_F(IRBuilderTest, Lifetime) {
IRBuilder<> Builder(BB);
AllocaInst *Var1 = Builder.CreateAlloca(Builder.getInt8Ty());
@@ -438,6 +459,62 @@ TEST_F(IRBuilderTest, DIBuilder) {
EXPECT_TRUE(verifyModule(*M));
}
+TEST_F(IRBuilderTest, createArtificialSubprogram) {
+ IRBuilder<> Builder(BB);
+ DIBuilder DIB(*M);
+ auto File = DIB.createFile("main.c", "/");
+ auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File, "clang",
+ /*isOptimized=*/true, /*Flags=*/"",
+ /*Runtime Version=*/0);
+ auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
+ auto SP = DIB.createFunction(CU, "foo", /*LinkageName=*/"", File,
+ /*LineNo=*/1, Type, /*isLocalToUnit=*/false,
+ /*isDefinition=*/true, /*ScopeLine=*/2,
+ DINode::FlagZero, /*isOptimized=*/true);
+ EXPECT_TRUE(SP->isDistinct());
+
+ F->setSubprogram(SP);
+ AllocaInst *I = Builder.CreateAlloca(Builder.getInt8Ty());
+ ReturnInst *R = Builder.CreateRetVoid();
+ I->setDebugLoc(DebugLoc::get(3, 2, SP));
+ R->setDebugLoc(DebugLoc::get(4, 2, SP));
+ DIB.finalize();
+ EXPECT_FALSE(verifyModule(*M));
+
+ Function *G = Function::Create(F->getFunctionType(),
+ Function::ExternalLinkage, "", M.get());
+ BasicBlock *GBB = BasicBlock::Create(Ctx, "", G);
+ Builder.SetInsertPoint(GBB);
+ I->removeFromParent();
+ Builder.Insert(I);
+ Builder.CreateRetVoid();
+ EXPECT_FALSE(verifyModule(*M));
+
+ DISubprogram *GSP = DIBuilder::createArtificialSubprogram(F->getSubprogram());
+ EXPECT_EQ(SP->getFile(), GSP->getFile());
+ EXPECT_EQ(SP->getType(), GSP->getType());
+ EXPECT_EQ(SP->getLine(), GSP->getLine());
+ EXPECT_EQ(SP->getScopeLine(), GSP->getScopeLine());
+ EXPECT_TRUE(GSP->isDistinct());
+
+ G->setSubprogram(GSP);
+ EXPECT_TRUE(verifyModule(*M));
+
+ auto *InlinedAtNode =
+ DILocation::getDistinct(Ctx, GSP->getScopeLine(), 0, GSP);
+ DebugLoc DL = I->getDebugLoc();
+ DenseMap<const MDNode *, MDNode *> IANodes;
+ auto IA = DebugLoc::appendInlinedAt(DL, InlinedAtNode, Ctx, IANodes);
+ auto NewDL = DebugLoc::get(DL.getLine(), DL.getCol(), DL.getScope(), IA);
+ I->setDebugLoc(NewDL);
+ EXPECT_FALSE(verifyModule(*M));
+
+ EXPECT_EQ("foo", SP->getName());
+ EXPECT_EQ("foo", GSP->getName());
+ EXPECT_FALSE(SP->isArtificial());
+ EXPECT_TRUE(GSP->isArtificial());
+}
+
TEST_F(IRBuilderTest, InsertExtractElement) {
IRBuilder<> Builder(BB);
diff --git a/unittests/IR/InstructionsTest.cpp b/unittests/IR/InstructionsTest.cpp
index 619ddc5413df..6bac59620d9e 100644
--- a/unittests/IR/InstructionsTest.cpp
+++ b/unittests/IR/InstructionsTest.cpp
@@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Instructions.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Analysis/ValueTracking.h"
@@ -21,6 +22,7 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/NoFolder.h"
#include "llvm/IR/Operator.h"
+#include "llvm/Support/SourceMgr.h"
#include "gmock/gmock-matchers.h"
#include "gtest/gtest.h"
#include <memory>
@@ -28,6 +30,14 @@
namespace llvm {
namespace {
+static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
+ if (!Mod)
+ Err.print("InstructionsTests", errs());
+ return Mod;
+}
+
TEST(InstructionsTest, ReturnInst) {
LLVMContext C;
@@ -747,5 +757,126 @@ TEST(InstructionsTest, CommuteShuffleMask) {
EXPECT_THAT(Indices, testing::ContainerEq(ArrayRef<int>({-1, 4, 3})));
}
+TEST(InstructionsTest, ShuffleMaskQueries) {
+ // Create the elements for various constant vectors.
+ LLVMContext Ctx;
+ Type *Int32Ty = Type::getInt32Ty(Ctx);
+ Constant *CU = UndefValue::get(Int32Ty);
+ Constant *C0 = ConstantInt::get(Int32Ty, 0);
+ Constant *C1 = ConstantInt::get(Int32Ty, 1);
+ Constant *C2 = ConstantInt::get(Int32Ty, 2);
+ Constant *C3 = ConstantInt::get(Int32Ty, 3);
+ Constant *C4 = ConstantInt::get(Int32Ty, 4);
+ Constant *C5 = ConstantInt::get(Int32Ty, 5);
+ Constant *C6 = ConstantInt::get(Int32Ty, 6);
+ Constant *C7 = ConstantInt::get(Int32Ty, 7);
+
+ Constant *Identity = ConstantVector::get({C0, CU, C2, C3, C4});
+ EXPECT_TRUE(ShuffleVectorInst::isIdentityMask(Identity));
+ EXPECT_FALSE(ShuffleVectorInst::isSelectMask(Identity)); // identity is distinguished from select
+ EXPECT_FALSE(ShuffleVectorInst::isReverseMask(Identity));
+ EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(Identity)); // identity is always single source
+ EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Identity));
+ EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(Identity));
+
+ Constant *Select = ConstantVector::get({CU, C1, C5});
+ EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(Select));
+ EXPECT_TRUE(ShuffleVectorInst::isSelectMask(Select));
+ EXPECT_FALSE(ShuffleVectorInst::isReverseMask(Select));
+ EXPECT_FALSE(ShuffleVectorInst::isSingleSourceMask(Select));
+ EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Select));
+ EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(Select));
+
+ Constant *Reverse = ConstantVector::get({C3, C2, C1, CU});
+ EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(Reverse));
+ EXPECT_FALSE(ShuffleVectorInst::isSelectMask(Reverse));
+ EXPECT_TRUE(ShuffleVectorInst::isReverseMask(Reverse));
+ EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(Reverse)); // reverse is always single source
+ EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Reverse));
+ EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(Reverse));
+
+ Constant *SingleSource = ConstantVector::get({C2, C2, C0, CU});
+ EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(SingleSource));
+ EXPECT_FALSE(ShuffleVectorInst::isSelectMask(SingleSource));
+ EXPECT_FALSE(ShuffleVectorInst::isReverseMask(SingleSource));
+ EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(SingleSource));
+ EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(SingleSource));
+ EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(SingleSource));
+
+ Constant *ZeroEltSplat = ConstantVector::get({C0, C0, CU, C0});
+ EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(ZeroEltSplat));
+ EXPECT_FALSE(ShuffleVectorInst::isSelectMask(ZeroEltSplat));
+ EXPECT_FALSE(ShuffleVectorInst::isReverseMask(ZeroEltSplat));
+ EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(ZeroEltSplat)); // 0-splat is always single source
+ EXPECT_TRUE(ShuffleVectorInst::isZeroEltSplatMask(ZeroEltSplat));
+ EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(ZeroEltSplat));
+
+ Constant *Transpose = ConstantVector::get({C0, C4, C2, C6});
+ EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(Transpose));
+ EXPECT_FALSE(ShuffleVectorInst::isSelectMask(Transpose));
+ EXPECT_FALSE(ShuffleVectorInst::isReverseMask(Transpose));
+ EXPECT_FALSE(ShuffleVectorInst::isSingleSourceMask(Transpose));
+ EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Transpose));
+ EXPECT_TRUE(ShuffleVectorInst::isTransposeMask(Transpose));
+
+ // More tests to make sure the logic is/stays correct...
+ EXPECT_TRUE(ShuffleVectorInst::isIdentityMask(ConstantVector::get({CU, C1, CU, C3})));
+ EXPECT_TRUE(ShuffleVectorInst::isIdentityMask(ConstantVector::get({C4, CU, C6, CU})));
+
+ EXPECT_TRUE(ShuffleVectorInst::isSelectMask(ConstantVector::get({C4, C1, C6, CU})));
+ EXPECT_TRUE(ShuffleVectorInst::isSelectMask(ConstantVector::get({CU, C1, C6, C3})));
+
+ EXPECT_TRUE(ShuffleVectorInst::isReverseMask(ConstantVector::get({C7, C6, CU, C4})));
+ EXPECT_TRUE(ShuffleVectorInst::isReverseMask(ConstantVector::get({C3, CU, C1, CU})));
+
+ EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(ConstantVector::get({C7, C5, CU, C7})));
+ EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(ConstantVector::get({C3, C0, CU, C3})));
+
+ EXPECT_TRUE(ShuffleVectorInst::isZeroEltSplatMask(ConstantVector::get({C4, CU, CU, C4})));
+ EXPECT_TRUE(ShuffleVectorInst::isZeroEltSplatMask(ConstantVector::get({CU, C0, CU, C0})));
+
+ EXPECT_TRUE(ShuffleVectorInst::isTransposeMask(ConstantVector::get({C1, C5, C3, C7})));
+ EXPECT_TRUE(ShuffleVectorInst::isTransposeMask(ConstantVector::get({C1, C3})));
+}
+
+TEST(InstructionsTest, SkipDebug) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = parseIR(C,
+ R"(
+ declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+ define void @f() {
+ entry:
+ call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !13
+ ret void
+ }
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!3, !4}
+ !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+ !1 = !DIFile(filename: "t2.c", directory: "foo")
+ !2 = !{}
+ !3 = !{i32 2, !"Dwarf Version", i32 4}
+ !4 = !{i32 2, !"Debug Info Version", i32 3}
+ !8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2)
+ !9 = !DISubroutineType(types: !10)
+ !10 = !{null}
+ !11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12)
+ !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+ !13 = !DILocation(line: 2, column: 7, scope: !8)
+ )");
+ ASSERT_TRUE(M);
+ Function *F = cast<Function>(M->getNamedValue("f"));
+ BasicBlock &BB = F->front();
+
+ // The first non-debug instruction is the terminator.
+ auto *Term = BB.getTerminator();
+ EXPECT_EQ(Term, BB.begin()->getNextNonDebugInstruction());
+ EXPECT_EQ(Term->getIterator(), skipDebugIntrinsics(BB.begin()));
+
+ // After the terminator, there are no non-debug instructions.
+ EXPECT_EQ(nullptr, Term->getNextNonDebugInstruction());
+}
+
} // end anonymous namespace
} // end namespace llvm
diff --git a/unittests/IR/LegacyPassManagerTest.cpp b/unittests/IR/LegacyPassManagerTest.cpp
index 0ff2ec717597..9f5f431a5585 100644
--- a/unittests/IR/LegacyPassManagerTest.cpp
+++ b/unittests/IR/LegacyPassManagerTest.cpp
@@ -26,6 +26,7 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/OptBisect.h"
#include "llvm/Pass.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
@@ -396,6 +397,67 @@ namespace llvm {
delete M;
}
+ // Skips or runs optional passes.
+ struct CustomOptPassGate : public OptPassGate {
+ bool Skip;
+ CustomOptPassGate(bool Skip) : Skip(Skip) { }
+ bool shouldRunPass(const Pass *P, const Module &U) { return !Skip; }
+ };
+
+ // Optional module pass.
+ struct ModuleOpt: public ModulePass {
+ char run = 0;
+ static char ID;
+ ModuleOpt() : ModulePass(ID) { }
+ bool runOnModule(Module &M) override {
+ if (!skipModule(M))
+ run++;
+ return false;
+ }
+ };
+ char ModuleOpt::ID=0;
+
+ TEST(PassManager, CustomOptPassGate) {
+ LLVMContext Context0;
+ LLVMContext Context1;
+ LLVMContext Context2;
+ CustomOptPassGate SkipOptionalPasses(true);
+ CustomOptPassGate RunOptionalPasses(false);
+
+ Module M0("custom-opt-bisect", Context0);
+ Module M1("custom-opt-bisect", Context1);
+ Module M2("custom-opt-bisect2", Context2);
+ struct ModuleOpt *mOpt0 = new ModuleOpt();
+ struct ModuleOpt *mOpt1 = new ModuleOpt();
+ struct ModuleOpt *mOpt2 = new ModuleOpt();
+
+ mOpt0->run = mOpt1->run = mOpt2->run = 0;
+
+ legacy::PassManager Passes0;
+ legacy::PassManager Passes1;
+ legacy::PassManager Passes2;
+
+ Passes0.add(mOpt0);
+ Passes1.add(mOpt1);
+ Passes2.add(mOpt2);
+
+ Context1.setOptPassGate(SkipOptionalPasses);
+ Context2.setOptPassGate(RunOptionalPasses);
+
+ Passes0.run(M0);
+ Passes1.run(M1);
+ Passes2.run(M2);
+
+ // By default optional passes are run.
+ EXPECT_EQ(1, mOpt0->run);
+
+ // The first context skips optional passes.
+ EXPECT_EQ(0, mOpt1->run);
+
+ // The second context runs optional passes.
+ EXPECT_EQ(1, mOpt2->run);
+ }
+
Module *makeLLVMModule(LLVMContext &Context) {
// Module Construction
Module *mod = new Module("test-mem", Context);
diff --git a/unittests/IR/ManglerTest.cpp b/unittests/IR/ManglerTest.cpp
new file mode 100644
index 000000000000..04f1ca6cf977
--- /dev/null
+++ b/unittests/IR/ManglerTest.cpp
@@ -0,0 +1,140 @@
+//===- llvm/unittest/IR/ManglerTest.cpp - Mangler 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/Mangler.h"
+#include "llvm/IR/CallingConv.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/Module.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+static std::string mangleStr(StringRef IRName, Mangler &Mang,
+ const DataLayout &DL) {
+ std::string Mangled;
+ raw_string_ostream SS(Mangled);
+ Mang.getNameWithPrefix(SS, IRName, DL);
+ SS.flush();
+ return Mangled;
+}
+
+static std::string mangleFunc(StringRef IRName,
+ GlobalValue::LinkageTypes Linkage,
+ llvm::CallingConv::ID CC, Module &Mod,
+ Mangler &Mang) {
+ Type *VoidTy = Type::getVoidTy(Mod.getContext());
+ Type *I32Ty = Type::getInt32Ty(Mod.getContext());
+ FunctionType *FTy =
+ FunctionType::get(VoidTy, {I32Ty, I32Ty, I32Ty}, /*isVarArg=*/false);
+ Function *F = Function::Create(FTy, Linkage, IRName, &Mod);
+ F->setCallingConv(CC);
+ std::string Mangled;
+ raw_string_ostream SS(Mangled);
+ Mang.getNameWithPrefix(SS, F, false);
+ SS.flush();
+ F->eraseFromParent();
+ return Mangled;
+}
+
+namespace {
+
+TEST(ManglerTest, MachO) {
+ LLVMContext Ctx;
+ DataLayout DL("m:o"); // macho
+ Module Mod("test", Ctx);
+ Mod.setDataLayout(DL);
+ Mangler Mang;
+ EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");
+ EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
+ EXPECT_EQ(mangleStr("?foo", Mang, DL), "_?foo");
+ EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "_foo");
+ EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "_?foo");
+ EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "L_foo");
+}
+
+TEST(ManglerTest, WindowsX86) {
+ LLVMContext Ctx;
+ DataLayout DL("m:x-p:32:32"); // 32-bit windows
+ Module Mod("test", Ctx);
+ Mod.setDataLayout(DL);
+ Mangler Mang;
+ EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");
+ EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
+ EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
+ EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "_foo");
+ EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "?foo");
+ EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "L_foo");
+
+ // Test calling conv mangling.
+ EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_StdCall, Mod, Mang),
+ "_stdcall@12");
+ EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_FastCall, Mod, Mang),
+ "@fastcall@12");
+ EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_VectorCall, Mod, Mang),
+ "vectorcall@@12");
+
+ // Adding a '?' prefix blocks calling convention mangling.
+ EXPECT_EQ(mangleFunc("?fastcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_FastCall, Mod, Mang),
+ "?fastcall");
+}
+
+TEST(ManglerTest, WindowsX64) {
+ LLVMContext Ctx;
+ DataLayout DL("m:w-p:64:64"); // windows
+ Module Mod("test", Ctx);
+ Mod.setDataLayout(DL);
+ Mangler Mang;
+ EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
+ EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
+ EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
+ EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "foo");
+ EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ "?foo");
+ EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
+ llvm::CallingConv::C, Mod, Mang),
+ ".Lfoo");
+
+ // Test calling conv mangling.
+ EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_StdCall, Mod, Mang),
+ "stdcall");
+ EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_FastCall, Mod, Mang),
+ "fastcall");
+ EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_VectorCall, Mod, Mang),
+ "vectorcall@@24");
+
+ // Adding a '?' prefix blocks calling convention mangling.
+ EXPECT_EQ(mangleFunc("?vectorcall", llvm::GlobalValue::ExternalLinkage,
+ llvm::CallingConv::X86_VectorCall, Mod, Mang),
+ "?vectorcall");
+}
+
+} // end anonymous namespace
diff --git a/unittests/IR/MetadataTest.cpp b/unittests/IR/MetadataTest.cpp
index 672de55fbdea..84a7b02fa983 100644
--- a/unittests/IR/MetadataTest.cpp
+++ b/unittests/IR/MetadataTest.cpp
@@ -932,8 +932,11 @@ typedef MetadataTest DISubrangeTest;
TEST_F(DISubrangeTest, get) {
auto *N = DISubrange::get(Context, 5, 7);
+ auto Count = N->getCount();
EXPECT_EQ(dwarf::DW_TAG_subrange_type, N->getTag());
- EXPECT_EQ(5, N->getCount());
+ ASSERT_TRUE(Count);
+ ASSERT_TRUE(Count.is<ConstantInt*>());
+ EXPECT_EQ(5, Count.get<ConstantInt*>()->getSExtValue());
EXPECT_EQ(7, N->getLowerBound());
EXPECT_EQ(N, DISubrange::get(Context, 5, 7));
EXPECT_EQ(DISubrange::get(Context, 5, 0), DISubrange::get(Context, 5));
@@ -944,23 +947,47 @@ TEST_F(DISubrangeTest, get) {
TEST_F(DISubrangeTest, getEmptyArray) {
auto *N = DISubrange::get(Context, -1, 0);
+ auto Count = N->getCount();
EXPECT_EQ(dwarf::DW_TAG_subrange_type, N->getTag());
- EXPECT_EQ(-1, N->getCount());
+ ASSERT_TRUE(Count);
+ ASSERT_TRUE(Count.is<ConstantInt*>());
+ EXPECT_EQ(-1, Count.get<ConstantInt*>()->getSExtValue());
EXPECT_EQ(0, N->getLowerBound());
EXPECT_EQ(N, DISubrange::get(Context, -1, 0));
}
+TEST_F(DISubrangeTest, getVariableCount) {
+ DILocalScope *Scope = getSubprogram();
+ DIFile *File = getFile();
+ DIType *Type = getDerivedType();
+ DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);
+ auto *VlaExpr = DILocalVariable::get(Context, Scope, "vla_expr", File, 8,
+ Type, 2, Flags, 8);
+
+ auto *N = DISubrange::get(Context, VlaExpr, 0);
+ auto Count = N->getCount();
+ ASSERT_TRUE(Count);
+ ASSERT_TRUE(Count.is<DIVariable*>());
+ EXPECT_EQ(VlaExpr, Count.get<DIVariable*>());
+ ASSERT_TRUE(isa<DIVariable>(N->getRawCountNode()));
+ EXPECT_EQ(0, N->getLowerBound());
+ EXPECT_EQ("vla_expr", Count.get<DIVariable*>()->getName());
+ EXPECT_EQ(N, DISubrange::get(Context, VlaExpr, 0));
+}
+
typedef MetadataTest DIEnumeratorTest;
TEST_F(DIEnumeratorTest, get) {
- auto *N = DIEnumerator::get(Context, 7, "name");
+ auto *N = DIEnumerator::get(Context, 7, false, "name");
EXPECT_EQ(dwarf::DW_TAG_enumerator, N->getTag());
EXPECT_EQ(7, N->getValue());
+ EXPECT_FALSE(N->isUnsigned());
EXPECT_EQ("name", N->getName());
- EXPECT_EQ(N, DIEnumerator::get(Context, 7, "name"));
+ EXPECT_EQ(N, DIEnumerator::get(Context, 7, false, "name"));
- EXPECT_NE(N, DIEnumerator::get(Context, 8, "name"));
- EXPECT_NE(N, DIEnumerator::get(Context, 7, "nam"));
+ EXPECT_NE(N, DIEnumerator::get(Context, 7, true, "name"));
+ EXPECT_NE(N, DIEnumerator::get(Context, 8, false, "name"));
+ EXPECT_NE(N, DIEnumerator::get(Context, 7, false, "nam"));
TempDIEnumerator Temp = N->clone();
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
@@ -1024,7 +1051,7 @@ TEST_F(DITypeTest, clone) {
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
}
-TEST_F(DITypeTest, setFlags) {
+TEST_F(DITypeTest, cloneWithFlags) {
// void (void)
Metadata *TypesOps[] = {nullptr};
Metadata *Types = MDTuple::get(Context, TypesOps);
@@ -1032,17 +1059,15 @@ TEST_F(DITypeTest, setFlags) {
DIType *D =
DISubroutineType::getDistinct(Context, DINode::FlagZero, 0, Types);
EXPECT_EQ(DINode::FlagZero, D->getFlags());
- D->setFlags(DINode::FlagRValueReference);
- EXPECT_EQ(DINode::FlagRValueReference, D->getFlags());
- D->setFlags(DINode::FlagZero);
+ TempDIType D2 = D->cloneWithFlags(DINode::FlagRValueReference);
+ EXPECT_EQ(DINode::FlagRValueReference, D2->getFlags());
EXPECT_EQ(DINode::FlagZero, D->getFlags());
TempDIType T =
DISubroutineType::getTemporary(Context, DINode::FlagZero, 0, Types);
EXPECT_EQ(DINode::FlagZero, T->getFlags());
- T->setFlags(DINode::FlagRValueReference);
- EXPECT_EQ(DINode::FlagRValueReference, T->getFlags());
- T->setFlags(DINode::FlagZero);
+ TempDIType T2 = T->cloneWithFlags(DINode::FlagRValueReference);
+ EXPECT_EQ(DINode::FlagRValueReference, T2->getFlags());
EXPECT_EQ(DINode::FlagZero, T->getFlags());
}
@@ -1330,6 +1355,51 @@ TEST_F(DICompositeTypeTest, replaceOperands) {
EXPECT_EQ(nullptr, N->getTemplateParams().get());
}
+TEST_F(DICompositeTypeTest, variant_part) {
+ unsigned Tag = dwarf::DW_TAG_variant_part;
+ StringRef Name = "some name";
+ DIFile *File = getFile();
+ unsigned Line = 1;
+ DIScope *Scope = getSubprogram();
+ DIType *BaseType = getCompositeType();
+ uint64_t SizeInBits = 2;
+ uint32_t AlignInBits = 3;
+ uint64_t OffsetInBits = 4;
+ DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5);
+ unsigned RuntimeLang = 6;
+ StringRef Identifier = "some id";
+ DIDerivedType *Discriminator = cast<DIDerivedType>(getDerivedType());
+ DIDerivedType *Discriminator2 = cast<DIDerivedType>(getDerivedType());
+
+ EXPECT_NE(Discriminator, Discriminator2);
+
+ auto *N = DICompositeType::get(
+ Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,
+ OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,
+ Discriminator);
+
+ // Test the hashing.
+ auto *Same = DICompositeType::get(
+ Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,
+ OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,
+ Discriminator);
+ auto *Other = DICompositeType::get(
+ Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,
+ OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,
+ Discriminator2);
+ auto *NoDisc = DICompositeType::get(
+ Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,
+ OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,
+ nullptr);
+
+ EXPECT_EQ(N, Same);
+ EXPECT_NE(Same, Other);
+ EXPECT_NE(Same, NoDisc);
+ EXPECT_NE(Other, NoDisc);
+
+ EXPECT_EQ(N->getDiscriminator(), Discriminator);
+}
+
typedef MetadataTest DISubroutineTypeTest;
TEST_F(DISubroutineTypeTest, get) {
@@ -1375,21 +1445,27 @@ typedef MetadataTest DIFileTest;
TEST_F(DIFileTest, get) {
StringRef Filename = "file";
StringRef Directory = "dir";
- DIFile::ChecksumKind CSKind = DIFile::CSK_MD5;
- StringRef Checksum = "000102030405060708090a0b0c0d0e0f";
- auto *N = DIFile::get(Context, Filename, Directory, CSKind, Checksum);
+ DIFile::ChecksumKind CSKind = DIFile::ChecksumKind::CSK_MD5;
+ StringRef ChecksumString = "000102030405060708090a0b0c0d0e0f";
+ DIFile::ChecksumInfo<StringRef> Checksum(CSKind, ChecksumString);
+ StringRef Source = "source";
+ auto *N = DIFile::get(Context, Filename, Directory, Checksum, Source);
EXPECT_EQ(dwarf::DW_TAG_file_type, N->getTag());
EXPECT_EQ(Filename, N->getFilename());
EXPECT_EQ(Directory, N->getDirectory());
- EXPECT_EQ(CSKind, N->getChecksumKind());
EXPECT_EQ(Checksum, N->getChecksum());
- EXPECT_EQ(N, DIFile::get(Context, Filename, Directory, CSKind, Checksum));
+ EXPECT_EQ(Source, N->getSource());
+ EXPECT_EQ(N, DIFile::get(Context, Filename, Directory, Checksum, Source));
- EXPECT_NE(N, DIFile::get(Context, "other", Directory, CSKind, Checksum));
- EXPECT_NE(N, DIFile::get(Context, Filename, "other", CSKind, Checksum));
+ EXPECT_NE(N, DIFile::get(Context, "other", Directory, Checksum, Source));
+ EXPECT_NE(N, DIFile::get(Context, Filename, "other", Checksum, Source));
+ DIFile::ChecksumInfo<StringRef> OtherChecksum(DIFile::ChecksumKind::CSK_SHA1, ChecksumString);
EXPECT_NE(
- N, DIFile::get(Context, Filename, Directory, DIFile::CSK_SHA1, Checksum));
+ N, DIFile::get(Context, Filename, Directory, OtherChecksum));
+ StringRef OtherSource = "other";
+ EXPECT_NE(N, DIFile::get(Context, Filename, Directory, Checksum, OtherSource));
+ EXPECT_NE(N, DIFile::get(Context, Filename, Directory, Checksum));
EXPECT_NE(N, DIFile::get(Context, Filename, Directory));
TempDIFile Temp = N->clone();
@@ -1518,7 +1594,7 @@ TEST_F(DISubprogramTest, get) {
bool IsOptimized = false;
MDTuple *TemplateParams = getTuple();
DISubprogram *Declaration = getSubprogram();
- MDTuple *Variables = getTuple();
+ MDTuple *RetainedNodes = getTuple();
MDTuple *ThrownTypes = getTuple();
DICompileUnit *Unit = getUnit();
@@ -1526,7 +1602,7 @@ TEST_F(DISubprogramTest, get) {
Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit,
IsDefinition, ScopeLine, ContainingType, Virtuality, VirtualIndex,
ThisAdjustment, Flags, IsOptimized, Unit, TemplateParams, Declaration,
- Variables, ThrownTypes);
+ RetainedNodes, ThrownTypes);
EXPECT_EQ(dwarf::DW_TAG_subprogram, N->getTag());
EXPECT_EQ(Scope, N->getScope());
@@ -1547,99 +1623,99 @@ TEST_F(DISubprogramTest, get) {
EXPECT_EQ(Unit, N->getUnit());
EXPECT_EQ(TemplateParams, N->getTemplateParams().get());
EXPECT_EQ(Declaration, N->getDeclaration());
- EXPECT_EQ(Variables, N->getVariables().get());
+ EXPECT_EQ(RetainedNodes, N->getRetainedNodes().get());
EXPECT_EQ(ThrownTypes, N->getThrownTypes().get());
EXPECT_EQ(N, DISubprogram::get(
Context, Scope, Name, LinkageName, File, Line, Type,
IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
- Unit, TemplateParams, Declaration, Variables, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
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, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
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, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
EXPECT_NE(N, DISubprogram::get(
Context, Scope, Name, "other", File, Line, Type,
IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
- Unit, TemplateParams, Declaration, Variables, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
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, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
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, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
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, ThrownTypes));
+ Declaration, RetainedNodes, ThrownTypes));
EXPECT_NE(N, DISubprogram::get(
Context, Scope, Name, LinkageName, File, Line, Type,
!IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
- Unit, TemplateParams, Declaration, Variables, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
EXPECT_NE(N, DISubprogram::get(
Context, Scope, Name, LinkageName, File, Line, Type,
IsLocalToUnit, !IsDefinition, ScopeLine, ContainingType,
Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
- Unit, TemplateParams, Declaration, Variables, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
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, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
EXPECT_NE(N, DISubprogram::get(
Context, Scope, Name, LinkageName, File, Line, Type,
IsLocalToUnit, IsDefinition, ScopeLine, getCompositeType(),
Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
- Unit, TemplateParams, Declaration, Variables, ThrownTypes));
+ Unit, TemplateParams, Declaration, RetainedNodes, ThrownTypes));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality + 1, VirtualIndex,
ThisAdjustment, Flags, IsOptimized, Unit,
- TemplateParams, Declaration, Variables,
+ TemplateParams, Declaration, RetainedNodes,
ThrownTypes));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex + 1,
ThisAdjustment, Flags, IsOptimized, Unit,
- TemplateParams, Declaration, Variables,
+ TemplateParams, Declaration, RetainedNodes,
ThrownTypes));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
ThisAdjustment, Flags, !IsOptimized, Unit,
- TemplateParams, Declaration, Variables,
+ TemplateParams, Declaration, RetainedNodes,
ThrownTypes));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
ContainingType, Virtuality, VirtualIndex,
ThisAdjustment, Flags, IsOptimized, nullptr,
- TemplateParams, Declaration, Variables,
+ TemplateParams, Declaration, RetainedNodes,
ThrownTypes));
EXPECT_NE(N, DISubprogram::get(
Context, Scope, Name, LinkageName, File, Line, Type,
IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
- Unit, getTuple(), Declaration, Variables, ThrownTypes));
+ Unit, getTuple(), Declaration, RetainedNodes, ThrownTypes));
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,
+ TemplateParams, getSubprogram(), RetainedNodes,
ThrownTypes));
EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,
Type, IsLocalToUnit, IsDefinition, ScopeLine,
@@ -1650,7 +1726,7 @@ TEST_F(DISubprogramTest, get) {
Context, Scope, Name, LinkageName, File, Line, Type,
IsLocalToUnit, IsDefinition, ScopeLine, ContainingType,
Virtuality, VirtualIndex, ThisAdjustment, Flags, IsOptimized,
- Unit, TemplateParams, Declaration, Variables, getTuple()));
+ Unit, TemplateParams, Declaration, RetainedNodes, getTuple()));
TempDISubprogram Temp = N->clone();
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
@@ -2035,14 +2111,20 @@ TEST_F(DIExpressionTest, get) {
// Test DIExpression::prepend().
uint64_t Elts0[] = {dwarf::DW_OP_LLVM_fragment, 0, 32};
auto *N0 = DIExpression::get(Context, Elts0);
- N0 = DIExpression::prepend(N0, true, 64, true, true);
+ auto *N0WithPrependedOps = DIExpression::prepend(N0, true, 64, true, true);
uint64_t Elts1[] = {dwarf::DW_OP_deref,
dwarf::DW_OP_plus_uconst, 64,
dwarf::DW_OP_deref,
dwarf::DW_OP_stack_value,
dwarf::DW_OP_LLVM_fragment, 0, 32};
auto *N1 = DIExpression::get(Context, Elts1);
- EXPECT_EQ(N0, N1);
+ EXPECT_EQ(N0WithPrependedOps, N1);
+
+ // Test DIExpression::append().
+ uint64_t Elts2[] = {dwarf::DW_OP_deref, dwarf::DW_OP_plus_uconst, 64,
+ dwarf::DW_OP_deref, dwarf::DW_OP_stack_value};
+ auto *N2 = DIExpression::append(N0, Elts2);
+ EXPECT_EQ(N0WithPrependedOps, N2);
}
TEST_F(DIExpressionTest, isValid) {
@@ -2436,9 +2518,20 @@ TEST_F(FunctionAttachmentTest, Verifier) {
TEST_F(FunctionAttachmentTest, EntryCount) {
Function *F = getFunction("foo");
EXPECT_FALSE(F->getEntryCount().hasValue());
- F->setEntryCount(12304);
- EXPECT_TRUE(F->getEntryCount().hasValue());
- EXPECT_EQ(12304u, *F->getEntryCount());
+ F->setEntryCount(12304, Function::PCT_Real);
+ auto Count = F->getEntryCount();
+ EXPECT_TRUE(Count.hasValue());
+ EXPECT_EQ(12304u, Count.getCount());
+ EXPECT_EQ(Function::PCT_Real, Count.getType());
+
+ // Repeat the same for synthetic counts.
+ F = getFunction("bar");
+ EXPECT_FALSE(F->getEntryCount().hasValue());
+ F->setEntryCount(123, Function::PCT_Synthetic);
+ Count = F->getEntryCount();
+ EXPECT_TRUE(Count.hasValue());
+ EXPECT_EQ(123u, Count.getCount());
+ EXPECT_EQ(Function::PCT_Synthetic, Count.getType());
}
TEST_F(FunctionAttachmentTest, SubprogramAttachment) {
diff --git a/unittests/IR/PassBuilderCallbacksTest.cpp b/unittests/IR/PassBuilderCallbacksTest.cpp
index df0b11f6cc71..e46fc1781507 100644
--- a/unittests/IR/PassBuilderCallbacksTest.cpp
+++ b/unittests/IR/PassBuilderCallbacksTest.cpp
@@ -39,7 +39,7 @@ using testing::Invoke;
using testing::WithArgs;
using testing::_;
-/// \brief A CRTP base for analysis mock handles
+/// A CRTP base for analysis mock handles
///
/// This class reconciles mocking with the value semantics implementation of the
/// AnalysisManager. Analysis mock handles should derive from this class and
@@ -110,7 +110,7 @@ protected:
}
};
-/// \brief A CRTP base for pass mock handles
+/// A CRTP base for pass mock handles
///
/// This class reconciles mocking with the value semantics implementation of the
/// PassManager. Pass mock handles should derive from this class and
diff --git a/unittests/IR/PassManagerTest.cpp b/unittests/IR/PassManagerTest.cpp
index 0131bce3d2b2..7709453cb744 100644
--- a/unittests/IR/PassManagerTest.cpp
+++ b/unittests/IR/PassManagerTest.cpp
@@ -28,7 +28,7 @@ public:
TestFunctionAnalysis(int &Runs) : Runs(Runs) {}
- /// \brief Run the analysis pass over the function and return a result.
+ /// Run the analysis pass over the function and return a result.
Result run(Function &F, FunctionAnalysisManager &AM) {
++Runs;
int Count = 0;
diff --git a/unittests/IR/PatternMatch.cpp b/unittests/IR/PatternMatch.cpp
index 5c13ba6ecd90..6b5686d53c66 100644
--- a/unittests/IR/PatternMatch.cpp
+++ b/unittests/IR/PatternMatch.cpp
@@ -65,6 +65,56 @@ TEST_F(PatternMatchTest, OneUse) {
EXPECT_FALSE(m_OneUse(m_Value()).match(Leaf));
}
+TEST_F(PatternMatchTest, CommutativeDeferredValue) {
+ Value *X = IRB.getInt32(1);
+ Value *Y = IRB.getInt32(2);
+
+ {
+ Value *tX = X;
+ EXPECT_TRUE(match(X, m_Deferred(tX)));
+ EXPECT_FALSE(match(Y, m_Deferred(tX)));
+ }
+ {
+ const Value *tX = X;
+ EXPECT_TRUE(match(X, m_Deferred(tX)));
+ EXPECT_FALSE(match(Y, m_Deferred(tX)));
+ }
+ {
+ Value *const tX = X;
+ EXPECT_TRUE(match(X, m_Deferred(tX)));
+ EXPECT_FALSE(match(Y, m_Deferred(tX)));
+ }
+ {
+ const Value *const tX = X;
+ EXPECT_TRUE(match(X, m_Deferred(tX)));
+ EXPECT_FALSE(match(Y, m_Deferred(tX)));
+ }
+
+ {
+ Value *tX = nullptr;
+ EXPECT_TRUE(match(IRB.CreateAnd(X, X), m_And(m_Value(tX), m_Deferred(tX))));
+ EXPECT_EQ(tX, X);
+ }
+ {
+ Value *tX = nullptr;
+ EXPECT_FALSE(
+ match(IRB.CreateAnd(X, Y), m_c_And(m_Value(tX), m_Deferred(tX))));
+ }
+
+ auto checkMatch = [X, Y](Value *Pattern) {
+ Value *tX = nullptr, *tY = nullptr;
+ EXPECT_TRUE(match(
+ Pattern, m_c_And(m_Value(tX), m_c_And(m_Deferred(tX), m_Value(tY)))));
+ EXPECT_EQ(tX, X);
+ EXPECT_EQ(tY, Y);
+ };
+
+ checkMatch(IRB.CreateAnd(X, IRB.CreateAnd(X, Y)));
+ checkMatch(IRB.CreateAnd(X, IRB.CreateAnd(Y, X)));
+ checkMatch(IRB.CreateAnd(IRB.CreateAnd(X, Y), X));
+ checkMatch(IRB.CreateAnd(IRB.CreateAnd(Y, X), X));
+}
+
TEST_F(PatternMatchTest, FloatingPointOrderedMin) {
Type *FltTy = IRB.getFloatTy();
Value *L = ConstantFP::get(FltTy, 1.0);
@@ -340,6 +390,150 @@ TEST_F(PatternMatchTest, OverflowingBinOps) {
EXPECT_FALSE(m_NUWShl(m_Value(), m_Value()).match(IRB.CreateNUWAdd(L, R)));
}
+TEST_F(PatternMatchTest, LoadStoreOps) {
+ // Create this load/store sequence:
+ //
+ // %p = alloca i32*
+ // %0 = load i32*, i32** %p
+ // store i32 42, i32* %0
+
+ Value *Alloca = IRB.CreateAlloca(IRB.getInt32Ty());
+ Value *LoadInst = IRB.CreateLoad(Alloca);
+ Value *FourtyTwo = IRB.getInt32(42);
+ Value *StoreInst = IRB.CreateStore(FourtyTwo, Alloca);
+ Value *MatchLoad, *MatchStoreVal, *MatchStorePointer;
+
+ EXPECT_TRUE(m_Load(m_Value(MatchLoad)).match(LoadInst));
+ EXPECT_EQ(Alloca, MatchLoad);
+
+ EXPECT_TRUE(m_Load(m_Specific(Alloca)).match(LoadInst));
+
+ EXPECT_FALSE(m_Load(m_Value(MatchLoad)).match(Alloca));
+
+ EXPECT_TRUE(m_Store(m_Value(MatchStoreVal), m_Value(MatchStorePointer))
+ .match(StoreInst));
+ EXPECT_EQ(FourtyTwo, MatchStoreVal);
+ EXPECT_EQ(Alloca, MatchStorePointer);
+
+ EXPECT_FALSE(m_Store(m_Value(MatchStoreVal), m_Value(MatchStorePointer))
+ .match(Alloca));
+
+ EXPECT_TRUE(m_Store(m_SpecificInt(42), m_Specific(Alloca))
+ .match(StoreInst));
+ EXPECT_FALSE(m_Store(m_SpecificInt(42), m_Specific(FourtyTwo))
+ .match(StoreInst));
+ EXPECT_FALSE(m_Store(m_SpecificInt(43), m_Specific(Alloca))
+ .match(StoreInst));
+}
+
+TEST_F(PatternMatchTest, VectorOps) {
+ // Build up small tree of vector operations
+ //
+ // Val = 0 + 1
+ // Val2 = Val + 3
+ // VI1 = insertelement <2 x i8> undef, i8 1, i32 0 = <1, undef>
+ // VI2 = insertelement <2 x i8> %VI1, i8 %Val2, i8 %Val = <1, 4>
+ // VI3 = insertelement <2 x i8> %VI1, i8 %Val2, i32 1 = <1, 4>
+ // VI4 = insertelement <2 x i8> %VI1, i8 2, i8 %Val = <1, 2>
+ //
+ // SI1 = shufflevector <2 x i8> %VI1, <2 x i8> undef, zeroinitializer
+ // SI2 = shufflevector <2 x i8> %VI3, <2 x i8> %VI4, <2 x i8> <i8 0, i8 2>
+ // SI3 = shufflevector <2 x i8> %VI3, <2 x i8> undef, zeroinitializer
+ // SI4 = shufflevector <2 x i8> %VI4, <2 x i8> undef, zeroinitializer
+ //
+ // SP1 = VectorSplat(2, i8 2)
+ // SP2 = VectorSplat(2, i8 %Val)
+ Type *VecTy = VectorType::get(IRB.getInt8Ty(), 2);
+ Type *i32 = IRB.getInt32Ty();
+ Type *i32VecTy = VectorType::get(i32, 2);
+
+ Value *Val = IRB.CreateAdd(IRB.getInt8(0), IRB.getInt8(1));
+ Value *Val2 = IRB.CreateAdd(Val, IRB.getInt8(3));
+
+ SmallVector<Constant *, 2> VecElemIdxs;
+ VecElemIdxs.push_back(ConstantInt::get(i32, 0));
+ VecElemIdxs.push_back(ConstantInt::get(i32, 2));
+ auto *IdxVec = ConstantVector::get(VecElemIdxs);
+
+ Value *UndefVec = UndefValue::get(VecTy);
+ Value *VI1 = IRB.CreateInsertElement(UndefVec, IRB.getInt8(1), (uint64_t)0);
+ Value *VI2 = IRB.CreateInsertElement(VI1, Val2, Val);
+ Value *VI3 = IRB.CreateInsertElement(VI1, Val2, (uint64_t)1);
+ Value *VI4 = IRB.CreateInsertElement(VI1, IRB.getInt8(2), Val);
+
+ Value *EX1 = IRB.CreateExtractElement(VI4, Val);
+ Value *EX2 = IRB.CreateExtractElement(VI4, (uint64_t)0);
+ Value *EX3 = IRB.CreateExtractElement(IdxVec, (uint64_t)1);
+
+ Value *Zero = ConstantAggregateZero::get(i32VecTy);
+ Value *SI1 = IRB.CreateShuffleVector(VI1, UndefVec, Zero);
+ Value *SI2 = IRB.CreateShuffleVector(VI3, VI4, IdxVec);
+ Value *SI3 = IRB.CreateShuffleVector(VI3, UndefVec, Zero);
+ Value *SI4 = IRB.CreateShuffleVector(VI4, UndefVec, Zero);
+
+ Value *SP1 = IRB.CreateVectorSplat(2, IRB.getInt8(2));
+ Value *SP2 = IRB.CreateVectorSplat(2, Val);
+
+ Value *A = nullptr, *B = nullptr, *C = nullptr;
+
+ // Test matching insertelement
+ EXPECT_TRUE(match(VI1, m_InsertElement(m_Value(), m_Value(), m_Value())));
+ EXPECT_TRUE(
+ match(VI1, m_InsertElement(m_Undef(), m_ConstantInt(), m_ConstantInt())));
+ EXPECT_TRUE(
+ match(VI1, m_InsertElement(m_Undef(), m_ConstantInt(), m_Zero())));
+ EXPECT_TRUE(
+ match(VI1, m_InsertElement(m_Undef(), m_SpecificInt(1), m_Zero())));
+ EXPECT_TRUE(match(VI2, m_InsertElement(m_Value(), m_Value(), m_Value())));
+ EXPECT_FALSE(
+ match(VI2, m_InsertElement(m_Value(), m_Value(), m_ConstantInt())));
+ EXPECT_FALSE(
+ match(VI2, m_InsertElement(m_Value(), m_ConstantInt(), m_Value())));
+ EXPECT_FALSE(match(VI2, m_InsertElement(m_Constant(), m_Value(), m_Value())));
+ EXPECT_TRUE(match(VI3, m_InsertElement(m_Value(A), m_Value(B), m_Value(C))));
+ EXPECT_TRUE(A == VI1);
+ EXPECT_TRUE(B == Val2);
+ EXPECT_TRUE(isa<ConstantInt>(C));
+ A = B = C = nullptr; // reset
+
+ // Test matching extractelement
+ EXPECT_TRUE(match(EX1, m_ExtractElement(m_Value(A), m_Value(B))));
+ EXPECT_TRUE(A == VI4);
+ EXPECT_TRUE(B == Val);
+ A = B = C = nullptr; // reset
+ EXPECT_FALSE(match(EX1, m_ExtractElement(m_Value(), m_ConstantInt())));
+ EXPECT_TRUE(match(EX2, m_ExtractElement(m_Value(), m_ConstantInt())));
+ EXPECT_TRUE(match(EX3, m_ExtractElement(m_Constant(), m_ConstantInt())));
+
+ // Test matching shufflevector
+ EXPECT_TRUE(match(SI1, m_ShuffleVector(m_Value(), m_Undef(), m_Zero())));
+ EXPECT_TRUE(match(SI2, m_ShuffleVector(m_Value(A), m_Value(B), m_Value(C))));
+ EXPECT_TRUE(A == VI3);
+ EXPECT_TRUE(B == VI4);
+ EXPECT_TRUE(C == IdxVec);
+ A = B = C = nullptr; // reset
+
+ // Test matching the vector splat pattern
+ EXPECT_TRUE(match(
+ SI1,
+ m_ShuffleVector(m_InsertElement(m_Undef(), m_SpecificInt(1), m_Zero()),
+ m_Undef(), m_Zero())));
+ EXPECT_FALSE(match(
+ SI3, m_ShuffleVector(m_InsertElement(m_Undef(), m_Value(), m_Zero()),
+ m_Undef(), m_Zero())));
+ EXPECT_FALSE(match(
+ SI4, m_ShuffleVector(m_InsertElement(m_Undef(), m_Value(), m_Zero()),
+ m_Undef(), m_Zero())));
+ EXPECT_TRUE(match(
+ SP1,
+ m_ShuffleVector(m_InsertElement(m_Undef(), m_SpecificInt(2), m_Zero()),
+ m_Undef(), m_Zero())));
+ EXPECT_TRUE(match(
+ SP2, m_ShuffleVector(m_InsertElement(m_Undef(), m_Value(A), m_Zero()),
+ m_Undef(), m_Zero())));
+ EXPECT_TRUE(A == Val);
+}
+
template <typename T> struct MutableConstTest : PatternMatchTest { };
typedef ::testing::Types<std::tuple<Value*, Instruction*>,
diff --git a/unittests/IR/ValueMapTest.cpp b/unittests/IR/ValueMapTest.cpp
index 28633b44b11d..cbcd6f6e53f4 100644
--- a/unittests/IR/ValueMapTest.cpp
+++ b/unittests/IR/ValueMapTest.cpp
@@ -195,7 +195,7 @@ struct LockMutex : ValueMapConfig<KeyT, MutexT> {
static MutexT *getMutex(const ExtraData &Data) { return Data.M; }
};
// FIXME: These tests started failing on Windows.
-#if LLVM_ENABLE_THREADS && !defined(LLVM_ON_WIN32)
+#if LLVM_ENABLE_THREADS && !defined(_WIN32)
TYPED_TEST(ValueMapTest, LocksMutex) {
sys::Mutex M(false); // Not recursive.
bool CalledRAUW = false, CalledDeleted = false;
diff --git a/unittests/IR/ValueTest.cpp b/unittests/IR/ValueTest.cpp
index 0087cb2fa82c..90466b2505ec 100644
--- a/unittests/IR/ValueTest.cpp
+++ b/unittests/IR/ValueTest.cpp
@@ -112,7 +112,13 @@ TEST(ValueTest, printSlots) {
// without a slot tracker.
LLVMContext C;
- const char *ModuleString = "define void @f(i32 %x, i32 %y) {\n"
+ const char *ModuleString = "@g0 = external global %500\n"
+ "@g1 = external global %900\n"
+ "\n"
+ "%900 = type { i32, i32 }\n"
+ "%500 = type { i32 }\n"
+ "\n"
+ "define void @f(i32 %x, i32 %y) {\n"
"entry:\n"
" %0 = add i32 %y, 1\n"
" %1 = add i32 %y, 1\n"
@@ -132,6 +138,11 @@ TEST(ValueTest, printSlots) {
Instruction *I1 = &*++BB.begin();
ASSERT_TRUE(I1);
+ GlobalVariable *G0 = M->getGlobalVariable("g0");
+ ASSERT_TRUE(G0);
+ GlobalVariable *G1 = M->getGlobalVariable("g1");
+ ASSERT_TRUE(G1);
+
ModuleSlotTracker MST(M.get());
#define CHECK_PRINT(INST, STR) \
@@ -172,6 +183,8 @@ TEST(ValueTest, printSlots) {
CHECK_PRINT_AS_OPERAND(I1, false, "%1");
CHECK_PRINT_AS_OPERAND(I0, true, "i32 %0");
CHECK_PRINT_AS_OPERAND(I1, true, "i32 %1");
+ CHECK_PRINT_AS_OPERAND(G0, true, "%0* @g0");
+ CHECK_PRINT_AS_OPERAND(G1, true, "%1* @g1");
#undef CHECK_PRINT_AS_OPERAND
}
diff --git a/unittests/Linker/CMakeLists.txt b/unittests/Linker/CMakeLists.txt
index 05f45c0a8ce8..e94f4be8a2e9 100644
--- a/unittests/Linker/CMakeLists.txt
+++ b/unittests/Linker/CMakeLists.txt
@@ -4,10 +4,6 @@ set(LLVM_LINK_COMPONENTS
linker
)
-set(LinkerSources
- LinkModulesTest.cpp
- )
-
add_llvm_unittest(LinkerTests
- ${LinkerSources}
+ LinkModulesTest.cpp
)
diff --git a/unittests/MC/Disassembler.cpp b/unittests/MC/Disassembler.cpp
index dd0f1ef9ace7..0a6871f6f857 100644
--- a/unittests/MC/Disassembler.cpp
+++ b/unittests/MC/Disassembler.cpp
@@ -21,7 +21,7 @@ static const char *symbolLookupCallback(void *DisInfo, uint64_t ReferenceValue,
return nullptr;
}
-TEST(Disassembler, Test1) {
+TEST(Disassembler, X86Test) {
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
@@ -62,3 +62,46 @@ TEST(Disassembler, Test1) {
LLVMDisasmDispose(DCR);
}
+
+TEST(Disassembler, WebAssemblyTest) {
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllDisassemblers();
+
+ uint8_t Bytes[] = {0x6a, 0x42, 0x7F, 0x35, 0x01, 0x10};
+ uint8_t *BytesP = Bytes;
+ const char OutStringSize = 100;
+ char OutString[OutStringSize];
+ LLVMDisasmContextRef DCR = LLVMCreateDisasm("wasm32-unknown-unknown", nullptr,
+ 0, nullptr, symbolLookupCallback);
+ if (!DCR)
+ return;
+
+ size_t InstSize;
+ unsigned NumBytes = sizeof(Bytes);
+ unsigned PC = 0;
+
+ InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,
+ OutStringSize);
+ EXPECT_EQ(InstSize, 1U);
+ EXPECT_EQ(StringRef(OutString), "\ti32.add ");
+ PC += InstSize;
+ BytesP += InstSize;
+ NumBytes -= InstSize;
+
+ InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,
+ OutStringSize);
+ EXPECT_EQ(InstSize, 2U);
+ EXPECT_EQ(StringRef(OutString), "\ti64.const\t-1");
+
+ PC += InstSize;
+ BytesP += InstSize;
+ NumBytes -= InstSize;
+
+ InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,
+ OutStringSize);
+ EXPECT_EQ(InstSize, 3U);
+ EXPECT_EQ(StringRef(OutString), "\ti64.load32_u\t16, :p2align=1");
+
+ LLVMDisasmDispose(DCR);
+}
diff --git a/unittests/MI/LiveIntervalTest.cpp b/unittests/MI/LiveIntervalTest.cpp
index 93d41d5daf13..a39fd7f73cf4 100644
--- a/unittests/MI/LiveIntervalTest.cpp
+++ b/unittests/MI/LiveIntervalTest.cpp
@@ -313,7 +313,7 @@ TEST(LiveIntervalTest, MoveUpValNos) {
liveIntervalTest(R"MIR(
successors: %bb.1, %bb.2
%0 = IMPLICIT_DEF
- S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc
+ S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.2:
S_NOP 0, implicit %0
@@ -343,10 +343,10 @@ TEST(LiveIntervalTest, MoveOverUndefUse0) {
TEST(LiveIntervalTest, MoveOverUndefUse1) {
// findLastUseBefore() used by handleMoveUp() must ignore undef operands.
liveIntervalTest(R"MIR(
- %sgpr0 = IMPLICIT_DEF
+ $sgpr0 = IMPLICIT_DEF
S_NOP 0
- S_NOP 0, implicit undef %sgpr0
- %sgpr0 = IMPLICIT_DEF implicit %sgpr0(tied-def 0)
+ S_NOP 0, implicit undef $sgpr0
+ $sgpr0 = IMPLICIT_DEF implicit $sgpr0(tied-def 0)
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 3, 1);
});
@@ -358,7 +358,7 @@ TEST(LiveIntervalTest, SubRegMoveDown) {
liveIntervalTest(R"MIR(
successors: %bb.1, %bb.2
%0 = IMPLICIT_DEF
- S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc
+ S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.2:
successors: %bb.1
@@ -384,7 +384,7 @@ TEST(LiveIntervalTest, SubRegMoveUp) {
successors: %bb.1, %bb.2
undef %0.sub0 = IMPLICIT_DEF
%0.sub1 = IMPLICIT_DEF
- S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc
+ S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.1:
S_NOP 0, implicit %0.sub1
@@ -395,6 +395,31 @@ TEST(LiveIntervalTest, SubRegMoveUp) {
});
}
+TEST(LiveIntervalTest, DeadSubRegMoveUp) {
+ // handleMoveUp had a bug where moving a dead subreg def into the middle of
+ // an earlier segment resulted in an invalid live range.
+ liveIntervalTest(R"MIR(
+ undef %125.sub0:vreg_128 = V_MOV_B32_e32 0, implicit $exec
+ %125.sub1:vreg_128 = COPY %125.sub0
+ %125.sub2:vreg_128 = COPY %125.sub0
+ undef %51.sub0:vreg_128 = V_MOV_B32_e32 898625526, implicit $exec
+ %51.sub1:vreg_128 = COPY %51.sub0
+ %51.sub2:vreg_128 = COPY %51.sub0
+ %52:vgpr_32 = V_MOV_B32_e32 986714345, implicit $exec
+ %54:vgpr_32 = V_MOV_B32_e32 1742342378, implicit $exec
+ %57:vgpr_32 = V_MOV_B32_e32 3168768712, implicit $exec
+ %59:vgpr_32 = V_MOV_B32_e32 1039972644, implicit $exec
+ %60:vgpr_32 = V_MAD_F32 0, %52, 0, undef %61:vgpr_32, 0, %59, 0, 0, implicit $exec
+ %63:vgpr_32 = V_ADD_F32_e32 %51.sub3, undef %64:vgpr_32, implicit $exec
+ dead %66:vgpr_32 = V_MAD_F32 0, %60, 0, undef %67:vgpr_32, 0, %125.sub2, 0, 0, implicit $exec
+ undef %124.sub1:vreg_128 = V_MAD_F32 0, %57, 0, undef %70:vgpr_32, 0, %125.sub1, 0, 0, implicit $exec
+ %124.sub0:vreg_128 = V_MAD_F32 0, %54, 0, undef %73:vgpr_32, 0, %125.sub0, 0, 0, implicit $exec
+ dead undef %125.sub3:vreg_128 = V_MAC_F32_e32 %63, undef %76:vgpr_32, %125.sub3, implicit $exec
+)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
+ testHandleMove(MF, LIS, 15, 12);
+ });
+}
+
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
initLLVM();
diff --git a/unittests/Option/OptionParsingTest.cpp b/unittests/Option/OptionParsingTest.cpp
index 6ac6283327bb..eef21ab51209 100644
--- a/unittests/Option/OptionParsingTest.cpp
+++ b/unittests/Option/OptionParsingTest.cpp
@@ -266,3 +266,48 @@ TEST(Option, FlagAliasToJoined) {
EXPECT_EQ(1U, AL.getAllArgValues(OPT_B).size());
EXPECT_EQ("", AL.getAllArgValues(OPT_B)[0]);
}
+
+TEST(Option, FindNearest) {
+ TestOptTable T;
+ std::string Nearest;
+
+ // Options that are too short should not be considered
+ // "near" other short options.
+ EXPECT_GT(T.findNearest("-A", Nearest), 4U);
+ EXPECT_GT(T.findNearest("/C", Nearest), 4U);
+ EXPECT_GT(T.findNearest("--C=foo", Nearest), 4U);
+
+ // The nearest candidate should mirror the amount of prefix
+ // characters used in the original string.
+ EXPECT_EQ(1U, T.findNearest("-blorb", Nearest));
+ EXPECT_EQ(Nearest, "-blorp");
+ EXPECT_EQ(1U, T.findNearest("--blorm", Nearest));
+ EXPECT_EQ(Nearest, "--blorp");
+ EXPECT_EQ(1U, T.findNearest("-fjormp", Nearest));
+ EXPECT_EQ(Nearest, "--fjormp");
+
+ // The nearest candidate respects the prefix and value delimiter
+ // of the original string.
+ EXPECT_EQ(1U, T.findNearest("/framb:foo", Nearest));
+ EXPECT_EQ(Nearest, "/cramb:foo");
+
+ // Flags should be included and excluded as specified.
+ EXPECT_EQ(1U, T.findNearest("-doopf", Nearest, /*FlagsToInclude=*/OptFlag2));
+ EXPECT_EQ(Nearest, "-doopf2");
+ EXPECT_EQ(1U, T.findNearest("-doopf", Nearest,
+ /*FlagsToInclude=*/0,
+ /*FlagsToExclude=*/OptFlag2));
+ EXPECT_EQ(Nearest, "-doopf1");
+}
+
+TEST(DISABLED_Option, FindNearestFIXME) {
+ TestOptTable T;
+ std::string Nearest;
+
+ // FIXME: Options with joined values should not have those values considered
+ // when calculating distance. The test below would fail if run, but it should
+ // succeed.
+ EXPECT_EQ(1U, T.findNearest("--erbghFoo", Nearest));
+ EXPECT_EQ(Nearest, "--ermghFoo");
+
+}
diff --git a/unittests/Option/Opts.td b/unittests/Option/Opts.td
index 25c98c6f6015..c4544b5b3f9b 100644
--- a/unittests/Option/Opts.td
+++ b/unittests/Option/Opts.td
@@ -28,3 +28,11 @@ def K : Flag<["-"], "K">, Alias<B>;
def Slurp : Option<["-"], "slurp", KIND_REMAINING_ARGS>;
def SlurpJoined : Option<["-"], "slurpjoined", KIND_REMAINING_ARGS_JOINED>;
+
+def Blorp : Flag<["-", "--"], "blorp">, HelpText<"The blorp option">, Flags<[OptFlag1]>;
+def Cramb : Joined<["/"], "cramb:">, HelpText<"The cramb option">, MetaVarName<"CRAMB">, Flags<[OptFlag1]>;
+def Doopf1 : Flag<["-"], "doopf1">, HelpText<"The doopf1 option">, Flags<[OptFlag1]>;
+def Doopf2 : Flag<["-"], "doopf2">, HelpText<"The doopf2 option">, Flags<[OptFlag2]>;
+def Ermgh : Joined<["--"], "ermgh">, HelpText<"The ermgh option">, MetaVarName<"ERMGH">, Flags<[OptFlag1]>;
+def Fjormp : Flag<["--"], "fjormp">, HelpText<"The fjormp option">, Flags<[OptFlag1]>;
+def DashDash : Option<["--"], "", KIND_REMAINING_ARGS>;
diff --git a/unittests/Passes/CMakeLists.txt b/unittests/Passes/CMakeLists.txt
new file mode 100644
index 000000000000..d90df209d4ec
--- /dev/null
+++ b/unittests/Passes/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Needed by LLVM's CMake checks because this file defines multiple targets.
+set(LLVM_OPTIONAL_SOURCES PluginsTest.cpp TestPlugin.cpp)
+
+# If plugins are disabled, this test will disable itself at runtime. Otherwise,
+# reconfiguring with plugins disabled will leave behind a stale executable.
+if (LLVM_ENABLE_PLUGINS)
+ add_definitions(-DLLVM_ENABLE_PLUGINS)
+endif()
+
+set(LLVM_LINK_COMPONENTS Support Passes Core)
+add_llvm_unittest(PluginsTests
+ PluginsTest.cpp
+ )
+export_executable_symbols(PluginsTests)
+
+set(LLVM_LINK_COMPONENTS)
+add_llvm_loadable_module(TestPlugin
+ TestPlugin.cpp
+ )
+
+# Put plugin next to the unit test executable.
+set_output_directory(TestPlugin
+ BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
+ LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
+ )
+set_target_properties(TestPlugin PROPERTIES FOLDER "Tests")
+
+add_dependencies(TestPlugin intrinsics_gen)
+add_dependencies(PluginsTests TestPlugin)
diff --git a/unittests/Passes/PluginsTest.cpp b/unittests/Passes/PluginsTest.cpp
new file mode 100644
index 000000000000..726978714e87
--- /dev/null
+++ b/unittests/Passes/PluginsTest.cpp
@@ -0,0 +1,61 @@
+//===- unittests/Passes/Plugins/PluginsTest.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/Config/config.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Transforms/Scalar/LoopPassManager.h"
+#include "gtest/gtest.h"
+
+#include "TestPlugin.h"
+
+#include <cstdint>
+
+using namespace llvm;
+
+void anchor() {}
+
+static std::string LibPath(const std::string Name = "TestPlugin") {
+ const std::vector<testing::internal::string> &Argvs =
+ testing::internal::GetArgvs();
+ const char *Argv0 = Argvs.size() > 0 ? Argvs[0].c_str() : "PluginsTests";
+ void *Ptr = (void *)(intptr_t)anchor;
+ std::string Path = sys::fs::getMainExecutable(Argv0, Ptr);
+ llvm::SmallString<256> Buf{sys::path::parent_path(Path)};
+ sys::path::append(Buf, (Name + LTDL_SHLIB_EXT).c_str());
+ return Buf.str();
+}
+
+TEST(PluginsTests, LoadPlugin) {
+#if !defined(LLVM_ENABLE_PLUGINS)
+ // Disable the test if plugins are disabled.
+ return;
+#endif
+
+ auto PluginPath = LibPath();
+ ASSERT_NE("", PluginPath);
+
+ Expected<PassPlugin> Plugin = PassPlugin::Load(PluginPath);
+ ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath;
+
+ ASSERT_EQ(TEST_PLUGIN_NAME, Plugin->getPluginName());
+ ASSERT_EQ(TEST_PLUGIN_VERSION, Plugin->getPluginVersion());
+
+ PassBuilder PB;
+ ModulePassManager PM;
+ ASSERT_FALSE(PB.parsePassPipeline(PM, "plugin-pass"));
+
+ Plugin->registerPassBuilderCallbacks(PB);
+ ASSERT_TRUE(PB.parsePassPipeline(PM, "plugin-pass"));
+}
diff --git a/unittests/Passes/TestPlugin.cpp b/unittests/Passes/TestPlugin.cpp
new file mode 100644
index 000000000000..3dc5cdc84768
--- /dev/null
+++ b/unittests/Passes/TestPlugin.cpp
@@ -0,0 +1,39 @@
+//===- unittests/Passes/Plugins/Plugin.cpp --------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+
+#include "TestPlugin.h"
+
+using namespace llvm;
+
+struct TestModulePass : public PassInfoMixin<TestModulePass> {
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
+ return PreservedAnalyses::all();
+ }
+};
+
+void registerCallbacks(PassBuilder &PB) {
+ PB.registerPipelineParsingCallback(
+ [](StringRef Name, ModulePassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
+ if (Name == "plugin-pass") {
+ PM.addPass(TestModulePass());
+ return true;
+ }
+ return false;
+ });
+}
+
+extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
+llvmGetPassPluginInfo() {
+ return {LLVM_PLUGIN_API_VERSION, TEST_PLUGIN_NAME, TEST_PLUGIN_VERSION,
+ registerCallbacks};
+}
diff --git a/unittests/Passes/TestPlugin.h b/unittests/Passes/TestPlugin.h
new file mode 100644
index 000000000000..801a89065cda
--- /dev/null
+++ b/unittests/Passes/TestPlugin.h
@@ -0,0 +1,2 @@
+#define TEST_PLUGIN_NAME "TestPlugin"
+#define TEST_PLUGIN_VERSION "0.1-unit"
diff --git a/unittests/ProfileData/CoverageMappingTest.cpp b/unittests/ProfileData/CoverageMappingTest.cpp
index 4d0f852da982..987f67699585 100644
--- a/unittests/ProfileData/CoverageMappingTest.cpp
+++ b/unittests/ProfileData/CoverageMappingTest.cpp
@@ -859,17 +859,34 @@ TEST_P(CoverageMappingTest, load_coverage_for_expanded_file) {
TEST_P(CoverageMappingTest, skip_duplicate_function_record) {
ProfileWriter.addRecord({"func", 0x1234, {1}}, Err);
+ // This record should be loaded.
startFunction("func", 0x1234);
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
+ // This record should be loaded.
startFunction("func", 0x1234);
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
+ addCMR(Counter::getCounter(0), "file2", 1, 1, 9, 9);
+
+ // This record should be skipped.
+ startFunction("func", 0x1234);
+ addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
+
+ // This record should be loaded.
+ startFunction("func", 0x1234);
+ addCMR(Counter::getCounter(0), "file2", 1, 1, 9, 9);
+ addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
+
+ // This record should be skipped.
+ startFunction("func", 0x1234);
+ addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
+ addCMR(Counter::getCounter(0), "file2", 1, 1, 9, 9);
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
auto Funcs = LoadedCoverage->getCoveredFunctions();
unsigned NumFuncs = std::distance(Funcs.begin(), Funcs.end());
- ASSERT_EQ(1U, NumFuncs);
+ ASSERT_EQ(3U, NumFuncs);
}
// FIXME: Use ::testing::Combine() when llvm updates its copy of googletest.
diff --git a/unittests/ProfileData/InstrProfTest.cpp b/unittests/ProfileData/InstrProfTest.cpp
index 79f880e475c6..0c99f7fde654 100644
--- a/unittests/ProfileData/InstrProfTest.cpp
+++ b/unittests/ProfileData/InstrProfTest.cpp
@@ -712,7 +712,7 @@ TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write) {
};
std::unique_ptr<InstrProfValueData[]> VD_0(
Record.getValueForSite(IPVK_IndirectCallTarget, 0));
- std::sort(&VD_0[0], &VD_0[5], Cmp);
+ llvm::sort(&VD_0[0], &VD_0[5], Cmp);
ASSERT_EQ(StringRef((const char *)VD_0[0].Value, 7), StringRef("callee2"));
ASSERT_EQ(1000U, VD_0[0].Count);
ASSERT_EQ(StringRef((const char *)VD_0[1].Value, 7), StringRef("callee3"));
@@ -726,7 +726,7 @@ TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write) {
std::unique_ptr<InstrProfValueData[]> VD_1(
Record.getValueForSite(IPVK_IndirectCallTarget, 1));
- std::sort(&VD_1[0], &VD_1[4], Cmp);
+ llvm::sort(&VD_1[0], &VD_1[4], Cmp);
ASSERT_EQ(StringRef((const char *)VD_1[0].Value, 7), StringRef("callee2"));
ASSERT_EQ(2500U, VD_1[0].Count);
ASSERT_EQ(StringRef((const char *)VD_1[1].Value, 7), StringRef("callee1"));
@@ -738,7 +738,7 @@ TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write) {
std::unique_ptr<InstrProfValueData[]> VD_2(
Record.getValueForSite(IPVK_IndirectCallTarget, 2));
- std::sort(&VD_2[0], &VD_2[3], Cmp);
+ llvm::sort(&VD_2[0], &VD_2[3], Cmp);
ASSERT_EQ(StringRef((const char *)VD_2[0].Value, 7), StringRef("callee4"));
ASSERT_EQ(5500U, VD_2[0].Count);
ASSERT_EQ(StringRef((const char *)VD_2[1].Value, 7), StringRef("callee3"));
@@ -748,7 +748,7 @@ TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write) {
std::unique_ptr<InstrProfValueData[]> VD_3(
Record.getValueForSite(IPVK_IndirectCallTarget, 3));
- std::sort(&VD_3[0], &VD_3[2], Cmp);
+ llvm::sort(&VD_3[0], &VD_3[2], Cmp);
ASSERT_EQ(StringRef((const char *)VD_3[0].Value, 7), StringRef("callee3"));
ASSERT_EQ(2000U, VD_3[0].Count);
ASSERT_EQ(StringRef((const char *)VD_3[1].Value, 7), StringRef("callee2"));
@@ -769,9 +769,8 @@ TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write_mapping) {
Symtab.mapAddress(uint64_t(callee3), 0x3000ULL);
Symtab.mapAddress(uint64_t(callee4), 0x4000ULL);
// Missing mapping for callee5
- Symtab.finalizeSymtab();
- VPData->deserializeTo(Record, &Symtab.getAddrHashMap());
+ VPData->deserializeTo(Record, &Symtab);
// Now read data from Record and sanity check the data
ASSERT_EQ(5U, Record.getNumValueSites(IPVK_IndirectCallTarget));
@@ -782,7 +781,7 @@ TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write_mapping) {
};
std::unique_ptr<InstrProfValueData[]> VD_0(
Record.getValueForSite(IPVK_IndirectCallTarget, 0));
- std::sort(&VD_0[0], &VD_0[5], Cmp);
+ llvm::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);
@@ -858,8 +857,6 @@ TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_test) {
EXPECT_THAT_ERROR(Symtab.addFuncName("blah_1"), Succeeded());
EXPECT_THAT_ERROR(Symtab.addFuncName("blah_2"), Succeeded());
EXPECT_THAT_ERROR(Symtab.addFuncName("blah_3"), Succeeded());
- // Finalize it
- Symtab.finalizeSymtab();
// Check again
R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("blah_1"));
diff --git a/unittests/ProfileData/SampleProfTest.cpp b/unittests/ProfileData/SampleProfTest.cpp
index 764bded2f030..3ebfd0e500fe 100644
--- a/unittests/ProfileData/SampleProfTest.cpp
+++ b/unittests/ProfileData/SampleProfTest.cpp
@@ -77,6 +77,11 @@ struct SampleProfTest : ::testing::Test {
BarSamples.addTotalSamples(20301);
BarSamples.addHeadSamples(1437);
BarSamples.addBodySamples(1, 0, 1437);
+ // Test how reader/writer handles unmangled names.
+ StringRef MconstructName("_M_construct<char *>");
+ StringRef StringviewName("string_view<std::allocator<char> >");
+ BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
+ BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
StringMap<FunctionSamples> Profiles;
Profiles[FooName] = std::move(FooSamples);
@@ -97,13 +102,29 @@ struct SampleProfTest : ::testing::Test {
StringMap<FunctionSamples> &ReadProfiles = Reader->getProfiles();
ASSERT_EQ(2u, ReadProfiles.size());
- FunctionSamples &ReadFooSamples = ReadProfiles[FooName];
+ std::string FooGUID;
+ StringRef FooRep = getRepInFormat(FooName, Format, FooGUID);
+ FunctionSamples &ReadFooSamples = ReadProfiles[FooRep];
ASSERT_EQ(7711u, ReadFooSamples.getTotalSamples());
ASSERT_EQ(610u, ReadFooSamples.getHeadSamples());
- FunctionSamples &ReadBarSamples = ReadProfiles[BarName];
+ std::string BarGUID;
+ StringRef BarRep = getRepInFormat(BarName, Format, BarGUID);
+ FunctionSamples &ReadBarSamples = ReadProfiles[BarRep];
ASSERT_EQ(20301u, ReadBarSamples.getTotalSamples());
ASSERT_EQ(1437u, ReadBarSamples.getHeadSamples());
+ ErrorOr<SampleRecord::CallTargetMap> CTMap =
+ ReadBarSamples.findCallTargetMapAt(1, 0);
+ ASSERT_FALSE(CTMap.getError());
+
+ std::string MconstructGUID;
+ StringRef MconstructRep =
+ getRepInFormat(MconstructName, Format, MconstructGUID);
+ std::string StringviewGUID;
+ StringRef StringviewRep =
+ getRepInFormat(StringviewName, Format, StringviewGUID);
+ ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
+ ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
auto VerifySummary = [](ProfileSummary &Summary) mutable {
ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());
@@ -158,10 +179,14 @@ TEST_F(SampleProfTest, roundtrip_text_profile) {
testRoundTrip(SampleProfileFormat::SPF_Text);
}
-TEST_F(SampleProfTest, roundtrip_binary_profile) {
+TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
testRoundTrip(SampleProfileFormat::SPF_Binary);
}
+TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
+ testRoundTrip(SampleProfileFormat::SPF_Compact_Binary);
+}
+
TEST_F(SampleProfTest, sample_overflow_saturation) {
const uint64_t Max = std::numeric_limits<uint64_t>::max();
sampleprof_error Result;
diff --git a/unittests/Support/AllocatorTest.cpp b/unittests/Support/AllocatorTest.cpp
index 4897c47eb28b..74b394f1b176 100644
--- a/unittests/Support/AllocatorTest.cpp
+++ b/unittests/Support/AllocatorTest.cpp
@@ -147,7 +147,7 @@ public:
// Allocate space for the alignment, the slab, and a void* that goes right
// before the slab.
size_t Alignment = 4096;
- void *MemBase = malloc(Size + Alignment - 1 + sizeof(void*));
+ void *MemBase = safe_malloc(Size + Alignment - 1 + sizeof(void*));
// Find the slab start.
void *Slab = (void *)alignAddr((char*)MemBase + sizeof(void *), Alignment);
diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt
index 299106e0dbf7..4e08d7df7ade 100644
--- a/unittests/Support/CMakeLists.txt
+++ b/unittests/Support/CMakeLists.txt
@@ -13,12 +13,15 @@ add_llvm_unittest(SupportTests
CachePruningTest.cpp
CrashRecoveryTest.cpp
Casting.cpp
+ CheckedArithmeticTest.cpp
Chrono.cpp
CommandLineTest.cpp
CompressionTest.cpp
ConvertUTFTest.cpp
DataExtractorTest.cpp
DebugTest.cpp
+ DebugCounterTest.cpp
+ DJBTest.cpp
EndianStreamTest.cpp
EndianTest.cpp
ErrnoTest.cpp
@@ -28,6 +31,7 @@ add_llvm_unittest(SupportTests
FormatVariadicTest.cpp
GlobPatternTest.cpp
Host.cpp
+ JSONTest.cpp
LEB128Test.cpp
LineIteratorTest.cpp
LockFileManagerTest.cpp
@@ -51,14 +55,17 @@ add_llvm_unittest(SupportTests
SwapByteOrderTest.cpp
TarWriterTest.cpp
TargetParserTest.cpp
+ TaskQueueTest.cpp
ThreadLocalTest.cpp
ThreadPool.cpp
Threading.cpp
TimerTest.cpp
TypeNameTest.cpp
+ TypeTraitsTest.cpp
TrailingObjectsTest.cpp
TrigramIndexTest.cpp
UnicodeTest.cpp
+ VersionTupleTest.cpp
YAMLIOTest.cpp
YAMLParserTest.cpp
formatted_raw_ostream_test.cpp
diff --git a/unittests/Support/CheckedArithmeticTest.cpp b/unittests/Support/CheckedArithmeticTest.cpp
new file mode 100644
index 000000000000..18eee4c447e4
--- /dev/null
+++ b/unittests/Support/CheckedArithmeticTest.cpp
@@ -0,0 +1,84 @@
+#include "llvm/Support/CheckedArithmetic.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(CheckedArithmetic, CheckedAdd) {
+ const int64_t Max = std::numeric_limits<int64_t>::max();
+ const int64_t Min = std::numeric_limits<int64_t>::min();
+ EXPECT_EQ(checkedAdd<int64_t>(Max, Max), None);
+ EXPECT_EQ(checkedAdd<int64_t>(Min, -1), None);
+ EXPECT_EQ(checkedAdd<int64_t>(Max, 1), None);
+ EXPECT_EQ(checkedAdd<int64_t>(10, 1), Optional<int64_t>(11));
+}
+
+TEST(CheckedArithmetic, CheckedAddSmall) {
+ const int16_t Max = std::numeric_limits<int16_t>::max();
+ const int16_t Min = std::numeric_limits<int16_t>::min();
+ EXPECT_EQ(checkedAdd<int16_t>(Max, Max), None);
+ EXPECT_EQ(checkedAdd<int16_t>(Min, -1), None);
+ EXPECT_EQ(checkedAdd<int16_t>(Max, 1), None);
+ EXPECT_EQ(checkedAdd<int16_t>(10, 1), Optional<int64_t>(11));
+}
+
+TEST(CheckedArithmetic, CheckedMul) {
+ const int64_t Max = std::numeric_limits<int64_t>::max();
+ const int64_t Min = std::numeric_limits<int64_t>::min();
+ EXPECT_EQ(checkedMul<int64_t>(Max, 2), None);
+ EXPECT_EQ(checkedMul<int64_t>(Max, Max), None);
+ EXPECT_EQ(checkedMul<int64_t>(Min, 2), None);
+ EXPECT_EQ(checkedMul<int64_t>(10, 2), Optional<int64_t>(20));
+}
+
+TEST(CheckedArithmetic, CheckedMulAdd) {
+ const int64_t Max = std::numeric_limits<int64_t>::max();
+ const int64_t Min = std::numeric_limits<int64_t>::min();
+ EXPECT_EQ(checkedMulAdd<int64_t>(Max, 1, 2), None);
+ EXPECT_EQ(checkedMulAdd<int64_t>(1, 1, Max), None);
+ EXPECT_EQ(checkedMulAdd<int64_t>(1, -1, Min), None);
+ EXPECT_EQ(checkedMulAdd<int64_t>(10, 2, 3), Optional<int64_t>(23));
+}
+
+TEST(CheckedArithmetic, CheckedMulSmall) {
+ const int16_t Max = std::numeric_limits<int16_t>::max();
+ const int16_t Min = std::numeric_limits<int16_t>::min();
+ EXPECT_EQ(checkedMul<int16_t>(Max, 2), None);
+ EXPECT_EQ(checkedMul<int16_t>(Max, Max), None);
+ EXPECT_EQ(checkedMul<int16_t>(Min, 2), None);
+ EXPECT_EQ(checkedMul<int16_t>(10, 2), Optional<int16_t>(20));
+}
+
+TEST(CheckedArithmetic, CheckedMulAddSmall) {
+ const int16_t Max = std::numeric_limits<int16_t>::max();
+ const int16_t Min = std::numeric_limits<int16_t>::min();
+ EXPECT_EQ(checkedMulAdd<int16_t>(Max, 1, 2), None);
+ EXPECT_EQ(checkedMulAdd<int16_t>(1, 1, Max), None);
+ EXPECT_EQ(checkedMulAdd<int16_t>(1, -1, Min), None);
+ EXPECT_EQ(checkedMulAdd<int16_t>(10, 2, 3), Optional<int16_t>(23));
+}
+
+TEST(CheckedArithmetic, CheckedAddUnsigned) {
+ const uint64_t Max = std::numeric_limits<uint64_t>::max();
+ EXPECT_EQ(checkedAddUnsigned<uint64_t>(Max, Max), None);
+ EXPECT_EQ(checkedAddUnsigned<uint64_t>(Max, 1), None);
+ EXPECT_EQ(checkedAddUnsigned<uint64_t>(10, 1), Optional<uint64_t>(11));
+}
+
+TEST(CheckedArithmetic, CheckedMulUnsigned) {
+ const uint64_t Max = std::numeric_limits<uint64_t>::max();
+ EXPECT_EQ(checkedMulUnsigned<uint64_t>(Max, 2), llvm::None);
+ EXPECT_EQ(checkedMulUnsigned<uint64_t>(Max, Max), llvm::None);
+ EXPECT_EQ(checkedMulUnsigned<uint64_t>(10, 2), Optional<uint64_t>(20));
+}
+
+TEST(CheckedArithmetic, CheckedMulAddUnsigned) {
+ const uint64_t Max = std::numeric_limits<uint64_t>::max();
+ EXPECT_EQ(checkedMulAddUnsigned<uint64_t>(Max, 1, 2), None);
+ EXPECT_EQ(checkedMulAddUnsigned<uint64_t>(1, 1, Max), None);
+ EXPECT_EQ(checkedMulAddUnsigned<uint64_t>(10, 2, 3), Optional<uint64_t>(23));
+}
+
+
+} // namespace
diff --git a/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp
index 1fb0213b4d18..a296912a305e 100644
--- a/unittests/Support/CommandLineTest.cpp
+++ b/unittests/Support/CommandLineTest.cpp
@@ -10,8 +10,10 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Triple.h"
#include "llvm/Config/config.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/StringSaver.h"
@@ -50,26 +52,11 @@ class TempEnvVar {
const char *const name;
};
-template <typename T>
-class StackOption : public cl::opt<T> {
- typedef cl::opt<T> Base;
+template <typename T, typename Base = cl::opt<T>>
+class StackOption : public Base {
public:
- // One option...
- template<class M0t>
- explicit StackOption(const M0t &M0) : Base(M0) {}
-
- // Two options...
- template<class M0t, class M1t>
- StackOption(const M0t &M0, const M1t &M1) : Base(M0, M1) {}
-
- // Three options...
- template<class M0t, class M1t, class M2t>
- StackOption(const M0t &M0, const M1t &M1, const M2t &M2) : Base(M0, M1, M2) {}
-
- // Four options...
- template<class M0t, class M1t, class M2t, class M3t>
- StackOption(const M0t &M0, const M1t &M1, const M2t &M2, const M3t &M3)
- : Base(M0, M1, M2, M3) {}
+ template <class... Ts>
+ explicit StackOption(Ts &&... Ms) : Base(std::forward<Ts>(Ms)...) {}
~StackOption() override { this->removeArgument(); }
@@ -95,9 +82,9 @@ cl::OptionCategory TestCategory("Test Options", "Description");
TEST(CommandLineTest, ModifyExisitingOption) {
StackOption<int> TestOption("test-option", cl::desc("old description"));
- const char Description[] = "New description";
- const char ArgString[] = "new-test-option";
- const char ValueString[] = "Integer";
+ static const char Description[] = "New description";
+ static const char ArgString[] = "new-test-option";
+ static const char ValueString[] = "Integer";
StringMap<cl::Option *> &Map =
cl::getRegisteredOptions(*cl::TopLevelSubCommand);
@@ -207,6 +194,85 @@ TEST(CommandLineTest, TokenizeWindowsCommandLine) {
array_lengthof(Output));
}
+TEST(CommandLineTest, TokenizeConfigFile1) {
+ const char *Input = "\\";
+ const char *const Output[] = { "\\" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile2) {
+ const char *Input = "\\abc";
+ const char *const Output[] = { "abc" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile3) {
+ const char *Input = "abc\\";
+ const char *const Output[] = { "abc\\" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile4) {
+ const char *Input = "abc\\\n123";
+ const char *const Output[] = { "abc123" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile5) {
+ const char *Input = "abc\\\r\n123";
+ const char *const Output[] = { "abc123" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile6) {
+ const char *Input = "abc\\\n";
+ const char *const Output[] = { "abc" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile7) {
+ const char *Input = "abc\\\r\n";
+ const char *const Output[] = { "abc" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile8) {
+ SmallVector<const char *, 0> Actual;
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ cl::tokenizeConfigFile("\\\n", Saver, Actual, /*MarkEOLs=*/false);
+ EXPECT_TRUE(Actual.empty());
+}
+
+TEST(CommandLineTest, TokenizeConfigFile9) {
+ SmallVector<const char *, 0> Actual;
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ cl::tokenizeConfigFile("\\\r\n", Saver, Actual, /*MarkEOLs=*/false);
+ EXPECT_TRUE(Actual.empty());
+}
+
+TEST(CommandLineTest, TokenizeConfigFile10) {
+ const char *Input = "\\\nabc";
+ const char *const Output[] = { "abc" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
+TEST(CommandLineTest, TokenizeConfigFile11) {
+ const char *Input = "\\\r\nabc";
+ const char *const Output[] = { "abc" };
+ testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
+ array_lengthof(Output));
+}
+
TEST(CommandLineTest, AliasesWithArguments) {
static const size_t ARGC = 3;
const char *const Inputs[][ARGC] = {
@@ -552,6 +618,40 @@ TEST(CommandLineTest, ArgumentLimit) {
EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data()));
}
+TEST(CommandLineTest, ResponseFileWindows) {
+ if (!Triple(sys::getProcessTriple()).isOSWindows())
+ return;
+
+ StackOption<std::string, cl::list<std::string>> InputFilenames(
+ cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore);
+ StackOption<bool> TopLevelOpt("top-level", cl::init(false));
+
+ // Create response file.
+ int FileDescriptor;
+ SmallString<64> TempPath;
+ std::error_code EC =
+ llvm::sys::fs::createTemporaryFile("resp-", ".txt", FileDescriptor, TempPath);
+ EXPECT_TRUE(!EC);
+
+ std::ofstream RspFile(TempPath.c_str());
+ EXPECT_TRUE(RspFile.is_open());
+ RspFile << "-top-level\npath\\dir\\file1\npath/dir/file2";
+ RspFile.close();
+
+ llvm::SmallString<128> RspOpt;
+ RspOpt.append(1, '@');
+ RspOpt.append(TempPath.c_str());
+ const char *args[] = {"prog", RspOpt.c_str()};
+ EXPECT_FALSE(TopLevelOpt);
+ EXPECT_TRUE(
+ cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
+ EXPECT_TRUE(TopLevelOpt);
+ EXPECT_TRUE(InputFilenames[0] == "path\\dir\\file1");
+ EXPECT_TRUE(InputFilenames[1] == "path/dir/file2");
+
+ llvm::sys::fs::remove(TempPath.c_str());
+}
+
TEST(CommandLineTest, ResponseFiles) {
llvm::SmallString<128> TestDir;
std::error_code EC =
@@ -646,6 +746,98 @@ TEST(CommandLineTest, SetDefautValue) {
EXPECT_TRUE(Opt1 == "true");
EXPECT_TRUE(Opt2);
EXPECT_TRUE(Opt3 == 3);
+ Alias.removeArgument();
}
+TEST(CommandLineTest, ReadConfigFile) {
+ llvm::SmallVector<const char *, 1> Argv;
+
+ llvm::SmallString<128> TestDir;
+ std::error_code EC =
+ llvm::sys::fs::createUniqueDirectory("unittest", TestDir);
+ EXPECT_TRUE(!EC);
+
+ llvm::SmallString<128> TestCfg;
+ llvm::sys::path::append(TestCfg, TestDir, "foo");
+ std::ofstream ConfigFile(TestCfg.c_str());
+ EXPECT_TRUE(ConfigFile.is_open());
+ ConfigFile << "# Comment\n"
+ "-option_1\n"
+ "@subconfig\n"
+ "-option_3=abcd\n"
+ "-option_4=\\\n"
+ "cdef\n";
+ ConfigFile.close();
+
+ llvm::SmallString<128> TestCfg2;
+ llvm::sys::path::append(TestCfg2, TestDir, "subconfig");
+ std::ofstream ConfigFile2(TestCfg2.c_str());
+ EXPECT_TRUE(ConfigFile2.is_open());
+ ConfigFile2 << "-option_2\n"
+ "\n"
+ " # comment\n";
+ ConfigFile2.close();
+
+ // Make sure the current directory is not the directory where config files
+ // resides. In this case the code that expands response files will not find
+ // 'subconfig' unless it resolves nested inclusions relative to the including
+ // file.
+ llvm::SmallString<128> CurrDir;
+ EC = llvm::sys::fs::current_path(CurrDir);
+ EXPECT_TRUE(!EC);
+ EXPECT_TRUE(StringRef(CurrDir) != StringRef(TestDir));
+
+ llvm::BumpPtrAllocator A;
+ llvm::StringSaver Saver(A);
+ bool Result = llvm::cl::readConfigFile(TestCfg, Saver, Argv);
+
+ EXPECT_TRUE(Result);
+ EXPECT_EQ(Argv.size(), 4U);
+ EXPECT_STREQ(Argv[0], "-option_1");
+ EXPECT_STREQ(Argv[1], "-option_2");
+ EXPECT_STREQ(Argv[2], "-option_3=abcd");
+ EXPECT_STREQ(Argv[3], "-option_4=cdef");
+
+ llvm::sys::fs::remove(TestCfg2);
+ llvm::sys::fs::remove(TestCfg);
+ llvm::sys::fs::remove(TestDir);
+}
+
+TEST(CommandLineTest, PositionalEatArgsError) {
+ StackOption<std::string, cl::list<std::string>> PosEatArgs(
+ "positional-eat-args", cl::Positional, cl::desc("<arguments>..."),
+ cl::ZeroOrMore, cl::PositionalEatsArgs);
+
+ const char *args[] = {"prog", "-positional-eat-args=XXXX"};
+ const char *args2[] = {"prog", "-positional-eat-args=XXXX", "-foo"};
+ const char *args3[] = {"prog", "-positional-eat-args", "-foo"};
+
+ std::string Errs;
+ raw_string_ostream OS(Errs);
+ EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); OS.flush();
+ EXPECT_FALSE(Errs.empty()); Errs.clear();
+ EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); OS.flush();
+ EXPECT_FALSE(Errs.empty()); Errs.clear();
+ EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); OS.flush();
+ EXPECT_TRUE(Errs.empty());
+}
+
+#ifdef _WIN32
+TEST(CommandLineTest, GetCommandLineArguments) {
+ int argc = __argc;
+ char **argv = __argv;
+
+ // GetCommandLineArguments is called in InitLLVM.
+ llvm::InitLLVM X(argc, argv);
+
+ EXPECT_EQ(llvm::sys::path::is_absolute(argv[0]),
+ llvm::sys::path::is_absolute(__argv[0]));
+
+ EXPECT_TRUE(llvm::sys::path::filename(argv[0])
+ .equals_lower("supporttests.exe"))
+ << "Filename of test executable is "
+ << llvm::sys::path::filename(argv[0]);
+}
+#endif
+
} // anonymous namespace
diff --git a/unittests/Support/CrashRecoveryTest.cpp b/unittests/Support/CrashRecoveryTest.cpp
index 3f13693632db..ac531b246c6e 100644
--- a/unittests/Support/CrashRecoveryTest.cpp
+++ b/unittests/Support/CrashRecoveryTest.cpp
@@ -11,7 +11,7 @@
#include "llvm/Support/CrashRecoveryContext.h"
#include "gtest/gtest.h"
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOGDI
#include <windows.h>
@@ -61,7 +61,7 @@ TEST(CrashRecoveryTest, Cleanup) {
EXPECT_EQ(1, GlobalInt);
}
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
static void raiseIt() {
RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL);
}
diff --git a/unittests/Support/DJBTest.cpp b/unittests/Support/DJBTest.cpp
new file mode 100644
index 000000000000..b157b96e6b11
--- /dev/null
+++ b/unittests/Support/DJBTest.cpp
@@ -0,0 +1,96 @@
+//===---------- llvm/unittest/Support/DJBTest.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/DJB.h"
+#include "llvm/ADT/Twine.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(DJBTest, caseFolding) {
+ struct TestCase {
+ StringLiteral One;
+ StringLiteral Two;
+ };
+
+ static constexpr TestCase Tests[] = {
+ {{"ASDF"}, {"asdf"}},
+ {{"qWeR"}, {"QwEr"}},
+ {{"qqqqqqqqqqqqqqqqqqqq"}, {"QQQQQQQQQQQQQQQQQQQQ"}},
+
+ {{"I"}, {"i"}},
+ // Latin Small Letter Dotless I
+ {{u8"\u0130"}, {"i"}},
+ // Latin Capital Letter I With Dot Above
+ {{u8"\u0131"}, {"i"}},
+
+ // Latin Capital Letter A With Grave
+ {{u8"\u00c0"}, {u8"\u00e0"}},
+ // Latin Capital Letter A With Macron
+ {{u8"\u0100"}, {u8"\u0101"}},
+ // Latin Capital Letter L With Acute
+ {{u8"\u0139"}, {u8"\u013a"}},
+ // Cyrillic Capital Letter Ie
+ {{u8"\u0415"}, {u8"\u0435"}},
+ // Latin Capital Letter A With Circumflex And Grave
+ {{u8"\u1ea6"}, {u8"\u1ea7"}},
+ // Kelvin Sign
+ {{u8"\u212a"}, {u8"\u006b"}},
+ // Glagolitic Capital Letter Chrivi
+ {{u8"\u2c1d"}, {u8"\u2c4d"}},
+ // Fullwidth Latin Capital Letter M
+ {{u8"\uff2d"}, {u8"\uff4d"}},
+ // Old Hungarian Capital Letter Ej
+ {{u8"\U00010c92"}, {u8"\U00010cd2"}},
+ };
+
+ for (const TestCase &T : Tests) {
+ SCOPED_TRACE("Comparing '" + T.One + "' and '" + T.Two + "'");
+ EXPECT_EQ(caseFoldingDjbHash(T.One), caseFoldingDjbHash(T.Two));
+ }
+}
+
+TEST(DJBTest, knownValuesLowerCase) {
+ struct TestCase {
+ StringLiteral Text;
+ uint32_t Hash;
+ };
+ static constexpr TestCase Tests[] = {
+ {{""}, 5381u},
+ {{"f"}, 177675u},
+ {{"fo"}, 5863386u},
+ {{"foo"}, 193491849u},
+ {{"foob"}, 2090263819u},
+ {{"fooba"}, 259229388u},
+ {{"foobar"}, 4259602622u},
+ {{"pneumonoultramicroscopicsilicovolcanoconiosis"}, 3999417781u},
+ };
+
+ for (const TestCase &T : Tests) {
+ SCOPED_TRACE("Text: '" + T.Text + "'");
+ EXPECT_EQ(T.Hash, djbHash(T.Text));
+ EXPECT_EQ(T.Hash, caseFoldingDjbHash(T.Text));
+ EXPECT_EQ(T.Hash, caseFoldingDjbHash(T.Text.upper()));
+ }
+}
+
+TEST(DJBTest, knownValuesUnicode) {
+ EXPECT_EQ(5866553u, djbHash(u8"\u0130"));
+ EXPECT_EQ(177678u, caseFoldingDjbHash(u8"\u0130"));
+ EXPECT_EQ(
+ 1302161417u,
+ djbHash(
+ u8"\u0130\u0131\u00c0\u00e0\u0100\u0101\u0139\u013a\u0415\u0435\u1ea6"
+ u8"\u1ea7\u212a\u006b\u2c1d\u2c4d\uff2d\uff4d\U00010c92\U00010cd2"));
+ EXPECT_EQ(
+ 1145571043u,
+ caseFoldingDjbHash(
+ u8"\u0130\u0131\u00c0\u00e0\u0100\u0101\u0139\u013a\u0415\u0435\u1ea6"
+ u8"\u1ea7\u212a\u006b\u2c1d\u2c4d\uff2d\uff4d\U00010c92\U00010cd2"));
+}
diff --git a/unittests/Support/DebugCounterTest.cpp b/unittests/Support/DebugCounterTest.cpp
new file mode 100644
index 000000000000..f3c2817a22e1
--- /dev/null
+++ b/unittests/Support/DebugCounterTest.cpp
@@ -0,0 +1,42 @@
+//===- llvm/unittest/Support/DebugCounterTest.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/DebugCounter.h"
+#include "gtest/gtest.h"
+
+#include <string>
+using namespace llvm;
+
+#ifndef NDEBUG
+DEBUG_COUNTER(TestCounter, "test-counter",
+ "Counter used for unit test");
+
+TEST(DebugCounterTest, CounterCheck) {
+ EXPECT_FALSE(DebugCounter::isCounterSet(TestCounter));
+
+ auto DC = &DebugCounter::instance();
+ DC->push_back("test-counter-skip=1");
+ DC->push_back("test-counter-count=3");
+
+ EXPECT_TRUE(DebugCounter::isCounterSet(TestCounter));
+
+ EXPECT_EQ(0, DebugCounter::getCounterValue(TestCounter));
+ EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter));
+
+ EXPECT_EQ(1, DebugCounter::getCounterValue(TestCounter));
+ EXPECT_TRUE(DebugCounter::shouldExecute(TestCounter));
+
+ DebugCounter::setCounterValue(TestCounter, 3);
+ EXPECT_TRUE(DebugCounter::shouldExecute(TestCounter));
+ EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter));
+
+ DebugCounter::setCounterValue(TestCounter, 100);
+ EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter));
+}
+#endif
diff --git a/unittests/Support/DynamicLibrary/CMakeLists.txt b/unittests/Support/DynamicLibrary/CMakeLists.txt
index 4f060e4020d1..ec202295ac15 100644
--- a/unittests/Support/DynamicLibrary/CMakeLists.txt
+++ b/unittests/Support/DynamicLibrary/CMakeLists.txt
@@ -1,14 +1,23 @@
+# Needed by LLVM's CMake checks because this file defines multiple targets.
+set(LLVM_OPTIONAL_SOURCES ExportedFuncs.cpp PipSqueak.cpp)
+
set(LLVM_LINK_COMPONENTS Support)
-add_library(DynamicLibraryLib STATIC ExportedFuncs.cxx)
+add_library(DynamicLibraryLib STATIC
+ ExportedFuncs.cpp
+ )
set_target_properties(DynamicLibraryLib PROPERTIES FOLDER "Tests")
-add_llvm_unittest(DynamicLibraryTests DynamicLibraryTest.cpp)
+add_llvm_unittest(DynamicLibraryTests
+ DynamicLibraryTest.cpp
+ )
target_link_libraries(DynamicLibraryTests PRIVATE DynamicLibraryLib)
export_executable_symbols(DynamicLibraryTests)
function(dynlib_add_module NAME)
- add_library(${NAME} SHARED PipSqueak.cxx)
+ add_library(${NAME} SHARED
+ PipSqueak.cpp
+ )
set_target_properties(${NAME} PROPERTIES FOLDER "Tests")
set_output_directory(${NAME}
@@ -18,7 +27,7 @@ function(dynlib_add_module NAME)
set_target_properties(${NAME}
PROPERTIES PREFIX ""
- SUFFIX ".so"
+ SUFFIX ${LTDL_SHLIB_EXT}
)
add_dependencies(DynamicLibraryTests ${NAME})
diff --git a/unittests/Support/DynamicLibrary/DynamicLibraryTest.cpp b/unittests/Support/DynamicLibrary/DynamicLibraryTest.cpp
index 370e1c5ed5e8..50a0f1e621fb 100644
--- a/unittests/Support/DynamicLibrary/DynamicLibraryTest.cpp
+++ b/unittests/Support/DynamicLibrary/DynamicLibraryTest.cpp
@@ -20,12 +20,14 @@ using namespace llvm;
using namespace llvm::sys;
std::string LibPath(const std::string Name = "PipSqueak") {
- const std::vector<testing::internal::string>& Argvs = testing::internal::GetArgvs();
- const char *Argv0 = Argvs.size() > 0 ? Argvs[0].c_str() : "DynamicLibraryTests";
+ const std::vector<testing::internal::string> &Argvs =
+ testing::internal::GetArgvs();
+ const char *Argv0 =
+ Argvs.size() > 0 ? Argvs[0].c_str() : "DynamicLibraryTests";
void *Ptr = (void*)(intptr_t)TestA;
std::string Path = fs::getMainExecutable(Argv0, Ptr);
llvm::SmallString<256> Buf(path::parent_path(Path));
- path::append(Buf, (Name+".so").c_str());
+ path::append(Buf, (Name + LTDL_SHLIB_EXT).c_str());
return Buf.str();
}
diff --git a/unittests/Support/DynamicLibrary/ExportedFuncs.cxx b/unittests/Support/DynamicLibrary/ExportedFuncs.cpp
index 97f190b0b9bc..370c8cb471d1 100644
--- a/unittests/Support/DynamicLibrary/ExportedFuncs.cxx
+++ b/unittests/Support/DynamicLibrary/ExportedFuncs.cpp
@@ -1,4 +1,4 @@
-//===- llvm/unittest/Support/DynamicLibrary/DynamicLibraryLib.cpp ---------===//
+//===- llvm/unittest/Support/DynamicLibrary/ExportedFuncs.cpp -------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/unittests/Support/DynamicLibrary/PipSqueak.cxx b/unittests/Support/DynamicLibrary/PipSqueak.cpp
index 375d72c0b535..e2f1cf7de082 100644
--- a/unittests/Support/DynamicLibrary/PipSqueak.cxx
+++ b/unittests/Support/DynamicLibrary/PipSqueak.cpp
@@ -1,4 +1,4 @@
-//===- llvm/unittest/Support/DynamicLibrary/PipSqueak.cxx -----------------===//
+//===- llvm/unittest/Support/DynamicLibrary/PipSqueak.cpp -----------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -46,4 +46,4 @@ extern "C" PIPSQUEAK_EXPORT void TestOrder(std::vector<std::string> &V) {
}
#define PIPSQUEAK_TESTA_RETURN "LibCall"
-#include "ExportedFuncs.cxx"
+#include "ExportedFuncs.cpp"
diff --git a/unittests/Support/EndianStreamTest.cpp b/unittests/Support/EndianStreamTest.cpp
index 48c5c3bc8175..9f938eef8791 100644
--- a/unittests/Support/EndianStreamTest.cpp
+++ b/unittests/Support/EndianStreamTest.cpp
@@ -21,7 +21,7 @@ TEST(EndianStream, WriteInt32LE) {
{
raw_svector_ostream OS(data);
- endian::Writer<little> LE(OS);
+ endian::Writer LE(OS, little);
LE.write(static_cast<int32_t>(-1362446643));
}
@@ -36,7 +36,7 @@ TEST(EndianStream, WriteInt32BE) {
{
raw_svector_ostream OS(data);
- endian::Writer<big> BE(OS);
+ endian::Writer BE(OS, big);
BE.write(static_cast<int32_t>(-1362446643));
}
@@ -52,7 +52,7 @@ TEST(EndianStream, WriteFloatLE) {
{
raw_svector_ostream OS(data);
- endian::Writer<little> LE(OS);
+ endian::Writer LE(OS, little);
LE.write(12345.0f);
}
@@ -67,7 +67,7 @@ TEST(EndianStream, WriteFloatBE) {
{
raw_svector_ostream OS(data);
- endian::Writer<big> BE(OS);
+ endian::Writer BE(OS, big);
BE.write(12345.0f);
}
@@ -82,7 +82,7 @@ TEST(EndianStream, WriteInt64LE) {
{
raw_svector_ostream OS(data);
- endian::Writer<little> LE(OS);
+ endian::Writer LE(OS, little);
LE.write(static_cast<int64_t>(-136244664332342323));
}
@@ -101,7 +101,7 @@ TEST(EndianStream, WriteInt64BE) {
{
raw_svector_ostream OS(data);
- endian::Writer<big> BE(OS);
+ endian::Writer BE(OS, big);
BE.write(static_cast<int64_t>(-136244664332342323));
}
@@ -120,7 +120,7 @@ TEST(EndianStream, WriteDoubleLE) {
{
raw_svector_ostream OS(data);
- endian::Writer<little> LE(OS);
+ endian::Writer LE(OS, little);
LE.write(-2349214918.58107);
}
@@ -139,7 +139,7 @@ TEST(EndianStream, WriteDoubleBE) {
{
raw_svector_ostream OS(data);
- endian::Writer<big> BE(OS);
+ endian::Writer BE(OS, big);
BE.write(-2349214918.58107);
}
@@ -158,7 +158,7 @@ TEST(EndianStream, WriteArrayLE) {
{
raw_svector_ostream OS(Data);
- endian::Writer<little> LE(OS);
+ endian::Writer LE(OS, little);
LE.write<uint16_t>({0x1234, 0x5678});
}
@@ -173,7 +173,7 @@ TEST(EndianStream, WriteVectorLE) {
{
raw_svector_ostream OS(Data);
- endian::Writer<little> LE(OS);
+ endian::Writer LE(OS, little);
std::vector<uint16_t> Vec{0x1234, 0x5678};
LE.write<uint16_t>(Vec);
}
@@ -189,7 +189,7 @@ TEST(EndianStream, WriteFloatArrayLE) {
{
raw_svector_ostream OS(Data);
- endian::Writer<little> LE(OS);
+ endian::Writer LE(OS, little);
LE.write<float>({12345.0f, 12346.0f});
}
diff --git a/unittests/Support/ErrnoTest.cpp b/unittests/Support/ErrnoTest.cpp
index 67f834a938da..701ac9608251 100644
--- a/unittests/Support/ErrnoTest.cpp
+++ b/unittests/Support/ErrnoTest.cpp
@@ -33,4 +33,7 @@ TEST(ErrnoTest, RetryAfterSignal) {
std::unique_ptr<int> P(RetryAfterSignal(nullptr, [] { return new int(47); }));
EXPECT_EQ(47, *P);
+
+ errno = EINTR;
+ EXPECT_EQ(-1, RetryAfterSignal(-1, [] { return -1; }));
}
diff --git a/unittests/Support/ErrorOrTest.cpp b/unittests/Support/ErrorOrTest.cpp
index 87dcab7b181e..2ffc6e5b7940 100644
--- a/unittests/Support/ErrorOrTest.cpp
+++ b/unittests/Support/ErrorOrTest.cpp
@@ -128,13 +128,13 @@ static_assert(
// ErrorOr<int*> x(nullptr);
// ErrorOr<std::unique_ptr<int>> y;
// y = x; // invalid conversion
-static_assert(!std::is_assignable<ErrorOr<std::unique_ptr<int>>,
+static_assert(!std::is_assignable<ErrorOr<std::unique_ptr<int>>&,
const ErrorOr<int *> &>::value,
"do not invoke explicit ctors in assignment");
// ErrorOr<std::unique_ptr<int>> x;
// x = ErrorOr<int*>(nullptr); // invalid conversion
-static_assert(!std::is_assignable<ErrorOr<std::unique_ptr<int>>,
+static_assert(!std::is_assignable<ErrorOr<std::unique_ptr<int>>&,
ErrorOr<int *> &&>::value,
"do not invoke explicit ctors in assignment");
} // end anon namespace
diff --git a/unittests/Support/ErrorTest.cpp b/unittests/Support/ErrorTest.cpp
index 2629e640f79c..66ffd23f8174 100644
--- a/unittests/Support/ErrorTest.cpp
+++ b/unittests/Support/ErrorTest.cpp
@@ -32,7 +32,7 @@ public:
// Log this error to a stream.
void log(raw_ostream &OS) const override {
- OS << "CustomError { " << getInfo() << "}";
+ OS << "CustomError {" << getInfo() << "}";
}
std::error_code convertToErrorCode() const override {
@@ -443,6 +443,29 @@ TEST(Error, StringError) {
<< "Failed to convert StringError to error_code.";
}
+TEST(Error, createStringError) {
+ static const char *Bar = "bar";
+ static const std::error_code EC = errc::invalid_argument;
+ std::string Msg;
+ raw_string_ostream S(Msg);
+ logAllUnhandledErrors(createStringError(EC, "foo%s%d0x%" PRIx8, Bar, 1, 0xff),
+ S, "");
+ EXPECT_EQ(S.str(), "foobar10xff\n")
+ << "Unexpected createStringError() log result";
+
+ S.flush();
+ Msg.clear();
+ logAllUnhandledErrors(createStringError(EC, Bar), S, "");
+ EXPECT_EQ(S.str(), "bar\n")
+ << "Unexpected createStringError() (overloaded) log result";
+
+ S.flush();
+ Msg.clear();
+ auto Res = errorToErrorCode(createStringError(EC, "foo%s", Bar));
+ EXPECT_EQ(Res, EC)
+ << "Failed to convert createStringError() result to error_code.";
+}
+
// Test that the ExitOnError utility works as expected.
TEST(Error, ExitOnError) {
ExitOnError ExitOnErr;
@@ -702,35 +725,79 @@ 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);
+ 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);
+ 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}"),
+ .compare("CustomError {0}\n"
+ "CustomError {1}"),
0);
}
+TEST(Error, Stream) {
+ {
+ Error OK = Error::success();
+ std::string Buf;
+ llvm::raw_string_ostream S(Buf);
+ S << OK;
+ EXPECT_EQ("success", S.str());
+ consumeError(std::move(OK));
+ }
+ {
+ Error E1 = make_error<CustomError>(0);
+ std::string Buf;
+ llvm::raw_string_ostream S(Buf);
+ S << E1;
+ EXPECT_EQ("CustomError {0}", S.str());
+ consumeError(std::move(E1));
+ }
+}
+
TEST(Error, ErrorMatchers) {
EXPECT_THAT_ERROR(Error::success(), Succeeded());
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT_ERROR(make_error<CustomError>(0), Succeeded()),
- "Expected: succeeded\n Actual: failed (CustomError { 0})");
+ "Expected: succeeded\n Actual: failed (CustomError {0})");
EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed());
EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(Error::success(), Failed()),
"Expected: failed\n Actual: succeeded");
+ EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed<CustomError>());
+ EXPECT_NONFATAL_FAILURE(
+ EXPECT_THAT_ERROR(Error::success(), Failed<CustomError>()),
+ "Expected: failed with Error of given type\n Actual: succeeded");
+ EXPECT_NONFATAL_FAILURE(
+ EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed<CustomSubError>()),
+ "Error was not of given type");
+ EXPECT_NONFATAL_FAILURE(
+ EXPECT_THAT_ERROR(
+ joinErrors(make_error<CustomError>(0), make_error<CustomError>(1)),
+ Failed<CustomError>()),
+ "multiple errors");
+
+ EXPECT_THAT_ERROR(
+ make_error<CustomError>(0),
+ Failed<CustomError>(testing::Property(&CustomError::getInfo, 0)));
+ EXPECT_NONFATAL_FAILURE(
+ EXPECT_THAT_ERROR(
+ make_error<CustomError>(0),
+ Failed<CustomError>(testing::Property(&CustomError::getInfo, 1))),
+ "Expected: failed with Error of given type and the error is an object "
+ "whose given property is equal to 1\n"
+ " Actual: failed (CustomError {0})");
+ EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed<ErrorInfoBase>());
+
EXPECT_THAT_EXPECTED(Expected<int>(0), Succeeded());
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),
Succeeded()),
- "Expected: succeeded\n Actual: failed (CustomError { 0})");
+ "Expected: succeeded\n Actual: failed (CustomError {0})");
EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)), Failed());
EXPECT_NONFATAL_FAILURE(
@@ -742,7 +809,7 @@ TEST(Error, ErrorMatchers) {
EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),
HasValue(0)),
"Expected: succeeded with value (is equal to 0)\n"
- " Actual: failed (CustomError { 0})");
+ " Actual: failed (CustomError {0})");
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT_EXPECTED(Expected<int>(1), HasValue(0)),
"Expected: succeeded with value (is equal to 0)\n"
@@ -762,7 +829,7 @@ TEST(Error, ErrorMatchers) {
EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),
HasValue(testing::Gt(1))),
"Expected: succeeded with value (is > 1)\n"
- " Actual: failed (CustomError { 0})");
+ " Actual: failed (CustomError {0})");
}
} // end anon namespace
diff --git a/unittests/Support/FileOutputBufferTest.cpp b/unittests/Support/FileOutputBufferTest.cpp
index e7f1fd765bde..554859587d45 100644
--- a/unittests/Support/FileOutputBufferTest.cpp
+++ b/unittests/Support/FileOutputBufferTest.cpp
@@ -11,6 +11,7 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
@@ -121,4 +122,53 @@ TEST(FileOutputBuffer, Test) {
// Clean up.
ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));
}
+
+TEST(FileOutputBuffer, TestModify) {
+ // Create unique temporary directory for these tests
+ SmallString<128> TestDirectory;
+ {
+ ASSERT_NO_ERROR(
+ fs::createUniqueDirectory("FileOutputBuffer-modify", TestDirectory));
+ }
+
+ SmallString<128> File1(TestDirectory);
+ File1.append("/file");
+ // First write some data.
+ {
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(File1, 10);
+ ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError()));
+ std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr;
+ memcpy(Buffer->getBufferStart(), "AAAAAAAAAA", 10);
+ ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit()));
+ }
+
+ // Then re-open the file for modify and change only some bytes.
+ {
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(File1, size_t(-1), FileOutputBuffer::F_modify);
+ ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError()));
+ std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr;
+ ASSERT_EQ(10U, Buffer->getBufferSize());
+ uint8_t *Data = Buffer->getBufferStart();
+ Data[0] = 'X';
+ Data[9] = 'X';
+ ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit()));
+ }
+
+ // Finally, re-open the file for read and verify that it has the modified
+ // contents.
+ {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFile(File1);
+ ASSERT_NO_ERROR(BufferOrErr.getError());
+ std::unique_ptr<MemoryBuffer> Buffer = std::move(*BufferOrErr);
+ ASSERT_EQ(10U, Buffer->getBufferSize());
+ EXPECT_EQ(StringRef("XAAAAAAAAX"), Buffer->getBuffer());
+ }
+
+ // Clean up.
+ ASSERT_NO_ERROR(fs::remove(File1));
+ ASSERT_NO_ERROR(fs::remove(TestDirectory));
+}
+
} // anonymous namespace
diff --git a/unittests/Support/FormatVariadicTest.cpp b/unittests/Support/FormatVariadicTest.cpp
index ddecffdeed1d..91a44bae3a9d 100644
--- a/unittests/Support/FormatVariadicTest.cpp
+++ b/unittests/Support/FormatVariadicTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FormatAdapters.h"
#include "gtest/gtest.h"
@@ -143,6 +144,58 @@ TEST(FormatVariadicTest, ValidReplacementSequence) {
EXPECT_EQ(0u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
EXPECT_EQ("0:1", Replacements[0].Options);
+
+ // 9. Custom padding character
+ Replacements = formatv_object_base::parseFormatString("{0,p+4:foo}");
+ ASSERT_EQ(1u, Replacements.size());
+ EXPECT_EQ("0,p+4:foo", Replacements[0].Spec);
+ EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
+ EXPECT_EQ(0u, Replacements[0].Index);
+ EXPECT_EQ(4u, Replacements[0].Align);
+ EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
+ EXPECT_EQ('p', Replacements[0].Pad);
+ EXPECT_EQ("foo", Replacements[0].Options);
+
+ // Format string special characters are allowed as padding character
+ Replacements = formatv_object_base::parseFormatString("{0,-+4:foo}");
+ ASSERT_EQ(1u, Replacements.size());
+ EXPECT_EQ("0,-+4:foo", Replacements[0].Spec);
+ EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
+ EXPECT_EQ(0u, Replacements[0].Index);
+ EXPECT_EQ(4u, Replacements[0].Align);
+ EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
+ EXPECT_EQ('-', Replacements[0].Pad);
+ EXPECT_EQ("foo", Replacements[0].Options);
+
+ Replacements = formatv_object_base::parseFormatString("{0,+-4:foo}");
+ ASSERT_EQ(1u, Replacements.size());
+ EXPECT_EQ("0,+-4:foo", Replacements[0].Spec);
+ EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
+ EXPECT_EQ(0u, Replacements[0].Index);
+ EXPECT_EQ(4u, Replacements[0].Align);
+ EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);
+ EXPECT_EQ('+', Replacements[0].Pad);
+ EXPECT_EQ("foo", Replacements[0].Options);
+
+ Replacements = formatv_object_base::parseFormatString("{0,==4:foo}");
+ ASSERT_EQ(1u, Replacements.size());
+ EXPECT_EQ("0,==4:foo", Replacements[0].Spec);
+ EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
+ EXPECT_EQ(0u, Replacements[0].Index);
+ EXPECT_EQ(4u, Replacements[0].Align);
+ EXPECT_EQ(AlignStyle::Center, Replacements[0].Where);
+ EXPECT_EQ('=', Replacements[0].Pad);
+ EXPECT_EQ("foo", Replacements[0].Options);
+
+ Replacements = formatv_object_base::parseFormatString("{0,:=4:foo}");
+ ASSERT_EQ(1u, Replacements.size());
+ EXPECT_EQ("0,:=4:foo", Replacements[0].Spec);
+ EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
+ EXPECT_EQ(0u, Replacements[0].Index);
+ EXPECT_EQ(4u, Replacements[0].Align);
+ EXPECT_EQ(AlignStyle::Center, Replacements[0].Where);
+ EXPECT_EQ(':', Replacements[0].Pad);
+ EXPECT_EQ("foo", Replacements[0].Options);
}
TEST(FormatVariadicTest, DefaultReplacementValues) {
@@ -421,6 +474,16 @@ TEST(FormatVariadicTest, DoubleFormatting) {
EXPECT_EQ(" -0.001", formatv("{0,8:F3}", -.0012345678).str());
}
+TEST(FormatVariadicTest, CustomPaddingCharacter) {
+ // 1. Padding with custom character
+ EXPECT_EQ("==123", formatv("{0,=+5}", 123).str());
+ EXPECT_EQ("=123=", formatv("{0,==5}", 123).str());
+ EXPECT_EQ("123==", formatv("{0,=-5}", 123).str());
+
+ // 2. Combined with zero padding
+ EXPECT_EQ("=00123=", formatv("{0,==7:5}", 123).str());
+}
+
struct format_tuple {
const char *Fmt;
explicit format_tuple(const char *Fmt) : Fmt(Fmt) {}
@@ -609,3 +672,20 @@ TEST(FormatVariadicTest, CopiesAndMoves) {
EXPECT_EQ(0, R.Copied);
EXPECT_EQ(0, R.Moved);
}
+
+namespace adl {
+struct X {};
+raw_ostream &operator<<(raw_ostream &OS, const X &) { return OS << "X"; }
+} // namespace adl
+TEST(FormatVariadicTest, FormatStreamable) {
+ adl::X X;
+ EXPECT_EQ("X", formatv("{0}", X).str());
+}
+
+TEST(FormatVariadicTest, FormatError) {
+ auto E1 = make_error<StringError>("X", inconvertibleErrorCode());
+ EXPECT_EQ("X", formatv("{0}", E1).str());
+ EXPECT_TRUE(E1.isA<StringError>()); // not consumed
+ EXPECT_EQ("X", formatv("{0}", fmt_consume(std::move(E1))).str());
+ EXPECT_FALSE(E1.isA<StringError>()); // consumed
+}
diff --git a/unittests/Support/Host.cpp b/unittests/Support/Host.cpp
index 736b04c2049c..e07d4151bb65 100644
--- a/unittests/Support/Host.cpp
+++ b/unittests/Support/Host.cpp
@@ -185,12 +185,12 @@ TEST_F(HostTest, getMacOSHostVersion) {
path::append(OutputFile, "out");
const char *SwVersPath = "/usr/bin/sw_vers";
- const char *argv[] = {SwVersPath, "-productVersion", nullptr};
+ StringRef argv[] = {SwVersPath, "-productVersion"};
StringRef OutputPath = OutputFile.str();
const Optional<StringRef> Redirects[] = {/*STDIN=*/None,
/*STDOUT=*/OutputPath,
/*STDERR=*/None};
- int RetCode = ExecuteAndWait(SwVersPath, argv, /*env=*/nullptr, Redirects);
+ int RetCode = ExecuteAndWait(SwVersPath, argv, /*env=*/llvm::None, Redirects);
ASSERT_EQ(0, RetCode);
int FD = 0;
diff --git a/unittests/Support/JSONTest.cpp b/unittests/Support/JSONTest.cpp
new file mode 100644
index 000000000000..64a2bb97bd8b
--- /dev/null
+++ b/unittests/Support/JSONTest.cpp
@@ -0,0 +1,387 @@
+//===-- JSONTest.cpp - JSON unit tests --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/JSON.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace json {
+
+namespace {
+
+std::string s(const Value &E) { return llvm::formatv("{0}", E).str(); }
+std::string sp(const Value &E) { return llvm::formatv("{0:2}", E).str(); }
+
+TEST(JSONTest, Types) {
+ EXPECT_EQ("true", s(true));
+ EXPECT_EQ("null", s(nullptr));
+ EXPECT_EQ("2.5", s(2.5));
+ EXPECT_EQ(R"("foo")", s("foo"));
+ EXPECT_EQ("[1,2,3]", s({1, 2, 3}));
+ EXPECT_EQ(R"({"x":10,"y":20})", s(Object{{"x", 10}, {"y", 20}}));
+
+#ifdef NDEBUG
+ EXPECT_EQ(R"("��")", s("\xC0\x80"));
+ EXPECT_EQ(R"({"��":0})", s(Object{{"\xC0\x80", 0}}));
+#else
+ EXPECT_DEATH(s("\xC0\x80"), "Invalid UTF-8");
+ EXPECT_DEATH(s(Object{{"\xC0\x80", 0}}), "Invalid UTF-8");
+#endif
+}
+
+TEST(JSONTest, Constructors) {
+ // Lots of edge cases around empty and singleton init lists.
+ EXPECT_EQ("[[[3]]]", s({{{3}}}));
+ EXPECT_EQ("[[[]]]", s({{{}}}));
+ EXPECT_EQ("[[{}]]", s({{Object{}}}));
+ EXPECT_EQ(R"({"A":{"B":{}}})", s(Object{{"A", Object{{"B", Object{}}}}}));
+ EXPECT_EQ(R"({"A":{"B":{"X":"Y"}}})",
+ s(Object{{"A", Object{{"B", Object{{"X", "Y"}}}}}}));
+ EXPECT_EQ("null", s(llvm::Optional<double>()));
+ EXPECT_EQ("2.5", s(llvm::Optional<double>(2.5)));
+}
+
+TEST(JSONTest, StringOwnership) {
+ char X[] = "Hello";
+ Value Alias = static_cast<const char *>(X);
+ X[1] = 'a';
+ EXPECT_EQ(R"("Hallo")", s(Alias));
+
+ std::string Y = "Hello";
+ Value Copy = Y;
+ Y[1] = 'a';
+ EXPECT_EQ(R"("Hello")", s(Copy));
+}
+
+TEST(JSONTest, CanonicalOutput) {
+ // Objects are sorted (but arrays aren't)!
+ EXPECT_EQ(R"({"a":1,"b":2,"c":3})", s(Object{{"a", 1}, {"c", 3}, {"b", 2}}));
+ EXPECT_EQ(R"(["a","c","b"])", s({"a", "c", "b"}));
+ EXPECT_EQ("3", s(3.0));
+}
+
+TEST(JSONTest, Escaping) {
+ std::string test = {
+ 0, // Strings may contain nulls.
+ '\b', '\f', // Have mnemonics, but we escape numerically.
+ '\r', '\n', '\t', // Escaped with mnemonics.
+ 'S', '\"', '\\', // Printable ASCII characters.
+ '\x7f', // Delete is not escaped.
+ '\xce', '\x94', // Non-ASCII UTF-8 is not escaped.
+ };
+
+ std::string teststring = R"("\u0000\u0008\u000c\r\n\tS\"\\)"
+ "\x7f\xCE\x94\"";
+
+ EXPECT_EQ(teststring, s(test));
+
+ EXPECT_EQ(R"({"object keys are\nescaped":true})",
+ s(Object{{"object keys are\nescaped", true}}));
+}
+
+TEST(JSONTest, PrettyPrinting) {
+ const char str[] = R"({
+ "empty_array": [],
+ "empty_object": {},
+ "full_array": [
+ 1,
+ null
+ ],
+ "full_object": {
+ "nested_array": [
+ {
+ "property": "value"
+ }
+ ]
+ }
+})";
+
+ EXPECT_EQ(str, sp(Object{
+ {"empty_object", Object{}},
+ {"empty_array", {}},
+ {"full_array", {1, nullptr}},
+ {"full_object",
+ Object{
+ {"nested_array",
+ {Object{
+ {"property", "value"},
+ }}},
+ }},
+ }));
+}
+
+TEST(JSONTest, Parse) {
+ auto Compare = [](llvm::StringRef S, Value Expected) {
+ if (auto E = parse(S)) {
+ // Compare both string forms and with operator==, in case we have bugs.
+ EXPECT_EQ(*E, Expected);
+ EXPECT_EQ(sp(*E), sp(Expected));
+ } else {
+ handleAllErrors(E.takeError(), [S](const llvm::ErrorInfoBase &E) {
+ FAIL() << "Failed to parse JSON >>> " << S << " <<<: " << E.message();
+ });
+ }
+ };
+
+ Compare(R"(true)", true);
+ Compare(R"(false)", false);
+ Compare(R"(null)", nullptr);
+
+ Compare(R"(42)", 42);
+ Compare(R"(2.5)", 2.5);
+ Compare(R"(2e50)", 2e50);
+ Compare(R"(1.2e3456789)", std::numeric_limits<double>::infinity());
+
+ Compare(R"("foo")", "foo");
+ Compare(R"("\"\\\b\f\n\r\t")", "\"\\\b\f\n\r\t");
+ Compare(R"("\u0000")", llvm::StringRef("\0", 1));
+ Compare("\"\x7f\"", "\x7f");
+ Compare(R"("\ud801\udc37")", u8"\U00010437"); // UTF16 surrogate pair escape.
+ Compare("\"\xE2\x82\xAC\xF0\x9D\x84\x9E\"", u8"\u20ac\U0001d11e"); // UTF8
+ Compare(
+ R"("LoneLeading=\ud801, LoneTrailing=\udc01, LeadingLeadingTrailing=\ud801\ud801\udc37")",
+ u8"LoneLeading=\ufffd, LoneTrailing=\ufffd, "
+ u8"LeadingLeadingTrailing=\ufffd\U00010437"); // Invalid unicode.
+
+ Compare(R"({"":0,"":0})", Object{{"", 0}});
+ Compare(R"({"obj":{},"arr":[]})", Object{{"obj", Object{}}, {"arr", {}}});
+ Compare(R"({"\n":{"\u0000":[[[[]]]]}})",
+ Object{{"\n", Object{
+ {llvm::StringRef("\0", 1), {{{{}}}}},
+ }}});
+ Compare("\r[\n\t] ", {});
+}
+
+TEST(JSONTest, ParseErrors) {
+ auto ExpectErr = [](llvm::StringRef Msg, llvm::StringRef S) {
+ if (auto E = parse(S)) {
+ // Compare both string forms and with operator==, in case we have bugs.
+ FAIL() << "Parsed JSON >>> " << S << " <<< but wanted error: " << Msg;
+ } else {
+ handleAllErrors(E.takeError(), [S, Msg](const llvm::ErrorInfoBase &E) {
+ EXPECT_THAT(E.message(), testing::HasSubstr(Msg)) << S;
+ });
+ }
+ };
+ ExpectErr("Unexpected EOF", "");
+ ExpectErr("Unexpected EOF", "[");
+ ExpectErr("Text after end of document", "[][]");
+ ExpectErr("Invalid JSON value (false?)", "fuzzy");
+ ExpectErr("Expected , or ]", "[2?]");
+ ExpectErr("Expected object key", "{a:2}");
+ ExpectErr("Expected : after object key", R"({"a",2})");
+ ExpectErr("Expected , or } after object property", R"({"a":2 "b":3})");
+ ExpectErr("Invalid JSON value", R"([&%!])");
+ ExpectErr("Invalid JSON value (number?)", "1e1.0");
+ ExpectErr("Unterminated string", R"("abc\"def)");
+ ExpectErr("Control character in string", "\"abc\ndef\"");
+ ExpectErr("Invalid escape sequence", R"("\030")");
+ ExpectErr("Invalid \\u escape sequence", R"("\usuck")");
+ ExpectErr("[3:3, byte=19]", R"({
+ "valid": 1,
+ invalid: 2
+})");
+ ExpectErr("Invalid UTF-8 sequence", "\"\xC0\x80\""); // WTF-8 null
+}
+
+// Direct tests of isUTF8 and fixUTF8. Internal uses are also tested elsewhere.
+TEST(JSONTest, UTF8) {
+ for (const char *Valid : {
+ "this is ASCII text",
+ "thïs tëxt häs BMP chäräctërs",
+ "𐌶𐌰L𐌾𐍈 C𐍈𐌼𐌴𐍃",
+ }) {
+ EXPECT_TRUE(isUTF8(Valid)) << Valid;
+ EXPECT_EQ(fixUTF8(Valid), Valid);
+ }
+ for (auto Invalid : std::vector<std::pair<const char *, const char *>>{
+ {"lone trailing \x81\x82 bytes", "lone trailing �� bytes"},
+ {"missing trailing \xD0 bytes", "missing trailing � bytes"},
+ {"truncated character \xD0", "truncated character �"},
+ {"not \xC1\x80 the \xE0\x9f\xBF shortest \xF0\x83\x83\x83 encoding",
+ "not �� the ��� shortest ���� encoding"},
+ {"too \xF9\x80\x80\x80\x80 long", "too ����� long"},
+ {"surrogate \xED\xA0\x80 invalid \xF4\x90\x80\x80",
+ "surrogate ��� invalid ����"}}) {
+ EXPECT_FALSE(isUTF8(Invalid.first)) << Invalid.first;
+ EXPECT_EQ(fixUTF8(Invalid.first), Invalid.second);
+ }
+}
+
+TEST(JSONTest, Inspection) {
+ llvm::Expected<Value> Doc = parse(R"(
+ {
+ "null": null,
+ "boolean": false,
+ "number": 2.78,
+ "string": "json",
+ "array": [null, true, 3.14, "hello", [1,2,3], {"time": "arrow"}],
+ "object": {"fruit": "banana"}
+ }
+ )");
+ EXPECT_TRUE(!!Doc);
+
+ Object *O = Doc->getAsObject();
+ ASSERT_TRUE(O);
+
+ EXPECT_FALSE(O->getNull("missing"));
+ EXPECT_FALSE(O->getNull("boolean"));
+ EXPECT_TRUE(O->getNull("null"));
+
+ EXPECT_EQ(O->getNumber("number"), llvm::Optional<double>(2.78));
+ EXPECT_FALSE(O->getInteger("number"));
+ EXPECT_EQ(O->getString("string"), llvm::Optional<llvm::StringRef>("json"));
+ ASSERT_FALSE(O->getObject("missing"));
+ ASSERT_FALSE(O->getObject("array"));
+ ASSERT_TRUE(O->getObject("object"));
+ EXPECT_EQ(*O->getObject("object"), (Object{{"fruit", "banana"}}));
+
+ Array *A = O->getArray("array");
+ ASSERT_TRUE(A);
+ EXPECT_EQ((*A)[1].getAsBoolean(), llvm::Optional<bool>(true));
+ ASSERT_TRUE((*A)[4].getAsArray());
+ EXPECT_EQ(*(*A)[4].getAsArray(), (Array{1, 2, 3}));
+ EXPECT_EQ((*(*A)[4].getAsArray())[1].getAsInteger(),
+ llvm::Optional<int64_t>(2));
+ int I = 0;
+ for (Value &E : *A) {
+ if (I++ == 5) {
+ ASSERT_TRUE(E.getAsObject());
+ EXPECT_EQ(E.getAsObject()->getString("time"),
+ llvm::Optional<llvm::StringRef>("arrow"));
+ } else
+ EXPECT_FALSE(E.getAsObject());
+ }
+}
+
+// Verify special integer handling - we try to preserve exact int64 values.
+TEST(JSONTest, Integers) {
+ struct {
+ const char *Desc;
+ Value Val;
+ const char *Str;
+ llvm::Optional<int64_t> AsInt;
+ llvm::Optional<double> AsNumber;
+ } TestCases[] = {
+ {
+ "Non-integer. Stored as double, not convertible.",
+ double{1.5},
+ "1.5",
+ llvm::None,
+ 1.5,
+ },
+
+ {
+ "Integer, not exact double. Stored as int64, convertible.",
+ int64_t{0x4000000000000001},
+ "4611686018427387905",
+ int64_t{0x4000000000000001},
+ double{0x4000000000000000},
+ },
+
+ {
+ "Negative integer, not exact double. Stored as int64, convertible.",
+ int64_t{-0x4000000000000001},
+ "-4611686018427387905",
+ int64_t{-0x4000000000000001},
+ double{-0x4000000000000000},
+ },
+
+ {
+ "Dynamically exact integer. Stored as double, convertible.",
+ double{0x6000000000000000},
+ "6.9175290276410819e+18",
+ int64_t{0x6000000000000000},
+ double{0x6000000000000000},
+ },
+
+ {
+ "Dynamically integer, >64 bits. Stored as double, not convertible.",
+ 1.5 * double{0x8000000000000000},
+ "1.3835058055282164e+19",
+ llvm::None,
+ 1.5 * double{0x8000000000000000},
+ },
+ };
+ for (const auto &T : TestCases) {
+ EXPECT_EQ(T.Str, s(T.Val)) << T.Desc;
+ llvm::Expected<Value> Doc = parse(T.Str);
+ EXPECT_TRUE(!!Doc) << T.Desc;
+ EXPECT_EQ(Doc->getAsInteger(), T.AsInt) << T.Desc;
+ EXPECT_EQ(Doc->getAsNumber(), T.AsNumber) << T.Desc;
+ EXPECT_EQ(T.Val, *Doc) << T.Desc;
+ EXPECT_EQ(T.Str, s(*Doc)) << T.Desc;
+ }
+}
+
+// Sample struct with typical JSON-mapping rules.
+struct CustomStruct {
+ CustomStruct() : B(false) {}
+ CustomStruct(std::string S, llvm::Optional<int> I, bool B)
+ : S(S), I(I), B(B) {}
+ std::string S;
+ llvm::Optional<int> I;
+ bool B;
+};
+inline bool operator==(const CustomStruct &L, const CustomStruct &R) {
+ return L.S == R.S && L.I == R.I && L.B == R.B;
+}
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const CustomStruct &S) {
+ return OS << "(" << S.S << ", " << (S.I ? std::to_string(*S.I) : "None")
+ << ", " << S.B << ")";
+}
+bool fromJSON(const Value &E, CustomStruct &R) {
+ ObjectMapper O(E);
+ if (!O || !O.map("str", R.S) || !O.map("int", R.I))
+ return false;
+ O.map("bool", R.B);
+ return true;
+}
+
+TEST(JSONTest, Deserialize) {
+ std::map<std::string, std::vector<CustomStruct>> R;
+ CustomStruct ExpectedStruct = {"foo", 42, true};
+ std::map<std::string, std::vector<CustomStruct>> Expected;
+ Value J = Object{
+ {"foo",
+ Array{
+ Object{
+ {"str", "foo"},
+ {"int", 42},
+ {"bool", true},
+ {"unknown", "ignored"},
+ },
+ Object{{"str", "bar"}},
+ Object{
+ {"str", "baz"}, {"bool", "string"}, // OK, deserialize ignores.
+ },
+ }}};
+ Expected["foo"] = {
+ CustomStruct("foo", 42, true),
+ CustomStruct("bar", llvm::None, false),
+ CustomStruct("baz", llvm::None, false),
+ };
+ ASSERT_TRUE(fromJSON(J, R));
+ EXPECT_EQ(R, Expected);
+
+ CustomStruct V;
+ EXPECT_FALSE(fromJSON(nullptr, V)) << "Not an object " << V;
+ EXPECT_FALSE(fromJSON(Object{}, V)) << "Missing required field " << V;
+ EXPECT_FALSE(fromJSON(Object{{"str", 1}}, V)) << "Wrong type " << V;
+ // Optional<T> must parse as the correct type if present.
+ EXPECT_FALSE(fromJSON(Object{{"str", 1}, {"int", "string"}}, V))
+ << "Wrong type for Optional<T> " << V;
+}
+
+} // namespace
+} // namespace json
+} // namespace llvm
diff --git a/unittests/Support/LockFileManagerTest.cpp b/unittests/Support/LockFileManagerTest.cpp
index efe3c3088b33..1775d05e44db 100644
--- a/unittests/Support/LockFileManagerTest.cpp
+++ b/unittests/Support/LockFileManagerTest.cpp
@@ -60,7 +60,7 @@ TEST(LockFileManagerTest, LinkLockExists) {
sys::path::append(TmpFileLock, "file.lock-000");
int FD;
- EC = sys::fs::openFileForWrite(StringRef(TmpFileLock), FD, sys::fs::F_None);
+ EC = sys::fs::openFileForWrite(StringRef(TmpFileLock), FD);
ASSERT_FALSE(EC);
int Ret = close(FD);
diff --git a/unittests/Support/MD5Test.cpp b/unittests/Support/MD5Test.cpp
index 8b151827a7bd..bac1ec2f2b90 100644
--- a/unittests/Support/MD5Test.cpp
+++ b/unittests/Support/MD5Test.cpp
@@ -19,7 +19,7 @@
using namespace llvm;
namespace {
-/// \brief Tests an arbitrary set of bytes passed as \p Input.
+/// Tests an arbitrary set of bytes passed as \p Input.
void TestMD5Sum(ArrayRef<uint8_t> Input, StringRef Final) {
MD5 Hash;
Hash.update(Input);
diff --git a/unittests/Support/ManagedStatic.cpp b/unittests/Support/ManagedStatic.cpp
index 07e324cdfb65..d3cc80cf5e92 100644
--- a/unittests/Support/ManagedStatic.cpp
+++ b/unittests/Support/ManagedStatic.cpp
@@ -6,6 +6,8 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Allocator.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Config/config.h"
#ifdef HAVE_PTHREAD_H
@@ -30,7 +32,7 @@ namespace test1 {
// Valgrind's leak checker complains glibc's stack allocation.
// To appease valgrind, we provide our own stack for each thread.
void *allocate_stack(pthread_attr_t &a, size_t n = 65536) {
- void *stack = malloc(n);
+ void *stack = safe_malloc(n);
pthread_attr_init(&a);
#if defined(__linux__)
pthread_attr_setstack(&a, stack, n);
@@ -83,7 +85,7 @@ TEST(ManagedStaticTest, NestedStatics) {
namespace CustomCreatorDeletor {
struct CustomCreate {
static void *call() {
- void *Mem = std::malloc(sizeof(int));
+ void *Mem = safe_malloc(sizeof(int));
*((int *)Mem) = 42;
return Mem;
}
diff --git a/unittests/Support/MemoryBufferTest.cpp b/unittests/Support/MemoryBufferTest.cpp
index 5e3c8db02791..034b2ea9495d 100644
--- a/unittests/Support/MemoryBufferTest.cpp
+++ b/unittests/Support/MemoryBufferTest.cpp
@@ -116,13 +116,13 @@ TEST_F(MemoryBufferTest, make_new) {
EXPECT_TRUE(nullptr != Two.get());
// 0-initialized buffer with no name
- OwningBuffer Three(MemoryBuffer::getNewMemBuffer(321, data));
+ OwningBuffer Three(WritableMemoryBuffer::getNewMemBuffer(321, data));
EXPECT_TRUE(nullptr != Three.get());
for (size_t i = 0; i < 321; ++i)
EXPECT_EQ(0, Three->getBufferStart()[0]);
// 0-initialized buffer with name
- OwningBuffer Four(MemoryBuffer::getNewMemBuffer(123, "zeros"));
+ OwningBuffer Four(WritableMemoryBuffer::getNewMemBuffer(123, "zeros"));
EXPECT_TRUE(nullptr != Four.get());
for (size_t i = 0; i < 123; ++i)
EXPECT_EQ(0, Four->getBufferStart()[0]);
@@ -260,4 +260,33 @@ TEST_F(MemoryBufferTest, writableSlice) {
for (size_t i = 0; i < MB.getBufferSize(); i += 0x10)
EXPECT_EQ("0123456789abcdef", MB.getBuffer().substr(i, 0x10)) << "i: " << i;
}
+
+TEST_F(MemoryBufferTest, writeThroughFile) {
+ // Create a file initialized with some data
+ int FD;
+ SmallString<64> TestPath;
+ sys::fs::createTemporaryFile("MemoryBufferTest_WriteThrough", "temp", FD,
+ TestPath);
+ FileRemover Cleanup(TestPath);
+ raw_fd_ostream OF(FD, true);
+ OF << "0123456789abcdef";
+ OF.close();
+ {
+ auto MBOrError = WriteThroughMemoryBuffer::getFile(TestPath);
+ ASSERT_FALSE(MBOrError.getError());
+ // Write some data. It should be mapped readwrite, so that upon completion
+ // the original file contents are modified.
+ WriteThroughMemoryBuffer &MB = **MBOrError;
+ ASSERT_EQ(16u, MB.getBufferSize());
+ char *Start = MB.getBufferStart();
+ ASSERT_EQ(MB.getBufferEnd(), MB.getBufferStart() + MB.getBufferSize());
+ ::memset(Start, 'x', MB.getBufferSize());
+ }
+
+ auto MBOrError = MemoryBuffer::getFile(TestPath);
+ ASSERT_FALSE(MBOrError.getError());
+ auto &MB = **MBOrError;
+ ASSERT_EQ(16u, MB.getBufferSize());
+ EXPECT_EQ("xxxxxxxxxxxxxxxx", MB.getBuffer());
+}
}
diff --git a/unittests/Support/ParallelTest.cpp b/unittests/Support/ParallelTest.cpp
index d734e0dd8586..8779a61b1853 100644
--- a/unittests/Support/ParallelTest.cpp
+++ b/unittests/Support/ParallelTest.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief Parallel.h unit tests.
+/// Parallel.h unit tests.
///
//===----------------------------------------------------------------------===//
diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp
index f624626f5e53..dc5bfb8c7bd5 100644
--- a/unittests/Support/Path.cpp
+++ b/unittests/Support/Path.cpp
@@ -12,6 +12,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
@@ -21,9 +22,11 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
+#include "gmock/gmock.h"
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Chrono.h"
#include <windows.h>
#include <winerror.h>
#endif
@@ -47,8 +50,22 @@ using namespace llvm::sys;
} else { \
}
+#define ASSERT_ERROR(x) \
+ if (!x) { \
+ SmallString<128> MessageStorage; \
+ raw_svector_ostream Message(MessageStorage); \
+ Message << #x ": did not return a failure error code.\n"; \
+ GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \
+ }
+
namespace {
+struct FileDescriptorCloser {
+ explicit FileDescriptorCloser(int FD) : FD(FD) {}
+ ~FileDescriptorCloser() { ::close(FD); }
+ int FD;
+};
+
TEST(is_separator, Works) {
EXPECT_TRUE(path::is_separator('/'));
EXPECT_FALSE(path::is_separator('\0'));
@@ -58,7 +75,7 @@ TEST(is_separator, Works) {
EXPECT_TRUE(path::is_separator('\\', path::Style::windows));
EXPECT_FALSE(path::is_separator('\\', path::Style::posix));
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
EXPECT_TRUE(path::is_separator('\\'));
#else
EXPECT_FALSE(path::is_separator('\\'));
@@ -78,6 +95,7 @@ TEST(Support, Path) {
paths.push_back("foo/bar");
paths.push_back("/foo/bar");
paths.push_back("//net");
+ paths.push_back("//net/");
paths.push_back("//net/foo");
paths.push_back("///foo///");
paths.push_back("///foo///bar");
@@ -108,27 +126,30 @@ TEST(Support, Path) {
paths.push_back("c:\\foo/");
paths.push_back("c:/foo\\bar");
- SmallVector<StringRef, 5> ComponentStack;
for (SmallVector<StringRef, 40>::const_iterator i = paths.begin(),
e = paths.end();
i != e;
++i) {
+ SCOPED_TRACE(*i);
+ SmallVector<StringRef, 5> ComponentStack;
for (sys::path::const_iterator ci = sys::path::begin(*i),
ce = sys::path::end(*i);
ci != ce;
++ci) {
- ASSERT_FALSE(ci->empty());
+ EXPECT_FALSE(ci->empty());
ComponentStack.push_back(*ci);
}
+ SmallVector<StringRef, 5> ReverseComponentStack;
for (sys::path::reverse_iterator ci = sys::path::rbegin(*i),
ce = sys::path::rend(*i);
ci != ce;
++ci) {
- ASSERT_TRUE(*ci == ComponentStack.back());
- ComponentStack.pop_back();
+ EXPECT_FALSE(ci->empty());
+ ReverseComponentStack.push_back(*ci);
}
- ASSERT_TRUE(ComponentStack.empty());
+ std::reverse(ReverseComponentStack.begin(), ReverseComponentStack.end());
+ EXPECT_THAT(ComponentStack, testing::ContainerEq(ReverseComponentStack));
// Crash test most of the API - since we're iterating over all of our paths
// here there isn't really anything reasonable to assert on in the results.
@@ -171,115 +192,77 @@ TEST(Support, Path) {
ASSERT_EQ("/root/foo.cpp", Relative);
}
-TEST(Support, RelativePathIterator) {
- SmallString<64> Path(StringRef("c/d/e/foo.txt"));
- typedef SmallVector<StringRef, 4> PathComponents;
- PathComponents ExpectedPathComponents;
- PathComponents ActualPathComponents;
+TEST(Support, FilenameParent) {
+ EXPECT_EQ("/", path::filename("/"));
+ EXPECT_EQ("", path::parent_path("/"));
- StringRef(Path).split(ExpectedPathComponents, '/');
+ EXPECT_EQ("\\", path::filename("c:\\", path::Style::windows));
+ EXPECT_EQ("c:", path::parent_path("c:\\", path::Style::windows));
- for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E;
- ++I) {
- ActualPathComponents.push_back(*I);
- }
+ EXPECT_EQ("/", path::filename("///"));
+ EXPECT_EQ("", path::parent_path("///"));
- ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size());
+ EXPECT_EQ("\\", path::filename("c:\\\\", path::Style::windows));
+ EXPECT_EQ("c:", path::parent_path("c:\\\\", path::Style::windows));
- for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) {
- EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str());
- }
-}
-
-TEST(Support, RelativePathDotIterator) {
- SmallString<64> Path(StringRef(".c/.d/../."));
- typedef SmallVector<StringRef, 4> PathComponents;
- PathComponents ExpectedPathComponents;
- PathComponents ActualPathComponents;
-
- StringRef(Path).split(ExpectedPathComponents, '/');
+ EXPECT_EQ("bar", path::filename("/foo/bar"));
+ EXPECT_EQ("/foo", path::parent_path("/foo/bar"));
- for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E;
- ++I) {
- ActualPathComponents.push_back(*I);
- }
+ EXPECT_EQ("foo", path::filename("/foo"));
+ EXPECT_EQ("/", path::parent_path("/foo"));
- ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size());
+ EXPECT_EQ("foo", path::filename("foo"));
+ EXPECT_EQ("", path::parent_path("foo"));
- for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) {
- EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str());
- }
-}
+ EXPECT_EQ(".", path::filename("foo/"));
+ EXPECT_EQ("foo", path::parent_path("foo/"));
-TEST(Support, AbsolutePathIterator) {
- SmallString<64> Path(StringRef("/c/d/e/foo.txt"));
- typedef SmallVector<StringRef, 4> PathComponents;
- PathComponents ExpectedPathComponents;
- PathComponents ActualPathComponents;
+ EXPECT_EQ("//net", path::filename("//net"));
+ EXPECT_EQ("", path::parent_path("//net"));
- StringRef(Path).split(ExpectedPathComponents, '/');
+ EXPECT_EQ("/", path::filename("//net/"));
+ EXPECT_EQ("//net", path::parent_path("//net/"));
- // The root path will also be a component when iterating
- ExpectedPathComponents[0] = "/";
+ EXPECT_EQ("foo", path::filename("//net/foo"));
+ EXPECT_EQ("//net/", path::parent_path("//net/foo"));
- for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E;
- ++I) {
- ActualPathComponents.push_back(*I);
- }
+ // These checks are just to make sure we do something reasonable with the
+ // paths below. They are not meant to prescribe the one true interpretation of
+ // these paths. Other decompositions (e.g. "//" -> "" + "//") are also
+ // possible.
+ EXPECT_EQ("/", path::filename("//"));
+ EXPECT_EQ("", path::parent_path("//"));
- ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size());
+ EXPECT_EQ("\\", path::filename("\\\\", path::Style::windows));
+ EXPECT_EQ("", path::parent_path("\\\\", path::Style::windows));
- for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) {
- EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str());
- }
+ EXPECT_EQ("\\", path::filename("\\\\\\", path::Style::windows));
+ EXPECT_EQ("", path::parent_path("\\\\\\", path::Style::windows));
}
-TEST(Support, AbsolutePathDotIterator) {
- SmallString<64> Path(StringRef("/.c/.d/../."));
- typedef SmallVector<StringRef, 4> PathComponents;
- PathComponents ExpectedPathComponents;
- PathComponents ActualPathComponents;
-
- StringRef(Path).split(ExpectedPathComponents, '/');
-
- // The root path will also be a component when iterating
- ExpectedPathComponents[0] = "/";
-
- for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E;
- ++I) {
- ActualPathComponents.push_back(*I);
- }
-
- ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size());
-
- for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) {
- EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str());
- }
+static std::vector<StringRef>
+GetComponents(StringRef Path, path::Style S = path::Style::native) {
+ return {path::begin(Path, S), path::end(Path)};
}
-TEST(Support, AbsolutePathIteratorWin32) {
- SmallString<64> Path(StringRef("c:\\c\\e\\foo.txt"));
- typedef SmallVector<StringRef, 4> PathComponents;
- PathComponents ExpectedPathComponents;
- PathComponents ActualPathComponents;
-
- StringRef(Path).split(ExpectedPathComponents, "\\");
-
- // The root path (which comes after the drive name) will also be a component
- // when iterating.
- ExpectedPathComponents.insert(ExpectedPathComponents.begin()+1, "\\");
-
- for (path::const_iterator I = path::begin(Path, path::Style::windows),
- E = path::end(Path);
- I != E; ++I) {
- ActualPathComponents.push_back(*I);
- }
-
- ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size());
-
- for (size_t i = 0; i <ExpectedPathComponents.size(); ++i) {
- EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str());
- }
+TEST(Support, PathIterator) {
+ EXPECT_THAT(GetComponents("/foo"), testing::ElementsAre("/", "foo"));
+ EXPECT_THAT(GetComponents("/"), testing::ElementsAre("/"));
+ EXPECT_THAT(GetComponents("//"), testing::ElementsAre("/"));
+ EXPECT_THAT(GetComponents("///"), testing::ElementsAre("/"));
+ EXPECT_THAT(GetComponents("c/d/e/foo.txt"),
+ testing::ElementsAre("c", "d", "e", "foo.txt"));
+ EXPECT_THAT(GetComponents(".c/.d/../."),
+ testing::ElementsAre(".c", ".d", "..", "."));
+ EXPECT_THAT(GetComponents("/c/d/e/foo.txt"),
+ testing::ElementsAre("/", "c", "d", "e", "foo.txt"));
+ EXPECT_THAT(GetComponents("/.c/.d/../."),
+ testing::ElementsAre("/", ".c", ".d", "..", "."));
+ EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows),
+ testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));
+ EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/"));
+ EXPECT_THAT(GetComponents("//net/c/foo.txt"),
+ testing::ElementsAre("//net", "/", "c", "foo.txt"));
}
TEST(Support, AbsolutePathIteratorEnd) {
@@ -287,10 +270,11 @@ TEST(Support, AbsolutePathIteratorEnd) {
SmallVector<std::pair<StringRef, path::Style>, 4> Paths;
Paths.emplace_back("/foo/", path::Style::native);
Paths.emplace_back("/foo//", path::Style::native);
- Paths.emplace_back("//net//", path::Style::native);
- Paths.emplace_back("c:\\\\", path::Style::windows);
+ Paths.emplace_back("//net/foo/", path::Style::native);
+ Paths.emplace_back("c:\\foo\\", path::Style::windows);
for (auto &Path : Paths) {
+ SCOPED_TRACE(Path.first);
StringRef LastComponent = *path::rbegin(Path.first, Path.second);
EXPECT_EQ(".", LastComponent);
}
@@ -299,8 +283,11 @@ TEST(Support, AbsolutePathIteratorEnd) {
RootPaths.emplace_back("/", path::Style::native);
RootPaths.emplace_back("//net/", path::Style::native);
RootPaths.emplace_back("c:\\", path::Style::windows);
+ RootPaths.emplace_back("//net//", path::Style::native);
+ RootPaths.emplace_back("c:\\\\", path::Style::windows);
for (auto &Path : RootPaths) {
+ SCOPED_TRACE(Path.first);
StringRef LastComponent = *path::rbegin(Path.first, Path.second);
EXPECT_EQ(1u, LastComponent.size());
EXPECT_TRUE(path::is_separator(LastComponent[0], Path.second));
@@ -309,7 +296,7 @@ TEST(Support, AbsolutePathIteratorEnd) {
TEST(Support, HomeDirectory) {
std::string expected;
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
if (wchar_t const *path = ::_wgetenv(L"USERPROFILE")) {
auto pathLen = ::wcslen(path);
ArrayRef<char> ref{reinterpret_cast<char const *>(path),
@@ -398,7 +385,7 @@ TEST(Support, TempDirectory) {
EXPECT_TRUE(!TempDir.empty());
}
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
static std::string path2regex(std::string Path) {
size_t Pos = 0;
while ((Pos = Path.find('\\', Pos)) != std::string::npos) {
@@ -464,6 +451,7 @@ protected:
/// Unique temporary directory in which all created filesystem entities must
/// be placed. It is removed at the end of each test (must be empty).
SmallString<128> TestDirectory;
+ SmallString<128> NonExistantFile;
void SetUp() override {
ASSERT_NO_ERROR(
@@ -471,6 +459,11 @@ protected:
// We don't care about this specific file.
errs() << "Test Directory: " << TestDirectory << '\n';
errs().flush();
+ NonExistantFile = TestDirectory;
+
+ // Even though this value is hardcoded, is a 128-bit GUID, so we should be
+ // guaranteed that this file will never exist.
+ sys::path::append(NonExistantFile, "1B28B495C16344CB9822E588CD4C3EF0");
}
void TearDown() override { ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); }
@@ -564,6 +557,25 @@ TEST_F(FileSystemTest, RealPath) {
ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/test1"));
}
+#ifdef LLVM_ON_UNIX
+TEST_F(FileSystemTest, RealPathNoReadPerm) {
+ SmallString<64> Expanded;
+
+ ASSERT_NO_ERROR(
+ fs::create_directories(Twine(TestDirectory) + "/noreadperm"));
+ ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/noreadperm"));
+
+ fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::no_perms);
+ fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::all_exe);
+
+ ASSERT_NO_ERROR(fs::real_path(Twine(TestDirectory) + "/noreadperm", Expanded,
+ false));
+
+ ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/noreadperm"));
+}
+#endif
+
+
TEST_F(FileSystemTest, TempFileKeepDiscard) {
// We can keep then discard.
auto TempFileOrError = fs::TempFile::create(TestDirectory + "/test-%%%%");
@@ -648,7 +660,7 @@ TEST_F(FileSystemTest, TempFiles) {
ASSERT_EQ(fs::access(Twine(TempPath), sys::fs::AccessMode::Exist),
errc::no_such_file_or_directory);
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
// Path name > 260 chars should get an error.
const char *Path270 =
"abcdefghijklmnopqrstuvwxyz9abcdefghijklmnopqrstuvwxyz8"
@@ -696,7 +708,7 @@ TEST_F(FileSystemTest, CreateDir) {
::umask(OldUmask);
#endif
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
// Prove that create_directories() can handle a pathname > 248 characters,
// which is the documented limit for CreateDirectory().
// (248 is MAX_PATH subtracting room for an 8.3 filename.)
@@ -866,58 +878,91 @@ TEST_F(FileSystemTest, BrokenSymlinkDirectoryIteration) {
fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/e"));
typedef std::vector<std::string> v_t;
- v_t visited;
-
- // The directory iterator doesn't stat the file, so we should be able to
- // iterate over the whole directory.
+ v_t VisitedNonBrokenSymlinks;
+ v_t VisitedBrokenSymlinks;
std::error_code ec;
+
+ // Broken symbol links are expected to throw an error.
for (fs::directory_iterator i(Twine(TestDirectory) + "/symlink", ec), e;
i != e; i.increment(ec)) {
+ if (ec == std::make_error_code(std::errc::no_such_file_or_directory)) {
+ VisitedBrokenSymlinks.push_back(path::filename(i->path()));
+ continue;
+ }
+
ASSERT_NO_ERROR(ec);
- visited.push_back(path::filename(i->path()));
+ VisitedNonBrokenSymlinks.push_back(path::filename(i->path()));
}
- std::sort(visited.begin(), visited.end());
- v_t expected = {"a", "b", "c", "d", "e"};
- ASSERT_TRUE(visited.size() == expected.size());
- ASSERT_TRUE(std::equal(visited.begin(), visited.end(), expected.begin()));
- visited.clear();
-
- // The recursive directory iterator has to stat the file, so we need to skip
- // the broken symlinks.
- for (fs::recursive_directory_iterator
- i(Twine(TestDirectory) + "/symlink", ec),
- e;
- i != e; i.increment(ec)) {
- ASSERT_NO_ERROR(ec);
-
- ErrorOr<fs::basic_file_status> status = i->status();
- if (status.getError() ==
- std::make_error_code(std::errc::no_such_file_or_directory)) {
- i.no_push();
+ llvm::sort(VisitedNonBrokenSymlinks.begin(), VisitedNonBrokenSymlinks.end());
+ llvm::sort(VisitedBrokenSymlinks.begin(), VisitedBrokenSymlinks.end());
+ v_t ExpectedNonBrokenSymlinks = {"b", "d"};
+ ASSERT_EQ(ExpectedNonBrokenSymlinks.size(), VisitedNonBrokenSymlinks.size());
+ ASSERT_TRUE(std::equal(VisitedNonBrokenSymlinks.begin(),
+ VisitedNonBrokenSymlinks.end(),
+ ExpectedNonBrokenSymlinks.begin()));
+ VisitedNonBrokenSymlinks.clear();
+
+ v_t ExpectedBrokenSymlinks = {"a", "c", "e"};
+ ASSERT_EQ(ExpectedBrokenSymlinks.size(), VisitedBrokenSymlinks.size());
+ ASSERT_TRUE(std::equal(VisitedBrokenSymlinks.begin(),
+ VisitedBrokenSymlinks.end(),
+ ExpectedBrokenSymlinks.begin()));
+ VisitedBrokenSymlinks.clear();
+
+ // Broken symbol links are expected to throw an error.
+ for (fs::recursive_directory_iterator i(
+ Twine(TestDirectory) + "/symlink", ec), e; i != e; i.increment(ec)) {
+ if (ec == std::make_error_code(std::errc::no_such_file_or_directory)) {
+ VisitedBrokenSymlinks.push_back(path::filename(i->path()));
continue;
}
- visited.push_back(path::filename(i->path()));
+ ASSERT_NO_ERROR(ec);
+ VisitedNonBrokenSymlinks.push_back(path::filename(i->path()));
}
- std::sort(visited.begin(), visited.end());
- expected = {"b", "bb", "d", "da", "dd", "ddd", "ddd"};
- ASSERT_TRUE(visited.size() == expected.size());
- ASSERT_TRUE(std::equal(visited.begin(), visited.end(), expected.begin()));
- visited.clear();
-
- // This recursive directory iterator doesn't follow symlinks, so we don't need
- // to skip them.
- for (fs::recursive_directory_iterator
- i(Twine(TestDirectory) + "/symlink", ec, /*follow_symlinks=*/false),
- e;
+ llvm::sort(VisitedNonBrokenSymlinks.begin(), VisitedNonBrokenSymlinks.end());
+ llvm::sort(VisitedBrokenSymlinks.begin(), VisitedBrokenSymlinks.end());
+ ExpectedNonBrokenSymlinks = {"b", "bb", "d", "da", "dd", "ddd", "ddd"};
+ ASSERT_EQ(ExpectedNonBrokenSymlinks.size(), VisitedNonBrokenSymlinks.size());
+ ASSERT_TRUE(std::equal(VisitedNonBrokenSymlinks.begin(),
+ VisitedNonBrokenSymlinks.end(),
+ ExpectedNonBrokenSymlinks.begin()));
+ VisitedNonBrokenSymlinks.clear();
+
+ ExpectedBrokenSymlinks = {"a", "ba", "bc", "c", "e"};
+ ASSERT_EQ(ExpectedBrokenSymlinks.size(), VisitedBrokenSymlinks.size());
+ ASSERT_TRUE(std::equal(VisitedBrokenSymlinks.begin(),
+ VisitedBrokenSymlinks.end(),
+ ExpectedBrokenSymlinks.begin()));
+ VisitedBrokenSymlinks.clear();
+
+ for (fs::recursive_directory_iterator i(
+ Twine(TestDirectory) + "/symlink", ec, /*follow_symlinks=*/false), e;
i != e; i.increment(ec)) {
+ if (ec == std::make_error_code(std::errc::no_such_file_or_directory)) {
+ VisitedBrokenSymlinks.push_back(path::filename(i->path()));
+ continue;
+ }
+
ASSERT_NO_ERROR(ec);
- visited.push_back(path::filename(i->path()));
+ VisitedNonBrokenSymlinks.push_back(path::filename(i->path()));
}
- std::sort(visited.begin(), visited.end());
- expected = {"a", "b", "ba", "bb", "bc", "c", "d", "da", "dd", "ddd", "e"};
- ASSERT_TRUE(visited.size() == expected.size());
- ASSERT_TRUE(std::equal(visited.begin(), visited.end(), expected.begin()));
+ llvm::sort(VisitedNonBrokenSymlinks.begin(), VisitedNonBrokenSymlinks.end());
+ llvm::sort(VisitedBrokenSymlinks.begin(), VisitedBrokenSymlinks.end());
+ ExpectedNonBrokenSymlinks = {"a", "b", "ba", "bb", "bc", "c", "d", "da", "dd",
+ "ddd", "e"};
+ ASSERT_EQ(ExpectedNonBrokenSymlinks.size(), VisitedNonBrokenSymlinks.size());
+ ASSERT_TRUE(std::equal(VisitedNonBrokenSymlinks.begin(),
+ VisitedNonBrokenSymlinks.end(),
+ ExpectedNonBrokenSymlinks.begin()));
+ VisitedNonBrokenSymlinks.clear();
+
+ ExpectedBrokenSymlinks = {};
+ ASSERT_EQ(ExpectedBrokenSymlinks.size(), VisitedBrokenSymlinks.size());
+ ASSERT_TRUE(std::equal(VisitedBrokenSymlinks.begin(),
+ VisitedBrokenSymlinks.end(),
+ ExpectedBrokenSymlinks.begin()));
+ VisitedBrokenSymlinks.clear();
ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/symlink"));
}
@@ -956,7 +1001,7 @@ TEST_F(FileSystemTest, Remove) {
ASSERT_FALSE(fs::exists(BaseDir));
}
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
TEST_F(FileSystemTest, CarriageReturn) {
SmallString<128> FilePathname(TestDirectory);
std::error_code EC;
@@ -1074,7 +1119,7 @@ TEST(Support, NormalizePath) {
EXPECT_EQ(std::get<2>(T), Posix);
}
-#if defined(LLVM_ON_WIN32)
+#if defined(_WIN32)
SmallString<64> PathHome;
path::home_directory(PathHome);
@@ -1195,8 +1240,8 @@ TEST_F(FileSystemTest, OpenFileForRead) {
// Open the file for read
int FileDescriptor2;
SmallString<64> ResultPath;
- ASSERT_NO_ERROR(
- fs::openFileForRead(Twine(TempPath), FileDescriptor2, &ResultPath))
+ ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor2,
+ fs::OF_None, &ResultPath))
// If we succeeded, check that the paths are the same (modulo case):
if (!ResultPath.empty()) {
@@ -1207,8 +1252,241 @@ TEST_F(FileSystemTest, OpenFileForRead) {
ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
ASSERT_EQ(D1, D2);
}
+ ::close(FileDescriptor);
+ ::close(FileDescriptor2);
+
+#ifdef _WIN32
+ // Since Windows Vista, file access time is not updated by default.
+ // This is instead updated manually by openFileForRead.
+ // https://blogs.technet.microsoft.com/filecab/2006/11/07/disabling-last-access-time-in-windows-vista-to-improve-ntfs-performance/
+ // This part of the unit test is Windows specific as the updating of
+ // access times can be disabled on Linux using /etc/fstab.
+
+ // Set access time to UNIX epoch.
+ ASSERT_NO_ERROR(sys::fs::openFileForWrite(Twine(TempPath), FileDescriptor,
+ fs::CD_OpenExisting));
+ TimePoint<> Epoch(std::chrono::milliseconds(0));
+ ASSERT_NO_ERROR(fs::setLastModificationAndAccessTime(FileDescriptor, Epoch));
+ ::close(FileDescriptor);
+
+ // Open the file and ensure access time is updated, when forced.
+ ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor,
+ fs::OF_UpdateAtime, &ResultPath));
+
+ sys::fs::file_status Status;
+ ASSERT_NO_ERROR(sys::fs::status(FileDescriptor, Status));
+ auto FileAccessTime = Status.getLastAccessedTime();
+ ASSERT_NE(Epoch, FileAccessTime);
::close(FileDescriptor);
+
+ // Ideally this test would include a case when ATime is not forced to update,
+ // however the expected behaviour will differ depending on the configuration
+ // of the Windows file system.
+#endif
+}
+
+static void createFileWithData(const Twine &Path, bool ShouldExistBefore,
+ fs::CreationDisposition Disp, StringRef Data) {
+ int FD;
+ ASSERT_EQ(ShouldExistBefore, fs::exists(Path));
+ ASSERT_NO_ERROR(fs::openFileForWrite(Path, FD, Disp));
+ FileDescriptorCloser Closer(FD);
+ ASSERT_TRUE(fs::exists(Path));
+
+ ASSERT_EQ(Data.size(), (size_t)write(FD, Data.data(), Data.size()));
+}
+
+static void verifyFileContents(const Twine &Path, StringRef Contents) {
+ auto Buffer = MemoryBuffer::getFile(Path);
+ ASSERT_TRUE((bool)Buffer);
+ StringRef Data = Buffer.get()->getBuffer();
+ ASSERT_EQ(Data, Contents);
+}
+
+TEST_F(FileSystemTest, CreateNew) {
+ int FD;
+ Optional<FileDescriptorCloser> Closer;
+
+ // Succeeds if the file does not exist.
+ ASSERT_FALSE(fs::exists(NonExistantFile));
+ ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));
+ ASSERT_TRUE(fs::exists(NonExistantFile));
+
+ FileRemover Cleanup(NonExistantFile);
+ Closer.emplace(FD);
+
+ // And creates a file of size 0.
+ sys::fs::file_status Status;
+ ASSERT_NO_ERROR(sys::fs::status(FD, Status));
+ EXPECT_EQ(0ULL, Status.getSize());
+
+ // Close this first, before trying to re-open the file.
+ Closer.reset();
+
+ // But fails if the file does exist.
+ ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));
+}
+
+TEST_F(FileSystemTest, CreateAlways) {
+ int FD;
+ Optional<FileDescriptorCloser> Closer;
+
+ // Succeeds if the file does not exist.
+ ASSERT_FALSE(fs::exists(NonExistantFile));
+ ASSERT_NO_ERROR(
+ fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));
+
+ Closer.emplace(FD);
+
+ ASSERT_TRUE(fs::exists(NonExistantFile));
+
+ FileRemover Cleanup(NonExistantFile);
+
+ // And creates a file of size 0.
+ uint64_t FileSize;
+ ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
+ ASSERT_EQ(0ULL, FileSize);
+
+ // If we write some data to it re-create it with CreateAlways, it succeeds and
+ // truncates to 0 bytes.
+ ASSERT_EQ(4, write(FD, "Test", 4));
+
+ Closer.reset();
+
+ ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
+ ASSERT_EQ(4ULL, FileSize);
+
+ ASSERT_NO_ERROR(
+ fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));
+ Closer.emplace(FD);
+ ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
+ ASSERT_EQ(0ULL, FileSize);
+}
+
+TEST_F(FileSystemTest, OpenExisting) {
+ int FD;
+
+ // Fails if the file does not exist.
+ ASSERT_FALSE(fs::exists(NonExistantFile));
+ ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));
+ ASSERT_FALSE(fs::exists(NonExistantFile));
+
+ // Make a dummy file now so that we can try again when the file does exist.
+ createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
+ FileRemover Cleanup(NonExistantFile);
+ uint64_t FileSize;
+ ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
+ ASSERT_EQ(4ULL, FileSize);
+
+ // If we re-create it with different data, it overwrites rather than
+ // appending.
+ createFileWithData(NonExistantFile, true, fs::CD_OpenExisting, "Buzz");
+ verifyFileContents(NonExistantFile, "Buzz");
+}
+
+TEST_F(FileSystemTest, OpenAlways) {
+ // Succeeds if the file does not exist.
+ createFileWithData(NonExistantFile, false, fs::CD_OpenAlways, "Fizz");
+ FileRemover Cleanup(NonExistantFile);
+ uint64_t FileSize;
+ ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
+ ASSERT_EQ(4ULL, FileSize);
+
+ // Now re-open it and write again, verifying the contents get over-written.
+ createFileWithData(NonExistantFile, true, fs::CD_OpenAlways, "Bu");
+ verifyFileContents(NonExistantFile, "Buzz");
+}
+
+TEST_F(FileSystemTest, AppendSetsCorrectFileOffset) {
+ fs::CreationDisposition Disps[] = {fs::CD_CreateAlways, fs::CD_OpenAlways,
+ fs::CD_OpenExisting};
+
+ // Write some data and re-open it with every possible disposition (this is a
+ // hack that shouldn't work, but is left for compatibility. F_Append
+ // overrides
+ // the specified disposition.
+ for (fs::CreationDisposition Disp : Disps) {
+ int FD;
+ Optional<FileDescriptorCloser> Closer;
+
+ createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
+
+ FileRemover Cleanup(NonExistantFile);
+
+ uint64_t FileSize;
+ ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
+ ASSERT_EQ(4ULL, FileSize);
+ ASSERT_NO_ERROR(
+ fs::openFileForWrite(NonExistantFile, FD, Disp, fs::OF_Append));
+ Closer.emplace(FD);
+ ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
+ ASSERT_EQ(4ULL, FileSize);
+
+ ASSERT_EQ(4, write(FD, "Buzz", 4));
+ Closer.reset();
+
+ verifyFileContents(NonExistantFile, "FizzBuzz");
+ }
+}
+
+static void verifyRead(int FD, StringRef Data, bool ShouldSucceed) {
+ std::vector<char> Buffer;
+ Buffer.resize(Data.size());
+ int Result = ::read(FD, Buffer.data(), Buffer.size());
+ if (ShouldSucceed) {
+ ASSERT_EQ((size_t)Result, Data.size());
+ ASSERT_EQ(Data, StringRef(Buffer.data(), Buffer.size()));
+ } else {
+ ASSERT_EQ(-1, Result);
+ ASSERT_EQ(EBADF, errno);
+ }
+}
+
+static void verifyWrite(int FD, StringRef Data, bool ShouldSucceed) {
+ int Result = ::write(FD, Data.data(), Data.size());
+ if (ShouldSucceed)
+ ASSERT_EQ((size_t)Result, Data.size());
+ else {
+ ASSERT_EQ(-1, Result);
+ ASSERT_EQ(EBADF, errno);
+ }
+}
+
+TEST_F(FileSystemTest, ReadOnlyFileCantWrite) {
+ createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
+ FileRemover Cleanup(NonExistantFile);
+
+ int FD;
+ ASSERT_NO_ERROR(fs::openFileForRead(NonExistantFile, FD));
+ FileDescriptorCloser Closer(FD);
+
+ verifyWrite(FD, "Buzz", false);
+ verifyRead(FD, "Fizz", true);
+}
+
+TEST_F(FileSystemTest, WriteOnlyFileCantRead) {
+ createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
+ FileRemover Cleanup(NonExistantFile);
+
+ int FD;
+ ASSERT_NO_ERROR(
+ fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));
+ FileDescriptorCloser Closer(FD);
+ verifyRead(FD, "Fizz", false);
+ verifyWrite(FD, "Buzz", true);
+}
+
+TEST_F(FileSystemTest, ReadWriteFileCanReadOrWrite) {
+ createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
+ FileRemover Cleanup(NonExistantFile);
+
+ int FD;
+ ASSERT_NO_ERROR(fs::openFileForReadWrite(NonExistantFile, FD,
+ fs::CD_OpenExisting, fs::OF_None));
+ FileDescriptorCloser Closer(FD);
+ verifyRead(FD, "Fizz", true);
+ verifyWrite(FD, "Buzz", true);
}
TEST_F(FileSystemTest, set_current_path) {
@@ -1254,7 +1532,7 @@ TEST_F(FileSystemTest, permissions) {
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::all_exe), NoError);
EXPECT_TRUE(CheckPermissions(fs::all_read | fs::all_exe));
-#if defined(LLVM_ON_WIN32)
+#if defined(_WIN32)
fs::perms ReadOnly = fs::all_read | fs::all_exe;
EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError);
EXPECT_TRUE(CheckPermissions(ReadOnly));
diff --git a/unittests/Support/ProcessTest.cpp b/unittests/Support/ProcessTest.cpp
index 37587bf47996..c6ba35fd9415 100644
--- a/unittests/Support/ProcessTest.cpp
+++ b/unittests/Support/ProcessTest.cpp
@@ -10,7 +10,7 @@
#include "llvm/Support/Process.h"
#include "gtest/gtest.h"
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
#include <windows.h>
#endif
@@ -45,7 +45,7 @@ TEST(ProcessTest, None) {
}
#endif
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
TEST(ProcessTest, EmptyVal) {
SetEnvironmentVariableA("__LLVM_TEST_ENVIRON_VAR__", "");
diff --git a/unittests/Support/ProgramTest.cpp b/unittests/Support/ProgramTest.cpp
index 3c272bb980c5..ec1c85a9f25a 100644
--- a/unittests/Support/ProgramTest.cpp
+++ b/unittests/Support/ProgramTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/Program.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/FileSystem.h"
@@ -26,7 +27,7 @@ extern char **environ;
void sleep_for(unsigned int seconds) {
sleep(seconds);
}
-#elif defined(LLVM_ON_WIN32)
+#elif defined(_WIN32)
#include <windows.h>
void sleep_for(unsigned int seconds) {
Sleep(seconds * 1000);
@@ -59,13 +60,13 @@ static cl::opt<std::string>
ProgramTestStringArg2("program-test-string-arg2");
class ProgramEnvTest : public testing::Test {
- std::vector<const char *> EnvTable;
+ std::vector<StringRef> EnvTable;
std::vector<std::string> EnvStorage;
protected:
void SetUp() override {
auto EnvP = [] {
-#if defined(LLVM_ON_WIN32)
+#if defined(_WIN32)
_wgetenv(L"TMP"); // Populate _wenviron, initially is null
return _wenviron;
#elif defined(__APPLE__)
@@ -76,8 +77,8 @@ protected:
}();
ASSERT_TRUE(EnvP);
- auto prepareEnvVar = [this](decltype(*EnvP) Var) {
-#if defined(LLVM_ON_WIN32)
+ auto prepareEnvVar = [this](decltype(*EnvP) Var) -> StringRef {
+#if defined(_WIN32)
// On Windows convert UTF16 encoded variable to UTF8
auto Len = wcslen(Var);
ArrayRef<char> Ref{reinterpret_cast<char const *>(Var),
@@ -85,10 +86,10 @@ protected:
EnvStorage.emplace_back();
auto convStatus = convertUTF16ToUTF8String(Ref, EnvStorage.back());
EXPECT_TRUE(convStatus);
- return EnvStorage.back().c_str();
+ return EnvStorage.back();
#else
(void)this;
- return Var;
+ return StringRef(Var);
#endif
};
@@ -103,19 +104,12 @@ protected:
EnvStorage.clear();
}
- void addEnvVar(const char *Var) {
- ASSERT_TRUE(EnvTable.empty() || EnvTable.back()) << "Env table sealed";
- EnvTable.emplace_back(Var);
- }
+ void addEnvVar(StringRef Var) { EnvTable.emplace_back(Var); }
- const char **getEnviron() {
- if (EnvTable.back() != nullptr)
- EnvTable.emplace_back(nullptr); // Seal table.
- return &EnvTable[0];
- }
+ ArrayRef<StringRef> getEnviron() const { return EnvTable; }
};
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
TEST_F(ProgramEnvTest, CreateProcessLongPath) {
if (getenv("LLVM_PROGRAM_TEST_LONG_PATH"))
exit(0);
@@ -128,11 +122,8 @@ TEST_F(ProgramEnvTest, CreateProcessLongPath) {
MyExe.append("\\\\?\\");
MyExe.append(MyAbsExe);
- const char *ArgV[] = {
- MyExe.c_str(),
- "--gtest_filter=ProgramEnvTest.CreateProcessLongPath",
- nullptr
- };
+ StringRef ArgV[] = {MyExe,
+ "--gtest_filter=ProgramEnvTest.CreateProcessLongPath"};
// Add LLVM_PROGRAM_TEST_LONG_PATH to the environment of the child.
addEnvVar("LLVM_PROGRAM_TEST_LONG_PATH=1");
@@ -172,13 +163,13 @@ TEST_F(ProgramEnvTest, CreateProcessTrailingSlash) {
std::string my_exe =
sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
- const char *argv[] = {
- my_exe.c_str(),
- "--gtest_filter=ProgramEnvTest.CreateProcessTrailingSlash",
- "-program-test-string-arg1", "has\\\\ trailing\\",
- "-program-test-string-arg2", "has\\\\ trailing\\",
- nullptr
- };
+ StringRef argv[] = {
+ my_exe,
+ "--gtest_filter=ProgramEnvTest.CreateProcessTrailingSlash",
+ "-program-test-string-arg1",
+ "has\\\\ trailing\\",
+ "-program-test-string-arg2",
+ "has\\\\ trailing\\"};
// Add LLVM_PROGRAM_TEST_CHILD to the environment of the child.
addEnvVar("LLVM_PROGRAM_TEST_CHILD=1");
@@ -186,7 +177,7 @@ TEST_F(ProgramEnvTest, CreateProcessTrailingSlash) {
std::string error;
bool ExecutionFailed;
// Redirect stdout and stdin to NUL, but let stderr through.
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
StringRef nul("NUL");
#else
StringRef nul("/dev/null");
@@ -209,11 +200,8 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) {
std::string Executable =
sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
- const char *argv[] = {
- Executable.c_str(),
- "--gtest_filter=ProgramEnvTest.TestExecuteNoWait",
- nullptr
- };
+ StringRef argv[] = {Executable,
+ "--gtest_filter=ProgramEnvTest.TestExecuteNoWait"};
// Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child.
addEnvVar("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1");
@@ -267,11 +255,8 @@ TEST_F(ProgramEnvTest, TestExecuteAndWaitTimeout) {
std::string Executable =
sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
- const char *argv[] = {
- Executable.c_str(),
- "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitTimeout",
- nullptr
- };
+ StringRef argv[] = {
+ Executable, "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitTimeout"};
// Add LLVM_PROGRAM_TEST_TIMEOUT to the environment of the child.
addEnvVar("LLVM_PROGRAM_TEST_TIMEOUT=1");
@@ -286,12 +271,12 @@ TEST_F(ProgramEnvTest, TestExecuteAndWaitTimeout) {
TEST(ProgramTest, TestExecuteNegative) {
std::string Executable = "i_dont_exist";
- const char *argv[] = { Executable.c_str(), nullptr };
+ StringRef argv[] = {Executable};
{
std::string Error;
bool ExecutionFailed;
- int RetCode = ExecuteAndWait(Executable, argv, nullptr, {}, 0, 0, &Error,
+ int RetCode = ExecuteAndWait(Executable, argv, llvm::None, {}, 0, 0, &Error,
&ExecutionFailed);
ASSERT_TRUE(RetCode < 0) << "On error ExecuteAndWait should return 0 or "
"positive value indicating the result code";
@@ -302,7 +287,7 @@ TEST(ProgramTest, TestExecuteNegative) {
{
std::string Error;
bool ExecutionFailed;
- ProcessInfo PI = ExecuteNoWait(Executable, argv, nullptr, {}, 0, &Error,
+ ProcessInfo PI = ExecuteNoWait(Executable, argv, llvm::None, {}, 0, &Error,
&ExecutionFailed);
ASSERT_EQ(PI.Pid, ProcessInfo::InvalidPid)
<< "On error ExecuteNoWait should return an invalid ProcessInfo";
@@ -312,7 +297,7 @@ TEST(ProgramTest, TestExecuteNegative) {
}
-#ifdef LLVM_ON_WIN32
+#ifdef _WIN32
const char utf16le_text[] =
"\x6c\x00\x69\x00\x6e\x00\x67\x00\xfc\x00\x69\x00\xe7\x00\x61\x00";
const char utf16be_text[] =
@@ -332,7 +317,7 @@ TEST(ProgramTest, TestWriteWithSystemEncoding) {
sys::WEM_UTF16));
int fd = 0;
ASSERT_NO_ERROR(fs::openFileForRead(file_pathname.c_str(), fd));
-#if defined(LLVM_ON_WIN32)
+#if defined(_WIN32)
char buf[18];
ASSERT_EQ(::read(fd, buf, 18), 18);
if (strncmp(buf, "\xfe\xff", 2) == 0) { // UTF16-BE
diff --git a/unittests/Support/ReplaceFileTest.cpp b/unittests/Support/ReplaceFileTest.cpp
index 794f36b1f654..15143be794fc 100644
--- a/unittests/Support/ReplaceFileTest.cpp
+++ b/unittests/Support/ReplaceFileTest.cpp
@@ -31,7 +31,7 @@ namespace {
std::error_code CreateFileWithContent(const SmallString<128> &FilePath,
const StringRef &content) {
int FD = 0;
- if (std::error_code ec = fs::openFileForWrite(FilePath, FD, fs::F_None))
+ if (std::error_code ec = fs::openFileForWrite(FilePath, FD))
return ec;
const bool ShouldClose = true;
diff --git a/unittests/Support/SourceMgrTest.cpp b/unittests/Support/SourceMgrTest.cpp
index 2a84a89912ad..7bb76f556f70 100644
--- a/unittests/Support/SourceMgrTest.cpp
+++ b/unittests/Support/SourceMgrTest.cpp
@@ -107,6 +107,320 @@ TEST_F(SourceMgrTest, LocationAtNewline) {
Output);
}
+TEST_F(SourceMgrTest, LocationAtEmptyBuffer) {
+ setMainBuffer("", "file.in");
+ printMessage(getLoc(0), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:1:1: error: message\n"
+ "\n"
+ "^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationJustOnSoleNewline) {
+ setMainBuffer("\n", "file.in");
+ printMessage(getLoc(0), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:1:1: error: message\n"
+ "\n"
+ "^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationJustAfterSoleNewline) {
+ setMainBuffer("\n", "file.in");
+ printMessage(getLoc(1), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:2:1: error: message\n"
+ "\n"
+ "^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationJustAfterNonNewline) {
+ setMainBuffer("123", "file.in");
+ printMessage(getLoc(3), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:1:4: error: message\n"
+ "123\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationOnFirstLineOfMultiline) {
+ setMainBuffer("1234\n6789\n", "file.in");
+ printMessage(getLoc(3), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:1:4: error: message\n"
+ "1234\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationOnEOLOfFirstLineOfMultiline) {
+ setMainBuffer("1234\n6789\n", "file.in");
+ printMessage(getLoc(4), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:1:5: error: message\n"
+ "1234\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationOnSecondLineOfMultiline) {
+ setMainBuffer("1234\n6789\n", "file.in");
+ printMessage(getLoc(5), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:2:1: error: message\n"
+ "6789\n"
+ "^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationOnSecondLineOfMultilineNoSecondEOL) {
+ setMainBuffer("1234\n6789", "file.in");
+ printMessage(getLoc(5), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:2:1: error: message\n"
+ "6789\n"
+ "^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationOnEOLOfSecondSecondLineOfMultiline) {
+ setMainBuffer("1234\n6789\n", "file.in");
+ printMessage(getLoc(9), SourceMgr::DK_Error, "message", None, None);
+
+ EXPECT_EQ("file.in:2:5: error: message\n"
+ "6789\n"
+ " ^\n",
+ Output);
+}
+
+#define STRING_LITERAL_253_BYTES \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n1234567890\n" \
+ "1234567890\n"
+
+//===----------------------------------------------------------------------===//
+// 255-byte buffer tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(SourceMgrTest, LocationBeforeEndOf255ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "12" // + 2 = 255 bytes
+ , "file.in");
+ printMessage(getLoc(253), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:1: error: message\n"
+ "12\n"
+ "^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationAtEndOf255ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "12" // + 2 = 255 bytes
+ , "file.in");
+ printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:2: error: message\n"
+ "12\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationPastEndOf255ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "12" // + 2 = 255 bytes
+ , "file.in");
+ printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:3: error: message\n"
+ "12\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationBeforeEndOf255ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "1\n" // + 2 = 255 bytes
+ , "file.in");
+ printMessage(getLoc(253), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:1: error: message\n"
+ "1\n"
+ "^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationAtEndOf255ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "1\n" // + 2 = 255 bytes
+ , "file.in");
+ printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:2: error: message\n"
+ "1\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationPastEndOf255ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "1\n" // + 2 = 255 bytes
+ , "file.in");
+ printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:25:1: error: message\n"
+ "\n"
+ "^\n",
+ Output);
+}
+
+//===----------------------------------------------------------------------===//
+// 256-byte buffer tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(SourceMgrTest, LocationBeforeEndOf256ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "123" // + 3 = 256 bytes
+ , "file.in");
+ printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:2: error: message\n"
+ "123\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationAtEndOf256ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "123" // + 3 = 256 bytes
+ , "file.in");
+ printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:3: error: message\n"
+ "123\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationPastEndOf256ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "123" // + 3 = 256 bytes
+ , "file.in");
+ printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:4: error: message\n"
+ "123\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationBeforeEndOf256ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "12\n" // + 3 = 256 bytes
+ , "file.in");
+ printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:2: error: message\n"
+ "12\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationAtEndOf256ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "12\n" // + 3 = 256 bytes
+ , "file.in");
+ printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:3: error: message\n"
+ "12\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationPastEndOf256ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "12\n" // + 3 = 256 bytes
+ , "file.in");
+ printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:25:1: error: message\n"
+ "\n"
+ "^\n",
+ Output);
+}
+
+//===----------------------------------------------------------------------===//
+// 257-byte buffer tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(SourceMgrTest, LocationBeforeEndOf257ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "1234" // + 4 = 257 bytes
+ , "file.in");
+ printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:3: error: message\n"
+ "1234\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationAtEndOf257ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "1234" // + 4 = 257 bytes
+ , "file.in");
+ printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:4: error: message\n"
+ "1234\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationPastEndOf257ByteBuffer) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "1234" // + 4 = 257 bytes
+ , "file.in");
+ printMessage(getLoc(257), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:5: error: message\n"
+ "1234\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationBeforeEndOf257ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "123\n" // + 4 = 257 bytes
+ , "file.in");
+ printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:3: error: message\n"
+ "123\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationAtEndOf257ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "123\n" // + 4 = 257 bytes
+ , "file.in");
+ printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:24:4: error: message\n"
+ "123\n"
+ " ^\n",
+ Output);
+}
+
+TEST_F(SourceMgrTest, LocationPastEndOf257ByteBufferEndingInNewline) {
+ setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes
+ "123\n" // + 4 = 257 bytes
+ , "file.in");
+ printMessage(getLoc(257), SourceMgr::DK_Error, "message", None, None);
+ EXPECT_EQ("file.in:25:1: error: message\n"
+ "\n"
+ "^\n",
+ Output);
+}
+
TEST_F(SourceMgrTest, BasicRange) {
setMainBuffer("aaa bbb\nccc ddd\n", "file.in");
printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(4, 3), None);
diff --git a/unittests/Support/TargetParserTest.cpp b/unittests/Support/TargetParserTest.cpp
index 48fffca1aa18..fc26c985f265 100644
--- a/unittests/Support/TargetParserTest.cpp
+++ b/unittests/Support/TargetParserTest.cpp
@@ -17,17 +17,18 @@ using namespace llvm;
namespace {
const char *ARMArch[] = {
- "armv2", "armv2a", "armv3", "armv3m", "armv4",
- "armv4t", "armv5", "armv5t", "armv5e", "armv5te",
- "armv5tej", "armv6", "armv6j", "armv6k", "armv6hl",
- "armv6t2", "armv6kz", "armv6z", "armv6zk", "armv6-m",
- "armv6m", "armv6sm", "armv6s-m", "armv7-a", "armv7",
- "armv7a", "armv7ve", "armv7hl", "armv7l", "armv7-r",
- "armv7r", "armv7-m", "armv7m", "armv7k", "armv7s",
- "armv7e-m", "armv7em", "armv8-a", "armv8", "armv8a",
- "armv8.1-a", "armv8.1a", "armv8.2-a", "armv8.2a", "armv8.3-a",
- "armv8.3a", "armv8-r", "armv8r", "armv8-m.base", "armv8m.base",
- "armv8-m.main", "armv8m.main", "iwmmxt", "iwmmxt2", "xscale"};
+ "armv2", "armv2a", "armv3", "armv3m", "armv4",
+ "armv4t", "armv5", "armv5t", "armv5e", "armv5te",
+ "armv5tej", "armv6", "armv6j", "armv6k", "armv6hl",
+ "armv6t2", "armv6kz", "armv6z", "armv6zk", "armv6-m",
+ "armv6m", "armv6sm", "armv6s-m", "armv7-a", "armv7",
+ "armv7a", "armv7ve", "armv7hl", "armv7l", "armv7-r",
+ "armv7r", "armv7-m", "armv7m", "armv7k", "armv7s",
+ "armv7e-m", "armv7em", "armv8-a", "armv8", "armv8a",
+ "armv8l", "armv8.1-a", "armv8.1a", "armv8.2-a", "armv8.2a",
+ "armv8.3-a", "armv8.3a", "armv8-r", "armv8r", "armv8-m.base",
+ "armv8m.base", "armv8-m.main", "armv8m.main", "iwmmxt", "iwmmxt2",
+ "xscale"};
bool testARMCPU(StringRef CPUName, StringRef ExpectedArch,
StringRef ExpectedFPU, unsigned ExpectedFlags,
@@ -252,18 +253,23 @@ TEST(TargetParserTest, testARMCPU) {
"8-A"));
EXPECT_TRUE(testARMCPU("exynos-m1", "armv8-a", "crypto-neon-fp-armv8",
ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |
- ARM::AEK_VIRT | ARM::AEK_HWDIVARM |
- ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,
+ ARM::AEK_VIRT | ARM::AEK_HWDIVARM |
+ ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,
"8-A"));
EXPECT_TRUE(testARMCPU("exynos-m2", "armv8-a", "crypto-neon-fp-armv8",
ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |
- ARM::AEK_VIRT | ARM::AEK_HWDIVARM |
- ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,
+ ARM::AEK_VIRT | ARM::AEK_HWDIVARM |
+ ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,
"8-A"));
EXPECT_TRUE(testARMCPU("exynos-m3", "armv8-a", "crypto-neon-fp-armv8",
ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |
- ARM::AEK_VIRT | ARM::AEK_HWDIVARM |
- ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,
+ ARM::AEK_VIRT | ARM::AEK_HWDIVARM |
+ ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,
+ "8-A"));
+ EXPECT_TRUE(testARMCPU("exynos-m4", "armv8-a", "crypto-neon-fp-armv8",
+ ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |
+ ARM::AEK_VIRT | ARM::AEK_HWDIVARM |
+ ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,
"8-A"));
EXPECT_TRUE(testARMCPU("cortex-m23", "armv8-m.base", "none",
ARM::AEK_HWDIVTHUMB, "8-M.Baseline"));
@@ -278,6 +284,20 @@ TEST(TargetParserTest, testARMCPU) {
"7-S"));
}
+static constexpr unsigned NumARMCPUArchs = 83;
+
+TEST(TargetParserTest, testARMCPUArchList) {
+ SmallVector<StringRef, NumARMCPUArchs> List;
+ ARM::fillValidCPUArchList(List);
+
+ // No list exists for these in this test suite, so ensure all are
+ // valid, and match the expected 'magic' count.
+ EXPECT_EQ(List.size(), NumARMCPUArchs);
+ for(StringRef CPU : List) {
+ EXPECT_NE(ARM::parseCPUArch(CPU), ARM::ArchKind::INVALID);
+ }
+}
+
TEST(TargetParserTest, testInvalidARMArch) {
auto InvalidArchStrings = {"armv", "armv99", "noarm"};
for (const char* InvalidArch : InvalidArchStrings)
@@ -558,12 +578,12 @@ TEST(TargetParserTest, ARMparseHWDiv) {
TEST(TargetParserTest, ARMparseArchEndianAndISA) {
const char *Arch[] = {
- "v2", "v2a", "v3", "v3m", "v4", "v4t", "v5", "v5t",
- "v5e", "v5te", "v5tej", "v6", "v6j", "v6k", "v6hl", "v6t2",
- "v6kz", "v6z", "v6zk", "v6-m", "v6m", "v6sm", "v6s-m", "v7-a",
- "v7", "v7a", "v7ve", "v7hl", "v7l", "v7-r", "v7r", "v7-m",
- "v7m", "v7k", "v7s", "v7e-m", "v7em", "v8-a", "v8", "v8a",
- "v8.1-a", "v8.1a", "v8.2-a", "v8.2a", "v8.3-a", "v8.3a", "v8-r"};
+ "v2", "v2a", "v3", "v3m", "v4", "v4t", "v5", "v5t",
+ "v5e", "v5te", "v5tej", "v6", "v6j", "v6k", "v6hl", "v6t2",
+ "v6kz", "v6z", "v6zk", "v6-m", "v6m", "v6sm", "v6s-m", "v7-a",
+ "v7", "v7a", "v7ve", "v7hl", "v7l", "v7-r", "v7r", "v7-m",
+ "v7m", "v7k", "v7s", "v7e-m", "v7em", "v8-a", "v8", "v8a",
+ "v8l", "v8.1-a", "v8.1a", "v8.2-a", "v8.2a", "v8.3-a", "v8.3a", "v8-r"};
for (unsigned i = 0; i < array_lengthof(Arch); i++) {
std::string arm_1 = "armeb" + (std::string)(Arch[i]);
@@ -713,6 +733,10 @@ TEST(TargetParserTest, testAArch64CPU) {
AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP |
AArch64::AEK_SIMD, "8-A"));
EXPECT_TRUE(testAArch64CPU(
+ "exynos-m4", "armv8-a", "crypto-neon-fp-armv8",
+ AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP |
+ AArch64::AEK_SIMD, "8-A"));
+ EXPECT_TRUE(testAArch64CPU(
"falkor", "armv8-a", "crypto-neon-fp-armv8",
AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP |
AArch64::AEK_SIMD | AArch64::AEK_RDM, "8-A"));
@@ -746,6 +770,20 @@ TEST(TargetParserTest, testAArch64CPU) {
"8-A"));
}
+static constexpr unsigned NumAArch64CPUArchs = 20;
+
+TEST(TargetParserTest, testAArch64CPUArchList) {
+ SmallVector<StringRef, NumAArch64CPUArchs> List;
+ AArch64::fillValidCPUArchList(List);
+
+ // No list exists for these in this test suite, so ensure all are
+ // valid, and match the expected 'magic' count.
+ EXPECT_EQ(List.size(), NumAArch64CPUArchs);
+ for(StringRef CPU : List) {
+ EXPECT_NE(AArch64::parseCPUArch(CPU), AArch64::ArchKind::INVALID);
+ }
+}
+
bool testAArch64Arch(StringRef Arch, StringRef DefaultCPU, StringRef SubArch,
unsigned ArchAttr) {
AArch64::ArchKind AK = AArch64::parseArch(Arch);
@@ -795,6 +833,8 @@ TEST(TargetParserTest, testAArch64Extension) {
AArch64::ArchKind::INVALID, "ras"));
EXPECT_FALSE(testAArch64Extension("exynos-m3",
AArch64::ArchKind::INVALID, "ras"));
+ EXPECT_FALSE(testAArch64Extension("exynos-m4",
+ AArch64::ArchKind::INVALID, "ras"));
EXPECT_TRUE(testAArch64Extension("falkor",
AArch64::ArchKind::INVALID, "rdm"));
EXPECT_FALSE(testAArch64Extension("kryo",
diff --git a/unittests/Support/TaskQueueTest.cpp b/unittests/Support/TaskQueueTest.cpp
new file mode 100644
index 000000000000..14ca9c057ac8
--- /dev/null
+++ b/unittests/Support/TaskQueueTest.cpp
@@ -0,0 +1,108 @@
+//========- unittests/Support/TaskQueue.cpp - TaskQueue.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/Config/llvm-config.h"
+
+#if LLVM_ENABLE_THREADS
+
+#include "llvm/Support/TaskQueue.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+class TaskQueueTest : public testing::Test {
+protected:
+ TaskQueueTest() {}
+};
+
+TEST_F(TaskQueueTest, OrderedFutures) {
+ ThreadPool TP(1);
+ TaskQueue TQ(TP);
+ std::atomic<int> X{ 0 };
+ std::atomic<int> Y{ 0 };
+ std::atomic<int> Z{ 0 };
+
+ std::mutex M1, M2, M3;
+ std::unique_lock<std::mutex> L1(M1);
+ std::unique_lock<std::mutex> L2(M2);
+ std::unique_lock<std::mutex> L3(M3);
+
+ std::future<void> F1 = TQ.async([&] {
+ std::unique_lock<std::mutex> Lock(M1);
+ ++X;
+ });
+ std::future<void> F2 = TQ.async([&] {
+ std::unique_lock<std::mutex> Lock(M2);
+ ++Y;
+ });
+ std::future<void> F3 = TQ.async([&] {
+ std::unique_lock<std::mutex> Lock(M3);
+ ++Z;
+ });
+
+ L1.unlock();
+ F1.wait();
+ ASSERT_EQ(1, X);
+ ASSERT_EQ(0, Y);
+ ASSERT_EQ(0, Z);
+
+ L2.unlock();
+ F2.wait();
+ ASSERT_EQ(1, X);
+ ASSERT_EQ(1, Y);
+ ASSERT_EQ(0, Z);
+
+ L3.unlock();
+ F3.wait();
+ ASSERT_EQ(1, X);
+ ASSERT_EQ(1, Y);
+ ASSERT_EQ(1, Z);
+}
+
+TEST_F(TaskQueueTest, UnOrderedFutures) {
+ ThreadPool TP(1);
+ TaskQueue TQ(TP);
+ std::atomic<int> X{ 0 };
+ std::atomic<int> Y{ 0 };
+ std::atomic<int> Z{ 0 };
+ std::mutex M;
+
+ std::unique_lock<std::mutex> Lock(M);
+
+ std::future<void> F1 = TQ.async([&] { ++X; });
+ std::future<void> F2 = TQ.async([&] { ++Y; });
+ std::future<void> F3 = TQ.async([&M, &Z] {
+ std::unique_lock<std::mutex> Lock(M);
+ ++Z;
+ });
+
+ F2.wait();
+ ASSERT_EQ(1, X);
+ ASSERT_EQ(1, Y);
+ ASSERT_EQ(0, Z);
+
+ Lock.unlock();
+
+ F3.wait();
+ ASSERT_EQ(1, X);
+ ASSERT_EQ(1, Y);
+ ASSERT_EQ(1, Z);
+}
+
+TEST_F(TaskQueueTest, FutureWithReturnValue) {
+ ThreadPool TP(1);
+ TaskQueue TQ(TP);
+ std::future<std::string> F1 = TQ.async([&] { return std::string("Hello"); });
+ std::future<int> F2 = TQ.async([&] { return 42; });
+
+ ASSERT_EQ(42, F2.get());
+ ASSERT_EQ("Hello", F1.get());
+}
+#endif
diff --git a/unittests/Support/TimerTest.cpp b/unittests/Support/TimerTest.cpp
index 082a7e35db52..a92ecf1ac510 100644
--- a/unittests/Support/TimerTest.cpp
+++ b/unittests/Support/TimerTest.cpp
@@ -10,7 +10,7 @@
#include "llvm/Support/Timer.h"
#include "gtest/gtest.h"
-#if LLVM_ON_WIN32
+#if _WIN32
#include <windows.h>
#else
#include <time.h>
@@ -22,7 +22,7 @@ namespace {
// FIXME: Put this somewhere in Support, it's also used in LockFileManager.
void SleepMS() {
-#if LLVM_ON_WIN32
+#if _WIN32
Sleep(1);
#else
struct timespec Interval;
diff --git a/unittests/Support/TypeTraitsTest.cpp b/unittests/Support/TypeTraitsTest.cpp
new file mode 100644
index 000000000000..2f7340dd92f0
--- /dev/null
+++ b/unittests/Support/TypeTraitsTest.cpp
@@ -0,0 +1,77 @@
+//===- TypeTraitsTest.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/type_traits.h"
+
+namespace {
+
+// Compile-time tests using static assert.
+namespace triviality {
+
+// Helper for compile time checking trivially copy constructible and trivially
+// move constructible type traits.
+template <typename T, bool IsTriviallyCopyConstructible,
+ bool IsTriviallyMoveConstructible>
+void TrivialityTester() {
+ static_assert(llvm::is_trivially_copy_constructible<T>::value ==
+ IsTriviallyCopyConstructible,
+ "Mismatch in expected trivial copy construction!");
+ static_assert(llvm::is_trivially_move_constructible<T>::value ==
+ IsTriviallyMoveConstructible,
+ "Mismatch in expected trivial move construction!");
+
+#if defined(_LIBCPP_VERSION) || defined(_MSC_VER)
+ // On compilers with support for the standard traits, make sure they agree.
+ static_assert(std::is_trivially_copy_constructible<T>::value ==
+ IsTriviallyCopyConstructible,
+ "Mismatch in expected trivial copy construction!");
+ static_assert(std::is_trivially_move_constructible<T>::value ==
+ IsTriviallyMoveConstructible,
+ "Mismatch in expected trivial move construction!");
+#endif
+};
+
+template void TrivialityTester<int, true, true>();
+template void TrivialityTester<void *, true, true>();
+template void TrivialityTester<int &, true, true>();
+template void TrivialityTester<int &&, false, true>();
+
+struct X {};
+struct Y {
+ Y(const Y &);
+};
+struct Z {
+ Z(const Z &);
+ Z(Z &&);
+};
+struct A {
+ A(const A &) = default;
+ A(A &&);
+};
+struct B {
+ B(const B &);
+ B(B &&) = default;
+};
+
+template void TrivialityTester<X, true, true>();
+template void TrivialityTester<Y, false, false>();
+template void TrivialityTester<Z, false, false>();
+template void TrivialityTester<A, true, false>();
+template void TrivialityTester<B, false, true>();
+
+template void TrivialityTester<Z &, true, true>();
+template void TrivialityTester<A &, true, true>();
+template void TrivialityTester<B &, true, true>();
+template void TrivialityTester<Z &&, false, true>();
+template void TrivialityTester<A &&, false, true>();
+template void TrivialityTester<B &&, false, true>();
+
+} // namespace triviality
+
+} // end anonymous namespace
diff --git a/unittests/Support/VersionTupleTest.cpp b/unittests/Support/VersionTupleTest.cpp
new file mode 100644
index 000000000000..cd7ecda5fefd
--- /dev/null
+++ b/unittests/Support/VersionTupleTest.cpp
@@ -0,0 +1,50 @@
+//===- VersionTupleTests.cpp - Version Number Handling 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/VersionTuple.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(VersionTuple, getAsString) {
+ EXPECT_EQ("0", VersionTuple().getAsString());
+ EXPECT_EQ("1", VersionTuple(1).getAsString());
+ EXPECT_EQ("1.2", VersionTuple(1, 2).getAsString());
+ EXPECT_EQ("1.2.3", VersionTuple(1, 2, 3).getAsString());
+ EXPECT_EQ("1.2.3.4", VersionTuple(1, 2, 3, 4).getAsString());
+}
+
+TEST(VersionTuple, tryParse) {
+ VersionTuple VT;
+
+ EXPECT_FALSE(VT.tryParse("1"));
+ EXPECT_EQ("1", VT.getAsString());
+
+ EXPECT_FALSE(VT.tryParse("1.2"));
+ EXPECT_EQ("1.2", VT.getAsString());
+
+ EXPECT_FALSE(VT.tryParse("1.2.3"));
+ EXPECT_EQ("1.2.3", VT.getAsString());
+
+ EXPECT_FALSE(VT.tryParse("1.2.3.4"));
+ EXPECT_EQ("1.2.3.4", VT.getAsString());
+
+ EXPECT_TRUE(VT.tryParse(""));
+ EXPECT_TRUE(VT.tryParse("1."));
+ EXPECT_TRUE(VT.tryParse("1.2."));
+ EXPECT_TRUE(VT.tryParse("1.2.3."));
+ EXPECT_TRUE(VT.tryParse("1.2.3.4."));
+ EXPECT_TRUE(VT.tryParse("1.2.3.4.5"));
+ EXPECT_TRUE(VT.tryParse("1-2"));
+ EXPECT_TRUE(VT.tryParse("1+2"));
+ EXPECT_TRUE(VT.tryParse(".1"));
+ EXPECT_TRUE(VT.tryParse(" 1"));
+ EXPECT_TRUE(VT.tryParse("1 "));
+ EXPECT_TRUE(VT.tryParse("."));
+}
diff --git a/unittests/Support/YAMLIOTest.cpp b/unittests/Support/YAMLIOTest.cpp
index 914b22f0fcdf..133648065240 100644
--- a/unittests/Support/YAMLIOTest.cpp
+++ b/unittests/Support/YAMLIOTest.cpp
@@ -13,6 +13,7 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/YAMLTraits.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
using llvm::yaml::Input;
@@ -25,6 +26,7 @@ using llvm::yaml::Hex8;
using llvm::yaml::Hex16;
using llvm::yaml::Hex32;
using llvm::yaml::Hex64;
+using ::testing::StartsWith;
@@ -249,6 +251,72 @@ TEST(YAMLIO, TestGivenFilename) {
EXPECT_TRUE(!!yin.error());
}
+struct WithStringField {
+ std::string str1;
+ std::string str2;
+ std::string str3;
+};
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<WithStringField> {
+ static void mapping(IO &io, WithStringField &fb) {
+ io.mapRequired("str1", fb.str1);
+ io.mapRequired("str2", fb.str2);
+ io.mapRequired("str3", fb.str3);
+ }
+};
+} // namespace yaml
+} // namespace llvm
+
+TEST(YAMLIO, MultilineStrings) {
+ WithStringField Original;
+ Original.str1 = "a multiline string\nfoobarbaz";
+ Original.str2 = "another one\rfoobarbaz";
+ Original.str3 = "a one-line string";
+
+ std::string Serialized;
+ {
+ llvm::raw_string_ostream OS(Serialized);
+ Output YOut(OS);
+ YOut << Original;
+ }
+ auto Expected = "---\n"
+ "str1: 'a multiline string\n"
+ "foobarbaz'\n"
+ "str2: 'another one\r"
+ "foobarbaz'\n"
+ "str3: a one-line string\n"
+ "...\n";
+ ASSERT_EQ(Serialized, Expected);
+
+ // Also check it parses back without the errors.
+ WithStringField Deserialized;
+ {
+ Input YIn(Serialized);
+ YIn >> Deserialized;
+ ASSERT_FALSE(YIn.error())
+ << "Parsing error occurred during deserialization. Serialized string:\n"
+ << Serialized;
+ }
+ EXPECT_EQ(Original.str1, Deserialized.str1);
+ EXPECT_EQ(Original.str2, Deserialized.str2);
+ EXPECT_EQ(Original.str3, Deserialized.str3);
+}
+
+TEST(YAMLIO, NoQuotesForTab) {
+ WithStringField WithTab;
+ WithTab.str1 = "aba\tcaba";
+ std::string Serialized;
+ {
+ llvm::raw_string_ostream OS(Serialized);
+ Output YOut(OS);
+ YOut << WithTab;
+ }
+ auto ExpectedPrefix = "---\n"
+ "str1: aba\tcaba\n";
+ EXPECT_THAT(Serialized, StartsWith(ExpectedPrefix));
+}
//===----------------------------------------------------------------------===//
// Test built-in types
@@ -533,6 +601,7 @@ struct StringTypes {
std::string stdstr9;
std::string stdstr10;
std::string stdstr11;
+ std::string stdstr12;
};
namespace llvm {
@@ -562,6 +631,7 @@ namespace yaml {
io.mapRequired("stdstr9", st.stdstr9);
io.mapRequired("stdstr10", st.stdstr10);
io.mapRequired("stdstr11", st.stdstr11);
+ io.mapRequired("stdstr12", st.stdstr12);
}
};
}
@@ -593,6 +663,7 @@ TEST(YAMLIO, TestReadWriteStringTypes) {
map.stdstr9 = "~";
map.stdstr10 = "0.2e20";
map.stdstr11 = "0x30";
+ map.stdstr12 = "- match";
llvm::raw_string_ostream ostr(intermediate);
Output yout(ostr);
@@ -611,6 +682,7 @@ TEST(YAMLIO, TestReadWriteStringTypes) {
EXPECT_NE(llvm::StringRef::npos, flowOut.find("'~'\n"));
EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0.2e20'\n"));
EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0x30'\n"));
+ EXPECT_NE(llvm::StringRef::npos, flowOut.find("'- match'\n"));
EXPECT_NE(std::string::npos, flowOut.find("'''eee"));
EXPECT_NE(std::string::npos, flowOut.find("'\"fff'"));
EXPECT_NE(std::string::npos, flowOut.find("'`ggg'"));
@@ -2460,7 +2532,10 @@ static void TestEscaped(llvm::StringRef Input, llvm::StringRef Expected) {
yamlize(xout, Input, true, Ctx);
ostr.flush();
- EXPECT_EQ(Expected, out);
+
+ // Make a separate StringRef so we get nice byte-by-byte output.
+ llvm::StringRef Got(out);
+ EXPECT_EQ(Expected, Got);
}
TEST(YAMLIO, TestEscaped) {
@@ -2481,4 +2556,16 @@ TEST(YAMLIO, TestEscaped) {
// UTF8 with single quote inside double quote
TestEscaped("parameter 'параметр' is unused",
"\"parameter 'параметр' is unused\"");
+
+ // String with embedded non-printable multibyte UTF-8 sequence (U+200B
+ // zero-width space). The thing to test here is that we emit a
+ // unicode-scalar level escape like \uNNNN (at the YAML level), and don't
+ // just pass the UTF-8 byte sequence through as with quoted printables.
+ {
+ const unsigned char foobar[10] = {'f', 'o', 'o',
+ 0xE2, 0x80, 0x8B, // UTF-8 of U+200B
+ 'b', 'a', 'r',
+ 0x0};
+ TestEscaped((char const *)foobar, "\"foo\\u200Bbar\"");
+ }
}
diff --git a/unittests/Support/raw_pwrite_stream_test.cpp b/unittests/Support/raw_pwrite_stream_test.cpp
index 249780a8c829..a528fd25b932 100644
--- a/unittests/Support/raw_pwrite_stream_test.cpp
+++ b/unittests/Support/raw_pwrite_stream_test.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/SmallString.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/raw_ostream.h"
@@ -83,7 +84,7 @@ TEST(raw_pwrite_ostreamTest, TestFD) {
#ifdef LLVM_ON_UNIX
TEST(raw_pwrite_ostreamTest, TestDevNull) {
int FD;
- sys::fs::openFileForWrite("/dev/null", FD, sys::fs::F_None);
+ sys::fs::openFileForWrite("/dev/null", FD, sys::fs::CD_OpenExisting);
raw_fd_ostream OS(FD, true);
OS << "abcd";
StringRef Test = "test";
diff --git a/unittests/Target/WebAssembly/CMakeLists.txt b/unittests/Target/WebAssembly/CMakeLists.txt
new file mode 100644
index 000000000000..5ec82240b038
--- /dev/null
+++ b/unittests/Target/WebAssembly/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories(
+ ${CMAKE_SOURCE_DIR}/lib/Target/WebAssembly
+ ${CMAKE_BINARY_DIR}/lib/Target/WebAssembly
+ )
+
+set(LLVM_LINK_COMPONENTS
+ CodeGen
+ Core
+ MC
+ MIRParser
+ WebAssemblyCodeGen
+ WebAssemblyDesc
+ WebAssemblyInfo
+ )
+
+add_llvm_unittest(WebAssemblyTests
+ WebAssemblyExceptionInfoTest.cpp
+ )
diff --git a/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp b/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp
new file mode 100644
index 000000000000..599f2e7f10fc
--- /dev/null
+++ b/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp
@@ -0,0 +1,549 @@
+//=== WebAssemblyExceptionInfoTest.cpp - WebAssebmlyExceptionInfo unit tests =//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "WebAssemblyExceptionInfo.h"
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineDominanceFrontier.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+std::unique_ptr<TargetMachine> createTargetMachine() {
+ auto TT(Triple::normalize("wasm32-unknown-unknown"));
+ std::string CPU("");
+ std::string FS("");
+
+ LLVMInitializeWebAssemblyTargetInfo();
+ LLVMInitializeWebAssemblyTarget();
+ LLVMInitializeWebAssemblyTargetMC();
+
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);
+ assert(TheTarget);
+
+ return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TT, CPU, FS, TargetOptions(), None, None, CodeGenOpt::Default));
+}
+
+std::unique_ptr<Module> parseMIR(LLVMContext &Context,
+ std::unique_ptr<MIRParser> &MIR,
+ const TargetMachine &TM, StringRef MIRCode,
+ const char *FuncName, MachineModuleInfo &MMI) {
+ 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->parseIRModule();
+ if (!M)
+ return nullptr;
+
+ M->setDataLayout(TM.createDataLayout());
+
+ if (MIR->parseMachineFunctions(*M, MMI))
+ return nullptr;
+
+ return M;
+}
+
+} // namespace
+
+TEST(WebAssemblyExceptionInfoTest, TEST0) {
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ ASSERT_TRUE(TM);
+
+ StringRef MIRString = R"MIR(
+--- |
+ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+ target triple = "wasm32-unknown-unknown"
+
+ declare i32 @__gxx_wasm_personality_v0(...)
+
+ define hidden void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+ unreachable
+ }
+
+...
+---
+name: test0
+liveins:
+ - { reg: '$arguments' }
+ - { reg: '$value_stack' }
+body: |
+ bb.0:
+ successors: %bb.1, %bb.2
+ liveins: $arguments, $value_stack
+ BR %bb.1, implicit-def dead $arguments
+
+ bb.1:
+ ; predecessors: %bb.0
+ successors: %bb.7
+ liveins: $value_stack
+ BR %bb.7, implicit-def $arguments
+
+ bb.2 (landing-pad):
+ ; predecessors: %bb.0
+ successors: %bb.3, %bb.9
+ liveins: $value_stack
+ CATCH_ALL implicit-def $arguments
+ CLEANUPRET implicit-def dead $arguments
+
+ bb.3 (landing-pad):
+ ; predecessors: %bb.2
+ successors: %bb.4, %bb.6
+ liveins: $value_stack
+ CATCH_ALL implicit-def $arguments
+ BR_IF %bb.4, %58:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+ BR %bb.6, implicit-def $arguments
+
+ bb.4:
+ ; predecessors: %bb.3
+ successors: %bb.5, %bb.8
+ liveins: $value_stack
+ BR %bb.5, implicit-def dead $arguments
+
+ bb.5:
+ ; predecessors: %bb.4
+ successors: %bb.7
+ liveins: $value_stack
+ CATCHRET %bb.7, %bb.0, implicit-def dead $arguments
+
+ bb.6:
+ ; predecessors: %bb.3
+ successors: %bb.10, %bb.9
+ liveins: $value_stack
+ BR %bb.10, implicit-def dead $arguments
+
+ bb.7:
+ ; predecessors: %bb.5, %bb.1
+ liveins: $value_stack
+ RETURN_VOID implicit-def $arguments
+
+ bb.8 (landing-pad):
+ ; predecessors: %bb.4
+ successors: %bb.9
+ liveins: $value_stack
+ CATCH_ALL implicit-def $arguments
+ CLEANUPRET implicit-def dead $arguments
+
+ bb.9 (landing-pad):
+ ; predecessors: %bb.2, %bb.6, %bb.8
+ liveins: $value_stack
+ CATCH_ALL implicit-def $arguments
+ CLEANUPRET implicit-def dead $arguments
+
+ bb.10:
+ ; predecessors: %bb.6
+ liveins: $value_stack
+ UNREACHABLE implicit-def $arguments
+)MIR";
+
+ LLVMContext Context;
+ std::unique_ptr<MIRParser> MIR;
+ MachineModuleInfo MMI(TM.get());
+ std::unique_ptr<Module> M =
+ parseMIR(Context, MIR, *TM, MIRString, "test0", MMI);
+ ASSERT_TRUE(M);
+
+ Function *F = M->getFunction("test0");
+ auto *MF = MMI.getMachineFunction(*F);
+ ASSERT_TRUE(MF);
+
+ WebAssemblyExceptionInfo WEI;
+ MachineDominatorTree MDT;
+ MachineDominanceFrontier MDF;
+ MDT.runOnMachineFunction(*MF);
+ MDF.getBase().analyze(MDT.getBase());
+ WEI.recalculate(MDT, MDF);
+
+ // Exception info structure:
+ // |- bb2 (ehpad), bb3, bb4, bb5, bb6, bb8, bb9, bb10
+ // |- bb3 (ehpad), bb4, bb5, bb6, bb8, bb10
+ // |- bb8 (ehpad)
+ // |- bb9 (ehpad)
+
+ auto *MBB2 = MF->getBlockNumbered(2);
+ auto *WE0 = WEI.getExceptionFor(MBB2);
+ ASSERT_TRUE(WE0);
+ EXPECT_EQ(WE0->getEHPad(), MBB2);
+ EXPECT_EQ(WE0->getParentException(), nullptr);
+ EXPECT_EQ(WE0->getExceptionDepth(), (unsigned)1);
+
+ auto *MBB3 = MF->getBlockNumbered(3);
+ auto *WE0_0 = WEI.getExceptionFor(MBB3);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+ EXPECT_EQ(WE0_0->getParentException(), WE0);
+ EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned)2);
+
+ auto *MBB4 = MF->getBlockNumbered(4);
+ WE0_0 = WEI.getExceptionFor(MBB4);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB5 = MF->getBlockNumbered(5);
+ WE0_0 = WEI.getExceptionFor(MBB5);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB6 = MF->getBlockNumbered(6);
+ WE0_0 = WEI.getExceptionFor(MBB6);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB10 = MF->getBlockNumbered(10);
+ WE0_0 = WEI.getExceptionFor(MBB10);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB8 = MF->getBlockNumbered(8);
+ auto *WE0_0_0 = WEI.getExceptionFor(MBB8);
+ ASSERT_TRUE(WE0_0_0);
+ EXPECT_EQ(WE0_0_0->getEHPad(), MBB8);
+ EXPECT_EQ(WE0_0_0->getParentException(), WE0_0);
+ EXPECT_EQ(WE0_0_0->getExceptionDepth(), (unsigned)3);
+
+ auto *MBB9 = MF->getBlockNumbered(9);
+ auto *WE0_1 = WEI.getExceptionFor(MBB9);
+ ASSERT_TRUE(WE0_1);
+ EXPECT_EQ(WE0_1->getEHPad(), MBB9);
+ EXPECT_EQ(WE0_1->getParentException(), WE0);
+ EXPECT_EQ(WE0_1->getExceptionDepth(), (unsigned)2);
+}
+
+TEST(WebAssemblyExceptionInfoTest, TEST1) {
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ ASSERT_TRUE(TM);
+
+ StringRef MIRString = R"MIR(
+--- |
+ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+ target triple = "wasm32-unknown-unknown"
+
+ declare i32 @__gxx_wasm_personality_v0(...)
+
+ define hidden void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+ unreachable
+ }
+
+...
+---
+name: test1
+liveins:
+ - { reg: '$arguments' }
+ - { reg: '$value_stack' }
+body: |
+ bb.0:
+ successors: %bb.9, %bb.1
+ liveins: $arguments, $value_stack
+ BR %bb.9, implicit-def dead $arguments
+
+ bb.1 (landing-pad):
+ ; predecessors: %bb.0
+ successors: %bb.2, %bb.8
+ liveins: $value_stack
+ %52:i32 = CATCH_I32 0, implicit-def dead $arguments
+ BR_IF %bb.2, %32:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+ BR %bb.8, implicit-def $arguments
+
+ bb.2:
+ ; predecessors: %bb.1
+ successors: %bb.7, %bb.3, %bb.11
+ liveins: $value_stack
+ BR %bb.7, implicit-def dead $arguments
+
+ bb.3 (landing-pad):
+ ; predecessors: %bb.2
+ successors: %bb.4, %bb.6
+ liveins: $value_stack
+ CATCH_ALL implicit-def $arguments
+ BR_IF %bb.4, %43:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+ BR %bb.6, implicit-def $arguments
+
+ bb.4:
+ ; predecessors: %bb.3
+ successors: %bb.5, %bb.10
+ liveins: $value_stack
+ BR %bb.5, implicit-def dead $arguments
+
+ bb.5:
+ ; predecessors: %bb.4
+ successors: %bb.7(0x80000000); %bb.7(200.00%)
+ liveins: $value_stack
+ CATCHRET %bb.7, %bb.1, implicit-def dead $arguments
+
+ bb.6:
+ ; predecessors: %bb.3
+ successors: %bb.12, %bb.11
+ liveins: $value_stack
+ BR %bb.12, implicit-def dead $arguments
+
+ bb.7:
+ ; predecessors: %bb.2, %bb.5
+ successors: %bb.9(0x80000000); %bb.9(200.00%)
+ liveins: $value_stack
+ CATCHRET %bb.9, %bb.0, implicit-def dead $arguments
+
+ bb.8:
+ ; predecessors: %bb.1
+ liveins: $value_stack
+ UNREACHABLE implicit-def $arguments
+
+ bb.9:
+ ; predecessors: %bb.0, %bb.7
+ liveins: $value_stack
+ RETURN_VOID implicit-def $arguments
+
+ bb.10 (landing-pad):
+ ; predecessors: %bb.4
+ successors: %bb.11
+ liveins: $value_stack
+ CATCH_ALL implicit-def $arguments
+ CLEANUPRET implicit-def dead $arguments
+
+ bb.11 (landing-pad):
+ ; predecessors: %bb.2, %bb.6, %bb.10
+ liveins: $value_stack
+ CATCH_ALL implicit-def $arguments
+ CLEANUPRET implicit-def dead $arguments
+
+ bb.12:
+ ; predecessors: %bb.6
+ liveins: $value_stack
+ UNREACHABLE implicit-def $arguments
+)MIR";
+
+ LLVMContext Context;
+ std::unique_ptr<MIRParser> MIR;
+ MachineModuleInfo MMI(TM.get());
+ std::unique_ptr<Module> M =
+ parseMIR(Context, MIR, *TM, MIRString, "test1", MMI);
+ ASSERT_TRUE(M);
+
+ Function *F = M->getFunction("test1");
+ auto *MF = MMI.getMachineFunction(*F);
+ ASSERT_TRUE(MF);
+
+ WebAssemblyExceptionInfo WEI;
+ MachineDominatorTree MDT;
+ MachineDominanceFrontier MDF;
+ MDT.runOnMachineFunction(*MF);
+ MDF.getBase().analyze(MDT.getBase());
+ WEI.recalculate(MDT, MDF);
+
+ // Exception info structure:
+ // |- bb1 (ehpad), bb2, bb3, bb4, bb5, bb6, bb7, bb8, bb10, bb11, bb12
+ // |- bb3 (ehpad), bb4, bb5, bb6, bb10, bb12
+ // |- bb10 (ehpad)
+ // |- bb11 (ehpad)
+
+ auto *MBB1 = MF->getBlockNumbered(1);
+ auto *WE0 = WEI.getExceptionFor(MBB1);
+ ASSERT_TRUE(WE0);
+ EXPECT_EQ(WE0->getEHPad(), MBB1);
+ EXPECT_EQ(WE0->getParentException(), nullptr);
+ EXPECT_EQ(WE0->getExceptionDepth(), (unsigned)1);
+
+ auto *MBB2 = MF->getBlockNumbered(2);
+ WE0 = WEI.getExceptionFor(MBB2);
+ ASSERT_TRUE(WE0);
+ EXPECT_EQ(WE0->getEHPad(), MBB1);
+
+ auto *MBB7 = MF->getBlockNumbered(7);
+ WE0 = WEI.getExceptionFor(MBB7);
+ ASSERT_TRUE(WE0);
+ EXPECT_EQ(WE0->getEHPad(), MBB1);
+
+ auto *MBB8 = MF->getBlockNumbered(8);
+ WE0 = WEI.getExceptionFor(MBB8);
+ ASSERT_TRUE(WE0);
+ EXPECT_EQ(WE0->getEHPad(), MBB1);
+
+ auto *MBB3 = MF->getBlockNumbered(3);
+ auto *WE0_0 = WEI.getExceptionFor(MBB3);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+ EXPECT_EQ(WE0_0->getParentException(), WE0);
+ EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned)2);
+
+ auto *MBB4 = MF->getBlockNumbered(4);
+ WE0_0 = WEI.getExceptionFor(MBB4);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB5 = MF->getBlockNumbered(5);
+ WE0_0 = WEI.getExceptionFor(MBB5);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB6 = MF->getBlockNumbered(6);
+ WE0_0 = WEI.getExceptionFor(MBB6);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB12 = MF->getBlockNumbered(12);
+ WE0_0 = WEI.getExceptionFor(MBB12);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB3);
+
+ auto *MBB10 = MF->getBlockNumbered(10);
+ auto *WE0_0_0 = WEI.getExceptionFor(MBB10);
+ ASSERT_TRUE(WE0_0_0);
+ EXPECT_EQ(WE0_0_0->getEHPad(), MBB10);
+ EXPECT_EQ(WE0_0_0->getParentException(), WE0_0);
+ EXPECT_EQ(WE0_0_0->getExceptionDepth(), (unsigned)3);
+
+ auto *MBB11 = MF->getBlockNumbered(11);
+ auto *WE0_1 = WEI.getExceptionFor(MBB11);
+ ASSERT_TRUE(WE0_1);
+ EXPECT_EQ(WE0_1->getEHPad(), MBB11);
+ EXPECT_EQ(WE0_1->getParentException(), WE0);
+ EXPECT_EQ(WE0_1->getExceptionDepth(), (unsigned)2);
+}
+
+// Terminate pad test
+TEST(WebAssemblyExceptionInfoTest, TEST2) {
+ std::unique_ptr<TargetMachine> TM = createTargetMachine();
+ ASSERT_TRUE(TM);
+
+ StringRef MIRString = R"MIR(
+--- |
+ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+ target triple = "wasm32-unknown-unknown"
+
+ declare i32 @__gxx_wasm_personality_v0(...)
+ declare void @_ZSt9terminatev()
+ declare void @__clang_call_terminate(i8*)
+
+ define hidden void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+ unreachable
+ }
+
+...
+---
+name: test2
+liveins:
+ - { reg: '$arguments' }
+ - { reg: '$value_stack' }
+body: |
+ bb.0:
+ successors: %bb.3, %bb.1
+ BR %bb.3, implicit-def dead $arguments
+
+ bb.1 (landing-pad):
+ ; predecessors: %bb.0
+ successors: %bb.2, %bb.4
+ %3:i32 = CATCH_I32 0, implicit-def dead $arguments
+ BR %bb.2, implicit-def dead $arguments
+
+ bb.2:
+ ; predecessors: %bb.1
+ successors: %bb.3(0x80000000); %bb.3(200.00%)
+ CATCHRET %bb.3, %bb.0, implicit-def dead $arguments
+
+ bb.3:
+ ; predecessors: %bb.0, %bb.2
+ RETURN_VOID implicit-def $arguments
+
+ bb.4 (landing-pad):
+ ; predecessors: %bb.1
+ successors: %bb.5, %bb.6
+ CATCH_ALL implicit-def $arguments
+ BR %bb.5, implicit-def dead $arguments
+
+ bb.5:
+ ; predecessors: %bb.4
+ CLEANUPRET implicit-def dead $arguments
+
+ bb.6 (landing-pad):
+ ; predecessors: %bb.4
+ successors: %bb.7(0x80000000); %bb.7(200.00%)
+ %6:i32 = CATCH_I32 0, implicit-def dead $arguments
+ CALL_VOID @__clang_call_terminate, %7:i32, implicit-def $arguments
+ UNREACHABLE implicit-def $arguments
+
+ bb.7 (landing-pad):
+ ; predecessors: %bb.6
+ CATCH_ALL implicit-def $arguments
+ CALL_VOID @_ZSt9terminatev, implicit-def $arguments
+ UNREACHABLE implicit-def $arguments
+)MIR";
+
+ LLVMContext Context;
+ std::unique_ptr<MIRParser> MIR;
+ MachineModuleInfo MMI(TM.get());
+ std::unique_ptr<Module> M =
+ parseMIR(Context, MIR, *TM, MIRString, "test2", MMI);
+ ASSERT_TRUE(M);
+
+ Function *F = M->getFunction("test2");
+ auto *MF = MMI.getMachineFunction(*F);
+ ASSERT_TRUE(MF);
+
+ WebAssemblyExceptionInfo WEI;
+ MachineDominatorTree MDT;
+ MachineDominanceFrontier MDF;
+ MDT.runOnMachineFunction(*MF);
+ MDF.getBase().analyze(MDT.getBase());
+ WEI.recalculate(MDT, MDF);
+
+ // Exception info structure:
+ // |- bb1 (ehpad), bb2, bb4, bb5, bb6, bb7
+ // |- bb4 (ehpad), bb5, bb6, bb7
+ // |- bb6 (ehpad), bb7
+ //
+ // Here, bb6 is a terminate pad with a 'catch' instruction, and bb7 is a
+ // terminate pad with a 'catch_all' instruction, In this case we put bb6 and
+ // bb7 into one exception.
+
+ auto *MBB1 = MF->getBlockNumbered(1);
+ auto *WE0 = WEI.getExceptionFor(MBB1);
+ ASSERT_TRUE(WE0);
+ EXPECT_EQ(WE0->getEHPad(), MBB1);
+ EXPECT_EQ(WE0->getParentException(), nullptr);
+ EXPECT_EQ(WE0->getExceptionDepth(), (unsigned)1);
+
+ auto *MBB2 = MF->getBlockNumbered(2);
+ WE0 = WEI.getExceptionFor(MBB2);
+ ASSERT_TRUE(WE0);
+ EXPECT_EQ(WE0->getEHPad(), MBB1);
+
+ auto *MBB4 = MF->getBlockNumbered(4);
+ auto *WE0_0 = WEI.getExceptionFor(MBB4);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB4);
+ EXPECT_EQ(WE0_0->getParentException(), WE0);
+ EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned)2);
+
+ auto *MBB5 = MF->getBlockNumbered(5);
+ WE0_0 = WEI.getExceptionFor(MBB5);
+ ASSERT_TRUE(WE0_0);
+ EXPECT_EQ(WE0_0->getEHPad(), MBB4);
+
+ auto *MBB6 = MF->getBlockNumbered(6);
+ auto *WE0_0_0 = WEI.getExceptionFor(MBB6);
+ ASSERT_TRUE(WE0_0_0);
+ EXPECT_EQ(WE0_0_0->getEHPad(), MBB6);
+ EXPECT_EQ(WE0_0_0->getParentException(), WE0_0);
+ EXPECT_EQ(WE0_0_0->getExceptionDepth(), (unsigned)3);
+
+ auto *MBB7 = MF->getBlockNumbered(7);
+ WE0_0_0 = WEI.getExceptionFor(MBB7);
+ ASSERT_TRUE(WE0_0_0);
+ EXPECT_EQ(WE0_0_0->getEHPad(), MBB6);
+}
diff --git a/unittests/Transforms/CMakeLists.txt b/unittests/Transforms/CMakeLists.txt
index e2570a3b6537..b7f1817849ab 100644
--- a/unittests/Transforms/CMakeLists.txt
+++ b/unittests/Transforms/CMakeLists.txt
@@ -1,3 +1,4 @@
add_subdirectory(IPO)
add_subdirectory(Scalar)
add_subdirectory(Utils)
+add_subdirectory(Vectorize)
diff --git a/unittests/Transforms/Scalar/LoopPassManagerTest.cpp b/unittests/Transforms/Scalar/LoopPassManagerTest.cpp
index 2b8130b9e009..57fb57ec66c1 100644
--- a/unittests/Transforms/Scalar/LoopPassManagerTest.cpp
+++ b/unittests/Transforms/Scalar/LoopPassManagerTest.cpp
@@ -962,7 +962,7 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) {
AR.DT.addNewBlock(NewLoop010PHBB, &Loop01BB);
AR.DT.addNewBlock(NewLoop010BB, NewLoop010PHBB);
AR.DT.addNewBlock(NewLoop01LatchBB, NewLoop010BB);
- AR.DT.verifyDomTree();
+ EXPECT_TRUE(AR.DT.verify());
L.addBasicBlockToLoop(NewLoop010PHBB, AR.LI);
NewLoop->addBasicBlockToLoop(NewLoop010BB, AR.LI);
L.addBasicBlockToLoop(NewLoop01LatchBB, AR.LI);
@@ -1004,7 +1004,7 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) {
AR.DT.addNewBlock(NewLoop011PHBB, NewLoop010BB);
auto *NewDTNode = AR.DT.addNewBlock(NewLoop011BB, NewLoop011PHBB);
AR.DT.changeImmediateDominator(AR.DT[NewLoop01LatchBB], NewDTNode);
- AR.DT.verifyDomTree();
+ EXPECT_TRUE(AR.DT.verify());
L.addBasicBlockToLoop(NewLoop011PHBB, AR.LI);
NewLoop->addBasicBlockToLoop(NewLoop011BB, AR.LI);
NewLoop->verifyLoop();
@@ -1149,7 +1149,7 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) {
AR.DT.addNewBlock(NewLoop01PHBB, &Loop00BB);
auto *NewDTNode = AR.DT.addNewBlock(NewLoop01BB, NewLoop01PHBB);
AR.DT.changeImmediateDominator(AR.DT[&Loop02PHBB], NewDTNode);
- AR.DT.verifyDomTree();
+ EXPECT_TRUE(AR.DT.verify());
L.getParentLoop()->addBasicBlockToLoop(NewLoop01PHBB, AR.LI);
NewLoop->addBasicBlockToLoop(NewLoop01BB, AR.LI);
L.getParentLoop()->verifyLoop();
@@ -1216,7 +1216,7 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) {
AR.DT.addNewBlock(NewLoop040PHBB, NewLoop04BB);
AR.DT.addNewBlock(NewLoop040BB, NewLoop040PHBB);
AR.DT.addNewBlock(NewLoop04LatchBB, NewLoop040BB);
- AR.DT.verifyDomTree();
+ EXPECT_TRUE(AR.DT.verify());
L.getParentLoop()->addBasicBlockToLoop(NewLoop03PHBB, AR.LI);
NewLoops[0]->addBasicBlockToLoop(NewLoop03BB, AR.LI);
L.getParentLoop()->addBasicBlockToLoop(NewLoop04PHBB, AR.LI);
@@ -1271,7 +1271,7 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) {
AR.DT.addNewBlock(NewLoop1PHBB, &Loop0BB);
auto *NewDTNode = AR.DT.addNewBlock(NewLoop1BB, NewLoop1PHBB);
AR.DT.changeImmediateDominator(AR.DT[&Loop2PHBB], NewDTNode);
- AR.DT.verifyDomTree();
+ EXPECT_TRUE(AR.DT.verify());
NewLoop->addBasicBlockToLoop(NewLoop1BB, AR.LI);
NewLoop->verifyLoop();
Updater.addSiblingLoops({NewLoop});
@@ -1508,7 +1508,7 @@ TEST_F(LoopPassManagerTest, LoopDeletion) {
AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB);
AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB],
AR.DT[NewLoop03BB]);
- AR.DT.verifyDomTree();
+ EXPECT_TRUE(AR.DT.verify());
ParentL->addBasicBlockToLoop(NewLoop03PHBB, AR.LI);
NewSibling->addBasicBlockToLoop(NewLoop03BB, AR.LI);
NewSibling->verifyLoop();
diff --git a/unittests/Transforms/Utils/BasicBlockUtils.cpp b/unittests/Transforms/Utils/BasicBlockUtils.cpp
new file mode 100644
index 000000000000..2d0a9302011f
--- /dev/null
+++ b/unittests/Transforms/Utils/BasicBlockUtils.cpp
@@ -0,0 +1,52 @@
+//===- BasicBlockUtils.cpp - Unit tests for BasicBlockUtils ---------------===//
+//
+// 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/BasicBlockUtils.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
+ if (!Mod)
+ Err.print("BasicBlockUtilsTests", errs());
+ return Mod;
+}
+
+TEST(BasicBlockUtils, SplitBlockPredecessors) {
+ LLVMContext C;
+
+ std::unique_ptr<Module> M = parseIR(
+ C,
+ "define i32 @basic_func(i1 %cond) {\n"
+ "entry:\n"
+ " br i1 %cond, label %bb0, label %bb1\n"
+ "bb0:\n"
+ " br label %bb1\n"
+ "bb1:\n"
+ " %phi = phi i32 [ 0, %entry ], [ 1, %bb0 ]"
+ " ret i32 %phi\n"
+ "}\n"
+ "\n"
+ );
+
+ auto *F = M->getFunction("basic_func");
+ DominatorTree DT(*F);
+
+ // Make sure the dominator tree is properly updated if calling this on the
+ // entry block.
+ SplitBlockPredecessors(&F->getEntryBlock(), {}, "split.entry", &DT);
+ EXPECT_TRUE(DT.verify());
+}
diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt
index e2bb0af0f773..9781b0aaedb0 100644
--- a/unittests/Transforms/Utils/CMakeLists.txt
+++ b/unittests/Transforms/Utils/CMakeLists.txt
@@ -8,11 +8,13 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(UtilsTests
ASanStackFrameLayoutTest.cpp
+ BasicBlockUtils.cpp
Cloning.cpp
CodeExtractor.cpp
FunctionComparator.cpp
IntegerDivision.cpp
Local.cpp
OrderedInstructions.cpp
+ SSAUpdaterBulk.cpp
ValueMapperTest.cpp
)
diff --git a/unittests/Transforms/Utils/Cloning.cpp b/unittests/Transforms/Utils/Cloning.cpp
index fe4e2ba7e943..9a1ad19ebaa4 100644
--- a/unittests/Transforms/Utils/Cloning.cpp
+++ b/unittests/Transforms/Utils/Cloning.cpp
@@ -251,6 +251,104 @@ TEST_F(CloneInstruction, DuplicateInstructionsToSplit) {
delete F;
}
+TEST_F(CloneInstruction, DuplicateInstructionsToSplitBlocksEq1) {
+ Type *ArgTy1[] = {Type::getInt32PtrTy(context)};
+ FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false);
+ V = new Argument(Type::getInt32Ty(context));
+
+ Function *F = Function::Create(FT, Function::ExternalLinkage);
+
+ BasicBlock *BB1 = BasicBlock::Create(context, "", F);
+ IRBuilder<> Builder1(BB1);
+
+ BasicBlock *BB2 = BasicBlock::Create(context, "", F);
+ IRBuilder<> Builder2(BB2);
+
+ Builder1.CreateBr(BB2);
+
+ Instruction *AddInst = cast<Instruction>(Builder2.CreateAdd(V, V));
+ Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));
+ Instruction *SubInst = cast<Instruction>(Builder2.CreateSub(MulInst, V));
+ Builder2.CreateBr(BB2);
+
+ ValueToValueMapTy Mapping;
+
+ auto Split = DuplicateInstructionsInSplitBetween(BB2, BB2, BB2->getTerminator(), Mapping);
+
+ EXPECT_TRUE(Split);
+ EXPECT_EQ(Mapping.size(), 3u);
+ EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end());
+ EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end());
+ EXPECT_TRUE(Mapping.find(SubInst) != Mapping.end());
+
+ auto AddSplit = dyn_cast<Instruction>(Mapping[AddInst]);
+ EXPECT_TRUE(AddSplit);
+ EXPECT_EQ(AddSplit->getOperand(0), V);
+ EXPECT_EQ(AddSplit->getOperand(1), V);
+ EXPECT_EQ(AddSplit->getParent(), Split);
+
+ auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);
+ EXPECT_TRUE(MulSplit);
+ EXPECT_EQ(MulSplit->getOperand(0), AddSplit);
+ EXPECT_EQ(MulSplit->getOperand(1), V);
+ EXPECT_EQ(MulSplit->getParent(), Split);
+
+ auto SubSplit = dyn_cast<Instruction>(Mapping[SubInst]);
+ EXPECT_EQ(MulSplit->getNextNode(), SubSplit);
+ EXPECT_EQ(SubSplit->getNextNode(), Split->getTerminator());
+ EXPECT_EQ(Split->getSingleSuccessor(), BB2);
+ EXPECT_EQ(BB2->getSingleSuccessor(), Split);
+
+ delete F;
+}
+
+TEST_F(CloneInstruction, DuplicateInstructionsToSplitBlocksEq2) {
+ Type *ArgTy1[] = {Type::getInt32PtrTy(context)};
+ FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false);
+ V = new Argument(Type::getInt32Ty(context));
+
+ Function *F = Function::Create(FT, Function::ExternalLinkage);
+
+ BasicBlock *BB1 = BasicBlock::Create(context, "", F);
+ IRBuilder<> Builder1(BB1);
+
+ BasicBlock *BB2 = BasicBlock::Create(context, "", F);
+ IRBuilder<> Builder2(BB2);
+
+ Builder1.CreateBr(BB2);
+
+ Instruction *AddInst = cast<Instruction>(Builder2.CreateAdd(V, V));
+ Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));
+ Instruction *SubInst = cast<Instruction>(Builder2.CreateSub(MulInst, V));
+ Builder2.CreateBr(BB2);
+
+ ValueToValueMapTy Mapping;
+
+ auto Split = DuplicateInstructionsInSplitBetween(BB2, BB2, SubInst, Mapping);
+
+ EXPECT_TRUE(Split);
+ EXPECT_EQ(Mapping.size(), 2u);
+ EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end());
+ EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end());
+
+ auto AddSplit = dyn_cast<Instruction>(Mapping[AddInst]);
+ EXPECT_TRUE(AddSplit);
+ EXPECT_EQ(AddSplit->getOperand(0), V);
+ EXPECT_EQ(AddSplit->getOperand(1), V);
+ EXPECT_EQ(AddSplit->getParent(), Split);
+
+ auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);
+ EXPECT_TRUE(MulSplit);
+ EXPECT_EQ(MulSplit->getOperand(0), AddSplit);
+ EXPECT_EQ(MulSplit->getOperand(1), V);
+ EXPECT_EQ(MulSplit->getParent(), Split);
+ EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator());
+ EXPECT_EQ(Split->getSingleSuccessor(), BB2);
+ EXPECT_EQ(BB2->getSingleSuccessor(), Split);
+
+ delete F;
+}
+
class CloneFunc : public ::testing::Test {
protected:
void SetUp() override {
@@ -527,7 +625,7 @@ protected:
DBuilder.finalize();
}
- void CreateNewModule() { NewM = llvm::CloneModule(OldM).release(); }
+ void CreateNewModule() { NewM = llvm::CloneModule(*OldM).release(); }
LLVMContext C;
Module *OldM;
diff --git a/unittests/Transforms/Utils/Local.cpp b/unittests/Transforms/Utils/Local.cpp
index 4789b0558d77..5850910403f1 100644
--- a/unittests/Transforms/Utils/Local.cpp
+++ b/unittests/Transforms/Utils/Local.cpp
@@ -15,6 +15,7 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Verifier.h"
#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
@@ -100,7 +101,7 @@ TEST(Local, RemoveDuplicatePHINodes) {
EXPECT_EQ(3U, BB->size());
}
-std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
+static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
SMDiagnostic Err;
std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
if (!Mod)
@@ -113,37 +114,31 @@ TEST(Local, ReplaceDbgDeclare) {
// Original C source to get debug info for a local variable:
// void f() { int x; }
- std::unique_ptr<Module> M = parseIR(
- C,
- "define void @f() !dbg !8 {\n"
- "entry:\n"
- " %x = alloca i32, align 4\n"
- " call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata "
- "!DIExpression()), !dbg !13\n"
- " call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata "
- "!DIExpression()), !dbg !13\n"
- " ret void, !dbg !14\n"
- "}\n"
- "declare void @llvm.dbg.declare(metadata, metadata, metadata)\n"
- "!llvm.dbg.cu = !{!0}\n"
- "!llvm.module.flags = !{!3, !4}\n"
- "!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "
- "\"clang version 6.0.0 \", isOptimized: false, runtimeVersion: 0, "
- "emissionKind: FullDebug, enums: !2)\n"
- "!1 = !DIFile(filename: \"t2.c\", directory: \"foo\")\n"
- "!2 = !{}\n"
- "!3 = !{i32 2, !\"Dwarf Version\", i32 4}\n"
- "!4 = !{i32 2, !\"Debug Info Version\", i32 3}\n"
- "!8 = distinct !DISubprogram(name: \"f\", scope: !1, file: !1, line: 1, "
- "type: !9, isLocal: false, isDefinition: true, scopeLine: 1, "
- "isOptimized: false, unit: !0, variables: !2)\n"
- "!9 = !DISubroutineType(types: !10)\n"
- "!10 = !{null}\n"
- "!11 = !DILocalVariable(name: \"x\", scope: !8, file: !1, line: 2, type: "
- "!12)\n"
- "!12 = !DIBasicType(name: \"int\", size: 32, encoding: DW_ATE_signed)\n"
- "!13 = !DILocation(line: 2, column: 7, scope: !8)\n"
- "!14 = !DILocation(line: 3, column: 1, scope: !8)\n");
+ std::unique_ptr<Module> M = parseIR(C,
+ R"(
+ define void @f() !dbg !8 {
+ entry:
+ %x = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata !DIExpression()), !dbg !13
+ call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata !DIExpression()), !dbg !13
+ ret void, !dbg !14
+ }
+ declare void @llvm.dbg.declare(metadata, metadata, metadata)
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!3, !4}
+ !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+ !1 = !DIFile(filename: "t2.c", directory: "foo")
+ !2 = !{}
+ !3 = !{i32 2, !"Dwarf Version", i32 4}
+ !4 = !{i32 2, !"Debug Info Version", i32 3}
+ !8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2)
+ !9 = !DISubroutineType(types: !10)
+ !10 = !{null}
+ !11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12)
+ !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+ !13 = !DILocation(line: 2, column: 7, scope: !8)
+ !14 = !DILocation(line: 3, column: 1, scope: !8)
+ )");
auto *GV = M->getNamedValue("f");
ASSERT_TRUE(GV);
auto *F = dyn_cast<Function>(GV);
@@ -182,23 +177,24 @@ static void runWithDomTree(
TEST(Local, MergeBasicBlockIntoOnlyPred) {
LLVMContext C;
- std::unique_ptr<Module> M = parseIR(
- C,
- "define i32 @f(i8* %str) {\n"
- "entry:\n"
- " br label %bb2.i\n"
- "bb2.i: ; preds = %bb4.i, %entry\n"
- " br i1 false, label %bb4.i, label %base2flt.exit204\n"
- "bb4.i: ; preds = %bb2.i\n"
- " br i1 false, label %base2flt.exit204, label %bb2.i\n"
- "bb10.i196.bb7.i197_crit_edge: ; No predecessors!\n"
- " br label %bb7.i197\n"
- "bb7.i197: ; preds = %bb10.i196.bb7.i197_crit_edge\n"
- " %.reg2mem.0 = phi i32 [ %.reg2mem.0, %bb10.i196.bb7.i197_crit_edge ]\n"
- " br i1 undef, label %base2flt.exit204, label %base2flt.exit204\n"
- "base2flt.exit204: ; preds = %bb7.i197, %bb7.i197, %bb2.i, %bb4.i\n"
- " ret i32 0\n"
- "}\n");
+ std::unique_ptr<Module> M = parseIR(C,
+ R"(
+ define i32 @f(i8* %str) {
+ entry:
+ br label %bb2.i
+ bb2.i: ; preds = %bb4.i, %entry
+ br i1 false, label %bb4.i, label %base2flt.exit204
+ bb4.i: ; preds = %bb2.i
+ br i1 false, label %base2flt.exit204, label %bb2.i
+ bb10.i196.bb7.i197_crit_edge: ; No predecessors!
+ br label %bb7.i197
+ bb7.i197: ; preds = %bb10.i196.bb7.i197_crit_edge
+ %.reg2mem.0 = phi i32 [ %.reg2mem.0, %bb10.i196.bb7.i197_crit_edge ]
+ br i1 undef, label %base2flt.exit204, label %base2flt.exit204
+ base2flt.exit204: ; preds = %bb7.i197, %bb7.i197, %bb2.i, %bb4.i
+ ret i32 0
+ }
+ )");
runWithDomTree(
*M, "f", [&](Function &F, DominatorTree *DT) {
for (Function::iterator I = F.begin(), E = F.end(); I != E;) {
@@ -212,3 +208,413 @@ TEST(Local, MergeBasicBlockIntoOnlyPred) {
EXPECT_TRUE(DT->verify());
});
}
+
+TEST(Local, ConstantFoldTerminator) {
+ LLVMContext C;
+
+ std::unique_ptr<Module> M = parseIR(C,
+ R"(
+ define void @br_same_dest() {
+ entry:
+ br i1 false, label %bb0, label %bb0
+ bb0:
+ ret void
+ }
+
+ define void @br_different_dest() {
+ entry:
+ br i1 true, label %bb0, label %bb1
+ bb0:
+ br label %exit
+ bb1:
+ br label %exit
+ exit:
+ ret void
+ }
+
+ define void @switch_2_different_dest() {
+ entry:
+ switch i32 0, label %default [ i32 0, label %bb0 ]
+ default:
+ ret void
+ bb0:
+ ret void
+ }
+ define void @switch_2_different_dest_default() {
+ entry:
+ switch i32 1, label %default [ i32 0, label %bb0 ]
+ default:
+ ret void
+ bb0:
+ ret void
+ }
+ define void @switch_3_different_dest() {
+ entry:
+ switch i32 0, label %default [ i32 0, label %bb0
+ i32 1, label %bb1 ]
+ default:
+ ret void
+ bb0:
+ ret void
+ bb1:
+ ret void
+ }
+
+ define void @switch_variable_2_default_dest(i32 %arg) {
+ entry:
+ switch i32 %arg, label %default [ i32 0, label %default ]
+ default:
+ ret void
+ }
+
+ define void @switch_constant_2_default_dest() {
+ entry:
+ switch i32 1, label %default [ i32 0, label %default ]
+ default:
+ ret void
+ }
+
+ define void @switch_constant_3_repeated_dest() {
+ entry:
+ switch i32 0, label %default [ i32 0, label %bb0
+ i32 1, label %bb0 ]
+ bb0:
+ ret void
+ default:
+ ret void
+ }
+
+ define void @indirectbr() {
+ entry:
+ indirectbr i8* blockaddress(@indirectbr, %bb0), [label %bb0, label %bb1]
+ bb0:
+ ret void
+ bb1:
+ ret void
+ }
+
+ define void @indirectbr_repeated() {
+ entry:
+ indirectbr i8* blockaddress(@indirectbr_repeated, %bb0), [label %bb0, label %bb0]
+ bb0:
+ ret void
+ }
+
+ define void @indirectbr_unreachable() {
+ entry:
+ indirectbr i8* blockaddress(@indirectbr_unreachable, %bb0), [label %bb1]
+ bb0:
+ ret void
+ bb1:
+ ret void
+ }
+ )");
+
+ auto CFAllTerminators = [&](Function &F, DominatorTree *DT) {
+ DeferredDominance DDT(*DT);
+ for (Function::iterator I = F.begin(), E = F.end(); I != E;) {
+ BasicBlock *BB = &*I++;
+ ConstantFoldTerminator(BB, true, nullptr, &DDT);
+ }
+
+ EXPECT_TRUE(DDT.flush().verify());
+ };
+
+ runWithDomTree(*M, "br_same_dest", CFAllTerminators);
+ runWithDomTree(*M, "br_different_dest", CFAllTerminators);
+ runWithDomTree(*M, "switch_2_different_dest", CFAllTerminators);
+ runWithDomTree(*M, "switch_2_different_dest_default", CFAllTerminators);
+ runWithDomTree(*M, "switch_3_different_dest", CFAllTerminators);
+ runWithDomTree(*M, "switch_variable_2_default_dest", CFAllTerminators);
+ runWithDomTree(*M, "switch_constant_2_default_dest", CFAllTerminators);
+ runWithDomTree(*M, "switch_constant_3_repeated_dest", CFAllTerminators);
+ runWithDomTree(*M, "indirectbr", CFAllTerminators);
+ runWithDomTree(*M, "indirectbr_repeated", CFAllTerminators);
+ runWithDomTree(*M, "indirectbr_unreachable", CFAllTerminators);
+}
+
+struct SalvageDebugInfoTest : ::testing::Test {
+ LLVMContext C;
+ std::unique_ptr<Module> M;
+ Function *F = nullptr;
+
+ void SetUp() {
+ M = parseIR(C,
+ R"(
+ define void @f() !dbg !8 {
+ entry:
+ %x = add i32 0, 1
+ %y = add i32 %x, 2
+ call void @llvm.dbg.value(metadata i32 %x, metadata !11, metadata !DIExpression()), !dbg !13
+ call void @llvm.dbg.value(metadata i32 %y, metadata !11, metadata !DIExpression()), !dbg !13
+ ret void, !dbg !14
+ }
+ declare void @llvm.dbg.value(metadata, metadata, metadata)
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!3, !4}
+ !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+ !1 = !DIFile(filename: "t2.c", directory: "foo")
+ !2 = !{}
+ !3 = !{i32 2, !"Dwarf Version", i32 4}
+ !4 = !{i32 2, !"Debug Info Version", i32 3}
+ !8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2)
+ !9 = !DISubroutineType(types: !10)
+ !10 = !{null}
+ !11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12)
+ !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+ !13 = !DILocation(line: 2, column: 7, scope: !8)
+ !14 = !DILocation(line: 3, column: 1, scope: !8)
+ )");
+
+ auto *GV = M->getNamedValue("f");
+ ASSERT_TRUE(GV);
+ F = dyn_cast<Function>(GV);
+ ASSERT_TRUE(F);
+ }
+
+ bool doesDebugValueDescribeX(const DbgValueInst &DI) {
+ const auto &CI = *cast<ConstantInt>(DI.getValue());
+ if (CI.isZero())
+ return DI.getExpression()->getElements().equals(
+ {dwarf::DW_OP_plus_uconst, 1, dwarf::DW_OP_stack_value});
+ else if (CI.isOneValue())
+ return DI.getExpression()->getElements().empty();
+ return false;
+ }
+
+ bool doesDebugValueDescribeY(const DbgValueInst &DI) {
+ const auto &CI = *cast<ConstantInt>(DI.getValue());
+ if (CI.isZero())
+ return DI.getExpression()->getElements().equals(
+ {dwarf::DW_OP_plus_uconst, 1, dwarf::DW_OP_plus_uconst, 2,
+ dwarf::DW_OP_stack_value});
+ else if (CI.isOneValue())
+ return DI.getExpression()->getElements().equals(
+ {dwarf::DW_OP_plus_uconst, 2, dwarf::DW_OP_stack_value});
+ return false;
+ }
+
+ void verifyDebugValuesAreSalvaged() {
+ // Check that the debug values for %x and %y are preserved.
+ bool FoundX = false;
+ bool FoundY = false;
+ for (const Instruction &I : F->front()) {
+ auto DI = dyn_cast<DbgValueInst>(&I);
+ if (!DI) {
+ // The function should only contain debug values and a terminator.
+ ASSERT_TRUE(isa<TerminatorInst>(&I));
+ continue;
+ }
+ EXPECT_EQ(DI->getVariable()->getName(), "x");
+ FoundX |= doesDebugValueDescribeX(*DI);
+ FoundY |= doesDebugValueDescribeY(*DI);
+ }
+ ASSERT_TRUE(FoundX);
+ ASSERT_TRUE(FoundY);
+ }
+};
+
+TEST_F(SalvageDebugInfoTest, RecursiveInstDeletion) {
+ Instruction *Inst = &F->front().front();
+ Inst = Inst->getNextNode(); // Get %y = add ...
+ ASSERT_TRUE(Inst);
+ bool Deleted = RecursivelyDeleteTriviallyDeadInstructions(Inst);
+ ASSERT_TRUE(Deleted);
+ verifyDebugValuesAreSalvaged();
+}
+
+TEST_F(SalvageDebugInfoTest, RecursiveBlockSimplification) {
+ BasicBlock *BB = &F->front();
+ ASSERT_TRUE(BB);
+ bool Deleted = SimplifyInstructionsInBlock(BB);
+ ASSERT_TRUE(Deleted);
+ verifyDebugValuesAreSalvaged();
+}
+
+TEST(Local, ReplaceAllDbgUsesWith) {
+ using namespace llvm::dwarf;
+
+ LLVMContext Ctx;
+
+ // Note: The datalayout simulates Darwin/x86_64.
+ std::unique_ptr<Module> M = parseIR(Ctx,
+ R"(
+ target datalayout = "e-m:o-i63:64-f80:128-n8:16:32:64-S128"
+
+ declare i32 @escape(i32)
+
+ define void @f() !dbg !6 {
+ entry:
+ %a = add i32 0, 1, !dbg !15
+ call void @llvm.dbg.value(metadata i32 %a, metadata !9, metadata !DIExpression()), !dbg !15
+
+ %b = add i64 0, 1, !dbg !16
+ call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression()), !dbg !16
+ call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul)), !dbg !16
+ call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul, DW_OP_stack_value)), !dbg !16
+ call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 8)), !dbg !16
+ call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul, DW_OP_LLVM_fragment, 0, 8)), !dbg !16
+ call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 8)), !dbg !16
+
+ %c = inttoptr i64 0 to i64*, !dbg !17
+ call void @llvm.dbg.declare(metadata i64* %c, metadata !13, metadata !DIExpression()), !dbg !17
+
+ %d = inttoptr i64 0 to i32*, !dbg !18
+ call void @llvm.dbg.addr(metadata i32* %d, metadata !20, metadata !DIExpression()), !dbg !18
+
+ %e = add <2 x i16> zeroinitializer, zeroinitializer
+ call void @llvm.dbg.value(metadata <2 x i16> %e, metadata !14, metadata !DIExpression()), !dbg !18
+
+ %f = call i32 @escape(i32 0)
+ call void @llvm.dbg.value(metadata i32 %f, metadata !9, metadata !DIExpression()), !dbg !15
+
+ %barrier = call i32 @escape(i32 0)
+
+ %g = call i32 @escape(i32 %f)
+ call void @llvm.dbg.value(metadata i32 %g, metadata !9, metadata !DIExpression()), !dbg !15
+
+ ret void, !dbg !19
+ }
+
+ declare void @llvm.dbg.addr(metadata, metadata, metadata)
+ declare void @llvm.dbg.declare(metadata, metadata, metadata)
+ declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!5}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+ !1 = !DIFile(filename: "/Users/vsk/Desktop/foo.ll", directory: "/")
+ !2 = !{}
+ !5 = !{i32 2, !"Debug Info Version", i32 3}
+ !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !8)
+ !7 = !DISubroutineType(types: !2)
+ !8 = !{!9, !11, !13, !14}
+ !9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
+ !10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_signed)
+ !11 = !DILocalVariable(name: "2", scope: !6, file: !1, line: 2, type: !12)
+ !12 = !DIBasicType(name: "ty64", size: 64, encoding: DW_ATE_signed)
+ !13 = !DILocalVariable(name: "3", scope: !6, file: !1, line: 3, type: !12)
+ !14 = !DILocalVariable(name: "4", scope: !6, file: !1, line: 4, type: !10)
+ !15 = !DILocation(line: 1, column: 1, scope: !6)
+ !16 = !DILocation(line: 2, column: 1, scope: !6)
+ !17 = !DILocation(line: 3, column: 1, scope: !6)
+ !18 = !DILocation(line: 4, column: 1, scope: !6)
+ !19 = !DILocation(line: 5, column: 1, scope: !6)
+ !20 = !DILocalVariable(name: "5", scope: !6, file: !1, line: 5, type: !10)
+ )");
+
+ bool BrokenDebugInfo = true;
+ verifyModule(*M, &errs(), &BrokenDebugInfo);
+ ASSERT_FALSE(BrokenDebugInfo);
+
+ Function &F = *cast<Function>(M->getNamedValue("f"));
+ DominatorTree DT{F};
+
+ BasicBlock &BB = F.front();
+ Instruction &A = BB.front();
+ Instruction &B = *A.getNextNonDebugInstruction();
+ Instruction &C = *B.getNextNonDebugInstruction();
+ Instruction &D = *C.getNextNonDebugInstruction();
+ Instruction &E = *D.getNextNonDebugInstruction();
+ Instruction &F_ = *E.getNextNonDebugInstruction();
+ Instruction &Barrier = *F_.getNextNonDebugInstruction();
+ Instruction &G = *Barrier.getNextNonDebugInstruction();
+
+ // Simulate i32 <-> i64* conversion. Expect no updates: the datalayout says
+ // pointers are 64 bits, so the conversion would be lossy.
+ EXPECT_FALSE(replaceAllDbgUsesWith(A, C, C, DT));
+ EXPECT_FALSE(replaceAllDbgUsesWith(C, A, A, DT));
+
+ // Simulate i32 <-> <2 x i16> conversion. This is unsupported.
+ EXPECT_FALSE(replaceAllDbgUsesWith(E, A, A, DT));
+ EXPECT_FALSE(replaceAllDbgUsesWith(A, E, E, DT));
+
+ // Simulate i32* <-> i64* conversion.
+ EXPECT_TRUE(replaceAllDbgUsesWith(D, C, C, DT));
+
+ SmallVector<DbgInfoIntrinsic *, 2> CDbgVals;
+ findDbgUsers(CDbgVals, &C);
+ EXPECT_EQ(2U, CDbgVals.size());
+ EXPECT_TRUE(any_of(CDbgVals, [](DbgInfoIntrinsic *DII) {
+ return isa<DbgAddrIntrinsic>(DII);
+ }));
+ EXPECT_TRUE(any_of(CDbgVals, [](DbgInfoIntrinsic *DII) {
+ return isa<DbgDeclareInst>(DII);
+ }));
+
+ EXPECT_TRUE(replaceAllDbgUsesWith(C, D, D, DT));
+
+ SmallVector<DbgInfoIntrinsic *, 2> DDbgVals;
+ findDbgUsers(DDbgVals, &D);
+ EXPECT_EQ(2U, DDbgVals.size());
+ EXPECT_TRUE(any_of(DDbgVals, [](DbgInfoIntrinsic *DII) {
+ return isa<DbgAddrIntrinsic>(DII);
+ }));
+ EXPECT_TRUE(any_of(DDbgVals, [](DbgInfoIntrinsic *DII) {
+ return isa<DbgDeclareInst>(DII);
+ }));
+
+ // Introduce a use-before-def. Check that the dbg.value for %a is salvaged.
+ EXPECT_TRUE(replaceAllDbgUsesWith(A, F_, F_, DT));
+
+ auto *ADbgVal = cast<DbgValueInst>(A.getNextNode());
+ EXPECT_EQ(ConstantInt::get(A.getType(), 0), ADbgVal->getVariableLocation());
+
+ // Introduce a use-before-def. Check that the dbg.values for %f are deleted.
+ EXPECT_TRUE(replaceAllDbgUsesWith(F_, G, G, DT));
+
+ SmallVector<DbgValueInst *, 1> FDbgVals;
+ findDbgValues(FDbgVals, &F);
+ EXPECT_EQ(0U, FDbgVals.size());
+
+ // Simulate i32 -> i64 conversion to test sign-extension. Here are some
+ // interesting cases to handle:
+ // 1) debug user has empty DIExpression
+ // 2) debug user has non-empty, non-stack-value'd DIExpression
+ // 3) debug user has non-empty, stack-value'd DIExpression
+ // 4-6) like (1-3), but with a fragment
+ EXPECT_TRUE(replaceAllDbgUsesWith(B, A, A, DT));
+
+ SmallVector<DbgValueInst *, 8> ADbgVals;
+ findDbgValues(ADbgVals, &A);
+ EXPECT_EQ(6U, ADbgVals.size());
+
+ // Check that %a has a dbg.value with a DIExpression matching \p Ops.
+ auto hasADbgVal = [&](ArrayRef<uint64_t> Ops) {
+ return any_of(ADbgVals, [&](DbgValueInst *DVI) {
+ assert(DVI->getVariable()->getName() == "2");
+ return DVI->getExpression()->getElements() == Ops;
+ });
+ };
+
+ // Case 1: The original expr is empty, so no deref is needed.
+ EXPECT_TRUE(hasADbgVal({DW_OP_dup, DW_OP_constu, 31, DW_OP_shr, DW_OP_lit0,
+ DW_OP_not, DW_OP_mul, DW_OP_or, DW_OP_stack_value}));
+
+ // Case 2: Perform an address calculation with the original expr, deref it,
+ // then sign-extend the result.
+ EXPECT_TRUE(hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_deref, DW_OP_dup,
+ DW_OP_constu, 31, DW_OP_shr, DW_OP_lit0, DW_OP_not,
+ DW_OP_mul, DW_OP_or, DW_OP_stack_value}));
+
+ // Case 3: Insert the sign-extension logic before the DW_OP_stack_value.
+ EXPECT_TRUE(hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_dup, DW_OP_constu, 31,
+ DW_OP_shr, DW_OP_lit0, DW_OP_not, DW_OP_mul, DW_OP_or,
+ DW_OP_stack_value}));
+
+ // Cases 4-6: Just like cases 1-3, but preserve the fragment at the end.
+ EXPECT_TRUE(hasADbgVal({DW_OP_dup, DW_OP_constu, 31, DW_OP_shr, DW_OP_lit0,
+ DW_OP_not, DW_OP_mul, DW_OP_or, DW_OP_stack_value,
+ DW_OP_LLVM_fragment, 0, 8}));
+ EXPECT_TRUE(
+ hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_deref, DW_OP_dup, DW_OP_constu,
+ 31, DW_OP_shr, DW_OP_lit0, DW_OP_not, DW_OP_mul, DW_OP_or,
+ DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 8}));
+ EXPECT_TRUE(hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_dup, DW_OP_constu, 31,
+ DW_OP_shr, DW_OP_lit0, DW_OP_not, DW_OP_mul, DW_OP_or,
+ DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 8}));
+
+ verifyModule(*M, &errs(), &BrokenDebugInfo);
+ ASSERT_FALSE(BrokenDebugInfo);
+}
diff --git a/unittests/Transforms/Utils/SSAUpdaterBulk.cpp b/unittests/Transforms/Utils/SSAUpdaterBulk.cpp
new file mode 100644
index 000000000000..61cbcb7b1a77
--- /dev/null
+++ b/unittests/Transforms/Utils/SSAUpdaterBulk.cpp
@@ -0,0 +1,195 @@
+//===- SSAUpdaterBulk.cpp - Unit tests for SSAUpdaterBulk -----------------===//
+//
+// 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/SSAUpdaterBulk.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(SSAUpdaterBulk, SimpleMerge) {
+ SSAUpdaterBulk Updater;
+ LLVMContext C;
+ Module M("SSAUpdaterTest", C);
+ IRBuilder<> B(C);
+ Type *I32Ty = B.getInt32Ty();
+ auto *F = Function::Create(FunctionType::get(B.getVoidTy(), {I32Ty}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ // Generate a simple program:
+ // if:
+ // br i1 true, label %true, label %false
+ // true:
+ // %1 = add i32 %0, 1
+ // %2 = sub i32 %0, 2
+ // br label %merge
+ // false:
+ // %3 = add i32 %0, 3
+ // %4 = sub i32 %0, 4
+ // br label %merge
+ // merge:
+ // %5 = add i32 %1, 5
+ // %6 = add i32 %3, 6
+ // %7 = add i32 %2, %4
+ // %8 = sub i32 %2, %4
+ Argument *FirstArg = &*(F->arg_begin());
+ BasicBlock *IfBB = BasicBlock::Create(C, "if", F);
+ BasicBlock *TrueBB = BasicBlock::Create(C, "true", F);
+ BasicBlock *FalseBB = BasicBlock::Create(C, "false", F);
+ BasicBlock *MergeBB = BasicBlock::Create(C, "merge", F);
+
+ B.SetInsertPoint(IfBB);
+ B.CreateCondBr(B.getTrue(), TrueBB, FalseBB);
+
+ B.SetInsertPoint(TrueBB);
+ Value *AddOp1 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 1));
+ Value *SubOp1 = B.CreateSub(FirstArg, ConstantInt::get(I32Ty, 2));
+ B.CreateBr(MergeBB);
+
+ B.SetInsertPoint(FalseBB);
+ Value *AddOp2 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 3));
+ Value *SubOp2 = B.CreateSub(FirstArg, ConstantInt::get(I32Ty, 4));
+ B.CreateBr(MergeBB);
+
+ B.SetInsertPoint(MergeBB, MergeBB->begin());
+ auto *I1 = cast<Instruction>(B.CreateAdd(AddOp1, ConstantInt::get(I32Ty, 5)));
+ auto *I2 = cast<Instruction>(B.CreateAdd(AddOp2, ConstantInt::get(I32Ty, 6)));
+ auto *I3 = cast<Instruction>(B.CreateAdd(SubOp1, SubOp2));
+ auto *I4 = cast<Instruction>(B.CreateSub(SubOp1, SubOp2));
+
+ // Now rewrite uses in instructions %5, %6, %7. They need to use a phi, which
+ // SSAUpdater should insert into %merge.
+ // Intentionally don't touch %8 to see that SSAUpdater only changes
+ // instructions that were explicitly specified.
+ unsigned VarNum = Updater.AddVariable("a", I32Ty);
+ Updater.AddAvailableValue(VarNum, TrueBB, AddOp1);
+ Updater.AddAvailableValue(VarNum, FalseBB, AddOp2);
+ Updater.AddUse(VarNum, &I1->getOperandUse(0));
+ Updater.AddUse(VarNum, &I2->getOperandUse(0));
+
+ VarNum = Updater.AddVariable("b", I32Ty);
+ Updater.AddAvailableValue(VarNum, TrueBB, SubOp1);
+ Updater.AddAvailableValue(VarNum, FalseBB, SubOp2);
+ Updater.AddUse(VarNum, &I3->getOperandUse(0));
+ Updater.AddUse(VarNum, &I3->getOperandUse(1));
+
+ DominatorTree DT(*F);
+ Updater.RewriteAllUses(&DT);
+
+ // Check how %5 and %6 were rewritten.
+ PHINode *UpdatePhiA = dyn_cast_or_null<PHINode>(I1->getOperand(0));
+ EXPECT_NE(UpdatePhiA, nullptr);
+ EXPECT_EQ(UpdatePhiA->getIncomingValueForBlock(TrueBB), AddOp1);
+ EXPECT_EQ(UpdatePhiA->getIncomingValueForBlock(FalseBB), AddOp2);
+ EXPECT_EQ(UpdatePhiA, dyn_cast_or_null<PHINode>(I1->getOperand(0)));
+
+ // Check how %7 was rewritten.
+ PHINode *UpdatePhiB = dyn_cast_or_null<PHINode>(I3->getOperand(0));
+ EXPECT_EQ(UpdatePhiB->getIncomingValueForBlock(TrueBB), SubOp1);
+ EXPECT_EQ(UpdatePhiB->getIncomingValueForBlock(FalseBB), SubOp2);
+ EXPECT_EQ(UpdatePhiB, dyn_cast_or_null<PHINode>(I3->getOperand(1)));
+
+ // Check that %8 was kept untouched.
+ EXPECT_EQ(I4->getOperand(0), SubOp1);
+ EXPECT_EQ(I4->getOperand(1), SubOp2);
+}
+
+TEST(SSAUpdaterBulk, Irreducible) {
+ SSAUpdaterBulk Updater;
+ LLVMContext C;
+ Module M("SSAUpdaterTest", C);
+ IRBuilder<> B(C);
+ Type *I32Ty = B.getInt32Ty();
+ auto *F = Function::Create(FunctionType::get(B.getVoidTy(), {I32Ty}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ // Generate a small program with a multi-entry loop:
+ // if:
+ // %1 = add i32 %0, 1
+ // br i1 true, label %loopmain, label %loopstart
+ //
+ // loopstart:
+ // %2 = add i32 %0, 2
+ // br label %loopmain
+ //
+ // loopmain:
+ // %3 = add i32 %1, 3
+ // br i1 true, label %loopstart, label %afterloop
+ //
+ // afterloop:
+ // %4 = add i32 %2, 4
+ // ret i32 %0
+ Argument *FirstArg = &*F->arg_begin();
+ BasicBlock *IfBB = BasicBlock::Create(C, "if", F);
+ BasicBlock *LoopStartBB = BasicBlock::Create(C, "loopstart", F);
+ BasicBlock *LoopMainBB = BasicBlock::Create(C, "loopmain", F);
+ BasicBlock *AfterLoopBB = BasicBlock::Create(C, "afterloop", F);
+
+ B.SetInsertPoint(IfBB);
+ Value *AddOp1 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 1));
+ B.CreateCondBr(B.getTrue(), LoopMainBB, LoopStartBB);
+
+ B.SetInsertPoint(LoopStartBB);
+ Value *AddOp2 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 2));
+ B.CreateBr(LoopMainBB);
+
+ B.SetInsertPoint(LoopMainBB);
+ auto *I1 = cast<Instruction>(B.CreateAdd(AddOp1, ConstantInt::get(I32Ty, 3)));
+ B.CreateCondBr(B.getTrue(), LoopStartBB, AfterLoopBB);
+
+ B.SetInsertPoint(AfterLoopBB);
+ auto *I2 = cast<Instruction>(B.CreateAdd(AddOp2, ConstantInt::get(I32Ty, 4)));
+ ReturnInst *Return = B.CreateRet(FirstArg);
+
+ // Now rewrite uses in instructions %3, %4, and 'ret i32 %0'. Only %4 needs a
+ // new phi, others should be able to work with existing values.
+ // The phi for %4 should be inserted into LoopMainBB and should look like
+ // this:
+ // %b = phi i32 [ %2, %loopstart ], [ undef, %if ]
+ // No other rewrites should be made.
+
+ // Add use in %3.
+ unsigned VarNum = Updater.AddVariable("c", I32Ty);
+ Updater.AddAvailableValue(VarNum, IfBB, AddOp1);
+ Updater.AddUse(VarNum, &I1->getOperandUse(0));
+
+ // Add use in %4.
+ VarNum = Updater.AddVariable("b", I32Ty);
+ Updater.AddAvailableValue(VarNum, LoopStartBB, AddOp2);
+ Updater.AddUse(VarNum, &I2->getOperandUse(0));
+
+ // Add use in the return instruction.
+ VarNum = Updater.AddVariable("a", I32Ty);
+ Updater.AddAvailableValue(VarNum, &F->getEntryBlock(), FirstArg);
+ Updater.AddUse(VarNum, &Return->getOperandUse(0));
+
+ // Save all inserted phis into a vector.
+ SmallVector<PHINode *, 8> Inserted;
+ DominatorTree DT(*F);
+ Updater.RewriteAllUses(&DT, &Inserted);
+
+ // Only one phi should have been inserted.
+ EXPECT_EQ(Inserted.size(), 1u);
+
+ // I1 and Return should use the same values as they used before.
+ EXPECT_EQ(I1->getOperand(0), AddOp1);
+ EXPECT_EQ(Return->getOperand(0), FirstArg);
+
+ // I2 should use the new phi.
+ PHINode *UpdatePhi = dyn_cast_or_null<PHINode>(I2->getOperand(0));
+ EXPECT_NE(UpdatePhi, nullptr);
+ EXPECT_EQ(UpdatePhi->getIncomingValueForBlock(LoopStartBB), AddOp2);
+ EXPECT_EQ(UpdatePhi->getIncomingValueForBlock(IfBB), UndefValue::get(I32Ty));
+}
diff --git a/unittests/Transforms/Vectorize/CMakeLists.txt b/unittests/Transforms/Vectorize/CMakeLists.txt
new file mode 100644
index 000000000000..4f2b8e9a139c
--- /dev/null
+++ b/unittests/Transforms/Vectorize/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(LLVM_LINK_COMPONENTS
+ Analysis
+ Core
+ Vectorize
+ AsmParser
+ )
+
+add_llvm_unittest(VectorizeTests
+ VPlanTest.cpp
+ VPlanHCFGTest.cpp
+ )
diff --git a/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp b/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp
new file mode 100644
index 000000000000..215c3ef40daa
--- /dev/null
+++ b/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp
@@ -0,0 +1,158 @@
+//===- llvm/unittest/Transforms/Vectorize/VPlanHCFGTest.cpp ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../lib/Transforms/Vectorize/VPlan.h"
+#include "../lib/Transforms/Vectorize/VPlanHCFGTransforms.h"
+#include "VPlanTestBase.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace {
+
+class VPlanHCFGTest : public VPlanTestBase {};
+
+TEST_F(VPlanHCFGTest, testBuildHCFGInnerLoop) {
+ const char *ModuleString =
+ "define void @f(i32* %A, i64 %N) {\n"
+ "entry:\n"
+ " br label %for.body\n"
+ "for.body:\n"
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
+ " %arr.idx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv\n"
+ " %l1 = load i32, i32* %arr.idx, align 4\n"
+ " %res = add i32 %l1, 10\n"
+ " store i32 %res, i32* %arr.idx, align 4\n"
+ " %indvars.iv.next = add i64 %indvars.iv, 1\n"
+ " %exitcond = icmp ne i64 %indvars.iv.next, %N\n"
+ " br i1 %exitcond, label %for.body, label %for.end\n"
+ "for.end:\n"
+ " ret void\n"
+ "}\n";
+
+ Module &M = parseModule(ModuleString);
+
+ Function *F = M.getFunction("f");
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
+ auto Plan = buildHCFG(LoopHeader);
+
+ VPBasicBlock *Entry = Plan->getEntry()->getEntryBasicBlock();
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
+ EXPECT_EQ(0u, Entry->getNumPredecessors());
+ EXPECT_EQ(1u, Entry->getNumSuccessors());
+ EXPECT_EQ(nullptr, Entry->getCondBit());
+
+ VPBasicBlock *VecBB = Entry->getSingleSuccessor()->getEntryBasicBlock();
+ EXPECT_EQ(7u, VecBB->size());
+ EXPECT_EQ(2u, VecBB->getNumPredecessors());
+ EXPECT_EQ(2u, VecBB->getNumSuccessors());
+
+ auto Iter = VecBB->begin();
+ VPInstruction *Phi = dyn_cast<VPInstruction>(&*Iter++);
+ EXPECT_EQ(Instruction::PHI, Phi->getOpcode());
+
+ VPInstruction *Idx = dyn_cast<VPInstruction>(&*Iter++);
+ EXPECT_EQ(Instruction::GetElementPtr, Idx->getOpcode());
+ EXPECT_EQ(2u, Idx->getNumOperands());
+ EXPECT_EQ(Phi, Idx->getOperand(1));
+
+ VPInstruction *Load = dyn_cast<VPInstruction>(&*Iter++);
+ EXPECT_EQ(Instruction::Load, Load->getOpcode());
+ EXPECT_EQ(1u, Load->getNumOperands());
+ EXPECT_EQ(Idx, Load->getOperand(0));
+
+ VPInstruction *Add = dyn_cast<VPInstruction>(&*Iter++);
+ EXPECT_EQ(Instruction::Add, Add->getOpcode());
+ EXPECT_EQ(2u, Add->getNumOperands());
+ EXPECT_EQ(Load, Add->getOperand(0));
+
+ VPInstruction *Store = dyn_cast<VPInstruction>(&*Iter++);
+ EXPECT_EQ(Instruction::Store, Store->getOpcode());
+ EXPECT_EQ(2u, Store->getNumOperands());
+ EXPECT_EQ(Add, Store->getOperand(0));
+ EXPECT_EQ(Idx, Store->getOperand(1));
+
+ VPInstruction *IndvarAdd = dyn_cast<VPInstruction>(&*Iter++);
+ EXPECT_EQ(Instruction::Add, IndvarAdd->getOpcode());
+ EXPECT_EQ(2u, IndvarAdd->getNumOperands());
+ EXPECT_EQ(Phi, IndvarAdd->getOperand(0));
+
+ VPInstruction *ICmp = dyn_cast<VPInstruction>(&*Iter++);
+ EXPECT_EQ(Instruction::ICmp, ICmp->getOpcode());
+ EXPECT_EQ(2u, ICmp->getNumOperands());
+ EXPECT_EQ(IndvarAdd, ICmp->getOperand(0));
+ EXPECT_EQ(VecBB->getCondBit(), ICmp);
+
+ LoopVectorizationLegality::InductionList Inductions;
+ SmallPtrSet<Instruction *, 1> DeadInstructions;
+ VPlanHCFGTransforms::VPInstructionsToVPRecipes(Plan, &Inductions,
+ DeadInstructions);
+}
+
+TEST_F(VPlanHCFGTest, testVPInstructionToVPRecipesInner) {
+ const char *ModuleString =
+ "define void @f(i32* %A, i64 %N) {\n"
+ "entry:\n"
+ " br label %for.body\n"
+ "for.body:\n"
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
+ " %arr.idx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv\n"
+ " %l1 = load i32, i32* %arr.idx, align 4\n"
+ " %res = add i32 %l1, 10\n"
+ " store i32 %res, i32* %arr.idx, align 4\n"
+ " %indvars.iv.next = add i64 %indvars.iv, 1\n"
+ " %exitcond = icmp ne i64 %indvars.iv.next, %N\n"
+ " br i1 %exitcond, label %for.body, label %for.end\n"
+ "for.end:\n"
+ " ret void\n"
+ "}\n";
+
+ Module &M = parseModule(ModuleString);
+
+ Function *F = M.getFunction("f");
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
+ auto Plan = buildHCFG(LoopHeader);
+
+ LoopVectorizationLegality::InductionList Inductions;
+ SmallPtrSet<Instruction *, 1> DeadInstructions;
+ VPlanHCFGTransforms::VPInstructionsToVPRecipes(Plan, &Inductions,
+ DeadInstructions);
+
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
+ EXPECT_EQ(0u, Entry->getNumPredecessors());
+ EXPECT_EQ(1u, Entry->getNumSuccessors());
+
+ VPBasicBlock *VecBB = Entry->getSingleSuccessor()->getEntryBasicBlock();
+ EXPECT_EQ(6u, VecBB->size());
+ EXPECT_EQ(2u, VecBB->getNumPredecessors());
+ EXPECT_EQ(2u, VecBB->getNumSuccessors());
+
+ auto Iter = VecBB->begin();
+ auto *Phi = dyn_cast<VPWidenPHIRecipe>(&*Iter++);
+ EXPECT_NE(nullptr, Phi);
+
+ auto *Idx = dyn_cast<VPWidenRecipe>(&*Iter++);
+ EXPECT_NE(nullptr, Idx);
+
+ auto *Load = dyn_cast<VPWidenMemoryInstructionRecipe>(&*Iter++);
+ EXPECT_NE(nullptr, Load);
+
+ auto *Add = dyn_cast<VPWidenRecipe>(&*Iter++);
+ EXPECT_NE(nullptr, Add);
+
+ auto *Store = dyn_cast<VPWidenMemoryInstructionRecipe>(&*Iter++);
+ EXPECT_NE(nullptr, Store);
+
+ auto *LastWiden = dyn_cast<VPWidenRecipe>(&*Iter++);
+ EXPECT_NE(nullptr, LastWiden);
+ EXPECT_EQ(VecBB->end(), Iter);
+}
+
+} // namespace
+} // namespace llvm
diff --git a/unittests/Transforms/Vectorize/VPlanTest.cpp b/unittests/Transforms/Vectorize/VPlanTest.cpp
new file mode 100644
index 000000000000..67712a7cae2e
--- /dev/null
+++ b/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -0,0 +1,64 @@
+//===- llvm/unittests/Transforms/Vectorize/VPlanTest.cpp - VPlan tests ----===//
+//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../lib/Transforms/Vectorize/VPlan.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace {
+
+#define CHECK_ITERATOR(Range1, ...) \
+ do { \
+ std::vector<VPInstruction *> Tmp = {__VA_ARGS__}; \
+ EXPECT_EQ((size_t)std::distance(Range1.begin(), Range1.end()), \
+ Tmp.size()); \
+ for (auto Pair : zip(Range1, make_range(Tmp.begin(), Tmp.end()))) \
+ EXPECT_EQ(&std::get<0>(Pair), std::get<1>(Pair)); \
+ } while (0)
+
+TEST(VPInstructionTest, insertBefore) {
+ VPInstruction *I1 = new VPInstruction(0, {});
+ VPInstruction *I2 = new VPInstruction(1, {});
+ VPInstruction *I3 = new VPInstruction(2, {});
+
+ VPBasicBlock VPBB1;
+ VPBB1.appendRecipe(I1);
+
+ I2->insertBefore(I1);
+ CHECK_ITERATOR(VPBB1, I2, I1);
+
+ I3->insertBefore(I2);
+ CHECK_ITERATOR(VPBB1, I3, I2, I1);
+}
+
+TEST(VPInstructionTest, eraseFromParent) {
+ VPInstruction *I1 = new VPInstruction(0, {});
+ VPInstruction *I2 = new VPInstruction(1, {});
+ VPInstruction *I3 = new VPInstruction(2, {});
+
+ VPBasicBlock VPBB1;
+ VPBB1.appendRecipe(I1);
+ VPBB1.appendRecipe(I2);
+ VPBB1.appendRecipe(I3);
+
+ I2->eraseFromParent();
+ CHECK_ITERATOR(VPBB1, I1, I3);
+
+ I1->eraseFromParent();
+ CHECK_ITERATOR(VPBB1, I3);
+
+ I3->eraseFromParent();
+ EXPECT_TRUE(VPBB1.empty());
+}
+
+} // namespace
+} // namespace llvm
diff --git a/unittests/Transforms/Vectorize/VPlanTestBase.h b/unittests/Transforms/Vectorize/VPlanTestBase.h
new file mode 100644
index 000000000000..da3b39f4df01
--- /dev/null
+++ b/unittests/Transforms/Vectorize/VPlanTestBase.h
@@ -0,0 +1,61 @@
+//===- llvm/unittest/Transforms/Vectorize/VPlanTestBase.h -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file defines a VPlanTestBase class, which provides helpers to parse
+/// a LLVM IR string and create VPlans given a loop entry block.
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_UNITTESTS_TRANSFORMS_VECTORIZE_VPLANTESTBASE_H
+#define LLVM_UNITTESTS_TRANSFORMS_VECTORIZE_VPLANTESTBASE_H
+
+#include "../lib/Transforms/Vectorize/VPlan.h"
+#include "../lib/Transforms/Vectorize/VPlanHCFGBuilder.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+
+/// Helper class to create a module from an assembly string and VPlans for a
+/// given loop entry block.
+class VPlanTestBase : public testing::Test {
+protected:
+ std::unique_ptr<LLVMContext> Ctx;
+ std::unique_ptr<Module> M;
+ std::unique_ptr<LoopInfo> LI;
+ std::unique_ptr<DominatorTree> DT;
+
+ VPlanTestBase() : Ctx(new LLVMContext) {}
+
+ Module &parseModule(const char *ModuleString) {
+ SMDiagnostic Err;
+ M = parseAssemblyString(ModuleString, Err, *Ctx);
+ EXPECT_TRUE(M);
+ return *M;
+ }
+
+ void doAnalysis(Function &F) {
+ DT.reset(new DominatorTree(F));
+ LI.reset(new LoopInfo(*DT));
+ }
+
+ VPlanPtr buildHCFG(BasicBlock *LoopHeader) {
+ doAnalysis(*LoopHeader->getParent());
+
+ auto Plan = llvm::make_unique<VPlan>();
+ VPlanHCFGBuilder HCFGBuilder(LI->getLoopFor(LoopHeader), LI.get());
+ HCFGBuilder.buildHierarchicalCFG(*Plan.get());
+ return Plan;
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_UNITTESTS_TRANSFORMS_VECTORIZE_VPLANTESTBASE_H
diff --git a/unittests/XRay/CMakeLists.txt b/unittests/XRay/CMakeLists.txt
index 30bccd1bbe62..9872c901d137 100644
--- a/unittests/XRay/CMakeLists.txt
+++ b/unittests/XRay/CMakeLists.txt
@@ -2,12 +2,8 @@ set(LLVM_LINK_COMPONENTS
Support
)
-set(XRAYSources
- GraphTest.cpp
- )
-
add_llvm_unittest(XRayTests
- ${XRAYSources}
+ GraphTest.cpp
)
add_dependencies(XRayTests intrinsics_gen)
diff --git a/unittests/tools/CMakeLists.txt b/unittests/tools/CMakeLists.txt
index 5ac4c950efe7..e7c7dca68d49 100644
--- a/unittests/tools/CMakeLists.txt
+++ b/unittests/tools/CMakeLists.txt
@@ -1,4 +1,10 @@
if(LLVM_TARGETS_TO_BUILD MATCHES "X86")
- add_subdirectory(llvm-cfi-verify)
+ add_subdirectory(
+ llvm-cfi-verify
+ )
endif()
+add_subdirectory(
+ llvm-exegesis
+)
+
diff --git a/unittests/tools/llvm-cfi-verify/CMakeLists.txt b/unittests/tools/llvm-cfi-verify/CMakeLists.txt
index e47bbdf7f131..8f865463de1b 100644
--- a/unittests/tools/llvm-cfi-verify/CMakeLists.txt
+++ b/unittests/tools/llvm-cfi-verify/CMakeLists.txt
@@ -13,5 +13,6 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(CFIVerifyTests
FileAnalysis.cpp
- GraphBuilder.cpp)
+ GraphBuilder.cpp
+ )
target_link_libraries(CFIVerifyTests PRIVATE LLVMCFIVerify)
diff --git a/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp b/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp
index 27275d6e2312..05880fcec805 100644
--- a/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp
+++ b/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp
@@ -45,10 +45,10 @@ using ::testing::Field;
namespace llvm {
namespace cfi_verify {
namespace {
-class ELFx86TestFileAnalysis : public FileAnalysis {
+class ELFTestFileAnalysis : public FileAnalysis {
public:
- ELFx86TestFileAnalysis()
- : FileAnalysis(Triple("x86_64--"), SubtargetFeatures()) {}
+ ELFTestFileAnalysis(StringRef Trip)
+ : FileAnalysis(Triple(Trip), SubtargetFeatures()) {}
// Expose this method publicly for testing.
void parseSectionContents(ArrayRef<uint8_t> SectionBytes,
@@ -62,6 +62,8 @@ public:
};
class BasicFileAnalysisTest : public ::testing::Test {
+public:
+ BasicFileAnalysisTest(StringRef Trip) : Analysis(Trip) {}
protected:
virtual void SetUp() {
IgnoreDWARFFlag = true;
@@ -70,17 +72,27 @@ protected:
handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) {
SuccessfullyInitialised = false;
outs()
- << "Note: CFIVerifyTests are disabled due to lack of x86 support "
+ << "Note: CFIVerifyTests are disabled due to lack of support "
"on this build.\n";
});
}
}
bool SuccessfullyInitialised;
- ELFx86TestFileAnalysis Analysis;
+ ELFTestFileAnalysis Analysis;
+};
+
+class BasicX86FileAnalysisTest : public BasicFileAnalysisTest {
+public:
+ BasicX86FileAnalysisTest() : BasicFileAnalysisTest("x86_64--") {}
+};
+
+class BasicAArch64FileAnalysisTest : public BasicFileAnalysisTest {
+public:
+ BasicAArch64FileAnalysisTest() : BasicFileAnalysisTest("aarch64--") {}
};
-TEST_F(BasicFileAnalysisTest, BasicDisassemblyTraversalTest) {
+TEST_F(BasicX86FileAnalysisTest, BasicDisassemblyTraversalTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -190,7 +202,7 @@ TEST_F(BasicFileAnalysisTest, BasicDisassemblyTraversalTest) {
EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));
}
-TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) {
+TEST_F(BasicX86FileAnalysisTest, PrevAndNextFromBadInst) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -213,7 +225,7 @@ TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) {
EXPECT_EQ(1u, GoodInstrMeta->InstructionSize);
}
-TEST_F(BasicFileAnalysisTest, CFITrapTest) {
+TEST_F(BasicX86FileAnalysisTest, CFITrapTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -248,7 +260,7 @@ TEST_F(BasicFileAnalysisTest, CFITrapTest) {
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 28)));
}
-TEST_F(BasicFileAnalysisTest, FallThroughTest) {
+TEST_F(BasicX86FileAnalysisTest, FallThroughTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -288,7 +300,7 @@ TEST_F(BasicFileAnalysisTest, FallThroughTest) {
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 19)));
}
-TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) {
+TEST_F(BasicX86FileAnalysisTest, DefiniteNextInstructionTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -378,7 +390,7 @@ TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) {
EXPECT_EQ(0xDEADBEEF + 4, Next->VMAddress);
}
-TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) {
+TEST_F(BasicX86FileAnalysisTest, ControlFlowXRefsTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -483,7 +495,7 @@ TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) {
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionInvalidTargets) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionInvalidTargets) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -507,7 +519,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionInvalidTargets) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -522,7 +534,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionBasicJumpToUd2) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicJumpToUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -537,7 +549,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionBasicJumpToUd2) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathUd2) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -555,7 +567,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathUd2) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathSingleUd2) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathSingleUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -572,7 +584,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathSingleUd2) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitUpwards) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitUpwards) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -597,7 +609,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitUpwards) {
SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitDownwards) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitDownwards) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -621,7 +633,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitDownwards) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionGoodAndBadPaths) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionGoodAndBadPaths) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -637,7 +649,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionGoodAndBadPaths) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -653,7 +665,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionComplexExample) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionComplexExample) {
if (!SuccessfullyInitialised)
return;
// See unittests/GraphBuilder.cpp::BuildFlowGraphComplexExample for this
@@ -683,7 +695,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionComplexExample) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
-TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTest) {
+TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTest) {
Analysis.parseSectionContents(
{
0x77, 0x0d, // 0x688118: ja 0x688127 [+12]
@@ -702,7 +714,7 @@ TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTest) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
-TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTestFarAway) {
+TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTestFarAway) {
Analysis.parseSectionContents(
{
0x74, 0x73, // 0x7759eb: je 0x775a60
@@ -741,7 +753,7 @@ TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTestFarAway) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -757,7 +769,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -773,7 +785,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -789,7 +801,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {
Analysis.validateCFIProtection(Result));
}
-TEST_F(BasicFileAnalysisTest, CFIProtectionClobberDualPathImplicit) {
+TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberDualPathImplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@@ -807,6 +819,243 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberDualPathImplicit) {
Analysis.validateCFIProtection(Result));
}
+TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicUnprotected) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x00, 0x01, 0x3f, 0xd6, // 0: blr x8
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicProtected) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x00, 0x01, 0x3f, 0xd6, // 8: blr x8
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 8);
+ EXPECT_EQ(CFIProtectionStatus::PROTECTED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberBasic) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x08, 0x05, 0x00, 0x91, // 8: add x8, x8, #1
+ 0x00, 0x01, 0x3f, 0xd6, // 12: blr x8
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberOneLoad) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 12: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12);
+ EXPECT_EQ(CFIProtectionStatus::PROTECTED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddGood) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x21, 0x04, 0x00, 0x91, // 8: add x1, x1, #1
+ 0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 16: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
+ EXPECT_EQ(CFIProtectionStatus::PROTECTED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
+ 0x21, 0x04, 0x00, 0x91, // 12: add x1, x1, #1
+ 0x20, 0x00, 0x1f, 0xd6, // 16: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad2) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x29, 0x04, 0x00, 0x91, // 16: add x9, x1, #1
+ 0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 16: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberTwoLoads) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
+ 0x21, 0x08, 0x40, 0xf9, // 12: ldr x1, [x1,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 16: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedSecondLoad) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
+ 0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 16: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
+ EXPECT_EQ(CFIProtectionStatus::PROTECTED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedLoads) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
+ 0x22, 0x09, 0x40, 0xf9, // 8: ldr x2, [x9,#16]
+ 0x22, 0x08, 0x40, 0xf9, // 12: ldr x2, [x1,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 16: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
+ EXPECT_EQ(CFIProtectionStatus::PROTECTED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64GoodAndBadPaths) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0x03, 0x00, 0x00, 0x14, // 0: b 12
+ 0x49, 0x00, 0x00, 0x54, // 4: b.ls 8
+ 0x20, 0x00, 0x20, 0xd4, // 8: brk #0x1
+ 0x20, 0x00, 0x1f, 0xd6, // 12: br x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPaths) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0xc9, 0x00, 0x00, 0x54, // 0: b.ls 24
+ 0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]
+ 0x03, 0x00, 0x00, 0x14, // 8: b 12
+ 0x69, 0x00, 0x00, 0x54, // 12: b.ls 12
+ 0x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 20: br x1
+ 0x20, 0x00, 0x20, 0xd4, // 24: brk #0x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 20);
+ EXPECT_EQ(CFIProtectionStatus::PROTECTED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad1) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0xe9, 0x00, 0x00, 0x54, // 0: b.ls 28
+ 0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]
+ 0x21, 0x08, 0x40, 0xf9, // 8: ldr x1, [x1,#16]
+ 0x03, 0x00, 0x00, 0x14, // 12: b 12
+ 0x69, 0x00, 0x00, 0x54, // 16: b.ls 12
+ 0x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 24: br x1
+ 0x20, 0x00, 0x20, 0xd4, // 28: brk #0x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 24);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
+ Analysis.validateCFIProtection(Result));
+}
+
+TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad2) {
+ if (!SuccessfullyInitialised)
+ return;
+ Analysis.parseSectionContents(
+ {
+ 0xe9, 0x00, 0x00, 0x54, // 0: b.ls 28
+ 0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]
+ 0x03, 0x00, 0x00, 0x14, // 8: b 12
+ 0x89, 0x00, 0x00, 0x54, // 12: b.ls 16
+ 0x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16]
+ 0x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16]
+ 0x20, 0x00, 0x1f, 0xd6, // 24: br x1
+ 0x20, 0x00, 0x20, 0xd4, // 28: brk #0x1
+ },
+ 0xDEADBEEF);
+ GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 24);
+ EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
+ Analysis.validateCFIProtection(Result));
+}
+
} // anonymous namespace
} // end namespace cfi_verify
} // end namespace llvm
diff --git a/unittests/tools/llvm-exegesis/AArch64/CMakeLists.txt b/unittests/tools/llvm-exegesis/AArch64/CMakeLists.txt
new file mode 100644
index 000000000000..392efce26c00
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/AArch64/CMakeLists.txt
@@ -0,0 +1,21 @@
+include_directories(
+ ${LLVM_MAIN_SRC_DIR}/lib/Target/AArch64
+ ${LLVM_BINARY_DIR}/lib/Target/AArch64
+ ${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib
+ )
+
+set(LLVM_LINK_COMPONENTS
+ MC
+ MCParser
+ Object
+ Support
+ Symbolize
+ AArch64
+ )
+
+add_llvm_unittest(LLVMExegesisAArch64Tests
+ TargetTest.cpp
+ )
+target_link_libraries(LLVMExegesisAArch64Tests PRIVATE
+ LLVMExegesis
+ LLVMExegesisAArch64)
diff --git a/unittests/tools/llvm-exegesis/AArch64/TargetTest.cpp b/unittests/tools/llvm-exegesis/AArch64/TargetTest.cpp
new file mode 100644
index 000000000000..cf3c11a9fdef
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/AArch64/TargetTest.cpp
@@ -0,0 +1,53 @@
+#include "Target.h"
+
+#include <cassert>
+#include <memory>
+
+#include "MCTargetDesc/AArch64MCTargetDesc.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace exegesis {
+
+void InitializeAArch64ExegesisTarget();
+
+namespace {
+
+using testing::Gt;
+using testing::NotNull;
+using testing::SizeIs;
+
+constexpr const char kTriple[] = "aarch64-unknown-linux";
+
+class AArch64TargetTest : public ::testing::Test {
+protected:
+ AArch64TargetTest()
+ : ExegesisTarget_(ExegesisTarget::lookup(llvm::Triple(kTriple))) {
+ EXPECT_THAT(ExegesisTarget_, NotNull());
+ std::string error;
+ Target_ = llvm::TargetRegistry::lookupTarget(kTriple, error);
+ EXPECT_THAT(Target_, NotNull());
+ }
+ static void SetUpTestCase() {
+ LLVMInitializeAArch64TargetInfo();
+ LLVMInitializeAArch64Target();
+ LLVMInitializeAArch64TargetMC();
+ InitializeAArch64ExegesisTarget();
+ }
+
+ const llvm::Target *Target_;
+ const ExegesisTarget *const ExegesisTarget_;
+};
+
+TEST_F(AArch64TargetTest, SetRegToConstant) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "generic", ""));
+ // The AArch64 target currently doesn't know how to set register values
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::AArch64::X0);
+ EXPECT_THAT(Insts, SizeIs(0));
+}
+
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/ARM/AssemblerTest.cpp b/unittests/tools/llvm-exegesis/ARM/AssemblerTest.cpp
new file mode 100644
index 000000000000..8c70b9eaaade
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/ARM/AssemblerTest.cpp
@@ -0,0 +1,48 @@
+//===-- AssemblerTest.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../Common/AssemblerUtils.h"
+#include "ARMInstrInfo.h"
+
+namespace exegesis {
+namespace {
+
+using llvm::MCInstBuilder;
+
+class ARMMachineFunctionGeneratorTest
+ : public MachineFunctionGeneratorBaseTest {
+protected:
+ ARMMachineFunctionGeneratorTest()
+ : MachineFunctionGeneratorBaseTest("armv7-none-linux-gnueabi", "") {}
+
+ static void SetUpTestCase() {
+ LLVMInitializeARMTargetInfo();
+ LLVMInitializeARMTargetMC();
+ LLVMInitializeARMTarget();
+ LLVMInitializeARMAsmPrinter();
+ }
+};
+
+TEST_F(ARMMachineFunctionGeneratorTest, DISABLED_JitFunction) {
+ Check(llvm::MCInst(), 0x1e, 0xff, 0x2f, 0xe1);
+}
+
+TEST_F(ARMMachineFunctionGeneratorTest, DISABLED_JitFunctionADDrr) {
+ Check(MCInstBuilder(llvm::ARM::ADDrr)
+ .addReg(llvm::ARM::R0)
+ .addReg(llvm::ARM::R0)
+ .addReg(llvm::ARM::R0)
+ .addImm(llvm::ARMCC::AL)
+ .addReg(0)
+ .addReg(0),
+ 0x00, 0x00, 0x80, 0xe0, 0x1e, 0xff, 0x2f, 0xe1);
+}
+
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/ARM/CMakeLists.txt b/unittests/tools/llvm-exegesis/ARM/CMakeLists.txt
new file mode 100644
index 000000000000..b000974938f7
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/ARM/CMakeLists.txt
@@ -0,0 +1,19 @@
+include_directories(
+ ${LLVM_MAIN_SRC_DIR}/lib/Target/ARM
+ ${LLVM_BINARY_DIR}/lib/Target/ARM
+ ${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib
+ )
+
+set(LLVM_LINK_COMPONENTS
+ MC
+ MCParser
+ Object
+ Support
+ Symbolize
+ ARM
+ )
+
+add_llvm_unittest(LLVMExegesisARMTests
+ AssemblerTest.cpp
+ )
+target_link_libraries(LLVMExegesisARMTests PRIVATE LLVMExegesis)
diff --git a/unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp b/unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp
new file mode 100644
index 000000000000..2b604dcdeb44
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp
@@ -0,0 +1,134 @@
+//===-- BenchmarkResultTest.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BenchmarkResult.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::get;
+using ::testing::Pointwise;
+using ::testing::Property;
+
+namespace exegesis {
+
+bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) {
+ return std::tie(A.Key, A.Value) == std::tie(B.Key, B.Value);
+}
+
+static std::string Dump(const llvm::MCInst &McInst) {
+ std::string Buffer;
+ llvm::raw_string_ostream OS(Buffer);
+ McInst.print(OS);
+ return Buffer;
+}
+
+MATCHER(EqMCInst, "") {
+ const std::string Lhs = Dump(get<0>(arg));
+ const std::string Rhs = Dump(get<1>(arg));
+ if (Lhs != Rhs) {
+ *result_listener << Lhs << " <=> " << Rhs;
+ return false;
+ }
+ return true;
+}
+
+namespace {
+
+static constexpr const unsigned kInstrId = 5;
+static constexpr const char kInstrName[] = "Instruction5";
+static constexpr const unsigned kReg1Id = 1;
+static constexpr const char kReg1Name[] = "Reg1";
+static constexpr const unsigned kReg2Id = 2;
+static constexpr const char kReg2Name[] = "Reg2";
+
+TEST(BenchmarkResultTest, WriteToAndReadFromDisk) {
+ llvm::ExitOnError ExitOnErr;
+ BenchmarkResultContext Ctx;
+ Ctx.addInstrEntry(kInstrId, kInstrName);
+ Ctx.addRegEntry(kReg1Id, kReg1Name);
+ Ctx.addRegEntry(kReg2Id, kReg2Name);
+
+ InstructionBenchmark ToDisk;
+
+ ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(kInstrId)
+ .addReg(kReg1Id)
+ .addReg(kReg2Id)
+ .addImm(123)
+ .addFPImm(0.5));
+ ToDisk.Key.Config = "config";
+ ToDisk.Mode = InstructionBenchmark::Latency;
+ ToDisk.CpuName = "cpu_name";
+ ToDisk.LLVMTriple = "llvm_triple";
+ ToDisk.NumRepetitions = 1;
+ ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, "debug a"});
+ ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, ""});
+ ToDisk.Error = "error";
+ ToDisk.Info = "info";
+
+ llvm::SmallString<64> Filename;
+ std::error_code EC;
+ EC = llvm::sys::fs::createUniqueDirectory("BenchmarkResultTestDir", Filename);
+ ASSERT_FALSE(EC);
+ llvm::sys::path::append(Filename, "data.yaml");
+ ExitOnErr(ToDisk.writeYaml(Ctx, Filename));
+
+ {
+ // One-element version.
+ const auto FromDisk =
+ ExitOnErr(InstructionBenchmark::readYaml(Ctx, Filename));
+
+ EXPECT_THAT(FromDisk.Key.Instructions,
+ Pointwise(EqMCInst(), ToDisk.Key.Instructions));
+ EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
+ EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
+ EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
+ EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
+ EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
+ EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
+ EXPECT_THAT(FromDisk.Error, ToDisk.Error);
+ EXPECT_EQ(FromDisk.Info, ToDisk.Info);
+ }
+ {
+ // Vector version.
+ const auto FromDiskVector =
+ ExitOnErr(InstructionBenchmark::readYamls(Ctx, Filename));
+ ASSERT_EQ(FromDiskVector.size(), size_t{1});
+ const auto FromDisk = FromDiskVector[0];
+ EXPECT_THAT(FromDisk.Key.Instructions,
+ Pointwise(EqMCInst(), ToDisk.Key.Instructions));
+ EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
+ EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
+ EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
+ EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
+ EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
+ EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
+ EXPECT_THAT(FromDisk.Error, ToDisk.Error);
+ EXPECT_EQ(FromDisk.Info, ToDisk.Info);
+ }
+}
+
+TEST(BenchmarkResultTest, BenchmarkMeasureStats) {
+ BenchmarkMeasureStats Stats;
+ Stats.push(BenchmarkMeasure{"a", 0.5, "debug a"});
+ Stats.push(BenchmarkMeasure{"a", 1.5, "debug a"});
+ Stats.push(BenchmarkMeasure{"a", -1.0, "debug a"});
+ Stats.push(BenchmarkMeasure{"a", 0.0, "debug a"});
+ EXPECT_EQ(Stats.min(), -1.0);
+ EXPECT_EQ(Stats.max(), 1.5);
+ EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4
+}
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/CMakeLists.txt b/unittests/tools/llvm-exegesis/CMakeLists.txt
new file mode 100644
index 000000000000..9afa17a8e801
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/CMakeLists.txt
@@ -0,0 +1,28 @@
+include_directories(
+ ${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib
+ )
+
+set(LLVM_LINK_COMPONENTS
+ MC
+ MCParser
+ Object
+ Support
+ Symbolize
+ )
+
+add_llvm_unittest(LLVMExegesisTests
+ BenchmarkResultTest.cpp
+ ClusteringTest.cpp
+ PerfHelperTest.cpp
+ )
+target_link_libraries(LLVMExegesisTests PRIVATE LLVMExegesis)
+
+if(LLVM_TARGETS_TO_BUILD MATCHES "X86")
+ add_subdirectory(X86)
+endif()
+if(LLVM_TARGETS_TO_BUILD MATCHES "ARM")
+ add_subdirectory(ARM)
+endif()
+if(LLVM_TARGETS_TO_BUILD MATCHES "AArch64")
+ add_subdirectory(AArch64)
+endif()
diff --git a/unittests/tools/llvm-exegesis/ClusteringTest.cpp b/unittests/tools/llvm-exegesis/ClusteringTest.cpp
new file mode 100644
index 000000000000..ecdb7d2a274b
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/ClusteringTest.cpp
@@ -0,0 +1,100 @@
+//===-- ClusteringTest.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Clustering.h"
+#include "BenchmarkResult.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace exegesis {
+
+namespace {
+
+using testing::Field;
+using testing::UnorderedElementsAre;
+using testing::UnorderedElementsAreArray;
+
+TEST(ClusteringTest, Clusters3D) {
+ std::vector<InstructionBenchmark> Points(6);
+
+ // Cluster around (x=0, y=1, z=2): points {0, 3}.
+ Points[0].Measurements = {{"x", 0.01, ""}, {"y", 1.02, ""}, {"z", 1.98, "A"}};
+ Points[3].Measurements = {{"x", -0.01, ""}, {"y", 1.02, ""}, {"z", 1.98, ""}};
+ // Cluster around (x=1, y=1, z=2): points {1, 4}.
+ Points[1].Measurements = {{"x", 1.01, ""}, {"y", 1.02, ""}, {"z", 1.98, ""}};
+ Points[4].Measurements = {{"x", 0.99, ""}, {"y", 1.02, ""}, {"z", 1.98, ""}};
+ // Cluster around (x=0, y=0, z=0): points {5}, marked as noise.
+ Points[5].Measurements = {{"x", 0.0, ""}, {"y", 0.01, ""}, {"z", -0.02, ""}};
+ // Error cluster: points {2}
+ Points[2].Error = "oops";
+
+ auto HasPoints = [](const std::vector<int> &Indices) {
+ return Field(&InstructionBenchmarkClustering::Cluster::PointIndices,
+ UnorderedElementsAreArray(Indices));
+ };
+
+ auto Clustering = InstructionBenchmarkClustering::create(Points, 2, 0.25);
+ ASSERT_TRUE((bool)Clustering);
+ EXPECT_THAT(Clustering.get().getValidClusters(),
+ UnorderedElementsAre(HasPoints({0, 3}), HasPoints({1, 4})));
+ EXPECT_THAT(Clustering.get().getCluster(
+ InstructionBenchmarkClustering::ClusterId::noise()),
+ HasPoints({5}));
+ EXPECT_THAT(Clustering.get().getCluster(
+ InstructionBenchmarkClustering::ClusterId::error()),
+ HasPoints({2}));
+
+ EXPECT_EQ(Clustering.get().getClusterIdForPoint(2),
+ InstructionBenchmarkClustering::ClusterId::error());
+ EXPECT_EQ(Clustering.get().getClusterIdForPoint(5),
+ InstructionBenchmarkClustering::ClusterId::noise());
+ EXPECT_EQ(Clustering.get().getClusterIdForPoint(0),
+ Clustering.get().getClusterIdForPoint(3));
+ EXPECT_EQ(Clustering.get().getClusterIdForPoint(1),
+ Clustering.get().getClusterIdForPoint(4));
+}
+
+TEST(ClusteringTest, Clusters3D_InvalidSize) {
+ std::vector<InstructionBenchmark> Points(6);
+ Points[0].Measurements = {{"x", 0.01, ""}, {"y", 1.02, ""}, {"z", 1.98, ""}};
+ Points[1].Measurements = {{"y", 1.02, ""}, {"z", 1.98, ""}};
+ auto Error =
+ InstructionBenchmarkClustering::create(Points, 2, 0.25).takeError();
+ ASSERT_TRUE((bool)Error);
+ consumeError(std::move(Error));
+}
+
+TEST(ClusteringTest, Clusters3D_InvalidOrder) {
+ std::vector<InstructionBenchmark> Points(6);
+ Points[0].Measurements = {{"x", 0.01, ""}, {"y", 1.02, ""}};
+ Points[1].Measurements = {{"y", 1.02, ""}, {"x", 1.98, ""}};
+ auto Error =
+ InstructionBenchmarkClustering::create(Points, 2, 0.25).takeError();
+ ASSERT_TRUE((bool)Error);
+ consumeError(std::move(Error));
+}
+
+TEST(ClusteringTest, Ordering) {
+ ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(1),
+ InstructionBenchmarkClustering::ClusterId::makeValid(2));
+
+ ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(2),
+ InstructionBenchmarkClustering::ClusterId::noise());
+
+ ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(2),
+ InstructionBenchmarkClustering::ClusterId::error());
+
+ ASSERT_LT(InstructionBenchmarkClustering::ClusterId::noise(),
+ InstructionBenchmarkClustering::ClusterId::error());
+}
+
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/Common/AssemblerUtils.h b/unittests/tools/llvm-exegesis/Common/AssemblerUtils.h
new file mode 100644
index 000000000000..a0cfb1d86c7a
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/Common/AssemblerUtils.h
@@ -0,0 +1,87 @@
+//===-- AssemblerUtils.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Assembler.h"
+#include "Target.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace exegesis {
+
+class MachineFunctionGeneratorBaseTest : public ::testing::Test {
+protected:
+ MachineFunctionGeneratorBaseTest(const std::string &TT,
+ const std::string &CpuName)
+ : TT(TT), CpuName(CpuName),
+ CanExecute(llvm::Triple(TT).getArch() ==
+ llvm::Triple(llvm::sys::getProcessTriple()).getArch()) {
+ if (!CanExecute) {
+ llvm::outs() << "Skipping execution, host:"
+ << llvm::sys::getProcessTriple() << ", target:" << TT
+ << "\n";
+ }
+ }
+
+ template <class... Bs> inline void Check(llvm::MCInst MCInst, Bs... Bytes) {
+ CheckWithSetup(ExegesisTarget::getDefault(), {}, MCInst, Bytes...);
+ }
+
+ template <class... Bs>
+ inline void CheckWithSetup(const ExegesisTarget &ET,
+ llvm::ArrayRef<unsigned> RegsToDef,
+ llvm::MCInst MCInst, Bs... Bytes) {
+ ExecutableFunction Function =
+ (MCInst.getOpcode() == 0) ? assembleToFunction(ET, RegsToDef, {})
+ : assembleToFunction(ET, RegsToDef, {MCInst});
+ ASSERT_THAT(Function.getFunctionBytes().str(),
+ testing::ElementsAre(Bytes...));
+ if (CanExecute)
+ Function();
+ }
+
+private:
+ std::unique_ptr<llvm::LLVMTargetMachine> createTargetMachine() {
+ std::string Error;
+ const llvm::Target *TheTarget =
+ llvm::TargetRegistry::lookupTarget(TT, Error);
+ EXPECT_TRUE(TheTarget) << Error << " " << TT;
+ const llvm::TargetOptions Options;
+ llvm::TargetMachine *TM = TheTarget->createTargetMachine(
+ TT, CpuName, "", Options, llvm::Reloc::Model::Static);
+ EXPECT_TRUE(TM) << TT << " " << CpuName;
+ return std::unique_ptr<llvm::LLVMTargetMachine>(
+ static_cast<llvm::LLVMTargetMachine *>(TM));
+ }
+
+ ExecutableFunction
+ assembleToFunction(const ExegesisTarget &ET,
+ llvm::ArrayRef<unsigned> RegsToDef,
+ llvm::ArrayRef<llvm::MCInst> Instructions) {
+ llvm::SmallString<256> Buffer;
+ llvm::raw_svector_ostream AsmStream(Buffer);
+ assembleToStream(ET, createTargetMachine(), RegsToDef, Instructions,
+ AsmStream);
+ return ExecutableFunction(createTargetMachine(),
+ getObjectFromBuffer(AsmStream.str()));
+ }
+
+ const std::string TT;
+ const std::string CpuName;
+ const bool CanExecute;
+};
+
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/PerfHelperTest.cpp b/unittests/tools/llvm-exegesis/PerfHelperTest.cpp
new file mode 100644
index 000000000000..a8205f9e3eb2
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/PerfHelperTest.cpp
@@ -0,0 +1,47 @@
+//===-- PerfHelperTest.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PerfHelper.h"
+#include "llvm/Config/config.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace exegesis {
+namespace pfm {
+namespace {
+
+using ::testing::IsEmpty;
+using ::testing::Not;
+
+TEST(PerfHelperTest, FunctionalTest) {
+#ifdef HAVE_LIBPFM
+ ASSERT_FALSE(pfmInitialize());
+ const PerfEvent SingleEvent("CYCLES:u");
+ const auto &EmptyFn = []() {};
+ std::string CallbackEventName;
+ std::string CallbackEventNameFullyQualifed;
+ int64_t CallbackEventCycles;
+ Measure(llvm::makeArrayRef(SingleEvent),
+ [&](const PerfEvent &Event, int64_t Value) {
+ CallbackEventName = Event.name();
+ CallbackEventNameFullyQualifed = Event.getPfmEventString();
+ CallbackEventCycles = Value;
+ },
+ EmptyFn);
+ EXPECT_EQ(CallbackEventName, "CYCLES:u");
+ EXPECT_THAT(CallbackEventNameFullyQualifed, Not(IsEmpty()));
+ pfmTerminate();
+#else
+ ASSERT_TRUE(pfmInitialize());
+#endif
+}
+
+} // namespace
+} // namespace pfm
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/X86/AnalysisTest.cpp b/unittests/tools/llvm-exegesis/X86/AnalysisTest.cpp
new file mode 100644
index 000000000000..d2d4c152d79e
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/X86/AnalysisTest.cpp
@@ -0,0 +1,102 @@
+#include "Analysis.h"
+
+#include <cassert>
+#include <memory>
+
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace exegesis {
+namespace {
+
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
+class AnalysisTest : public ::testing::Test {
+protected:
+ AnalysisTest() {
+ const std::string TT = "x86_64-unknown-linux";
+ std::string error;
+ const llvm::Target *const TheTarget =
+ llvm::TargetRegistry::lookupTarget(TT, error);
+ if (!TheTarget) {
+ llvm::errs() << error << "\n";
+ return;
+ }
+ STI.reset(TheTarget->createMCSubtargetInfo(TT, "haswell", ""));
+
+ // Compute the ProxResIdx of ports unes in tests.
+ const auto &SM = STI->getSchedModel();
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const std::string Name = SM.getProcResource(I)->Name;
+ if (Name == "HWPort0") {
+ P0Idx = I;
+ } else if (Name == "HWPort1") {
+ P1Idx = I;
+ } else if (Name == "HWPort5") {
+ P5Idx = I;
+ } else if (Name == "HWPort6") {
+ P6Idx = I;
+ } else if (Name == "HWPort05") {
+ P05Idx = I;
+ } else if (Name == "HWPort0156") {
+ P0156Idx = I;
+ }
+ }
+ EXPECT_NE(P0Idx, 0);
+ EXPECT_NE(P1Idx, 0);
+ EXPECT_NE(P5Idx, 0);
+ EXPECT_NE(P6Idx, 0);
+ EXPECT_NE(P05Idx, 0);
+ EXPECT_NE(P0156Idx, 0);
+ }
+
+ static void SetUpTestCase() {
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86TargetMC();
+ }
+
+protected:
+ std::unique_ptr<const llvm::MCSubtargetInfo> STI;
+ uint16_t P0Idx = 0;
+ uint16_t P1Idx = 0;
+ uint16_t P5Idx = 0;
+ uint16_t P6Idx = 0;
+ uint16_t P05Idx = 0;
+ uint16_t P0156Idx = 0;
+};
+
+TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_2P0) {
+ const auto Pressure =
+ computeIdealizedProcResPressure(STI->getSchedModel(), {{P0Idx, 2}});
+ EXPECT_THAT(Pressure, UnorderedElementsAre(Pair(P0Idx, 2.0)));
+}
+
+TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_2P05) {
+ const auto Pressure =
+ computeIdealizedProcResPressure(STI->getSchedModel(), {{P05Idx, 2}});
+ EXPECT_THAT(Pressure,
+ UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P5Idx, 1.0)));
+}
+
+TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_2P05_2P0156) {
+ const auto Pressure = computeIdealizedProcResPressure(
+ STI->getSchedModel(), {{P05Idx, 2}, {P0156Idx, 2}});
+ EXPECT_THAT(Pressure,
+ UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),
+ Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));
+}
+
+TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_1P1_1P05_2P0156) {
+ const auto Pressure = computeIdealizedProcResPressure(
+ STI->getSchedModel(), {{P1Idx, 1}, {P05Idx, 1}, {P0156Idx, 2}});
+ EXPECT_THAT(Pressure,
+ UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),
+ Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));
+}
+
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/X86/AssemblerTest.cpp b/unittests/tools/llvm-exegesis/X86/AssemblerTest.cpp
new file mode 100644
index 000000000000..4e3c54accac9
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/X86/AssemblerTest.cpp
@@ -0,0 +1,57 @@
+//===-- AssemblerTest.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../Common/AssemblerUtils.h"
+#include "X86InstrInfo.h"
+
+namespace exegesis {
+namespace {
+
+using llvm::MCInstBuilder;
+using llvm::X86::EAX;
+using llvm::X86::MOV32ri;
+using llvm::X86::MOV64ri32;
+using llvm::X86::RAX;
+using llvm::X86::XOR32rr;
+
+class X86MachineFunctionGeneratorTest
+ : public MachineFunctionGeneratorBaseTest {
+protected:
+ X86MachineFunctionGeneratorTest()
+ : MachineFunctionGeneratorBaseTest("x86_64-unknown-linux", "haswell") {}
+
+ static void SetUpTestCase() {
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86TargetMC();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86AsmPrinter();
+ }
+};
+
+TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunction) {
+ Check(llvm::MCInst(), 0xc3);
+}
+
+TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionXOR32rr) {
+ Check(MCInstBuilder(XOR32rr).addReg(EAX).addReg(EAX).addReg(EAX), 0x31, 0xc0,
+ 0xc3);
+}
+
+TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionMOV64ri) {
+ Check(MCInstBuilder(MOV64ri32).addReg(RAX).addImm(42), 0x48, 0xc7, 0xc0, 0x2a,
+ 0x00, 0x00, 0x00, 0xc3);
+}
+
+TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionMOV32ri) {
+ Check(MCInstBuilder(MOV32ri).addReg(EAX).addImm(42), 0xb8, 0x2a, 0x00, 0x00,
+ 0x00, 0xc3);
+}
+
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/X86/CMakeLists.txt b/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
new file mode 100644
index 000000000000..5a74c87d9b5f
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
@@ -0,0 +1,25 @@
+include_directories(
+ ${LLVM_MAIN_SRC_DIR}/lib/Target/X86
+ ${LLVM_BINARY_DIR}/lib/Target/X86
+ ${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib
+ )
+
+set(LLVM_LINK_COMPONENTS
+ MC
+ MCParser
+ Object
+ Support
+ Symbolize
+ X86
+ )
+
+add_llvm_unittest(LLVMExegesisX86Tests
+ AssemblerTest.cpp
+ AnalysisTest.cpp
+ SnippetGeneratorTest.cpp
+ RegisterAliasingTest.cpp
+ TargetTest.cpp
+ )
+target_link_libraries(LLVMExegesisX86Tests PRIVATE
+ LLVMExegesis
+ LLVMExegesisX86)
diff --git a/unittests/tools/llvm-exegesis/X86/RegisterAliasingTest.cpp b/unittests/tools/llvm-exegesis/X86/RegisterAliasingTest.cpp
new file mode 100644
index 000000000000..12f76541d4d6
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/X86/RegisterAliasingTest.cpp
@@ -0,0 +1,91 @@
+#include "RegisterAliasing.h"
+
+#include <cassert>
+#include <memory>
+
+#include "X86InstrInfo.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace exegesis {
+namespace {
+
+class RegisterAliasingTest : public ::testing::Test {
+protected:
+ RegisterAliasingTest() {
+ const std::string TT = "x86_64-unknown-linux";
+ std::string error;
+ const llvm::Target *const TheTarget =
+ llvm::TargetRegistry::lookupTarget(TT, error);
+ if (!TheTarget) {
+ llvm::errs() << error << "\n";
+ return;
+ }
+ MCRegInfo.reset(TheTarget->createMCRegInfo(TT));
+ }
+
+ static void SetUpTestCase() {
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86TargetMC();
+ }
+
+ const llvm::MCRegisterInfo &getMCRegInfo() {
+ assert(MCRegInfo);
+ return *MCRegInfo;
+ }
+
+private:
+ std::unique_ptr<const llvm::MCRegisterInfo> MCRegInfo;
+};
+
+TEST_F(RegisterAliasingTest, TrackSimpleRegister) {
+ const auto &RegInfo = getMCRegInfo();
+ const RegisterAliasingTracker tracker(RegInfo, llvm::X86::EAX);
+ std::set<llvm::MCPhysReg> ActualAliasedRegisters;
+ for (unsigned I : tracker.aliasedBits().set_bits())
+ ActualAliasedRegisters.insert(static_cast<llvm::MCPhysReg>(I));
+ const std::set<llvm::MCPhysReg> ExpectedAliasedRegisters = {
+ llvm::X86::AL, llvm::X86::AH, llvm::X86::AX,
+ llvm::X86::EAX, llvm::X86::HAX, llvm::X86::RAX};
+ ASSERT_THAT(ActualAliasedRegisters, ExpectedAliasedRegisters);
+ for (llvm::MCPhysReg aliased : ExpectedAliasedRegisters) {
+ ASSERT_THAT(tracker.getOrigin(aliased), llvm::X86::EAX);
+ }
+}
+
+TEST_F(RegisterAliasingTest, TrackRegisterClass) {
+ // The alias bits for GR8_ABCD_LRegClassID are the union of the alias bits for
+ // AL, BL, CL and DL.
+ const auto &RegInfo = getMCRegInfo();
+ const llvm::BitVector NoReservedReg(RegInfo.getNumRegs());
+
+ const RegisterAliasingTracker RegClassTracker(
+ RegInfo, NoReservedReg,
+ RegInfo.getRegClass(llvm::X86::GR8_ABCD_LRegClassID));
+
+ llvm::BitVector sum(RegInfo.getNumRegs());
+ sum |= RegisterAliasingTracker(RegInfo, llvm::X86::AL).aliasedBits();
+ sum |= RegisterAliasingTracker(RegInfo, llvm::X86::BL).aliasedBits();
+ sum |= RegisterAliasingTracker(RegInfo, llvm::X86::CL).aliasedBits();
+ sum |= RegisterAliasingTracker(RegInfo, llvm::X86::DL).aliasedBits();
+
+ ASSERT_THAT(RegClassTracker.aliasedBits(), sum);
+}
+
+TEST_F(RegisterAliasingTest, TrackRegisterClassCache) {
+ // Fetching twice the same tracker yields the same pointers.
+ const auto &RegInfo = getMCRegInfo();
+ const llvm::BitVector NoReservedReg(RegInfo.getNumRegs());
+ RegisterAliasingTrackerCache Cache(RegInfo, NoReservedReg);
+ ASSERT_THAT(&Cache.getRegister(llvm::X86::AX),
+ &Cache.getRegister(llvm::X86::AX));
+
+ ASSERT_THAT(&Cache.getRegisterClass(llvm::X86::GR8_ABCD_LRegClassID),
+ &Cache.getRegisterClass(llvm::X86::GR8_ABCD_LRegClassID));
+}
+
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp b/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
new file mode 100644
index 000000000000..edacd7fe6291
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
@@ -0,0 +1,285 @@
+//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../Common/AssemblerUtils.h"
+#include "Latency.h"
+#include "LlvmState.h"
+#include "MCInstrDescView.h"
+#include "RegisterAliasing.h"
+#include "Uops.h"
+#include "X86InstrInfo.h"
+
+#include <unordered_set>
+
+namespace exegesis {
+namespace {
+
+using testing::AnyOf;
+using testing::ElementsAre;
+using testing::HasSubstr;
+using testing::Not;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+
+MATCHER(IsInvalid, "") { return !arg.isValid(); }
+MATCHER(IsReg, "") { return arg.isReg(); }
+
+class X86SnippetGeneratorTest : public ::testing::Test {
+protected:
+ X86SnippetGeneratorTest()
+ : State("x86_64-unknown-linux", "haswell"),
+ MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
+
+ static void SetUpTestCase() {
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86TargetMC();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86AsmPrinter();
+ }
+
+ const LLVMState State;
+ const llvm::MCInstrInfo &MCInstrInfo;
+ const llvm::MCRegisterInfo &MCRegisterInfo;
+};
+
+template <typename BenchmarkRunner>
+class SnippetGeneratorTest : public X86SnippetGeneratorTest {
+protected:
+ SnippetGeneratorTest() : Runner(State) {}
+
+ SnippetPrototype checkAndGetConfigurations(unsigned Opcode) {
+ randomGenerator().seed(0); // Initialize seed.
+ auto ProtoOrError = Runner.generatePrototype(Opcode);
+ EXPECT_FALSE(ProtoOrError.takeError()); // Valid configuration.
+ return std::move(ProtoOrError.get());
+ }
+
+ BenchmarkRunner Runner;
+};
+
+using LatencySnippetGeneratorTest =
+ SnippetGeneratorTest<LatencyBenchmarkRunner>;
+
+using UopsSnippetGeneratorTest = SnippetGeneratorTest<UopsBenchmarkRunner>;
+
+TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
+ // ADC16i16 self alias because of implicit use and def.
+
+ // explicit use 0 : imm
+ // implicit def : AX
+ // implicit def : EFLAGS
+ // implicit use : AX
+ // implicit use : EFLAGS
+ const unsigned Opcode = llvm::X86::ADC16i16;
+ EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
+ EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
+ EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
+ EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("implicit"));
+ ASSERT_THAT(Proto.Snippet, SizeIs(1));
+ const InstructionInstance &II = Proto.Snippet[0];
+ EXPECT_THAT(II.getOpcode(), Opcode);
+ ASSERT_THAT(II.VariableValues, SizeIs(1)); // Imm.
+ EXPECT_THAT(II.VariableValues[0], IsInvalid()) << "Immediate is not set";
+}
+
+TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
+ // ADD16ri self alias because Op0 and Op1 are tied together.
+
+ // explicit def 0 : reg RegClass=GR16
+ // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
+ // explicit use 2 : imm
+ // implicit def : EFLAGS
+ const unsigned Opcode = llvm::X86::ADD16ri;
+ EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("explicit"));
+ ASSERT_THAT(Proto.Snippet, SizeIs(1));
+ const InstructionInstance &II = Proto.Snippet[0];
+ EXPECT_THAT(II.getOpcode(), Opcode);
+ ASSERT_THAT(II.VariableValues, SizeIs(2));
+ EXPECT_THAT(II.VariableValues[0], IsReg()) << "Operand 0 and 1";
+ EXPECT_THAT(II.VariableValues[1], IsInvalid()) << "Operand 2 is not set";
+}
+
+TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
+ // CMP64rr
+ // explicit use 0 : reg RegClass=GR64
+ // explicit use 1 : reg RegClass=GR64
+ // implicit def : EFLAGS
+
+ const unsigned Opcode = llvm::X86::CMP64rr;
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
+ ASSERT_THAT(Proto.Snippet, SizeIs(2));
+ const InstructionInstance &II = Proto.Snippet[0];
+ EXPECT_THAT(II.getOpcode(), Opcode);
+ ASSERT_THAT(II.VariableValues, SizeIs(2));
+ EXPECT_THAT(II.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()),
+ ElementsAre(IsInvalid(), IsReg())));
+ EXPECT_THAT(Proto.Snippet[1].getOpcode(), Not(Opcode));
+ // TODO: check that the two instructions alias each other.
+}
+
+TEST_F(LatencySnippetGeneratorTest, LAHF) {
+ const unsigned Opcode = llvm::X86::LAHF;
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
+ ASSERT_THAT(Proto.Snippet, SizeIs(2));
+ const InstructionInstance &II = Proto.Snippet[0];
+ EXPECT_THAT(II.getOpcode(), Opcode);
+ ASSERT_THAT(II.VariableValues, SizeIs(0));
+}
+
+TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
+ // BNDCL32rr is parallel no matter what.
+
+ // explicit use 0 : reg RegClass=BNDR
+ // explicit use 1 : reg RegClass=GR32
+
+ const unsigned Opcode = llvm::X86::BNDCL32rr;
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("parallel"));
+ ASSERT_THAT(Proto.Snippet, SizeIs(1));
+ const InstructionInstance &II = Proto.Snippet[0];
+ EXPECT_THAT(II.getOpcode(), Opcode);
+ ASSERT_THAT(II.VariableValues, SizeIs(2));
+ EXPECT_THAT(II.VariableValues[0], IsInvalid());
+ EXPECT_THAT(II.VariableValues[1], IsInvalid());
+}
+
+TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
+ // CDQ is serial no matter what.
+
+ // implicit def : EAX
+ // implicit def : EDX
+ // implicit use : EAX
+ const unsigned Opcode = llvm::X86::CDQ;
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("serial"));
+ ASSERT_THAT(Proto.Snippet, SizeIs(1));
+ const InstructionInstance &II = Proto.Snippet[0];
+ EXPECT_THAT(II.getOpcode(), Opcode);
+ ASSERT_THAT(II.VariableValues, SizeIs(0));
+}
+
+TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
+ // CMOVA32rr has tied variables, we enumarate the possible values to execute
+ // as many in parallel as possible.
+
+ // explicit def 0 : reg RegClass=GR32
+ // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
+ // explicit use 2 : reg RegClass=GR32
+ // implicit use : EFLAGS
+ const unsigned Opcode = llvm::X86::CMOVA32rr;
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("static renaming"));
+ constexpr const unsigned kInstructionCount = 15;
+ ASSERT_THAT(Proto.Snippet, SizeIs(kInstructionCount));
+ std::unordered_set<unsigned> AllDefRegisters;
+ for (const auto &II : Proto.Snippet) {
+ ASSERT_THAT(II.VariableValues, SizeIs(2));
+ AllDefRegisters.insert(II.VariableValues[0].getReg());
+ }
+ EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))
+ << "Each instruction writes to a different register";
+}
+
+TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
+ // CMOV_GR32 has no tied variables, we make sure def and use are different
+ // from each other.
+
+ // explicit def 0 : reg RegClass=GR32
+ // explicit use 1 : reg RegClass=GR32
+ // explicit use 2 : reg RegClass=GR32
+ // explicit use 3 : imm
+ // implicit use : EFLAGS
+ const unsigned Opcode = llvm::X86::CMOV_GR32;
+ const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
+ EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables"));
+ ASSERT_THAT(Proto.Snippet, SizeIs(1));
+ const InstructionInstance &II = Proto.Snippet[0];
+ EXPECT_THAT(II.getOpcode(), Opcode);
+ ASSERT_THAT(II.VariableValues, SizeIs(4));
+ EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[1].getReg()))
+ << "Def is different from first Use";
+ EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[2].getReg()))
+ << "Def is different from second Use";
+ EXPECT_THAT(II.VariableValues[3], IsInvalid());
+}
+
+class FakeBenchmarkRunner : public BenchmarkRunner {
+public:
+ FakeBenchmarkRunner(const LLVMState &State)
+ : BenchmarkRunner(State, InstructionBenchmark::Unknown) {}
+
+ Instruction createInstruction(unsigned Opcode) {
+ return Instruction(State.getInstrInfo().get(Opcode), RATC);
+ }
+
+private:
+ llvm::Expected<SnippetPrototype>
+ generatePrototype(unsigned Opcode) const override {
+ return llvm::make_error<llvm::StringError>("not implemented",
+ llvm::inconvertibleErrorCode());
+ }
+
+ std::vector<BenchmarkMeasure>
+ runMeasurements(const ExecutableFunction &EF,
+ const unsigned NumRepetitions) const override {
+ return {};
+ }
+};
+
+using FakeSnippetGeneratorTest = SnippetGeneratorTest<FakeBenchmarkRunner>;
+
+TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd16ri) {
+ // ADD16ri:
+ // explicit def 0 : reg RegClass=GR16
+ // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
+ // explicit use 2 : imm
+ // implicit def : EFLAGS
+ InstructionInstance II(Runner.createInstruction(llvm::X86::ADD16ri));
+ II.getValueFor(II.Instr.Variables[0]) =
+ llvm::MCOperand::createReg(llvm::X86::AX);
+ std::vector<InstructionInstance> Snippet;
+ Snippet.push_back(std::move(II));
+ const auto RegsToDef = Runner.computeRegsToDef(Snippet);
+ EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::AX));
+}
+
+TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd64rr) {
+ // ADD64rr:
+ // mov64ri rax, 42
+ // add64rr rax, rax, rbx
+ // -> only rbx needs defining.
+ std::vector<InstructionInstance> Snippet;
+ {
+ InstructionInstance Mov(Runner.createInstruction(llvm::X86::MOV64ri));
+ Mov.getValueFor(Mov.Instr.Variables[0]) =
+ llvm::MCOperand::createReg(llvm::X86::RAX);
+ Mov.getValueFor(Mov.Instr.Variables[1]) = llvm::MCOperand::createImm(42);
+ Snippet.push_back(std::move(Mov));
+ }
+ {
+ InstructionInstance Add(Runner.createInstruction(llvm::X86::ADD64rr));
+ Add.getValueFor(Add.Instr.Variables[0]) =
+ llvm::MCOperand::createReg(llvm::X86::RAX);
+ Add.getValueFor(Add.Instr.Variables[1]) =
+ llvm::MCOperand::createReg(llvm::X86::RBX);
+ Snippet.push_back(std::move(Add));
+ }
+
+ const auto RegsToDef = Runner.computeRegsToDef(Snippet);
+ EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::RBX));
+}
+
+} // namespace
+} // namespace exegesis
diff --git a/unittests/tools/llvm-exegesis/X86/TargetTest.cpp b/unittests/tools/llvm-exegesis/X86/TargetTest.cpp
new file mode 100644
index 000000000000..0c2f3fbd9f9d
--- /dev/null
+++ b/unittests/tools/llvm-exegesis/X86/TargetTest.cpp
@@ -0,0 +1,170 @@
+#include "Target.h"
+
+#include <cassert>
+#include <memory>
+
+#include "MCTargetDesc/X86MCTargetDesc.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace exegesis {
+
+void InitializeX86ExegesisTarget();
+
+namespace {
+
+using testing::Gt;
+using testing::NotNull;
+using testing::SizeIs;
+
+constexpr const char kTriple[] = "x86_64-unknown-linux";
+
+class X86TargetTest : public ::testing::Test {
+protected:
+ X86TargetTest()
+ : ExegesisTarget_(ExegesisTarget::lookup(llvm::Triple(kTriple))) {
+ EXPECT_THAT(ExegesisTarget_, NotNull());
+ std::string error;
+ Target_ = llvm::TargetRegistry::lookupTarget(kTriple, error);
+ EXPECT_THAT(Target_, NotNull());
+ }
+ static void SetUpTestCase() {
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86TargetMC();
+ InitializeX86ExegesisTarget();
+ }
+
+ const llvm::Target *Target_;
+ const ExegesisTarget *const ExegesisTarget_;
+};
+
+TEST_F(X86TargetTest, SetRegToConstantGPR) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", ""));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::EAX);
+ EXPECT_THAT(Insts, SizeIs(1));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::MOV32ri);
+ EXPECT_EQ(Insts[0].getOperand(0).getReg(), llvm::X86::EAX);
+}
+
+TEST_F(X86TargetTest, SetRegToConstantXMM_SSE2) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", ""));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::XMM1);
+ EXPECT_THAT(Insts, SizeIs(7U));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8);
+ EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOVDQUrm);
+ EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::ADD64ri8);
+}
+
+TEST_F(X86TargetTest, SetRegToConstantXMM_AVX) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", "+avx"));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::XMM1);
+ EXPECT_THAT(Insts, SizeIs(7U));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8);
+ EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::VMOVDQUrm);
+ EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::ADD64ri8);
+}
+
+TEST_F(X86TargetTest, SetRegToConstantXMM_AVX512) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", "+avx512vl"));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::XMM1);
+ EXPECT_THAT(Insts, SizeIs(7U));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8);
+ EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::VMOVDQU32Z128rm);
+ EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::ADD64ri8);
+}
+
+TEST_F(X86TargetTest, SetRegToConstantMMX) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", ""));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::MM1);
+ EXPECT_THAT(Insts, SizeIs(5U));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8);
+ EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MMX_MOVQ64rm);
+ EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::ADD64ri8);
+}
+
+TEST_F(X86TargetTest, SetRegToConstantYMM_AVX) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", "+avx"));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::YMM1);
+ EXPECT_THAT(Insts, SizeIs(11U));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8);
+ EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[7].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[8].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[9].getOpcode(), llvm::X86::VMOVDQUYrm);
+ EXPECT_EQ(Insts[10].getOpcode(), llvm::X86::ADD64ri8);
+}
+
+TEST_F(X86TargetTest, SetRegToConstantYMM_AVX512) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", "+avx512vl"));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::YMM1);
+ EXPECT_THAT(Insts, SizeIs(11U));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8);
+ EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[7].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[8].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[9].getOpcode(), llvm::X86::VMOVDQU32Z256rm);
+ EXPECT_EQ(Insts[10].getOpcode(), llvm::X86::ADD64ri8);
+}
+
+TEST_F(X86TargetTest, SetRegToConstantZMM_AVX512) {
+ const std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ Target_->createMCSubtargetInfo(kTriple, "core2", "+avx512vl"));
+ const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::ZMM1);
+ EXPECT_THAT(Insts, SizeIs(19U));
+ EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8);
+ EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[7].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[8].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[9].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[10].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[11].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[12].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[13].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[14].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[15].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[16].getOpcode(), llvm::X86::MOV32mi);
+ EXPECT_EQ(Insts[17].getOpcode(), llvm::X86::VMOVDQU32Zrm);
+ EXPECT_EQ(Insts[18].getOpcode(), llvm::X86::ADD64ri8);
+}
+
+} // namespace
+} // namespace exegesis