| 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 | |