[llvm] [RISC-V] Pool duplicate scheduling class descriptors (PR #202659)

David Zbarsky via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 6 06:50:18 PDT 2026


https://github.com/dzbarsky updated https://github.com/llvm/llvm-project/pull/202659

>From 4c5ba9c65f0afb57dd673a90138294960706fd2f Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Tue, 9 Jun 2026 03:08:33 -0400
Subject: [PATCH 1/2] [TableGen] Pool duplicate scheduling class descriptors

Release builds currently emit one MCSchedClassDesc table for every
processor model. The RISC-V backend emits 158,654 descriptors, but only
4,206 descriptors are unique.

Build one target-wide descriptor pool in SubtargetEmitter and emit a
uint16_t index table for each processor model when the pooled
representation is smaller. Keep the existing per-model tables in debug
and dump-enabled builds so scheduling class names remain available.
MCSchedModel::getSchedClassDesc resolves the index before returning the
descriptor. Assert the release and debug MCSchedClassDesc sizes used by
the size calculation, the generated index width, and every generated
index-table length.

On an arm64 Release build with all RISC-V processors, stripped sizes
change as follows:

  llc:       57,253,952 -> 55,388,272 bytes (-1,865,680)
  llvm-mca:   6,242,424 ->  4,376,744 bytes (-1,865,680)

Pooling the debug tables was also measured. The debug-pooling version changes +320/-64 lines in five files, while this release-only version changes +224/-7 lines in four files. In matched current builds, fully stripped llc and llvm-mca have identical sizes. The release-only raw binaries are 1,488 and 1,520 bytes larger, respectively; __TEXT,__const is 16 and 0 bytes larger, __DATA_CONST,__const is 184 bytes larger in both, and linked fixup counts are identical. Keep the direct debug tables because debug pooling does not reduce the fully stripped binaries.

All 158,654 reconstructed RISC-V descriptors matched the direct tables.
llc output was byte-identical for the default CPU, sifive-p670,
spacemit-x100, and xiangshan-kunminghu. llvm-mca output was also
byte-identical.

A 100-pair alternating llvm-mca benchmark used the SCR7 ALU test with
100,000 iterations. Direct and pooled medians were both 1.31 seconds.
The paired mean difference was +0.0039 seconds, with a bootstrapped 95%
interval of -0.0031 to +0.0110 seconds. TableGen generation time changed
from a 2.630-second median to 2.645 seconds.

The new TableGen test checks pooled descriptors, per-model indices, and
debug-table preservation.
---
 llvm/include/llvm/MC/MCSchedule.h            |   9 ++
 llvm/lib/MC/MCSchedule.cpp                   |   1 +
 llvm/test/TableGen/SchedClassTablePooling.td |  74 ++++++++++
 llvm/utils/TableGen/SubtargetEmitter.cpp     | 147 ++++++++++++++++++-
 4 files changed, 224 insertions(+), 7 deletions(-)
 create mode 100644 llvm/test/TableGen/SchedClassTablePooling.td

diff --git a/llvm/include/llvm/MC/MCSchedule.h b/llvm/include/llvm/MC/MCSchedule.h
index 7ed86df0a8caf..18b7fae388886 100644
--- a/llvm/include/llvm/MC/MCSchedule.h
+++ b/llvm/include/llvm/MC/MCSchedule.h
@@ -152,6 +152,12 @@ struct MCSchedClassDesc {
   }
 };
 
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+static_assert(sizeof(MCSchedClassDesc) == 20);
+#else
+static_assert(sizeof(MCSchedClassDesc) == 14);
+#endif
+
 /// Specify the cost of a register definition in terms of number of physical
 /// register allocated at register renaming stage. For example, AMD Jaguar.
 /// natively supports 128-bit data types, and operations on 256-bit registers
@@ -337,6 +343,7 @@ struct MCSchedModel {
   const InstrItinerary *InstrItineraries;
 
   const MCExtraProcessorInfo *ExtraProcessorInfo;
+  const uint16_t *SchedClassTableIndices;
 
   bool hasExtraProcessorInfo() const { return ExtraProcessorInfo; }
 
@@ -373,6 +380,8 @@ struct MCSchedModel {
     assert(hasInstrSchedModel() && "No scheduling machine model");
 
     assert(SchedClassIdx < NumSchedClasses && "bad scheduling class idx");
+    if (SchedClassTableIndices)
+      SchedClassIdx = SchedClassTableIndices[SchedClassIdx];
     return &SchedClassTable[SchedClassIdx];
   }
 
diff --git a/llvm/lib/MC/MCSchedule.cpp b/llvm/lib/MC/MCSchedule.cpp
index e2124c1c76355..cebe0c2fb5c2d 100644
--- a/llvm/lib/MC/MCSchedule.cpp
+++ b/llvm/lib/MC/MCSchedule.cpp
@@ -54,6 +54,7 @@ const MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
                                             0,
                                             nullptr,
                                             nullptr,
+                                            nullptr,
                                             nullptr};
 
 int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI,
