[llvm-branch-commits] [llvm] d38be2b - [llvm-mca] Initial implementation of serialization using JSON. The views

Wolfgang Pieb via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jan 21 15:20:52 PST 2021


Author: Wolfgang Pieb
Date: 2021-01-21T15:15:54-08:00
New Revision: d38be2ba0e4ebfed4c13ab79f3a8631011d185eb

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

LOG: [llvm-mca] Initial implementation of serialization using JSON. The views
implemented at this time are Summary, Timeline, ResourcePressure and InstructionInfo.
Use --json on the command line to obtain JSON output.

Added: 
    llvm/test/tools/llvm-mca/JSON/X86/views.s
    llvm/tools/llvm-mca/Views/InstructionView.cpp
    llvm/tools/llvm-mca/Views/InstructionView.h

Modified: 
    llvm/docs/CommandGuide/llvm-mca.rst
    llvm/docs/ReleaseNotes.rst
    llvm/tools/llvm-mca/CMakeLists.txt
    llvm/tools/llvm-mca/PipelinePrinter.cpp
    llvm/tools/llvm-mca/PipelinePrinter.h
    llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
    llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
    llvm/tools/llvm-mca/Views/DispatchStatistics.h
    llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
    llvm/tools/llvm-mca/Views/InstructionInfoView.h
    llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
    llvm/tools/llvm-mca/Views/ResourcePressureView.cpp
    llvm/tools/llvm-mca/Views/ResourcePressureView.h
    llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
    llvm/tools/llvm-mca/Views/SchedulerStatistics.h
    llvm/tools/llvm-mca/Views/SummaryView.cpp
    llvm/tools/llvm-mca/Views/SummaryView.h
    llvm/tools/llvm-mca/Views/TimelineView.cpp
    llvm/tools/llvm-mca/Views/TimelineView.h
    llvm/tools/llvm-mca/Views/View.cpp
    llvm/tools/llvm-mca/Views/View.h
    llvm/tools/llvm-mca/llvm-mca.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-mca.rst b/llvm/docs/CommandGuide/llvm-mca.rst
index 092aa2beb6c6..1122cb1de974 100644
--- a/llvm/docs/CommandGuide/llvm-mca.rst
+++ b/llvm/docs/CommandGuide/llvm-mca.rst
@@ -206,6 +206,12 @@ option specifies "``-``", then the output will also be sent to standard output.
   can be expensive, and it is disabled by default.  Bottlenecks are highlighted
   in the summary view.
 
+.. option:: -json
+
+  Print the requested views in JSON format. The instructions and the processor
+  resources are printed as members of special top level JSON objects.  The
+  individual views refer to them by index.
+
 
 EXIT STATUS
 -----------

diff  --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst
index 37742d28ba47..de8431fe3908 100644
--- a/llvm/docs/ReleaseNotes.rst
+++ b/llvm/docs/ReleaseNotes.rst
@@ -182,6 +182,10 @@ Changes to the LLVM tools
   executed with no input files instead of reading an input from stdin.
   Reading from stdin can still be achieved by specifying `-` as an input file.
 
+* llvm-mca supports serialization of the timeline and summary views.
+  The `--json` command line option prints a JSON representation of
+  these views to stdout.
+
 Changes to LLDB
 ---------------------------------
 

