xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
18 namespace llvm {
19 namespace mca {
20 
ResourcePressureView(const llvm::MCSubtargetInfo & sti,MCInstPrinter & Printer,ArrayRef<MCInst> S)21 ResourcePressureView::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(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(std::pair<unsigned, unsigned>(I, R2VIndex));
36     R2VIndex += ProcResource.NumUnits;
37   }
38 
39   NumResourceUnits = R2VIndex;
40   ResourceUsage.resize(getSource().size());
41 
42   ResourceReleaseAtCycles InitValue{0, 0};
43   auto Generator = [&InitValue]() {
44     ResourceReleaseAtCycles Old = InitValue;
45     ++InitValue.ResourceIdx;
46     return Old;
47   };
48   std::generate_n(std::back_inserter(CommonResourceUsage), NumResourceUnits,
49                   Generator);
50 }
51 
onEvent(const HWInstructionEvent & Event)52 void 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(RR.second);
71 
72     InstResourceUsage &RU = ResourceUsage[SourceIdx];
73     ResourceReleaseAtCycles NewUsage{R2VIndex, Use.second};
74     auto ResCyclesIt =
75         lower_bound(RU, NewUsage, [](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(ResCyclesIt, std::move(NewUsage));
82 
83     CommonResourceUsage[R2VIndex].Cycles += NewUsage.Cycles;
84   }
85 }
86 
printColumnNames(formatted_raw_ostream & OS,const MCSchedModel & SM)87 static 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(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(Column);
105     }
106 
107     ResourceIndex++;
108   }
109 }
110 
printResourcePressure(formatted_raw_ostream & OS,double Pressure,unsigned Col)111 static 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("%.2f", floor((Pressure * 100) + 0.5) / 100);
118   }
119   OS.PadToColumn(Col);
120 }
121 
printResourcePressurePerIter(raw_ostream & OS) const122 void 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(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(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(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(FOS, Pressure, (I + 1) * 7);
166   }
167   assert(UsageEntryIt == UsageEntryEnd);
168 
169   FOS.flush();
170   OS << Buffer;
171 }
172 
printResourcePressurePerInst(raw_ostream & OS) const173 void 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(FOS, 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(FOS, Pressure, (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 
toJSON() const207 json::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         json::Object({{"InstructionIndex", InstIndex},
222                       {"ResourceIndex", RU.ResourceIdx},
223                       {"ResourceUsage", Usage}}));
224   };
225   for (const auto &[InstIndex, Usages] : enumerate(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({{"ResourcePressureInfo", std::move(ResourcePressureInfo)}});
234   return JO;
235 }
236 } // namespace mca
237 } // namespace llvm
238