Skip to content

Commit 885ba90

Browse files
[mca] New option -scheduling-info
Outputs micro ops, latency, bypass latency, throughput, llvm opcode name, used resources and parsed assembly instruction with comments. This option is used to compare scheduling info from micro architecture documents. Reference scheduling information (from Architecture and micro architecture) are in comment section after each instruction (// or /* */). These information may be generated from Architecture Description Language. By this way, it is easy to compare information from llvm and from documentation/ADL. LLVM Opcode name help to find right instruction regexp to fix in Target Scheduling Info specification. Example: Input: abs D20, D11 // ABS <V><d>, <V><n> \\ ASIMD arith, basic \\ 1 2 2 4.0 V1UnitV Output: 1 | 2 | 2 | 4.00 | V1UnitV | ABSv1i64 | abs d20, d11 // ABS <V><d>, <V><n> \\ ASIMD arith, basic \\ 1 2 2 4.0 V1UnitV
1 parent a058741 commit 885ba90

File tree

9 files changed

+7985
-11
lines changed

9 files changed

+7985
-11
lines changed

llvm/docs/CommandGuide/llvm-mca.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,20 @@ option specifies "``-``", then the output will also be sent to standard output.
170170
Enable extra scheduler statistics. This view collects and analyzes instruction
171171
issue events. This view is disabled by default.
172172

173+
.. option:: -scheduling-info
174+
175+
Enable scheduling info view. This view reports scheduling information defined
176+
in LLVM target description in the form:
177+
uOps | Latency | Bypass Latency | Throughput | LLVM OpcodeName | Resources
178+
units | assembly instruction and its comment (// or /* */) if defined.
179+
It allows to compare scheduling info with architecture documents and fix them
180+
in target description by fixing InstrRW for the reported LLVM opcode.
181+
Scheduling information can be defined in the same order in each instruction
182+
comments to check easily reported and reference scheduling information.
183+
Suggested information in comment:
184+
``// <architecture instruction form> \\ <scheduling documentation title> \\
185+
<uOps>, <Latency>, <Bypass Latency>, <Throughput>, <Resources units>``
186+
173187
.. option:: -retire-stats
174188

175189
Enable extra retire control unit statistics. This view is disabled by default.

llvm/include/llvm/MC/MCSchedule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,10 @@ struct MCSchedModel {
402402
static unsigned getForwardingDelayCycles(ArrayRef<MCReadAdvanceEntry> Entries,
403403
unsigned WriteResourceIdx = 0);
404404

405+
/// Returns the maximum forwarding delay for maximum write latency.
406+
static unsigned getForwardingDelayCycles(const MCSubtargetInfo &STI,
407+
const MCSchedClassDesc &SCDesc);
408+
405409
/// Returns the default initialized model.
406410
static const MCSchedModel Default;
407411
};

llvm/lib/MC/MCSchedule.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,40 @@ MCSchedModel::getForwardingDelayCycles(ArrayRef<MCReadAdvanceEntry> Entries,
174174

175175
return std::abs(DelayCycles);
176176
}
177+
178+
unsigned
179+
MCSchedModel::getForwardingDelayCycles(const MCSubtargetInfo &STI,
180+
const MCSchedClassDesc &SCDesc) {
181+
182+
ArrayRef<MCReadAdvanceEntry> Entries = STI.getReadAdvanceEntries(SCDesc);
183+
if (Entries.empty())
184+
return 0;
185+
186+
unsigned Latency = 0;
187+
unsigned maxLatency = 0;
188+
unsigned WriteResourceID = 0;
189+
unsigned DefEnd = SCDesc.NumWriteLatencyEntries;
190+
191+
for (unsigned DefIdx = 0; DefIdx != DefEnd; ++DefIdx) {
192+
// Lookup the definition's write latency in SubtargetInfo.
193+
const MCWriteLatencyEntry *WLEntry =
194+
STI.getWriteLatencyEntry(&SCDesc, DefIdx);
195+
// Early exit if we found an invalid latency.
196+
// Consider no bypass
197+
if (WLEntry->Cycles < 0)
198+
return 0;
199+
maxLatency = std::max(Latency, static_cast<unsigned>(WLEntry->Cycles));
200+
if (maxLatency > Latency) {
201+
WriteResourceID = WLEntry->WriteResourceID;
202+
}
203+
Latency = maxLatency;
204+
}
205+
206+
for (const MCReadAdvanceEntry &E : Entries) {
207+
if (E.WriteResourceID == WriteResourceID) {
208+
return E.Cycles;
209+
}
210+
}
211+
212+
llvm_unreachable("WriteResourceID not found in MCReadAdvanceEntry entries");
213+
}

llvm/test/tools/llvm-mca/AArch64/Neoverse/V1-scheduling-info.s

Lines changed: 7588 additions & 0 deletions
Large diffs are not rendered by default.

llvm/tools/llvm-mca/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_llvm_tool(llvm-mca
1919
Views/BottleneckAnalysis.cpp
2020
Views/DispatchStatistics.cpp
2121
Views/InstructionInfoView.cpp
22+
Views/SchedulingInfoView.cpp
2223
Views/InstructionView.cpp
2324
Views/RegisterFileStatistics.cpp
2425
Views/ResourcePressureView.cpp

llvm/tools/llvm-mca/Views/InstructionInfoView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class InstructionInfoView : public InstructionView {
6666
struct InstructionInfoViewData {
6767
unsigned NumMicroOpcodes = 0;
6868
unsigned Latency = 0;
69+
unsigned Advance = 0; // ReadAvance Bypasses cycles
6970
std::optional<double> RThroughput = 0.0;
7071
bool mayLoad = false;
7172
bool mayStore = false;
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
//===--------------------- SchedulingInfoView.cpp --------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
///
10+
/// This file implements the SchedulingInfoView API.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "Views/SchedulingInfoView.h"
15+
#include "llvm/Support/FormattedStream.h"
16+
#include "llvm/Support/JSON.h"
17+
18+
namespace llvm {
19+
namespace mca {
20+
21+
void SchedulingInfoView::getComment(const MCInst &MCI,
22+
std::string &CommentString) const {
23+
StringRef s = MCI.getLoc().getPointer();
24+
std::string InstrStr;
25+
size_t pos = 0, pos_cmt = 0;
26+
27+
// Recognized comments are after assembly instructions on the same line.
28+
// It is usefull to add in comment scheduling information from architecture
29+
// specification.
30+
// '#' comment mark is not supported by llvm-mca
31+
32+
CommentString = "";
33+
if ((pos = s.find("\n")) != std::string::npos) {
34+
InstrStr = s.substr(0, pos);
35+
// C style comment
36+
if (((pos_cmt = InstrStr.find("/*")) != std::string::npos) &&
37+
((pos = InstrStr.find("*/")) != std::string::npos)) {
38+
CommentString = InstrStr.substr(pos_cmt, pos);
39+
return;
40+
}
41+
// C++ style comment
42+
if ((pos_cmt = InstrStr.find("//")) != std::string::npos) {
43+
CommentString = InstrStr.substr(pos_cmt, pos);
44+
return;
45+
}
46+
}
47+
return;
48+
}
49+
50+
void SchedulingInfoView::printView(raw_ostream &OS) const {
51+
std::string Buffer;
52+
std::string CommentString;
53+
raw_string_ostream TempStream(Buffer);
54+
formatted_raw_ostream FOS(TempStream);
55+
56+
ArrayRef<MCInst> Source = getSource();
57+
if (!Source.size())
58+
return;
59+
60+
IIVDVec IIVD(Source.size());
61+
collectData(IIVD);
62+
63+
FOS << "\n\nResources:\n";
64+
const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
65+
for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
66+
I < E; ++I) {
67+
const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
68+
unsigned NumUnits = ProcResource.NumUnits;
69+
// Skip invalid resources with zero units.
70+
if (!NumUnits)
71+
continue;
72+
73+
FOS << '[' << ResourceIndex << ']';
74+
FOS.PadToColumn(6);
75+
FOS << "- " << ProcResource.Name << ':' << NumUnits;
76+
if (ProcResource.SubUnitsIdxBegin) {
77+
FOS.PadToColumn(20);
78+
for (unsigned U = 0; U < NumUnits; ++U) {
79+
FOS << SM.getProcResource(ProcResource.SubUnitsIdxBegin[U])->Name;
80+
if ((U + 1) < NumUnits) {
81+
FOS << ", ";
82+
}
83+
}
84+
}
85+
FOS << '\n';
86+
ResourceIndex++;
87+
}
88+
89+
FOS << "\n\nScheduling Info:\n";
90+
FOS << "[1]: #uOps\n[2]: Latency\n[3]: Bypass Latency\n"
91+
<< "[4]: Throughput\n[5]: Resources\n"
92+
<< "[6]: LLVM OpcodeName\n[7]: Instruction\n"
93+
<< "[8]: Comment if any\n";
94+
95+
// paddings for each scheduling info output. Start at [2]
96+
std::vector<unsigned> paddings = {7, 12, 18, 27, 94, 113, 150};
97+
for (unsigned i = 0; i < paddings.size(); i++) {
98+
FOS << " [" << i + 1 << "]";
99+
FOS.PadToColumn(paddings[i]);
100+
}
101+
FOS << "[" << paddings.size() + 1 << "]\n";
102+
103+
for (const auto &[Index, IIVDEntry, Inst] : enumerate(IIVD, Source)) {
104+
getComment(Inst, CommentString);
105+
106+
FOS << " " << IIVDEntry.NumMicroOpcodes;
107+
FOS.PadToColumn(paddings[0]);
108+
FOS << "| " << IIVDEntry.Latency;
109+
FOS.PadToColumn(paddings[1]);
110+
FOS << "| " << IIVDEntry.Bypass;
111+
FOS.PadToColumn(paddings[2]);
112+
if (IIVDEntry.Throughput) {
113+
double RT = *IIVDEntry.Throughput;
114+
FOS << "| " << format("%.2f", RT);
115+
} else {
116+
FOS << "| -";
117+
}
118+
FOS.PadToColumn(paddings[3]);
119+
FOS << "| " << IIVDEntry.Resources;
120+
FOS.PadToColumn(paddings[4]);
121+
FOS << "| " << IIVDEntry.OpcodeName;
122+
FOS.PadToColumn(paddings[5]);
123+
FOS << "| " << printInstructionString(Inst);
124+
FOS.PadToColumn(paddings[6]);
125+
FOS << ' ' << CommentString << '\n';
126+
}
127+
128+
FOS.flush();
129+
OS << Buffer;
130+
}
131+
132+
void SchedulingInfoView::collectData(
133+
MutableArrayRef<SchedulingInfoViewData> IIVD) const {
134+
const MCSubtargetInfo &STI = getSubTargetInfo();
135+
const MCSchedModel &SM = STI.getSchedModel();
136+
for (const auto &[Inst, IIVDEntry] : zip(getSource(), IIVD)) {
137+
const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());
138+
139+
// Obtain the scheduling class information from the instruction
140+
// and instruments.
141+
auto IVecIt = InstToInstruments.find(&Inst);
142+
unsigned SchedClassID =
143+
IVecIt == InstToInstruments.end()
144+
? MCDesc.getSchedClass()
145+
: IM.getSchedClassID(MCII, Inst, IVecIt->second);
146+
unsigned CPUID = SM.getProcessorID();
147+
148+
// Try to solve variant scheduling classes.
149+
while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
150+
SchedClassID =
151+
STI.resolveVariantSchedClass(SchedClassID, &Inst, &MCII, CPUID);
152+
153+
const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
154+
IIVDEntry.OpcodeName = (std::string)MCII.getName(Inst.getOpcode());
155+
IIVDEntry.NumMicroOpcodes = SCDesc.NumMicroOps;
156+
IIVDEntry.Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
157+
IIVDEntry.Bypass =
158+
IIVDEntry.Latency - MCSchedModel::getForwardingDelayCycles(STI, SCDesc);
159+
IIVDEntry.Throughput =
160+
1.0 / MCSchedModel::getReciprocalThroughput(STI, SCDesc);
161+
raw_string_ostream TempStream(IIVDEntry.Resources);
162+
163+
const MCWriteProcResEntry *Index = STI.getWriteProcResBegin(&SCDesc);
164+
const MCWriteProcResEntry *Last = STI.getWriteProcResEnd(&SCDesc);
165+
auto sep = "";
166+
for (; Index != Last; ++Index) {
167+
if (!Index->ReleaseAtCycle)
168+
continue;
169+
const MCProcResourceDesc *MCProc =
170+
SM.getProcResource(Index->ProcResourceIdx);
171+
if (Index->ReleaseAtCycle != 1) {
172+
// Output ReleaseAtCycle between [] if not 1 (default)
173+
TempStream << sep
174+
<< format("%s[%d]", MCProc->Name, Index->ReleaseAtCycle);
175+
} else {
176+
TempStream << sep << format("%s", MCProc->Name);
177+
}
178+
sep = ", ";
179+
}
180+
TempStream.flush();
181+
}
182+
}
183+
184+
// Construct a JSON object from a single SchedulingInfoViewData object.
185+
json::Object
186+
SchedulingInfoView::toJSON(const SchedulingInfoViewData &IIVD) const {
187+
json::Object JO({{"NumMicroOpcodes", IIVD.NumMicroOpcodes},
188+
{"Latency", IIVD.Latency},
189+
{"LatencyWithBypass", IIVD.Bypass},
190+
{"Throughput", IIVD.Throughput.value_or(0.0)}});
191+
return JO;
192+
}
193+
194+
json::Value SchedulingInfoView::toJSON() const {
195+
ArrayRef<MCInst> Source = getSource();
196+
if (!Source.size())
197+
return json::Value(nullptr);
198+
199+
IIVDVec IIVD(Source.size());
200+
collectData(IIVD);
201+
202+
json::Array InstInfo;
203+
for (const auto &I : enumerate(IIVD)) {
204+
const SchedulingInfoViewData &IIVDEntry = I.value();
205+
json::Object JO = toJSON(IIVDEntry);
206+
JO.try_emplace("Instruction", (unsigned)I.index());
207+
InstInfo.push_back(std::move(JO));
208+
}
209+
return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}});
210+
}
211+
} // namespace mca.
212+
} // namespace llvm
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===--------------------- SchedulingInfoView.h ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
///
10+
/// This file implements the instruction scheduling info view.
11+
///
12+
/// The goal fo the instruction scheduling info view is to print the latency,
13+
/// latency with bypass,
14+
// throughput, pipeline ressources and uOps information for every instruction
15+
// in the input sequence.
16+
///
17+
/// Example:
18+
///
19+
/// Instruction Info:
20+
/// [1]: #uOps
21+
/// [2]: Latency
22+
/// [3]: Bypass latency
23+
/// [3]: Throughput
24+
/// [4]: Resources
25+
///
26+
/// [1] [2] [3] [4] [5] [6] Instructions:
27+
/// 1 4 1 vmulps %xmm0, %xmm1, %xmm2
28+
/// 1 3 1.00 vhaddps %xmm2, %xmm2, %xmm3
29+
/// 1 3 1.00 vhaddps %xmm3, %xmm3, %xmm4
30+
//
31+
//===----------------------------------------------------------------------===//
32+
33+
#ifndef LLVM_TOOLS_LLVM_MCA_SCHEDULINGINFOVIEW_H
34+
#define LLVM_TOOLS_LLVM_MCA_SCHEDULINGINFOVIEW_H
35+
36+
#include "Views/InstructionView.h"
37+
#include "llvm/ADT/ArrayRef.h"
38+
#include "llvm/ADT/SmallVector.h"
39+
#include "llvm/MC/MCInst.h"
40+
#include "llvm/MC/MCInstPrinter.h"
41+
#include "llvm/MC/MCInstrInfo.h"
42+
#include "llvm/MC/MCSubtargetInfo.h"
43+
#include "llvm/MCA/CodeEmitter.h"
44+
#include "llvm/MCA/CustomBehaviour.h"
45+
#include "llvm/Support/raw_ostream.h"
46+
47+
#define DEBUG_TYPE "llvm-mca"
48+
49+
namespace llvm {
50+
namespace mca {
51+
52+
/// A view that prints out generic instruction information.
53+
class SchedulingInfoView : public InstructionView {
54+
const llvm::MCInstrInfo &MCII;
55+
CodeEmitter &CE;
56+
using UniqueInst = std::unique_ptr<Instruction>;
57+
ArrayRef<UniqueInst> LoweredInsts;
58+
const InstrumentManager &IM;
59+
using InstToInstrumentsT =
60+
DenseMap<const MCInst *, SmallVector<mca::Instrument *>>;
61+
const InstToInstrumentsT &InstToInstruments;
62+
63+
struct SchedulingInfoViewData {
64+
unsigned NumMicroOpcodes = 0;
65+
unsigned Latency = 0;
66+
unsigned Bypass = 0; // ReadAvance Bypasses cycles
67+
std::optional<double> Throughput = 0.0;
68+
std::string OpcodeName = "";
69+
std::string Resources = "";
70+
};
71+
using IIVDVec = SmallVector<SchedulingInfoViewData, 16>;
72+
73+
/// Place the data into the array of SchedulingInfoViewData IIVD.
74+
void collectData(MutableArrayRef<SchedulingInfoViewData> IIVD) const;
75+
76+
public:
77+
SchedulingInfoView(const llvm::MCSubtargetInfo &ST,
78+
const llvm::MCInstrInfo &II, CodeEmitter &C,
79+
llvm::ArrayRef<llvm::MCInst> S, llvm::MCInstPrinter &IP,
80+
ArrayRef<UniqueInst> LoweredInsts,
81+
const InstrumentManager &IM,
82+
const InstToInstrumentsT &InstToInstruments)
83+
: InstructionView(ST, IP, S), MCII(II), CE(C), LoweredInsts(LoweredInsts),
84+
IM(IM), InstToInstruments(InstToInstruments) {}
85+
86+
/// Extract comment (//, /* */) from the source assembly placed just after
87+
/// instruction.
88+
void getComment(const llvm::MCInst &Inst, std::string &CommentString) const;
89+
void printView(llvm::raw_ostream &OS) const override;
90+
StringRef getNameAsString() const override { return "SchedulingInfoView"; }
91+
json::Value toJSON() const override;
92+
json::Object toJSON(const SchedulingInfoViewData &IIVD) const;
93+
};
94+
} // namespace mca
95+
} // namespace llvm
96+
97+
#endif

0 commit comments

Comments
 (0)