mirror of
https://github.com/RPCS3/llvm.git
synced 2025-05-21 21:06:04 +00:00

The goal of this patch is to address most of PR36874. To fully fix PR36874 we need to split the "InstructionInfo" view from the "SummaryView". That would make easy to check the latency and rthroughput as well. The patch reuses all the logic from ResourcePressureView to print out the "instruction tables". We have an entry for every instruction in the input sequence. Each entry reports the theoretical resource pressure distribution. Resource pressure is uniformly distributed across all the processor resource units of a group. At the moment, the backend pipeline is not configurable, so the only way to fix this is by creating a different driver that simply sends instruction events to the resource pressure view. That means, we don't use the Backend interface. Instead, it is simpler to just have a different code-path for when flag -instruction-tables is specified. Once Clement addresses bug 36663, then we can port the "instruction tables" logic into a stage of our configurable pipeline. Updated the BtVer2 test cases (thanks Simon for the help). Now we pass flag -instruction-tables to each modified test. Differential Revision: https://reviews.llvm.org/D44839 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@328487 91177308-0d34-0410-b5e6-96231b3b80d8
379 lines
13 KiB
C++
379 lines
13 KiB
C++
//===-- llvm-mca.cpp - Machine Code Analyzer -------------------*- C++ -* -===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 'generic'.
|
|
// The output defaults to standard output.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "BackendPrinter.h"
|
|
#include "BackendStatistics.h"
|
|
#include "InstructionInfoView.h"
|
|
#include "InstructionTables.h"
|
|
#include "ResourcePressureView.h"
|
|
#include "SummaryView.h"
|
|
#include "TimelineView.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCObjectFileInfo.h"
|
|
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/ErrorOr.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/PrettyStackTrace.h"
|
|
#include "llvm/Support/Signals.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static cl::opt<std::string>
|
|
InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
|
|
|
|
static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
|
|
cl::init("-"),
|
|
cl::value_desc("filename"));
|
|
|
|
static cl::opt<std::string>
|
|
ArchName("march", cl::desc("Target arch to assemble for, "
|
|
"see -version for available targets"));
|
|
|
|
static cl::opt<std::string>
|
|
TripleName("mtriple", cl::desc("Target triple to assemble for, "
|
|
"see -version for available targets"));
|
|
|
|
static cl::opt<std::string>
|
|
MCPU("mcpu",
|
|
cl::desc("Target a specific cpu type (-mcpu=help for details)"),
|
|
cl::value_desc("cpu-name"), cl::init("generic"));
|
|
|
|
static cl::opt<unsigned>
|
|
OutputAsmVariant("output-asm-variant",
|
|
cl::desc("Syntax variant to use for output printing"));
|
|
|
|
static cl::opt<unsigned> Iterations("iterations",
|
|
cl::desc("Number of iterations to run"),
|
|
cl::init(0));
|
|
|
|
static cl::opt<unsigned> DispatchWidth(
|
|
"dispatch",
|
|
cl::desc("Dispatch Width. By default it is set equal to IssueWidth"),
|
|
cl::init(0));
|
|
|
|
static cl::opt<unsigned> MaxRetirePerCycle(
|
|
"max-retire-per-cycle",
|
|
cl::desc("Maximum number of instructions that can be retired in one cycle"),
|
|
cl::init(0));
|
|
|
|
static cl::opt<unsigned>
|
|
RegisterFileSize("register-file-size",
|
|
cl::desc("Maximum number of temporary registers which can "
|
|
"be used for register mappings"),
|
|
cl::init(0));
|
|
|
|
static cl::opt<bool>
|
|
PrintResourcePressureView("resource-pressure",
|
|
cl::desc("Print the resource pressure view"),
|
|
cl::init(true));
|
|
|
|
static cl::opt<bool> PrintTimelineView("timeline",
|
|
cl::desc("Print the timeline view"),
|
|
cl::init(false));
|
|
|
|
static cl::opt<unsigned> TimelineMaxIterations(
|
|
"timeline-max-iterations",
|
|
cl::desc("Maximum number of iterations to print in timeline view"),
|
|
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::init(80));
|
|
|
|
static cl::opt<bool> PrintModeVerbose("verbose",
|
|
cl::desc("Enable verbose output"),
|
|
cl::init(false));
|
|
|
|
static cl::opt<bool> AssumeNoAlias(
|
|
"noalias",
|
|
cl::desc("If set, it assumes that loads and stores do not alias"),
|
|
cl::init(true));
|
|
|
|
static cl::opt<unsigned>
|
|
LoadQueueSize("lqueue", cl::desc("Size of the load queue"), cl::init(0));
|
|
|
|
static cl::opt<unsigned>
|
|
StoreQueueSize("squeue", cl::desc("Size of the store queue"), cl::init(0));
|
|
|
|
static cl::opt<bool>
|
|
PrintInstructionTables("instruction-tables",
|
|
cl::desc("Print instruction tables"),
|
|
cl::init(false));
|
|
|
|
static const Target *getTarget(const char *ProgName) {
|
|
TripleName = Triple::normalize(TripleName);
|
|
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;
|
|
}
|
|
|
|
// Return the found target.
|
|
return TheTarget;
|
|
}
|
|
|
|
static int AssembleInput(const char *ProgName, const Target *TheTarget,
|
|
SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str,
|
|
MCAsmInfo &MAI, MCSubtargetInfo &STI,
|
|
MCInstrInfo &MCII, MCTargetOptions &MCOptions) {
|
|
std::unique_ptr<MCAsmParser> Parser(createMCAsmParser(SrcMgr, Ctx, Str, MAI));
|
|
std::unique_ptr<MCTargetAsmParser> TAP(
|
|
TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions));
|
|
|
|
if (!TAP) {
|
|
errs() << ProgName
|
|
<< ": error: this target does not support assembly parsing.\n";
|
|
return 1;
|
|
}
|
|
|
|
Parser->setTargetParser(*TAP);
|
|
return Parser->Run(false);
|
|
}
|
|
|
|
static ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
|
|
if (OutputFilename == "")
|
|
OutputFilename = "-";
|
|
std::error_code EC;
|
|
auto Out =
|
|
llvm::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::F_None);
|
|
if (!EC)
|
|
return std::move(Out);
|
|
return EC;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class MCStreamerWrapper final : public MCStreamer {
|
|
using InstVec = std::vector<std::unique_ptr<const MCInst>>;
|
|
InstVec &Insts;
|
|
|
|
public:
|
|
MCStreamerWrapper(MCContext &Context, InstVec &Vec)
|
|
: MCStreamer(Context), Insts(Vec) {}
|
|
|
|
// We only want to intercept the emission of new instructions.
|
|
virtual void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI,
|
|
bool /* unused */) override {
|
|
Insts.emplace_back(new MCInst(Inst));
|
|
}
|
|
|
|
bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
|
|
return true;
|
|
}
|
|
|
|
void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
|
|
unsigned ByteAlignment) override {}
|
|
void EmitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
|
|
uint64_t Size = 0, unsigned ByteAlignment = 0) override {}
|
|
void EmitGPRel32Value(const MCExpr *Value) override {}
|
|
void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {}
|
|
void EmitCOFFSymbolStorageClass(int StorageClass) override {}
|
|
void EmitCOFFSymbolType(int Type) override {}
|
|
void EndCOFFSymbolDef() override {}
|
|
|
|
const InstVec &GetInstructionSequence() const { return Insts; }
|
|
};
|
|
} // end of anonymous namespace
|
|
|
|
int main(int argc, char **argv) {
|
|
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
|
PrettyStackTraceProgram X(argc, argv);
|
|
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
|
|
|
|
// Initialize targets and assembly parsers.
|
|
llvm::InitializeAllTargetInfos();
|
|
llvm::InitializeAllTargetMCs();
|
|
llvm::InitializeAllAsmParsers();
|
|
|
|
// Enable printing of available targets when flag --version is specified.
|
|
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
|
|
|
|
// Parse flags and initialize target options.
|
|
cl::ParseCommandLineOptions(argc, argv,
|
|
"llvm machine code performance analyzer.\n");
|
|
MCTargetOptions MCOptions;
|
|
MCOptions.PreserveAsmComments = false;
|
|
|
|
// 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()) {
|
|
errs() << InputFilename << ": " << EC.message() << '\n';
|
|
return 1;
|
|
}
|
|
|
|
SourceMgr SrcMgr;
|
|
|
|
// Tell SrcMgr about this buffer, which is what the parser will pick up.
|
|
SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
|
|
|
|
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
|
|
assert(MRI && "Unable to create target register info!");
|
|
|
|
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
|
|
assert(MAI && "Unable to create target asm info!");
|
|
|
|
MCObjectFileInfo MOFI;
|
|
MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr);
|
|
MOFI.InitMCObjectFileInfo(TheTriple, /* PIC= */ false, Ctx);
|
|
|
|
std::unique_ptr<buffer_ostream> BOS;
|
|
|
|
std::unique_ptr<mca::SourceMgr> S =
|
|
llvm::make_unique<mca::SourceMgr>(PrintInstructionTables ? 1 : Iterations);
|
|
MCStreamerWrapper Str(Ctx, S->getSequence());
|
|
|
|
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
|
|
std::unique_ptr<MCSubtargetInfo> STI(
|
|
TheTarget->createMCSubtargetInfo(TripleName, MCPU, /* FeaturesStr */ ""));
|
|
if (!STI->isCPUStringValid(MCPU))
|
|
return 1;
|
|
|
|
if (!STI->getSchedModel().isOutOfOrder()) {
|
|
errs() << "error: please specify an out-of-order cpu. '" << MCPU
|
|
<< "' is an in-order cpu.\n";
|
|
return 1;
|
|
}
|
|
|
|
if (!STI->getSchedModel().hasInstrSchedModel()) {
|
|
errs()
|
|
<< "error: unable to find instruction-level scheduling information for"
|
|
<< " target triple '" << TheTriple.normalize() << "' and cpu '" << MCPU
|
|
<< "'.\n";
|
|
|
|
if (STI->getSchedModel().InstrItineraries)
|
|
errs() << "note: cpu '" << MCPU << "' provides itineraries. However, "
|
|
<< "instruction itineraries are currently unsupported.\n";
|
|
return 1;
|
|
}
|
|
|
|
std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
|
|
Triple(TripleName), OutputAsmVariant, *MAI, *MCII, *MRI));
|
|
if (!IP) {
|
|
errs() << "error: unable to create instruction printer for target triple '"
|
|
<< TheTriple.normalize() << "' with assembly variant "
|
|
<< OutputAsmVariant << ".\n";
|
|
return 1;
|
|
}
|
|
|
|
int Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, Str, *MAI, *STI,
|
|
*MCII, MCOptions);
|
|
if (Res)
|
|
return Res;
|
|
|
|
if (S->isEmpty()) {
|
|
errs() << "error: no assembly instructions found.\n";
|
|
return 1;
|
|
}
|
|
|
|
// Now initialize the output file.
|
|
auto OF = getOutputStream();
|
|
if (std::error_code EC = OF.getError()) {
|
|
errs() << EC.message() << '\n';
|
|
return 1;
|
|
}
|
|
|
|
std::unique_ptr<llvm::ToolOutputFile> TOF = std::move(*OF);
|
|
|
|
const MCSchedModel &SM = STI->getSchedModel();
|
|
|
|
unsigned Width = SM.IssueWidth;
|
|
if (DispatchWidth)
|
|
Width = DispatchWidth;
|
|
|
|
// Create an instruction builder.
|
|
std::unique_ptr<mca::InstrBuilder> IB =
|
|
llvm::make_unique<mca::InstrBuilder>(*STI, *MCII);
|
|
|
|
if (PrintInstructionTables) {
|
|
mca::InstructionTables IT(STI->getSchedModel(), *IB, *S);
|
|
mca::ResourcePressureView RPV(*STI, *IP, *S);
|
|
mca::InstructionInfoView IIV(*STI, *MCII, *S, *IP);
|
|
IT.addEventListener(&IIV);
|
|
IT.addEventListener(&RPV);
|
|
IT.run();
|
|
RPV.printView(TOF->os());
|
|
TOF->keep();
|
|
return 0;
|
|
}
|
|
|
|
std::unique_ptr<mca::Backend> B = llvm::make_unique<mca::Backend>(
|
|
*STI, *MRI, *IB, *S, Width, RegisterFileSize, MaxRetirePerCycle,
|
|
LoadQueueSize, StoreQueueSize, AssumeNoAlias);
|
|
|
|
std::unique_ptr<mca::BackendPrinter> Printer =
|
|
llvm::make_unique<mca::BackendPrinter>(*B);
|
|
|
|
Printer->addView(llvm::make_unique<mca::SummaryView>(*S, Width));
|
|
|
|
Printer->addView(
|
|
llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, *S, *IP));
|
|
|
|
if (PrintModeVerbose)
|
|
Printer->addView(llvm::make_unique<mca::BackendStatistics>(*STI));
|
|
|
|
if (PrintResourcePressureView)
|
|
Printer->addView(
|
|
llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, *S));
|
|
|
|
if (PrintTimelineView) {
|
|
Printer->addView(llvm::make_unique<mca::TimelineView>(
|
|
*STI, *IP, *S, TimelineMaxIterations, TimelineMaxCycles));
|
|
}
|
|
|
|
B->run();
|
|
Printer->printReport(TOF->os());
|
|
TOF->keep();
|
|
|
|
return 0;
|
|
}
|