[MCA] Introducing incremental SourceMgr and resumable pipeline
authorMin-Yih Hsu <minyihh@uci.edu>
Mon, 30 May 2022 23:50:09 +0000 (16:50 -0700)
committerMin-Yih Hsu <minyihh@uci.edu>
Fri, 24 Jun 2022 22:39:51 +0000 (15:39 -0700)
The new resumable mca::Pipeline capability introduced in this patch
allows users to save the current state of pipeline and resume from the
very checkpoint.
It is better (but not require) to use with the new IncrementalSourceMgr,
where users can add mca::Instruction incrementally rather than having a
fixed number of instructions ahead-of-time.

Note that we're using unit tests to test these new features. Because
integrating them into the `llvm-mca` tool will make too many churns.

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

18 files changed:
llvm/include/llvm/MCA/IncrementalSourceMgr.h [new file with mode: 0644]
llvm/include/llvm/MCA/Pipeline.h
llvm/include/llvm/MCA/SourceMgr.h
llvm/include/llvm/MCA/Stages/EntryStage.h
llvm/include/llvm/MCA/Stages/Stage.h
llvm/lib/MCA/Pipeline.cpp
llvm/lib/MCA/Stages/EntryStage.cpp
llvm/lib/MCA/Stages/Stage.cpp
llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp
llvm/tools/llvm-mca/llvm-mca.cpp
llvm/unittests/tools/CMakeLists.txt
llvm/unittests/tools/llvm-mca/CMakeLists.txt [new file with mode: 0644]
llvm/unittests/tools/llvm-mca/MCATestBase.cpp [new file with mode: 0644]
llvm/unittests/tools/llvm-mca/MCATestBase.h [new file with mode: 0644]
llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt [new file with mode: 0644]
llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp [new file with mode: 0644]
llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp [new file with mode: 0644]
llvm/unittests/tools/llvm-mca/X86/X86TestBase.h [new file with mode: 0644]

diff --git a/llvm/include/llvm/MCA/IncrementalSourceMgr.h b/llvm/include/llvm/MCA/IncrementalSourceMgr.h
new file mode 100644 (file)
index 0000000..a84b87c
--- /dev/null
@@ -0,0 +1,72 @@
+//===---------------- IncrementalSourceMgr.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file contains IncrementalSourceMgr, an implementation of SourceMgr
+/// that allows users to add new instructions incrementally / dynamically.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_MCA_INCREMENTALSOURCEMGR_H
+#define LLVM_MCA_INCREMENTALSOURCEMGR_H
+
+#include "llvm/MCA/SourceMgr.h"
+#include <deque>
+
+namespace llvm {
+namespace mca {
+
+/// An implementation of \a SourceMgr that allows users to add new instructions
+/// incrementally / dynamically.
+/// Note that this SourceMgr takes ownership of all \a mca::Instruction.
+class IncrementalSourceMgr : public SourceMgr {
+  /// Owner of all mca::Instruction instances. Note that we use std::deque here
+  /// to have a better throughput, in comparison to std::vector or
+  /// llvm::SmallVector, as they usually pay a higher re-allocation cost when
+  /// there is a large number of instructions.
+  std::deque<UniqueInst> InstStorage;
+
+  /// Current instruction index.
+  unsigned TotalCounter;
+
+  /// End-of-stream flag.
+  bool EOS;
+
+public:
+  IncrementalSourceMgr() : TotalCounter(0U), EOS(false) {}
+
+  void clear() {
+    InstStorage.clear();
+    TotalCounter = 0U;
+    EOS = false;
+  }
+
+  ArrayRef<UniqueInst> getInstructions() const override {
+    llvm_unreachable("Not applicable");
+  }
+
+  bool hasNext() const override { return TotalCounter < InstStorage.size(); }
+  bool isEnd() const override { return EOS; }
+
+  SourceRef peekNext() const override {
+    assert(hasNext());
+    return SourceRef(TotalCounter, *InstStorage[TotalCounter]);
+  }
+
+  /// Add a new instruction.
+  void addInst(UniqueInst &&Inst) { InstStorage.emplace_back(std::move(Inst)); }
+
+  void updateNext() override { ++TotalCounter; }
+
+  /// Mark the end of instruction stream.
+  void endOfStream() { EOS = true; }
+};
+
+} // end namespace mca
+} // end namespace llvm
+
+#endif // LLVM_MCA_INCREMENTALSOURCEMGR_H
index 0ac988c..92c3836 100644 (file)
@@ -51,6 +51,13 @@ class Pipeline {
   Pipeline(const Pipeline &P) = delete;
   Pipeline &operator=(const Pipeline &P) = delete;
 
+  enum class State {
+    Created, // Pipeline was just created. The default state.
+    Started, // Pipeline has started running.
+    Paused   // Pipeline is paused.
+  };
+  State CurrentState;
+
   /// An ordered list of stages that define this instruction pipeline.
   SmallVector<std::unique_ptr<Stage>, 8> Stages;
   std::set<HWEventListener *> Listeners;
@@ -62,13 +69,16 @@ class Pipeline {
   void notifyCycleEnd();
 
 public:
-  Pipeline() : Cycles(0) {}
+  Pipeline() : CurrentState(State::Created), Cycles(0) {}
   void appendStage(std::unique_ptr<Stage> S);
 
   /// Returns the total number of simulated cycles.
   Expected<unsigned> run();
 
   void addEventListener(HWEventListener *Listener);
+
+  /// Returns whether the pipeline is currently paused.
+  bool isPaused() const { return CurrentState == State::Paused; }
 };
 } // namespace mca
 } // namespace llvm
