1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 12:12:47 +01:00
llvm-mirror/tools/llvm-mca/llvm-mca.cpp
Patrick Holland e52d4f2208 [MCA] Adding the CustomBehaviour class to llvm-mca
Some instructions are not defined well enough within the target’s scheduling
model for llvm-mca to be able to properly simulate its behaviour. The ideal
solution to this situation is to modify the scheduling model, but that’s not
always a viable strategy. Maybe other parts of the backend depend on that
instruction being modelled the way that it is. Or maybe the instruction is quite
complex and it’s difficult to fully capture its behaviour with tablegen. The
CustomBehaviour class (which I will refer to as CB frequently) is designed to
provide intuitive scaffolding for developers to implement the correct modelling
for these instructions.

Implementation details:

llvm-mca does its best to extract relevant register, resource, and memory
information from every MCInst when lowering them to an mca::Instruction. It then
uses this information to detect dependencies and simulate stalls within the
pipeline. For some instructions, the information that gets captured within the
mca::Instruction is not enough for mca to simulate them properly. In these
cases, there are two main possibilities:

1. The instruction has a dependency that isn’t detected by mca.
2. mca is incorrectly enforcing a dependency that shouldn’t exist.

For the rest of this discussion, I will be focusing on (1), but I have put some
thought into (2) and I may revisit it in the future.

So we have an instruction that has dependencies that aren’t picked up by mca.
The basic idea for both pipelines in mca is that when an instruction wants to be
dispatched, we first check for register hazards and then we check for resource
hazards. This is where CB is injected. If no register or resource hazards have
been detected, we make a call to CustomBehaviour::checkCustomHazard() to give
the target specific CB the chance to detect and enforce any custom dependencies.

The return value for checkCustomHazaard() is an unsigned int representing the
(minimum) number of cycles that the instruction needs to stall for. It’s fine to
underestimate this value because when StallCycles gets down to 0, we’ll end up
checking for all the hazards again before the instruction is actually
dispatched. However, it’s important not to overestimate the value and the more
accurate your estimate is, the more efficient mca’s execution can be.

In general, for checkCustomHazard() to be able to detect these custom
dependencies, it needs information about the current instruction and also all of
the instructions that are still executing within the pipeline. The mca pipeline
uses mca::Instruction rather than MCInst and the current information encoded
within each mca::Instruction isn’t sufficient for my use cases. I had to add a
few extra attributes to the mca::Instruction class and have them get set by the
MCInst during instruction building. For example, the current mca::Instruction
doesn’t know its opcode, and it also doesn’t know anything about its immediate
operands (both of which I had to add to the class).

With information about the current instruction, a list of all currently
executing instructions, and some target specific objects (MCSubtargetInfo and
MCInstrInfo which the base CB class has references to), developers should be
able to detect and enforce most custom dependencies within checkCustomHazard. If
you need more information than is present in the mca::Instruction, feel free to
add attributes to that class and have them set during the lowering sequence from
MCInst.

Fortunately, in the in-order pipeline, it’s very convenient for us to pass these
arguments to checkCustomHazard. The hazard checking is taken care of within
InOrderIssueStage::canExecute(). This function takes a const InstRef as a
parameter (representing the instruction that currently wants to be dispatched)
and the InOrderIssueStage class maintains a SmallVector<InstRef, 4> which holds
all of the currently executing instructions. For the out-of-order pipeline, it’s
a bit trickier to get the list of executing instructions and this is why I have
held off on implementing it myself. This is the main topic I will bring up when
I eventually make a post to discuss and ask for feedback.

CB is a base class where targets implement their own derived classes. If a
target specific CB does not exist (or we pass in the -disable-cb flag), the base
class is used. This base class trivially returns 0 from its checkCustomHazard()
implementation (meaning that the current instruction needs to stall for 0 cycles
aka no hazard is detected). For this reason, targets or users who choose not to
use CB shouldn’t see any negative impacts to accuracy or performance (in
comparison to pre-patch llvm-mca).

Differential Revision: https://reviews.llvm.org/D104149
2021-06-15 21:30:48 +01:00

669 lines
24 KiB
C++

