[llvm-exegesis][NFC] Split BenchmarkRunner class

Summary:
The snippet-generation part goes to the SnippetGenerator class.

This will allow benchmarking arbitrary code (see PR38437).

Reviewers: gchatelet

Subscribers: mgorny, tschuett, llvm-commits

Differential Revision: https://reviews.llvm.org/D51979

llvm-svn: 342117
diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkCode.h b/llvm/tools/llvm-exegesis/lib/BenchmarkCode.h
new file mode 100644
index 0000000..1195adf4
--- /dev/null
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkCode.h
@@ -0,0 +1,38 @@
+//===-- BenchmarkCode.h -----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
+
+#include "llvm/MC/MCInst.h"
+#include <string>
+#include <vector>
+
+namespace exegesis {
+
+// A collection of instructions that are to be assembled, executed and measured.
+struct BenchmarkCode {
+  // The sequence of instructions that are to be repeated.
+  std::vector<llvm::MCInst> Instructions;
+
+  // Before the code is executed some instructions are added to setup the
+  // registers initial values.
+  std::vector<unsigned> RegsToDef;
+
+  // We also need to provide the registers that are live on entry for the
+  // assembler to generate proper prologue/epilogue.
+  std::vector<unsigned> LiveIns;
+
+  // Informations about how this configuration was built.
+  std::string Info;
+};
+
+} // namespace exegesis
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
index 7fa3926..2d82f59 100644
--- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -17,7 +17,6 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/FileSystem.h"
-#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Program.h"
 
@@ -28,37 +27,10 @@
 
 BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
                                  InstructionBenchmark::ModeE Mode)
-    : State(State), RATC(State.getRegInfo(),
-                         getFunctionReservedRegs(State.getTargetMachine())),
-      Mode(Mode), Scratch(llvm::make_unique<ScratchSpace>()) {}
+    : State(State), Mode(Mode), Scratch(llvm::make_unique<ScratchSpace>()) {}
 
 BenchmarkRunner::~BenchmarkRunner() = default;
 
-llvm::Expected<std::vector<InstructionBenchmark>>
-BenchmarkRunner::run(unsigned Opcode, unsigned NumRepetitions) {
-  const llvm::MCInstrDesc &InstrDesc = State.getInstrInfo().get(Opcode);
-  // Ignore instructions that we cannot run.
-  if (InstrDesc.isPseudo())
-    return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
-  if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
-    return llvm::make_error<BenchmarkFailure>(
-        "Unsupported opcode: isBranch/isIndirectBranch");
-  if (InstrDesc.isCall() || InstrDesc.isReturn())
-    return llvm::make_error<BenchmarkFailure>(
-        "Unsupported opcode: isCall/isReturn");
-
-  llvm::Expected<std::vector<BenchmarkCode>> ConfigurationOrError =
-      generateConfigurations(Opcode);
-
-  if (llvm::Error E = ConfigurationOrError.takeError())
-    return std::move(E);
-
-  std::vector<InstructionBenchmark> InstrBenchmarks;
-  for (const BenchmarkCode &Conf : ConfigurationOrError.get())
-    InstrBenchmarks.push_back(runConfiguration(Conf, NumRepetitions));
-  return InstrBenchmarks;
-}
-
 // Repeat the snippet until there are at least NumInstructions in the resulting
 // code.
 static std::vector<llvm::MCInst>
@@ -122,74 +94,6 @@
   return InstrBenchmark;
 }
 
