1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-02-01 05:01:59 +01:00
llvm-mirror/tools/llvm-exegesis/llvm-exegesis.cpp
Roman Lebedev 8691a62a19 [llvm-exegesis] Split Epsilon param into two (PR40787)
Summary:
This eps param is used for two distinct things:
* initial point clusterization
* checking clusters against the llvm values

What if one wants to only look at highly different clusters, without changing
the clustering itself? In particular, this helps to weed out noisy measurements
(since the clusterization epsilon is still small, so there is a better chance
that noisy measurements from the same opcode will go into different clusters)

By splitting it into two params it is now possible.

This is nearly-free performance-wise:
Old:
```
$ perf stat -r 25 ./bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency-1.yaml -analysis-inconsistencies-output-file=/tmp/clusters-old.html
no exegesis target for x86_64-unknown-linux-gnu, using default
Parsed 10099 benchmark points
Printing sched class consistency analysis results to file '/tmp/clusters-old.html'
...
 Performance counter stats for './bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency-1.yaml -analysis-inconsistencies-output-file=/tmp/clusters-old.html' (25 runs):

            390.01 msec task-clock                #    0.998 CPUs utilized            ( +-  0.25% )
                12      context-switches          #   31.735 M/sec                    ( +- 27.38% )
                 0      cpu-migrations            #    0.000 K/sec
              4745      page-faults               # 12183.732 M/sec                   ( +-  0.54% )
        1562711900      cycles                    # 4012303.327 GHz                   ( +-  0.24% )  (82.90%)
         185567822      stalled-cycles-frontend   #   11.87% frontend cycles idle     ( +-  0.52% )  (83.30%)
         392106234      stalled-cycles-backend    #   25.09% backend cycles idle      ( +-  1.31% )  (33.79%)
        1839236666      instructions              #    1.18  insn per cycle
                                                  #    0.21  stalled cycles per insn  ( +-  0.15% )  (50.37%)
         407035764      branches                  # 1045074878.710 M/sec              ( +-  0.12% )  (66.80%)
          10896459      branch-misses             #    2.68% of all branches          ( +-  0.17% )  (83.20%)

          0.390629 +- 0.000972 seconds time elapsed  ( +-  0.25% )
```
```
$ perf stat -r 9 ./bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency.yml -analysis-inconsistencies-output-file=/tmp/clusters-old.html
no exegesis target for x86_64-unknown-linux-gnu, using default
Parsed 50572 benchmark points
Printing sched class consistency analysis results to file '/tmp/clusters-old.html'
...
 Performance counter stats for './bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency.yml -analysis-inconsistencies-output-file=/tmp/clusters-old.html' (9 runs):

           6803.36 msec task-clock                #    0.999 CPUs utilized            ( +-  0.96% )
               262      context-switches          #   38.546 M/sec                    ( +- 23.06% )
                 0      cpu-migrations            #    0.065 M/sec                    ( +- 76.03% )
             13287      page-faults               # 1953.206 M/sec                    ( +-  0.32% )
       27252537904      cycles                    # 4006024.257 GHz                   ( +-  0.95% )  (83.31%)
        1496314935      stalled-cycles-frontend   #    5.49% frontend cycles idle     ( +-  0.97% )  (83.32%)
       16128404524      stalled-cycles-backend    #   59.18% backend cycles idle      ( +-  0.30% )  (33.37%)
       17611143370      instructions              #    0.65  insn per cycle
                                                  #    0.92  stalled cycles per insn  ( +-  0.05% )  (50.04%)
        3894906599      branches                  # 572537147.437 M/sec               ( +-  0.03% )  (66.69%)
         116314514      branch-misses             #    2.99% of all branches          ( +-  0.20% )  (83.35%)

            6.8118 +- 0.0689 seconds time elapsed  ( +-  1.01%)
```
New:
```
$ perf stat -r 25 ./bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency-1.yaml -analysis-inconsistencies-output-file=/tmp/clusters-new.html
no exegesis target for x86_64-unknown-linux-gnu, using default
Parsed 10099 benchmark points
Printing sched class consistency analysis results to file '/tmp/clusters-new.html'
...
 Performance counter stats for './bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency-1.yaml -analysis-inconsistencies-output-file=/tmp/clusters-new.html' (25 runs):

            400.14 msec task-clock                #    0.998 CPUs utilized            ( +-  0.66% )
                12      context-switches          #   29.429 M/sec                    ( +- 25.95% )
                 0      cpu-migrations            #    0.100 M/sec                    ( +-100.00% )
              4714      page-faults               # 11796.496 M/sec                   ( +-  0.55% )
        1603131306      cycles                    # 4011840.105 GHz                   ( +-  0.66% )  (82.85%)
         199538509      stalled-cycles-frontend   #   12.45% frontend cycles idle     ( +-  2.40% )  (83.10%)
         402249109      stalled-cycles-backend    #   25.09% backend cycles idle      ( +-  1.19% )  (34.05%)
        1847783963      instructions              #    1.15  insn per cycle
                                                  #    0.22  stalled cycles per insn  ( +-  0.18% )  (50.64%)
         407162722      branches                  # 1018925730.631 M/sec              ( +-  0.12% )  (67.02%)
          10932779      branch-misses             #    2.69% of all branches          ( +-  0.51% )  (83.28%)

           0.40077 +- 0.00267 seconds time elapsed  ( +-  0.67% )

lebedevri@pini-pini:/build/llvm-build-Clang-release$ perf stat -r 9 ./bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency.yml -analysis-inconsistencies-output-file=/tmp/clusters-new.html
no exegesis target for x86_64-unknown-linux-gnu, using default
Parsed 50572 benchmark points
Printing sched class consistency analysis results to file '/tmp/clusters-new.html'
...
 Performance counter stats for './bin/llvm-exegesis -mode=analysis -benchmarks-file=/home/lebedevri/PileDriver-Sched/benchmarks-latency.yml -analysis-inconsistencies-output-file=/tmp/clusters-new.html' (9 runs):

           6947.79 msec task-clock                #    1.000 CPUs utilized            ( +-  0.90% )
               217      context-switches          #   31.236 M/sec                    ( +- 36.16% )
                 1      cpu-migrations            #    0.096 M/sec                    ( +- 50.00% )
             13258      page-faults               # 1908.389 M/sec                    ( +-  0.34% )
       27830796523      cycles                    # 4006032.286 GHz                   ( +-  0.89% )  (83.30%)
        1504554006      stalled-cycles-frontend   #    5.41% frontend cycles idle     ( +-  2.10% )  (83.32%)
       16716574843      stalled-cycles-backend    #   60.07% backend cycles idle      ( +-  0.65% )  (33.38%)
       17755545931      instructions              #    0.64  insn per cycle
                                                  #    0.94  stalled cycles per insn  ( +-  0.09% )  (50.04%)
        3897255686      branches                  # 560980426.597 M/sec               ( +-  0.06% )  (66.70%)
         117045395      branch-misses             #    3.00% of all branches          ( +-  0.47% )  (83.34%)

            6.9507 +- 0.0627 seconds time elapsed  ( +-  0.90% )
```

