Skip to content

[MCA] New option to report scheduling information: -scheduling-info #126703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions llvm/docs/CommandGuide/llvm-mca.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,20 @@ option specifies "``-``", then the output will also be sent to standard output.
Enable extra scheduler statistics. This view collects and analyzes instruction
issue events. This view is disabled by default.

.. option:: -scheduling-info

Enable scheduling info view. This view reports scheduling information defined
in LLVM target description in the form:
uOps | Latency | Bypass Latency | Throughput | LLVM OpcodeName | Resources
units | assembly instruction and its comment (// or /* */) if defined.
It allows to compare scheduling info with architecture documents and fix them
in target description by fixing InstrRW for the reported LLVM opcode.
Scheduling information can be defined in the same order in each instruction
comments to check easily reported and reference scheduling information.
Suggested information in comment:
``// <architecture instruction form> \\ <scheduling documentation title> \\
<uOps>, <Latency>, <Bypass Latency>, <Throughput>, <Resources units>``

.. option:: -retire-stats

Enable extra retire control unit statistics. This view is disabled by default.
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/MC/MCSchedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,10 @@ struct MCSchedModel {
static unsigned getForwardingDelayCycles(ArrayRef<MCReadAdvanceEntry> Entries,
unsigned WriteResourceIdx = 0);

/// Returns the bypass delay cycle for the maximum latency write cycle
static unsigned getBypassDelayCycles(const MCSubtargetInfo &STI,
const MCSchedClassDesc &SCDesc);

/// Returns the default initialized model.
static const MCSchedModel Default;
};
Expand Down
35 changes: 35 additions & 0 deletions llvm/lib/MC/MCSchedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,38 @@ MCSchedModel::getForwardingDelayCycles(ArrayRef<MCReadAdvanceEntry> Entries,

return std::abs(DelayCycles);
}

unsigned MCSchedModel::getBypassDelayCycles(const MCSubtargetInfo &STI,
const MCSchedClassDesc &SCDesc) {

ArrayRef<MCReadAdvanceEntry> Entries = STI.getReadAdvanceEntries(SCDesc);
if (Entries.empty())
return 0;

unsigned Latency = 0;
unsigned MaxLatency = 0;
unsigned WriteResourceID = 0;
unsigned DefEnd = SCDesc.NumWriteLatencyEntries;

for (unsigned DefIdx = 0; DefIdx != DefEnd; ++DefIdx) {
// Lookup the definition's write latency in SubtargetInfo.
const MCWriteLatencyEntry *WLEntry =
STI.getWriteLatencyEntry(&SCDesc, DefIdx);
unsigned Cycles = (unsigned)WLEntry->Cycles;
// Invalid latency. Consider 0 cycle latency
if (WLEntry->Cycles < 0)
Cycles = 0;
if (Cycles > Latency) {
MaxLatency = Cycles;
WriteResourceID = WLEntry->WriteResourceID;
}
Latency = MaxLatency;
}

for (const MCReadAdvanceEntry &E : Entries) {
if (E.WriteResourceID == WriteResourceID)
return E.Cycles;
}

llvm_unreachable("WriteResourceID not found in MCReadAdvanceEntry entries");
}
7,588 changes: 7,588 additions & 0 deletions llvm/test/tools/llvm-mca/AArch64/Neoverse/V1-scheduling-info.s

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions llvm/tools/llvm-mca/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_llvm_tool(llvm-mca
Views/BottleneckAnalysis.cpp
Views/DispatchStatistics.cpp
Views/InstructionInfoView.cpp
Views/SchedulingInfoView.cpp
Views/InstructionView.cpp
Views/RegisterFileStatistics.cpp
Views/ResourcePressureView.cpp
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-mca/Views/InstructionInfoView.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class InstructionInfoView : public InstructionView {
struct InstructionInfoViewData {
unsigned NumMicroOpcodes = 0;
unsigned Latency = 0;
unsigned Advance = 0; // ReadAvance Bypasses cycles
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please improve this comment

std::optional<double> RThroughput = 0.0;
bool mayLoad = false;
bool mayStore = false;
Expand Down
218 changes: 218 additions & 0 deletions llvm/tools/llvm-mca/Views/SchedulingInfoView.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
//===--------------------- SchedulingInfoView.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 implements the SchedulingInfoView API.
///
//===----------------------------------------------------------------------===//

#include "Views/SchedulingInfoView.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/JSON.h"

namespace llvm {
namespace mca {

void SchedulingInfoView::getComment(const MCInst &MCI,
std::string &CommentString) const {
StringRef s = MCI.getLoc().getPointer();
std::string InstrStr;
size_t pos = 0, pos_cmt = 0;

// Recognized comments are after assembly instructions on the same line.
// It is usefull to add in comment scheduling information from architecture
// specification.
// '#' comment mark is not supported by llvm-mca

CommentString = "";
if ((pos = s.find("\n")) != std::string::npos) {
InstrStr = s.substr(0, pos);
// C style comment
if (((pos_cmt = InstrStr.find("/*")) != std::string::npos) &&
((pos = InstrStr.find("*/")) != std::string::npos)) {
CommentString = InstrStr.substr(pos_cmt, pos);
return;
}
// C++ style comment
if ((pos_cmt = InstrStr.find("//")) != std::string::npos) {
CommentString = InstrStr.substr(pos_cmt, pos);
return;
}
}
return;
}

void SchedulingInfoView::printView(raw_ostream &OS) const {
std::string Buffer;
std::string CommentString;
raw_string_ostream TempStream(Buffer);
formatted_raw_ostream FOS(TempStream);

ArrayRef<MCInst> Source = getSource();
if (!Source.size())
return;

IIVDVec IIVD(Source.size());
collectData(IIVD);

FOS << "\n\nResources:\n";
const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
I < E; ++I) {
const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
unsigned NumUnits = ProcResource.NumUnits;
// Skip invalid resources with zero units.
if (!NumUnits)
continue;

FOS << '[' << ResourceIndex << ']';
FOS.PadToColumn(6);
FOS << "- " << ProcResource.Name << ':' << NumUnits;
if (ProcResource.SubUnitsIdxBegin) {
FOS.PadToColumn(20);
for (unsigned U = 0; U < NumUnits; ++U) {
FOS << SM.getProcResource(ProcResource.SubUnitsIdxBegin[U])->Name;
if ((U + 1) < NumUnits) {
FOS << ", ";
}
}
}
FOS << '\n';
ResourceIndex++;
}

FOS << "\n\nScheduling Info:\n";
FOS << "[1]: #uOps\n[2]: Latency\n[3]: Bypass Latency\n"
<< "[4]: Throughput\n[5]: Resources\n"
<< "[6]: LLVM OpcodeName\n[7]: Instruction\n"
<< "[8]: Comment if any\n";

// paddings for each scheduling info output. Start at [2]
std::vector<unsigned> paddings = {7, 12, 18, 27, 94, 113, 150};
for (unsigned i = 0; i < paddings.size(); i++) {
FOS << " [" << i + 1 << "]";
FOS.PadToColumn(paddings[i]);
}
FOS << "[" << paddings.size() + 1 << "]\n";

for (const auto &[Index, IIVDEntry, Inst] : enumerate(IIVD, Source)) {
getComment(Inst, CommentString);

FOS << " " << IIVDEntry.NumMicroOpcodes;
FOS.PadToColumn(paddings[0]);
FOS << "| " << IIVDEntry.Latency;
FOS.PadToColumn(paddings[1]);
FOS << "| " << IIVDEntry.Bypass;
FOS.PadToColumn(paddings[2]);
if (IIVDEntry.Throughput) {
double RT = *IIVDEntry.Throughput;
FOS << "| " << format("%.2f", RT);
} else {
FOS << "| -";
}
FOS.PadToColumn(paddings[3]);
FOS << "| " << IIVDEntry.Resources;
FOS.PadToColumn(paddings[4]);
FOS << "| " << IIVDEntry.OpcodeName;
FOS.PadToColumn(paddings[5]);
FOS << "| " << printInstructionString(Inst);
FOS.PadToColumn(paddings[6]);
FOS << ' ' << CommentString << '\n';
}

FOS.flush();
OS << Buffer;
}

void SchedulingInfoView::collectData(
MutableArrayRef<SchedulingInfoViewData> IIVD) const {
const MCSubtargetInfo &STI = getSubTargetInfo();
const MCSchedModel &SM = STI.getSchedModel();
for (const auto &[Inst, IIVDEntry] : zip(getSource(), IIVD)) {
const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());

// Obtain the scheduling class information from the instruction
// and instruments.
auto IVecIt = InstToInstruments.find(&Inst);
unsigned SchedClassID =
IVecIt == InstToInstruments.end()
? MCDesc.getSchedClass()
: IM.getSchedClassID(MCII, Inst, IVecIt->second);
unsigned CPUID = SM.getProcessorID();

// Try to solve variant scheduling classes.
while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
SchedClassID =
STI.resolveVariantSchedClass(SchedClassID, &Inst, &MCII, CPUID);

const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
IIVDEntry.OpcodeName = (std::string)MCII.getName(Inst.getOpcode());
IIVDEntry.NumMicroOpcodes = SCDesc.NumMicroOps;
IIVDEntry.Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
// Add extra latency due to delays in the forwarding data paths.
IIVDEntry.Latency += MCSchedModel::getForwardingDelayCycles(
STI.getReadAdvanceEntries(SCDesc));
// Get latency with bypass
IIVDEntry.Bypass =
IIVDEntry.Latency - MCSchedModel::getBypassDelayCycles(STI, SCDesc);
IIVDEntry.Throughput =
1.0 / MCSchedModel::getReciprocalThroughput(STI, SCDesc);
raw_string_ostream TempStream(IIVDEntry.Resources);

const MCWriteProcResEntry *Index = STI.getWriteProcResBegin(&SCDesc);
const MCWriteProcResEntry *Last = STI.getWriteProcResEnd(&SCDesc);
auto sep = "";
for (; Index != Last; ++Index) {
if (!Index->ReleaseAtCycle)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does AcquireAtCycles need to be handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because the goal is to get reservation cycles to compute the throughput.
https://github.com/llvm/llvm-project/blob/main/llvm/lib/MC/MCSchedule.cpp#L97
But for now, AcquireAtCycle does not appear and cannot be checked regarding scheduling modele...

continue;
const MCProcResourceDesc *MCProc =
SM.getProcResource(Index->ProcResourceIdx);
if (Index->ReleaseAtCycle > 1) {
// Output ReleaseAtCycle between [] if not 1 (default)
// This is to be able to evaluate throughput.
// See getReciprocalThroughput in MCSchedule.cpp
// TODO: report AcquireAtCycle to check this scheduling info.
TempStream << sep << format("%s[%d]", MCProc->Name, Index->ReleaseAtCycle);
} else {
TempStream << sep << format("%s", MCProc->Name);
}
sep = ", ";
}
TempStream.flush();
}
}

// Construct a JSON object from a single SchedulingInfoViewData object.
json::Object
SchedulingInfoView::toJSON(const SchedulingInfoViewData &IIVD) const {
json::Object JO({{"NumMicroOpcodes", IIVD.NumMicroOpcodes},
{"Latency", IIVD.Latency},
{"LatencyWithBypass", IIVD.Bypass},
{"Throughput", IIVD.Throughput.value_or(0.0)}});
return JO;
}

json::Value SchedulingInfoView::toJSON() const {
ArrayRef<MCInst> Source = getSource();
if (!Source.size())
return json::Value(nullptr);

IIVDVec IIVD(Source.size());
collectData(IIVD);

json::Array InstInfo;
for (const auto &I : enumerate(IIVD)) {
const SchedulingInfoViewData &IIVDEntry = I.value();
json::Object JO = toJSON(IIVDEntry);
JO.try_emplace("Instruction", (unsigned)I.index());
InstInfo.push_back(std::move(JO));
}
return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}});
}
} // namespace mca.
} // namespace llvm
Loading
Loading