1 | //===--------------------- InstructionInfoView.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 the InstructionInfoView API. |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "Views/InstructionInfoView.h" |
15 | #include "llvm/ADT/StringExtras.h" |
16 | #include "llvm/Support/FormattedStream.h" |
17 | #include "llvm/Support/JSON.h" |
18 | #include "llvm/Support/WithColor.h" |
19 | |
20 | namespace llvm { |
21 | namespace mca { |
22 | |
23 | void InstructionInfoView::(raw_ostream &OS, const MCInst &MCI) const { |
24 | StringRef S = MCI.getLoc().getPointer(); |
25 | size_t Pos = 0, PosCmt = 0; |
26 | |
27 | // Recognized comments are after assembly instructions on the same line. |
28 | // It is usefull to add in comment scheduling information from architecture |
29 | // specification. |
30 | // '#' comment mark is not supported by llvm-mca |
31 | |
32 | if (Pos = S.find(Str: "\n" ); Pos != StringRef::npos) { |
33 | StringRef InstrStr = S.take_front(N: Pos); |
34 | // C style comment |
35 | if (((PosCmt = InstrStr.find(Str: "/*" )) != StringRef::npos) && |
36 | ((Pos = InstrStr.find(Str: "*/" )) != StringRef::npos)) { |
37 | OS << InstrStr.substr(Start: PosCmt, N: Pos); |
38 | return; |
39 | } |
40 | // C++ style comment |
41 | if ((PosCmt = InstrStr.find(Str: "//" )) != StringRef::npos) { |
42 | OS << InstrStr.substr(Start: PosCmt); |
43 | } |
44 | } |
45 | } |
46 | |
47 | void InstructionInfoView::printView(raw_ostream &OS) const { |
48 | std::string Buffer; |
49 | raw_string_ostream TempStream(Buffer); |
50 | formatted_raw_ostream FOS(TempStream); |
51 | |
52 | ArrayRef<llvm::MCInst> Source = getSource(); |
53 | if (!Source.size()) |
54 | return; |
55 | |
56 | IIVDVec IIVD(Source.size()); |
57 | collectData(IIVD); |
58 | |
59 | if (PrintFullInfo) { |
60 | FOS << "\n\nResources:\n" ; |
61 | const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); |
62 | for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds(); |
63 | I < E; ++I) { |
64 | const MCProcResourceDesc &ProcResource = *SM.getProcResource(ProcResourceIdx: I); |
65 | unsigned NumUnits = ProcResource.NumUnits; |
66 | // Skip invalid resources with zero units. |
67 | if (!NumUnits) |
68 | continue; |
69 | |
70 | FOS << '[' << ResourceIndex << ']'; |
71 | FOS.PadToColumn(NewCol: 6); |
72 | FOS << "- " << ProcResource.Name << ':' << NumUnits; |
73 | if (ProcResource.SubUnitsIdxBegin) { |
74 | FOS.PadToColumn(NewCol: 20); |
75 | for (unsigned U = 0; U < NumUnits; ++U) { |
76 | FOS << SM.getProcResource(ProcResourceIdx: ProcResource.SubUnitsIdxBegin[U])->Name; |
77 | if ((U + 1) < NumUnits) |
78 | FOS << ", " ; |
79 | } |
80 | } |
81 | FOS << '\n'; |
82 | ResourceIndex++; |
83 | } |
84 | } |
85 | |
86 | SmallVector<unsigned, 16> Paddings = {0, 7, 14, 21, 28, 35, 42}; |
87 | SmallVector<StringRef, 16> Fields = {"#uOps" , "Latency" , |
88 | "RThroughput" , "MayLoad" , |
89 | "MayStore" , "HasSideEffects (U)" }; |
90 | SmallVector<StringRef, 8> EndFields; |
91 | unsigned LastPadding = Paddings.back(); |
92 | if (PrintFullInfo) { |
93 | Fields.push_back(Elt: "Bypass Latency" ); |
94 | // Reserving 7 chars for |
95 | Paddings.push_back(Elt: LastPadding += 7); |
96 | Fields.push_back(Elt: "Resources (<Name> | <Name>[<ReleaseAtCycle>] | " |
97 | "<Name>[<AcquireAtCycle>,<ReleaseAtCycle])" ); |
98 | Paddings.push_back(Elt: LastPadding += 43); |
99 | Fields.push_back(Elt: "LLVM Opcode Name" ); |
100 | Paddings.push_back(Elt: LastPadding += 27); |
101 | } |
102 | if (PrintBarriers) { |
103 | Fields.push_back(Elt: "LoadBarrier" ); |
104 | Paddings.push_back(Elt: LastPadding += 7); |
105 | Fields.push_back(Elt: "StoreBarrier" ); |
106 | Paddings.push_back(Elt: LastPadding += 7); |
107 | } |
108 | if (PrintEncodings) { |
109 | Fields.push_back(Elt: "Encoding Size" ); |
110 | Paddings.push_back(Elt: LastPadding += 7); |
111 | EndFields.push_back(Elt: "Encodings:" ); |
112 | Paddings.push_back(Elt: LastPadding += 30); |
113 | } |
114 | EndFields.push_back(Elt: "Instructions:" ); |
115 | |
116 | FOS << "\n\nInstruction Info:\n" ; |
117 | for (unsigned i = 0, N = Fields.size(); i < N; i++) |
118 | FOS << "[" << i + 1 << "]: " << Fields[i] << "\n" ; |
119 | FOS << "\n" ; |
120 | |
121 | for (unsigned i = 0, N = Paddings.size(); i < N; i++) { |
122 | if (Paddings[i]) |
123 | FOS.PadToColumn(NewCol: Paddings[i]); |
124 | if (i < Fields.size()) |
125 | FOS << "[" << i + 1 << "]" ; |
126 | else |
127 | FOS << EndFields[i - Fields.size()]; |
128 | } |
129 | FOS << "\n" ; |
130 | |
131 | for (const auto &[Index, IIVDEntry, Inst] : enumerate(First&: IIVD, Rest&: Source)) { |
132 | FOS.PadToColumn(NewCol: Paddings[0] + 1); |
133 | FOS << IIVDEntry.NumMicroOpcodes; |
134 | FOS.PadToColumn(NewCol: Paddings[1] + 1); |
135 | FOS << IIVDEntry.Latency; |
136 | FOS.PadToColumn(NewCol: Paddings[2]); |
137 | if (IIVDEntry.RThroughput) { |
138 | double RT = *IIVDEntry.RThroughput; |
139 | FOS << format(Fmt: "%.2f" , Vals: RT); |
140 | } else { |
141 | FOS << " -" ; |
142 | } |
143 | FOS.PadToColumn(NewCol: Paddings[3] + 1); |
144 | FOS << (IIVDEntry.mayLoad ? "*" : " " ); |
145 | FOS.PadToColumn(NewCol: Paddings[4] + 1); |
146 | FOS << (IIVDEntry.mayStore ? "*" : " " ); |
147 | FOS.PadToColumn(NewCol: Paddings[5] + 1); |
148 | FOS << (IIVDEntry.hasUnmodeledSideEffects ? "U" : " " ); |
149 | unsigned LastPaddingIdx = 5; |
150 | |
151 | if (PrintFullInfo) { |
152 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1] + 1); |
153 | FOS << IIVDEntry.Bypass; |
154 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1]); |
155 | FOS << IIVDEntry.Resources; |
156 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1]); |
157 | FOS << IIVDEntry.OpcodeName; |
158 | } |
159 | |
160 | if (PrintBarriers) { |
161 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1] + 1); |
162 | FOS << (LoweredInsts[Index]->isALoadBarrier() ? "*" : " " ); |
163 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1] + 1); |
164 | FOS << (LoweredInsts[Index]->isAStoreBarrier() ? "*" : " " ); |
165 | } |
166 | |
167 | if (PrintEncodings) { |
168 | StringRef Encoding(CE.getEncoding(MCID: Index)); |
169 | unsigned EncodingSize = Encoding.size(); |
170 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1] + 1); |
171 | FOS << EncodingSize; |
172 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1]); |
173 | for (unsigned i = 0, e = Encoding.size(); i != e; ++i) |
174 | FOS << format(Fmt: "%02x " , Vals: (uint8_t)Encoding[i]); |
175 | } |
176 | FOS.PadToColumn(NewCol: Paddings[LastPaddingIdx += 1]); |
177 | FOS << printInstructionString(MCI: Inst); |
178 | if (PrintFullInfo) { |
179 | FOS << "\t" ; |
180 | getComment(OS&: FOS, MCI: Inst); |
181 | } |
182 | FOS << '\n'; |
183 | } |
184 | |
185 | OS << Buffer; |
186 | } |
187 | |
188 | void InstructionInfoView::collectData( |
189 | MutableArrayRef<InstructionInfoViewData> IIVD) const { |
190 | const llvm::MCSubtargetInfo &STI = getSubTargetInfo(); |
191 | const MCSchedModel &SM = STI.getSchedModel(); |
192 | for (const auto I : zip(t: getSource(), u&: IIVD)) { |
193 | const MCInst &Inst = std::get<0>(t: I); |
194 | InstructionInfoViewData &IIVDEntry = std::get<1>(t: I); |
195 | const MCInstrDesc &MCDesc = MCII.get(Opcode: Inst.getOpcode()); |
196 | |
197 | // Obtain the scheduling class information from the instruction |
198 | // and instruments. |
199 | auto IVecIt = InstToInstruments.find(Val: &Inst); |
200 | unsigned SchedClassID = |
201 | IVecIt == InstToInstruments.end() |
202 | ? MCDesc.getSchedClass() |
203 | : IM.getSchedClassID(MCII, MCI: Inst, IVec: IVecIt->second); |
204 | unsigned CPUID = SM.getProcessorID(); |
205 | |
206 | // Try to solve variant scheduling classes. |
207 | while (SchedClassID && SM.getSchedClassDesc(SchedClassIdx: SchedClassID)->isVariant()) |
208 | SchedClassID = |
209 | STI.resolveVariantSchedClass(SchedClass: SchedClassID, MI: &Inst, MCII: &MCII, CPUID); |
210 | |
211 | const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassIdx: SchedClassID); |
212 | IIVDEntry.NumMicroOpcodes = SCDesc.NumMicroOps; |
213 | IIVDEntry.Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); |
214 | // Add extra latency due to delays in the forwarding data paths. |
215 | IIVDEntry.Latency += MCSchedModel::getForwardingDelayCycles( |
216 | Entries: STI.getReadAdvanceEntries(SC: SCDesc)); |
217 | IIVDEntry.RThroughput = MCSchedModel::getReciprocalThroughput(STI, SCDesc); |
218 | IIVDEntry.mayLoad = MCDesc.mayLoad(); |
219 | IIVDEntry.mayStore = MCDesc.mayStore(); |
220 | IIVDEntry.hasUnmodeledSideEffects = MCDesc.hasUnmodeledSideEffects(); |
221 | |
222 | if (PrintFullInfo) { |
223 | // Get latency with bypass |
224 | IIVDEntry.Bypass = |
225 | IIVDEntry.Latency - MCSchedModel::getBypassDelayCycles(STI, SCDesc); |
226 | IIVDEntry.OpcodeName = MCII.getName(Opcode: Inst.getOpcode()); |
227 | raw_string_ostream TempStream(IIVDEntry.Resources); |
228 | const MCWriteProcResEntry *Index = STI.getWriteProcResBegin(SC: &SCDesc); |
229 | const MCWriteProcResEntry *Last = STI.getWriteProcResEnd(SC: &SCDesc); |
230 | ListSeparator LS("," ); |
231 | for (; Index != Last; ++Index) { |
232 | if (!Index->ReleaseAtCycle) |
233 | continue; |
234 | const MCProcResourceDesc *MCProc = |
235 | SM.getProcResource(ProcResourceIdx: Index->ProcResourceIdx); |
236 | if (Index->ReleaseAtCycle > 1) { |
237 | // Output ReleaseAtCycle between [] if not 1 (default) |
238 | // This is to be able to evaluate throughput. |
239 | // See getReciprocalThroughput in MCSchedule.cpp |
240 | if (Index->AcquireAtCycle > 0) |
241 | TempStream << LS |
242 | << format(Fmt: "%s[%d,%d]" , Vals: MCProc->Name, |
243 | Vals: Index->AcquireAtCycle, Vals: Index->ReleaseAtCycle); |
244 | else |
245 | TempStream << LS |
246 | << format(Fmt: "%s[%d]" , Vals: MCProc->Name, Vals: Index->ReleaseAtCycle); |
247 | } else { |
248 | TempStream << LS << MCProc->Name; |
249 | } |
250 | } |
251 | } |
252 | } |
253 | } |
254 | |
255 | // Construct a JSON object from a single InstructionInfoViewData object. |
256 | json::Object |
257 | InstructionInfoView::toJSON(const InstructionInfoViewData &IIVD) const { |
258 | json::Object JO({{.K: "NumMicroOpcodes" , .V: IIVD.NumMicroOpcodes}, |
259 | {.K: "Latency" , .V: IIVD.Latency}, |
260 | {.K: "mayLoad" , .V: IIVD.mayLoad}, |
261 | {.K: "mayStore" , .V: IIVD.mayStore}, |
262 | {.K: "hasUnmodeledSideEffects" , .V: IIVD.hasUnmodeledSideEffects}}); |
263 | JO.try_emplace(K: "RThroughput" , Args: IIVD.RThroughput.value_or(u: 0.0)); |
264 | return JO; |
265 | } |
266 | |
267 | json::Value InstructionInfoView::toJSON() const { |
268 | ArrayRef<llvm::MCInst> Source = getSource(); |
269 | if (!Source.size()) |
270 | return json::Value(0); |
271 | |
272 | IIVDVec IIVD(Source.size()); |
273 | collectData(IIVD); |
274 | |
275 | json::Array InstInfo; |
276 | for (const auto &I : enumerate(First&: IIVD)) { |
277 | const InstructionInfoViewData &IIVDEntry = I.value(); |
278 | json::Object JO = toJSON(IIVD: IIVDEntry); |
279 | JO.try_emplace(K: "Instruction" , Args: (unsigned)I.index()); |
280 | InstInfo.push_back(E: std::move(JO)); |
281 | } |
282 | return json::Object({{.K: "InstructionList" , .V: json::Value(std::move(InstInfo))}}); |
283 | } |
284 | } // namespace mca. |
285 | } // namespace llvm |
286 | |