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: NumResourceUnits * (getSource().size() + 1));
41 std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0);
42}
43
44void 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, ReleaseAtCycles> &Use :
58 IssueEvent.UsedResources) {
59 const ResourceRef &RR = Use.first;
60 assert(Resource2VecIndex.contains(RR.first));
61 unsigned R2VIndex = Resource2VecIndex[RR.first];
62 R2VIndex += llvm::countr_zero(Val: RR.second);
63 ResourceUsage[R2VIndex + NumResourceUnits * SourceIdx] += Use.second;
64 ResourceUsage[R2VIndex + NumResourceUnits * Source.size()] += Use.second;
65 }
66}
67
68static 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(ProcResourceIdx: 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(NewCol: Column);
86 }
87
88 ResourceIndex++;
89 }
90}
91
92static 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(Fmt: "%.2f", Vals: floor(x: (Pressure * 100) + 0.5) / 100);
99 }
100 OS.PadToColumn(NewCol: Col);
101}
102
103void 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(ProcResourceIdx: 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(NewCol: 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(OS&: 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(OS&: FOS, Pressure: Usage / Executions, Col: (I + 1) * 7);
141 }
142
143 FOS.flush();
144 OS << Buffer;
145}
146
147void 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(OS&: FOS, SM: 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(OS&: FOS, Pressure: Usage / Executions, Col: (J + 1) * 7);
164 }
165
166 FOS << printInstructionString(MCI) << '\n';
167 FOS.flush();
168 OS << Buffer;
169 Buffer = "";
170
171 ++InstrIndex;
172 }
173}
174
175json::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(First: ResourceUsage)) {
184 const ReleaseAtCycles &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 E: json::Object({{.K: "InstructionIndex", .V: InstructionIndex},
192 {.K: "ResourceIndex", .V: ResourceIndex},
193 {.K: "ResourceUsage", .V: Usage}}));
194 }
195
196 json::Object JO({{.K: "ResourcePressureInfo", .V: std::move(ResourcePressureInfo)}});
197 return JO;
198}
199} // namespace mca
200} // namespace llvm
201