[llvm] r334596 - [llvm-exegesis] Cleaner design without mutable data.

Guillaume Chatelet via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 13 06:24:41 PDT 2018


Author: gchatelet
Date: Wed Jun 13 06:24:41 2018
New Revision: 334596

URL: http://llvm.org/viewvc/llvm-project?rev=334596&view=rev
Log:
[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

Added:
    llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
Modified:
    llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
    llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h
    llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp
    llvm/trunk/tools/llvm-exegesis/lib/Latency.h
    llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp
    llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h
    llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.cpp
    llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.h
    llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp
    llvm/trunk/tools/llvm-exegesis/lib/Uops.h
    llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt

Modified: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp Wed Jun 13 06:24:41 2018
@@ -23,6 +23,9 @@
 
 namespace exegesis {
 
+BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
+    : llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
+
 BenchmarkRunner::InstructionFilter::~InstructionFilter() = default;
 
 BenchmarkRunner::BenchmarkRunner(const LLVMState &State)
@@ -38,14 +41,13 @@ BenchmarkRunner::run(unsigned Opcode, co
                      unsigned NumRepetitions) {
   // Ignore instructions that we cannot run.
   if (State.getInstrInfo().get(Opcode).isPseudo())
-    return llvm::make_error<llvm::StringError>("Unsupported opcode: isPseudo",
-                                               llvm::inconvertibleErrorCode());
+    return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
 
   if (llvm::Error E = Filter.shouldRun(State, Opcode))
     return std::move(E);
 
   llvm::Expected<std::vector<BenchmarkConfiguration>> ConfigurationOrError =
-      createConfigurations(RATC, Opcode);
+      createConfigurations(Opcode);
 
   if (llvm::Error E = ConfigurationOrError.takeError())
     return std::move(E);

Modified: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h Wed Jun 13 06:24:41 2018
@@ -26,6 +26,13 @@
 
 namespace exegesis {
 
+// A class representing failures that happened during Benchmark, they are used
+// to report informations to the user.
+class BenchmarkFailure : public llvm::StringError {
+public:
+  BenchmarkFailure(const llvm::Twine &S);
+};
+
 // A collection of instructions that are to be assembled, executed and measured.
 struct BenchmarkConfiguration {
   // This code is run before the Snippet is iterated. Since it is part of the
@@ -67,6 +74,7 @@ protected:
   const LLVMState &State;
   const llvm::MCInstrInfo &MCInstrInfo;
   const llvm::MCRegisterInfo &MCRegisterInfo;
+  const RegisterAliasingTrackerCache RATC;
 
 private:
   InstructionBenchmark runOne(const BenchmarkConfiguration &Configuration,
@@ -75,8 +83,7 @@ private:
   virtual InstructionBenchmark::ModeE getMode() const = 0;
 
   virtual llvm::Expected<std::vector<BenchmarkConfiguration>>
-  createConfigurations(RegisterAliasingTrackerCache &RATC,
-                       unsigned Opcode) const = 0;
+  createConfigurations(unsigned Opcode) const = 0;
 
   virtual std::vector<BenchmarkMeasure>
   runMeasurements(const ExecutableFunction &EF,
@@ -84,8 +91,6 @@ private:
 
   llvm::Expected<std::string>
   writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const;
-
-  RegisterAliasingTrackerCache RATC;
 };
 
 } // namespace exegesis

Modified: llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp Wed Jun 13 06:24:41 2018
@@ -19,93 +19,113 @@
 
 namespace exegesis {
 
-static bool HasUnknownOperand(const llvm::MCOperandInfo &OpInfo) {
+static bool hasUnknownOperand(const llvm::MCOperandInfo &OpInfo) {
   return OpInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN;
 }
 
 // FIXME: Handle memory, see PR36905.
-static bool HasMemoryOperand(const llvm::MCOperandInfo &OpInfo) {
+static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) {
   return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
 }
 
-static bool IsInfeasible(const Instruction &Instruction, std::string &Error) {
-  const auto &MCInstrDesc = Instruction.Description;
-  if (MCInstrDesc.isPseudo()) {
-    Error = "is pseudo";
-    return true;
-  }
-  if (llvm::any_of(MCInstrDesc.operands(), HasUnknownOperand)) {
-    Error = "has unknown operands";
-    return true;
-  }
-  if (llvm::any_of(MCInstrDesc.operands(), HasMemoryOperand)) {
-    Error = "has memory operands";
-    return true;
-  }
-  return false;
-}
-
 LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
 
 InstructionBenchmark::ModeE LatencyBenchmarkRunner::getMode() const {
   return InstructionBenchmark::Latency;
 }
 
-llvm::Expected<std::vector<BenchmarkConfiguration>>
-LatencyBenchmarkRunner::createConfigurations(RegisterAliasingTrackerCache &RATC,
-                                             unsigned Opcode) const {
-  const llvm::MCInstrDesc &MCInstrDesc = MCInstrInfo.get(Opcode);
-  const Instruction ThisInstruction(MCInstrDesc, RATC);
-
-  std::string Error;
-  if (IsInfeasible(ThisInstruction, Error))
-    return llvm::make_error<llvm::StringError>(
-        llvm::Twine("Infeasible : ").concat(Error),
-        llvm::inconvertibleErrorCode());
+llvm::Error LatencyBenchmarkRunner::isInfeasible(
+    const llvm::MCInstrDesc &MCInstrDesc) const {
+  if (MCInstrDesc.isPseudo())
+    return llvm::make_error<BenchmarkFailure>("Infeasible : is pseudo");
+  if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand))
+    return llvm::make_error<BenchmarkFailure>(
+        "Infeasible : has unknown operands");
+  if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand))
+    return llvm::make_error<BenchmarkFailure>(
+        "Infeasible : has memory operands");
+  return llvm::Error::success();
+}
 
+llvm::Expected<BenchmarkConfiguration>
+LatencyBenchmarkRunner::generateSelfAliasingConfiguration(
+    const Instruction &Instr,
+    const AliasingConfigurations &SelfAliasing) const {
   BenchmarkConfiguration Conf;
-  const AliasingConfigurations SelfAliasing(ThisInstruction, ThisInstruction);
-  if (!SelfAliasing.empty()) {
-    if (!SelfAliasing.hasImplicitAliasing()) {
-      Conf.Info = "explicit self cycles, selecting one aliasing Conf.";
-      setRandomAliasing(SelfAliasing);
-    } else {
-      Conf.Info = "implicit Self cycles, picking random values.";
-    }
-    Conf.Snippet = {randomizeUnsetVariablesAndBuild(ThisInstruction)};
-    return std::vector<BenchmarkConfiguration>{Conf};
+  InstructionInstance II(Instr);
+  if (SelfAliasing.hasImplicitAliasing()) {
+    Conf.Info = "implicit Self cycles, picking random values.";
+  } else {
+    Conf.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 II in the following call.
+    setRandomAliasing(SelfAliasing, II, II);
   }
+  Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
+  return Conf;
+}
 
-  // Let's try to create a dependency through another opcode.
+llvm::Expected<BenchmarkConfiguration>
+LatencyBenchmarkRunner::generateTwoInstructionConfiguration(
+    const Instruction &Instr,
+    const AliasingConfigurations &SelfAliasing) const {
   std::vector<unsigned> Opcodes;
   Opcodes.resize(MCInstrInfo.getNumOpcodes());
   std::iota(Opcodes.begin(), Opcodes.end(), 0U);
   std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator());
   for (const unsigned OtherOpcode : Opcodes) {
-    clearVariableAssignments(ThisInstruction);
-    if (OtherOpcode == Opcode)
+    if (OtherOpcode == Instr.Description.Opcode)
       continue;
-    const Instruction OtherInstruction(MCInstrInfo.get(OtherOpcode), RATC);
-    if (IsInfeasible(OtherInstruction, Error))
+    const auto &OtherInstrDesc = MCInstrInfo.get(OtherOpcode);
+    if (auto E = isInfeasible(OtherInstrDesc)) {
+      llvm::consumeError(std::move(E));
       continue;
-    const AliasingConfigurations Forward(ThisInstruction, OtherInstruction);
-    const AliasingConfigurations Back(OtherInstruction, ThisInstruction);
+    }
+    const Instruction OtherInstr(OtherInstrDesc, RATC);
+    const AliasingConfigurations Forward(Instr, OtherInstr);
+    const AliasingConfigurations Back(OtherInstr, Instr);
     if (Forward.empty() || Back.empty())
       continue;
-    setRandomAliasing(Forward);
-    setRandomAliasing(Back);
+    InstructionInstance ThisII(Instr);
+    InstructionInstance OtherII(OtherInstr);
+    if (!Forward.hasImplicitAliasing())
+      setRandomAliasing(Forward, ThisII, OtherII);
+    if (!Back.hasImplicitAliasing())
+      setRandomAliasing(Back, OtherII, ThisII);
+    BenchmarkConfiguration Conf;
     Conf.Info = llvm::Twine("creating cycle through ")
                     .concat(MCInstrInfo.getName(OtherOpcode))
                     .concat(".")
                     .str();
-    Conf.Snippet.push_back(randomizeUnsetVariablesAndBuild(ThisInstruction));
-    Conf.Snippet.push_back(randomizeUnsetVariablesAndBuild(OtherInstruction));
-    return std::vector<BenchmarkConfiguration>{Conf};
+    Conf.Snippet.push_back(ThisII.randomizeUnsetVariablesAndBuild());
+    Conf.Snippet.push_back(OtherII.randomizeUnsetVariablesAndBuild());
+    return Conf;
   }
+  return llvm::make_error<BenchmarkFailure>(
+      "Infeasible : Didn't find any scheme to make the instruction serial");
+}
 
-  return llvm::make_error<llvm::StringError>(
-      "Infeasible : Didn't find any scheme to make the instruction serial",
-      llvm::inconvertibleErrorCode());
+llvm::Expected<BenchmarkConfiguration>
+LatencyBenchmarkRunner::generateConfiguration(unsigned Opcode) const {
+  const auto &InstrDesc = MCInstrInfo.get(Opcode);
+  if (auto E = isInfeasible(InstrDesc))
+    return std::move(E);
+  const Instruction Instr(InstrDesc, RATC);
+  const AliasingConfigurations SelfAliasing(Instr, Instr);
+  if (SelfAliasing.empty()) {
+    // No self aliasing, trying to create a dependency through another opcode.
+    return generateTwoInstructionConfiguration(Instr, SelfAliasing);
+  } else {
+    return generateSelfAliasingConfiguration(Instr, SelfAliasing);
+  }
+}
+
+llvm::Expected<std::vector<BenchmarkConfiguration>>
+LatencyBenchmarkRunner::createConfigurations(unsigned Opcode) const {
+  if (auto E = generateConfiguration(Opcode))
+    return std::vector<BenchmarkConfiguration>{E.get()};
+  else
+    return E.takeError();
 }
 
 std::vector<BenchmarkMeasure>

Modified: llvm/trunk/tools/llvm-exegesis/lib/Latency.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/Latency.h?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/Latency.h (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/Latency.h Wed Jun 13 06:24:41 2018
@@ -16,6 +16,7 @@
 #define LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
 
 #include "BenchmarkRunner.h"
+#include "MCInstrDescView.h"
 
 namespace exegesis {
 
@@ -24,12 +25,24 @@ public:
   using BenchmarkRunner::BenchmarkRunner;
   ~LatencyBenchmarkRunner() override;
 
+  llvm::Expected<BenchmarkConfiguration>
+  generateConfiguration(unsigned Opcode) const;
+
 private:
+  llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const;
+
+  llvm::Expected<BenchmarkConfiguration> generateSelfAliasingConfiguration(
+      const Instruction &Instr,
+      const AliasingConfigurations &SelfAliasing) const;
+
+  llvm::Expected<BenchmarkConfiguration> generateTwoInstructionConfiguration(
+      const Instruction &Instr,
+      const AliasingConfigurations &SelfAliasing) const;
+
   InstructionBenchmark::ModeE getMode() const override;
 
   llvm::Expected<std::vector<BenchmarkConfiguration>>
-  createConfigurations(RegisterAliasingTrackerCache &RATC,
-                       unsigned OpcodeIndex) const override;
+  createConfigurations(unsigned OpcodeIndex) const override;
 
   std::vector<BenchmarkMeasure>
   runMeasurements(const ExecutableFunction &EF,

Modified: llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp Wed Jun 13 06:24:41 2018
@@ -17,14 +17,8 @@
 
 namespace exegesis {
 
-static void tie(const Operand *FromOperand, llvm::Optional<Variable> &Var) {
-  if (!Var)
-    Var.emplace();
-  Var->TiedOperands.push_back(FromOperand);
-}
-
 Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
-                         RegisterAliasingTrackerCache &RATC)
+                         const RegisterAliasingTrackerCache &RATC)
     : Description(MCInstrDesc) {
   unsigned OpIndex = 0;
   for (; OpIndex < MCInstrDesc.getNumOperands(); ++OpIndex) {
@@ -36,6 +30,8 @@ Instruction::Instruction(const llvm::MCI
     // TODO(gchatelet): Handle isLookupPtrRegClass.
     if (OpInfo.RegClass >= 0)
       Operand.Tracker = &RATC.getRegisterClass(OpInfo.RegClass);
+    Operand.TiedToIndex =
+        MCInstrDesc.getOperandConstraint(OpIndex, llvm::MCOI::TIED_TO);
     Operand.Info = &OpInfo;
     Operands.push_back(Operand);
   }
@@ -59,24 +55,23 @@ Instruction::Instruction(const llvm::MCI
     Operand.ImplicitReg = MCPhysReg;
     Operands.push_back(Operand);
   }
-  // Set TiedTo for operands.
-  for (auto &Op : Operands) {
-    if (Op.IsExplicit) {
-      const int TiedTo =
-          MCInstrDesc.getOperandConstraint(Op.Index, llvm::MCOI::TIED_TO);
-      if (TiedTo >= 0) {
-        Op.TiedTo = &Operands[TiedTo];
-        tie(&Op, Operands[TiedTo].Var);
-      } else {
-        tie(&Op, Op.Var);
-      }
-    }
-  }
-  for (auto &Op : Operands) {
-    if (Op.Var) {
-      Variables.push_back(&*Op.Var);
-    }
-  }
+  // Assigning Variables to non tied explicit operands.
+  Variables.reserve(Operands.size()); // Variables.size() <= Operands.size()
+  for (auto &Op : Operands)
+    if (Op.IsExplicit && Op.TiedToIndex < 0) {
+      const size_t VariableIndex = Variables.size();
+      Op.VariableIndex = VariableIndex;
+      Variables.emplace_back();
+      Variables.back().Index = VariableIndex;
+    }
+  // Assigning Variables to tied operands.
+  for (auto &Op : Operands)
+    if (Op.TiedToIndex >= 0)
+      Op.VariableIndex = Operands[Op.TiedToIndex].VariableIndex;
+  // Assigning Operands to Variables.
+  for (auto &Op : Operands)
+    if (Op.VariableIndex >= 0)
+      Variables[Op.VariableIndex].TiedOperands.push_back(&Op);
   // Processing Aliasing.
   DefRegisters = RATC.emptyRegisters();
   UseRegisters = RATC.emptyRegisters();
@@ -88,6 +83,35 @@ Instruction::Instruction(const llvm::MCI
   }
 }
 
+InstructionInstance::InstructionInstance(const Instruction &Instr)
+    : Instr(Instr), VariableValues(Instr.Variables.size()) {}
+
+llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) {
+  return VariableValues[Var.Index];
+}
+
+llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) {
+  assert(Op.VariableIndex >= 0);
+  return getValueFor(Instr.Variables[Op.VariableIndex]);
+}
+
+// forward declaration.
+static void randomize(const Variable &Var, llvm::MCOperand &AssignedValue);
+
+llvm::MCInst InstructionInstance::randomizeUnsetVariablesAndBuild() {
+  for (const Variable &Var : Instr.Variables) {
+    llvm::MCOperand &AssignedValue = getValueFor(Var);
+    if (!AssignedValue.isValid())
+      randomize(Var, AssignedValue);
+  }
+  llvm::MCInst Result;
+  Result.setOpcode(Instr.Description.Opcode);
+  for (const auto &Op : Instr.Operands)
+    if (Op.IsExplicit)
+      Result.addOperand(getValueFor(Op));
+  return Result;
+}
+
 bool RegisterOperandAssignment::
 operator==(const RegisterOperandAssignment &Other) const {
   return std::tie(Op, Reg) == std::tie(Other.Op, Other.Reg);
@@ -159,7 +183,7 @@ static auto randomElement(const C &Conta
   return Container[randomIndex(Container.size())];
 }
 
-static void randomize(Variable &Var) {
+static void randomize(const Variable &Var, llvm::MCOperand &AssignedValue) {
   assert(!Var.TiedOperands.empty());
   assert(Var.TiedOperands.front() != nullptr);
   const Operand &Op = *Var.TiedOperands.front();
@@ -168,12 +192,12 @@ static void randomize(Variable &Var) {
   switch (OpInfo.OperandType) {
   case llvm::MCOI::OperandType::OPERAND_IMMEDIATE:
     // FIXME: explore immediate values too.
-    Var.AssignedValue = llvm::MCOperand::createImm(1);
+    AssignedValue = llvm::MCOperand::createImm(1);
     break;
   case llvm::MCOI::OperandType::OPERAND_REGISTER: {
     assert(Op.Tracker);
     const auto &Registers = Op.Tracker->sourceBits();
-    Var.AssignedValue = llvm::MCOperand::createReg(randomBit(Registers));
+    AssignedValue = llvm::MCOperand::createReg(randomBit(Registers));
     break;
   }
   default:
@@ -181,15 +205,16 @@ static void randomize(Variable &Var) {
   }
 }
 
-static void setRegisterOperandValue(const RegisterOperandAssignment &ROV) {
-  const Operand *Op = ROV.Op->TiedTo ? ROV.Op->TiedTo : ROV.Op;
-  assert(Op->Var);
-  auto &AssignedValue = Op->Var->AssignedValue;
+static void setRegisterOperandValue(const RegisterOperandAssignment &ROV,
+                                    InstructionInstance &II) {
+  assert(ROV.Op);
+  assert(ROV.Op->IsExplicit);
+  auto &AssignedValue = II.getValueFor(*ROV.Op);
   if (AssignedValue.isValid()) {
     assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg);
     return;
   }
-  Op->Var->AssignedValue = llvm::MCOperand::createReg(ROV.Reg);
+  AssignedValue = llvm::MCOperand::createReg(ROV.Reg);
 }
 
 size_t randomBit(const llvm::BitVector &Vector) {
@@ -200,41 +225,13 @@ size_t randomBit(const llvm::BitVector &
   return *Itr;
 }
 
-void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations) {
+void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
+                       InstructionInstance &DefII, InstructionInstance &UseII) {
   assert(!AliasingConfigurations.empty());
   assert(!AliasingConfigurations.hasImplicitAliasing());
   const auto &RandomConf = randomElement(AliasingConfigurations.Configurations);
-  setRegisterOperandValue(randomElement(RandomConf.Defs));
-  setRegisterOperandValue(randomElement(RandomConf.Uses));
-}
-
-void randomizeUnsetVariable(const Instruction &Instruction) {
-  for (auto *Var : Instruction.Variables)
-    if (!Var->AssignedValue.isValid())
-      randomize(*Var);
-}
-
-void clearVariableAssignments(const Instruction &Instruction) {
-  for (auto *Var : Instruction.Variables)
-    Var->AssignedValue = llvm::MCOperand();
-}
-
-llvm::MCInst build(const Instruction &Instruction) {
-  llvm::MCInst Result;
-  Result.setOpcode(Instruction.Description.Opcode);
-  for (const auto &Op : Instruction.Operands) {
-    if (Op.IsExplicit) {
-      auto &Var = Op.TiedTo ? Op.TiedTo->Var : Op.Var;
-      assert(Var);
-      Result.addOperand(Var->AssignedValue);
-    }
-  }
-  return Result;
-}
-
-llvm::MCInst randomizeUnsetVariablesAndBuild(const Instruction &Instruction) {
-  randomizeUnsetVariable(Instruction);
-  return build(Instruction);
+  setRegisterOperandValue(randomElement(RandomConf.Defs), DefII);
+  setRegisterOperandValue(randomElement(RandomConf.Uses), UseII);
 }
 
 void DumpMCOperand(const llvm::MCRegisterInfo &MCRegisterInfo,

Modified: llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h Wed Jun 13 06:24:41 2018
@@ -32,11 +32,14 @@ namespace exegesis {
 
 struct Operand; // forward declaration.
 
-// A variable represents the value of an Operand or a set of Operands if they ar
-// tied together.
+// A variable represents the value associated to an Operand or a set of Operands
+// if they are tied together.
 struct Variable {
   llvm::SmallVector<const Operand *, 2> TiedOperands;
   llvm::MCOperand AssignedValue;
+  // The index of this Variable in Instruction.Variables and its associated
+  // Value in InstructionInstance.VariableValues.
+  unsigned Index = -1;
 };
 
 // MCOperandInfo can only represents Explicit operands. This object gives a
@@ -46,35 +49,50 @@ struct Variable {
 // - Tracker: is set for Register Operands and is used to keep track of possible
 // registers and the registers reachable from them (aliasing registers).
 // - Info: a shortcut for MCInstrDesc::operands()[Index].
-// - TiedTo: a pointer to the Operand holding the value or nullptr.
+// - TiedToIndex: the index of the Operand holding the value or -1.
 // - ImplicitReg: a pointer to the register value when Operand is Implicit,
 // nullptr otherwise.
-// - Variable: The value associated with this Operand. It is only set for
-// explicit operands that are not TiedTo.
+// - VariableIndex: the index of the Variable holding the value for this Operand
+// or -1 if this operand is implicit.
 struct Operand {
-  uint8_t Index = 0;
+  unsigned Index = 0;
   bool IsDef = false;
   bool IsExplicit = false;
   const RegisterAliasingTracker *Tracker = nullptr; // Set for Register Op.
   const llvm::MCOperandInfo *Info = nullptr;        // Set for Explicit Op.
-  const Operand *TiedTo = nullptr;                  // Set for Reg/Explicit Op.
+  int TiedToIndex = -1;                             // Set for Reg/Explicit Op.
   const llvm::MCPhysReg *ImplicitReg = nullptr;     // Set for Implicit Op.
-  mutable llvm::Optional<Variable> Var;             // Set for Explicit Op.
+  int VariableIndex = -1;                           // Set for Reg/Explicit Op.
 };
 
 // A view over an MCInstrDesc offering a convenient interface to compute
-// Register aliasing and assign values to Operands.
+// Register aliasing.
 struct Instruction {
   Instruction(const llvm::MCInstrDesc &MCInstrDesc,
-              RegisterAliasingTrackerCache &ATC);
+              const RegisterAliasingTrackerCache &ATC);
 
   const llvm::MCInstrDesc &Description;
   llvm::SmallVector<Operand, 8> Operands;
-  llvm::SmallVector<Variable *, 8> Variables;
+  llvm::SmallVector<Variable, 4> Variables;
   llvm::BitVector DefRegisters; // The union of the aliased def registers.
   llvm::BitVector UseRegisters; // The union of the aliased use registers.
 };
 
+// An instance of an Instruction holding values for each of its Variables.
+struct InstructionInstance {
+  InstructionInstance(const Instruction &Instr);
+
+  llvm::MCOperand &getValueFor(const Variable &Var);
+  llvm::MCOperand &getValueFor(const Operand &Op);
+
+  // Assigns a Random Value to all Variables that are still Invalid and returns
+  // the instance as an llvm::MCInst.
+  llvm::MCInst randomizeUnsetVariablesAndBuild();
+
+  const Instruction &Instr;
+  llvm::SmallVector<llvm::MCOperand, 4> VariableValues;
+};
+
 // Represents the assignment of a Register to an Operand.
 struct RegisterOperandAssignment {
   RegisterOperandAssignment(const Operand *Operand, llvm::MCPhysReg Reg)
@@ -126,17 +144,10 @@ std::mt19937 &randomGenerator();
 // Precondition: Vector must have at least one bit set.
 size_t randomBit(const llvm::BitVector &Vector);
 
-// Picks a random configuration, then select a random def and a random use from
-// it and set the target Variables to the selected values.
-// FIXME: This function mutates some nested variables in a const object, please
-// fix ASAP.
-void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations);
-
-// Set all Instruction's Variables AssignedValue to Invalid.
-void clearVariableAssignments(const Instruction &Instruction);
-
-// Assigns a Random Value to all Instruction's Variables that are still Invalid.
-llvm::MCInst randomizeUnsetVariablesAndBuild(const Instruction &Instruction);
+// Picks a random configuration, then selects a random def and a random use from
+// it and finally set the selected values in the provided InstructionInstances.
+void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
+                       InstructionInstance &DefII, InstructionInstance &UseII);
 
 // Writes MCInst to OS.
 // This is not assembly but the internal LLVM's name for instructions and

Modified: llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.cpp?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.cpp (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.cpp Wed Jun 13 06:24:41 2018
@@ -64,7 +64,7 @@ RegisterAliasingTrackerCache::RegisterAl
       EmptyRegisters(RegInfo.getNumRegs()) {}
 
 const RegisterAliasingTracker &
-RegisterAliasingTrackerCache::getRegister(llvm::MCPhysReg PhysReg) {
+RegisterAliasingTrackerCache::getRegister(llvm::MCPhysReg PhysReg) const {
   auto &Found = Registers[PhysReg];
   if (!Found)
     Found.reset(new RegisterAliasingTracker(RegInfo, PhysReg));
@@ -72,7 +72,7 @@ RegisterAliasingTrackerCache::getRegiste
 }
 
 const RegisterAliasingTracker &
-RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) {
+RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) const {
   auto &Found = RegisterClasses[RegClassIndex];
   const auto &RegClass = RegInfo.getRegClass(RegClassIndex);
   if (!Found)

Modified: llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.h?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.h (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/RegisterAliasing.h Wed Jun 13 06:24:41 2018
@@ -87,18 +87,18 @@ struct RegisterAliasingTrackerCache {
   const llvm::MCRegisterInfo &regInfo() const { return RegInfo; }
 
   // Retrieves the RegisterAliasingTracker for this particular register.
-  const RegisterAliasingTracker &getRegister(llvm::MCPhysReg Reg);
+  const RegisterAliasingTracker &getRegister(llvm::MCPhysReg Reg) const;
 
   // Retrieves the RegisterAliasingTracker for this particular register class.
-  const RegisterAliasingTracker &getRegisterClass(unsigned RegClassIndex);
+  const RegisterAliasingTracker &getRegisterClass(unsigned RegClassIndex) const;
 
 private:
   const llvm::MCRegisterInfo &RegInfo;
   const llvm::BitVector ReservedReg;
   const llvm::BitVector EmptyRegisters;
-  std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
+  mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
       Registers;
-  std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
+  mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
       RegisterClasses;
 };
 

Modified: llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp Wed Jun 13 06:24:41 2018
@@ -89,28 +89,24 @@ static bool hasMemoryOperand(const llvm:
   return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
 }
 
-static bool isInfeasible(const Instruction &Instruction, std::string &Error) {
-  const auto &MCInstrDesc = Instruction.Description;
-  if (MCInstrDesc.isPseudo()) {
-    Error = "is pseudo";
-    return true;
-  }
-  if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand)) {
-    Error = "has unknown operands";
-    return true;
-  }
-  if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand)) {
-    Error = "has memory operands";
-    return true;
-  }
-  return false;
+llvm::Error
+UopsBenchmarkRunner::isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const {
+  if (MCInstrDesc.isPseudo())
+    return llvm::make_error<BenchmarkFailure>("Infeasible : is pseudo");
+  if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand))
+    return llvm::make_error<BenchmarkFailure>(
+        "Infeasible : has unknown operands");
+  if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand))
+    return llvm::make_error<BenchmarkFailure>(
+        "Infeasible : has memory operands");
+  return llvm::Error::success();
 }
 
 // Returns whether this Variable ties Use and Def operands together.
-static bool hasTiedOperands(const Variable *Var) {
+static bool hasTiedOperands(const Variable &Var) {
   bool HasUse = false;
   bool HasDef = false;
-  for (const Operand *Op : Var->TiedOperands) {
+  for (const Operand *Op : Var.TiedOperands) {
     if (Op->IsDef)
       HasDef = true;
     else
@@ -119,12 +115,12 @@ static bool hasTiedOperands(const Variab
   return HasUse && HasDef;
 }
 
-static llvm::SmallVector<Variable *, 8>
-getTiedVariables(const Instruction &Instruction) {
-  llvm::SmallVector<Variable *, 8> Result;
-  for (auto *Var : Instruction.Variables)
+static llvm::SmallVector<const Variable *, 8>
+getTiedVariables(const Instruction &Instr) {
+  llvm::SmallVector<const Variable *, 8> Result;
+  for (const auto &Var : Instr.Variables)
     if (hasTiedOperands(Var))
-      Result.push_back(Var);
+      Result.push_back(&Var);
   return Result;
 }
 
@@ -140,79 +136,85 @@ InstructionBenchmark::ModeE UopsBenchmar
   return InstructionBenchmark::Uops;
 }
 
-llvm::Expected<std::vector<BenchmarkConfiguration>>
-UopsBenchmarkRunner::createConfigurations(RegisterAliasingTrackerCache &RATC,
-                                          unsigned Opcode) const {
-  const llvm::MCInstrDesc &MCInstrDesc = MCInstrInfo.get(Opcode);
-  const Instruction Instruction(MCInstrDesc, RATC);
-
-  std::string Error;
-  if (isInfeasible(Instruction, Error))
-    return llvm::make_error<llvm::StringError>(
-        llvm::Twine("Infeasible : ").concat(Error),
-        llvm::inconvertibleErrorCode());
-
-  BenchmarkConfiguration Conf;
-  const AliasingConfigurations SelfAliasing(Instruction, Instruction);
+llvm::Expected<BenchmarkConfiguration>
+UopsBenchmarkRunner::generateConfiguration(unsigned Opcode) const {
+  const auto &InstrDesc = MCInstrInfo.get(Opcode);
+  if (auto E = isInfeasible(InstrDesc))
+    return std::move(E);
+  const Instruction Instr(InstrDesc, RATC);
+  const AliasingConfigurations SelfAliasing(Instr, Instr);
   if (SelfAliasing.empty()) {
+    InstructionInstance II(Instr);
+    BenchmarkConfiguration Conf;
     Conf.Info = "instruction is parallel, repeating a random one.";
-    Conf.Snippet = {randomizeUnsetVariablesAndBuild(Instruction)};
-    return std::vector<BenchmarkConfiguration>{Conf};
+    Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
+    return Conf;
   }
   if (SelfAliasing.hasImplicitAliasing()) {
+    InstructionInstance II(Instr);
+    BenchmarkConfiguration Conf;
     Conf.Info = "instruction is serial, repeating a random one.";
-    Conf.Snippet = {randomizeUnsetVariablesAndBuild(Instruction)};
-    return std::vector<BenchmarkConfiguration>{Conf};
+    Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
+    return Conf;
   }
-  const auto TiedVariables = getTiedVariables(Instruction);
+  const auto TiedVariables = getTiedVariables(Instr);
   if (!TiedVariables.empty()) {
     if (TiedVariables.size() > 1)
       return llvm::make_error<llvm::StringError>(
           "Infeasible : don't know how to handle several tied variables",
           llvm::inconvertibleErrorCode());
+    BenchmarkConfiguration Conf;
     Conf.Info = "instruction has tied variables using static renaming.";
-    Variable *Var = TiedVariables.front();
+    const Variable *Var = TiedVariables.front();
     assert(Var);
     assert(!Var->TiedOperands.empty());
     const Operand &Operand = *Var->TiedOperands.front();
     assert(Operand.Tracker);
     for (const llvm::MCPhysReg Reg : Operand.Tracker->sourceBits().set_bits()) {
-      clearVariableAssignments(Instruction);
-      Var->AssignedValue = llvm::MCOperand::createReg(Reg);
-      Conf.Snippet.push_back(randomizeUnsetVariablesAndBuild(Instruction));
+      InstructionInstance II(Instr);
+      II.getValueFor(*Var) = llvm::MCOperand::createReg(Reg);
+      Conf.Snippet.push_back(II.randomizeUnsetVariablesAndBuild());
     }
-    return std::vector<BenchmarkConfiguration>{Conf};
+    return Conf;
   }
+  InstructionInstance II(Instr);
   // No tied variables, we pick random values for defs.
   llvm::BitVector Defs(MCRegisterInfo.getNumRegs());
-  for (const auto &Op : Instruction.Operands) {
+  for (const auto &Op : Instr.Operands) {
     if (Op.Tracker && Op.IsExplicit && Op.IsDef) {
-      assert(Op.Var);
       auto PossibleRegisters = Op.Tracker->sourceBits();
       remove(PossibleRegisters, RATC.reservedRegisters());
       assert(PossibleRegisters.any() && "No register left to choose from");
       const auto RandomReg = randomBit(PossibleRegisters);
       Defs.set(RandomReg);
-      Op.Var->AssignedValue = llvm::MCOperand::createReg(RandomReg);
+      II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
     }
   }
   // And pick random use values that are not reserved and don't alias with defs.
   const auto DefAliases = getAliasedBits(MCRegisterInfo, Defs);
-  for (const auto &Op : Instruction.Operands) {
+  for (const auto &Op : Instr.Operands) {
     if (Op.Tracker && Op.IsExplicit && !Op.IsDef) {
-      assert(Op.Var);
       auto PossibleRegisters = Op.Tracker->sourceBits();
       remove(PossibleRegisters, RATC.reservedRegisters());
       remove(PossibleRegisters, DefAliases);
       assert(PossibleRegisters.any() && "No register left to choose from");
       const auto RandomReg = randomBit(PossibleRegisters);
-      Op.Var->AssignedValue = llvm::MCOperand::createReg(RandomReg);
+      II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
     }
   }
+  BenchmarkConfiguration Conf;
   Conf.Info =
       "instruction has no tied variables picking Uses different from defs";
-  Conf.Snippet = {randomizeUnsetVariablesAndBuild(Instruction)};
-  return std::vector<BenchmarkConfiguration>{Conf};
+  Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
+  return Conf;
+}
+
+llvm::Expected<std::vector<BenchmarkConfiguration>>
+UopsBenchmarkRunner::createConfigurations(unsigned Opcode) const {
+  if (auto E = generateConfiguration(Opcode))
+    return std::vector<BenchmarkConfiguration>{E.get()};
+  else
+    return E.takeError();
 }
 
 std::vector<BenchmarkMeasure>
@@ -232,7 +234,7 @@ UopsBenchmarkRunner::runMeasurements(con
     int64_t CounterValue = 0;
     llvm::SmallVector<llvm::StringRef, 2> CounterNames;
     llvm::StringRef(PfmCounters).split(CounterNames, ',');
-    for (const auto& CounterName : CounterNames) {
+    for (const auto &CounterName : CounterNames) {
       pfm::PerfEvent UopPerfEvent(CounterName);
       if (!UopPerfEvent.valid())
         llvm::report_fatal_error(

Modified: llvm/trunk/tools/llvm-exegesis/lib/Uops.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-exegesis/lib/Uops.h?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-exegesis/lib/Uops.h (original)
+++ llvm/trunk/tools/llvm-exegesis/lib/Uops.h Wed Jun 13 06:24:41 2018
@@ -24,12 +24,16 @@ public:
   using BenchmarkRunner::BenchmarkRunner;
   ~UopsBenchmarkRunner() override;
 
+  llvm::Expected<BenchmarkConfiguration>
+  generateConfiguration(unsigned Opcode) const;
+
 private:
+  llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const;
+
   InstructionBenchmark::ModeE getMode() const override;
 
   llvm::Expected<std::vector<BenchmarkConfiguration>>
-  createConfigurations(RegisterAliasingTrackerCache &RATC,
-                       unsigned Opcode) const override;
+  createConfigurations(unsigned Opcode) const override;
 
   std::vector<BenchmarkMeasure>
   runMeasurements(const ExecutableFunction &EF,

Modified: llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt?rev=334596&r1=334595&r2=334596&view=diff
==============================================================================
--- llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt (original)
+++ llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt Wed Jun 13 06:24:41 2018
@@ -14,8 +14,9 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_llvm_unittest(LLVMExegesisX86Tests
-  RegisterAliasingTest.cpp
   AssemblerTest.cpp
   AnalysisTest.cpp
+  SnippetGeneratorTest.cpp
+  RegisterAliasingTest.cpp
   )
 target_link_libraries(LLVMExegesisX86Tests PRIVATE LLVMExegesis)

Added: llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp?rev=334596&view=auto
==============================================================================
--- llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp (added)
+++ llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp Wed Jun 13 06:24:41 2018
@@ -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




More information about the llvm-commits mailing list