-llvm::Expected<std::vector<BenchmarkCode>>
-BenchmarkRunner::generateConfigurations(unsigned Opcode) const {
-  if (auto E = generateCodeTemplate(Opcode)) {
-    CodeTemplate &CT = E.get();
-    std::vector<BenchmarkCode> Output;
-    // TODO: Generate as many BenchmarkCode as needed.
-    {
-      BenchmarkCode BC;
-      BC.Info = CT.Info;
-      for (InstructionBuilder &IB : CT.Instructions) {
-        IB.randomizeUnsetVariables(
-            CT.ScratchSpacePointerInReg
-                ? RATC.getRegister(CT.ScratchSpacePointerInReg).aliasedBits()
-                : RATC.emptyRegisters());
-        BC.Instructions.push_back(IB.build());
-      }
-      if (CT.ScratchSpacePointerInReg)
-        BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
-      BC.RegsToDef = computeRegsToDef(CT.Instructions);
-      Output.push_back(std::move(BC));
-    }
-    return Output;
-  } else
-    return E.takeError();
-}
-
-std::vector<unsigned> BenchmarkRunner::computeRegsToDef(
-    const std::vector<InstructionBuilder> &Instructions) const {
-  // Collect all register uses and create an assignment for each of them.
-  // Ignore memory operands which are handled separately.
-  // Loop invariant: DefinedRegs[i] is true iif it has been set at least once
-  // before the current instruction.
-  llvm::BitVector DefinedRegs = RATC.emptyRegisters();
-  std::vector<unsigned> RegsToDef;
-  for (const InstructionBuilder &IB : Instructions) {
-    // Returns the register that this Operand sets or uses, or 0 if this is not
-    // a register.
-    const auto GetOpReg = [&IB](const Operand &Op) -> unsigned {
-      if (Op.IsMem)
-        return 0;
-      if (Op.ImplicitReg)
-        return *Op.ImplicitReg;
-      if (Op.IsExplicit && IB.getValueFor(Op).isReg())
-        return IB.getValueFor(Op).getReg();
-      return 0;
-    };
-    // Collect used registers that have never been def'ed.
-    for (const Operand &Op : IB.Instr.Operands) {
-      if (!Op.IsDef) {
-        const unsigned Reg = GetOpReg(Op);
-        if (Reg > 0 && !DefinedRegs.test(Reg)) {
-          RegsToDef.push_back(Reg);
-          DefinedRegs.set(Reg);
-        }
-      }
-    }
-    // Mark defs as having been def'ed.
-    for (const Operand &Op : IB.Instr.Operands) {
-      if (Op.IsDef) {
-        const unsigned Reg = GetOpReg(Op);
-        if (Reg > 0)
-          DefinedRegs.set(Reg);
-      }
-    }
-  }
-  return RegsToDef;
-}
-
 llvm::Expected<std::string>
 BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
                                  llvm::ArrayRef<llvm::MCInst> Code) const {
@@ -204,32 +108,4 @@
   return ResultPath.str();
 }
 
-llvm::Expected<CodeTemplate> BenchmarkRunner::generateSelfAliasingCodeTemplate(
-    const Instruction &Instr) const {
-  const AliasingConfigurations SelfAliasing(Instr, Instr);
-  if (SelfAliasing.empty()) {
-    return llvm::make_error<BenchmarkFailure>("empty self aliasing");
-  }
-  CodeTemplate CT;
-  InstructionBuilder IB(Instr);
-  if (SelfAliasing.hasImplicitAliasing()) {
-    CT.Info = "implicit Self cycles, picking random values.";
-  } else {
-    CT.Info = "explicit self cycles, selecting one aliasing Conf.";
-    // This is a self aliasing instruction so defs and uses are from the same
-    // instance, hence twice IB in the following call.
-    setRandomAliasing(SelfAliasing, IB, IB);
-  }
-  CT.Instructions.push_back(std::move(IB));
-  return std::move(CT);
-}
-
-llvm::Expected<CodeTemplate>
-BenchmarkRunner::generateUnconstrainedCodeTemplate(const Instruction &Instr,
-                                                   llvm::StringRef Msg) const {
-  CodeTemplate CT;
-  CT.Info = llvm::formatv("{0}, repeating an unconstrained assignment", Msg);
-  CT.Instructions.emplace_back(Instr);
-  return std::move(CT);
-}
 } // namespace exegesis
diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h
index 5d7ea25..07ab23c 100644
--- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h
@@ -17,10 +17,10 @@
 #define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
 
 #include "Assembler.h"
+#include "BenchmarkCode.h"
 #include "BenchmarkResult.h"
 #include "LlvmState.h"
 #include "MCInstrDescView.h"
-#include "RegisterAliasing.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/Support/Error.h"
 #include <cstdlib>
@@ -36,23 +36,6 @@
   BenchmarkFailure(const llvm::Twine &S);
 };
 
