238 lines · cpp
1//===--------------------- ResourcePressureView.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-exception6//7//===----------------------------------------------------------------------===//8/// \file9///10/// This file implements methods in the ResourcePressureView interface.11///12//===----------------------------------------------------------------------===//13 14#include "Views/ResourcePressureView.h"15#include "llvm/Support/FormattedStream.h"16#include "llvm/Support/raw_ostream.h"17 18namespace llvm {19namespace mca {20 21ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti,22 MCInstPrinter &Printer,23 ArrayRef<MCInst> S)24 : InstructionView(sti, Printer, S), LastInstructionIdx(0) {25 // Populate the map of resource descriptors.26 unsigned R2VIndex = 0;27 const MCSchedModel &SM = getSubTargetInfo().getSchedModel();28 for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {29 const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);30 unsigned NumUnits = ProcResource.NumUnits;31 // Skip groups and invalid resources with zero units.32 if (ProcResource.SubUnitsIdxBegin || !NumUnits)33 continue;34 35 Resource2VecIndex.insert(std::pair<unsigned, unsigned>(I, R2VIndex));36 R2VIndex += ProcResource.NumUnits;37 }38 39 NumResourceUnits = R2VIndex;40 ResourceUsage.resize(getSource().size());41 42 ResourceReleaseAtCycles InitValue{0, 0};43 auto Generator = [&InitValue]() {44 ResourceReleaseAtCycles Old = InitValue;45 ++InitValue.ResourceIdx;46 return Old;47 };48 std::generate_n(std::back_inserter(CommonResourceUsage), NumResourceUnits,49 Generator);50}51 52void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {53 if (Event.Type == HWInstructionEvent::Dispatched) {54 LastInstructionIdx = Event.IR.getSourceIndex();55 return;56 }57 58 // We're only interested in Issue events.59 if (Event.Type != HWInstructionEvent::Issued)60 return;61 62 const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);63 ArrayRef<llvm::MCInst> Source = getSource();64 const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();65 for (const std::pair<ResourceRef, ReleaseAtCycles> &Use :66 IssueEvent.UsedResources) {67 const ResourceRef &RR = Use.first;68 assert(Resource2VecIndex.contains(RR.first));69 unsigned R2VIndex = Resource2VecIndex[RR.first];70 R2VIndex += llvm::countr_zero(RR.second);71 72 InstResourceUsage &RU = ResourceUsage[SourceIdx];73 ResourceReleaseAtCycles NewUsage{R2VIndex, Use.second};74 auto ResCyclesIt =75 lower_bound(RU, NewUsage, [](const auto &L, const auto &R) {76 return L.ResourceIdx < R.ResourceIdx;77 });78 if (ResCyclesIt != RU.end() && ResCyclesIt->ResourceIdx == R2VIndex)79 ResCyclesIt->Cycles += NewUsage.Cycles;80 else81 RU.insert(ResCyclesIt, std::move(NewUsage));82 83 CommonResourceUsage[R2VIndex].Cycles += NewUsage.Cycles;84 }85}86 87static void printColumnNames(formatted_raw_ostream &OS,88 const MCSchedModel &SM) {89 unsigned Column = OS.getColumn();90 for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();91 I < E; ++I) {92 const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);93 unsigned NumUnits = ProcResource.NumUnits;94 // Skip groups and invalid resources with zero units.95 if (ProcResource.SubUnitsIdxBegin || !NumUnits)96 continue;97 98 for (unsigned J = 0; J < NumUnits; ++J) {99 Column += 7;100 OS << "[" << ResourceIndex;101 if (NumUnits > 1)102 OS << '.' << J;103 OS << ']';104 OS.PadToColumn(Column);105 }106 107 ResourceIndex++;108 }109}110 111static void printResourcePressure(formatted_raw_ostream &OS, double Pressure,112 unsigned Col) {113 if (!Pressure || Pressure < 0.005) {114 OS << " - ";115 } else {116 // Round to the value to the nearest hundredth and then print it.117 OS << format("%.2f", floor((Pressure * 100) + 0.5) / 100);118 }119 OS.PadToColumn(Col);120}121 122void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {123 std::string Buffer;124 raw_string_ostream TempStream(Buffer);125 formatted_raw_ostream FOS(TempStream);126 127 FOS << "\n\nResources:\n";128 const MCSchedModel &SM = getSubTargetInfo().getSchedModel();129 for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();130 I < E; ++I) {131 const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);132 unsigned NumUnits = ProcResource.NumUnits;133 // Skip groups and invalid resources with zero units.134 if (ProcResource.SubUnitsIdxBegin || !NumUnits)135 continue;136 137 for (unsigned J = 0; J < NumUnits; ++J) {138 FOS << '[' << ResourceIndex;139 if (NumUnits > 1)140 FOS << '.' << J;141 FOS << ']';142 FOS.PadToColumn(6);143 FOS << "- " << ProcResource.Name << '\n';144 }145 146 ResourceIndex++;147 }148 149 FOS << "\n\nResource pressure per iteration:\n";150 FOS.flush();151 printColumnNames(FOS, SM);152 FOS << '\n';153 FOS.flush();154 155 ArrayRef<llvm::MCInst> Source = getSource();156 const unsigned Executions = LastInstructionIdx / Source.size() + 1;157 auto UsageEntryEnd = CommonResourceUsage.end();158 auto UsageEntryIt = CommonResourceUsage.begin();159 for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) {160 double Pressure = 0.0;161 if (UsageEntryIt != UsageEntryEnd && UsageEntryIt->ResourceIdx == I) {162 Pressure = UsageEntryIt->Cycles / Executions;163 ++UsageEntryIt;164 }165 printResourcePressure(FOS, Pressure, (I + 1) * 7);166 }167 assert(UsageEntryIt == UsageEntryEnd);168 169 FOS.flush();170 OS << Buffer;171}172 173void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {174 std::string Buffer;175 raw_string_ostream TempStream(Buffer);176 formatted_raw_ostream FOS(TempStream);177 178 FOS << "\n\nResource pressure by instruction:\n";179 printColumnNames(FOS, getSubTargetInfo().getSchedModel());180 FOS << "Instructions:\n";181 182 unsigned InstrIndex = 0;183 ArrayRef<llvm::MCInst> Source = getSource();184 const unsigned Executions = LastInstructionIdx / Source.size() + 1;185 for (const MCInst &MCI : Source) {186 auto UsageEntryEnd = ResourceUsage[InstrIndex].end();187 auto UsageEntryIt = ResourceUsage[InstrIndex].begin();188 for (unsigned J = 0; J < NumResourceUnits; ++J) {189 double Pressure = 0.0;190 if (UsageEntryIt != UsageEntryEnd && UsageEntryIt->ResourceIdx == J) {191 Pressure = UsageEntryIt->Cycles / Executions;192 ++UsageEntryIt;193 }194 printResourcePressure(FOS, Pressure, (J + 1) * 7);195 }196 assert(UsageEntryIt == UsageEntryEnd);197 198 FOS << printInstructionString(MCI) << '\n';199 FOS.flush();200 OS << Buffer;201 Buffer = "";202 203 ++InstrIndex;204 }205}206 207json::Value ResourcePressureView::toJSON() const {208 // We're dumping the instructions and the ResourceUsage array.209 json::Array ResourcePressureInfo;210 211 // The ResourceUsage matrix is sparse, so we only consider212 // non-zero values.213 ArrayRef<llvm::MCInst> Source = getSource();214 const unsigned Executions = LastInstructionIdx / Source.size() + 1;215 216 auto AddToJSON = [&ResourcePressureInfo, Executions](217 const ResourceReleaseAtCycles &RU, unsigned InstIndex) {218 assert(RU.Cycles.getNumerator() != 0);219 double Usage = RU.Cycles / Executions;220 ResourcePressureInfo.push_back(221 json::Object({{"InstructionIndex", InstIndex},222 {"ResourceIndex", RU.ResourceIdx},223 {"ResourceUsage", Usage}}));224 };225 for (const auto &[InstIndex, Usages] : enumerate(ResourceUsage))226 for (const auto &RU : Usages)227 AddToJSON(RU, InstIndex);228 for (const auto &RU : CommonResourceUsage) {229 if (RU.Cycles.getNumerator() != 0)230 AddToJSON(RU, Source.size());231 }232 233 json::Object JO({{"ResourcePressureInfo", std::move(ResourcePressureInfo)}});234 return JO;235}236} // namespace mca237} // namespace llvm238