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
20namespace llvm {
21namespace mca {
22
23void InstructionInfoView::getComment(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
47void 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
188void 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.
256json::Object
257InstructionInfoView::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
267json::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