-// A collection of instructions that are to be assembled, executed and measured.
-struct BenchmarkCode {
-  // The sequence of instructions that are to be repeated.
-  std::vector<llvm::MCInst> Instructions;
-
-  // Before the code is executed some instructions are added to setup the
-  // registers initial values.
-  std::vector<unsigned> RegsToDef;
-
-  // We also need to provide the registers that are live on entry for the
-  // assembler to generate proper prologue/epilogue.
-  std::vector<unsigned> LiveIns;
-
-  // Informations about how this configuration was built.
-  std::string Info;
-};
-
 // Common code for all benchmark modes.
 class BenchmarkRunner {
 public:
@@ -61,12 +44,8 @@
 
   virtual ~BenchmarkRunner();
 
-  llvm::Expected<std::vector<InstructionBenchmark>>
-  run(unsigned Opcode, unsigned NumRepetitions);
-
-  // Given a snippet, computes which registers the setup code needs to define.
-  std::vector<unsigned>
-  computeRegsToDef(const std::vector<InstructionBuilder> &Snippet) const;
+  InstructionBenchmark runConfiguration(const BenchmarkCode &Configuration,
+                                        unsigned NumRepetitions) const;
 
   // Scratch space to run instructions that touch memory.
   struct ScratchSpace {
@@ -87,33 +66,12 @@
 
 protected:
   const LLVMState &State;
-  const RegisterAliasingTrackerCache RATC;
-
-  // Generates a single code template that has a self-dependency.
-  llvm::Expected<CodeTemplate>
-  generateSelfAliasingCodeTemplate(const Instruction &Instr) const;
-  // Generates a single code template without assignment constraints.
-  llvm::Expected<CodeTemplate>
-  generateUnconstrainedCodeTemplate(const Instruction &Instr,
-                                    llvm::StringRef Msg) const;
 
 private:
-  // API to be implemented by subclasses.
-  virtual llvm::Expected<CodeTemplate>
-  generateCodeTemplate(unsigned Opcode) const = 0;
-
   virtual std::vector<BenchmarkMeasure>
   runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch,
                   const unsigned NumRepetitions) const = 0;
 
-  // Internal helpers.
-  InstructionBenchmark runConfiguration(const BenchmarkCode &Configuration,
-                                        unsigned NumRepetitions) const;
-
-  // Calls generateCodeTemplate and expands it into one or more BenchmarkCode.
-  llvm::Expected<std::vector<BenchmarkCode>>
-  generateConfigurations(unsigned Opcode) const;
-
   llvm::Expected<std::string>
   writeObjectFile(const BenchmarkCode &Configuration,
                   llvm::ArrayRef<llvm::MCInst> Code) const;
diff --git a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
index 175c2ad..17e6a98 100644
--- a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
+++ b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
@@ -19,6 +19,7 @@
   MCInstrDescView.cpp
   PerfHelper.cpp
   RegisterAliasing.cpp
+  SnippetGenerator.cpp
   Target.cpp
   Uops.cpp
   )
diff --git a/llvm/tools/llvm-exegesis/lib/Latency.cpp b/llvm/tools/llvm-exegesis/lib/Latency.cpp
index b8452ac3..d09cdc5 100644
--- a/llvm/tools/llvm-exegesis/lib/Latency.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Latency.cpp
@@ -29,9 +29,9 @@
   return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
 }
 
-LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
+LatencySnippetGenerator::~LatencySnippetGenerator() = default;
 
-llvm::Error LatencyBenchmarkRunner::isInfeasible(
+llvm::Error LatencySnippetGenerator::isInfeasible(
     const llvm::MCInstrDesc &MCInstrDesc) const {
   if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand))
     return llvm::make_error<BenchmarkFailure>(
@@ -43,7 +43,7 @@
 }
 
 llvm::Expected<CodeTemplate>
