summaryrefslogtreecommitdiff
path: root/unittests/tools
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/tools')
-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
18 files changed, 1552 insertions, 29 deletions
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