reland r332579: [llvm-exegesis] Update to cover latency through another opcode.

Restructuring the code to measure latency and uops.
The end goal is to have this program spawn another process to deal with SIGILL and other malformed programs. It is not yet the case in this redesign, it is still the main program that runs the code (and may crash).
It now uses BitVector instead of Graph for performance reasons.

https://reviews.llvm.org/D46821

(with fixed ARM tests)

Authored by Guillaume Chatelet

llvm-svn: 332592
diff --git a/llvm/tools/llvm-exegesis/lib/Latency.cpp b/llvm/tools/llvm-exegesis/lib/Latency.cpp
index 4a2632c..4233345 100644
--- a/llvm/tools/llvm-exegesis/lib/Latency.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Latency.cpp
@@ -8,25 +8,41 @@
 //===----------------------------------------------------------------------===//
 
 #include "Latency.h"
-#include "BenchmarkResult.h"
-#include "InstructionSnippetGenerator.h"
+
+#include "Assembler.h"
+#include "BenchmarkRunner.h"
+#include "MCInstrDescView.h"
 #include "PerfHelper.h"
-#include "llvm/MC/MCInstrDesc.h"
-#include "llvm/Support/Error.h"
-#include <algorithm>
-#include <random>
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstBuilder.h"
 
 namespace exegesis {
 
+static bool HasUnknownOperand(const llvm::MCOperandInfo &OpInfo) {
+  return OpInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN;
+}
+
 // FIXME: Handle memory, see PR36905.
-static bool isInvalidOperand(const llvm::MCOperandInfo &OpInfo) {
-  switch (OpInfo.OperandType) {
-  default:
+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;
-  case llvm::MCOI::OPERAND_IMMEDIATE:
-  case llvm::MCOI::OPERAND_REGISTER:
-    return false;
   }
+  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;
 }
 
 static llvm::Error makeError(llvm::Twine Msg) {
@@ -38,39 +54,61 @@
 
 const char *LatencyBenchmarkRunner::getDisplayName() const { return "latency"; }
 
-llvm::Expected<std::vector<llvm::MCInst>> LatencyBenchmarkRunner::createCode(
-    const LLVMState &State, const unsigned OpcodeIndex,
-    const unsigned NumRepetitions, const JitFunctionContext &Context) const {
-  std::default_random_engine RandomEngine;
-  const auto GetRandomIndex = [&RandomEngine](size_t Size) {
-    assert(Size > 0 && "trying to get select a random element of an empty set");
-    return std::uniform_int_distribution<>(0, Size - 1)(RandomEngine);
-  };
+llvm::Expected<std::vector<llvm::MCInst>>
+LatencyBenchmarkRunner::createSnippet(RegisterAliasingTrackerCache &RATC,
+                                      unsigned Opcode,
+                                      llvm::raw_ostream &Info) const {
+  std::vector<llvm::MCInst> Snippet;
+  const llvm::MCInstrDesc &MCInstrDesc = MCInstrInfo.get(Opcode);
+  const Instruction ThisInstruction(MCInstrDesc, RATC);
 
-  const auto &InstrInfo = State.getInstrInfo();
-  const auto &RegInfo = State.getRegInfo();
-  const llvm::MCInstrDesc &InstrDesc = InstrInfo.get(OpcodeIndex);
-  for (const llvm::MCOperandInfo &OpInfo : InstrDesc.operands()) {
-    if (isInvalidOperand(OpInfo))
-      return makeError("Only registers and immediates are supported");
+  std::string Error;
+  if (IsInfeasible(ThisInstruction, Error))
+    return makeError(llvm::Twine("Infeasible : ").concat(Error));
+
+  const AliasingConfigurations SelfAliasing(ThisInstruction, ThisInstruction);
+  if (!SelfAliasing.empty()) {
+    if (!SelfAliasing.hasImplicitAliasing()) {
+      Info << "explicit self cycles, selecting one aliasing configuration.\n";
+      setRandomAliasing(SelfAliasing);
+    } else {
+      Info << "implicit Self cycles, picking random values.\n";
+    }
+    Snippet.push_back(randomizeUnsetVariablesAndBuild(ThisInstruction));
+    return Snippet;
   }
 
-  const auto Vars = getVariables(RegInfo, InstrDesc, Context.getReservedRegs());
-  const std::vector<AssignmentChain> AssignmentChains =
-      computeSequentialAssignmentChains(RegInfo, Vars);
-  if (AssignmentChains.empty())
-    return makeError("Unable to find a dependency chain.");
-  const std::vector<llvm::MCPhysReg> Regs =
-      getRandomAssignment(Vars, AssignmentChains, GetRandomIndex);
-  const llvm::MCInst Inst = generateMCInst(InstrDesc, Vars, Regs);
-  if (!State.canAssemble(Inst))
-    return makeError("MCInst does not assemble.");
-  return std::vector<llvm::MCInst>(NumRepetitions, Inst);
+  // Let's try to create a dependency through another opcode.
+  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)
+      continue;
+    const Instruction OtherInstruction(MCInstrInfo.get(OtherOpcode), RATC);
+    if (IsInfeasible(OtherInstruction, Error))
+      continue;
+    const AliasingConfigurations Forward(ThisInstruction, OtherInstruction);
+    const AliasingConfigurations Back(OtherInstruction, ThisInstruction);
+    if (Forward.empty() || Back.empty())
+      continue;
+    setRandomAliasing(Forward);
+    setRandomAliasing(Back);
+    Info << "creating cycle through " << MCInstrInfo.getName(OtherOpcode)
+         << ".\n";
+    Snippet.push_back(randomizeUnsetVariablesAndBuild(ThisInstruction));
+    Snippet.push_back(randomizeUnsetVariablesAndBuild(OtherInstruction));
+    return Snippet;
+  }
+
+  return makeError(
+      "Infeasible : Didn't find any scheme to make the instruction serial\n");
 }
 
 std::vector<BenchmarkMeasure>
-LatencyBenchmarkRunner::runMeasurements(const LLVMState &State,
-                                        const JitFunction &Function,
+LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
                                         const unsigned NumRepetitions) const {
   // Cycle measurements include some overhead from the kernel. Repeat the
   // measure several times and take the minimum value.