diff  --git a/llvm/test/tools/llvm-mca/JSON/X86/views.s b/llvm/test/tools/llvm-mca/JSON/X86/views.s
new file mode 100644
index 000000000000..ddfe724a10c4
--- /dev/null
+++ b/llvm/test/tools/llvm-mca/JSON/X86/views.s
@@ -0,0 +1,160 @@
+# NOTE: Assertions have been autogenerated by utils/update_mca_test_checks.py
+# Verify that we create proper JSON for the MCA views TimelineView, ResourcePressureview,
+# InstructionInfoView and SummaryView.
+
+# RUN: llvm-mca -mcpu=haswell --json --timeline-max-iterations=1 --timeline < %s | FileCheck %s
+
+add %eax, %eax
+add %ebx, %ebx
+add %ecx, %ecx
+add %edx, %edx
+
+# CHECK:      {
+# CHECK-NEXT:   "Instructions": [
+# CHECK-NEXT:     "addl\t%eax, %eax",
+# CHECK-NEXT:     "addl\t%ebx, %ebx",
+# CHECK-NEXT:     "addl\t%ecx, %ecx",
+# CHECK-NEXT:     "addl\t%edx, %edx"
+# CHECK-NEXT:   ],
+# CHECK-NEXT:   "Resources": {
+# CHECK-NEXT:     "CPUName": "haswell",
+# CHECK-NEXT:     "Resources": [
+# CHECK-NEXT:       "HWDivider",
+# CHECK-NEXT:       "HWFPDivider",
+# CHECK-NEXT:       "HWPort0",
+# CHECK-NEXT:       "HWPort1",
+# CHECK-NEXT:       "HWPort2",
+# CHECK-NEXT:       "HWPort3",
+# CHECK-NEXT:       "HWPort4",
+# CHECK-NEXT:       "HWPort5",
+# CHECK-NEXT:       "HWPort6",
+# CHECK-NEXT:       "HWPort7"
+# CHECK-NEXT:     ]
+# CHECK-NEXT:   }
+# CHECK-NEXT: }
+# CHECK-NEXT: {
+# CHECK-NEXT:   "SummaryView": {
+# CHECK-NEXT:     "BlockRThroughput": 1,
+# CHECK-NEXT:     "DispatchWidth": 4,
+# CHECK-NEXT:     "IPC": 3.883495145631068,
+# CHECK-NEXT:     "Instructions": 400,
+# CHECK-NEXT:     "Iterations": 100,
+# CHECK-NEXT:     "TotalCycles": 103,
+# CHECK-NEXT:     "TotaluOps": 400,
+# CHECK-NEXT:     "uOpsPerCycle": 3.883495145631068
+# CHECK-NEXT:   }
+# CHECK-NEXT: }
+# CHECK-NEXT: [
+# CHECK-NEXT:   {
+# CHECK-NEXT:     "Instruction": 0,
+# CHECK-NEXT:     "Latency": 1,
+# CHECK-NEXT:     "NumMicroOpcodes": 1,
+# CHECK-NEXT:     "RThroughput": 0.25,
+# CHECK-NEXT:     "hasUnmodeledSideEffects": false,
+# CHECK-NEXT:     "mayLoad": false,
+# CHECK-NEXT:     "mayStore": false
+# CHECK-NEXT:   },
+# CHECK-NEXT:   {
+# CHECK-NEXT:     "Instruction": 1,
+# CHECK-NEXT:     "Latency": 1,
+# CHECK-NEXT:     "NumMicroOpcodes": 1,
+# CHECK-NEXT:     "RThroughput": 0.25,
+# CHECK-NEXT:     "hasUnmodeledSideEffects": false,
+# CHECK-NEXT:     "mayLoad": false,
+# CHECK-NEXT:     "mayStore": false
+# CHECK-NEXT:   },
+# CHECK-NEXT:   {
+# CHECK-NEXT:     "Instruction": 2,
+# CHECK-NEXT:     "Latency": 1,
+# CHECK-NEXT:     "NumMicroOpcodes": 1,
+# CHECK-NEXT:     "RThroughput": 0.25,
+# CHECK-NEXT:     "hasUnmodeledSideEffects": false,
+# CHECK-NEXT:     "mayLoad": false,
+# CHECK-NEXT:     "mayStore": false
+# CHECK-NEXT:   },
+# CHECK-NEXT:   {
+# CHECK-NEXT:     "Instruction": 3,
+# CHECK-NEXT:     "Latency": 1,
+# CHECK-NEXT:     "NumMicroOpcodes": 1,
+# CHECK-NEXT:     "RThroughput": 0.25,
+# CHECK-NEXT:     "hasUnmodeledSideEffects": false,
+# CHECK-NEXT:     "mayLoad": false,
+# CHECK-NEXT:     "mayStore": false
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+# CHECK-NEXT: {
+# CHECK-NEXT:   "ResourcePressureInfo": [
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 0,
+# CHECK-NEXT:       "ResourceIndex": 8,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 1,
+# CHECK-NEXT:       "ResourceIndex": 7,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 2,
+# CHECK-NEXT:       "ResourceIndex": 3,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 3,
+# CHECK-NEXT:       "ResourceIndex": 2,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 4,
+# CHECK-NEXT:       "ResourceIndex": 2,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 4,
+# CHECK-NEXT:       "ResourceIndex": 3,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 4,
+# CHECK-NEXT:       "ResourceIndex": 7,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "InstructionIndex": 4,
+# CHECK-NEXT:       "ResourceIndex": 8,
+# CHECK-NEXT:       "ResourceUsage": 1
+# CHECK-NEXT:     }
+# CHECK-NEXT:   ]
+# CHECK-NEXT: }
+# CHECK-NEXT: {
+# CHECK-NEXT:   "TimelineInfo": [
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "CycleDispatched": 0,
+# CHECK-NEXT:       "CycleExecuted": 2,
+# CHECK-NEXT:       "CycleIssued": 1,
+# CHECK-NEXT:       "CycleReady": 0,
+# CHECK-NEXT:       "CycleRetired": 3
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "CycleDispatched": 0,
+# CHECK-NEXT:       "CycleExecuted": 2,
+# CHECK-NEXT:       "CycleIssued": 1,
+# CHECK-NEXT:       "CycleReady": 0,
+# CHECK-NEXT:       "CycleRetired": 3
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "CycleDispatched": 0,
+# CHECK-NEXT:       "CycleExecuted": 2,
+# CHECK-NEXT:       "CycleIssued": 1,
+# CHECK-NEXT:       "CycleReady": 0,
+# CHECK-NEXT:       "CycleRetired": 3
+# CHECK-NEXT:     },
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "CycleDispatched": 0,
+# CHECK-NEXT:       "CycleExecuted": 2,
+# CHECK-NEXT:       "CycleIssued": 1,
+# CHECK-NEXT:       "CycleReady": 0,
+# CHECK-NEXT:       "CycleRetired": 3
+# CHECK-NEXT:     }
+# CHECK-NEXT:   ]
+# CHECK-NEXT: }

