diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test b/llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test new file mode 100644 index 0000000000000..471b9f89578a4 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test @@ -0,0 +1,12 @@ +# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -opcode-name=LEA64r --use-dummy-perf-counters | FileCheck %s +# TODO Support non-Linux systems +# REQUIRES: exegesis-can-execute-x86_64, system-linux + +CHECK: --- +CHECK-NEXT: mode: latency +CHECK-NEXT: key: +CHECK-NEXT: instructions: +CHECK-NEXT: LEA64r +# 'measurements' field should not contain misleading values +CHECK: measurements: [] +CHECK: assembled_snippet: {{[A-Z0-9]+}}{{$}} diff --git a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp index 8be4876b5be31..6c3d8ff963034 100644 --- a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp +++ b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp @@ -24,7 +24,8 @@ namespace exegesis { Expected LLVMState::Create(std::string TripleName, std::string CpuName, - const StringRef Features) { + const StringRef Features, + bool UseDummyPerfCounters) { if (TripleName.empty()) TripleName = Triple::normalize(sys::getDefaultTargetTriple()); @@ -73,16 +74,17 @@ Expected LLVMState::Create(std::string TripleName, "no Exegesis target for triple " + TripleName, llvm::inconvertibleErrorCode()); } - return LLVMState(std::move(TM), ET, CpuName); + const PfmCountersInfo &PCI = UseDummyPerfCounters + ? 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/LlvmState.h b/llvm/tools/llvm-exegesis/lib/LlvmState.h index 6039bdb658de4..137ba1b5a54fa 100644 --- a/llvm/tools/llvm-exegesis/lib/LlvmState.h +++ b/llvm/tools/llvm-exegesis/lib/LlvmState.h @@ -41,9 +41,12 @@ class LLVMState { // Factory function. // If `Triple` is empty, uses the host triple. // If `CpuName` is empty, uses the host CPU. - // `Features` is intended for tests. + // If `UseDummyPerfCounters` is set, does not query the kernel + // for event counts. + // `UseDummyPerfCounters` and `Features` are intended for tests. static Expected Create(std::string TripleName, std::string CpuName, - StringRef Features = ""); + StringRef Features = "", + bool UseDummyPerfCounters = false); const TargetMachine &getTargetMachine() const { return *TheTargetMachine; } std::unique_ptr createTargetMachine() const; @@ -86,7 +89,7 @@ class LLVMState { 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/PerfHelper.cpp b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp index 8a0b399dc982b..4bf748523cf27 100644 --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp @@ -44,6 +44,13 @@ void pfmTerminate() { #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(PerfEvent &&Other) 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 @@ StringRef PerfEvent::getPfmEventString() const { 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. @@ -106,16 +126,28 @@ Counter::Counter(PerfEvent &&E) : Event(std::move(E)){ errs() << "Unable to open event. ERRNO: " << strerror(errno) << ". Make sure your kernel allows user " "space perf monitoring.\nYou may want to try:\n$ sudo sh " - "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'\n"; + "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'.\n" + << "If you are debugging and just want to execute the snippet " + "without actually reading performance counters, " + "pass --use-dummy-perf-counters command line option.\n"; } 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 +163,15 @@ int64_t Counter::read() const { 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 +180,7 @@ Counter::readOrError(StringRef /*unused*/) const { int Counter::numValues() const { return 1; } #else -Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {} +void Counter::initRealEvent(const PerfEvent &) {} Counter::~Counter() = default; @@ -155,6 +192,11 @@ int64_t Counter::read() const { return 42; } llvm::Expected> Counter::readOrError(StringRef /*unused*/) const { + if (IsDummyEvent) { + llvm::SmallVector Result; + Result.push_back(42); + return Result; + } return llvm::make_error("Not implemented", llvm::errc::io_error); } diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.h b/llvm/tools/llvm-exegesis/lib/PerfHelper.h index 506917f16cdcb..f3e2b1b6b9522 100644 --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.h +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.h @@ -37,6 +37,9 @@ void pfmTerminate(); // 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 @@ class PerfEvent { 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 @@ class Counter { #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/Target.cpp b/llvm/tools/llvm-exegesis/lib/Target.cpp index 4717f9f7fa8d3..268d99e96d62e 100644 --- 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,8 +93,9 @@ ExegesisTarget::createBenchmarkRunner( .concat(ModeName) .concat( "' mode, sched model does not define a cycle counter. You " - "can pass --skip-measurements to skip the actual " - "benchmarking.")); + "can pass --benchmark-phase=... to skip the actual " + "benchmarking or --use-dummy-perf-counters to not query " + "the kernel for real event counts.")); } return createLatencyBenchmarkRunner(State, Mode, BenchmarkPhaseSelector, ResultAggMode); @@ -102,8 +104,9 @@ ExegesisTarget::createBenchmarkRunner( !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 " - "benchmarking."); + "counters. You can pass --benchmark-phase=... to skip the actual " + "benchmarking or --use-dummy-perf-counters to not query the kernel " + "for real event counts."); return createUopsBenchmarkRunner(State, BenchmarkPhaseSelector, ResultAggMode); } @@ -138,6 +141,9 @@ static_assert(std::is_trivial_v, "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 +167,10 @@ const PfmCountersInfo &ExegesisTarget::getPfmCounters(StringRef CpuName) const { return *Found->PCI; } +const PfmCountersInfo &ExegesisTarget::getDummyPfmCounters() const { + return PfmCountersInfo::Dummy; +} + ExegesisTarget::SavedState::~SavedState() {} // anchor. namespace { diff --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h index 9f21122a03b03..3cc36457134fd 100644 --- a/llvm/tools/llvm-exegesis/lib/Target.h +++ b/llvm/tools/llvm-exegesis/lib/Target.h @@ -59,6 +59,7 @@ struct PfmCountersInfo { unsigned NumIssueCounters; static const PfmCountersInfo Default; + static const PfmCountersInfo Dummy; }; struct CpuAndPfmCounters { @@ -178,6 +179,10 @@ class ExegesisTarget { // 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/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp index 06ae924bb9b26..ce028eb008929 100644 --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -140,6 +140,12 @@ static cl::opt BenchmarkPhaseSelector( "(default)")), cl::init(exegesis::BenchmarkPhaseSelectorE::Measure)); +static cl::opt + UseDummyPerfCounters("use-dummy-perf-counters", + cl::desc("Do not read real performance counters, use " + "dummy values (for testing)"), + cl::cat(BenchmarkOptions), cl::init(false)); + static cl::opt NumRepetitions("num-repetitions", cl::desc("number of time to repeat the asm snippet"), @@ -412,16 +418,24 @@ static void runBenchmarkConfigurations( } } + // With dummy counters, measurements are rather meaningless, + // so drop them altogether. + if (UseDummyPerfCounters) + Result.Measurements.clear(); + ExitOnFileError(BenchmarkFile, Result.writeYamlTo(State, Ostr)); } } void benchmarkMain() { - if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure) { + if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure && + !UseDummyPerfCounters) { #ifndef HAVE_LIBPFM ExitWithError( - "benchmarking unavailable, LLVM was built without libpfm. You can pass " - "--skip-measurements to skip the actual benchmarking."); + "benchmarking unavailable, LLVM was built without libpfm. You can " + "pass --benchmark-phase=... to skip the actual benchmarking or " + "--use-dummy-perf-counters to not query the kernel for real event " + "counts."); #else if (exegesis::pfm::pfmInitialize()) ExitWithError("cannot initialize libpfm"); @@ -432,7 +446,8 @@ void benchmarkMain() { InitializeAllAsmParsers(); InitializeAllExegesisTargets(); - const LLVMState State = ExitOnErr(LLVMState::Create(TripleName, MCPU)); + const LLVMState State = + ExitOnErr(LLVMState::Create(TripleName, MCPU, "", UseDummyPerfCounters)); // Preliminary check to ensure features needed for requested // benchmark mode are present on target CPU and/or OS.