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.
#include "llvm/MC/MCInst.h"
#include "llvm/MCA/Support.h"
#include "llvm/Support/Format.h"
-#include "llvm/Support/FormattedStream.h"
namespace llvm {
namespace mca {
}
}
-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();
}
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;
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++;
}
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);
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 {
while (CurrentIID < Source.size()) {
FOS << "\n " << CurrentIID << ".";
- printInstruction(FOS, STI, MCIP, Source[CurrentIID]);
+ printInstruction(FOS, Source[CurrentIID]);
CurrentIID++;
}
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),
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);
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);
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);
if (Event.Type != HWInstructionEvent::Issued)
return;
+ ArrayRef<llvm::MCInst> Source = getSource();
const Instruction &IS = *Event.IR.getInstruction();
unsigned To = IID % Source.size();
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) {
#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 {
};
/// 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;
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;
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;
}
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();
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());
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;
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;
};
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;
}
NumResourceUnits = R2VIndex;
- ResourceUsage.resize(NumResourceUnits * (Source.size() + 1));
+ ResourceUsage.resize(NumResourceUnits * (getSource().size() + 1));
std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0);
}
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) {
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);
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];
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;
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 = "";
/// 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
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);
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);
// 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 &&
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)
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())
"[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) {
printWaitTimeEntry(FOS, TotalWaitTime, IID, Executions);
FOS << " "
<< "<total>" << '\n';
- InstrStream.flush();
+ FOS.flush();
}
}
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) {
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;
}
/// 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;
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
#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"
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