diff  --git a/llvm/tools/llvm-mca/CMakeLists.txt b/llvm/tools/llvm-mca/CMakeLists.txt
index 9918cd8c8dc6..9df1923a5bdc 100644
--- a/llvm/tools/llvm-mca/CMakeLists.txt
+++ b/llvm/tools/llvm-mca/CMakeLists.txt
@@ -19,6 +19,7 @@ add_llvm_tool(llvm-mca
   Views/BottleneckAnalysis.cpp
   Views/DispatchStatistics.cpp
   Views/InstructionInfoView.cpp
+  Views/InstructionView.cpp
   Views/RegisterFileStatistics.cpp
   Views/ResourcePressureView.cpp
   Views/RetireControlUnitStatistics.cpp

diff  --git a/llvm/tools/llvm-mca/PipelinePrinter.cpp b/llvm/tools/llvm-mca/PipelinePrinter.cpp
index 90d468075996..e7dfbfdce26d 100644
--- a/llvm/tools/llvm-mca/PipelinePrinter.cpp
+++ b/llvm/tools/llvm-mca/PipelinePrinter.cpp
@@ -19,7 +19,7 @@ namespace mca {
 
 void PipelinePrinter::printReport(llvm::raw_ostream &OS) const {
   for (const auto &V : Views)
-    V->printView(OS);
+    V->printView(OutputKind, OS);
 }
 } // namespace mca.
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/PipelinePrinter.h b/llvm/tools/llvm-mca/PipelinePrinter.h
index 004309cd7b8e..ae18140d32b7 100644
--- a/llvm/tools/llvm-mca/PipelinePrinter.h
+++ b/llvm/tools/llvm-mca/PipelinePrinter.h
@@ -36,9 +36,11 @@ namespace mca {
 class PipelinePrinter {
   Pipeline &P;
   llvm::SmallVector<std::unique_ptr<View>, 8> Views;
+  View::OutputKind OutputKind;
 
 public:
-  PipelinePrinter(Pipeline &pipeline) : P(pipeline) {}
+  PipelinePrinter(Pipeline &pipeline, View::OutputKind OutputKind)
+      : P(pipeline), OutputKind(OutputKind) {}
 
   void addView(std::unique_ptr<View> V) {
     P.addEventListener(V.get());

diff  --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
index 519b928fda5d..38a8e2ef9c53 100644
--- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
+++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
@@ -287,7 +287,6 @@ void BottleneckAnalysis::printInstruction(formatted_raw_ostream &FOS,
                                           const MCInst &MCI,
                                           bool UseDifferentColor) const {
   FOS.PadToColumn(14);
-
   if (UseDifferentColor)
     FOS.changeColor(raw_ostream::CYAN, true, false);
   FOS << printInstructionString(MCI);

diff  --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
index a0aad9faff40..427937d9e3d7 100644
--- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
+++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
@@ -80,14 +80,14 @@
 #ifndef LLVM_TOOLS_LLVM_MCA_BOTTLENECK_ANALYSIS_H
 #define LLVM_TOOLS_LLVM_MCA_BOTTLENECK_ANALYSIS_H
 
-#include "Views/View.h"
+#include "Views/InstructionView.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/MC/MCInstPrinter.h"
 #include "llvm/MC/MCSchedule.h"
 #include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace llvm {
 namespace mca {
@@ -332,6 +332,8 @@ class BottleneckAnalysis : public InstructionView {
   void onEvent(const HWInstructionEvent &Event) override;
 
   void printView(raw_ostream &OS) const override;
+  StringRef getNameAsString() const override { return "BottleneckAnalysis"; }
+  json::Value toJSON() const override { return "not implemented"; }
 
 #ifndef NDEBUG
   void dump(raw_ostream &OS, MCInstPrinter &MCIP) const { DG.dump(OS, MCIP); }

diff  --git a/llvm/tools/llvm-mca/Views/DispatchStatistics.h b/llvm/tools/llvm-mca/Views/DispatchStatistics.h
index 07c0f5a4c68f..8d999fb0acfe 100644
--- a/llvm/tools/llvm-mca/Views/DispatchStatistics.h
+++ b/llvm/tools/llvm-mca/Views/DispatchStatistics.h
@@ -78,6 +78,7 @@ class DispatchStatistics : public View {
     printDispatchStalls(OS);
     printDispatchHistogram(OS);
   }
+  StringRef getNameAsString() const override { return "DispatchStatistics"; }
 };
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
index 803b3ec578aa..bff5729bbc96 100644
--- a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
+++ b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
@@ -13,6 +13,7 @@
 
 #include "Views/InstructionInfoView.h"
 #include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/JSON.h"
 
 namespace llvm {
 namespace mca {
@@ -39,7 +40,7 @@ void InstructionInfoView::printView(raw_ostream &OS) const {
     TempStream << "\n[1]    [2]    [3]    [4]    [5]    [6]    Instructions:\n";
   }
 
-  for (auto I : enumerate(zip(IIVD, Source))) {
+  for (const auto &I : enumerate(zip(IIVD, Source))) {
     const InstructionInfoViewData &IIVDEntry = std::get<0>(I.value());
 
     TempStream << ' ' << IIVDEntry.NumMicroOpcodes << "    ";
@@ -92,7 +93,7 @@ void InstructionInfoView::collectData(
     MutableArrayRef<InstructionInfoViewData> IIVD) const {
   const llvm::MCSubtargetInfo &STI = getSubTargetInfo();
   const MCSchedModel &SM = STI.getSchedModel();
-  for (auto I : zip(getSource(), IIVD)) {
+  for (const auto &I : zip(getSource(), IIVD)) {
     const MCInst &Inst = std::get<0>(I);
     InstructionInfoViewData &IIVDEntry = std::get<1>(I);
     const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());
@@ -118,5 +119,35 @@ void InstructionInfoView::collectData(
     IIVDEntry.hasUnmodeledSideEffects = MCDesc.hasUnmodeledSideEffects();
   }
 }
+
+// Construct a JSON object from a single InstructionInfoViewData object.
+json::Object
+InstructionInfoView::toJSON(const InstructionInfoViewData &IIVD) const {
+  json::Object JO({{"NumMicroOpcodes", IIVD.NumMicroOpcodes},
+                   {"Latency", IIVD.Latency},
+                   {"mayLoad", IIVD.mayLoad},
+                   {"mayStore", IIVD.mayStore},
+                   {"hasUnmodeledSideEffects", IIVD.hasUnmodeledSideEffects}});
+  JO.try_emplace("RThroughput", IIVD.RThroughput.getValueOr(0.0));
+  return JO;
+}
+
+json::Value InstructionInfoView::toJSON() const {
+  ArrayRef<llvm::MCInst> Source = getSource();
+  if (!Source.size())
+    return json::Value(0);
+
+  IIVDVec IIVD(Source.size());
+  collectData(IIVD);
+
+  json::Array InstInfo;
+  for (const auto I : enumerate(IIVD)) {
+    const InstructionInfoViewData &IIVDEntry = I.value();
+    json::Object JO = toJSON(IIVDEntry);
+    JO.try_emplace("Instruction", (unsigned)I.index());
+    InstInfo.push_back(std::move(JO));
+  }
+  return json::Value(std::move(InstInfo));
+}
 } // namespace mca.
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/InstructionInfoView.h b/llvm/tools/llvm-mca/Views/InstructionInfoView.h
index c2093b2d0429..82b2d678ea6b 100644
--- a/llvm/tools/llvm-mca/Views/InstructionInfoView.h
+++ b/llvm/tools/llvm-mca/Views/InstructionInfoView.h
@@ -34,7 +34,7 @@
 #ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
 #define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
 
-#include "Views/View.h"
+#include "Views/InstructionView.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/MC/MCInst.h"
@@ -77,6 +77,9 @@ class InstructionInfoView : public InstructionView {
         PrintEncodings(ShouldPrintEncodings) {}
 
   void printView(llvm::raw_ostream &OS) const override;
+  StringRef getNameAsString() const override { return "InstructionInfoView"; }
+  json::Value toJSON() const;
+  json::Object toJSON(const InstructionInfoViewData &IIVD) const;
 };
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/InstructionView.cpp b/llvm/tools/llvm-mca/Views/InstructionView.cpp
new file mode 100644
index 000000000000..7f7a5b7cdbbb
--- /dev/null
+++ b/llvm/tools/llvm-mca/Views/InstructionView.cpp
@@ -0,0 +1,60 @@
+//===----------------------- View.cpp ---------------------------*- 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 defines the member functions of the class InstructionView.
+///
+//===----------------------------------------------------------------------===//
+
+#include <sstream>
+#include "Views/InstructionView.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+
+namespace llvm {
+namespace mca {
+
+StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const {
+  InstructionString = "";
+  MCIP.printInst(&MCI, 0, "", STI, InstrStream);
+  InstrStream.flush();
+  // Remove any tabs or spaces at the beginning of the instruction.
+  return StringRef(InstructionString).ltrim();
+}
+
+json::Value InstructionView::toJSON() const {
+  json::Object JO;
+  json::Array SourceInfo;
+  for (const auto &MCI : getSource()) {
+    StringRef Instruction = printInstructionString(MCI);
+    SourceInfo.push_back(Instruction.str());
+  }
+  JO.try_emplace("Instructions", std::move(SourceInfo));
+
+  json::Array Resources;
+  const MCSchedModel &SM = STI.getSchedModel();
+  for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+    const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+    unsigned NumUnits = ProcResource.NumUnits;
+    // Skip groups and invalid resources with zero units.
+    if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+      continue;
+    for (unsigned J = 0; J < NumUnits; ++J) {
+      std::stringstream ResNameStream;
+      ResNameStream << ProcResource.Name;
+      if (NumUnits > 1)
+        ResNameStream << "." << J;
+      Resources.push_back(ResNameStream.str());
+    }
+  }
+  JO.try_emplace("Resources", json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}}));
+
+  return JO;
+}
+} // namespace mca
+} // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/InstructionView.h b/llvm/tools/llvm-mca/Views/InstructionView.h
new file mode 100644
index 000000000000..3f967471fabe
--- /dev/null
+++ b/llvm/tools/llvm-mca/Views/InstructionView.h
@@ -0,0 +1,64 @@
+//===----------------------- InstrucionView.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 defines the main interface for Views that examine and reference
+/// a sequence of machine instructions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONVIEW_H
+
+#include "Views/View.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/JSON.h"
+
+namespace llvm {
+namespace mca {
+
+// The base class for views that deal with individual machine instructions.
+class InstructionView : public View {
+  const llvm::MCSubtargetInfo &STI;
+  llvm::MCInstPrinter &MCIP;
+  llvm::ArrayRef<llvm::MCInst> Source;
+  StringRef MCPU;
+
+  mutable std::string InstructionString;
+  mutable raw_string_ostream InstrStream;
+
+public:
+  void printView(llvm::raw_ostream &) const {}
+  InstructionView(const llvm::MCSubtargetInfo &STI,
+                  llvm::MCInstPrinter &Printer,
+                  llvm::ArrayRef<llvm::MCInst> S,
+                  StringRef MCPU = StringRef())
+      : STI(STI), MCIP(Printer), Source(S), MCPU(MCPU), InstrStream(InstructionString) {}
+
+  virtual ~InstructionView() = default;
+
+  StringRef getNameAsString() const { return "Instructions and CPU resources"; }
+  // Return a reference to a string representing a given machine instruction.
+  // The result should be used or copied before the next call to
+  // printInstructionString() as it will overwrite the previous result.
+  StringRef printInstructionString(const llvm::MCInst &MCI) const;
+  const llvm::MCSubtargetInfo &getSubTargetInfo() const { return STI; }
+
+  llvm::MCInstPrinter &getInstPrinter() const { return MCIP; }
+  llvm::ArrayRef<llvm::MCInst> getSource() const { return Source; }
+  json::Value toJSON() const override;
+  virtual void printViewJSON(llvm::raw_ostream &OS) override {
+    json::Value JV = toJSON();
+    OS << formatv("{0:2}", JV) << "\n";
+  }
+};
+} // namespace mca
+} // namespace llvm
+
+#endif

diff  --git a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
index a2273dd48b22..cf384dbfe337 100644
--- a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
+++ b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
@@ -73,6 +73,9 @@ class RegisterFileStatistics : public View {
   void onCycleEnd() override;
   void onEvent(const HWInstructionEvent &Event) override;
   void printView(llvm::raw_ostream &OS) const override;
+  StringRef getNameAsString() const override {
+    return "RegisterFileStatistics";
+  }
 };
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp b/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp
index a5a74210c672..77b3ba0b7c8d 100644
--- a/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp
+++ b/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp
@@ -171,5 +171,30 @@ void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
     ++InstrIndex;
   }
 }
+
+json::Value ResourcePressureView::toJSON() const {
+  // We're dumping the instructions and the ResourceUsage array.
+  json::Array ResourcePressureInfo;
+
+  // The ResourceUsage matrix is sparse, so we only consider
+  // non-zero values.
+  ArrayRef<llvm::MCInst> Source = getSource();
+  const unsigned Executions = LastInstructionIdx / Source.size() + 1;
+  for (const auto &R : enumerate(ResourceUsage)) {
+    const ResourceCycles &RU = R.value();
+    if (RU.getNumerator() == 0)
+      continue;
+    unsigned InstructionIndex = R.index() / NumResourceUnits;
+    unsigned ResourceIndex = R.index() % NumResourceUnits;
+    double Usage = RU / Executions;
+    ResourcePressureInfo.push_back(
+        json::Object({{"InstructionIndex", InstructionIndex},
+                      {"ResourceIndex", ResourceIndex},
+                      {"ResourceUsage", Usage}}));
+  }
+
+  json::Object JO({{"ResourcePressureInfo", std::move(ResourcePressureInfo)}});
+  return JO;
+}
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/ResourcePressureView.h b/llvm/tools/llvm-mca/Views/ResourcePressureView.h
index 39914f9e2f27..5a9b5caee503 100644
--- a/llvm/tools/llvm-mca/Views/ResourcePressureView.h
+++ b/llvm/tools/llvm-mca/Views/ResourcePressureView.h
@@ -57,12 +57,13 @@
 #ifndef LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
 #define LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
 
-#include "Views/View.h"
+#include "Views/InstructionView.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCInstPrinter.h"
 #include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/JSON.h"
 
 namespace llvm {
 namespace mca {
@@ -93,6 +94,8 @@ class ResourcePressureView : public InstructionView {
     printResourcePressurePerIter(OS);
     printResourcePressurePerInst(OS);
   }
+  StringRef getNameAsString() const override { return "ResourcePressureView"; }
+  json::Value toJSON() const;
 };
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
index 1a4d3dec5c56..662a223662e6 100644
--- a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
+++ b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
@@ -52,6 +52,9 @@ class RetireControlUnitStatistics : public View {
   void onEvent(const HWInstructionEvent &Event) override;
   void onCycleEnd() override;
   void printView(llvm::raw_ostream &OS) const override;
+  StringRef getNameAsString() const override {
+    return "RetireControlUnitStatistics";
+  }
 };
 
 } // namespace mca

diff  --git a/llvm/tools/llvm-mca/Views/SchedulerStatistics.h b/llvm/tools/llvm-mca/Views/SchedulerStatistics.h
index 32711b4483b4..734046c3112f 100644
--- a/llvm/tools/llvm-mca/Views/SchedulerStatistics.h
+++ b/llvm/tools/llvm-mca/Views/SchedulerStatistics.h
@@ -88,6 +88,7 @@ class SchedulerStatistics final : public View {
                          llvm::ArrayRef<unsigned> Buffers) override;
 
   void printView(llvm::raw_ostream &OS) const override;
+  StringRef getNameAsString() const override { return "SchedulerStatistics"; }
 };
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/SummaryView.cpp b/llvm/tools/llvm-mca/Views/SummaryView.cpp
index e15b1b4cd7a0..c0fe3b5193a7 100644
--- a/llvm/tools/llvm-mca/Views/SummaryView.cpp
+++ b/llvm/tools/llvm-mca/Views/SummaryView.cpp
@@ -96,5 +96,19 @@ void SummaryView::collectData(DisplayValues &DV) const {
   DV.BlockRThroughput = computeBlockRThroughput(SM, DispatchWidth, NumMicroOps,
                                                 ProcResourceUsage);
 }
+
+json::Value SummaryView::toJSON() const {
+  DisplayValues DV;
+  collectData(DV);
+  json::Object JO({{"Iterations", DV.Iterations},
+                   {"Instructions", DV.TotalInstructions},
+                   {"TotalCycles", DV.TotalCycles},
+                   {"TotaluOps", DV.TotalUOps},
+                   {"DispatchWidth", DV.DispatchWidth},
+                   {"uOpsPerCycle", DV.UOpsPerCycle},
+                   {"IPC", DV.IPC},
+                   {"BlockRThroughput", DV.BlockRThroughput}});
+  return JO;
+}
 } // namespace mca.
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/SummaryView.h b/llvm/tools/llvm-mca/Views/SummaryView.h
index bc957ea9152b..2622e869ef23 100644
--- a/llvm/tools/llvm-mca/Views/SummaryView.h
+++ b/llvm/tools/llvm-mca/Views/SummaryView.h
@@ -87,8 +87,9 @@ class SummaryView : public View {
   void onCycleEnd() override { ++TotalCycles; }
   void onEvent(const HWInstructionEvent &Event) override;
   void printView(llvm::raw_ostream &OS) const override;
+  StringRef getNameAsString() const override { return "SummaryView"; }
+  json::Value toJSON() const override;
 };
-
 } // namespace mca
 } // namespace llvm
 

