|
| 1 | +//===--------------------- SchedulingInfoView.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 SchedulingInfoView API. |
| 11 | +/// |
| 12 | +//===----------------------------------------------------------------------===// |
| 13 | + |
| 14 | +#include "Views/SchedulingInfoView.h" |
| 15 | +#include "llvm/Support/FormattedStream.h" |
| 16 | +#include "llvm/Support/JSON.h" |
| 17 | + |
| 18 | +namespace llvm { |
| 19 | +namespace mca { |
| 20 | + |
| 21 | +void SchedulingInfoView::getComment(const MCInst &MCI, std::string &CommentString) const { |
| 22 | + StringRef s = MCI.getLoc().getPointer(); |
| 23 | + std::string InstrStr; |
| 24 | + size_t pos = 0, pos_cmt = 0; |
| 25 | + |
| 26 | + // Recognized comments are after assembly instructions on the same line. |
| 27 | + // It is usefull to add in comment scheduling information from architecture |
| 28 | + // specification. |
| 29 | + // '#' comment mark is not supported by llvm-mca |
| 30 | + |
| 31 | + CommentString = ""; |
| 32 | + if ((pos = s.find("\n")) != std::string::npos) { |
| 33 | + InstrStr = s.substr(0, pos); |
| 34 | + // C style comment |
| 35 | + if (((pos_cmt = InstrStr.find("/*")) != std::string::npos) && |
| 36 | + ((pos = InstrStr.find("*/")) != std::string::npos)) { |
| 37 | + CommentString = InstrStr.substr(pos_cmt, pos); |
| 38 | + return; |
| 39 | + } |
| 40 | + // C++ style comment |
| 41 | + if ((pos_cmt = InstrStr.find("//")) != std::string::npos) { |
| 42 | + CommentString = InstrStr.substr(pos_cmt, pos); |
| 43 | + return; |
| 44 | + } |
| 45 | + } |
| 46 | + return; |
| 47 | +} |
| 48 | + |
| 49 | +void SchedulingInfoView::printView(raw_ostream &OS) const { |
| 50 | + std::string Buffer; |
| 51 | + std::string CommentString; |
| 52 | + raw_string_ostream TempStream(Buffer); |
| 53 | + formatted_raw_ostream FOS(TempStream); |
| 54 | + |
| 55 | + ArrayRef<MCInst> Source = getSource(); |
| 56 | + if (!Source.size()) |
| 57 | + return; |
| 58 | + |
| 59 | + IIVDVec IIVD(Source.size()); |
| 60 | + collectData(IIVD); |
| 61 | + |
| 62 | + FOS << "\n\nResources:\n"; |
| 63 | + const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); |
| 64 | + for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds(); |
| 65 | + I < E; ++I) { |
| 66 | + const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); |
| 67 | + unsigned NumUnits = ProcResource.NumUnits; |
| 68 | + // Skip invalid resources with zero units. |
| 69 | + if (!NumUnits) |
| 70 | + continue; |
| 71 | + |
| 72 | + FOS << '[' << ResourceIndex << ']'; |
| 73 | + FOS.PadToColumn(6); |
| 74 | + FOS << "- " << ProcResource.Name << ':' << NumUnits; |
| 75 | + if (ProcResource.SubUnitsIdxBegin) { |
| 76 | + FOS.PadToColumn(20); |
| 77 | + for (unsigned U = 0; U < NumUnits; ++U) { |
| 78 | + FOS << SM.getProcResource(ProcResource.SubUnitsIdxBegin[U])->Name; |
| 79 | + if((U + 1) < NumUnits) { |
| 80 | + FOS << ", "; |
| 81 | + } |
| 82 | + } |
| 83 | + } |
| 84 | + FOS << '\n'; |
| 85 | + ResourceIndex++; |
| 86 | + } |
| 87 | + |
| 88 | + FOS << "\n\nScheduling Info:\n"; |
| 89 | + FOS << "[1]: #uOps\n[2]: Latency\n[3]: Bypass Latency\n" |
| 90 | + << "[4]: Throughput\n[5]: Resources\n" |
| 91 | + << "[6]: LLVM OpcodeName\n[7]: Instruction\n" |
| 92 | + << "[8]: Comment if any\n"; |
| 93 | + |
| 94 | + // paddings for each scheduling info output. Start at [2] |
| 95 | + std::vector<unsigned> paddings = {7, 12, 18, 27, 94, 113, 150}; |
| 96 | + for (unsigned i = 0; i < paddings.size(); i++) { |
| 97 | + FOS << " [" << i + 1 << "]"; |
| 98 | + FOS.PadToColumn(paddings[i]); |
| 99 | + } |
| 100 | + FOS << "[" << paddings.size() + 1 << "]\n"; |
| 101 | + |
| 102 | + for (const auto &[Index, IIVDEntry, Inst] : enumerate(IIVD, Source)) { |
| 103 | + getComment(Inst,CommentString); |
| 104 | + |
| 105 | + FOS << " " << IIVDEntry.NumMicroOpcodes; |
| 106 | + FOS.PadToColumn(paddings[0]); |
| 107 | + FOS << "| " << IIVDEntry.Latency; |
| 108 | + FOS.PadToColumn(paddings[1]); |
| 109 | + FOS << "| " << IIVDEntry.Bypass; |
| 110 | + FOS.PadToColumn(paddings[2]); |
| 111 | + if (IIVDEntry.Throughput) { |
| 112 | + double RT = *IIVDEntry.Throughput; |
| 113 | + FOS << "| " << format("%.2f", RT); |
| 114 | + } else { |
| 115 | + FOS << "| -"; |
| 116 | + } |
| 117 | + FOS.PadToColumn(paddings[3]); |
| 118 | + FOS << "| " << IIVDEntry.Resources; |
| 119 | + FOS.PadToColumn(paddings[4]); |
| 120 | + FOS << "| " << IIVDEntry.OpcodeName; |
| 121 | + FOS.PadToColumn(paddings[5]); |
| 122 | + FOS << "| " << printInstructionString(Inst); |
| 123 | + FOS.PadToColumn(paddings[6]); |
| 124 | + FOS << ' ' << CommentString << '\n'; |
| 125 | + } |
| 126 | + |
| 127 | + FOS.flush(); |
| 128 | + OS << Buffer; |
| 129 | +} |
| 130 | + |
| 131 | +void SchedulingInfoView::collectData( |
| 132 | + MutableArrayRef<SchedulingInfoViewData> IIVD) const { |
| 133 | + const MCSubtargetInfo &STI = getSubTargetInfo(); |
| 134 | + const MCSchedModel &SM = STI.getSchedModel(); |
| 135 | + for (const auto &[Inst, IIVDEntry] : zip(getSource(), IIVD)) { |
| 136 | + const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode()); |
| 137 | + |
| 138 | + // Obtain the scheduling class information from the instruction |
| 139 | + // and instruments. |
| 140 | + auto IVecIt = InstToInstruments.find(&Inst); |
| 141 | + unsigned SchedClassID = |
| 142 | + IVecIt == InstToInstruments.end() |
| 143 | + ? MCDesc.getSchedClass() |
| 144 | + : IM.getSchedClassID(MCII, Inst, IVecIt->second); |
| 145 | + unsigned CPUID = SM.getProcessorID(); |
| 146 | + |
| 147 | + // Try to solve variant scheduling classes. |
| 148 | + while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant()) |
| 149 | + SchedClassID = |
| 150 | + STI.resolveVariantSchedClass(SchedClassID, &Inst, &MCII, CPUID); |
| 151 | + |
| 152 | + const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); |
| 153 | + IIVDEntry.OpcodeName = (std::string)MCII.getName(Inst.getOpcode()); |
| 154 | + IIVDEntry.NumMicroOpcodes = SCDesc.NumMicroOps; |
| 155 | + IIVDEntry.Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); |
| 156 | + IIVDEntry.Bypass = |
| 157 | + IIVDEntry.Latency - MCSchedModel::getForwardingDelayCycles(STI, SCDesc); |
| 158 | + IIVDEntry.Throughput = |
| 159 | + 1.0 / MCSchedModel::getReciprocalThroughput(STI, SCDesc); |
| 160 | + raw_string_ostream TempStream(IIVDEntry.Resources); |
| 161 | + |
| 162 | + const MCWriteProcResEntry *Index = STI.getWriteProcResBegin(&SCDesc); |
| 163 | + const MCWriteProcResEntry *Last = STI.getWriteProcResEnd(&SCDesc); |
| 164 | + auto sep = ""; |
| 165 | + for (; Index != Last; ++Index) { |
| 166 | + if (!Index->ReleaseAtCycle) |
| 167 | + continue; |
| 168 | + const MCProcResourceDesc *MCProc = |
| 169 | + SM.getProcResource(Index->ProcResourceIdx); |
| 170 | + if (Index->ReleaseAtCycle != 1) { |
| 171 | + // Output ReleaseAtCycle between [] if not 1 (default) |
| 172 | + TempStream << sep << format("%s[%d]", MCProc->Name, Index->ReleaseAtCycle); |
| 173 | + } else { |
| 174 | + TempStream << sep << format("%s", MCProc->Name); |
| 175 | + } |
| 176 | + sep = ", "; |
| 177 | + } |
| 178 | + TempStream.flush(); |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +// Construct a JSON object from a single SchedulingInfoViewData object. |
| 183 | +json::Object |
| 184 | +SchedulingInfoView::toJSON(const SchedulingInfoViewData &IIVD) const { |
| 185 | + json::Object JO({{"NumMicroOpcodes", IIVD.NumMicroOpcodes}, |
| 186 | + {"Latency", IIVD.Latency}, |
| 187 | + {"LatencyWithBypass", IIVD.Bypass}, |
| 188 | + {"Throughput", IIVD.Throughput.value_or(0.0)}}); |
| 189 | + return JO; |
| 190 | +} |
| 191 | + |
| 192 | +json::Value SchedulingInfoView::toJSON() const { |
| 193 | + ArrayRef<MCInst> Source = getSource(); |
| 194 | + if (!Source.size()) |
| 195 | + return json::Value(nullptr); |
| 196 | + |
| 197 | + IIVDVec IIVD(Source.size()); |
| 198 | + collectData(IIVD); |
| 199 | + |
| 200 | + json::Array InstInfo; |
| 201 | + for (const auto &I : enumerate(IIVD)) { |
| 202 | + const SchedulingInfoViewData &IIVDEntry = I.value(); |
| 203 | + json::Object JO = toJSON(IIVDEntry); |
| 204 | + JO.try_emplace("Instruction", (unsigned)I.index()); |
| 205 | + InstInfo.push_back(std::move(JO)); |
| 206 | + } |
| 207 | + return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}}); |
| 208 | +} |
| 209 | +} // namespace mca. |
| 210 | +} // namespace llvm |
0 commit comments