1//===--------------------- ResourcePressureView.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 methods in the ResourcePressureView interface.
11///
12//===----------------------------------------------------------------------===//
13
14#include "Views/ResourcePressureView.h"
15#include "llvm/Support/FormattedStream.h"
16#include "llvm/Support/raw_ostream.h"
17
18namespace llvm {
19namespace mca {
20
21ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti,
22 MCInstPrinter &Printer,
23 ArrayRef<MCInst> S)
24 : InstructionView(sti, Printer, S), LastInstructionIdx(0) {
25 // Populate the map of resource descriptors.
26 unsigned R2VIndex = 0;
27 const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
28 for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
29 const MCProcResourceDesc &ProcResource = *SM.getProcResource(ProcResourceIdx: I);
30 unsigned NumUnits = ProcResource.NumUnits;
31 // Skip groups and invalid resources with zero units.
32 if (ProcResource.SubUnitsIdxBegin || !NumUnits)
33 continue;
34
35 Resource2VecIndex.insert(KV: std::pair<unsigned, unsigned>(I, R2VIndex));
36 R2VIndex += ProcResource.NumUnits;
37 }
38
39 NumResourceUnits = R2VIndex;
40 ResourceUsage.resize(new_size: getSource().size());
41
42 ResourceReleaseAtCycles InitValue{.ResourceIdx: 0, .Cycles: 0};
43 auto Generator = [&InitValue]() {
44 ResourceReleaseAtCycles Old = InitValue;
45 ++InitValue.ResourceIdx;
46 return Old;
47 };
48 std::generate_n(first: std::back_inserter(x&: CommonResourceUsage), n: NumResourceUnits,
49 gen: Generator);
50}
51
52void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {
53 if (Event.Type == HWInstructionEvent::Dispatched) {
54 LastInstructionIdx = Event.IR.getSourceIndex();
55 return;
56 }
57
58 // We're only interested in Issue events.
59 if (Event.Type != HWInstructionEvent::Issued)
60 return;
61
62 const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);
63 ArrayRef<llvm::MCInst> Source = getSource();
64 const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
65 for (const std::pair<ResourceRef, ReleaseAtCycles> &Use :
66 IssueEvent.UsedResources) {
67 const ResourceRef &RR = Use.first;
68 assert(Resource2VecIndex.contains(RR.first));
69 unsigned R2VIndex = Resource2VecIndex[RR.first];
70 R2VIndex += llvm::countr_zero(Val: RR.second);
71
72 InstResourceUsage &RU = ResourceUsage[SourceIdx];
73 ResourceReleaseAtCycles NewUsage{.ResourceIdx: R2VIndex, .Cycles: Use.second};
74 auto ResCyclesIt =
75 lower_bound(Range&: RU, Value&: NewUsage, C: [](const auto &L, const auto &R) {
76 return L.ResourceIdx < R.ResourceIdx;
77 });
78 if (ResCyclesIt != RU.end() && ResCyclesIt->ResourceIdx == R2VIndex)
79 ResCyclesIt->Cycles += NewUsage.Cycles;
80 else
81 RU.insert(position: ResCyclesIt, x: std::move(NewUsage));
82
83 CommonResourceUsage[R2VIndex].Cycles += NewUsage.Cycles;
84 }
85}
86
87static void printColumnNames(formatted_raw_ostream &OS,
88 const MCSchedModel &SM) {
89 unsigned Column = OS.getColumn();
90 for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
91 I < E; ++I) {
92 const MCProcResourceDesc &ProcResource = *SM.getProcResource(ProcResourceIdx: I);
93 unsigned NumUnits = ProcResource.NumUnits;
94 // Skip groups and invalid resources with zero units.
95 if (ProcResource.SubUnitsIdxBegin || !NumUnits)
96 continue;
97
98 for (unsigned J = 0; J < NumUnits; ++J) {
99 Column += 7;
100 OS << "[" << ResourceIndex;
101 if (NumUnits > 1)
102 OS << '.' << J;
103 OS << ']';
104 OS.PadToColumn(NewCol: Column);
105 }
106
107 ResourceIndex++;
108 }
109}
110
111static void printResourcePressure(formatted_raw_ostream &OS, double Pressure,
112 unsigned Col) {
113 if (!Pressure || Pressure < 0.005) {
114 OS << " - ";
115 } else {
116 // Round to the value to the nearest hundredth and then print it.
117 OS << format(Fmt: "%.2f", Vals: floor(x: (Pressure * 100) + 0.5) / 100);
118 }
119 OS.PadToColumn(NewCol: Col);
120}
121
122void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {
123 std::string Buffer;
124 raw_string_ostream TempStream(Buffer);
125 formatted_raw_ostream FOS(TempStream);
126
127 FOS << "\n\nResources:\n";
128 const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
129 for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
130 I < E; ++I) {
131 const MCProcResourceDesc &ProcResource = *SM.getProcResource(ProcResourceIdx: I);
132 unsigned NumUnits = ProcResource.NumUnits;
133 // Skip groups and invalid resources with zero units.
134 if (ProcResource.SubUnitsIdxBegin || !NumUnits)
135 continue;
136
137 for (unsigned J = 0; J < NumUnits; ++J) {
138 FOS << '[' << ResourceIndex;
139 if (NumUnits > 1)
140 FOS << '.' << J;
141 FOS << ']';
142 FOS.PadToColumn(NewCol: 6);
143 FOS << "- " << ProcResource.Name << '\n';
144 }
145
146 ResourceIndex++;
147 }
148
149 FOS << "\n\nResource pressure per iteration:\n";
150 FOS.flush();
151 printColumnNames(OS&: FOS, SM);
152 FOS << '\n';
153 FOS.flush();
154
155 ArrayRef<llvm::MCInst> Source = getSource();
156 const unsigned Executions = LastInstructionIdx / Source.size() + 1;
157 auto UsageEntryEnd = CommonResourceUsage.end();
158 auto UsageEntryIt = CommonResourceUsage.begin();
159 for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) {
160 double Pressure = 0.0;
161 if (UsageEntryIt != UsageEntryEnd && UsageEntryIt->ResourceIdx == I) {
162 Pressure = UsageEntryIt->Cycles / Executions;
163 ++UsageEntryIt;
164 }
165 printResourcePressure(OS&: FOS, Pressure, Col: (I + 1) * 7);
166 }
167 assert(UsageEntryIt == UsageEntryEnd);
168
169 FOS.flush();
170 OS << Buffer;
171}
172
173void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
174 std::string Buffer;
175 raw_string_ostream TempStream(Buffer);
176 formatted_raw_ostream FOS(TempStream);
177
178 FOS << "\n\nResource pressure by instruction:\n";
179 printColumnNames(OS&: FOS, SM: getSubTargetInfo().getSchedModel());
180 FOS << "Instructions:\n";
181
182 unsigned InstrIndex = 0;
183 ArrayRef<llvm::MCInst> Source = getSource();
184 const unsigned Executions = LastInstructionIdx / Source.size() + 1;
185 for (const MCInst &MCI : Source) {
186 auto UsageEntryEnd = ResourceUsage[InstrIndex].end();
187 auto UsageEntryIt = ResourceUsage[InstrIndex].begin();
188 for (unsigned J = 0; J < NumResourceUnits; ++J) {
189 double Pressure = 0.0;
190 if (UsageEntryIt != UsageEntryEnd && UsageEntryIt->ResourceIdx == J) {
191 Pressure = UsageEntryIt->Cycles / Executions;
192 ++UsageEntryIt;
193 }
194 printResourcePressure(OS&: FOS, Pressure, Col: (J + 1) * 7);
195 }
196 assert(UsageEntryIt == UsageEntryEnd);
197
198 FOS << printInstructionString(MCI) << '\n';
199 FOS.flush();
200 OS << Buffer;
201 Buffer = "";
202
203 ++InstrIndex;
204 }
205}
206
207json::Value ResourcePressureView::toJSON() const {
208 // We're dumping the instructions and the ResourceUsage array.
209 json::Array ResourcePressureInfo;
210
211 // The ResourceUsage matrix is sparse, so we only consider
212 // non-zero values.
213 ArrayRef<llvm::MCInst> Source = getSource();
214 const unsigned Executions = LastInstructionIdx / Source.size() + 1;
215
216 auto AddToJSON = [&ResourcePressureInfo, Executions](
217 const ResourceReleaseAtCycles &RU, unsigned InstIndex) {
218 assert(RU.Cycles.getNumerator() != 0);
219 double Usage = RU.Cycles / Executions;
220 ResourcePressureInfo.push_back(
221 E: json::Object({{.K: "InstructionIndex", .V: InstIndex},
222 {.K: "ResourceIndex", .V: RU.ResourceIdx},
223 {.K: "ResourceUsage", .V: Usage}}));
224 };
225 for (const auto &[InstIndex, Usages] : enumerate(First: ResourceUsage))
226 for (const auto &RU : Usages)
227 AddToJSON(RU, InstIndex);
228 for (const auto &RU : CommonResourceUsage) {
229 if (RU.Cycles.getNumerator() != 0)
230 AddToJSON(RU, Source.size());
231 }
232
233 json::Object JO({{.K: "ResourcePressureInfo", .V: std::move(ResourcePressureInfo)}});
234 return JO;
235}
236} // namespace mca
237} // namespace llvm
238