diff --git a/llvm/test/TableGen/SchedClassTablePooling.td b/llvm/test/TableGen/SchedClassTablePooling.td
new file mode 100644
index 0000000000000..cbb7cca50df42
--- /dev/null
+++ b/llvm/test/TableGen/SchedClassTablePooling.td
@@ -0,0 +1,74 @@
+// RUN: llvm-tblgen -gen-subtarget -I %p/../../include %s -o - | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+let OutOperandList = (outs), InOperandList = (ins) in {
+  def InstA : Instruction;
+  def InstB : Instruction;
+}
+
+let CompleteModel = 0 in {
+  def ModelA : SchedMachineModel;
+  def ModelB : SchedMachineModel;
+}
+
+let SchedModel = ModelA in {
+  def WriteA0 : SchedWriteRes<[]>;
+  def WriteB0 : SchedWriteRes<[]>;
+
+  def : InstRW<[WriteA0], (instrs InstA)>;
+  def : InstRW<[WriteB0], (instrs InstB)>;
+}
+
+let SchedModel = ModelB in {
+  def WriteA1 : SchedWriteRes<[]>;
+  def WriteB1 : SchedWriteRes<[]>;
+
+  def : InstRW<[WriteA1], (instrs InstA)>;
+  def : InstRW<[WriteB1], (instrs InstB)>;
+}
+
+def ProcessorA : ProcessorModel<"processor-a", ModelA, []>;
+def ProcessorB : ProcessorModel<"processor-b", ModelB, []>;
+
+// Debug builds retain the per-processor tables so class names are available.
+// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+// CHECK: static const llvm::MCSchedClassDesc ModelASchedClasses[] = {
+// CHECK: static const llvm::MCSchedClassDesc ModelBSchedClasses[] = {
+
+// Release builds share identical descriptors through a compact index table.
+// CHECK: #else
+// CHECK: static const llvm::MCSchedClassDesc MyTargetSchedClassPool[] = {
+// CHECK: static_assert(sizeof(MyTargetSchedClassPool) / sizeof(MyTargetSchedClassPool[0]) <= (1ULL << 16));
+// CHECK: static const uint16_t ModelASchedClassIndices[] = {
+// CHECK: static_assert(sizeof(ModelASchedClassIndices[0]) == 2);
+// CHECK: static_assert(sizeof(ModelASchedClassIndices) / sizeof(ModelASchedClassIndices[0]) == 3);
+// CHECK: static const uint16_t ModelBSchedClassIndices[] = {
+// CHECK: static_assert(sizeof(ModelBSchedClassIndices[0]) == 2);
+// CHECK: static_assert(sizeof(ModelBSchedClassIndices) / sizeof(ModelBSchedClassIndices[0]) == 3);
+
+// CHECK: static const llvm::MCSchedModel ModelA = {
+// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+// CHECK-NEXT:   ModelASchedClasses,
+// CHECK-NEXT: #else
+// CHECK-NEXT:   MyTargetSchedClassPool,
+// CHECK-NEXT: #endif
+// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+// CHECK-NEXT:   nullptr,
+// CHECK-NEXT: #else
+// CHECK-NEXT:   ModelASchedClassIndices,
+// CHECK-NEXT: #endif
+
+// CHECK: static const llvm::MCSchedModel ModelB = {
+// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+// CHECK-NEXT:   ModelBSchedClasses,
+// CHECK-NEXT: #else
+// CHECK-NEXT:   MyTargetSchedClassPool,
+// CHECK-NEXT: #endif
+// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+// CHECK-NEXT:   nullptr,
+// CHECK-NEXT: #else
+// CHECK-NEXT:   ModelBSchedClassIndices,
+// CHECK-NEXT: #endif
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index bede5ebc44007..8c2d445bac808 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -36,7 +36,10 @@
 #include <cassert>
 #include <cstdint>
 #include <iterator>
+#include <limits>
+#include <map>
 #include <string>
+#include <tuple>
 #include <vector>
 
 using namespace llvm;
