diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h --- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -38,6 +38,7 @@ PrepareSnippet, PrepareAndAssembleSnippet, AssembleMeasuredCode, + ExecuteWithDummyCounters, Measure, }; diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -213,7 +213,8 @@ << *ObjectFilePath << "\n"; } - if (BenchmarkPhaseSelector < BenchmarkPhaseSelectorE::Measure) { + if (BenchmarkPhaseSelector < + BenchmarkPhaseSelectorE::ExecuteWithDummyCounters) { InstrBenchmark.Error = "actual measurements skipped."; return std::move(InstrBenchmark); } diff --git a/llvm/tools/llvm-exegesis/lib/LlvmState.h b/llvm/tools/llvm-exegesis/lib/LlvmState.h --- a/llvm/tools/llvm-exegesis/lib/LlvmState.h +++ b/llvm/tools/llvm-exegesis/lib/LlvmState.h @@ -41,9 +41,12 @@ // Factory function. // If `Triple` is empty, uses the host triple. // If `CpuName` is empty, uses the host CPU. - // `Features` is intended for tests. + // If `UseDummyCounters` is set, does not query the kernel + // for instruction counts. + // `DummyCounters` and `Features` are intended for tests. static Expected Create(std::string TripleName, std::string CpuName, - StringRef Features = ""); + StringRef Features = "", + bool UseDummyCounters = false); const TargetMachine &getTargetMachine() const { return *TheTargetMachine; } std::unique_ptr createTargetMachine() const; @@ -86,7 +89,7 @@ createRegNameToRegNoMapping() const; LLVMState(std::unique_ptr TM, const ExegesisTarget *ET, - StringRef CpuName); + const PfmCountersInfo *PCI); const ExegesisTarget *TheExegesisTarget; std::unique_ptr TheTargetMachine; diff --git a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp --- a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp +++ b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp @@ -24,7 +24,8 @@ Expected LLVMState::Create(std::string TripleName, std::string CpuName, - const StringRef Features) { + const StringRef Features, + bool UseDummyCounters) { if (TripleName.empty()) TripleName = Triple::normalize(sys::getDefaultTargetTriple()); @@ -73,16 +74,16 @@ "no Exegesis target for triple " + TripleName, llvm::inconvertibleErrorCode()); } - return LLVMState(std::move(TM), ET, CpuName); + const PfmCountersInfo &PCI = UseDummyCounters ? ET->getDummyPfmCounters() + : ET->getPfmCounters(CpuName); + return LLVMState(std::move(TM), ET, &PCI); } LLVMState::LLVMState(std::unique_ptr TM, - const ExegesisTarget *ET, const StringRef CpuName) - : TheExegesisTarget(ET), TheTargetMachine(std::move(TM)), + const ExegesisTarget *ET, const PfmCountersInfo *PCI) + : TheExegesisTarget(ET), TheTargetMachine(std::move(TM)), PfmCounters(PCI), OpcodeNameToOpcodeIdxMapping(createOpcodeNameToOpcodeIdxMapping()), RegNameToRegNoMapping(createRegNameToRegNoMapping()) { - PfmCounters = &TheExegesisTarget->getPfmCounters(CpuName); - BitVector ReservedRegs = getFunctionReservedRegs(getTargetMachine()); for (const unsigned Reg : TheExegesisTarget->getUnavailableRegisters()) ReservedRegs.set(Reg); diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.h b/llvm/tools/llvm-exegesis/lib/PerfHelper.h --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.h +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.h @@ -37,6 +37,9 @@ // NOTE: pfm_initialize() must be called before creating PerfEvent objects. class PerfEvent { public: + // Dummy event that does not require access to counters (for tests). + static const char *const DummyEventString; + // http://perfmon2.sourceforge.net/manv4/libpfm.html // Events are expressed as strings. e.g. "INSTRUCTION_RETIRED" explicit PerfEvent(StringRef PfmEventString); @@ -63,6 +66,9 @@ std::string EventString; std::string FullQualifiedEventString; perf_event_attr *Attr; + +private: + void initRealEvent(StringRef PfmEventString); }; // Uses a valid PerfEvent to configure the Kernel so we can measure the @@ -102,6 +108,10 @@ #ifdef HAVE_LIBPFM int FileDescriptor = -1; #endif + bool IsDummyEvent; + +private: + void initRealEvent(const PerfEvent &E); }; } // namespace pfm diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp @@ -44,6 +44,13 @@ #endif } +// Performance counters may be unavailable for a number of reasons (such as +// kernel.perf_event_paranoid restriction or CPU being unknown to libpfm). +// +// Dummy event can be specified to skip interaction with real performance +// counters while still passing control to the generated code snippet. +const char *const PerfEvent::DummyEventString = "not-really-an-event"; + PerfEvent::~PerfEvent() { #ifdef HAVE_LIBPFM delete Attr; @@ -60,6 +67,13 @@ PerfEvent::PerfEvent(StringRef PfmEventString) : EventString(PfmEventString.str()), Attr(nullptr) { + if (PfmEventString != DummyEventString) + initRealEvent(PfmEventString); + else + FullQualifiedEventString = PfmEventString; +} + +void PerfEvent::initRealEvent(StringRef PfmEventString) { #ifdef HAVE_LIBPFM char *Fstr = nullptr; pfm_perf_encode_arg_t Arg = {}; @@ -93,9 +107,15 @@ return FullQualifiedEventString; } -#ifdef HAVE_LIBPFM Counter::Counter(PerfEvent &&E) : Event(std::move(E)){ assert(Event.valid()); + IsDummyEvent = Event.name() == PerfEvent::DummyEventString; + if (!IsDummyEvent) + initRealEvent(E); +} + +#ifdef HAVE_LIBPFM +void Counter::initRealEvent(const PerfEvent &E) { const pid_t Pid = 0; // measure current process/thread. const int Cpu = -1; // measure any processor. const int GroupFd = -1; // no grouping of counters. @@ -111,11 +131,20 @@ assert(FileDescriptor != -1 && "Unable to open event"); } -Counter::~Counter() { close(FileDescriptor); } +Counter::~Counter() { + if (!IsDummyEvent) + close(FileDescriptor); +} -void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); } +void Counter::start() { + if (!IsDummyEvent) + ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); +} -void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); } +void Counter::stop() { + if (!IsDummyEvent) + ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); +} int64_t Counter::read() const { auto ValueOrError = readOrError(); @@ -131,10 +160,15 @@ llvm::Expected> Counter::readOrError(StringRef /*unused*/) const { int64_t Count = 0; - ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count)); - if (ReadSize != sizeof(Count)) - return llvm::make_error("Failed to read event counter", - llvm::errc::io_error); + if (!IsDummyEvent) { + ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count)); + if (ReadSize != sizeof(Count)) + return llvm::make_error("Failed to read event counter", + llvm::errc::io_error); + } else { + Count = 42; + } + llvm::SmallVector Result; Result.push_back(Count); return Result; @@ -143,7 +177,7 @@ int Counter::numValues() const { return 1; } #else -Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {} +void Counter::initRealEvent(const PerfEvent &) {} Counter::~Counter() = default; diff --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h --- a/llvm/tools/llvm-exegesis/lib/Target.h +++ b/llvm/tools/llvm-exegesis/lib/Target.h @@ -59,6 +59,7 @@ unsigned NumIssueCounters; static const PfmCountersInfo Default; + static const PfmCountersInfo Dummy; }; struct CpuAndPfmCounters { @@ -178,6 +179,10 @@ // counters are defined for this CPU). const PfmCountersInfo &getPfmCounters(StringRef CpuName) const; + // Returns dummy Pfm counters which can be used to execute generated snippet + // without access to performance counters. + const PfmCountersInfo &getDummyPfmCounters() const; + // Saves the CPU state that needs to be preserved when running a benchmark, // and returns and RAII object that restores the state on destruction. // By default no state is preserved. diff --git a/llvm/tools/llvm-exegesis/lib/Target.cpp b/llvm/tools/llvm-exegesis/lib/Target.cpp --- a/llvm/tools/llvm-exegesis/lib/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/Target.cpp @@ -9,6 +9,7 @@ #include "LatencyBenchmarkRunner.h" #include "ParallelSnippetGenerator.h" +#include "PerfHelper.h" #include "SerialSnippetGenerator.h" #include "UopsBenchmarkRunner.h" #include "llvm/ADT/Twine.h" @@ -92,7 +93,7 @@ .concat(ModeName) .concat( "' mode, sched model does not define a cycle counter. You " - "can pass --skip-measurements to skip the actual " + "can pass --benchmark-phase=... to skip the actual " "benchmarking.")); } return createLatencyBenchmarkRunner(State, Mode, BenchmarkPhaseSelector, @@ -102,7 +103,7 @@ !PfmCounters.UopsCounter && !PfmCounters.IssueCounters) return make_error( "can't run 'uops' mode, sched model does not define uops or issue " - "counters. You can pass --skip-measurements to skip the actual " + "counters. You can pass --benchmark-phase=... to skip the actual " "benchmarking."); return createUopsBenchmarkRunner(State, BenchmarkPhaseSelector, ResultAggMode); @@ -138,6 +139,9 @@ "We shouldn't have dynamic initialization here"); const PfmCountersInfo PfmCountersInfo::Default = {nullptr, nullptr, nullptr, 0u}; +const PfmCountersInfo PfmCountersInfo::Dummy = { + pfm::PerfEvent::DummyEventString, pfm::PerfEvent::DummyEventString, nullptr, + 0u}; const PfmCountersInfo &ExegesisTarget::getPfmCounters(StringRef CpuName) const { assert(llvm::is_sorted( @@ -161,6 +165,10 @@ return *Found->PCI; } +const PfmCountersInfo &ExegesisTarget::getDummyPfmCounters() const { + return PfmCountersInfo::Dummy; +} + ExegesisTarget::SavedState::~SavedState() {} // anchor. namespace { diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -134,6 +134,10 @@ "Same as prepare-and-assemble-snippet, but also creates the " "full sequence " "that can be dumped to a file using --dump-object-to-disk"), + clEnumValN(exegesis::BenchmarkPhaseSelectorE::ExecuteWithDummyCounters, + "execute-with-dummy-counters", + "Same as prepare-measured-code, but also runs the snippet " + "(without real measurement - for testing only!)"), clEnumValN( exegesis::BenchmarkPhaseSelectorE::Measure, "measure", "Same as prepare-measured-code, but also runs the measurement " @@ -412,6 +416,12 @@ } } + // With dummy counters, measurements are rather meaningless, + // so drop them altogether. + if (BenchmarkPhaseSelector == + BenchmarkPhaseSelectorE::ExecuteWithDummyCounters) + Result.Measurements.clear(); + ExitOnFileError(BenchmarkFile, Result.writeYamlTo(State, Ostr)); } } @@ -432,7 +442,10 @@ InitializeAllAsmParsers(); InitializeAllExegesisTargets(); - const LLVMState State = ExitOnErr(LLVMState::Create(TripleName, MCPU)); + bool UseDummyCounters = BenchmarkPhaseSelector == + BenchmarkPhaseSelectorE::ExecuteWithDummyCounters; + const LLVMState State = + ExitOnErr(LLVMState::Create(TripleName, MCPU, "", UseDummyCounters)); // Preliminary check to ensure features needed for requested // benchmark mode are present on target CPU and/or OS.