[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.
This commit is contained in:
Wolfgang Pieb 2021-01-21 14:04:13 -08:00
parent 922b26cde4
commit d38be2ba0e
25 changed files with 444 additions and 47 deletions

View File

@ -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
-----------

View File

@ -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
---------------------------------

View File

@ -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: }

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -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);

View File

@ -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 @@ public:
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); }

View File

@ -78,6 +78,7 @@ public:
printDispatchStalls(OS);
printDispatchHistogram(OS);
}
StringRef getNameAsString() const override { return "DispatchStatistics"; }
};
} // namespace mca
} // namespace llvm

View File

@ -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

View File

@ -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 @@ public:
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

View File

@ -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

View File

@ -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

View File

@ -73,6 +73,9 @@ public:
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

View File

@ -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

View File

@ -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 @@ public:
printResourcePressurePerIter(OS);
printResourcePressurePerInst(OS);
}
StringRef getNameAsString() const override { return "ResourcePressureView"; }
json::Value toJSON() const;
};
} // namespace mca
} // namespace llvm

View File

@ -52,6 +52,9 @@ public:
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

View File

@ -88,6 +88,7 @@ public:
llvm::ArrayRef<unsigned> Buffers) override;
void printView(llvm::raw_ostream &OS) const override;
StringRef getNameAsString() const override { return "SchedulerStatistics"; }
};
} // namespace mca
} // namespace llvm

View File

@ -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

View File

@ -87,8 +87,9 @@ public:
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

View File

@ -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

View File

@ -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 @@ public:
printTimeline(OS);
printAverageWaitTimes(OS);
}
StringRef getNameAsString() const override { return "TimelineView"; }
json::Value toJSON() const;
};
} // namespace mca
} // namespace llvm

View File

@ -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

View File

@ -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

View File

@ -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(