[llvm-exegesis] Cleaner design without mutable data.
Summary: Previous design was relying on the 'mutate' keyword and was quite confusing. This version separate mutable from immutable data and makes it clearer what changes and what doesn't.
Reviewers: courbet
Subscribers: tschuett, llvm-commits
Differential Revision: https://reviews.llvm.org/D48020
llvm-svn: 334596
diff --git a/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt b/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
index b4e5ff6..b96bdb0 100644
--- a/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
+++ b/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
@@ -14,8 +14,9 @@
)
add_llvm_unittest(LLVMExegesisX86Tests
- RegisterAliasingTest.cpp
AssemblerTest.cpp
AnalysisTest.cpp
+ SnippetGeneratorTest.cpp
+ RegisterAliasingTest.cpp
)
target_link_libraries(LLVMExegesisX86Tests PRIVATE LLVMExegesis)
diff --git a/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
new file mode 100644
index 0000000..0e4a3fb
--- /dev/null
+++ b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
@@ -0,0 +1,199 @@
+//===-- 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 {
+
+class X86SnippetGeneratorTest : public ::testing::Test {
+protected:
+ X86SnippetGeneratorTest()
+ : MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
+
+ static void SetUpTestCase() {
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86TargetMC();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86AsmPrinter();
+ }
+
+ const LLVMState State;
+ const llvm::MCInstrInfo &MCInstrInfo;
+ const llvm::MCRegisterInfo &MCRegisterInfo;
+};
+
+class LatencySnippetGeneratorTest : public X86SnippetGeneratorTest {
+protected:
+ LatencySnippetGeneratorTest() : Runner(State) {}
+
+ BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) {
+ randomGenerator().seed(0); // Initialize seed.
+ auto ConfOrError = Runner.generateConfiguration(Opcode);
+ EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration.
+ return ConfOrError.get();
+ }
+
+ LatencyBenchmarkRunner Runner;
+};
+
+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;
+ auto Conf = checkAndGetConfiguration(Opcode);
+ EXPECT_THAT(Conf.Info, testing::HasSubstr("implicit"));
+ ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
+ const llvm::MCInst Instr = Conf.Snippet[0];
+ EXPECT_THAT(Instr.getOpcode(), Opcode);
+ EXPECT_THAT(Instr.getNumOperands(), 1);
+ EXPECT_TRUE(Instr.getOperand(0).isImm()); // Use
+ 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);
+}
+
+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;
+ auto Conf = checkAndGetConfiguration(Opcode);
+ EXPECT_THAT(Conf.Info, testing::HasSubstr("explicit"));
+ ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
+ const llvm::MCInst Instr = Conf.Snippet[0];
+ EXPECT_THAT(Instr.getOpcode(), Opcode);
+ EXPECT_THAT(Instr.getNumOperands(), 3);
+ EXPECT_TRUE(Instr.getOperand(0).isReg());
+ EXPECT_TRUE(Instr.getOperand(1).isReg());
+ EXPECT_THAT(Instr.getOperand(0).getReg(), Instr.getOperand(1).getReg())
+ << "Op0 and Op1 should have the same value";
+ EXPECT_TRUE(Instr.getOperand(2).isImm());
+ EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
+}
+
+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;
+ auto Conf = checkAndGetConfiguration(Opcode);
+ EXPECT_THAT(Conf.Info, testing::HasSubstr("cycle through CMOVLE16rr"));
+ ASSERT_THAT(Conf.Snippet, testing::SizeIs(2));
+ // TODO: check that the two instructions alias each other.
+}
+
+class UopsSnippetGeneratorTest : public X86SnippetGeneratorTest {
+protected:
+ UopsSnippetGeneratorTest() : Runner(State) {}
+
+ BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) {
+ randomGenerator().seed(0); // Initialize seed.
+ auto ConfOrError = Runner.generateConfiguration(Opcode);
+ EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration.
+ return ConfOrError.get();
+ }
+
+ UopsBenchmarkRunner Runner;
+};
+
+TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
+ // BNDCL32rr is parallelno matter what.
+
+ // explicit use 0 : reg RegClass=BNDR
+ // explicit use 1 : reg RegClass=GR32
+
+ const unsigned Opcode = llvm::X86::BNDCL32rr;
+ auto Conf = checkAndGetConfiguration(Opcode);
+ EXPECT_THAT(Conf.Info, testing::HasSubstr("parallel"));
+ ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
+ const llvm::MCInst Instr = Conf.Snippet[0];
+ EXPECT_THAT(Instr.getOpcode(), Opcode);
+}
+
+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;
+ auto Conf = checkAndGetConfiguration(Opcode);
+ EXPECT_THAT(Conf.Info, testing::HasSubstr("serial"));
+ ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
+ const llvm::MCInst Instr = Conf.Snippet[0];
+ EXPECT_THAT(Instr.getOpcode(), Opcode);
+}
+
+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;
+ auto Conf = checkAndGetConfiguration(Opcode);
+ EXPECT_THAT(Conf.Info, testing::HasSubstr("static renaming"));
+ constexpr const unsigned kInstructionCount = 15;
+ ASSERT_THAT(Conf.Snippet, testing::SizeIs(kInstructionCount));
+ std::unordered_set<unsigned> AllDefRegisters;
+ for (const auto &Inst : Conf.Snippet)
+ AllDefRegisters.insert(Inst.getOperand(0).getReg());
+ EXPECT_THAT(AllDefRegisters, testing::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;
+ auto Conf = checkAndGetConfiguration(Opcode);
+ EXPECT_THAT(Conf.Info, testing::HasSubstr("no tied variables"));
+ ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
+ const llvm::MCInst Instr = Conf.Snippet[0];
+ EXPECT_THAT(Instr.getOpcode(), Opcode);
+ EXPECT_THAT(Instr.getNumOperands(), 4);
+ EXPECT_THAT(Instr.getOperand(0).getReg(),
+ testing::Not(Instr.getOperand(1).getReg()))
+ << "Def is different from first Use";
+ EXPECT_THAT(Instr.getOperand(0).getReg(),
+ testing::Not(Instr.getOperand(2).getReg()))
+ << "Def is different from second Use";
+ EXPECT_THAT(Instr.getOperand(3).getImm(), 1);
+}
+
+} // namespace
+} // namespace exegesis