xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
getComment(raw_ostream & OS,const MCInst & MCI) const23 void 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("\n"); Pos != StringRef::npos) {
33     StringRef InstrStr = S.take_front(Pos);
34     // C style comment
35     if (((PosCmt = InstrStr.find("/*")) != StringRef::npos) &&
36         ((Pos = InstrStr.find("*/")) != StringRef::npos)) {
37       OS << InstrStr.substr(PosCmt, Pos);
38       return;
39     }
40     // C++ style comment
41     if ((PosCmt = InstrStr.find("//")) != StringRef::npos) {
42       OS << InstrStr.substr(PosCmt);
43     }
44   }
45 }
46 
printView(raw_ostream & OS) const47 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(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(6);
72       FOS << "- " << ProcResource.Name << ':' << NumUnits;
73       if (ProcResource.SubUnitsIdxBegin) {
74         FOS.PadToColumn(20);
75         for (unsigned U = 0; U < NumUnits; ++U) {
76           FOS << SM.getProcResource(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("Bypass Latency");
94     // Reserving 7 chars for
95     Paddings.push_back(LastPadding += 7);
96     Fields.push_back("Resources (<Name> | <Name>[<ReleaseAtCycle>] | "
97                      "<Name>[<AcquireAtCycle>,<ReleaseAtCycle])");
98     Paddings.push_back(LastPadding += 43);
99     Fields.push_back("LLVM Opcode Name");
100     Paddings.push_back(LastPadding += 27);
101   }
102   if (PrintBarriers) {
103     Fields.push_back("LoadBarrier");
104     Paddings.push_back(LastPadding += 7);
105     Fields.push_back("StoreBarrier");
106     Paddings.push_back(LastPadding += 7);
107   }
108   if (PrintEncodings) {
109     Fields.push_back("Encoding Size");
110     Paddings.push_back(LastPadding += 7);
111     EndFields.push_back("Encodings:");
112     Paddings.push_back(LastPadding += 30);
113   }
114   EndFields.push_back("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(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(IIVD, Source)) {
132     FOS.PadToColumn(Paddings[0] + 1);
133     FOS << IIVDEntry.NumMicroOpcodes;
134     FOS.PadToColumn(Paddings[1] + 1);
135     FOS << IIVDEntry.Latency;
136     FOS.PadToColumn(Paddings[2]);
137     if (IIVDEntry.RThroughput) {
138       double RT = *IIVDEntry.RThroughput;
139       FOS << format("%.2f", RT);
140     } else {
141       FOS << " -";
142     }
143     FOS.PadToColumn(Paddings[3] + 1);
144     FOS << (IIVDEntry.mayLoad ? "*" : " ");
145     FOS.PadToColumn(Paddings[4] + 1);
146     FOS << (IIVDEntry.mayStore ? "*" : " ");
147     FOS.PadToColumn(Paddings[5] + 1);
148     FOS << (IIVDEntry.hasUnmodeledSideEffects ? "U" : " ");
149     unsigned LastPaddingIdx = 5;
150 
151     if (PrintFullInfo) {
152       FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1);
153       FOS << IIVDEntry.Bypass;
154       FOS.PadToColumn(Paddings[LastPaddingIdx += 1]);
155       FOS << IIVDEntry.Resources;
156       FOS.PadToColumn(Paddings[LastPaddingIdx += 1]);
157       FOS << IIVDEntry.OpcodeName;
158     }
159 
160     if (PrintBarriers) {
161       FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1);
162       FOS << (LoweredInsts[Index]->isALoadBarrier() ? "*" : " ");
163       FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1);
164       FOS << (LoweredInsts[Index]->isAStoreBarrier() ? "*" : " ");
165     }
166 
167     if (PrintEncodings) {
168       StringRef Encoding(CE.getEncoding(Index));
169       unsigned EncodingSize = Encoding.size();
170       FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1);
171       FOS << EncodingSize;
172       FOS.PadToColumn(Paddings[LastPaddingIdx += 1]);
173       for (unsigned i = 0, e = Encoding.size(); i != e; ++i)
174         FOS << format("%02x ", (uint8_t)Encoding[i]);
175     }
176     FOS.PadToColumn(Paddings[LastPaddingIdx += 1]);
177     FOS << printInstructionString(Inst);
178     if (PrintFullInfo) {
179       FOS << "\t";
180       getComment(FOS, Inst);
181     }
182     FOS << '\n';
183   }
184 
185   OS << Buffer;
186 }
187 
collectData(MutableArrayRef<InstructionInfoViewData> IIVD) const188 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(getSource(), IIVD)) {
193     const MCInst &Inst = std::get<0>(I);
194     InstructionInfoViewData &IIVDEntry = std::get<1>(I);
195     const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());
196 
197     // Obtain the scheduling class information from the instruction
198     // and instruments.
199     auto IVecIt = InstToInstruments.find(&Inst);
200     unsigned SchedClassID =
201         IVecIt == InstToInstruments.end()
202             ? MCDesc.getSchedClass()
203             : IM.getSchedClassID(MCII, Inst, IVecIt->second);
204     unsigned CPUID = SM.getProcessorID();
205 
206     // Try to solve variant scheduling classes.
207     while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
208       SchedClassID =
209           STI.resolveVariantSchedClass(SchedClassID, &Inst, &MCII, CPUID);
210 
211     const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(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         STI.getReadAdvanceEntries(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(Inst.getOpcode());
227       raw_string_ostream TempStream(IIVDEntry.Resources);
228       const MCWriteProcResEntry *Index = STI.getWriteProcResBegin(&SCDesc);
229       const MCWriteProcResEntry *Last = STI.getWriteProcResEnd(&SCDesc);
230       ListSeparator LS(",");
231       for (; Index != Last; ++Index) {
232         if (!Index->ReleaseAtCycle)
233           continue;
234         const MCProcResourceDesc *MCProc =
235             SM.getProcResource(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("%s[%d,%d]", MCProc->Name,
243                                  Index->AcquireAtCycle, Index->ReleaseAtCycle);
244           else
245             TempStream << LS
246                        << format("%s[%d]", MCProc->Name, 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
toJSON(const InstructionInfoViewData & IIVD) const257 InstructionInfoView::toJSON(const InstructionInfoViewData &IIVD) const {
258   json::Object JO({{"NumMicroOpcodes", IIVD.NumMicroOpcodes},
259                    {"Latency", IIVD.Latency},
260                    {"mayLoad", IIVD.mayLoad},
261                    {"mayStore", IIVD.mayStore},
262                    {"hasUnmodeledSideEffects", IIVD.hasUnmodeledSideEffects}});
263   JO.try_emplace("RThroughput", IIVD.RThroughput.value_or(0.0));
264   return JO;
265 }
266 
toJSON() const267 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(IIVD)) {
277     const InstructionInfoViewData &IIVDEntry = I.value();
278     json::Object JO = toJSON(IIVDEntry);
279     JO.try_emplace("Instruction", (unsigned)I.index());
280     InstInfo.push_back(std::move(JO));
281   }
282   return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}});
283 }
284 } // namespace mca.
285 } // namespace llvm
286