[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