diff  --git a/llvm/tools/llvm-mca/Views/TimelineView.cpp b/llvm/tools/llvm-mca/Views/TimelineView.cpp
index f520c5c31d3f..c8b481bc7ce6 100644
--- a/llvm/tools/llvm-mca/Views/TimelineView.cpp
+++ b/llvm/tools/llvm-mca/Views/TimelineView.cpp
@@ -297,5 +297,19 @@ void TimelineView::printTimeline(raw_ostream &OS) const {
     }
   }
 }
+
+json::Value TimelineView::toJSON() const {
+  json::Array TimelineInfo;
+
+  for (const TimelineViewEntry &TLE : Timeline) {
+    TimelineInfo.push_back(
+        json::Object({{"CycleDispatched", TLE.CycleDispatched},
+                      {"CycleReady", TLE.CycleReady},
+                      {"CycleIssued", TLE.CycleIssued},
+                      {"CycleExecuted", TLE.CycleExecuted},
+                      {"CycleRetired", TLE.CycleRetired}}));
+  }
+  return json::Object({{"TimelineInfo", std::move(TimelineInfo)}});
+}
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/TimelineView.h b/llvm/tools/llvm-mca/Views/TimelineView.h
index 528579edf708..a9e94d7b2a92 100644
--- a/llvm/tools/llvm-mca/Views/TimelineView.h
+++ b/llvm/tools/llvm-mca/Views/TimelineView.h
@@ -100,12 +100,13 @@
 #ifndef LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
 #define LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
 