-LatencyBenchmarkRunner::generateTwoInstructionPrototype(
+LatencySnippetGenerator::generateTwoInstructionPrototype(
     const Instruction &Instr) const {
   std::vector<unsigned> Opcodes;
   Opcodes.resize(State.getInstrInfo().getNumOpcodes());
@@ -80,7 +80,7 @@
 }
 
 llvm::Expected<CodeTemplate>
-LatencyBenchmarkRunner::generateCodeTemplate(unsigned Opcode) const {
+LatencySnippetGenerator::generateCodeTemplate(unsigned Opcode) const {
   const auto &InstrDesc = State.getInstrInfo().get(Opcode);
   if (auto E = isInfeasible(InstrDesc))
     return std::move(E);
@@ -105,6 +105,8 @@
   return CounterName;
 }
 
+LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
+
 std::vector<BenchmarkMeasure>
 LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
                                         ScratchSpace &Scratch,
diff --git a/llvm/tools/llvm-exegesis/lib/Latency.h b/llvm/tools/llvm-exegesis/lib/Latency.h
index f8c524a..5b9be00 100644
--- a/llvm/tools/llvm-exegesis/lib/Latency.h
+++ b/llvm/tools/llvm-exegesis/lib/Latency.h
@@ -17,14 +17,14 @@
 
 #include "BenchmarkRunner.h"
 #include "MCInstrDescView.h"
+#include "SnippetGenerator.h"
 
 namespace exegesis {
 
-class LatencyBenchmarkRunner : public BenchmarkRunner {
+class LatencySnippetGenerator : public SnippetGenerator {
 public:
-  LatencyBenchmarkRunner(const LLVMState &State)
-      : BenchmarkRunner(State, InstructionBenchmark::Latency) {}
-  ~LatencyBenchmarkRunner() override;
+  LatencySnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
+  ~LatencySnippetGenerator() override;
 
   llvm::Expected<CodeTemplate>
   generateCodeTemplate(unsigned Opcode) const override;
@@ -34,14 +34,21 @@
 
   llvm::Expected<CodeTemplate>
   generateTwoInstructionPrototype(const Instruction &Instr) const;
+};
 
+class LatencyBenchmarkRunner : public BenchmarkRunner {
+public:
+  LatencyBenchmarkRunner(const LLVMState &State)
+      : BenchmarkRunner(State, InstructionBenchmark::Latency) {}
+  ~LatencyBenchmarkRunner() override;
+
+private:
   std::vector<BenchmarkMeasure>
   runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch,
                   const unsigned NumRepetitions) const override;
 
   virtual const char *getCounterName() const;
 };
-
 } // namespace exegesis
 
 #endif // LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
new file mode 100644
index 0000000..5b51a09
--- /dev/null
+++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
@@ -0,0 +1,130 @@
+//===-- SnippetGenerator.cpp ------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <array>
+#include <string>
+
+#include "Assembler.h"
+#include "MCInstrDescView.h"
+#include "SnippetGenerator.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Program.h"
+
+namespace exegesis {
+
+SnippetGeneratorFailure::SnippetGeneratorFailure(const llvm::Twine &S)
+    : llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
+
+SnippetGenerator::SnippetGenerator(const LLVMState &State)
+    : State(State), RATC(State.getRegInfo(),
+                         getFunctionReservedRegs(State.getTargetMachine())) {}
+
+SnippetGenerator::~SnippetGenerator() = default;
+
+llvm::Expected<std::vector<BenchmarkCode>>
+SnippetGenerator::generateConfigurations(unsigned Opcode) const {
+  if (auto E = generateCodeTemplate(Opcode)) {
+    CodeTemplate &CT = E.get();
+    std::vector<BenchmarkCode> Output;
+    // TODO: Generate as many BenchmarkCode as needed.
+    {
+      BenchmarkCode BC;
+      BC.Info = CT.Info;
+      for (InstructionBuilder &IB : CT.Instructions) {
+        IB.randomizeUnsetVariables(
+            CT.ScratchSpacePointerInReg
+                ? RATC.getRegister(CT.ScratchSpacePointerInReg).aliasedBits()
+                : RATC.emptyRegisters());
+        BC.Instructions.push_back(IB.build());
+      }
+      if (CT.ScratchSpacePointerInReg)
+        BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
+      BC.RegsToDef = computeRegsToDef(CT.Instructions);
+      Output.push_back(std::move(BC));
+    }
+    return Output;
+  } else
+    return E.takeError();
+}
+
+std::vector<unsigned> SnippetGenerator::computeRegsToDef(
+    const std::vector<InstructionBuilder> &Instructions) const {
+  // Collect all register uses and create an assignment for each of them.
+  // Ignore memory operands which are handled separately.
+  // Loop invariant: DefinedRegs[i] is true iif it has been set at least once
+  // before the current instruction.
+  llvm::BitVector DefinedRegs = RATC.emptyRegisters();
+  std::vector<unsigned> RegsToDef;
+  for (const InstructionBuilder &IB : Instructions) {
+    // Returns the register that this Operand sets or uses, or 0 if this is not
+    // a register.
+    const auto GetOpReg = [&IB](const Operand &Op) -> unsigned {
+      if (Op.IsMem)
+        return 0;
+      if (Op.ImplicitReg)
+        return *Op.ImplicitReg;
+      if (Op.IsExplicit && IB.getValueFor(Op).isReg())
+        return IB.getValueFor(Op).getReg();
+      return 0;
+    };
+    // Collect used registers that have never been def'ed.
+    for (const Operand &Op : IB.Instr.Operands) {
+      if (!Op.IsDef) {
+        const unsigned Reg = GetOpReg(Op);
+        if (Reg > 0 && !DefinedRegs.test(Reg)) {
+          RegsToDef.push_back(Reg);
+          DefinedRegs.set(Reg);
+        }
+      }
+    }
+    // Mark defs as having been def'ed.
+    for (const Operand &Op : IB.Instr.Operands) {
+      if (Op.IsDef) {
+        const unsigned Reg = GetOpReg(Op);
+        if (Reg > 0)
+          DefinedRegs.set(Reg);
+      }
+    }
+  }
+  return RegsToDef;
+}
+
+llvm::Expected<CodeTemplate> SnippetGenerator::generateSelfAliasingCodeTemplate(
+    const Instruction &Instr) const {
+  const AliasingConfigurations SelfAliasing(Instr, Instr);
+  if (SelfAliasing.empty()) {
+    return llvm::make_error<SnippetGeneratorFailure>("empty self aliasing");
+  }
+  CodeTemplate CT;
+  InstructionBuilder IB(Instr);
+  if (SelfAliasing.hasImplicitAliasing()) {
+    CT.Info = "implicit Self cycles, picking random values.";
+  } else {
+    CT.Info = "explicit self cycles, selecting one aliasing Conf.";
+    // This is a self aliasing instruction so defs and uses are from the same
+    // instance, hence twice IB in the following call.
+    setRandomAliasing(SelfAliasing, IB, IB);
+  }
+  CT.Instructions.push_back(std::move(IB));
+  return std::move(CT);
+}
+
+llvm::Expected<CodeTemplate>
+SnippetGenerator::generateUnconstrainedCodeTemplate(const Instruction &Instr,
+                                                    llvm::StringRef Msg) const {
+  CodeTemplate CT;
+  CT.Info = llvm::formatv("{0}, repeating an unconstrained assignment", Msg);
+  CT.Instructions.emplace_back(Instr);
+  return std::move(CT);
+}
+} // namespace exegesis
diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
new file mode 100644
index 0000000..ced8ebc
--- /dev/null
+++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
@@ -0,0 +1,74 @@
+//===-- SnippetGenerator.h --------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines the abstract SnippetGenerator class for generating code that allows
+/// measuring a certain property of instructions (e.g. latency).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
+
+#include "Assembler.h"
+#include "BenchmarkCode.h"
+#include "LlvmState.h"
+#include "MCInstrDescView.h"
+#include "RegisterAliasing.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Error.h"
+#include <cstdlib>
+#include <memory>
+#include <vector>
+
+namespace exegesis {
+
+// A class representing failures that happened during Benchmark, they are used
+// to report informations to the user.
+class SnippetGeneratorFailure : public llvm::StringError {
+public:
+  SnippetGeneratorFailure(const llvm::Twine &S);
+};
+
+// Common code for all benchmark modes.
+class SnippetGenerator {
+public:
+  explicit SnippetGenerator(const LLVMState &State);
+
+  virtual ~SnippetGenerator();
+
+  // Calls generateCodeTemplate and expands it into one or more BenchmarkCode.
+  llvm::Expected<std::vector<BenchmarkCode>>
+  generateConfigurations(unsigned Opcode) const;
+
+  // Given a snippet, computes which registers the setup code needs to define.
+  std::vector<unsigned>
+  computeRegsToDef(const std::vector<InstructionBuilder> &Snippet) const;
+
+protected:
+  const LLVMState &State;
+  const RegisterAliasingTrackerCache RATC;
+
+  // Generates a single code template that has a self-dependency.
+  llvm::Expected<CodeTemplate>
+  generateSelfAliasingCodeTemplate(const Instruction &Instr) const;
+  // Generates a single code template without assignment constraints.
+  llvm::Expected<CodeTemplate>
+  generateUnconstrainedCodeTemplate(const Instruction &Instr,
+                                    llvm::StringRef Msg) const;
+
+private:
+  // API to be implemented by subclasses.
+  virtual llvm::Expected<CodeTemplate>
+  generateCodeTemplate(unsigned Opcode) const = 0;
+};
+
+} // namespace exegesis
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
diff --git a/llvm/tools/llvm-exegesis/lib/Target.cpp b/llvm/tools/llvm-exegesis/lib/Target.cpp
index 67a9ea6..a8b3487 100644
--- a/llvm/tools/llvm-exegesis/lib/Target.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Target.cpp
@@ -36,6 +36,20 @@
   FirstTarget = Target;
 }
 
+std::unique_ptr<SnippetGenerator>
+ExegesisTarget::createSnippetGenerator(InstructionBenchmark::ModeE Mode,
+                                       const LLVMState &State) const {
+  switch (Mode) {
+  case InstructionBenchmark::Unknown:
+    return nullptr;
+  case InstructionBenchmark::Latency:
+    return createLatencySnippetGenerator(State);
+  case InstructionBenchmark::Uops:
+    return createUopsSnippetGenerator(State);
+  }
+  return nullptr;
+}
+
 std::unique_ptr<BenchmarkRunner>
 ExegesisTarget::createBenchmarkRunner(InstructionBenchmark::ModeE Mode,
                                       const LLVMState &State) const {
@@ -50,6 +64,16 @@
   return nullptr;
 }
 
+std::unique_ptr<SnippetGenerator>
+ExegesisTarget::createLatencySnippetGenerator(const LLVMState &State) const {
+  return llvm::make_unique<LatencySnippetGenerator>(State);
+}
+
+std::unique_ptr<SnippetGenerator>
+ExegesisTarget::createUopsSnippetGenerator(const LLVMState &State) const {
+  return llvm::make_unique<UopsSnippetGenerator>(State);
+}
+
 std::unique_ptr<BenchmarkRunner>
 ExegesisTarget::createLatencyBenchmarkRunner(const LLVMState &State) const {
   return llvm::make_unique<LatencyBenchmarkRunner>(State);
diff --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h
index 9f8fbc3..fe8a9e2 100644
--- a/llvm/tools/llvm-exegesis/lib/Target.h
+++ b/llvm/tools/llvm-exegesis/lib/Target.h
@@ -20,6 +20,7 @@
 #include "BenchmarkResult.h"
 #include "BenchmarkRunner.h"
 #include "LlvmState.h"
+#include "SnippetGenerator.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
 #include "llvm/IR/CallingConv.h"
@@ -61,6 +62,10 @@
   // matter as long as it's large enough.
   virtual unsigned getMaxMemoryAccessSize() const { return 0; }
 
+  // Creates a snippet generator for the given mode.
+  std::unique_ptr<SnippetGenerator>
+  createSnippetGenerator(InstructionBenchmark::ModeE Mode,
+                         const LLVMState &State) const;
   // Creates a benchmark runner for the given mode.
   std::unique_ptr<BenchmarkRunner>
   createBenchmarkRunner(InstructionBenchmark::ModeE Mode,
@@ -79,8 +84,12 @@
 private:
   virtual bool matchesArch(llvm::Triple::ArchType Arch) const = 0;
 
-  // Targets can implement their own Latency/Uops benchmarks runners by
+  // Targets can implement their own snippet generators/benchmarks runners by
   // implementing these.
+  std::unique_ptr<SnippetGenerator> virtual createLatencySnippetGenerator(
+      const LLVMState &State) const;
+  std::unique_ptr<SnippetGenerator> virtual createUopsSnippetGenerator(
+      const LLVMState &State) const;
   std::unique_ptr<BenchmarkRunner> virtual createLatencyBenchmarkRunner(
       const LLVMState &State) const;
   std::unique_ptr<BenchmarkRunner> virtual createUopsBenchmarkRunner(
diff --git a/llvm/tools/llvm-exegesis/lib/Uops.cpp b/llvm/tools/llvm-exegesis/lib/Uops.cpp
index d7346de..beb9fa8 100644
--- a/llvm/tools/llvm-exegesis/lib/Uops.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Uops.cpp
@@ -86,7 +86,7 @@
 }
 
 llvm::Error
-UopsBenchmarkRunner::isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const {
+UopsSnippetGenerator::isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const {
   if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand))
     return llvm::make_error<BenchmarkFailure>(
         "Infeasible : has unknown operands");
@@ -123,8 +123,9 @@
 }
 
 UopsBenchmarkRunner::~UopsBenchmarkRunner() = default;
+UopsSnippetGenerator::~UopsSnippetGenerator() = default;
 
-void UopsBenchmarkRunner::instantiateMemoryOperands(
+void UopsSnippetGenerator::instantiateMemoryOperands(
     const unsigned ScratchSpacePointerInReg,
     std::vector<InstructionBuilder> &Instructions) const {
   if (ScratchSpacePointerInReg == 0)
@@ -144,11 +145,12 @@
     ++I;
     Instructions.push_back(std::move(IB));
   }
-  assert(I * MemStep < ScratchSpace::kSize && "not enough scratch space");
+  assert(I * MemStep < BenchmarkRunner::ScratchSpace::kSize &&
+         "not enough scratch space");
 }
 
 llvm::Expected<CodeTemplate>
-UopsBenchmarkRunner::generateCodeTemplate(unsigned Opcode) const {
+UopsSnippetGenerator::generateCodeTemplate(unsigned Opcode) const {
   const auto &InstrDesc = State.getInstrInfo().get(Opcode);
   if (auto E = isInfeasible(InstrDesc))
     return std::move(E);
@@ -285,6 +287,6 @@
   return Result;
 }
 
-constexpr const size_t UopsBenchmarkRunner::kMinNumDifferentAddresses;
+constexpr const size_t UopsSnippetGenerator::kMinNumDifferentAddresses;
 
 } // namespace exegesis
diff --git a/llvm/tools/llvm-exegesis/lib/Uops.h b/llvm/tools/llvm-exegesis/lib/Uops.h
index 0236cd9..160397a 100644
--- a/llvm/tools/llvm-exegesis/lib/Uops.h
+++ b/llvm/tools/llvm-exegesis/lib/Uops.h
@@ -16,14 +16,14 @@
 #define LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H
 
 #include "BenchmarkRunner.h"
+#include "SnippetGenerator.h"
 
 namespace exegesis {
 
-class UopsBenchmarkRunner : public BenchmarkRunner {
+class UopsSnippetGenerator : public SnippetGenerator {
 public:
-  UopsBenchmarkRunner(const LLVMState &State)
-      : BenchmarkRunner(State, InstructionBenchmark::Uops) {}
-  ~UopsBenchmarkRunner() override;
+  UopsSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
+  ~UopsSnippetGenerator() override;
 
   llvm::Expected<CodeTemplate>
   generateCodeTemplate(unsigned Opcode) const override;
@@ -33,10 +33,6 @@
 private:
   llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const;
 
-  std::vector<BenchmarkMeasure>
-  runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch,
-                  const unsigned NumRepetitions) const override;
-
   // Instantiates memory operands within a snippet.
   // To make computations as parallel as possible, we generate independant
   // memory locations for instructions that load and store. If there are less
@@ -65,6 +61,20 @@
                             std::vector<InstructionBuilder> &Snippet) const;
 };
 
+class UopsBenchmarkRunner : public BenchmarkRunner {
+public:
+  UopsBenchmarkRunner(const LLVMState &State)
+      : BenchmarkRunner(State, InstructionBenchmark::Uops) {}
+  ~UopsBenchmarkRunner() override;
+
+  static constexpr const size_t kMinNumDifferentAddresses = 6;
+
+private:
+  std::vector<BenchmarkMeasure>
+  runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch,
+                  const unsigned NumRepetitions) const override;
+};
+
 } // namespace exegesis
 
 #endif // LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H
