mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
86bf3b3bb5
Currently the cycle counter is taken from the subtarget schedule model, which isn't any use if the subtarget doesn't have one. Delegate the decision to the target benchmark runner, as it may know better what to do in that case, with the default being the current behaviour. Differential Revision: https://reviews.llvm.org/D48779 llvm-svn: 336099
243 lines
8.4 KiB
C++
243 lines
8.4 KiB
C++
//===-- llvm-exegesis.cpp ---------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \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/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include <algorithm>
|
|
#include <random>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
static llvm::cl::opt<unsigned>
|
|
OpcodeIndex("opcode-index", llvm::cl::desc("opcode to measure, by index"),
|
|
llvm::cl::init(0));
|
|
|
|
static llvm::cl::opt<std::string>
|
|
OpcodeName("opcode-name", llvm::cl::desc("opcode to measure, by name"),
|
|
llvm::cl::init(""));
|
|
|
|
static llvm::cl::opt<std::string>
|
|
BenchmarkFile("benchmarks-file", llvm::cl::desc(""), llvm::cl::init(""));
|
|
|
|
static llvm::cl::opt<exegesis::InstructionBenchmark::ModeE> BenchmarkMode(
|
|
"mode", llvm::cl::desc("the mode to run"),
|
|
llvm::cl::values(clEnumValN(exegesis::InstructionBenchmark::Latency,
|
|
"latency", "Instruction Latency"),
|
|
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 llvm::cl::opt<unsigned>
|
|
NumRepetitions("num-repetitions",
|
|
llvm::cl::desc("number of time to repeat the asm snippet"),
|
|
llvm::cl::init(10000));
|
|
|
|
static llvm::cl::opt<bool> IgnoreInvalidSchedClass(
|
|
"ignore-invalid-sched-class",
|
|
llvm::cl::desc("ignore instructions that do not define a sched class"),
|
|
llvm::cl::init(false));
|
|
|
|
static llvm::cl::opt<unsigned> AnalysisNumPoints(
|
|
"analysis-numpoints",
|
|
llvm::cl::desc("minimum number of points in an analysis cluster"),
|
|
llvm::cl::init(3));
|
|
|
|
static llvm::cl::opt<float>
|
|
AnalysisEpsilon("analysis-epsilon",
|
|
llvm::cl::desc("dbscan epsilon for analysis clustering"),
|
|
llvm::cl::init(0.1));
|
|
|
|
static llvm::cl::opt<std::string>
|
|
AnalysisClustersOutputFile("analysis-clusters-output-file",
|
|
llvm::cl::desc(""), llvm::cl::init("-"));
|
|
static llvm::cl::opt<std::string>
|
|
AnalysisInconsistenciesOutputFile("analysis-inconsistencies-output-file",
|
|
llvm::cl::desc(""), llvm::cl::init("-"));
|
|
|
|
namespace exegesis {
|
|
|
|
static llvm::ExitOnError ExitOnErr;
|
|
|
|
#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
|
|
void LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
|
|
#endif
|
|
|
|
static unsigned GetOpcodeOrDie(const llvm::MCInstrInfo &MCInstrInfo) {
|
|
if (OpcodeName.empty() && (OpcodeIndex == 0))
|
|
llvm::report_fatal_error(
|
|
"please provide one and only one of 'opcode-index' or 'opcode-name'");
|
|
if (OpcodeIndex > 0)
|
|
return OpcodeIndex;
|
|
// Resolve opcode name -> opcode.
|
|
for (unsigned I = 0, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
|
|
if (MCInstrInfo.getName(I) == OpcodeName)
|
|
return I;
|
|
llvm::report_fatal_error(llvm::Twine("unknown opcode ").concat(OpcodeName));
|
|
}
|
|
|
|
static BenchmarkResultContext
|
|
getBenchmarkResultContext(const LLVMState &State) {
|
|
BenchmarkResultContext Ctx;
|
|
|
|
const llvm::MCInstrInfo &InstrInfo = State.getInstrInfo();
|
|
for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I)
|
|
Ctx.addInstrEntry(I, InstrInfo.getName(I).data());
|
|
|
|
const llvm::MCRegisterInfo &RegInfo = State.getRegInfo();
|
|
for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I)
|
|
Ctx.addRegEntry(I, RegInfo.getName(I));
|
|
|
|
return Ctx;
|
|
}
|
|
|
|
void benchmarkMain() {
|
|
if (exegesis::pfm::pfmInitialize())
|
|
llvm::report_fatal_error("cannot initialize libpfm");
|
|
|
|
llvm::InitializeNativeTarget();
|
|
llvm::InitializeNativeTargetAsmPrinter();
|
|
#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
|
|
LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
|
|
#endif
|
|
|
|
const LLVMState State;
|
|
const auto Opcode = GetOpcodeOrDie(State.getInstrInfo());
|
|
|
|
// Ignore instructions without a sched class if -ignore-invalid-sched-class is
|
|
// passed.
|
|
if (IgnoreInvalidSchedClass &&
|
|
State.getInstrInfo().get(Opcode).getSchedClass() == 0) {
|
|
llvm::errs() << "ignoring instruction without sched class\n";
|
|
return;
|
|
}
|
|
|
|
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 = "-";
|
|
|
|
const BenchmarkResultContext Context = getBenchmarkResultContext(State);
|
|
std::vector<InstructionBenchmark> Results =
|
|
ExitOnErr(Runner->run(Opcode, NumRepetitions));
|
|
for (InstructionBenchmark &Result : Results)
|
|
ExitOnErr(Result.writeYaml(Context, 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.");
|
|
|
|
llvm::InitializeNativeTarget();
|
|
llvm::InitializeNativeTargetAsmPrinter();
|
|
llvm::InitializeNativeTargetDisassembler();
|
|
// Read benchmarks.
|
|
const LLVMState State;
|
|
const std::vector<InstructionBenchmark> Points =
|
|
ExitOnErr(InstructionBenchmark::readYamls(
|
|
getBenchmarkResultContext(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;
|
|
}
|
|
const auto Clustering = ExitOnErr(InstructionBenchmarkClustering::create(
|
|
Points, AnalysisNumPoints, AnalysisEpsilon));
|
|
|
|
const Analysis Analyzer(*TheTarget, Clustering);
|
|
|
|
maybeRunAnalysis<Analysis::PrintClusters>(Analyzer, "analysis clusters",
|
|
AnalysisClustersOutputFile);
|
|
maybeRunAnalysis<Analysis::PrintSchedClassInconsistencies>(
|
|
Analyzer, "sched class consistency analysis",
|
|
AnalysisInconsistenciesOutputFile);
|
|
}
|
|
|
|
} // namespace exegesis
|
|
|
|
int main(int Argc, char **Argv) {
|
|
llvm::cl::ParseCommandLineOptions(Argc, Argv, "");
|
|
|
|
exegesis::ExitOnErr.setExitCodeMapper([](const llvm::Error &Err) {
|
|
if (Err.isA<llvm::StringError>())
|
|
return EXIT_SUCCESS;
|
|
return EXIT_FAILURE;
|
|
});
|
|
|
|
if (BenchmarkMode == exegesis::InstructionBenchmark::Unknown) {
|
|
exegesis::analysisMain();
|
|
} else {
|
|
exegesis::benchmarkMain();
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|