blob: c708ab8647734b8c1b76d6a2a974467a987f6f97 [file] [log] [blame] [edit]
//===--------------------- 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", RT);
} 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