[llvm] [llvm-exegesis] Add raw PMU encoding in TargetPfmCounters tablegen (PR #201228)

Maxim Potapchik via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 06:27:05 PDT 2026


https://github.com/MaximPotapchik updated https://github.com/llvm/llvm-project/pull/201228

>From dbc8e829cb3a7b191362a7019a15566abeab1d6d Mon Sep 17 00:00:00 2001
From: MaximPotapchik <maximp1012 at gmail.com>
Date: Tue, 2 Jun 2026 17:59:51 -0400
Subject: [PATCH] [llvm-exegesis] Add raw PMU encoding in TargetPfmCounters
 tablegen

Adds optional EventSelect and UMask fields to PfmCounter in
TargetPfmCounters.td. EventSelect defaults to -1 (no raw encoding).
When set, ExegesisEmitter outputs raw hex values instead of a libpfm
symbolic name, allowing per-CPU .td entries to bypass
pfm_get_os_event_encoding for counters that are undocumented or
unsupported in libpfm.

Extends PfmCountersInfo with CycleCounterEventSelect, CycleCounterUMask,
UopsCounterEventSelect, and UopsCounterUMask fields. PerfHelper wiring
in a subsequent patch.

Towards #187930
---
 llvm/include/llvm/Target/TargetPfmCounters.td |  5 ++
 llvm/test/TableGen/ExegesisRawPfmCounters.td  | 32 +++++++++++++
 llvm/tools/llvm-exegesis/lib/Target.cpp       |  8 +++-
 llvm/tools/llvm-exegesis/lib/Target.h         |  8 ++++
 llvm/utils/TableGen/ExegesisEmitter.cpp       | 47 +++++++++++++++----
 5 files changed, 90 insertions(+), 10 deletions(-)
 create mode 100644 llvm/test/TableGen/ExegesisRawPfmCounters.td

diff --git a/llvm/include/llvm/Target/TargetPfmCounters.td b/llvm/include/llvm/Target/TargetPfmCounters.td
index cfe432a992b71..f822636fb9a52 100644
--- a/llvm/include/llvm/Target/TargetPfmCounters.td
+++ b/llvm/include/llvm/Target/TargetPfmCounters.td
@@ -16,6 +16,11 @@ class PfmCounter<string counter> {
   // The name can be "some_counter + some_other_counter", in which case the
   // measured value is the sum of events on these counters.
   string Counter = counter;
+  // Optional raw PMU encoding that bypasses libpfm symbolic lookup.
+  // EventSelect is an up to 16-bit event identifier.
+  // UMask is an 8-bit unit mask qualifying the event.
+  int EventSelect = -1;
+  int UMask = 0;
 }
 
 // Issue counters can be tied to a ProcResource
diff --git a/llvm/test/TableGen/ExegesisRawPfmCounters.td b/llvm/test/TableGen/ExegesisRawPfmCounters.td
new file mode 100644
index 0000000000000..60f87c7812866
--- /dev/null
+++ b/llvm/test/TableGen/ExegesisRawPfmCounters.td
@@ -0,0 +1,32 @@
+// RUN: llvm-tblgen --gen-exegesis -I %p/../../include %s | FileCheck %s
+// CHECK-LABEL: static const PfmCountersInfo TestTargetTestPfmCounters = {
+// CHECK-NEXT:  nullptr, // Cycle counter uses raw encoding below.
+// CHECK-NEXT:  0x003c, 0x00, // Raw cycle counter
+// CHECK-NEXT:  nullptr, // Uops counter uses raw encoding below.
+// CHECK-NEXT:  0x00c2, 0x01, // Raw uops counter
+// CHECK-NEXT:  nullptr, 0, // No issue counters
+// CHECK-NEXT:  nullptr, 0 // No validation counters.
+// CHECK-NEXT: };
+
+include "llvm/Target/TargetPfmCounters.td"
+
+// Fake target for ExegesisEmitter
+class Target {}
+def TestTarget : Target {}
+
+def TestRawCycleCounter : PfmCounter<"testCycles"> {
+  let EventSelect = 0x003c;
+  let UMask = 0x00;
+}
+
+def TestRawUopsCounter : PfmCounter<"testUops"> {
+  let EventSelect = 0x00c2;
+  let UMask = 0x01;
+}
+
+def TestPfmCounters : ProcPfmCounters {
+  let CycleCounter = TestRawCycleCounter;
+  let UopsCounter = TestRawUopsCounter;
+}
+
+def : PfmCountersDefaultBinding<TestPfmCounters>;
diff --git a/llvm/tools/llvm-exegesis/lib/Target.cpp b/llvm/tools/llvm-exegesis/lib/Target.cpp
index 2ad6c5af4de4d..9dc58dafbef7a 100644
--- a/llvm/tools/llvm-exegesis/lib/Target.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Target.cpp
@@ -179,11 +179,15 @@ std::unique_ptr<BenchmarkRunner> ExegesisTarget::createUopsBenchmarkRunner(
 static_assert(std::is_trivial_v<PfmCountersInfo>,
               "We shouldn't have dynamic initialization here");
 
-const PfmCountersInfo PfmCountersInfo::Default = {nullptr, nullptr, nullptr,
-                                                  0u,      nullptr, 0u};
+const PfmCountersInfo PfmCountersInfo::Default = {
+    nullptr, -1, 0, nullptr, -1, 0, nullptr, 0u, nullptr, 0u};
 const PfmCountersInfo PfmCountersInfo::Dummy = {
     pfm::PerfEvent::DummyEventString,
+    -1,
+    0,
     pfm::PerfEvent::DummyEventString,
+    -1,
+    0,
     nullptr,
     0u,
     nullptr,
diff --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h
index 77fbaa6e95412..1d01b0558f75c 100644
--- a/llvm/tools/llvm-exegesis/lib/Target.h
+++ b/llvm/tools/llvm-exegesis/lib/Target.h
@@ -45,10 +45,18 @@ struct PfmCountersInfo {
   // cycles.
   const char *CycleCounter;
 
+  // Optional raw PMU encoding for Cycle counter.
+  int CycleCounterEventSelect;
+  int CycleCounterUMask;
+
   // An optional name of a performance counter that can be used to measure
   // uops.
   const char *UopsCounter;
 
+  // Optional raw PMU encoding for Uops counter.
+  int UopsCounterEventSelect;
+  int UopsCounterUMask;
+
   // An IssueCounter specifies how to measure uops issued to specific proc
   // resources.
   struct IssueCounter {
diff --git a/llvm/utils/TableGen/ExegesisEmitter.cpp b/llvm/utils/TableGen/ExegesisEmitter.cpp
index bd69919bb462e..8aaecb9c1cdbd 100644
--- a/llvm/utils/TableGen/ExegesisEmitter.cpp
+++ b/llvm/utils/TableGen/ExegesisEmitter.cpp
@@ -13,11 +13,14 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Error.h"
 #include "llvm/TableGen/Record.h"
 #include "llvm/TableGen/TableGenBackend.h"
 #include <cassert>
+#include <cstdint>
 #include <map>
 #include <string>
 #include <vector>
@@ -122,10 +125,10 @@ static bool EventNumberLess(const ValidationCounterInfo &LHS,
 void ExegesisEmitter::emitPfmCountersInfo(const Record &Def,
                                           unsigned &IssueCountersTableOffset,
                                           raw_ostream &OS) const {
-  const auto CycleCounter =
-      Def.getValueAsDef("CycleCounter")->getValueAsString("Counter");
-  const auto UopsCounter =
-      Def.getValueAsDef("UopsCounter")->getValueAsString("Counter");
+  const Record *CycleCounterDef = Def.getValueAsDef("CycleCounter");
+  const auto CycleCounter = CycleCounterDef->getValueAsString("Counter");
+  const Record *UopsCounterDef = Def.getValueAsDef("UopsCounter");
+  const auto UopsCounter = UopsCounterDef->getValueAsString("Counter");
   const size_t NumIssueCounters =
       Def.getValueAsListOfDefs("IssueCounters").size();
   const size_t NumValidationCounters =
@@ -158,18 +161,46 @@ void ExegesisEmitter::emitPfmCountersInfo(const Record &Def,
      << " = {\n";
 
   // Cycle Counter.
-  if (CycleCounter.empty())
+  if (CycleCounterDef->getValueAsInt("EventSelect") != -1) {
+    const int64_t EventSelect = CycleCounterDef->getValueAsInt("EventSelect");
+    if (!isInt<16>(EventSelect))
+      PrintFatalError(CycleCounterDef->getLoc(),
+                      "EventSelect must fit in 16 bits");
+    const int64_t UMask = CycleCounterDef->getValueAsInt("UMask");
+    if (!isInt<8>(UMask))
+      PrintFatalError(CycleCounterDef->getLoc(), "UMask must fit in 8 bits");
+    OS << "  nullptr,  // Cycle counter uses raw encoding below.\n";
+    OS << "  0x" << format_hex_no_prefix(EventSelect, 4) << ", 0x"
+       << format_hex_no_prefix(UMask, 2) << ", // Raw cycle counter\n";
+  } else if (CycleCounter.empty()) {
     OS << "  nullptr,  // No cycle counter.\n";
-  else
+    OS << "  -1, 0, // No raw cycle counter\n";
+  } else {
     OS << "  " << Target << "PfmCounterNames[" << getPfmCounterId(CycleCounter)
        << "],  // Cycle counter\n";
+    OS << "  -1, 0, // No raw cycle counter\n";
+  }
 
   // Uops Counter.
-  if (UopsCounter.empty())
+  if (UopsCounterDef->getValueAsInt("EventSelect") != -1) {
+    const int64_t EventSelect = UopsCounterDef->getValueAsInt("EventSelect");
+    if (!isInt<16>(EventSelect))
+      PrintFatalError(UopsCounterDef->getLoc(),
+                      "EventSelect must fit in 16 bits");
+    const int64_t UMask = UopsCounterDef->getValueAsInt("UMask");
+    if (!isInt<8>(UMask))
+      PrintFatalError(UopsCounterDef->getLoc(), "UMask must fit in 8 bits");
+    OS << "  nullptr,  // Uops counter uses raw encoding below.\n";
+    OS << "  0x" << format_hex_no_prefix(EventSelect, 4) << ", 0x"
+       << format_hex_no_prefix(UMask, 2) << ", // Raw uops counter\n";
+  } else if (UopsCounter.empty()) {
     OS << "  nullptr,  // No uops counter.\n";
-  else
+    OS << "  -1, 0, // No raw uops counter\n";
+  } else {
     OS << "  " << Target << "PfmCounterNames[" << getPfmCounterId(UopsCounter)
        << "],  // Uops counter\n";
+    OS << "  -1, 0, // No raw uops counter\n";
+  }
 
   // Issue Counters
   if (NumIssueCounters == 0)



More information about the llvm-commits mailing list