-#include "Views/View.h"
+#include "Views/InstructionView.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCInstPrinter.h"
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/JSON.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace llvm {
@@ -178,6 +179,8 @@ class TimelineView : public InstructionView {
     printTimeline(OS);
     printAverageWaitTimes(OS);
   }
+  StringRef getNameAsString() const override { return "TimelineView"; }
+  json::Value toJSON() const;
 };
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/View.cpp b/llvm/tools/llvm-mca/Views/View.cpp
index 4cef7456f366..09d08d3ae007 100644
--- a/llvm/tools/llvm-mca/Views/View.cpp
+++ b/llvm/tools/llvm-mca/Views/View.cpp
@@ -12,18 +12,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "Views/View.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCSubtargetInfo.h"
 
 namespace llvm {
 namespace mca {
 
 void View::anchor() {}
 
-StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const {
-    InstructionString = "";
-    MCIP.printInst(&MCI, 0, "", STI, InstrStream);
-    InstrStream.flush();
-    // Remove any tabs or spaces at the beginning of the instruction.
-    return StringRef(InstructionString).ltrim();
-  }
 } // namespace mca
 } // namespace llvm

diff  --git a/llvm/tools/llvm-mca/Views/View.h b/llvm/tools/llvm-mca/Views/View.h
index 1af6e5959f31..85464bfda662 100644
--- a/llvm/tools/llvm-mca/Views/View.h
+++ b/llvm/tools/llvm-mca/Views/View.h
@@ -18,43 +18,33 @@
 #include "llvm/MC/MCInstPrinter.h"
 #include "llvm/MCA/HWEventListener.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/JSON.h"
 
 namespace llvm {
 namespace mca {
 
 class View : public HWEventListener {
 public:
+  enum OutputKind { OK_READABLE, OK_JSON };
+
+  void printView(OutputKind OutputKind, llvm::raw_ostream &OS) {
+    if (OutputKind == OK_JSON)
+      printViewJSON(OS);
+    else
+      printView(OS);
+  }
+
   virtual void printView(llvm::raw_ostream &OS) const = 0;
+  virtual void printViewJSON(llvm::raw_ostream &OS) {
+    json::Object JO;
+    JO.try_emplace(getNameAsString().str(), toJSON());
+    OS << formatv("{0:2}", json::Value(std::move(JO))) << "\n";
+  }
   virtual ~View() = default;
+  virtual StringRef getNameAsString() const = 0;
+  virtual json::Value toJSON() const { return "not implemented"; }
   void anchor() override;
 };
-
-// The base class for views that deal with individual machine instructions.
-class InstructionView : public View {
-  const llvm::MCSubtargetInfo &STI;
-  llvm::MCInstPrinter &MCIP;
-  llvm::ArrayRef<llvm::MCInst> Source;
-
-  mutable std::string InstructionString;
-  mutable raw_string_ostream InstrStream;
-
-protected:
-  InstructionView(const llvm::MCSubtargetInfo &STI,
-                  llvm::MCInstPrinter &Printer,
-                  llvm::ArrayRef<llvm::MCInst> S)
-      : STI(STI), MCIP(Printer), Source(S), InstrStream(InstructionString) {}
-
-  virtual ~InstructionView() = default;
-
-  // Return a reference to a string representing a given machine instruction.
-  // The result should be used or copied before the next call to
-  // printInstructionString() as it will overwrite the previous result.
-  StringRef printInstructionString(const llvm::MCInst &MCI) const;
-  
-  const llvm::MCSubtargetInfo &getSubTargetInfo() const { return STI; }
-  llvm::MCInstPrinter &getInstPrinter() const { return MCIP; }
-  llvm::ArrayRef<llvm::MCInst> getSource() const { return Source; }
-};
 } // namespace mca
 } // namespace llvm
 

