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(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 | |
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, 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 | |
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(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 | |
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(Fmt: "%.2f" , Vals: floor(x: (Pressure * 100) + 0.5) / 100); |
99 | } |
100 | OS.PadToColumn(NewCol: 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(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 | |
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(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 | |
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(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 | |