[llvm-mca][NFC] Refactor handling of views that examine individual instructions,

including printing them.

Reviewers: andreadb, lebedev.ri

Differential Review: https://reviews.llvm.org/D86390

Introduces a new base class "InstructionView" that such views derive from.
Other views still use the "View" base class.
This commit is contained in:
Wolfgang Pieb 2020-08-21 15:52:02 -07:00
parent 4d69bcb12f
commit e02920fe55
10 changed files with 94 additions and 113 deletions

View File

@ -16,7 +16,6 @@
#include "llvm/MC/MCInst.h"
#include "llvm/MCA/Support.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormattedStream.h"
namespace llvm {
namespace mca {
@ -284,21 +283,14 @@ void DependencyGraph::getCriticalSequence(
}
}
static void printInstruction(formatted_raw_ostream &FOS,
const MCSubtargetInfo &STI, MCInstPrinter &MCIP,
const MCInst &MCI,
bool UseDifferentColor = false) {
std::string Instruction;
raw_string_ostream InstrStream(Instruction);
void BottleneckAnalysis::printInstruction(formatted_raw_ostream &FOS,
const MCInst &MCI,
bool UseDifferentColor) const {
FOS.PadToColumn(14);
MCIP.printInst(&MCI, 0, "", STI, InstrStream);
InstrStream.flush();
if (UseDifferentColor)
FOS.changeColor(raw_ostream::CYAN, true, false);
FOS << StringRef(Instruction).ltrim();
FOS << printInstructionString(MCI);
if (UseDifferentColor)
FOS.resetColor();
}
@ -316,6 +308,7 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const {
OS << "\nCritical sequence based on the simulation:\n\n";
const DependencyEdge &FirstEdge = *Seq[0];
ArrayRef<llvm::MCInst> Source = getSource();
unsigned FromIID = FirstEdge.FromIID % Source.size();
unsigned ToIID = FirstEdge.ToIID % Source.size();
bool IsLoopCarried = FromIID >= ToIID;
@ -331,17 +324,17 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const {
unsigned CurrentIID = 0;
if (IsLoopCarried) {
FOS << "\n +----< " << FromIID << ".";
printInstruction(FOS, STI, MCIP, Source[FromIID], HasColors);
printInstruction(FOS, Source[FromIID], HasColors);
FOS << "\n |\n | < loop carried > \n |";
} else {
while (CurrentIID < FromIID) {
FOS << "\n " << CurrentIID << ".";
printInstruction(FOS, STI, MCIP, Source[CurrentIID]);
printInstruction(FOS, Source[CurrentIID]);
CurrentIID++;
}
FOS << "\n +----< " << CurrentIID << ".";
printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors);
printInstruction(FOS, Source[CurrentIID], HasColors);
CurrentIID++;
}
@ -351,17 +344,17 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const {
while (CurrentIID < LastIID) {
FOS << "\n | " << CurrentIID << ".";
printInstruction(FOS, STI, MCIP, Source[CurrentIID]);
printInstruction(FOS, Source[CurrentIID]);
CurrentIID++;
}
if (CurrentIID == ToIID) {
FOS << "\n +----> " << ToIID << ".";
printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors);
printInstruction(FOS, Source[CurrentIID], HasColors);
} else {
FOS << "\n |\n | < loop carried > \n |"
<< "\n +----> " << ToIID << ".";
printInstruction(FOS, STI, MCIP, Source[ToIID], HasColors);
printInstruction(FOS, Source[ToIID], HasColors);
}
FOS.PadToColumn(58);
@ -373,7 +366,7 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const {
FOS << "## REGISTER dependency: ";
if (HasColors)
FOS.changeColor(raw_ostream::MAGENTA, true, false);
MCIP.printRegName(FOS, Dep.ResourceOrRegID);
getInstPrinter().printRegName(FOS, Dep.ResourceOrRegID);
} else if (Dep.Type == DependencyEdge::DT_MEMORY) {
FOS << "## MEMORY dependency.";
} else {
@ -397,7 +390,7 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const {
while (CurrentIID < Source.size()) {
FOS << "\n " << CurrentIID << ".";
printInstruction(FOS, STI, MCIP, Source[CurrentIID]);
printInstruction(FOS, Source[CurrentIID]);
CurrentIID++;
}
@ -451,8 +444,8 @@ void DependencyGraph::addDependency(unsigned From, unsigned To,
BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti,
MCInstPrinter &Printer,
ArrayRef<MCInst> S, unsigned NumIter)
: STI(sti), MCIP(Printer), Tracker(STI.getSchedModel()), DG(S.size() * 3),
Source(S), Iterations(NumIter), TotalCycles(0),
: InstructionView(sti, Printer, S), Tracker(sti.getSchedModel()),
DG(S.size() * 3), Iterations(NumIter), TotalCycles(0),
PressureIncreasedBecauseOfResources(false),
PressureIncreasedBecauseOfRegisterDependencies(false),
PressureIncreasedBecauseOfMemoryDependencies(false),
@ -461,7 +454,7 @@ BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti,
void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To,
unsigned RegID, unsigned Cost) {
bool IsLoopCarried = From >= To;
unsigned SourceSize = Source.size();
unsigned SourceSize = getSource().size();
if (IsLoopCarried) {
DG.addRegisterDep(From, To + SourceSize, RegID, Cost);
DG.addRegisterDep(From + SourceSize, To + (SourceSize * 2), RegID, Cost);
@ -473,7 +466,7 @@ void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To,
void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To,
unsigned Cost) {
bool IsLoopCarried = From >= To;
unsigned SourceSize = Source.size();
unsigned SourceSize = getSource().size();
if (IsLoopCarried) {
DG.addMemoryDep(From, To + SourceSize, Cost);
DG.addMemoryDep(From + SourceSize, To + (SourceSize * 2), Cost);
@ -485,7 +478,7 @@ void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To,
void BottleneckAnalysis::addResourceDep(unsigned From, unsigned To,
uint64_t Mask, unsigned Cost) {
bool IsLoopCarried = From >= To;
unsigned SourceSize = Source.size();
unsigned SourceSize = getSource().size();
if (IsLoopCarried) {
DG.addResourceDep(From, To + SourceSize, Mask, Cost);
DG.addResourceDep(From + SourceSize, To + (SourceSize * 2), Mask, Cost);
@ -508,6 +501,7 @@ void BottleneckAnalysis::onEvent(const HWInstructionEvent &Event) {
if (Event.Type != HWInstructionEvent::Issued)
return;
ArrayRef<llvm::MCInst> Source = getSource();
const Instruction &IS = *Event.IR.getInstruction();
unsigned To = IID % Source.size();
@ -617,7 +611,7 @@ void BottleneckAnalysis::printBottleneckHints(raw_ostream &OS) const {
if (BPI.PressureIncreaseCycles) {
ArrayRef<unsigned> Distribution = Tracker.getResourcePressureDistribution();
const MCSchedModel &SM = STI.getSchedModel();
const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
for (unsigned I = 0, E = Distribution.size(); I < E; ++I) {
unsigned ResourceCycles = Distribution[I];
if (ResourceCycles) {

View File

@ -87,6 +87,7 @@
#include "llvm/MC/MCSchedule.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/FormattedStream.h"
namespace llvm {
namespace mca {
@ -282,13 +283,10 @@ public:
};
/// A view that collects and prints a few performance numbers.
class BottleneckAnalysis : public View {
const MCSubtargetInfo &STI;
MCInstPrinter &MCIP;
class BottleneckAnalysis : public InstructionView {
PressureTracker Tracker;
DependencyGraph DG;
ArrayRef<MCInst> Source;
unsigned Iterations;
unsigned TotalCycles;
@ -317,6 +315,9 @@ class BottleneckAnalysis : public View {
void addMemoryDep(unsigned From, unsigned To, unsigned Cy);
void addResourceDep(unsigned From, unsigned To, uint64_t Mask, unsigned Cy);
void printInstruction(formatted_raw_ostream &FOS, const MCInst &MCI,
bool UseDifferentColor = false) const;
// Prints a bottleneck message to OS.
void printBottleneckHints(raw_ostream &OS) const;
void printCriticalSequence(raw_ostream &OS) const;

View File

@ -20,9 +20,8 @@ namespace mca {
void InstructionInfoView::printView(raw_ostream &OS) const {
std::string Buffer;
raw_string_ostream TempStream(Buffer);
std::string Instruction;
raw_string_ostream InstrStream(Instruction);
ArrayRef<llvm::MCInst> Source = getSource();
if (!Source.size())
return;
@ -82,14 +81,7 @@ void InstructionInfoView::printView(raw_ostream &OS) const {
}
const MCInst &Inst = std::get<1>(I.value());
MCIP.printInst(&Inst, 0, "", STI, InstrStream);
InstrStream.flush();
// Consume any tabs or spaces at the beginning of the string.
StringRef Str(Instruction);
Str = Str.ltrim();
TempStream << Str << '\n';
Instruction = "";
TempStream << printInstructionString(Inst) << '\n';
}
TempStream.flush();
@ -98,8 +90,9 @@ void InstructionInfoView::printView(raw_ostream &OS) const {
void InstructionInfoView::collectData(
MutableArrayRef<InstructionInfoViewData> IIVD) const {
const llvm::MCSubtargetInfo &STI = getSubTargetInfo();
const MCSchedModel &SM = STI.getSchedModel();
for (auto I : zip(Source, IIVD)) {
for (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());

View File

@ -50,13 +50,10 @@ namespace llvm {
namespace mca {
/// A view that prints out generic instruction information.
class InstructionInfoView : public View {
const llvm::MCSubtargetInfo &STI;
class InstructionInfoView : public InstructionView {
const llvm::MCInstrInfo &MCII;
CodeEmitter &CE;
bool PrintEncodings;
llvm::ArrayRef<llvm::MCInst> Source;
llvm::MCInstPrinter &MCIP;
struct InstructionInfoViewData {
unsigned NumMicroOpcodes = 0;
@ -76,8 +73,8 @@ public:
const llvm::MCInstrInfo &II, CodeEmitter &C,
bool ShouldPrintEncodings, llvm::ArrayRef<llvm::MCInst> S,
llvm::MCInstPrinter &IP)
: STI(ST), MCII(II), CE(C), PrintEncodings(ShouldPrintEncodings),
Source(S), MCIP(IP) {}
: InstructionView(ST, IP, S), MCII(II), CE(C),
PrintEncodings(ShouldPrintEncodings) {}
void printView(llvm::raw_ostream &OS) const override;
};

View File

@ -21,10 +21,10 @@ namespace mca {
ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti,
MCInstPrinter &Printer,
ArrayRef<MCInst> S)
: STI(sti), MCIP(Printer), Source(S), LastInstructionIdx(0) {
: InstructionView(sti, Printer, S), LastInstructionIdx(0) {
// Populate the map of resource descriptors.
unsigned R2VIndex = 0;
const MCSchedModel &SM = STI.getSchedModel();
const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
unsigned NumUnits = ProcResource.NumUnits;
@ -37,7 +37,7 @@ ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti,
}
NumResourceUnits = R2VIndex;
ResourceUsage.resize(NumResourceUnits * (Source.size() + 1));
ResourceUsage.resize(NumResourceUnits * (getSource().size() + 1));
std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0);
}
@ -52,6 +52,7 @@ void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {
return;
const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);
ArrayRef<llvm::MCInst> Source = getSource();
const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
for (const std::pair<ResourceRef, ResourceCycles> &Use :
IssueEvent.UsedResources) {
@ -105,7 +106,7 @@ void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {
formatted_raw_ostream FOS(TempStream);
FOS << "\n\nResources:\n";
const MCSchedModel &SM = STI.getSchedModel();
const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
I < E; ++I) {
const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
@ -132,6 +133,7 @@ void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {
FOS << '\n';
FOS.flush();
ArrayRef<llvm::MCInst> Source = getSource();
const unsigned Executions = LastInstructionIdx / Source.size() + 1;
for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) {
double Usage = ResourceUsage[I + Source.size() * E];
@ -148,13 +150,11 @@ void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
formatted_raw_ostream FOS(TempStream);
FOS << "\n\nResource pressure by instruction:\n";
printColumnNames(FOS, STI.getSchedModel());
printColumnNames(FOS, getSubTargetInfo().getSchedModel());
FOS << "Instructions:\n";
std::string Instruction;
raw_string_ostream InstrStream(Instruction);
unsigned InstrIndex = 0;
ArrayRef<llvm::MCInst> Source = getSource();
const unsigned Executions = LastInstructionIdx / Source.size() + 1;
for (const MCInst &MCI : Source) {
unsigned BaseEltIdx = InstrIndex * NumResourceUnits;
@ -163,16 +163,7 @@ void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
printResourcePressure(FOS, Usage / Executions, (J + 1) * 7);
}
MCIP.printInst(&MCI, 0, "", STI, InstrStream);
InstrStream.flush();
StringRef Str(Instruction);
// Remove any tabs or spaces at the beginning of the instruction.
Str = Str.ltrim();
FOS << Str << '\n';
Instruction = "";
FOS << printInstructionString(MCI) << '\n';
FOS.flush();
OS << Buffer;
Buffer = "";

View File

@ -69,10 +69,7 @@ namespace mca {
/// This class collects resource pressure statistics and it is able to print
/// out all the collected information as a table to an output stream.
class ResourcePressureView : public View {
const llvm::MCSubtargetInfo &STI;
llvm::MCInstPrinter &MCIP;
llvm::ArrayRef<llvm::MCInst> Source;
class ResourcePressureView : public InstructionView {
unsigned LastInstructionIdx;
// Map to quickly obtain the ResourceUsage column index from a processor

View File

@ -20,10 +20,10 @@ namespace mca {
TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer,
llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations,
unsigned Cycles)
: STI(sti), MCIP(Printer), Source(S), CurrentCycle(0),
: InstructionView(sti, Printer, S), CurrentCycle(0),
MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()),
UsedBuffer(S.size()) {
unsigned NumInstructions = Source.size();
unsigned NumInstructions = getSource().size();
assert(Iterations && "Invalid number of iterations specified!");
NumInstructions *= Iterations;
Timeline.resize(NumInstructions);
@ -40,10 +40,10 @@ TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer,
void TimelineView::onReservedBuffers(const InstRef &IR,
ArrayRef<unsigned> Buffers) {
if (IR.getSourceIndex() >= Source.size())
if (IR.getSourceIndex() >= getSource().size())
return;
const MCSchedModel &SM = STI.getSchedModel();
const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
std::pair<unsigned, int> BufferInfo = {0, -1};
for (const unsigned Buffer : Buffers) {
const MCProcResourceDesc &MCDesc = *SM.getProcResource(Buffer);
@ -70,7 +70,7 @@ void TimelineView::onEvent(const HWInstructionEvent &Event) {
// Update the WaitTime entry which corresponds to this Index.
assert(TVEntry.CycleDispatched >= 0 && "Invalid TVEntry found!");
unsigned CycleDispatched = static_cast<unsigned>(TVEntry.CycleDispatched);
WaitTimeEntry &WTEntry = WaitTime[Index % Source.size()];
WaitTimeEntry &WTEntry = WaitTime[Index % getSource().size()];
WTEntry.CyclesSpentInSchedulerQueue +=
TVEntry.CycleIssued - CycleDispatched;
assert(CycleDispatched <= TVEntry.CycleReady &&
@ -133,7 +133,7 @@ void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS,
const WaitTimeEntry &Entry,
unsigned SourceIndex,
unsigned Executions) const {
bool PrintingTotals = SourceIndex == Source.size();
bool PrintingTotals = SourceIndex == getSource().size();
unsigned CumulativeExecutions = PrintingTotals ? Timeline.size() : Executions;
if (!PrintingTotals)
@ -164,7 +164,8 @@ void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS,
OS.PadToColumn(27);
if (!PrintingTotals)
tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire,
CumulativeExecutions, STI.getSchedModel().MicroOpBufferSize);
CumulativeExecutions,
getSubTargetInfo().getSchedModel().MicroOpBufferSize);
OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10);
if (OS.has_colors())
@ -181,33 +182,19 @@ void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
"[3]: Average time elapsed from WB until retire stage\n\n"
" [0] [1] [2] [3]\n";
OS << Header;
// Use a different string stream for printing instructions.
std::string Instruction;
raw_string_ostream InstrStream(Instruction);
formatted_raw_ostream FOS(OS);
unsigned Executions = Timeline.size() / Source.size();
unsigned Executions = Timeline.size() / getSource().size();
unsigned IID = 0;
for (const MCInst &Inst : Source) {
for (const MCInst &Inst : getSource()) {
printWaitTimeEntry(FOS, WaitTime[IID], IID, Executions);
// Append the instruction info at the end of the line.
MCIP.printInst(&Inst, 0, "", STI, InstrStream);
InstrStream.flush();
// Consume any tabs or spaces at the beginning of the string.
StringRef Str(Instruction);
Str = Str.ltrim();
FOS << " " << Str << '\n';
FOS << " " << printInstructionString(Inst) << '\n';
FOS.flush();
Instruction = "";
++IID;
}
// If the timeline contains more than one instruction,
// let's also print global averages.
if (Source.size() != 1) {
if (getSource().size() != 1) {
WaitTimeEntry TotalWaitTime = std::accumulate(
WaitTime.begin(), WaitTime.end(), WaitTimeEntry{0, 0, 0},
[](const WaitTimeEntry &A, const WaitTimeEntry &B) {
@ -220,7 +207,7 @@ void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
printWaitTimeEntry(FOS, TotalWaitTime, IID, Executions);
FOS << " "
<< "<total>" << '\n';
InstrStream.flush();
FOS.flush();
}
}
@ -292,11 +279,8 @@ void TimelineView::printTimeline(raw_ostream &OS) const {
printTimelineHeader(FOS, LastCycle);
FOS.flush();
// Use a different string stream for the instruction.
std::string Instruction;
raw_string_ostream InstrStream(Instruction);
unsigned IID = 0;
ArrayRef<llvm::MCInst> Source = getSource();
const unsigned Iterations = Timeline.size() / Source.size();
for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) {
for (const MCInst &Inst : Source) {
@ -306,16 +290,8 @@ void TimelineView::printTimeline(raw_ostream &OS) const {
unsigned SourceIndex = IID % Source.size();
printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex);
// Append the instruction info at the end of the line.
MCIP.printInst(&Inst, 0, "", STI, InstrStream);
InstrStream.flush();
// Consume any tabs or spaces at the beginning of the string.
StringRef Str(Instruction);
Str = Str.ltrim();
FOS << " " << Str << '\n';
FOS << " " << printInstructionString(Inst) << '\n';
FOS.flush();
Instruction = "";
++IID;
}

View File

@ -118,11 +118,7 @@ namespace mca {
/// a TimelineViewEntry object. TimelineViewEntry objects are then used
/// to print the timeline information, as well as the "average wait times"
/// for every instruction in the input assembly sequence.
class TimelineView : public View {
const llvm::MCSubtargetInfo &STI;
llvm::MCInstPrinter &MCIP;
llvm::ArrayRef<llvm::MCInst> Source;
class TimelineView : public InstructionView {
unsigned CurrentCycle;
unsigned MaxCycle;
unsigned LastCycle;

View File

@ -17,5 +17,13 @@ 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

@ -15,6 +15,7 @@
#ifndef LLVM_TOOLS_LLVM_MCA_VIEW_H
#define LLVM_TOOLS_LLVM_MCA_VIEW_H
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MCA/HWEventListener.h"
#include "llvm/Support/raw_ostream.h"
@ -27,6 +28,33 @@ public:
virtual ~View() = default;
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