diff  --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp
index d122e641ad25..13a2c6363579 100644
--- a/llvm/tools/llvm-mca/llvm-mca.cpp
+++ b/llvm/tools/llvm-mca/llvm-mca.cpp
@@ -96,6 +96,11 @@ static cl::opt<std::string>
           cl::desc("Additional target features."),
           cl::cat(ToolOptions));
 
+static cl::opt<bool>
+    PrintJson("json",
+          cl::desc("Print the output in json format"),
+          cl::cat(ToolOptions), cl::init(false));
+
 static cl::opt<int>
     OutputAsmVariant("output-asm-variant",
                      cl::desc("Syntax variant to use for output printing"),
@@ -501,7 +506,7 @@ int main(int argc, char **argv) {
       auto P = std::make_unique<mca::Pipeline>();
       P->appendStage(std::make_unique<mca::EntryStage>(S));
       P->appendStage(std::make_unique<mca::InstructionTables>(SM));
-      mca::PipelinePrinter Printer(*P);
+      mca::PipelinePrinter Printer(*P, mca::View::OK_READABLE);
 
       // Create the views for this pipeline, execute, and emit a report.
       if (PrintInstructionInfoView) {
@@ -520,7 +525,14 @@ int main(int argc, char **argv) {
 
     // Create a basic pipeline simulating an out-of-order backend.
     auto P = MCA.createDefaultPipeline(PO, S);
-    mca::PipelinePrinter Printer(*P);
+    mca::PipelinePrinter Printer(*P, PrintJson ? mca::View::OK_JSON
+                                               : mca::View::OK_READABLE);
+
+    // When we output JSON, we add a view that contains the instructions
+    // and CPU resource information.
+    if (PrintJson)
+      Printer.addView(
+          std::make_unique<mca::InstructionView>(*STI, *IP, Insts, MCPU));
 
     if (PrintSummaryView)
       Printer.addView(


        


More information about the llvm-branch-commits mailing list