@@ -45,6 +48,17 @@ using namespace llvm;
 
 namespace {
 
+using SchedClassKey = std::tuple<uint16_t, bool, bool, bool, uint16_t, uint16_t,
+                                 uint16_t, uint16_t, uint16_t, uint16_t>;
+
+static SchedClassKey getSchedClassKey(const MCSchedClassDesc &Desc) {
+  return {Desc.NumMicroOps,     Desc.BeginGroup,
+          Desc.EndGroup,        Desc.RetireOOO,
+          Desc.WriteProcResIdx, Desc.NumWriteProcResEntries,
+          Desc.WriteLatencyIdx, Desc.NumWriteLatencyEntries,
+          Desc.ReadAdvanceIdx,  Desc.NumReadAdvanceEntries};
+}
+
 class SubtargetEmitter : TargetFeaturesEmitter {
   // Each processor has a SchedClassDesc table with an entry for each
   // SchedClass. The SchedClassDesc table indexes into a global write resource
@@ -55,6 +69,9 @@ class SubtargetEmitter : TargetFeaturesEmitter {
     std::vector<MCWriteLatencyEntry> WriteLatencies;
     std::vector<std::string> WriterNames;
     std::vector<MCReadAdvanceEntry> ReadAdvanceEntries;
+    std::vector<MCSchedClassDesc> SchedClassPool;
+    std::vector<std::vector<uint16_t>> ProcSchedClassIndices;
+    bool UseSchedClassPool = false;
 
     // Reserve an invalid entry at index 0
     SchedClassTables() {
@@ -124,8 +141,10 @@ class SubtargetEmitter : TargetFeaturesEmitter {
                            const CodeGenProcModel &ProcModel);
   void genSchedClassTables(const CodeGenProcModel &ProcModel,
                            SchedClassTables &SchedTables);
+  void buildSchedClassPool(SchedClassTables &SchedTables);
   void emitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS);
-  void emitProcessorModels(raw_ostream &OS);
+  void emitProcessorModels(const SchedClassTables &SchedTables,
+                           raw_ostream &OS);
   void emitSchedModelHelpers(const std::string &ClassName, raw_ostream &OS);
   void emitSchedModelHelpersImpl(raw_ostream &OS,
                                  bool OnlyExpandMCInstPredicates = false);
@@ -1334,6 +1353,53 @@ void SubtargetEmitter::genSchedClassTables(const CodeGenProcModel &ProcModel,
   }
 }
 
+void SubtargetEmitter::buildSchedClassPool(SchedClassTables &SchedTables) {
+  constexpr size_t ReleaseSchedClassDescSize = 14;
+  std::map<SchedClassKey, uint16_t> PoolIndices;
+  size_t NumSchedClassDescs = 0;
+
+  SchedTables.ProcSchedClassIndices.clear();
+  SchedTables.ProcSchedClassIndices.resize(SchedTables.ProcSchedClasses.size());
+
+  for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
+    if (!Proc.hasInstrSchedModel())
+      continue;
+
+    const std::vector<MCSchedClassDesc> &SCTab =
+        SchedTables.ProcSchedClasses[1 + Idx];
+    std::vector<uint16_t> &Indices = SchedTables.ProcSchedClassIndices[1 + Idx];
+    Indices.reserve(SCTab.size());
+    NumSchedClassDescs += SCTab.size();
+
+    for (const MCSchedClassDesc &Desc : SCTab) {
+      SchedClassKey Key = getSchedClassKey(Desc);
+      auto It = PoolIndices.find(Key);
+      if (It == PoolIndices.end()) {
+        if (SchedTables.SchedClassPool.size() >
+            std::numeric_limits<uint16_t>::max()) {
+          SchedTables.SchedClassPool.clear();
+          SchedTables.ProcSchedClassIndices.clear();
+          return;
+        }
+        uint16_t PoolIdx = SchedTables.SchedClassPool.size();
+        It = PoolIndices.emplace(std::move(Key), PoolIdx).first;
+        SchedTables.SchedClassPool.push_back(Desc);
+      }
+      Indices.push_back(It->second);
+    }
+  }
+
+  size_t DirectSize = NumSchedClassDescs * ReleaseSchedClassDescSize;
+  size_t PooledSize =
+      SchedTables.SchedClassPool.size() * ReleaseSchedClassDescSize +
+      NumSchedClassDescs * sizeof(uint16_t);
+  SchedTables.UseSchedClassPool = PooledSize < DirectSize;
+  if (!SchedTables.UseSchedClassPool) {
+    SchedTables.SchedClassPool.clear();
+    SchedTables.ProcSchedClassIndices.clear();
+  }
+}
+
 // Emit SchedClass tables for all processors and associated global tables.
 void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
                                             raw_ostream &OS) {
@@ -1391,6 +1457,9 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
   StringToOffsetTable StrTab;
   unsigned InvalidNameOff = StrTab.GetOrAddStringOffset("InvalidSchedClass");
 
+  if (SchedTables.UseSchedClassPool)
+    OS << "\n#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n";
+
   // Emit a SchedClass table for each processor.
   for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
     if (!Proc.hasInstrSchedModel())
@@ -1432,10 +1501,53 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
     OS << "}; // " << Proc.ModelName << "SchedClasses\n";
   }
 
+  if (SchedTables.UseSchedClassPool) {
+    OS << "#else\n";
+    OS << "\n// {NumMicroOps, BeginGroup, EndGroup, RetireOOO,"
+       << " WriteProcResIdx,#, WriteLatencyIdx,#, ReadAdvanceIdx,#}\n";
+    OS << "static const llvm::MCSchedClassDesc " << Target
+       << "SchedClassPool[] = {\n";
+    for (const auto &[PoolIdx, MCDesc] :
+         enumerate(SchedTables.SchedClassPool)) {
+      OS << "  {DBGFIELD(0) " << MCDesc.NumMicroOps << ", "
+         << (MCDesc.BeginGroup ? "true" : "false") << ", "
+         << (MCDesc.EndGroup ? "true" : "false") << ", "
+         << (MCDesc.RetireOOO ? "true" : "false") << ", "
+         << format("%2d", MCDesc.WriteProcResIdx) << ", "
+         << MCDesc.NumWriteProcResEntries << ", "
+         << format("%2d", MCDesc.WriteLatencyIdx) << ", "
+         << MCDesc.NumWriteLatencyEntries << ", "
+         << format("%2d", MCDesc.ReadAdvanceIdx) << ", "
+         << MCDesc.NumReadAdvanceEntries << "}, // #" << PoolIdx << '\n';
+    }
+    OS << "}; // " << Target << "SchedClassPool\n";
+    OS << "static_assert(sizeof(" << Target << "SchedClassPool) / sizeof("
+       << Target << "SchedClassPool[0]) <= (1ULL << 16));\n";
+
+    for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
+      if (!Proc.hasInstrSchedModel())
+        continue;
+
+      ArrayRef<uint16_t> Indices = SchedTables.ProcSchedClassIndices[1 + Idx];
+      OS << "\nstatic const uint16_t " << Proc.ModelName
+         << "SchedClassIndices[] = {\n";
+      for (const auto [SCIdx, PoolIdx] : enumerate(Indices))
+        OS << "  " << PoolIdx << ", // #" << SCIdx << '\n';
+      OS << "}; // " << Proc.ModelName << "SchedClassIndices\n";
+      OS << "static_assert(sizeof(" << Proc.ModelName
+         << "SchedClassIndices[0]) == 2);\n";
+      OS << "static_assert(sizeof(" << Proc.ModelName
+         << "SchedClassIndices) / sizeof(" << Proc.ModelName
+         << "SchedClassIndices[0]) == " << Indices.size() << ");\n";
+    }
+    OS << "#endif\n";
+  }
+
   StrTab.EmitStringTableDef(OS, Target + "SchedClassNames");
 }
 
