Index: llvm/trunk/docs/CommandGuide/llvm-exegesis.rst =================================================================== --- llvm/trunk/docs/CommandGuide/llvm-exegesis.rst +++ llvm/trunk/docs/CommandGuide/llvm-exegesis.rst @@ -175,9 +175,10 @@ Specify the opcode to measure, by index. See example 1 for details. Either `opcode-index`, `opcode-name` or `snippets-file` must be set. -.. option:: -opcode-name= +.. option:: -opcode-name=,,... - Specify the opcode to measure, by name. See example 1 for details. + Specify the opcode to measure, by name. Several opcodes can be specified as + a comma-separated list. See example 1 for details. Either `opcode-index`, `opcode-name` or `snippets-file` must be set. .. option:: -snippets-file= Index: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -64,13 +64,21 @@ char *const AlignedPtr; }; + // A helper to measure counters while executing a function in a sandboxed + // context. + class FunctionExecutor { + public: + ~FunctionExecutor(); + virtual llvm::Expected + runAndMeasure(const char *Counters) const = 0; + }; + protected: const LLVMState &State; private: - virtual std::vector - runMeasurements(const ExecutableFunction &EF, - ScratchSpace &Scratch) const = 0; + virtual llvm::Expected> + runMeasurements(const FunctionExecutor &Executor) const = 0; llvm::Expected writeObjectFile(const BenchmarkCode &Configuration, Index: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -13,9 +13,11 @@ #include "Assembler.h" #include "BenchmarkRunner.h" #include "MCInstrDescView.h" +#include "PerfHelper.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" @@ -43,6 +45,54 @@ return Code; } +namespace { +class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor { +public: + FunctionExecutorImpl(const LLVMState &State, + llvm::object::OwningBinary Obj, + BenchmarkRunner::ScratchSpace *Scratch) + : Function(State.createTargetMachine(), std::move(Obj)), + Scratch(Scratch) {} + +private: + llvm::Expected runAndMeasure(const char *Counters) const override { + // We sum counts when there are several counters for a single ProcRes + // (e.g. P23 on SandyBridge). + int64_t CounterValue = 0; + llvm::SmallVector CounterNames; + llvm::StringRef(Counters).split(CounterNames, ','); + char *const ScratchPtr = Scratch->ptr(); + for (const auto &CounterName : CounterNames) { + pfm::PerfEvent PerfEvent(CounterName); + if (!PerfEvent.valid()) + llvm::report_fatal_error( + llvm::Twine("invalid perf event ").concat(Counters)); + pfm::Counter Counter(PerfEvent); + Scratch->clear(); + { + llvm::CrashRecoveryContext CRC; + llvm::CrashRecoveryContext::Enable(); + const bool Crashed = !CRC.RunSafely([this, &Counter, ScratchPtr]() { + Counter.start(); + Function(ScratchPtr); + Counter.stop(); + }); + llvm::CrashRecoveryContext::Disable(); + // FIXME: Better diagnosis. + if (Crashed) + return llvm::make_error( + "snippet crashed while running"); + } + CounterValue += Counter.read(); + } + return CounterValue; + } + + const ExecutableFunction Function; + BenchmarkRunner::ScratchSpace *const Scratch; +}; +} // namespace + InstructionBenchmark BenchmarkRunner::runConfiguration(const BenchmarkCode &BC, unsigned NumRepetitions) const { @@ -86,16 +136,21 @@ } llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d " << *ObjectFilePath << "\n"; - const ExecutableFunction EF(State.createTargetMachine(), - getObjectFromFile(*ObjectFilePath)); - InstrBenchmark.Measurements = runMeasurements(EF, *Scratch); + const FunctionExecutorImpl Executor(State, getObjectFromFile(*ObjectFilePath), + Scratch.get()); + auto Measurements = runMeasurements(Executor); + if (llvm::Error E = Measurements.takeError()) { + InstrBenchmark.Error = llvm::toString(std::move(E)); + return InstrBenchmark; + } + InstrBenchmark.Measurements = std::move(*Measurements); assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions"); for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) { // Scale the measurements by instruction. BM.PerInstructionValue /= InstrBenchmark.NumRepetitions; // Scale the measurements by snippet. BM.PerSnippetValue *= static_cast(BC.Instructions.size()) / - InstrBenchmark.NumRepetitions; + InstrBenchmark.NumRepetitions; } return InstrBenchmark; @@ -115,4 +170,6 @@ return ResultPath.str(); } +BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {} + } // namespace exegesis Index: llvm/trunk/tools/llvm-exegesis/lib/Latency.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Latency.h +++ llvm/trunk/tools/llvm-exegesis/lib/Latency.h @@ -37,9 +37,8 @@ ~LatencyBenchmarkRunner() override; private: - std::vector - runMeasurements(const ExecutableFunction &EF, - ScratchSpace &Scratch) const override; + llvm::Expected> + runMeasurements(const FunctionExecutor &Executor) const override; virtual const char *getCounterName() const; }; Index: llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp @@ -12,7 +12,6 @@ #include "Assembler.h" #include "BenchmarkRunner.h" #include "MCInstrDescView.h" -#include "PerfHelper.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" @@ -178,30 +177,26 @@ LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default; -std::vector -LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function, - ScratchSpace &Scratch) const { +llvm::Expected> +LatencyBenchmarkRunner::runMeasurements( + const FunctionExecutor &Executor) const { // Cycle measurements include some overhead from the kernel. Repeat the // measure several times and take the minimum value. constexpr const int NumMeasurements = 30; - int64_t MinLatency = std::numeric_limits::max(); + int64_t MinValue = std::numeric_limits::max(); const char *CounterName = getCounterName(); if (!CounterName) llvm::report_fatal_error("could not determine cycle counter name"); - const pfm::PerfEvent CyclesPerfEvent(CounterName); - if (!CyclesPerfEvent.valid()) - llvm::report_fatal_error("invalid perf event"); for (size_t I = 0; I < NumMeasurements; ++I) { - pfm::Counter Counter(CyclesPerfEvent); - Scratch.clear(); - Counter.start(); - Function(Scratch.ptr()); - Counter.stop(); - const int64_t Value = Counter.read(); - if (Value < MinLatency) - MinLatency = Value; - } - return {BenchmarkMeasure::Create("latency", MinLatency)}; + auto ExpectedCounterValue = Executor.runAndMeasure(CounterName); + if (!ExpectedCounterValue) + return ExpectedCounterValue.takeError(); + if (*ExpectedCounterValue < MinValue) + MinValue = *ExpectedCounterValue; + } + std::vector Result = { + BenchmarkMeasure::Create("latency", MinValue)}; + return std::move(Result); } } // namespace exegesis Index: llvm/trunk/tools/llvm-exegesis/lib/Uops.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Uops.h +++ llvm/trunk/tools/llvm-exegesis/lib/Uops.h @@ -68,9 +68,8 @@ static constexpr const size_t kMinNumDifferentAddresses = 6; private: - std::vector - runMeasurements(const ExecutableFunction &EF, - ScratchSpace &Scratch) const override; + llvm::Expected> + runMeasurements(const FunctionExecutor &Executor) const override; }; } // namespace exegesis Index: llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp @@ -12,7 +12,6 @@ #include "Assembler.h" #include "BenchmarkRunner.h" #include "MCInstrDescView.h" -#include "PerfHelper.h" #include "Target.h" // FIXME: Load constants into registers (e.g. with fld1) to not break @@ -221,33 +220,10 @@ return getSingleton(std::move(CT)); } -std::vector -UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function, - ScratchSpace &Scratch) const { +llvm::Expected> +UopsBenchmarkRunner::runMeasurements(const FunctionExecutor &Executor) const { const auto &SchedModel = State.getSubtargetInfo().getSchedModel(); - const auto RunMeasurement = [&Function, - &Scratch](const char *const Counters) { - // We sum counts when there are several counters for a single ProcRes - // (e.g. P23 on SandyBridge). - int64_t CounterValue = 0; - llvm::SmallVector CounterNames; - llvm::StringRef(Counters).split(CounterNames, ','); - for (const auto &CounterName : CounterNames) { - pfm::PerfEvent UopPerfEvent(CounterName); - if (!UopPerfEvent.valid()) - llvm::report_fatal_error( - llvm::Twine("invalid perf event ").concat(Counters)); - pfm::Counter Counter(UopPerfEvent); - Scratch.clear(); - Counter.start(); - Function(Scratch.ptr()); - Counter.stop(); - CounterValue += Counter.read(); - } - return CounterValue; - }; - std::vector Result; const auto &PfmCounters = SchedModel.getExtraProcessorInfo().PfmCounters; // Uops per port. @@ -256,16 +232,21 @@ const char *const Counters = PfmCounters.IssueCounters[ProcResIdx]; if (!Counters) continue; - const double CounterValue = RunMeasurement(Counters); + auto ExpectedCounterValue = Executor.runAndMeasure(Counters); + if (!ExpectedCounterValue) + return ExpectedCounterValue.takeError(); Result.push_back(BenchmarkMeasure::Create( - SchedModel.getProcResource(ProcResIdx)->Name, CounterValue)); + SchedModel.getProcResource(ProcResIdx)->Name, *ExpectedCounterValue)); } // NumMicroOps. if (const char *const UopsCounter = PfmCounters.UopsCounter) { - const double CounterValue = RunMeasurement(UopsCounter); - Result.push_back(BenchmarkMeasure::Create("NumMicroOps", CounterValue)); + auto ExpectedCounterValue = Executor.runAndMeasure(UopsCounter); + if (!ExpectedCounterValue) + return ExpectedCounterValue.takeError(); + Result.push_back( + BenchmarkMeasure::Create("NumMicroOps", *ExpectedCounterValue)); } - return Result; + return std::move(Result); } constexpr const size_t UopsSnippetGenerator::kMinNumDifferentAddresses; Index: llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp +++ llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp @@ -38,13 +38,14 @@ #include #include -static llvm::cl::opt +static llvm::cl::opt OpcodeIndex("opcode-index", llvm::cl::desc("opcode to measure, by index"), llvm::cl::init(0)); -static llvm::cl::opt - OpcodeName("opcode-name", llvm::cl::desc("opcode to measure, by name"), - llvm::cl::init("")); +static llvm::cl::opt OpcodeNames( + "opcode-name", + llvm::cl::desc("comma-separated list of opcodes to measure, by name"), + llvm::cl::init("")); static llvm::cl::opt SnippetsFile("snippets-file", llvm::cl::desc("code snippets to measure"), @@ -99,11 +100,12 @@ void LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET(); #endif -// Checks that only one of OpcodeName, OpcodeIndex or SnippetsFile is provided, -// and returns the opcode index or 0 if snippets should be read from +// 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 unsigned getOpcodeOrDie(const llvm::MCInstrInfo &MCInstrInfo) { - const size_t NumSetFlags = (OpcodeName.empty() ? 0 : 1) + +static std::vector +getOpcodesOrDie(const llvm::MCInstrInfo &MCInstrInfo) { + const size_t NumSetFlags = (OpcodeNames.empty() ? 0 : 1) + (OpcodeIndex == 0 ? 0 : 1) + (SnippetsFile.empty() ? 0 : 1); if (NumSetFlags != 1) @@ -111,14 +113,35 @@ "please provide one and only one of 'opcode-index', 'opcode-name' or " "'snippets-file'"); if (!SnippetsFile.empty()) - return 0; + return {}; if (OpcodeIndex > 0) - return OpcodeIndex; + return {static_cast(OpcodeIndex)}; + if (OpcodeIndex < 0) { + std::vector Result; + for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I <= E; ++I) + Result.push_back(I); + return Result; + } // 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)); + 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 Pieces; + llvm::StringRef(OpcodeNames.getValue()) + .split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false); + std::vector 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`. @@ -299,18 +322,29 @@ #endif const LLVMState State; - const auto Opcode = getOpcodeOrDie(State.getInstrInfo()); + const auto Opcodes = getOpcodesOrDie(State.getInstrInfo()); std::vector Configurations; - if (Opcode > 0) { - // 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; + 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)); } - Configurations = ExitOnErr(generateSnippets(State, Opcode)); } else { Configurations = ExitOnErr(readSnippets(State, SnippetsFile)); }