index e844171..16a60d1 100644 (file)
@@ -6,9 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 /// \file
-/// This file implements class SourceMgr. Class SourceMgr abstracts the input
-/// code sequence (a sequence of MCInst), and assings unique identifiers to
-/// every instruction in the sequence.
+/// This file contains abstract class SourceMgr and the default implementation,
+/// CircularSourceMgr.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -25,30 +24,62 @@ namespace mca {
 // prevent compiler error C2139 about intrinsic type trait '__is_assignable'.
 typedef std::pair<unsigned, const Instruction &> SourceRef;
 
-class SourceMgr {
+/// Abstracting the input code sequence (a sequence of MCInst) and assigning
+/// unique identifiers to every instruction in the sequence.
+struct SourceMgr {
   using UniqueInst = std::unique_ptr<Instruction>;
+
+  /// Provides a fixed range of \a UniqueInst to iterate.
+  virtual ArrayRef<UniqueInst> getInstructions() const = 0;
+
+  /// (Fixed) Number of \a UniqueInst. Returns the size of
+  /// \a getInstructions by default.
+  virtual size_t size() const { return getInstructions().size(); }
+
+  /// Whether there is any \a SourceRef to inspect / peek next.
+  /// Note that returning false from this doesn't mean the instruction
+  /// stream has ended.
+  virtual bool hasNext() const = 0;
+
+  /// Whether the instruction stream has eneded.
+  virtual bool isEnd() const = 0;
+
+  /// The next \a SourceRef.
+  virtual SourceRef peekNext() const = 0;
+
+  /// Advance to the next \a SourceRef.
+  virtual void updateNext() = 0;
+
+  virtual ~SourceMgr() {}
+};
+
+/// The default implementation of \a SourceMgr. It always takes a fixed number
+/// of instructions and provides an option to loop the given sequence for a
+/// certain iterations.
+class CircularSourceMgr : public SourceMgr {
   ArrayRef<UniqueInst> Sequence;
   unsigned Current;
   const unsigned Iterations;
   static const unsigned DefaultIterations = 100;
 
 public:
-  SourceMgr(ArrayRef<UniqueInst> S, unsigned Iter)
-      : Sequence(S), Current(0), Iterations(Iter ? Iter : DefaultIterations) {}
+  CircularSourceMgr(ArrayRef<UniqueInst> S, unsigned Iter)
+      : Sequence(S), Current(0U), Iterations(Iter ? Iter : DefaultIterations) {}
+
+  ArrayRef<UniqueInst> getInstructions() const override { return Sequence; }
 
   unsigned getNumIterations() const { return Iterations; }
-  unsigned size() const { return Sequence.size(); }
-  bool hasNext() const { return Current < (Iterations * Sequence.size()); }
-  void updateNext() { ++Current; }
+  bool hasNext() const override {
+    return Current < (Iterations * Sequence.size());
+  }
+  bool isEnd() const override { return !hasNext(); }
 
-  SourceRef peekNext() const {
+  SourceRef peekNext() const override {
     assert(hasNext() && "Already at end of sequence!");
     return SourceRef(Current, *Sequence[Current % Sequence.size()]);
   }
 
-  using const_iterator = ArrayRef<UniqueInst>::const_iterator;
-  const_iterator begin() const { return Sequence.begin(); }
-  const_iterator end() const { return Sequence.end(); }
+  void updateNext() override { ++Current; }
 };
 
 } // namespace mca
index 4c50838..fb1244a 100644 (file)
@@ -30,7 +30,7 @@ class EntryStage final : public Stage {
   unsigned NumRetired;
 
   // Updates the program counter, and sets 'CurrentInstruction'.
-  void getNextInstruction();
+  Error getNextInstruction();
 
   EntryStage(const EntryStage &Other) = delete;
   EntryStage &operator=(const EntryStage &Other) = delete;
@@ -42,6 +42,7 @@ public:
   bool hasWorkToComplete() const override;
   Error execute(InstRef &IR) override;
   Error cycleStart() override;
+  Error cycleResume() override;
   Error cycleEnd() override;
 };
 
index 84868e8..2477b9b 100644 (file)
@@ -48,6 +48,9 @@ public:
   /// phase to prepare for the executions during the cycle.
   virtual Error cycleStart() { return ErrorSuccess(); }
 
+  /// Called after the pipeline is resumed from pausing state.
+  virtual Error cycleResume() { return ErrorSuccess(); }
+
   /// Called once at the end of each cycle.
   virtual Error cycleEnd() { return ErrorSuccess(); }
 
@@ -82,6 +85,16 @@ public:
   }
 };
 
