[llvm] 97579dc - [MCA] Introducing incremental SourceMgr and resumable pipeline

Min-Yih Hsu via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 24 15:41:37 PDT 2022


Author: Min-Yih Hsu
Date: 2022-06-24T15:39:51-07:00
New Revision: 97579dcc6d3cd129c75b0502f7c43a18292d44b1

URL: https://github.com/llvm/llvm-project/commit/97579dcc6d3cd129c75b0502f7c43a18292d44b1
DIFF: https://github.com/llvm/llvm-project/commit/97579dcc6d3cd129c75b0502f7c43a18292d44b1.diff

LOG: [MCA] Introducing incremental SourceMgr and resumable pipeline

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

Added: 
    llvm/include/llvm/MCA/IncrementalSourceMgr.h
    llvm/unittests/tools/llvm-mca/CMakeLists.txt
    llvm/unittests/tools/llvm-mca/MCATestBase.cpp
    llvm/unittests/tools/llvm-mca/MCATestBase.h
    llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt
    llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp
    llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp
    llvm/unittests/tools/llvm-mca/X86/X86TestBase.h

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/MCA/IncrementalSourceMgr.h b/llvm/include/llvm/MCA/IncrementalSourceMgr.h
new file mode 100644
index 0000000000000..a84b87c96fef2
--- /dev/null
+++ b/llvm/include/llvm/MCA/IncrementalSourceMgr.h
@@ -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

diff  --git a/llvm/include/llvm/MCA/Pipeline.h b/llvm/include/llvm/MCA/Pipeline.h
index 0ac988c52dc13..92c3836124add 100644
--- a/llvm/include/llvm/MCA/Pipeline.h
+++ b/llvm/include/llvm/MCA/Pipeline.h
@@ -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

diff  --git a/llvm/include/llvm/MCA/SourceMgr.h b/llvm/include/llvm/MCA/SourceMgr.h
index e844171bdcab9..16a60d1116ad6 100644
--- a/llvm/include/llvm/MCA/SourceMgr.h
+++ b/llvm/include/llvm/MCA/SourceMgr.h
@@ -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

diff  --git a/llvm/include/llvm/MCA/Stages/EntryStage.h b/llvm/include/llvm/MCA/Stages/EntryStage.h
index 4c50838bef4b1..fb1244aa1933a 100644
--- a/llvm/include/llvm/MCA/Stages/EntryStage.h
+++ b/llvm/include/llvm/MCA/Stages/EntryStage.h
@@ -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 @@ class EntryStage final : public Stage {
   bool hasWorkToComplete() const override;
   Error execute(InstRef &IR) override;
   Error cycleStart() override;
+  Error cycleResume() override;
   Error cycleEnd() override;
 };
 

diff  --git a/llvm/include/llvm/MCA/Stages/Stage.h b/llvm/include/llvm/MCA/Stages/Stage.h
index 84868e89ac298..2477b9b3d69c2 100644
--- a/llvm/include/llvm/MCA/Stages/Stage.h
+++ b/llvm/include/llvm/MCA/Stages/Stage.h
@@ -48,6 +48,9 @@ class Stage {
   /// 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 @@ class Stage {
   }
 };
 
+/// 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

diff  --git a/llvm/lib/MCA/Pipeline.cpp b/llvm/lib/MCA/Pipeline.cpp
index 22b9d0799f77d..c94fe1422a690 100644
--- a/llvm/lib/MCA/Pipeline.cpp
+++ b/llvm/lib/MCA/Pipeline.cpp
@@ -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();

diff  --git a/llvm/lib/MCA/Stages/EntryStage.cpp b/llvm/lib/MCA/Stages/EntryStage.cpp
index 66135790a4cd7..6b3fbb8c6236b 100644
--- a/llvm/lib/MCA/Stages/EntryStage.cpp
+++ b/llvm/lib/MCA/Stages/EntryStage.cpp
@@ -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());

diff  --git a/llvm/lib/MCA/Stages/Stage.cpp b/llvm/lib/MCA/Stages/Stage.cpp
index ed512ac9711c5..5613d4d6bd072 100644
--- a/llvm/lib/MCA/Stages/Stage.cpp
+++ b/llvm/lib/MCA/Stages/Stage.cpp
@@ -24,5 +24,6 @@ void Stage::addListener(HWEventListener *Listener) {
   Listeners.insert(Listener);
 }
 
+char InstStreamPause::ID = 0;
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp b/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp
index 912bcc792e4d1..24c9cc2d7dd2f 100644
--- a/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp
+++ b/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp
@@ -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) &&

diff  --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp
index cfd114817e58c..409de283e5a17 100644
--- a/llvm/tools/llvm-mca/llvm-mca.cpp
+++ b/llvm/tools/llvm-mca/llvm-mca.cpp
@@ -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.

diff  --git a/llvm/unittests/tools/CMakeLists.txt b/llvm/unittests/tools/CMakeLists.txt
index 7861da8c0e38d..7ef64f1180a00 100644
--- a/llvm/unittests/tools/CMakeLists.txt
+++ b/llvm/unittests/tools/CMakeLists.txt
@@ -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
index 0000000000000..00848404cf837
--- /dev/null
+++ b/llvm/unittests/tools/llvm-mca/CMakeLists.txt
@@ -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
index 0000000000000..6667813cd607e
--- /dev/null
+++ b/llvm/unittests/tools/llvm-mca/MCATestBase.cpp
@@ -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
index 0000000000000..c1deb419ec5d2
--- /dev/null
+++ b/llvm/unittests/tools/llvm-mca/MCATestBase.h
@@ -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
index 0000000000000..42925f6f42a89
--- /dev/null
+++ b/llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt
@@ -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
index 0000000000000..6ed1eb511abda
--- /dev/null
+++ b/llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp
@@ -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
index 0000000000000..6672c29a7de65
--- /dev/null
+++ b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp
@@ -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
index 0000000000000..4704904ae70b0
--- /dev/null
+++ b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.h
@@ -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


        


More information about the llvm-commits mailing list