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 
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(NumResourceUnits * (getSource().size() + 1));
41   std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0);
42 }
43 
44 void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {
45   if (Event.Type == HWInstructionEvent::Dispatched) {
46     LastInstructionIdx = Event.IR.getSourceIndex();
47     return;
48   }
49 
50   // We're only interested in Issue events.
51   if (Event.Type != HWInstructionEvent::Issued)
52     return;
53 
54   const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);
55   ArrayRef<llvm::MCInst> Source = getSource();
56   const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
57   for (const std::pair<ResourceRef, ResourceCycles> &Use :
58        IssueEvent.UsedResources) {
59     const ResourceRef &RR = Use.first;
60     assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end());
61     unsigned R2VIndex = Resource2VecIndex[RR.first];
62     R2VIndex += countTrailingZeros(RR.second);
63     ResourceUsage[R2VIndex + NumResourceUnits * SourceIdx] += Use.second;
64     ResourceUsage[R2VIndex + NumResourceUnits * Source.size()] += Use.second;
65   }
66 }
67 
68 static void printColumnNames(formatted_raw_ostream &OS,
69                              const MCSchedModel &SM) {
70   unsigned Column = OS.getColumn();
71   for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
72        I < E; ++I) {
73     const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
74     unsigned NumUnits = ProcResource.NumUnits;
75     // Skip groups and invalid resources with zero units.
76     if (ProcResource.SubUnitsIdxBegin || !NumUnits)
77       continue;
78 
79     for (unsigned J = 0; J < NumUnits; ++J) {
80       Column += 7;
81       OS << "[" << ResourceIndex;
82       if (NumUnits > 1)
83         OS << '.' << J;
84       OS << ']';
85       OS.PadToColumn(Column);
86     }
87 
88     ResourceIndex++;
89   }
90 }
91 
92 static void printResourcePressure(formatted_raw_ostream &OS, double Pressure,
93                                   unsigned Col) {
94   if (!Pressure || Pressure < 0.005) {
95     OS << " - ";
96   } else {
97     // Round to the value to the nearest hundredth and then print it.
98     OS << format("%.2f", floor((Pressure * 100) + 0.5) / 100);
99   }
100   OS.PadToColumn(Col);
101 }
102 
103 void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {
104   std::string Buffer;
105   raw_string_ostream TempStream(Buffer);
106   formatted_raw_ostream FOS(TempStream);
107 
108   FOS << "\n\nResources:\n";
109   const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
110   for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
111        I < E; ++I) {
112     const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
113     unsigned NumUnits = ProcResource.NumUnits;
114     // Skip groups and invalid resources with zero units.
115     if (ProcResource.SubUnitsIdxBegin || !NumUnits)
116       continue;
117 
118     for (unsigned J = 0; J < NumUnits; ++J) {
119       FOS << '[' << ResourceIndex;
120       if (NumUnits > 1)
121         FOS << '.' << J;
122       FOS << ']';
123       FOS.PadToColumn(6);
124       FOS << "- " << ProcResource.Name << '\n';
125     }
126 
127     ResourceIndex++;
128   }
129 
130   FOS << "\n\nResource pressure per iteration:\n";
131   FOS.flush();
132   printColumnNames(FOS, SM);
133   FOS << '\n';
134   FOS.flush();
135 
136   ArrayRef<llvm::MCInst> Source = getSource();
137   const unsigned Executions = LastInstructionIdx / Source.size() + 1;
138   for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) {
139     double Usage = ResourceUsage[I + Source.size() * E];
140     printResourcePressure(FOS, Usage / Executions, (I + 1) * 7);
141   }
142 
143   FOS.flush();
144   OS << Buffer;
145 }
146 
147 void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
148   std::string Buffer;
149   raw_string_ostream TempStream(Buffer);
150   formatted_raw_ostream FOS(TempStream);
151 
152   FOS << "\n\nResource pressure by instruction:\n";
153   printColumnNames(FOS, getSubTargetInfo().getSchedModel());
154   FOS << "Instructions:\n";
155 
156   unsigned InstrIndex = 0;
157   ArrayRef<llvm::MCInst> Source = getSource();
158   const unsigned Executions = LastInstructionIdx / Source.size() + 1;
159   for (const MCInst &MCI : Source) {
160     unsigned BaseEltIdx = InstrIndex * NumResourceUnits;
161     for (unsigned J = 0; J < NumResourceUnits; ++J) {
162       double Usage = ResourceUsage[J + BaseEltIdx];
163       printResourcePressure(FOS, Usage / Executions, (J + 1) * 7);
164     }
165 
166     FOS << printInstructionString(MCI) << '\n';
167     FOS.flush();
168     OS << Buffer;
169     Buffer = "";
170 
171     ++InstrIndex;
172   }
173 }
174 
175 json::Value ResourcePressureView::toJSON() const {
176   // We're dumping the instructions and the ResourceUsage array.
177   json::Array ResourcePressureInfo;
178 
179   // The ResourceUsage matrix is sparse, so we only consider
180   // non-zero values.
181   ArrayRef<llvm::MCInst> Source = getSource();
182   const unsigned Executions = LastInstructionIdx / Source.size() + 1;
183   for (const auto &R : enumerate(ResourceUsage)) {
184     const ResourceCycles &RU = R.value();
185     if (RU.getNumerator() == 0)
186       continue;
187     unsigned InstructionIndex = R.index() / NumResourceUnits;
188     unsigned ResourceIndex = R.index() % NumResourceUnits;
189     double Usage = RU / Executions;
190     ResourcePressureInfo.push_back(
191         json::Object({{"InstructionIndex", InstructionIndex},
192                       {"ResourceIndex", ResourceIndex},
193                       {"ResourceUsage", Usage}}));
194   }
195 
196   json::Object JO({{"ResourcePressureInfo", std::move(ResourcePressureInfo)}});
197   return JO;
198 }
199 } // namespace mca
200 } // namespace llvm
201