+/// This is actually not an error but a marker to indicate that
+/// the instruction stream is paused.
+struct InstStreamPause : public ErrorInfo<InstStreamPause> {
+  static char ID;
+
+  std::error_code convertToErrorCode() const override {
+    return llvm::inconvertibleErrorCode();
+  }
+  void log(raw_ostream &OS) const override { OS << "Stream is paused"; }
+};
 } // namespace mca
 } // namespace llvm
 #endif // LLVM_MCA_STAGES_STAGE_H
index 22b9d07..c94fe14 100644 (file)
@@ -38,7 +38,8 @@ Expected<unsigned> Pipeline::run() {
   assert(!Stages.empty() && "Unexpected empty pipeline found!");
 
   do {
-    notifyCycleBegin();
+    if (!isPaused())
+      notifyCycleBegin();
     if (Error Err = runCycle())
       return std::move(Err);
     notifyCycleEnd();
@@ -53,15 +54,25 @@ Error Pipeline::runCycle() {
   // Update stages before we start processing new instructions.
   for (auto I = Stages.rbegin(), E = Stages.rend(); I != E && !Err; ++I) {
     const std::unique_ptr<Stage> &S = *I;
-    Err = S->cycleStart();
+    if (isPaused())
+      Err = S->cycleResume();
+    else
+      Err = S->cycleStart();
   }
 
+  CurrentState = State::Started;
+
   // Now fetch and execute new instructions.
   InstRef IR;
   Stage &FirstStage = *Stages[0];
   while (!Err && FirstStage.isAvailable(IR))
     Err = FirstStage.execute(IR);
 
+  if (Err.isA<InstStreamPause>()) {
+    CurrentState = State::Paused;
+    return Err;
+  }
+
   // Update stages in preparation for a new cycle.
   for (const std::unique_ptr<Stage> &S : Stages) {
     Err = S->cycleEnd();
index 6613579..6b3fbb8 100644 (file)
@@ -19,7 +19,7 @@ namespace llvm {
 namespace mca {
 
 bool EntryStage::hasWorkToComplete() const {
-  return static_cast<bool>(CurrentInstruction);
+  return static_cast<bool>(CurrentInstruction) || !SM.isEnd();
 }
 
 bool EntryStage::isAvailable(const InstRef & /* unused */) const {
@@ -28,15 +28,20 @@ bool EntryStage::isAvailable(const InstRef & /* unused */) const {
   return false;
 }
 
-void EntryStage::getNextInstruction() {
+Error EntryStage::getNextInstruction() {
   assert(!CurrentInstruction && "There is already an instruction to process!");
-  if (!SM.hasNext())
-    return;
+  if (!SM.hasNext()) {
+    if (!SM.isEnd())
+      return llvm::make_error<InstStreamPause>();
+    else
+      return llvm::ErrorSuccess();
+  }
   SourceRef SR = SM.peekNext();
   std::unique_ptr<Instruction> Inst = std::make_unique<Instruction>(SR.second);
   CurrentInstruction = InstRef(SR.first, Inst.get());
   Instructions.emplace_back(std::move(Inst));
   SM.updateNext();
+  return llvm::ErrorSuccess();
 }
 
 llvm::Error EntryStage::execute(InstRef & /*unused */) {
@@ -46,16 +51,20 @@ llvm::Error EntryStage::execute(InstRef & /*unused */) {
 
   // Move the program counter.
   CurrentInstruction.invalidate();
-  getNextInstruction();
-  return llvm::ErrorSuccess();
+  return getNextInstruction();
 }
 
 llvm::Error EntryStage::cycleStart() {
   if (!CurrentInstruction)
-    getNextInstruction();
+    return getNextInstruction();
   return llvm::ErrorSuccess();
 }
 
+llvm::Error EntryStage::cycleResume() {
+  assert(!CurrentInstruction);
+  return getNextInstruction();
+}
+
 llvm::Error EntryStage::cycleEnd() {
   // Find the first instruction which hasn't been retired.
   auto Range = make_range(&Instructions[NumRetired], Instructions.end());
index ed512ac..5613d4d 100644 (file)
@@ -24,5 +24,6 @@ void Stage::addListener(HWEventListener *Listener) {
   Listeners.insert(Listener);
 }
 
+char InstStreamPause::ID = 0;
 } // namespace mca
 } // namespace llvm
index 912bcc7..24c9cc2 100644 (file)
@@ -239,9 +239,9 @@ void AMDGPUCustomBehaviour::generateWaitCntInfo() {
   AMDGPU::IsaVersion IV = AMDGPU::getIsaVersion(STI.getCPU());
   InstrWaitCntInfo.resize(SrcMgr.size());
 
-  int Index = 0;
-  for (auto I = SrcMgr.begin(), E = SrcMgr.end(); I != E; ++I, ++Index) {
-    const std::unique_ptr<Instruction> &Inst = *I;
+  for (const auto &EN : llvm::enumerate(SrcMgr.getInstructions())) {
+    const std::unique_ptr<Instruction> &Inst = EN.value();
+    unsigned Index = EN.index();
     unsigned Opcode = Inst->getOpcode();
     const MCInstrDesc &MCID = MCII.get(Opcode);
     if ((MCID.TSFlags & SIInstrFlags::DS) &&
index cfd1148..409de28 100644 (file)
@@ -542,7 +542,8 @@ int main(int argc, char **argv) {
       LoweredSequence.emplace_back(std::move(Inst.get()));
     }
 
-    mca::SourceMgr S(LoweredSequence, PrintInstructionTables ? 1 : Iterations);
+    mca::CircularSourceMgr S(LoweredSequence,
+                             PrintInstructionTables ? 1 : Iterations);
 
     if (PrintInstructionTables) {
       //  Create a pipeline, stages, and a printer.
index 7861da8..7ef64f1 100644 (file)
@@ -8,3 +8,4 @@ add_subdirectory(
   llvm-exegesis
 )
 add_subdirectory(llvm-profgen)
+add_subdirectory(llvm-mca)
diff --git a/llvm/unittests/tools/llvm-mca/CMakeLists.txt b/llvm/unittests/tools/llvm-mca/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0084840
--- /dev/null
@@ -0,0 +1,52 @@
+set(LLVM_LINK_COMPONENTS
+  MC
+  MCA
+  Object
+  Support
+  )
+
+set(mca_root ${LLVM_MAIN_SRC_DIR}/tools/llvm-mca)
+
+set(mca_includes
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${mca_root}
+  )
+
+# Right now we only need SummaryView.
+set(mca_views_sources
+  SummaryView.cpp
+  )
+list(TRANSFORM mca_views_sources PREPEND "${mca_root}/Views/")
+
+set(mca_sources
+  MCATestBase.cpp
+  ${mca_views_sources}
+  )
+
+function(add_llvm_mca_unittest_includes)
+  set(mca_includes ${mca_includes} ${ARGV} PARENT_SCOPE)
+endfunction()
+
+function(add_llvm_mca_unittest_sources)
+  set(sources ${ARGV})
+  list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_LIST_DIR}/")
+  set(mca_sources ${mca_sources} ${sources} PARENT_SCOPE)
+endfunction()
+
+function(add_llvm_mca_unittest_link_components comps)
+  set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} ${ARGV} PARENT_SCOPE)
+endfunction()
+
+if(LLVM_TARGETS_TO_BUILD MATCHES "X86")
+  include(X86/CMakeLists.txt)
+endif()
+
+list(REMOVE_DUPLICATES LLVM_LINK_COMPONENTS)
+
+include_directories(${mca_includes})
+
+add_llvm_target_unittest(LLVMMCATests
+  ${mca_sources}
+  )
+
+set_property(TARGET LLVMMCATests PROPERTY FOLDER "Tests/UnitTests/ToolTests")
diff --git a/llvm/unittests/tools/llvm-mca/MCATestBase.cpp b/llvm/unittests/tools/llvm-mca/MCATestBase.cpp
new file mode 100644 (file)
index 0000000..6667813
--- /dev/null
@@ -0,0 +1,123 @@
+#include "MCATestBase.h"
+#include "Views/SummaryView.h"
+#include "llvm/MCA/CustomBehaviour.h"
+#include "llvm/MCA/InstrBuilder.h"
+#include "llvm/MCA/Pipeline.h"
+#include "llvm/MCA/SourceMgr.h"
+#include "llvm/MCA/View.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/WithColor.h"
+#include <string>
+
+using namespace llvm;
+using namespace mca;
+
+const Target *MCATestBase::getLLVMTarget() const {
+  std::string Error;
+  return TargetRegistry::lookupTarget(TheTriple.getTriple(), Error);
+}
+
+mca::PipelineOptions MCATestBase::getDefaultPipelineOptions() {
+  mca::PipelineOptions PO(/*MicroOpQueue=*/0, /*DecoderThroughput=*/0,
+                          /*DispatchWidth=*/0,
+                          /*RegisterFileSize=*/0,
+                          /*LoadQueueSize=*/0, /*StoreQueueSize=*/0,
+                          /*AssumeNoAlias=*/true,
+                          /*EnableBottleneckAnalysis=*/false);
+  return PO;
+}
+
+void MCATestBase::SetUp() {
+  TheTarget = getLLVMTarget();
+  ASSERT_NE(TheTarget, nullptr);
+
+  StringRef TripleName = TheTriple.getTriple();
+
+  STI.reset(TheTarget->createMCSubtargetInfo(TripleName, CPUName, MAttr));
+  ASSERT_TRUE(STI);
+  ASSERT_TRUE(STI->isCPUStringValid(CPUName));
+
+  MRI.reset(TheTarget->createMCRegInfo(TripleName));
+  ASSERT_TRUE(MRI);
+
+  auto MCOptions = getMCTargetOptions();
+  MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+  ASSERT_TRUE(MAI);
+
+  Ctx = std::make_unique<MCContext>(TheTriple, MAI.get(), MRI.get(), STI.get());
+  MOFI.reset(TheTarget->createMCObjectFileInfo(*Ctx, /*PIC=*/false));
+  Ctx->setObjectFileInfo(MOFI.get());
+
+  MCII.reset(TheTarget->createMCInstrInfo());
+  ASSERT_TRUE(MCII);
+
+  MCIA.reset(TheTarget->createMCInstrAnalysis(MCII.get()));
+  ASSERT_TRUE(MCIA);
+
+  IP.reset(TheTarget->createMCInstPrinter(TheTriple, /*AssemblerDialect=*/0,
+                                          *MAI, *MCII, *MRI));
+  ASSERT_TRUE(IP);
+}
+
+Error MCATestBase::runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
+                                  ArrayRef<mca::View *> Views,
+                                  const mca::PipelineOptions *PO) {
+  mca::Context MCA(*MRI, *STI);
+
+  mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());
+
+  SmallVector<std::unique_ptr<mca::Instruction>> LoweredInsts;
+  for (const auto &MCI : Insts) {
+    Expected<std::unique_ptr<mca::Instruction>> Inst =
+        IB.createInstruction(MCI);
+    if (!Inst) {
+      if (auto NewE =
+              handleErrors(Inst.takeError(),
+                           [this](const mca::InstructionError<MCInst> &IE) {
+                             std::string InstructionStr;
+                             raw_string_ostream SS(InstructionStr);
+                             WithColor::error() << IE.Message << '\n';
+                             IP->printInst(&IE.Inst, 0, "", *STI, SS);
+                             WithColor::note()
+                                 << "instruction: " << InstructionStr << '\n';
+                           })) {
+        // Default case.
+        return std::move(NewE);
+      }
+    } else {
+      LoweredInsts.emplace_back(std::move(Inst.get()));
+    }
+  }
+
+  mca::CircularSourceMgr SM(LoweredInsts, /*Iterations=*/1);
+
+  // Empty CustomBehaviour.
+  auto CB = std::make_unique<mca::CustomBehaviour>(*STI, SM, *MCII);
+
+  mca::PipelineOptions ThePO = PO ? *PO : getDefaultPipelineOptions();
+  auto P = MCA.createDefaultPipeline(ThePO, SM, *CB);
+
+  SmallVector<std::unique_ptr<mca::View>, 1> DefaultViews;
+  if (Views.empty()) {
+    // By default, we only add SummaryView.
+    auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), Insts,
+                                            ThePO.DispatchWidth);
+    P->addEventListener(SV.get());
+    DefaultViews.emplace_back(std::move(SV));
+  } else {
+    for (auto *V : Views)
+      P->addEventListener(V);
+  }
+
+  // Run the pipeline.
+  Expected<unsigned> Cycles = P->run();
+  if (!Cycles)
+    return Cycles.takeError();
+
+  for (const auto *V : Views)
+    Result[V->getNameAsString()] = V->toJSON();
+  for (const auto &V : DefaultViews)
+    Result[V->getNameAsString()] = V->toJSON();
+
+  return Error::success();
+}
diff --git a/llvm/unittests/tools/llvm-mca/MCATestBase.h b/llvm/unittests/tools/llvm-mca/MCATestBase.h
new file mode 100644 (file)
index 0000000..c1deb41
--- /dev/null
@@ -0,0 +1,83 @@
+//===---- MCATestBase.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Test fixture common to all MCA tests.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H
+#define LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/MCA/Context.h"
+
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace json {
+class Object;
+} // end namespace json
+
+namespace mca {
+class View;
+
+class MCATestBase : public ::testing::Test {
+protected:
+  // Note: Subclass ctors are expected to perform target-specific
+  // initializations.
+  MCATestBase(StringRef TripleStr, StringRef CPUName, StringRef MAttr = "")
+      : TheTriple(TripleStr), CPUName(CPUName), MAttr(MAttr) {}
+
+  /// Factory function to create a Target.
+  virtual const Target *getLLVMTarget() const;
+
+  /// Factory function to create a MCTargetOptions instance. Returns an
+  /// empty one by default.
+  virtual MCTargetOptions getMCTargetOptions() { return MCTargetOptions(); }
+
+  const Target *TheTarget;
+  const Triple TheTriple;
+  StringRef CPUName;
+  StringRef MAttr;
+
+  // MC components.
+  std::unique_ptr<MCSubtargetInfo> STI;
+  std::unique_ptr<MCRegisterInfo> MRI;
+  std::unique_ptr<MCAsmInfo> MAI;
+  std::unique_ptr<MCObjectFileInfo> MOFI;
+  std::unique_ptr<MCContext> Ctx;
+  std::unique_ptr<MCInstrInfo> MCII;
+  std::unique_ptr<MCInstrAnalysis> MCIA;
+  std::unique_ptr<MCInstPrinter> IP;
+
+  static mca::PipelineOptions getDefaultPipelineOptions();
+
+  void SetUp() override;
+
+  /// Utility function to run MCA with (nearly) the same configuration as the
+  /// `llvm-mca` tool to verify result correctness.
+  /// This function only displays on SummaryView by default.
+  virtual Error runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
+                               ArrayRef<mca::View *> Views = None,
+                               const mca::PipelineOptions *PO = nullptr);
+};
+
+} // end namespace mca
+} // end namespace llvm
+#endif
diff --git a/llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt b/llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt
new file mode 100644 (file)
index 0000000..42925f6
--- /dev/null
@@ -0,0 +1,13 @@
+add_llvm_mca_unittest_includes(
+  ${LLVM_MAIN_SRC_DIR}/lib/Target/X86
+  ${LLVM_BINARY_DIR}/lib/Target/X86
+  )
+
+add_llvm_mca_unittest_sources(
+  TestIncrementalMCA.cpp
+  X86TestBase.cpp
+  )
+
+add_llvm_mca_unittest_link_components(
+  X86
+  )
diff --git a/llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp b/llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp
new file mode 100644 (file)
index 0000000..6ed1eb5
--- /dev/null
@@ -0,0 +1,80 @@
+#include "Views/SummaryView.h"
+#include "X86TestBase.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/MCA/CustomBehaviour.h"
+#include "llvm/MCA/IncrementalSourceMgr.h"
+#include "llvm/MCA/InstrBuilder.h"
+#include "llvm/MCA/Pipeline.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
+#include <unordered_map>
+
+using namespace llvm;
+using namespace mca;
+
+TEST_F(X86TestBase, TestResumablePipeline) {
+  mca::Context MCA(*MRI, *STI);
+
+  mca::IncrementalSourceMgr ISM;
+  // Empty CustomBehaviour.
+  auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
+
+  auto PO = getDefaultPipelineOptions();
+  auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
+  ASSERT_TRUE(P);
+
+  SmallVector<MCInst> MCIs;
+  getSimpleInsts(MCIs, /*Repeats=*/100);
+
+  // Add views.
+  auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,
+                                          PO.DispatchWidth);
+  P->addEventListener(SV.get());
+
+  mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());
+
+  // Tile size = 7
+  for (unsigned i = 0U, E = MCIs.size(); i < E;) {
+    for (unsigned TE = i + 7; i < TE && i < E; ++i) {
+      Expected<std::unique_ptr<mca::Instruction>> InstOrErr =
+          IB.createInstruction(MCIs[i]);
+      ASSERT_TRUE(bool(InstOrErr));
+      ISM.addInst(std::move(InstOrErr.get()));
+    }
+
+    // Run the pipeline.
+    Expected<unsigned> Cycles = P->run();
+    if (!Cycles) {
+      // Should be a stream pause error.
+      ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());
+      llvm::consumeError(Cycles.takeError());
+    }
+  }
+
+  ISM.endOfStream();
+  // Has to terminate properly.
+  Expected<unsigned> Cycles = P->run();
+  ASSERT_TRUE(bool(Cycles));
+
+  json::Value Result = SV->toJSON();
+  auto *ResultObj = Result.getAsObject();
+  ASSERT_TRUE(ResultObj);
+
+  // Run the baseline.
+  json::Object BaselineResult;
+  auto E = runBaselineMCA(BaselineResult, MCIs);
+  ASSERT_FALSE(bool(E)) << "Failed to run baseline";
+  auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());
+  ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";
+
+  // Compare the results.
+  constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps",
+                                    "BlockRThroughput"};
+  for (const auto *F : Fields) {
+    auto V = ResultObj->getInteger(F);
+    auto BV = BaselineObj->getInteger(F);
+    ASSERT_TRUE(V && BV);
+    ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";
+  }
+}
diff --git a/llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp
new file mode 100644 (file)
index 0000000..6672c29
--- /dev/null
@@ -0,0 +1,35 @@
+#include "X86TestBase.h"
+#include "MCTargetDesc/X86MCTargetDesc.h"
+#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/Support/TargetSelect.h"
+
+using namespace llvm;
+using namespace mca;
+
+X86TestBase::X86TestBase() : MCATestBase("x86_64-unknown-linux", "skylake") {
+  LLVMInitializeX86TargetInfo();
+  LLVMInitializeX86TargetMC();
+  LLVMInitializeX86Target();
+  LLVMInitializeX86AsmPrinter();
+}
+
+void X86TestBase::getSimpleInsts(SmallVectorImpl<MCInst> &Insts,
+                                 unsigned Repeats) {
+  for (unsigned i = 0U; i < Repeats; ++i) {
+    // vmulps  %xmm0, %xmm1, %xmm2
+    Insts.push_back(MCInstBuilder(X86::VMULPSrr)
+                        .addReg(X86::XMM2)
+                        .addReg(X86::XMM1)
+                        .addReg(X86::XMM0));
+    // vhaddps %xmm2, %xmm2, %xmm3
+    Insts.push_back(MCInstBuilder(X86::VHADDPSrr)
+                        .addReg(X86::XMM3)
+                        .addReg(X86::XMM2)
+                        .addReg(X86::XMM2));
+    // vhaddps %xmm3, %xmm3, %xmm4
+    Insts.push_back(MCInstBuilder(X86::VHADDPSrr)
+                        .addReg(X86::XMM4)
+                        .addReg(X86::XMM3)
+                        .addReg(X86::XMM3));
+  }
+}
diff --git a/llvm/unittests/tools/llvm-mca/X86/X86TestBase.h b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.h
new file mode 100644 (file)
index 0000000..4704904
--- /dev/null
@@ -0,0 +1,30 @@
+//===---- X86TestBase.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Test fixture common to all X86 MCA tests.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H
+#define LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H
+
+#include "MCATestBase.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+namespace mca {
+
+class X86TestBase : public MCATestBase {
+protected:
+  X86TestBase();
+
+  void getSimpleInsts(SmallVectorImpl<MCInst> &Insts, unsigned Repeats = 1);
+};
+
+} // end namespace mca
+} // end namespace llvm
+
+#endif