//===-- llvm-mca.cpp - Machine Code Analyzer -------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This utility is a simple driver that allows static performance analysis on
// machine code similarly to how IACA (Intel Architecture Code Analyzer) works.
//
// llvm-mca [options] <file-name>
// -march <type>
// -mcpu <cpu>
// -o <file>
//
// The target defaults to the host target.
// The cpu defaults to the 'native' host cpu.
// The output defaults to standard output.
//
//===----------------------------------------------------------------------===//
#include "CodeRegion.h"
#include "CodeRegionGenerator.h"
#include "PipelinePrinter.h"
#include "Views/BottleneckAnalysis.h"
#include "Views/DispatchStatistics.h"
#include "Views/InstructionInfoView.h"
#include "Views/RegisterFileStatistics.h"
#include "Views/ResourcePressureView.h"
#include "Views/RetireControlUnitStatistics.h"
#include "Views/SchedulerStatistics.h"
#include "Views/SummaryView.h"
#include "Views/TimelineView.h"
#include "lib/AMDGPU/AMDGPUCustomBehaviour.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/MCA/CodeEmitter.h"
#include "llvm/MCA/Context.h"
#include "llvm/MCA/CustomBehaviour.h"
#include "llvm/MCA/InstrBuilder.h"
#include "llvm/MCA/Pipeline.h"
#include "llvm/MCA/Stages/EntryStage.h"
#include "llvm/MCA/Stages/InstructionTables.h"
#include "llvm/MCA/Support.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
using namespace llvm;
static mc::RegisterMCTargetOptionsFlags MOF;
static cl::OptionCategory ToolOptions("Tool Options");
static cl::OptionCategory ViewOptions("View Options");
static cl::opt<std::string> InputFilename(cl::Positional,
cl::desc("<input file>"),
cl::cat(ToolOptions), cl::init("-"));
static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
cl::init("-"), cl::cat(ToolOptions),
cl::value_desc("filename"));
static cl::opt<std::string>
ArchName("march",
cl::desc("Target architecture. "
"See -version for available targets"),
cl::cat(ToolOptions));
static cl::opt<std::string>
TripleName("mtriple",
cl::desc("Target triple. See -version for available targets"),
cl::cat(ToolOptions));
static cl::opt<std::string>
MCPU("mcpu",
cl::desc("Target a specific cpu type (-mcpu=help for details)"),
cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native"));
static cl::opt<std::string>
MATTR("mattr",
cl::desc("Additional target features."),
cl::cat(ToolOptions));
static cl::opt<bool>
PrintJson("json",
cl::desc("Print the output in json format"),
cl::cat(ToolOptions), cl::init(false));
static cl::opt<int>
OutputAsmVariant("output-asm-variant",
cl::desc("Syntax variant to use for output printing"),
cl::cat(ToolOptions), cl::init(-1));
static cl::opt<bool>
PrintImmHex("print-imm-hex", cl::cat(ToolOptions), cl::init(false),
cl::desc("Prefer hex format when printing immediate values"));
static cl::opt<unsigned> Iterations("iterations",
cl::desc("Number of iterations to run"),
cl::cat(ToolOptions), cl::init(0));
static cl::opt<unsigned>
DispatchWidth("dispatch", cl::desc("Override the processor dispatch width"),
cl::cat(ToolOptions), cl::init(0));
static cl::opt<unsigned>
RegisterFileSize("register-file-size",
cl::desc("Maximum number of physical registers which can "
"be used for register mappings"),
cl::cat(ToolOptions), cl::init(0));
static cl::opt<unsigned>
MicroOpQueue("micro-op-queue-size", cl::Hidden,
cl::desc("Number of entries in the micro-op queue"),
cl::cat(ToolOptions), cl::init(0));
static cl::opt<unsigned>
DecoderThroughput("decoder-throughput", cl::Hidden,
cl::desc("Maximum throughput from the decoders "
"(instructions per cycle)"),
cl::cat(ToolOptions), cl::init(0));
static cl::opt<bool>
PrintRegisterFileStats("register-file-stats",
cl::desc("Print register file statistics"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool> PrintDispatchStats("dispatch-stats",
cl::desc("Print dispatch statistics"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool>
PrintSummaryView("summary-view", cl::Hidden,
cl::desc("Print summary view (enabled by default)"),
cl::cat(ViewOptions), cl::init(true));
static cl::opt<bool> PrintSchedulerStats("scheduler-stats",
cl::desc("Print scheduler statistics"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool>
PrintRetireStats("retire-stats",
cl::desc("Print retire control unit statistics"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool> PrintResourcePressureView(
"resource-pressure",
cl::desc("Print the resource pressure view (enabled by default)"),
cl::cat(ViewOptions), cl::init(true));
static cl::opt<bool> PrintTimelineView("timeline",
cl::desc("Print the timeline view"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<unsigned> TimelineMaxIterations(
"timeline-max-iterations",
cl::desc("Maximum number of iterations to print in timeline view"),
cl::cat(ViewOptions), cl::init(0));
static cl::opt<unsigned> TimelineMaxCycles(
"timeline-max-cycles",
cl::desc(
"Maximum number of cycles in the timeline view. Defaults to 80 cycles"),
cl::cat(ViewOptions), cl::init(80));
static cl::opt<bool>
AssumeNoAlias("noalias",
cl::desc("If set, assume that loads and stores do not alias"),
cl::cat(ToolOptions), cl::init(true));
static cl::opt<unsigned> LoadQueueSize("lqueue",
cl::desc("Size of the load queue"),
cl::cat(ToolOptions), cl::init(0));
static cl::opt<unsigned> StoreQueueSize("squeue",
cl::desc("Size of the store queue"),
cl::cat(ToolOptions), cl::init(0));
static cl::opt<bool>
PrintInstructionTables("instruction-tables",
cl::desc("Print instruction tables"),
cl::cat(ToolOptions), cl::init(false));
static cl::opt<bool> PrintInstructionInfoView(
"instruction-info",
cl::desc("Print the instruction info view (enabled by default)"),
cl::cat(ViewOptions), cl::init(true));
static cl::opt<bool> EnableAllStats("all-stats",
cl::desc("Print all hardware statistics"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool>
EnableAllViews("all-views",
cl::desc("Print all views including hardware statistics"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool> EnableBottleneckAnalysis(
"bottleneck-analysis",
cl::desc("Enable bottleneck analysis (disabled by default)"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool> ShowEncoding(
"show-encoding",
cl::desc("Print encoding information in the instruction info view"),
cl::cat(ViewOptions), cl::init(false));
static cl::opt<bool> DisableCustomBehaviour(
"disable-cb",
cl::desc(
"Disable custom behaviour (use the default class which does nothing)."),
cl::cat(ViewOptions), cl::init(false));
namespace {
const Target *getTarget(const char *ProgName) {
if (TripleName.empty())
TripleName = Triple::normalize(sys::getDefaultTargetTriple());
Triple TheTriple(TripleName);
// Get the target specific parser.
std::string Error;
const Target *TheTarget =
TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
if (!TheTarget) {
errs() << ProgName << ": " << Error;
return nullptr;
}
// Update TripleName with the updated triple from the target lookup.
TripleName = TheTriple.str();
// Return the found target.
return TheTarget;
}
ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
if (OutputFilename == "")
OutputFilename = "-";
std::error_code EC;
auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC,
sys::fs::OF_TextWithCRLF);
if (!EC)
return std::move(Out);
return EC;
}
} // end of anonymous namespace
static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) {
if (!O.getNumOccurrences() || O.getPosition() < Default.getPosition())
O = Default.getValue();
}
static void processViewOptions(bool IsOutOfOrder) {
if (!EnableAllViews.getNumOccurrences() &&
!EnableAllStats.getNumOccurrences())
return;
if (EnableAllViews.getNumOccurrences()) {
processOptionImpl(PrintSummaryView, EnableAllViews);
if (IsOutOfOrder)
processOptionImpl(EnableBottleneckAnalysis, EnableAllViews);
processOptionImpl(PrintResourcePressureView, EnableAllViews);
processOptionImpl(PrintTimelineView, EnableAllViews);
processOptionImpl(PrintInstructionInfoView, EnableAllViews);
}
const cl::opt<bool> &Default =
EnableAllViews.getPosition() < EnableAllStats.getPosition()
? EnableAllStats
: EnableAllViews;
processOptionImpl(PrintRegisterFileStats, Default);
processOptionImpl(PrintDispatchStats, Default);
processOptionImpl(PrintSchedulerStats, Default);
if (IsOutOfOrder)
processOptionImpl(PrintRetireStats, Default);
}
std::unique_ptr<mca::InstrPostProcess>
createInstrPostProcess(const Triple &TheTriple, const MCSubtargetInfo &STI,
const MCInstrInfo &MCII) {
// Might be a good idea to have a separate flag so that InstrPostProcess
// can be used with or without CustomBehaviour
if (DisableCustomBehaviour)
return std::make_unique<mca::InstrPostProcess>(STI, MCII);
if (TheTriple.isAMDGPU())
return std::make_unique<mca::AMDGPUInstrPostProcess>(STI, MCII);
return std::make_unique<mca::InstrPostProcess>(STI, MCII);
}
std::unique_ptr<mca::CustomBehaviour>
createCustomBehaviour(const Triple &TheTriple, const MCSubtargetInfo &STI,
const mca::SourceMgr &SrcMgr, const MCInstrInfo &MCII) {
// Build the appropriate CustomBehaviour object for the current target.
// The CustomBehaviour class should never depend on the source code,
// but it can depend on the list of mca::Instruction and any classes
// that can be built using just the target info. If you need extra
// information from the source code or the list of MCInst, consider
// adding that information to the mca::Instruction class and setting
// it during InstrBuilder::createInstruction().
if (DisableCustomBehaviour)
return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII);
if (TheTriple.isAMDGPU())
return std::make_unique<mca::AMDGPUCustomBehaviour>(STI, SrcMgr, MCII);
return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII);
}
// Returns true on success.
static bool runPipeline(mca::Pipeline &P) {
// Handle pipeline errors here.
Expected<unsigned> Cycles = P.run();
if (!Cycles) {
WithColor::error() << toString(Cycles.takeError());
return false;
}
return true;
}
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
// Initialize targets and assembly parsers.
InitializeAllTargetInfos();
InitializeAllTargetMCs();
InitializeAllAsmParsers();
// Enable printing of available targets when flag --version is specified.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
cl::HideUnrelatedOptions({&ToolOptions, &ViewOptions});
// Parse flags and initialize target options.
cl::ParseCommandLineOptions(argc, argv,
"llvm machine code performance analyzer.\n");
// Get the target from the triple. If a triple is not specified, then select
// the default triple for the host. If the triple doesn't correspond to any
// registered target, then exit with an error message.
const char *ProgName = argv[0];
const Target *TheTarget = getTarget(ProgName);
if (!TheTarget)
return 1;
// GetTarget() may replaced TripleName with a default triple.
// For safety, reconstruct the Triple object.
Triple TheTriple(TripleName);
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
MemoryBuffer::getFileOrSTDIN(InputFilename);
if (std::error_code EC = BufferPtr.getError()) {
WithColor::error() << InputFilename << ": " << EC.message() << '\n';
return 1;
}
if (MCPU == "native")
MCPU = std::string(llvm::sys::getHostCPUName());
std::unique_ptr<MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, MCPU, MATTR));
assert(STI && "Unable to create subtarget info!");
if (!STI->isCPUStringValid(MCPU))
return 1;
bool IsOutOfOrder = STI->getSchedModel().isOutOfOrder();
if (!PrintInstructionTables && !IsOutOfOrder) {
WithColor::warning() << "support for in-order CPU '" << MCPU
<< "' is experimental.\n";
}
if (!STI->getSchedModel().hasInstrSchedModel()) {
WithColor::error()
<< "unable to find instruction-level scheduling information for"
<< " target triple '" << TheTriple.normalize() << "' and cpu '" << MCPU
<< "'.\n";
if (STI->getSchedModel().InstrItineraries)
WithColor::note()
<< "cpu '" << MCPU << "' provides itineraries. However, "
<< "instruction itineraries are currently unsupported.\n";
return 1;
}
// Apply overrides to llvm-mca specific options.
processViewOptions(IsOutOfOrder);
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
assert(MRI && "Unable to create target register info!");
MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
assert(MAI && "Unable to create target asm info!");
SourceMgr SrcMgr;
// Tell SrcMgr about this buffer, which is what the parser will pick up.
SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr);
std::unique_ptr<MCObjectFileInfo> MOFI(
TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
Ctx.setObjectFileInfo(MOFI.get());
std::unique_ptr<buffer_ostream> BOS;
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
assert(MCII && "Unable to create instruction info!");
std::unique_ptr<MCInstrAnalysis> MCIA(
TheTarget->createMCInstrAnalysis(MCII.get()));
// Need to initialize an MCInstPrinter as it is
// required for initializing the MCTargetStreamer
// which needs to happen within the CRG.parseCodeRegions() call below.
// Without an MCTargetStreamer, certain assembly directives can trigger a
// segfault. (For example, the .cv_fpo_proc directive on x86 will segfault if
// we don't initialize the MCTargetStreamer.)
unsigned IPtempOutputAsmVariant =
OutputAsmVariant == -1 ? 0 : OutputAsmVariant;
std::unique_ptr<MCInstPrinter> IPtemp(TheTarget->createMCInstPrinter(
Triple(TripleName), IPtempOutputAsmVariant, *MAI, *MCII, *MRI));
if (!IPtemp) {
WithColor::error()
<< "unable to create instruction printer for target triple '"
<< TheTriple.normalize() << "' with assembly variant "
<< IPtempOutputAsmVariant << ".\n";
return 1;
}
// Parse the input and create CodeRegions that llvm-mca can analyze.
mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII);
Expected<const mca::CodeRegions &> RegionsOrErr =
CRG.parseCodeRegions(std::move(IPtemp));
if (!RegionsOrErr) {
if (auto Err =
handleErrors(RegionsOrErr.takeError(), [](const StringError &E) {
WithColor::error() << E.getMessage() << '\n';
})) {
// Default case.
WithColor::error() << toString(std::move(Err)) << '\n';
}
return 1;
}
const mca::CodeRegions &Regions = *RegionsOrErr;
// Early exit if errors were found by the code region parsing logic.
if (!Regions.isValid())
return 1;
if (Regions.empty()) {
WithColor::error() << "no assembly instructions found.\n";
return 1;
}
// Now initialize the output file.
auto OF = getOutputStream();
if (std::error_code EC = OF.getError()) {
WithColor::error() << EC.message() << '\n';
return 1;
}
unsigned AssemblerDialect = CRG.getAssemblerDialect();
if (OutputAsmVariant >= 0)
AssemblerDialect = static_cast<unsigned>(OutputAsmVariant);
std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
Triple(TripleName), AssemblerDialect, *MAI, *MCII, *MRI));
if (!IP) {
WithColor::error()
<< "unable to create instruction printer for target triple '"
<< TheTriple.normalize() << "' with assembly variant "
<< AssemblerDialect << ".\n";
return 1;
}
// Set the display preference for hex vs. decimal immediates.
IP->setPrintImmHex(PrintImmHex);
std::unique_ptr<ToolOutputFile> TOF = std::move(*OF);
const MCSchedModel &SM = STI->getSchedModel();
// Create an instruction builder.
mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());
// Create a context to control ownership of the pipeline hardware.
mca::Context MCA(*MRI, *STI);
mca::PipelineOptions PO(MicroOpQueue, DecoderThroughput, DispatchWidth,
RegisterFileSize, LoadQueueSize, StoreQueueSize,
AssumeNoAlias, EnableBottleneckAnalysis);
// Number each region in the sequence.
unsigned RegionIdx = 0;
std::unique_ptr<MCCodeEmitter> MCE(
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
assert(MCE && "Unable to create code emitter!");
std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend(
*STI, *MRI, mc::InitMCTargetOptionsFromFlags()));
assert(MAB && "Unable to create asm backend!");
for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) {
// Skip empty code regions.
if (Region->empty())
continue;
// Don't print the header of this region if it is the default region, and
// it doesn't have an end location.
if (Region->startLoc().isValid() || Region->endLoc().isValid()) {
TOF->os() << "\n[" << RegionIdx++ << "] Code Region";
StringRef Desc = Region->getDescription();
if (!Desc.empty())
TOF->os() << " - " << Desc;
TOF->os() << "\n\n";
}
// Lower the MCInst sequence into an mca::Instruction sequence.
ArrayRef<MCInst> Insts = Region->getInstructions();
mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts);
std::unique_ptr<mca::InstrPostProcess> IPP =
createInstrPostProcess(TheTriple, *STI, *MCII);
std::vector<std::unique_ptr<mca::Instruction>> LoweredSequence;
for (const MCInst &MCI : Insts) {
Expected<std::unique_ptr<mca::Instruction>> Inst =
IB.createInstruction(MCI);
if (!Inst) {
if (auto NewE = handleErrors(
Inst.takeError(),
[&IP, &STI](const mca::InstructionError<MCInst> &IE) {
std::string InstructionStr;
raw_string_ostream SS(InstructionStr);
WithColor::error() << IE.Message << '\n';
IP->printInst(&IE.Inst, 0, "", *STI, SS);
SS.flush();
WithColor::note()
<< "instruction: " << InstructionStr << '\n';
})) {
// Default case.
WithColor::error() << toString(std::move(NewE));
}
return 1;
}
IPP->postProcessInstruction(Inst.get(), MCI);
LoweredSequence.emplace_back(std::move(Inst.get()));
}
mca::SourceMgr S(LoweredSequence, PrintInstructionTables ? 1 : Iterations);
if (PrintInstructionTables) {
// Create a pipeline, stages, and a printer.
auto P = std::make_unique<mca::Pipeline>();
P->appendStage(std::make_unique<mca::EntryStage>(S));
P->appendStage(std::make_unique<mca::InstructionTables>(SM));
mca::PipelinePrinter Printer(*P, mca::View::OK_READABLE);
// Create the views for this pipeline, execute, and emit a report.
if (PrintInstructionInfoView) {
Printer.addView(std::make_unique<mca::InstructionInfoView>(
*STI, *MCII, CE, ShowEncoding, Insts, *IP));
}
Printer.addView(
std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
if (!runPipeline(*P))
return 1;
Printer.printReport(TOF->os());
continue;
}
// Create the CustomBehaviour object for enforcing Target Specific
// behaviours and dependencies that aren't expressed well enough
// in the tablegen. CB cannot depend on the list of MCInst or
// the source code (but it can depend on the list of
// mca::Instruction or any objects that can be reconstructed
// from the target information).
std::unique_ptr<mca::CustomBehaviour> CB =
createCustomBehaviour(TheTriple, *STI, S, *MCII);
// Create a basic pipeline simulating an out-of-order backend.
auto P = MCA.createDefaultPipeline(PO, S, *CB);
mca::PipelinePrinter Printer(*P, PrintJson ? mca::View::OK_JSON
: mca::View::OK_READABLE);
// When we output JSON, we add a view that contains the instructions
// and CPU resource information.
if (PrintJson)
Printer.addView(
std::make_unique<mca::InstructionView>(*STI, *IP, Insts, MCPU));
if (PrintSummaryView)
Printer.addView(
std::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth));
if (EnableBottleneckAnalysis) {
if (!IsOutOfOrder) {
WithColor::warning()
<< "bottleneck analysis is not supported for in-order CPU '" << MCPU
<< "'.\n";
}
Printer.addView(std::make_unique<mca::BottleneckAnalysis>(
*STI, *IP, Insts, S.getNumIterations()));
}
if (PrintInstructionInfoView)
Printer.addView(std::make_unique<mca::InstructionInfoView>(
*STI, *MCII, CE, ShowEncoding, Insts, *IP));
if (PrintDispatchStats)
Printer.addView(std::make_unique<mca::DispatchStatistics>());
if (PrintSchedulerStats)
Printer.addView(std::make_unique<mca::SchedulerStatistics>(*STI));
if (PrintRetireStats)
Printer.addView(std::make_unique<mca::RetireControlUnitStatistics>(SM));
if (PrintRegisterFileStats)
Printer.addView(std::make_unique<mca::RegisterFileStatistics>(*STI));
if (PrintResourcePressureView)
Printer.addView(
std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
if (PrintTimelineView) {
unsigned TimelineIterations =
TimelineMaxIterations ? TimelineMaxIterations : 10;
Printer.addView(std::make_unique<mca::TimelineView>(
*STI, *IP, Insts, std::min(TimelineIterations, S.getNumIterations()),
TimelineMaxCycles));
}
if (!runPipeline(*P))
return 1;
Printer.printReport(TOF->os());
// Clear the InstrBuilder internal state in preparation for another round.
IB.clear();
}
TOF->keep();
return 0;
}