| //===--------------------- InstructionInfoView.cpp --------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// \file | |
| /// | |
| /// This file implements the InstructionInfoView API. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "Views/InstructionInfoView.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Support/FormattedStream.h" | |
| #include "llvm/Support/JSON.h" | |
| #include "llvm/Support/WithColor.h" | |
| namespace llvm { | |
| namespace mca { | |
| void InstructionInfoView::getComment(raw_ostream &OS, const MCInst &MCI) const { | |
| StringRef S = MCI.getLoc().getPointer(); | |
| size_t Pos = 0, PosCmt = 0; | |
| // Recognized comments are after assembly instructions on the same line. | |
| // It is usefull to add in comment scheduling information from architecture | |
| // specification. | |
| // '#' comment mark is not supported by llvm-mca | |
| if (Pos = S.find("\n"); Pos != StringRef::npos) { | |
| StringRef InstrStr = S.take_front(Pos); | |
| // C style comment | |
| if (((PosCmt = InstrStr.find("/*")) != StringRef::npos) && | |
| ((Pos = InstrStr.find("*/")) != StringRef::npos)) { | |
| OS << InstrStr.substr(PosCmt, Pos); | |
| return; | |
| } | |
| // C++ style comment | |
| if ((PosCmt = InstrStr.find("//")) != StringRef::npos) { | |
| OS << InstrStr.substr(PosCmt); | |
| } | |
| } | |
| } | |
| void InstructionInfoView::printView(raw_ostream &OS) const { | |
| std::string Buffer; | |
| raw_string_ostream TempStream(Buffer); | |
| formatted_raw_ostream FOS(TempStream); | |
| ArrayRef<llvm::MCInst> Source = getSource(); | |
| if (!Source.size()) | |
| return; | |
| IIVDVec IIVD(Source.size()); | |
| collectData(IIVD); | |
| if (PrintFullInfo) { | |
| FOS << "\n\nResources:\n"; | |
| const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); | |
| for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds(); | |
| I < E; ++I) { | |
| const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); | |
| unsigned NumUnits = ProcResource.NumUnits; | |
| // Skip invalid resources with zero units. | |
| if (!NumUnits) | |
| continue; | |
| FOS << '[' << ResourceIndex << ']'; | |
| FOS.PadToColumn(6); | |
| FOS << "- " << ProcResource.Name << ':' << NumUnits; | |
| if (ProcResource.SubUnitsIdxBegin) { | |
| FOS.PadToColumn(20); | |
| for (unsigned U = 0; U < NumUnits; ++U) { | |
| FOS << SM.getProcResource(ProcResource.SubUnitsIdxBegin[U])->Name; | |
| if ((U + 1) < NumUnits) | |
| FOS << ", "; | |
| } | |
| } | |
| FOS << '\n'; | |
| ResourceIndex++; | |
| } | |
| } | |
| SmallVector<unsigned, 16> Paddings = {0, 7, 14, 21, 28, 35, 42}; | |
| SmallVector<StringRef, 16> Fields = {"#uOps", "Latency", | |
| "RThroughput", "MayLoad", | |
| "MayStore", "HasSideEffects (U)"}; | |
| SmallVector<StringRef, 8> EndFields; | |
| unsigned LastPadding = Paddings.back(); | |
| if (PrintFullInfo) { | |
| Fields.push_back("Bypass Latency"); | |
| // Reserving 7 chars for | |
| Paddings.push_back(LastPadding += 7); | |
| Fields.push_back("Resources (<Name> | <Name>[<ReleaseAtCycle>] | " | |
| "<Name>[<AcquireAtCycle>,<ReleaseAtCycle])"); | |
| Paddings.push_back(LastPadding += 43); | |
| Fields.push_back("LLVM Opcode Name"); | |
| Paddings.push_back(LastPadding += 27); | |
| } | |
| if (PrintBarriers) { | |
| Fields.push_back("LoadBarrier"); | |
| Paddings.push_back(LastPadding += 7); | |
| Fields.push_back("StoreBarrier"); | |
| Paddings.push_back(LastPadding += 7); | |
| } | |
| if (PrintEncodings) { | |
| Fields.push_back("Encoding Size"); | |
| Paddings.push_back(LastPadding += 7); | |
| EndFields.push_back("Encodings:"); | |
| Paddings.push_back(LastPadding += 30); | |
| } | |
| EndFields.push_back("Instructions:"); | |
| FOS << "\n\nInstruction Info:\n"; | |
| for (unsigned i = 0, N = Fields.size(); i < N; i++) | |
| FOS << "[" << i + 1 << "]: " << Fields[i] << "\n"; | |
| FOS << "\n"; | |
| for (unsigned i = 0, N = Paddings.size(); i < N; i++) { | |
| if (Paddings[i]) | |
| FOS.PadToColumn(Paddings[i]); | |
| if (i < Fields.size()) | |
| FOS << "[" << i + 1 << "]"; | |
| else | |
| FOS << EndFields[i - Fields.size()]; | |
| } | |
| FOS << "\n"; | |
| for (const auto &[Index, IIVDEntry, Inst] : enumerate(IIVD, Source)) { | |
| FOS.PadToColumn(Paddings[0] + 1); | |
| FOS << IIVDEntry.NumMicroOpcodes; | |
| FOS.PadToColumn(Paddings[1] + 1); | |
| FOS << IIVDEntry.Latency; | |
| FOS.PadToColumn(Paddings[2]); | |
| if (IIVDEntry.RThroughput) { | |
| double RT = *IIVDEntry.RThroughput; | |
| FOS << format("%.2f", floor((RT * 100) + 0.5) / 100); | |
| } else { | |
| FOS << " -"; | |
| } | |
| FOS.PadToColumn(Paddings[3] + 1); | |
| FOS << (IIVDEntry.mayLoad ? "*" : " "); | |
| FOS.PadToColumn(Paddings[4] + 1); | |
| FOS << (IIVDEntry.mayStore ? "*" : " "); | |
| FOS.PadToColumn(Paddings[5] + 1); | |
| FOS << (IIVDEntry.hasUnmodeledSideEffects ? "U" : " "); | |
| unsigned LastPaddingIdx = 5; | |
| if (PrintFullInfo) { | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1); | |
| FOS << IIVDEntry.Bypass; | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1]); | |
| FOS << IIVDEntry.Resources; | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1]); | |
| FOS << IIVDEntry.OpcodeName; | |
| } | |
| if (PrintBarriers) { | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1); | |
| FOS << (LoweredInsts[Index]->isALoadBarrier() ? "*" : " "); | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1); | |
| FOS << (LoweredInsts[Index]->isAStoreBarrier() ? "*" : " "); | |
| } | |
| if (PrintEncodings) { | |
| StringRef Encoding(CE.getEncoding(Index)); | |
| unsigned EncodingSize = Encoding.size(); | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1] + 1); | |
| FOS << EncodingSize; | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1]); | |
| for (unsigned i = 0, e = Encoding.size(); i != e; ++i) | |
| FOS << format("%02x ", (uint8_t)Encoding[i]); | |
| } | |
| FOS.PadToColumn(Paddings[LastPaddingIdx += 1]); | |
| FOS << printInstructionString(Inst); | |
| if (PrintFullInfo) { | |
| FOS << "\t"; | |
| getComment(FOS, Inst); | |
| } | |
| FOS << '\n'; | |
| } | |
| OS << Buffer; | |
| } | |
| void InstructionInfoView::collectData( | |
| MutableArrayRef<InstructionInfoViewData> IIVD) const { | |
| const llvm::MCSubtargetInfo &STI = getSubTargetInfo(); | |
| const MCSchedModel &SM = STI.getSchedModel(); | |
| for (const auto I : zip(getSource(), IIVD)) { | |
| const MCInst &Inst = std::get<0>(I); | |
| InstructionInfoViewData &IIVDEntry = std::get<1>(I); | |
| const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode()); | |
| // Obtain the scheduling class information from the instruction | |
| // and instruments. | |
| auto IVecIt = InstToInstruments.find(&Inst); | |
| unsigned SchedClassID = | |
| IVecIt == InstToInstruments.end() | |
| ? MCDesc.getSchedClass() | |
| : IM.getSchedClassID(MCII, Inst, IVecIt->second); | |
| unsigned CPUID = SM.getProcessorID(); | |
| // Try to solve variant scheduling classes. | |
| while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant()) | |
| SchedClassID = | |
| STI.resolveVariantSchedClass(SchedClassID, &Inst, &MCII, CPUID); | |
| const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); | |
| IIVDEntry.NumMicroOpcodes = SCDesc.NumMicroOps; | |
| IIVDEntry.Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); | |
| // Add extra latency due to delays in the forwarding data paths. | |
| IIVDEntry.Latency += MCSchedModel::getForwardingDelayCycles( | |
| STI.getReadAdvanceEntries(SCDesc)); | |
| IIVDEntry.RThroughput = MCSchedModel::getReciprocalThroughput(STI, SCDesc); | |
| IIVDEntry.mayLoad = MCDesc.mayLoad(); | |
| IIVDEntry.mayStore = MCDesc.mayStore(); | |
| IIVDEntry.hasUnmodeledSideEffects = MCDesc.hasUnmodeledSideEffects(); | |
| if (PrintFullInfo) { | |
| // Get latency with bypass | |
| IIVDEntry.Bypass = | |
| IIVDEntry.Latency - MCSchedModel::getBypassDelayCycles(STI, SCDesc); | |
| IIVDEntry.OpcodeName = MCII.getName(Inst.getOpcode()); | |
| raw_string_ostream TempStream(IIVDEntry.Resources); | |
| const MCWriteProcResEntry *Index = STI.getWriteProcResBegin(&SCDesc); | |
| const MCWriteProcResEntry *Last = STI.getWriteProcResEnd(&SCDesc); | |
| ListSeparator LS(","); | |
| for (; Index != Last; ++Index) { | |
| if (!Index->ReleaseAtCycle) | |
| continue; | |
| const MCProcResourceDesc *MCProc = | |
| SM.getProcResource(Index->ProcResourceIdx); | |
| if (Index->ReleaseAtCycle > 1) { | |
| // Output ReleaseAtCycle between [] if not 1 (default) | |
| // This is to be able to evaluate throughput. | |
| // See getReciprocalThroughput in MCSchedule.cpp | |
| if (Index->AcquireAtCycle > 0) | |
| TempStream << LS | |
| << format("%s[%d,%d]", MCProc->Name, | |
| Index->AcquireAtCycle, Index->ReleaseAtCycle); | |
| else | |
| TempStream << LS | |
| << format("%s[%d]", MCProc->Name, Index->ReleaseAtCycle); | |
| } else { | |
| TempStream << LS << MCProc->Name; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Construct a JSON object from a single InstructionInfoViewData object. | |
| json::Object | |
| InstructionInfoView::toJSON(const InstructionInfoViewData &IIVD) const { | |
| json::Object JO({{"NumMicroOpcodes", IIVD.NumMicroOpcodes}, | |
| {"Latency", IIVD.Latency}, | |
| {"mayLoad", IIVD.mayLoad}, | |
| {"mayStore", IIVD.mayStore}, | |
| {"hasUnmodeledSideEffects", IIVD.hasUnmodeledSideEffects}}); | |
| JO.try_emplace("RThroughput", IIVD.RThroughput.value_or(0.0)); | |
| return JO; | |
| } | |
| json::Value InstructionInfoView::toJSON() const { | |
| ArrayRef<llvm::MCInst> Source = getSource(); | |
| if (!Source.size()) | |
| return json::Value(0); | |
| IIVDVec IIVD(Source.size()); | |
| collectData(IIVD); | |
| json::Array InstInfo; | |
| for (const auto &I : enumerate(IIVD)) { | |
| const InstructionInfoViewData &IIVDEntry = I.value(); | |
| json::Object JO = toJSON(IIVDEntry); | |
| JO.try_emplace("Instruction", (unsigned)I.index()); | |
| InstInfo.push_back(std::move(JO)); | |
| } | |
| return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}}); | |
| } | |
| } // namespace mca. | |
| } // namespace llvm |