-void SubtargetEmitter::emitProcessorModels(raw_ostream &OS) {
+void SubtargetEmitter::emitProcessorModels(const SchedClassTables &SchedTables,
+                                           raw_ostream &OS) {
   // For each processor model.
   for (const CodeGenProcModel &PM : SchedModels.procModels()) {
     // Emit extra processor info if available.
@@ -1479,12 +1591,24 @@ void SubtargetEmitter::emitProcessorModels(raw_ostream &OS) {
 
     OS << "  " << PM.Index << ", // Processor ID\n";
     if (PM.hasInstrSchedModel())
-      OS << "  " << PM.ModelName << "ProcResources" << ",\n"
+      OS << "  " << PM.ModelName << "ProcResources" << ",\n";
+    else
+      OS << "  nullptr,\n";
+    if (PM.hasInstrSchedModel() && SchedTables.UseSchedClassPool)
+      OS << "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n"
          << "  " << PM.ModelName << "SchedClasses" << ",\n"
-         << "  " << PM.ProcResourceDefs.size() + 1 << ",\n"
+         << "#else\n"
+         << "  " << Target << "SchedClassPool,\n"
+         << "#endif\n";
+    else if (PM.hasInstrSchedModel())
+      OS << "  " << PM.ModelName << "SchedClasses" << ",\n";
+    else
+      OS << "  nullptr,\n";
+    if (PM.hasInstrSchedModel())
+      OS << "  " << PM.ProcResourceDefs.size() + 1 << ",\n"
          << "  " << SchedModels.schedClasses().size() << ",\n";
     else
-      OS << "  nullptr, nullptr, 0, 0,"
+      OS << "  0, 0,"
          << " // No instruction-level machine model.\n";
     OS << "  DBGVAL_OR_NULLPTR(&" << Target
        << "SchedClassNames), // SchedClassNames\n";
@@ -1495,7 +1619,15 @@ void SubtargetEmitter::emitProcessorModels(raw_ostream &OS) {
     if (PM.hasExtraProcessorInfo())
       OS << "  &" << PM.ModelName << "ExtraInfo,\n";
     else
-      OS << "  nullptr // No extra processor descriptor\n";
+      OS << "  nullptr, // No extra processor descriptor\n";
+    if (PM.hasInstrSchedModel() && SchedTables.UseSchedClassPool)
+      OS << "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n"
+         << "  nullptr,\n"
+         << "#else\n"
+         << "  " << PM.ModelName << "SchedClassIndices,\n"
+         << "#endif\n";
+    else
+      OS << "  nullptr,\n";
     OS << "};\n";
   }
 }
@@ -1528,10 +1660,11 @@ void SubtargetEmitter::emitSchedModel(raw_ostream &OS) {
   for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) {
     genSchedClassTables(ProcModel, SchedTables);
   }
+  buildSchedClassPool(SchedTables);
   emitSchedClassTables(SchedTables, OS);
 
   // Emit the processor machine model
-  emitProcessorModels(OS);
+  emitProcessorModels(SchedTables, OS);
 
   OS << "\n#undef DBGFIELD\n";
   OS << "\n#undef DBGVAL_OR_NULLPTR\n";

>From de1be2692bcd9d9efc63ed05ae56b61d750495a6 Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Mon, 6 Jul 2026 09:36:40 -0400
Subject: [PATCH 2/2] [TableGen] Store scheduling class name offsets separately

Remove NameOff from MCSchedClassDesc so its layout no longer depends on NDEBUG. Emit one target-wide name-offset table indexed by SchedClassIdx and use pooled descriptor tables in all builds.

Co-authored-by: OpenAI Codex <noreply at openai.com>
---
 llvm/include/llvm/MC/MCSchedule.h             |  12 +-
 llvm/lib/MC/MCSchedule.cpp                    |   1 +
 .../TableGen/CompressWriteLatencyEntry.td     |   8 +-
 llvm/test/TableGen/InvalidMCSchedClassDesc.td |  21 +--
 llvm/test/TableGen/SchedClassTablePooling.td  |  41 ++----
 llvm/utils/TableGen/SubtargetEmitter.cpp      | 133 +++++++++---------
 6 files changed, 98 insertions(+), 118 deletions(-)

diff --git a/llvm/include/llvm/MC/MCSchedule.h b/llvm/include/llvm/MC/MCSchedule.h
index 969e1c54c7000..fd114ebb86f4a 100644
--- a/llvm/include/llvm/MC/MCSchedule.h
+++ b/llvm/include/llvm/MC/MCSchedule.h
@@ -130,9 +130,6 @@ struct MCSchedClassDesc {
   static const unsigned short InvalidNumMicroOps = (1U << 13) - 1;
   static const unsigned short VariantNumMicroOps = InvalidNumMicroOps - 1;
 
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-  uint32_t NameOff;
-#endif
   uint16_t NumMicroOps : 13;
   uint16_t BeginGroup : 1;
   uint16_t EndGroup : 1;
@@ -152,13 +149,9 @@ struct MCSchedClassDesc {
   }
 };
 
-// If either assertion fails, try to repack MCSchedClassDesc to preserve the
+// If this assertion fails, try to repack MCSchedClassDesc to preserve the
 // compact layout; remove the assertion if the layout can no longer be kept.
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-static_assert(sizeof(MCSchedClassDesc) == 20);
-#else
 static_assert(sizeof(MCSchedClassDesc) == 14);
-#endif
 
 /// Specify the cost of a register definition in terms of number of physical
 /// register allocated at register renaming stage. For example, AMD Jaguar.
@@ -340,6 +333,7 @@ struct MCSchedModel {
   unsigned NumProcResourceKinds;
   unsigned NumSchedClasses;
   const StringTable *SchedClassNames;
+  const uint32_t *SchedClassNameOffsets;
   // Instruction itinerary tables used by InstrItineraryData.
   friend class InstrItineraryData;
   const InstrItinerary *InstrItineraries;
@@ -389,7 +383,7 @@ struct MCSchedModel {
 
   StringRef getSchedClassName(unsigned SchedClassIdx) const {
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-    return (*SchedClassNames)[SchedClassTable[SchedClassIdx].NameOff];
+    return (*SchedClassNames)[SchedClassNameOffsets[SchedClassIdx]];
 #else
     return "<unknown>";
 #endif
diff --git a/llvm/lib/MC/MCSchedule.cpp b/llvm/lib/MC/MCSchedule.cpp
index cebe0c2fb5c2d..21dbf1d5766b4 100644
--- a/llvm/lib/MC/MCSchedule.cpp
+++ b/llvm/lib/MC/MCSchedule.cpp
@@ -55,6 +55,7 @@ const MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
                                             nullptr,
                                             nullptr,
                                             nullptr,
+                                            nullptr,
                                             nullptr};
 
 int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI,
diff --git a/llvm/test/TableGen/CompressWriteLatencyEntry.td b/llvm/test/TableGen/CompressWriteLatencyEntry.td
index d6a9f0ac0dd76..b09ee0b0434d8 100644
--- a/llvm/test/TableGen/CompressWriteLatencyEntry.td
+++ b/llvm/test/TableGen/CompressWriteLatencyEntry.td
@@ -33,10 +33,10 @@ def Read_D : SchedRead;
 // CHECK-NEXT: }; // MyTargetReadAdvanceTable
 
 // CHECK:  static const llvm::MCSchedClassDesc SchedModel_ASchedClasses[] = {
-// CHECK-NEXT:  {DBGFIELD(1)  8191, false, false, false, 0, 0,  0, 0,  0, 0},
-// CHECK-NEXT:  {DBGFIELD(/*Inst_A*/ {{[0-9]+}})             1, false, false, false,  0, 0,  1, 1,  0, 0}, // #1
-// CHECK-NEXT:  {DBGFIELD(/*Inst_B*/ {{[0-9]+}})             1, false, false, false,  0, 0,  2, 1,  0, 0}, // #2
-// CHECK-NEXT:  {DBGFIELD(/*Inst_C*/ {{[0-9]+}})             1, false, false, false,  0, 0,  1, 1,  1, 1}, // #3
+// CHECK-NEXT:  {8191, false, false, false, 0, 0,  0, 0,  0, 0},
+// CHECK-NEXT:  {/*Inst_A*/             1, false, false, false,  0, 0,  1, 1,  0, 0}, // #1
+// CHECK-NEXT:  {/*Inst_B*/             1, false, false, false,  0, 0,  2, 1,  0, 0}, // #2
+// CHECK-NEXT:  {/*Inst_C*/             1, false, false, false,  0, 0,  1, 1,  1, 1}, // #3
 // CHECK-NEXT: }; // SchedModel_ASchedClasses
 
 let SchedModel = SchedModel_A in {
diff --git a/llvm/test/TableGen/InvalidMCSchedClassDesc.td b/llvm/test/TableGen/InvalidMCSchedClassDesc.td
index e43edd4174589..0221bf59acaeb 100644
--- a/llvm/test/TableGen/InvalidMCSchedClassDesc.td
+++ b/llvm/test/TableGen/InvalidMCSchedClassDesc.td
@@ -17,26 +17,29 @@ let CompleteModel = 0 in {
 }
 
 // Inst_B didn't have the resoures, and it is invalid.
-// CHECK: SchedModel_ASchedClasses[] = {
-// CHECK: {DBGFIELD(/*Inst_A*/ 19)             1
-// CHECK-NEXT: {DBGFIELD(/*Inst_B*/ 26)             8191
+// CHECK: SchedModel_ASchedClassIndices[] = {
+// CHECK-NEXT: 0, // #0
+// CHECK-NEXT: 1, // #1
+// CHECK-NEXT: 0, // #2
 let SchedModel = SchedModel_A in {
   def Write_A : SchedWriteRes<[]>;
   def : InstRW<[Write_A], (instrs Inst_A)>;
 }
 
 // Inst_A didn't have the resoures, and it is invalid.
-// CHECK: SchedModel_BSchedClasses[] = {
-// CHECK: {DBGFIELD(/*Inst_A*/ 19)             8191
-// CHECK-NEXT: {DBGFIELD(/*Inst_B*/ 26)             1
+// CHECK: SchedModel_BSchedClassIndices[] = {
+// CHECK-NEXT: 0, // #0
+// CHECK-NEXT: 0, // #1
+// CHECK-NEXT: 1, // #2
 let SchedModel = SchedModel_B in {
   def Write_B: SchedWriteRes<[]>;
   def : InstRW<[Write_B], (instrs Inst_B)>;
 }
 
-// CHECK: SchedModel_CSchedClasses[] = {
-// CHECK: {DBGFIELD(/*Inst_A*/ 19)             1
-// CHECK-NEXT: {DBGFIELD(/*Inst_B*/ 26)             1
+// CHECK: SchedModel_CSchedClassIndices[] = {
+// CHECK-NEXT: 0, // #0
+// CHECK-NEXT: 1, // #1
+// CHECK-NEXT: 1, // #2
 let SchedModel = SchedModel_C in {
   def Write_C: SchedWriteRes<[]>;
   def : InstRW<[Write_C], (instrs Inst_A, Inst_B)>;
diff --git a/llvm/test/TableGen/SchedClassTablePooling.td b/llvm/test/TableGen/SchedClassTablePooling.td
index cbb7cca50df42..2fc43bbebe03f 100644
--- a/llvm/test/TableGen/SchedClassTablePooling.td
+++ b/llvm/test/TableGen/SchedClassTablePooling.td
@@ -33,13 +33,7 @@ let SchedModel = ModelB in {
 def ProcessorA : ProcessorModel<"processor-a", ModelA, []>;
 def ProcessorB : ProcessorModel<"processor-b", ModelB, []>;
 
-// Debug builds retain the per-processor tables so class names are available.
-// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-// CHECK: static const llvm::MCSchedClassDesc ModelASchedClasses[] = {
-// CHECK: static const llvm::MCSchedClassDesc ModelBSchedClasses[] = {
-
-// Release builds share identical descriptors through a compact index table.
-// CHECK: #else
+// All builds share identical descriptors through compact index tables.
 // CHECK: static const llvm::MCSchedClassDesc MyTargetSchedClassPool[] = {
 // CHECK: static_assert(sizeof(MyTargetSchedClassPool) / sizeof(MyTargetSchedClassPool[0]) <= (1ULL << 16));
 // CHECK: static const uint16_t ModelASchedClassIndices[] = {
@@ -49,26 +43,19 @@ def ProcessorB : ProcessorModel<"processor-b", ModelB, []>;
 // CHECK: static_assert(sizeof(ModelBSchedClassIndices[0]) == 2);
 // CHECK: static_assert(sizeof(ModelBSchedClassIndices) / sizeof(ModelBSchedClassIndices[0]) == 3);
 
+// Names are stored separately and indexed by the original scheduling class.
+// CHECK: static const uint32_t MyTargetSchedClassNameOffsets[] = {
+// CHECK: static_assert(sizeof(MyTargetSchedClassNameOffsets[0]) == 4);
+// CHECK: static_assert(sizeof(MyTargetSchedClassNameOffsets) / sizeof(MyTargetSchedClassNameOffsets[0]) == 3);
+
 // CHECK: static const llvm::MCSchedModel ModelA = {
-// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-// CHECK-NEXT:   ModelASchedClasses,
-// CHECK-NEXT: #else
-// CHECK-NEXT:   MyTargetSchedClassPool,
-// CHECK-NEXT: #endif
-// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-// CHECK-NEXT:   nullptr,
-// CHECK-NEXT: #else
-// CHECK-NEXT:   ModelASchedClassIndices,
-// CHECK-NEXT: #endif
+// CHECK:   MyTargetSchedClassPool,
+// CHECK:   DBGVAL_OR_NULLPTR(&MyTargetSchedClassNames), // SchedClassNames
+// CHECK-NEXT:   DBGVAL_OR_NULLPTR(MyTargetSchedClassNameOffsets), // SchedClassNameOffsets
+// CHECK:   ModelASchedClassIndices,
 
 // CHECK: static const llvm::MCSchedModel ModelB = {
-// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-// CHECK-NEXT:   ModelBSchedClasses,
-// CHECK-NEXT: #else
-// CHECK-NEXT:   MyTargetSchedClassPool,
-// CHECK-NEXT: #endif
-// CHECK: #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-// CHECK-NEXT:   nullptr,
-// CHECK-NEXT: #else
-// CHECK-NEXT:   ModelBSchedClassIndices,
-// CHECK-NEXT: #endif
+// CHECK:   MyTargetSchedClassPool,
+// CHECK:   DBGVAL_OR_NULLPTR(&MyTargetSchedClassNames), // SchedClassNames
+// CHECK-NEXT:   DBGVAL_OR_NULLPTR(MyTargetSchedClassNameOffsets), // SchedClassNameOffsets
+// CHECK:   ModelBSchedClassIndices,
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index 8c2d445bac808..e2a50736473c6 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -1455,70 +1455,64 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
 
   // Pool all SchedClass names in a string table.
   StringToOffsetTable StrTab;
-  unsigned InvalidNameOff = StrTab.GetOrAddStringOffset("InvalidSchedClass");
+  std::vector<unsigned> SchedClassNameOffsets(
+      SchedModels.schedClasses().size());
+  assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" &&
+         "invalid class not first");
+  SchedClassNameOffsets[0] = StrTab.GetOrAddStringOffset("InvalidSchedClass");
+  for (unsigned Idx = 1; Idx != SchedModels.schedClasses().size(); ++Idx)
+    SchedClassNameOffsets[Idx] =
+        StrTab.GetOrAddStringOffset(SchedModels.getSchedClass(Idx).Name);
+
+  auto emitDesc = [&OS](const MCSchedClassDesc &Desc) {
+    OS << Desc.NumMicroOps << ", " << (Desc.BeginGroup ? "true" : "false")
+       << ", " << (Desc.EndGroup ? "true" : "false") << ", "
+       << (Desc.RetireOOO ? "true" : "false") << ", "
+       << format("%2d", Desc.WriteProcResIdx) << ", "
+       << Desc.NumWriteProcResEntries << ", "
+       << format("%2d", Desc.WriteLatencyIdx) << ", "
+       << Desc.NumWriteLatencyEntries << ", "
+       << format("%2d", Desc.ReadAdvanceIdx) << ", "
+       << Desc.NumReadAdvanceEntries;
+  };
 
-  if (SchedTables.UseSchedClassPool)
-    OS << "\n#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n";
+  if (!SchedTables.UseSchedClassPool) {
+    // Emit a SchedClass table for each processor.
+    for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
+      if (!Proc.hasInstrSchedModel())
+        continue;
 
-  // Emit a SchedClass table for each processor.
-  for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
-    if (!Proc.hasInstrSchedModel())
-      continue;
+      std::vector<MCSchedClassDesc> &SCTab =
+          SchedTables.ProcSchedClasses[1 + Idx];
 
-    std::vector<MCSchedClassDesc> &SCTab =
-        SchedTables.ProcSchedClasses[1 + Idx];
+      OS << "\n// {NumMicroOps, BeginGroup, EndGroup, RetireOOO,"
+         << " WriteProcResIdx,#, WriteLatencyIdx,#, ReadAdvanceIdx,#}\n";
+      OS << "static const llvm::MCSchedClassDesc " << Proc.ModelName
+         << "SchedClasses[] = {\n";
 
-    OS << "\n// {Name, NumMicroOps, BeginGroup, EndGroup, RetireOOO,"
-       << " WriteProcResIdx,#, WriteLatencyIdx,#, ReadAdvanceIdx,#}\n";
-    OS << "static const llvm::MCSchedClassDesc " << Proc.ModelName
-       << "SchedClasses[] = {\n";
-
-    // The first class is always invalid. We no way to distinguish it except by
-    // name and position.
-    assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" &&
-           "invalid class not first");
-    OS << "  {DBGFIELD(" << InvalidNameOff << ")  "
-       << MCSchedClassDesc::InvalidNumMicroOps
-       << ", false, false, false, 0, 0,  0, 0,  0, 0},\n";
-
-    for (unsigned SCIdx = 1, SCEnd = SCTab.size(); SCIdx != SCEnd; ++SCIdx) {
-      MCSchedClassDesc &MCDesc = SCTab[SCIdx];
-      const CodeGenSchedClass &SchedClass = SchedModels.getSchedClass(SCIdx);
-      unsigned NameOff = StrTab.GetOrAddStringOffset(SchedClass.Name);
-      OS << "  {DBGFIELD(/*" << SchedClass.Name << "*/ " << NameOff << ") ";
-      if (SchedClass.Name.size() < 18)
-        OS.indent(18 - SchedClass.Name.size());
-      OS << MCDesc.NumMicroOps << ", " << (MCDesc.BeginGroup ? "true" : "false")
-         << ", " << (MCDesc.EndGroup ? "true" : "false") << ", "
-         << (MCDesc.RetireOOO ? "true" : "false") << ", "
-         << format("%2d", MCDesc.WriteProcResIdx) << ", "
-         << MCDesc.NumWriteProcResEntries << ", "
-         << format("%2d", MCDesc.WriteLatencyIdx) << ", "
-         << MCDesc.NumWriteLatencyEntries << ", "
-         << format("%2d", MCDesc.ReadAdvanceIdx) << ", "
-         << MCDesc.NumReadAdvanceEntries << "}, // #" << SCIdx << '\n';
-    }
-    OS << "}; // " << Proc.ModelName << "SchedClasses\n";
-  }
+      OS << "  {" << MCSchedClassDesc::InvalidNumMicroOps
+         << ", false, false, false, 0, 0,  0, 0,  0, 0},\n";
 
-  if (SchedTables.UseSchedClassPool) {
-    OS << "#else\n";
+      for (unsigned SCIdx = 1, SCEnd = SCTab.size(); SCIdx != SCEnd; ++SCIdx) {
+        const CodeGenSchedClass &SchedClass = SchedModels.getSchedClass(SCIdx);
+        OS << "  {/*" << SchedClass.Name << "*/ ";
+        if (SchedClass.Name.size() < 18)
+          OS.indent(18 - SchedClass.Name.size());
+        emitDesc(SCTab[SCIdx]);
+        OS << "}, // #" << SCIdx << '\n';
+      }
+      OS << "}; // " << Proc.ModelName << "SchedClasses\n";
+    }
+  } else {
     OS << "\n// {NumMicroOps, BeginGroup, EndGroup, RetireOOO,"
        << " WriteProcResIdx,#, WriteLatencyIdx,#, ReadAdvanceIdx,#}\n";
     OS << "static const llvm::MCSchedClassDesc " << Target
        << "SchedClassPool[] = {\n";
     for (const auto &[PoolIdx, MCDesc] :
          enumerate(SchedTables.SchedClassPool)) {
-      OS << "  {DBGFIELD(0) " << MCDesc.NumMicroOps << ", "
-         << (MCDesc.BeginGroup ? "true" : "false") << ", "
-         << (MCDesc.EndGroup ? "true" : "false") << ", "
-         << (MCDesc.RetireOOO ? "true" : "false") << ", "
-         << format("%2d", MCDesc.WriteProcResIdx) << ", "
-         << MCDesc.NumWriteProcResEntries << ", "
-         << format("%2d", MCDesc.WriteLatencyIdx) << ", "
-         << MCDesc.NumWriteLatencyEntries << ", "
-         << format("%2d", MCDesc.ReadAdvanceIdx) << ", "
-         << MCDesc.NumReadAdvanceEntries << "}, // #" << PoolIdx << '\n';
+      OS << "  {";
+      emitDesc(MCDesc);
+      OS << "}, // #" << PoolIdx << '\n';
     }
     OS << "}; // " << Target << "SchedClassPool\n";
     OS << "static_assert(sizeof(" << Target << "SchedClassPool) / sizeof("
@@ -1540,10 +1534,19 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
          << "SchedClassIndices) / sizeof(" << Proc.ModelName
          << "SchedClassIndices[0]) == " << Indices.size() << ");\n";
     }
-    OS << "#endif\n";
   }
 
   StrTab.EmitStringTableDef(OS, Target + "SchedClassNames");
+  OS << "\nstatic const uint32_t " << Target << "SchedClassNameOffsets[] = {\n";
+  for (const auto [SCIdx, NameOff] : enumerate(SchedClassNameOffsets))
+    OS << "  " << NameOff << ", // #" << SCIdx << '\n';
+  OS << "}; // " << Target << "SchedClassNameOffsets\n";
+  OS << "static_assert(sizeof(" << Target
+     << "SchedClassNameOffsets[0]) == 4);\n";
+  OS << "static_assert(sizeof(" << Target << "SchedClassNameOffsets) / sizeof("
+     << Target
+     << "SchedClassNameOffsets[0]) == " << SchedClassNameOffsets.size()
+     << ");\n";
 }
 
 void SubtargetEmitter::emitProcessorModels(const SchedClassTables &SchedTables,
@@ -1595,11 +1598,7 @@ void SubtargetEmitter::emitProcessorModels(const SchedClassTables &SchedTables,
     else
       OS << "  nullptr,\n";
     if (PM.hasInstrSchedModel() && SchedTables.UseSchedClassPool)
-      OS << "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n"
-         << "  " << PM.ModelName << "SchedClasses" << ",\n"
-         << "#else\n"
-         << "  " << Target << "SchedClassPool,\n"
-         << "#endif\n";
+      OS << "  " << Target << "SchedClassPool,\n";
     else if (PM.hasInstrSchedModel())
       OS << "  " << PM.ModelName << "SchedClasses" << ",\n";
     else
@@ -1612,6 +1611,8 @@ void SubtargetEmitter::emitProcessorModels(const SchedClassTables &SchedTables,
          << " // No instruction-level machine model.\n";
     OS << "  DBGVAL_OR_NULLPTR(&" << Target
        << "SchedClassNames), // SchedClassNames\n";
+    OS << "  DBGVAL_OR_NULLPTR(" << Target
+       << "SchedClassNameOffsets), // SchedClassNameOffsets\n";
     if (PM.hasItineraries())
       OS << "  " << PM.ItinsDef->getName() << ",\n";
     else
@@ -1621,11 +1622,7 @@ void SubtargetEmitter::emitProcessorModels(const SchedClassTables &SchedTables,
     else
       OS << "  nullptr, // No extra processor descriptor\n";
     if (PM.hasInstrSchedModel() && SchedTables.UseSchedClassPool)
-      OS << "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n"
-         << "  nullptr,\n"
-         << "#else\n"
-         << "  " << PM.ModelName << "SchedClassIndices,\n"
-         << "#endif\n";
+      OS << "  " << PM.ModelName << "SchedClassIndices,\n";
     else
       OS << "  nullptr,\n";
     OS << "};\n";
@@ -1636,14 +1633,13 @@ void SubtargetEmitter::emitProcessorModels(const SchedClassTables &SchedTables,
 // EmitSchedModel - Emits all scheduling model tables, folding common patterns.
 //
 void SubtargetEmitter::emitSchedModel(raw_ostream &OS) {
-  OS << "#ifdef DBGFIELD\n"
-     << "#error \"<target>GenSubtargetInfo.inc requires a DBGFIELD macro\"\n"
+  OS << "#ifdef DBGVAL_OR_NULLPTR\n"
+     << "#error \"<target>GenSubtargetInfo.inc requires a DBGVAL_OR_NULLPTR "
+        "macro\"\n"
      << "#endif\n"
      << "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n"
-     << "#define DBGFIELD(x) x,\n"
      << "#define DBGVAL_OR_NULLPTR(x) x\n"
      << "#else\n"
-     << "#define DBGFIELD(x)\n"
      << "#define DBGVAL_OR_NULLPTR(x) nullptr\n"
      << "#endif\n";
 
@@ -1666,7 +1662,6 @@ void SubtargetEmitter::emitSchedModel(raw_ostream &OS) {
   // Emit the processor machine model
   emitProcessorModels(SchedTables, OS);
 
-  OS << "\n#undef DBGFIELD\n";
   OS << "\n#undef DBGVAL_OR_NULLPTR\n";
 }
 



More information about the llvm-commits mailing list