[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/tools/llvm-exegesis/lib/Uops.cpp b/llvm/tools/llvm-exegesis/lib/Uops.cpp
index ae92881..4c6edaa 100644
--- a/llvm/tools/llvm-exegesis/lib/Uops.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Uops.cpp
@@ -89,28 +89,24 @@
   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 @@
   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 @@
   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 @@
     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(