Index: llvm/docs/CommandGuide/llvm-mca.rst =================================================================== --- llvm/docs/CommandGuide/llvm-mca.rst +++ llvm/docs/CommandGuide/llvm-mca.rst @@ -206,6 +206,12 @@ 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 ----------- Index: llvm/docs/ReleaseNotes.rst =================================================================== --- llvm/docs/ReleaseNotes.rst +++ llvm/docs/ReleaseNotes.rst @@ -182,6 +182,10 @@ 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 --------------------------------- Index: llvm/test/tools/llvm-mca/JSON/X86/views.s =================================================================== --- /dev/null +++ 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: } Index: llvm/tools/llvm-mca/CMakeLists.txt =================================================================== --- llvm/tools/llvm-mca/CMakeLists.txt +++ llvm/tools/llvm-mca/CMakeLists.txt @@ -19,6 +19,7 @@ Views/BottleneckAnalysis.cpp Views/DispatchStatistics.cpp Views/InstructionInfoView.cpp + Views/InstructionView.cpp Views/RegisterFileStatistics.cpp Views/ResourcePressureView.cpp Views/RetireControlUnitStatistics.cpp Index: llvm/tools/llvm-mca/PipelinePrinter.h =================================================================== --- llvm/tools/llvm-mca/PipelinePrinter.h +++ llvm/tools/llvm-mca/PipelinePrinter.h @@ -36,9 +36,11 @@ class PipelinePrinter { Pipeline &P; llvm::SmallVector, 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 V) { P.addEventListener(V.get()); Index: llvm/tools/llvm-mca/PipelinePrinter.cpp =================================================================== --- llvm/tools/llvm-mca/PipelinePrinter.cpp +++ llvm/tools/llvm-mca/PipelinePrinter.cpp @@ -19,7 +19,7 @@ void PipelinePrinter::printReport(llvm::raw_ostream &OS) const { for (const auto &V : Views) - V->printView(OS); + V->printView(OutputKind, OS); } } // namespace mca. } // namespace llvm Index: llvm/tools/llvm-mca/Views/BottleneckAnalysis.h =================================================================== --- llvm/tools/llvm-mca/Views/BottleneckAnalysis.h +++ 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 @@ 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); } Index: llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp =================================================================== --- llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp +++ llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp @@ -287,7 +287,6 @@ const MCInst &MCI, bool UseDifferentColor) const { FOS.PadToColumn(14); - if (UseDifferentColor) FOS.changeColor(raw_ostream::CYAN, true, false); FOS << printInstructionString(MCI); Index: llvm/tools/llvm-mca/Views/DispatchStatistics.h =================================================================== --- llvm/tools/llvm-mca/Views/DispatchStatistics.h +++ llvm/tools/llvm-mca/Views/DispatchStatistics.h @@ -78,6 +78,7 @@ printDispatchStalls(OS); printDispatchHistogram(OS); } + StringRef getNameAsString() const override { return "DispatchStatistics"; } }; } // namespace mca } // namespace llvm Index: llvm/tools/llvm-mca/Views/InstructionInfoView.h =================================================================== --- llvm/tools/llvm-mca/Views/InstructionInfoView.h +++ 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 @@ 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 Index: llvm/tools/llvm-mca/Views/InstructionInfoView.cpp =================================================================== --- llvm/tools/llvm-mca/Views/InstructionInfoView.cpp +++ 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 @@ 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 @@ MutableArrayRef 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 @@ 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 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 Index: llvm/tools/llvm-mca/Views/InstructionView.h =================================================================== --- /dev/null +++ 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 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 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 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 Index: llvm/tools/llvm-mca/Views/InstructionView.cpp =================================================================== --- /dev/null +++ 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 +#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 Index: llvm/tools/llvm-mca/Views/RegisterFileStatistics.h =================================================================== --- llvm/tools/llvm-mca/Views/RegisterFileStatistics.h +++ llvm/tools/llvm-mca/Views/RegisterFileStatistics.h @@ -73,6 +73,9 @@ 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 Index: llvm/tools/llvm-mca/Views/ResourcePressureView.h =================================================================== --- llvm/tools/llvm-mca/Views/ResourcePressureView.h +++ 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 @@ printResourcePressurePerIter(OS); printResourcePressurePerInst(OS); } + StringRef getNameAsString() const override { return "ResourcePressureView"; } + json::Value toJSON() const; }; } // namespace mca } // namespace llvm Index: llvm/tools/llvm-mca/Views/ResourcePressureView.cpp =================================================================== --- llvm/tools/llvm-mca/Views/ResourcePressureView.cpp +++ llvm/tools/llvm-mca/Views/ResourcePressureView.cpp @@ -171,5 +171,30 @@ ++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 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 Index: llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h =================================================================== --- llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h +++ llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h @@ -52,6 +52,9 @@ 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 Index: llvm/tools/llvm-mca/Views/SchedulerStatistics.h =================================================================== --- llvm/tools/llvm-mca/Views/SchedulerStatistics.h +++ llvm/tools/llvm-mca/Views/SchedulerStatistics.h @@ -88,6 +88,7 @@ llvm::ArrayRef Buffers) override; void printView(llvm::raw_ostream &OS) const override; + StringRef getNameAsString() const override { return "SchedulerStatistics"; } }; } // namespace mca } // namespace llvm Index: llvm/tools/llvm-mca/Views/SummaryView.h =================================================================== --- llvm/tools/llvm-mca/Views/SummaryView.h +++ llvm/tools/llvm-mca/Views/SummaryView.h @@ -87,8 +87,9 @@ 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 Index: llvm/tools/llvm-mca/Views/SummaryView.cpp =================================================================== --- llvm/tools/llvm-mca/Views/SummaryView.cpp +++ llvm/tools/llvm-mca/Views/SummaryView.cpp @@ -96,5 +96,19 @@ 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 Index: llvm/tools/llvm-mca/Views/TimelineView.h =================================================================== --- llvm/tools/llvm-mca/Views/TimelineView.h +++ 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 @@ printTimeline(OS); printAverageWaitTimes(OS); } + StringRef getNameAsString() const override { return "TimelineView"; } + json::Value toJSON() const; }; } // namespace mca } // namespace llvm Index: llvm/tools/llvm-mca/Views/TimelineView.cpp =================================================================== --- llvm/tools/llvm-mca/Views/TimelineView.cpp +++ llvm/tools/llvm-mca/Views/TimelineView.cpp @@ -297,5 +297,19 @@ } } } + +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 Index: llvm/tools/llvm-mca/Views/View.h =================================================================== --- llvm/tools/llvm-mca/Views/View.h +++ 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 Source; - - mutable std::string InstructionString; - mutable raw_string_ostream InstrStream; - -protected: - InstructionView(const llvm::MCSubtargetInfo &STI, - llvm::MCInstPrinter &Printer, - llvm::ArrayRef 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 getSource() const { return Source; } -}; } // namespace mca } // namespace llvm Index: llvm/tools/llvm-mca/Views/View.cpp =================================================================== --- llvm/tools/llvm-mca/Views/View.cpp +++ 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 Index: llvm/tools/llvm-mca/llvm-mca.cpp =================================================================== --- llvm/tools/llvm-mca/llvm-mca.cpp +++ llvm/tools/llvm-mca/llvm-mca.cpp @@ -96,6 +96,11 @@ cl::desc("Additional target features."), cl::cat(ToolOptions)); +static cl::opt + PrintJson("json", + cl::desc("Print the output in json format"), + cl::cat(ToolOptions), cl::init(false)); + static cl::opt OutputAsmVariant("output-asm-variant", cl::desc("Syntax variant to use for output printing"), @@ -501,7 +506,7 @@ auto P = std::make_unique(); P->appendStage(std::make_unique(S)); P->appendStage(std::make_unique(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 @@ // 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(*STI, *IP, Insts, MCPU)); if (PrintSummaryView) Printer.addView(