diff options
Diffstat (limited to 'unittests/tools')
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 |