I.e. it's +2.6% slowdown for one whole sweep, or +2% for 5 whole sweeps.
Within noise i'd say.

Should help with [[ https://bugs.llvm.org/show_bug.cgi?id=40787 | PR40787 ]].

Reviewers: courbet, gchatelet

Reviewed By: courbet

Subscribers: tschuett, RKSimon, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D58476

llvm-svn: 354767
2019-02-25 09:36:12 +00:00

486 lines
18 KiB
C++

//===-- llvm-exegesis.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
/// Measures execution properties (latencies/uops) of an instruction.
///
//===----------------------------------------------------------------------===//
#include "lib/Analysis.h"
#include "lib/BenchmarkResult.h"
#include "lib/BenchmarkRunner.h"
#include "lib/Clustering.h"
#include "lib/LlvmState.h"
#include "lib/PerfHelper.h"
#include "lib/Target.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include <algorithm>
#include <string>
namespace llvm {
namespace exegesis {
static cl::opt<int> OpcodeIndex("opcode-index",
cl::desc("opcode to measure, by index"),
cl::init(0));
static cl::opt<std::string>
OpcodeNames("opcode-name",
cl::desc("comma-separated list of opcodes to measure, by name"),
cl::init(""));
static cl::opt<std::string> SnippetsFile("snippets-file",
cl::desc("code snippets to measure"),
cl::init(""));
static cl::opt<std::string> BenchmarkFile("benchmarks-file", cl::desc(""),
cl::init(""));
static cl::opt<exegesis::InstructionBenchmark::ModeE> BenchmarkMode(
"mode", cl::desc("the mode to run"),
cl::values(clEnumValN(exegesis::InstructionBenchmark::Latency, "latency",
"Instruction Latency"),
clEnumValN(exegesis::InstructionBenchmark::InverseThroughput,
"inverse_throughput",
"Instruction Inverse Throughput"),
clEnumValN(exegesis::InstructionBenchmark::Uops, "uops",
"Uop Decomposition"),
// When not asking for a specific benchmark mode,
// we'll analyse the results.
clEnumValN(exegesis::InstructionBenchmark::Unknown, "analysis",
"Analysis")));
static cl::opt<unsigned>
NumRepetitions("num-repetitions",
cl::desc("number of time to repeat the asm snippet"),
cl::init(10000));
static cl::opt<bool> IgnoreInvalidSchedClass(
"ignore-invalid-sched-class",
cl::desc("ignore instructions that do not define a sched class"),
cl::init(false));
static cl::opt<unsigned> AnalysisNumPoints(
"analysis-numpoints",
cl::desc("minimum number of points in an analysis cluster"), cl::init(3));
static cl::opt<float> AnalysisClusteringEpsilon(
"analysis-clustering-epsilon",
cl::desc("dbscan epsilon for benchmark point clustering"), cl::init(0.1));
static cl::opt<float> AnalysisInconsistencyEpsilon(
"analysis-inconsistency-epsilon",
cl::desc("epsilon for detection of when the cluster is different from the "
"LLVM schedule profile values"),
cl::init(0.1));
static cl::opt<std::string>
AnalysisClustersOutputFile("analysis-clusters-output-file", cl::desc(""),
cl::init(""));
static cl::opt<std::string>
AnalysisInconsistenciesOutputFile("analysis-inconsistencies-output-file",
cl::desc(""), cl::init(""));
static cl::opt<bool> AnalysisDisplayUnstableOpcodes(
"analysis-display-unstable-clusters",
cl::desc("if there is more than one benchmark for an opcode, said "
"benchmarks may end up not being clustered into the same cluster "
"if the measured performance characteristics are different. by "
"default all such opcodes are filtered out. this flag will "
"instead show only such unstable opcodes"),
cl::init(false));
static cl::opt<std::string>
CpuName("mcpu",
cl::desc(
"cpu name to use for pfm counters, leave empty to autodetect"),
cl::init(""));
static ExitOnError ExitOnErr;
#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
void LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
#endif
// Checks that only one of OpcodeNames, OpcodeIndex or SnippetsFile is provided,
// and returns the opcode indices or {} if snippets should be read from
// `SnippetsFile`.
static std::vector<unsigned>
getOpcodesOrDie(const llvm::MCInstrInfo &MCInstrInfo) {
const size_t NumSetFlags = (OpcodeNames.empty() ? 0 : 1) +
(OpcodeIndex == 0 ? 0 : 1) +
(SnippetsFile.empty() ? 0 : 1);
if (NumSetFlags != 1)
llvm::report_fatal_error(
"please provide one and only one of 'opcode-index', 'opcode-name' or "
"'snippets-file'");
if (!SnippetsFile.empty())
return {};
if (OpcodeIndex > 0)
return {static_cast<unsigned>(OpcodeIndex)};
if (OpcodeIndex < 0) {
std::vector<unsigned> Result;
for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
Result.push_back(I);
return Result;
}
// Resolve opcode name -> opcode.
const auto ResolveName =
[&MCInstrInfo](llvm::StringRef OpcodeName) -> unsigned {
for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
if (MCInstrInfo.getName(I) == OpcodeName)
return I;
return 0u;
};
llvm::SmallVector<llvm::StringRef, 2> Pieces;
llvm::StringRef(OpcodeNames.getValue())
.split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false);
std::vector<unsigned> Result;
for (const llvm::StringRef OpcodeName : Pieces) {
if (unsigned Opcode = ResolveName(OpcodeName))
Result.push_back(Opcode);
else
llvm::report_fatal_error(
llvm::Twine("unknown opcode ").concat(OpcodeName));
}
return Result;
}
// Generates code snippets for opcode `Opcode`.
static llvm::Expected<std::vector<BenchmarkCode>>
generateSnippets(const LLVMState &State, unsigned Opcode) {
const Instruction &Instr = State.getIC().getInstr(Opcode);
const llvm::MCInstrDesc &InstrDesc = *Instr.Description;
// Ignore instructions that we cannot run.
if (InstrDesc.isPseudo())
return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
return llvm::make_error<BenchmarkFailure>(
"Unsupported opcode: isBranch/isIndirectBranch");
if (InstrDesc.isCall() || InstrDesc.isReturn())
return llvm::make_error<BenchmarkFailure>(
"Unsupported opcode: isCall/isReturn");
const std::unique_ptr<SnippetGenerator> Generator =
State.getExegesisTarget().createSnippetGenerator(BenchmarkMode, State);
if (!Generator)
llvm::report_fatal_error("cannot create snippet generator");
return Generator->generateConfigurations(Instr);
}
namespace {
// An MCStreamer that reads a BenchmarkCode definition from a file.
// The BenchmarkCode definition is just an asm file, with additional comments to
// specify which registers should be defined or are live on entry.
class BenchmarkCodeStreamer : public llvm::MCStreamer,
public llvm::AsmCommentConsumer {
public:
explicit BenchmarkCodeStreamer(llvm::MCContext *Context,
const llvm::MCRegisterInfo *TheRegInfo,
BenchmarkCode *Result)
: llvm::MCStreamer(*Context), RegInfo(TheRegInfo), Result(Result) {}
// Implementation of the llvm::MCStreamer interface. We only care about
// instructions.
void EmitInstruction(const llvm::MCInst &Instruction,
const llvm::MCSubtargetInfo &STI) override {
Result->Instructions.push_back(Instruction);
}
// Implementation of the llvm::AsmCommentConsumer.
void HandleComment(llvm::SMLoc Loc, llvm::StringRef CommentText) override {
CommentText = CommentText.trim();
if (!CommentText.consume_front("LLVM-EXEGESIS-"))
return;
if (CommentText.consume_front("DEFREG")) {
// LLVM-EXEGESIS-DEFREF <reg> <hex_value>
RegisterValue RegVal;
llvm::SmallVector<llvm::StringRef, 2> Parts;
CommentText.split(Parts, ' ', /*unlimited splits*/ -1,
/*do not keep empty strings*/ false);
if (Parts.size() != 2) {
llvm::errs() << "invalid comment 'LLVM-EXEGESIS-DEFREG " << CommentText
<< "\n";
++InvalidComments;
}
if (!(RegVal.Register = findRegisterByName(Parts[0].trim()))) {
llvm::errs() << "unknown register in 'LLVM-EXEGESIS-DEFREG "
<< CommentText << "\n";
++InvalidComments;
return;
}
const llvm::StringRef HexValue = Parts[1].trim();
RegVal.Value = llvm::APInt(
/* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16);
Result->RegisterInitialValues.push_back(std::move(RegVal));
return;
}
if (CommentText.consume_front("LIVEIN")) {
// LLVM-EXEGESIS-LIVEIN <reg>
if (unsigned Reg = findRegisterByName(CommentText.ltrim()))
Result->LiveIns.push_back(Reg);
else {
llvm::errs() << "unknown register in 'LLVM-EXEGESIS-LIVEIN "
<< CommentText << "\n";
++InvalidComments;
}
return;
}
}
unsigned numInvalidComments() const { return InvalidComments; }
private:
// We only care about instructions, we don't implement this part of the API.
void EmitCommonSymbol(llvm::MCSymbol *Symbol, uint64_t Size,
unsigned ByteAlignment) override {}
bool EmitSymbolAttribute(llvm::MCSymbol *Symbol,
llvm::MCSymbolAttr Attribute) override {
return false;
}
void EmitValueToAlignment(unsigned ByteAlignment, int64_t Value,
unsigned ValueSize,
unsigned MaxBytesToEmit) override {}
void EmitZerofill(llvm::MCSection *Section, llvm::MCSymbol *Symbol,
uint64_t Size, unsigned ByteAlignment,
llvm::SMLoc Loc) override {}
unsigned findRegisterByName(const llvm::StringRef RegName) const {
// FIXME: Can we do better than this ?
for (unsigned I = 0, E = RegInfo->getNumRegs(); I < E; ++I) {
if (RegName == RegInfo->getName(I))
return I;
}
llvm::errs() << "'" << RegName
<< "' is not a valid register name for the target\n";
return 0;
}
const llvm::MCRegisterInfo *const RegInfo;
BenchmarkCode *const Result;
unsigned InvalidComments = 0;
};
} // namespace
// Reads code snippets from file `Filename`.
static llvm::Expected<std::vector<BenchmarkCode>>
readSnippets(const LLVMState &State, llvm::StringRef Filename) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferPtr =
llvm::MemoryBuffer::getFileOrSTDIN(Filename);
if (std::error_code EC = BufferPtr.getError()) {
return llvm::make_error<BenchmarkFailure>(
"cannot read snippet: " + Filename + ": " + EC.message());
}
llvm::SourceMgr SM;
SM.AddNewSourceBuffer(std::move(BufferPtr.get()), llvm::SMLoc());
BenchmarkCode Result;
llvm::MCObjectFileInfo ObjectFileInfo;
const llvm::TargetMachine &TM = State.getTargetMachine();
llvm::MCContext Context(TM.getMCAsmInfo(), TM.getMCRegisterInfo(),
&ObjectFileInfo);
ObjectFileInfo.InitMCObjectFileInfo(TM.getTargetTriple(), /*PIC*/ false,
Context);
BenchmarkCodeStreamer Streamer(&Context, TM.getMCRegisterInfo(), &Result);
const std::unique_ptr<llvm::MCAsmParser> AsmParser(
llvm::createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo()));
if (!AsmParser)
return llvm::make_error<BenchmarkFailure>("cannot create asm parser");
AsmParser->getLexer().setCommentConsumer(&Streamer);
const std::unique_ptr<llvm::MCTargetAsmParser> TargetAsmParser(
TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser,
*TM.getMCInstrInfo(),
llvm::MCTargetOptions()));
if (!TargetAsmParser)
return llvm::make_error<BenchmarkFailure>(
"cannot create target asm parser");
AsmParser->setTargetParser(*TargetAsmParser);
if (AsmParser->Run(false))
return llvm::make_error<BenchmarkFailure>("cannot parse asm file");
if (Streamer.numInvalidComments())
return llvm::make_error<BenchmarkFailure>(
llvm::Twine("found ")
.concat(llvm::Twine(Streamer.numInvalidComments()))
.concat(" invalid LLVM-EXEGESIS comments"));
return std::vector<BenchmarkCode>{std::move(Result)};
}
void benchmarkMain() {
if (exegesis::pfm::pfmInitialize())
llvm::report_fatal_error("cannot initialize libpfm");
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
#endif
const LLVMState State(CpuName);
const auto Opcodes = getOpcodesOrDie(State.getInstrInfo());
std::vector<BenchmarkCode> Configurations;
if (!Opcodes.empty()) {
for (const unsigned Opcode : Opcodes) {
// Ignore instructions without a sched class if
// -ignore-invalid-sched-class is passed.
if (IgnoreInvalidSchedClass &&
State.getInstrInfo().get(Opcode).getSchedClass() == 0) {
llvm::errs() << State.getInstrInfo().getName(Opcode)
<< ": ignoring instruction without sched class\n";
continue;
}
auto ConfigsForInstr = generateSnippets(State, Opcode);
if (!ConfigsForInstr) {
llvm::logAllUnhandledErrors(
ConfigsForInstr.takeError(), llvm::errs(),
llvm::Twine(State.getInstrInfo().getName(Opcode)).concat(": "));
continue;
}
std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(),
std::back_inserter(Configurations));
}
} else {
Configurations = ExitOnErr(readSnippets(State, SnippetsFile));
}
const std::unique_ptr<BenchmarkRunner> Runner =
State.getExegesisTarget().createBenchmarkRunner(BenchmarkMode, State);
if (!Runner) {
llvm::report_fatal_error("cannot create benchmark runner");
}
if (NumRepetitions == 0)
llvm::report_fatal_error("--num-repetitions must be greater than zero");
// Write to standard output if file is not set.
if (BenchmarkFile.empty())
BenchmarkFile = "-";
for (const BenchmarkCode &Conf : Configurations) {
InstructionBenchmark Result =
Runner->runConfiguration(Conf, NumRepetitions);
ExitOnErr(Result.writeYaml(State, BenchmarkFile));
}
exegesis::pfm::pfmTerminate();
}
// Prints the results of running analysis pass `Pass` to file `OutputFilename`
// if OutputFilename is non-empty.
template <typename Pass>
static void maybeRunAnalysis(const Analysis &Analyzer, const std::string &Name,
const std::string &OutputFilename) {
if (OutputFilename.empty())
return;
if (OutputFilename != "-") {
llvm::errs() << "Printing " << Name << " results to file '"
<< OutputFilename << "'\n";
}
std::error_code ErrorCode;
llvm::raw_fd_ostream ClustersOS(OutputFilename, ErrorCode,
llvm::sys::fs::FA_Read |
llvm::sys::fs::FA_Write);
if (ErrorCode)
llvm::report_fatal_error("cannot open out file: " + OutputFilename);
if (auto Err = Analyzer.run<Pass>(ClustersOS))
llvm::report_fatal_error(std::move(Err));
}
static void analysisMain() {
if (BenchmarkFile.empty())
llvm::report_fatal_error("--benchmarks-file must be set.");
if (AnalysisClustersOutputFile.empty() &&
AnalysisInconsistenciesOutputFile.empty()) {
llvm::report_fatal_error(
"At least one of --analysis-clusters-output-file and "
"--analysis-inconsistencies-output-file must be specified.");
}
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetDisassembler();
// Read benchmarks.
const LLVMState State("");
const std::vector<InstructionBenchmark> Points =
ExitOnErr(InstructionBenchmark::readYamls(State, BenchmarkFile));
llvm::outs() << "Parsed " << Points.size() << " benchmark points\n";
if (Points.empty()) {
llvm::errs() << "no benchmarks to analyze\n";
return;
}
// FIXME: Check that all points have the same triple/cpu.
// FIXME: Merge points from several runs (latency and uops).
std::string Error;
const auto *TheTarget =
llvm::TargetRegistry::lookupTarget(Points[0].LLVMTriple, Error);
if (!TheTarget) {
llvm::errs() << "unknown target '" << Points[0].LLVMTriple << "'\n";
return;
}
std::unique_ptr<llvm::MCInstrInfo> InstrInfo(TheTarget->createMCInstrInfo());
const auto Clustering = ExitOnErr(InstructionBenchmarkClustering::create(
Points, AnalysisNumPoints, AnalysisClusteringEpsilon,
InstrInfo->getNumOpcodes()));
const Analysis Analyzer(*TheTarget, std::move(InstrInfo), Clustering,
AnalysisInconsistencyEpsilon,
AnalysisDisplayUnstableOpcodes);
maybeRunAnalysis<Analysis::PrintClusters>(Analyzer, "analysis clusters",
AnalysisClustersOutputFile);
maybeRunAnalysis<Analysis::PrintSchedClassInconsistencies>(
Analyzer, "sched class consistency analysis",
AnalysisInconsistenciesOutputFile);
}
} // namespace exegesis
} // namespace llvm
int main(int Argc, char **Argv) {
using namespace llvm;
cl::ParseCommandLineOptions(Argc, Argv, "");
exegesis::ExitOnErr.setExitCodeMapper([](const llvm::Error &Err) {
if (Err.isA<llvm::StringError>())
return EXIT_SUCCESS;
return EXIT_FAILURE;
});
if (exegesis::BenchmarkMode == exegesis::InstructionBenchmark::Unknown) {
exegesis::analysisMain();
} else {
exegesis::benchmarkMain();
}
return EXIT_SUCCESS;
}