Index: docs/CommandGuide/llvm-exegesis.rst =================================================================== --- docs/CommandGuide/llvm-exegesis.rst +++ 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: tools/llvm-exegesis/lib/BenchmarkRunner.h =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.h +++ 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: tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ 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,53 @@ 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 { + using llvm::CrashRecoveryContext; + // 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(); + { + CrashRecoveryContext CRC; + CrashRecoveryContext::Enable(); + Counter.start(); + if (!CRC.RunSafely([this, ScratchPtr]() { Function(ScratchPtr); })) { + // FIXME: Better diagnosis. + return llvm::make_error( + "snippet crashed while running"); + } + Counter.stop(); + CrashRecoveryContext::Disable(); + } + CounterValue += Counter.read(); + } + return CounterValue; + } + + const ExecutableFunction Function; + BenchmarkRunner::ScratchSpace *const Scratch; +}; +} // namespace + InstructionBenchmark BenchmarkRunner::runConfiguration(const BenchmarkCode &BC, unsigned NumRepetitions) const { @@ -86,16 +135,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 +169,6 @@ return ResultPath.str(); } +BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {} + } // namespace exegesis Index: tools/llvm-exegesis/lib/Latency.h =================================================================== --- tools/llvm-exegesis/lib/Latency.h +++ 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: tools/llvm-exegesis/lib/Latency.cpp =================================================================== --- tools/llvm-exegesis/lib/Latency.cpp +++ 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" @@ -83,9 +82,9 @@ 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; @@ -93,20 +92,17 @@ 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; + auto CounterValue = Executor.runAndMeasure(CounterName); + if (!CounterValue) { + return CounterValue.takeError(); + } + if (*CounterValue < MinLatency) + MinLatency = static_cast(*CounterValue); } - return {BenchmarkMeasure::Create("latency", MinLatency)}; + std::vector Result = { + BenchmarkMeasure::Create("latency", MinLatency)}; + return std::move(Result); } } // namespace exegesis Index: tools/llvm-exegesis/lib/Uops.h =================================================================== --- tools/llvm-exegesis/lib/Uops.h +++ 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: tools/llvm-exegesis/lib/Uops.cpp =================================================================== --- tools/llvm-exegesis/lib/Uops.cpp +++ 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 @@ -163,10 +162,10 @@ } const auto TiedVariables = getVariablesWithTiedOperands(Instr); if (!TiedVariables.empty()) { - if (TiedVariables.size() > 1) + /*if (TiedVariables.size() > 1) return llvm::make_error( "Infeasible : don't know how to handle several tied variables", - llvm::inconvertibleErrorCode()); + llvm::inconvertibleErrorCode());*/ const Variable *Var = TiedVariables.front(); assert(Var); const Operand &Op = Instr.getPrimaryOperand(*Var); @@ -221,33 +220,10 @@ return getSingleton(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,22 @@ const char *const Counters = PfmCounters.IssueCounters[ProcResIdx]; if (!Counters) continue; - const double CounterValue = RunMeasurement(Counters); + auto CounterValue = Executor.runAndMeasure(Counters); + if (!CounterValue) { + return CounterValue.takeError(); + } Result.push_back(BenchmarkMeasure::Create( - SchedModel.getProcResource(ProcResIdx)->Name, CounterValue)); + SchedModel.getProcResource(ProcResIdx)->Name, *CounterValue)); } // NumMicroOps. if (const char *const UopsCounter = PfmCounters.UopsCounter) { - const double CounterValue = RunMeasurement(UopsCounter); - Result.push_back(BenchmarkMeasure::Create("NumMicroOps", CounterValue)); + auto CounterValue = Executor.runAndMeasure(UopsCounter); + if (!CounterValue) { + return CounterValue.takeError(); + } + Result.push_back(BenchmarkMeasure::Create("NumMicroOps", *CounterValue)); } - return Result; + return std::move(Result); } constexpr const size_t UopsSnippetGenerator::kMinNumDifferentAddresses; Index: tools/llvm-exegesis/llvm-exegesis.cpp =================================================================== --- tools/llvm-exegesis/llvm-exegesis.cpp +++ 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,33 @@ "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(MCInstrInfo.getNumOpcodes()); + std::iota(Result.begin(), Result.end(), 1); + 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) { + 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 +320,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)); }