diff --git a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
index 511aa55..29a7dca 100644
--- a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
+++ b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
@@ -22,7 +22,7 @@
 namespace {
 
 // Common code for X86 Uops and Latency runners.
-template <typename Impl> class X86BenchmarkRunner : public Impl {
+template <typename Impl> class X86SnippetGenerator : public Impl {
   using Impl::Impl;
 
   llvm::Expected<CodeTemplate>
@@ -71,21 +71,23 @@
   }
 };
 
-class X86LatencyImpl : public LatencyBenchmarkRunner {
+class X86LatencyImpl : public LatencySnippetGenerator {
 protected:
-  using Base = LatencyBenchmarkRunner;
+  using Base = LatencySnippetGenerator;
   using Base::Base;
   llvm::Expected<CodeTemplate> handleCompareFP(const Instruction &Instr) const {
-    return llvm::make_error<BenchmarkFailure>("Unsupported x87 CompareFP");
+    return llvm::make_error<SnippetGeneratorFailure>(
+        "Unsupported x87 CompareFP");
   }
   llvm::Expected<CodeTemplate> handleCondMovFP(const Instruction &Instr) const {
-    return llvm::make_error<BenchmarkFailure>("Unsupported x87 CondMovFP");
+    return llvm::make_error<SnippetGeneratorFailure>(
+        "Unsupported x87 CondMovFP");
   }
 };
 
-class X86UopsImpl : public UopsBenchmarkRunner {
+class X86UopsImpl : public UopsSnippetGenerator {
 protected:
-  using Base = UopsBenchmarkRunner;
+  using Base = UopsSnippetGenerator;
   using Base::Base;
   // We can compute uops for any FP instruction that does not grow or shrink the
   // stack (either do not touch the stack or push as much as they pop).
@@ -193,14 +195,14 @@
     return {};
   }
 
-  std::unique_ptr<BenchmarkRunner>
-  createLatencyBenchmarkRunner(const LLVMState &State) const override {
-    return llvm::make_unique<X86BenchmarkRunner<X86LatencyImpl>>(State);
+  std::unique_ptr<SnippetGenerator>
+  createLatencySnippetGenerator(const LLVMState &State) const override {
+    return llvm::make_unique<X86SnippetGenerator<X86LatencyImpl>>(State);
   }
 
-  std::unique_ptr<BenchmarkRunner>
-  createUopsBenchmarkRunner(const LLVMState &State) const override {
-    return llvm::make_unique<X86BenchmarkRunner<X86UopsImpl>>(State);
+  std::unique_ptr<SnippetGenerator>
+  createUopsSnippetGenerator(const LLVMState &State) const override {
+    return llvm::make_unique<X86SnippetGenerator<X86UopsImpl>>(State);
   }
 
   bool matchesArch(llvm::Triple::ArchType Arch) const override {
diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
index 6b626b0..1465d14 100644
--- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
+++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -119,6 +119,30 @@
   return Ctx;
 }
 
+// Generates code snippets for opcode `Opcode`.
+llvm::Expected<std::vector<BenchmarkCode>>
+generateSnippets(const LLVMState &State, unsigned Opcode,
+                 unsigned NumRepetitions) {
+  const std::unique_ptr<SnippetGenerator> Generator =
+      State.getExegesisTarget().createSnippetGenerator(BenchmarkMode, State);
+  if (!Generator) {
+    llvm::report_fatal_error("cannot create snippet generator");
+  }
+
+  const llvm::MCInstrDesc &InstrDesc = State.getInstrInfo().get(Opcode);
+  // Ignore instructions that we cannot run.
+  if (InstrDesc.isPseudo())
+    return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
+  if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
+    return llvm::make_error<BenchmarkFailure>(
+        "Unsupported opcode: isBranch/isIndirectBranch");
+  if (InstrDesc.isCall() || InstrDesc.isReturn())
+    return llvm::make_error<BenchmarkFailure>(
+        "Unsupported opcode: isCall/isReturn");
+
+  return Generator->generateConfigurations(Opcode);
+}
+
 void benchmarkMain() {
   if (exegesis::pfm::pfmInitialize())
     llvm::report_fatal_error("cannot initialize libpfm");
@@ -140,6 +164,10 @@
     return;
   }
 
+  // FIXME: Allow arbitrary code.
+  const std::vector<BenchmarkCode> Configurations =
+      ExitOnErr(generateSnippets(State, Opcode, NumRepetitions));
+
   const std::unique_ptr<BenchmarkRunner> Runner =
       State.getExegesisTarget().createBenchmarkRunner(BenchmarkMode, State);
   if (!Runner) {
@@ -154,11 +182,12 @@
     BenchmarkFile = "-";
 
   const BenchmarkResultContext Context = getBenchmarkResultContext(State);
-  std::vector<InstructionBenchmark> Results =
-      ExitOnErr(Runner->run(Opcode, NumRepetitions));
-  for (InstructionBenchmark &Result : Results)
-    ExitOnErr(Result.writeYaml(Context, BenchmarkFile));
 
+  for (const BenchmarkCode &Conf : Configurations) {
+    InstructionBenchmark Result =
+        Runner->runConfiguration(Conf, NumRepetitions);
+    ExitOnErr(Result.writeYaml(Context, BenchmarkFile));
+  }
   exegesis::pfm::pfmTerminate();
 }