//===--------------------- 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 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 Paddings = {0, 7, 14, 21, 28, 35, 42}; SmallVector Fields = {"#uOps", "Latency", "RThroughput", "MayLoad", "MayStore", "HasSideEffects (U)"}; SmallVector 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 ( | [] | " "[,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 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 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