[llvm] [MC] Pack scheduling class entry counts (PR #202649)

David Zbarsky via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 07:01:20 PDT 2026


https://github.com/dzbarsky created https://github.com/llvm/llvm-project/pull/202649

Generated scheduling classes use at most 20 write-resource entries and 35 write-latency entries, so their 16-bit counts waste space in every MCSchedClassDesc. Keep the read-advance count at 16 bits because RISC-V models reach 504 entries, narrow the other two counts to 8 bits, and reorder the fields.

This reduces sizeof(MCSchedClassDesc) from 14 to 12 bytes in Release builds and from 20 to 16 bytes with debug fields. Fully stripped arm64 llvm-mca decreases from 36,535,760 to 35,990,864 bytes, saving 544,896 bytes (1.49%). The stripped all-tools multicall binary decreases from 143,567,584 to 143,039,208 bytes, saving 528,376 bytes (0.368%).

All 1,417 llvm-mca tests pass with three expected failures, both scheduling TableGen tests pass, and nine AArch64 SVE scheduling-model tests pass. AArch64 and X86 llc output and llvm-mca instruction tables are byte-identical.

The existing Znver4 instruction-table workload, repeated 50 times across 20 runs in both orderings, changed mean user CPU time from 0.270977 to 0.271476 seconds (+0.184%).

Work towards #202616

>From c4d54f05d1706429be2e425db0059e4ebd59f0fb Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Mon, 8 Jun 2026 15:38:18 -0400
Subject: [PATCH] [MC] Pack scheduling class entry counts

Generated scheduling classes use at most 20 write-resource entries and 35 write-latency entries, so their 16-bit counts waste space in every MCSchedClassDesc. Keep the read-advance count at 16 bits because RISC-V models reach 504 entries, narrow the other two counts to 8 bits, and reorder the fields.

This reduces sizeof(MCSchedClassDesc) from 14 to 12 bytes in Release builds and from 20 to 16 bytes with debug fields. Fully stripped arm64 llvm-mca decreases from 36,535,760 to 35,990,864 bytes, saving 544,896 bytes (1.49%). The stripped all-tools multicall binary decreases from 143,567,584 to 143,039,208 bytes, saving 528,376 bytes (0.368%).

All 1,417 llvm-mca tests pass with three expected failures, both scheduling TableGen tests pass, and nine AArch64 SVE scheduling-model tests pass. AArch64 and X86 llc output and llvm-mca instruction tables are byte-identical.

The existing Znver4 instruction-table workload, repeated 50 times across 20 runs in both orderings, changed mean user CPU time from 0.270977 to 0.271476 seconds (+0.184%).
---
 llvm/include/llvm/MC/MCSchedule.h             | 10 +++++--
 .../TableGen/CompressWriteLatencyEntry.td     |  6 ++--
 llvm/utils/TableGen/SubtargetEmitter.cpp      | 29 +++++++++++++++----
 3 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/llvm/include/llvm/MC/MCSchedule.h b/llvm/include/llvm/MC/MCSchedule.h
index 8c21dbd0ae385..736ea44fbe99f 100644
--- a/llvm/include/llvm/MC/MCSchedule.h
+++ b/llvm/include/llvm/MC/MCSchedule.h
@@ -138,11 +138,11 @@ struct MCSchedClassDesc {
   uint16_t EndGroup : 1;
   uint16_t RetireOOO : 1;
   uint16_t WriteProcResIdx; // First index into WriteProcResTable.
-  uint16_t NumWriteProcResEntries;
   uint16_t WriteLatencyIdx; // First index into WriteLatencyTable.
-  uint16_t NumWriteLatencyEntries;
   uint16_t ReadAdvanceIdx; // First index into ReadAdvanceTable.
   uint16_t NumReadAdvanceEntries;
+  uint8_t NumWriteProcResEntries;
+  uint8_t NumWriteLatencyEntries;
 
   bool isValid() const {
     return NumMicroOps != InvalidNumMicroOps;
@@ -152,6 +152,12 @@ struct MCSchedClassDesc {
   }
 };
 
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+static_assert(sizeof(MCSchedClassDesc) == 16);
+#else
+static_assert(sizeof(MCSchedClassDesc) == 12);
+#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
diff --git a/llvm/test/TableGen/CompressWriteLatencyEntry.td b/llvm/test/TableGen/CompressWriteLatencyEntry.td
index d6a9f0ac0dd76..c5626925362e0 100644
--- a/llvm/test/TableGen/CompressWriteLatencyEntry.td
+++ b/llvm/test/TableGen/CompressWriteLatencyEntry.td
@@ -34,9 +34,9 @@ def Read_D : SchedRead;
 
 // 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:  {DBGFIELD(/*Inst_A*/ {{[0-9]+}})             1, false, false, false,  0,  1,  0, 0, 0, 1}, // #1
+// CHECK-NEXT:  {DBGFIELD(/*Inst_B*/ {{[0-9]+}})             1, false, false, false,  0,  2,  0, 0, 0, 1}, // #2
+// CHECK-NEXT:  {DBGFIELD(/*Inst_C*/ {{[0-9]+}})             1, false, false, false,  0,  1,  1, 1, 0, 1}, // #3
 // CHECK-NEXT: }; // SchedModel_ASchedClasses
 
 let SchedModel = SchedModel_A in {
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index c3a9d69d6ec3e..82e037fcb3cb4 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -36,6 +36,7 @@
 #include <cassert>
 #include <cstdint>
 #include <iterator>
+#include <limits>
 #include <string>
 #include <vector>
 
@@ -45,6 +46,17 @@ using namespace llvm;
 
 namespace {
 
+static uint8_t checkedSchedClassEntryCount(
+    size_t Count, StringRef EntryKind, const CodeGenProcModel &ProcModel,
+    const CodeGenSchedClass &SchedClass) {
+  if (Count > std::numeric_limits<uint8_t>::max())
+    PrintFatalError(ProcModel.ModelDef->getLoc(),
+                    Twine("Too many ") + EntryKind +
+                        " entries in scheduling class '" + SchedClass.Name +
+                        "' for processor model '" + ProcModel.ModelName + "'");
+  return static_cast<uint8_t>(Count);
+}
+
 class SubtargetEmitter : TargetFeaturesEmitter {
   // Each processor has a SchedClassDesc table with an entry for each
   // SchedClass. The SchedClassDesc table indexes into a global write resource
@@ -1287,7 +1299,8 @@ void SubtargetEmitter::genSchedClassTables(const CodeGenProcModel &ProcModel,
     // WritePrecRes entries are sorted by ProcResIdx.
     llvm::sort(WriteProcResources, LessWriteProcResources());
 
-    SCDesc.NumWriteProcResEntries = WriteProcResources.size();
+    SCDesc.NumWriteProcResEntries = checkedSchedClassEntryCount(
+        WriteProcResources.size(), "write processor resource", ProcModel, SC);
     std::vector<MCWriteProcResEntry>::iterator WPRPos =
         std::search(SchedTables.WriteProcResources.begin(),
                     SchedTables.WriteProcResources.end(),
@@ -1300,7 +1313,8 @@ void SubtargetEmitter::genSchedClassTables(const CodeGenProcModel &ProcModel,
                                             WriteProcResources.end());
     }
     // Latency entries must remain in operand order.
-    SCDesc.NumWriteLatencyEntries = WriteLatencies.size();
+    SCDesc.NumWriteLatencyEntries = checkedSchedClassEntryCount(
+        WriteLatencies.size(), "write latency", ProcModel, SC);
     std::vector<MCWriteLatencyEntry>::iterator WLPos = std::search(
         SchedTables.WriteLatencies.begin(), SchedTables.WriteLatencies.end(),
         WriteLatencies.begin(), WriteLatencies.end());
@@ -1398,7 +1412,9 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
         SchedTables.ProcSchedClasses[1 + Idx];
 
     OS << "\n// {Name, NumMicroOps, BeginGroup, EndGroup, RetireOOO,"
-       << " WriteProcResIdx,#, WriteLatencyIdx,#, ReadAdvanceIdx,#}\n";
+       << " WriteProcResIdx, WriteLatencyIdx, ReadAdvanceIdx,"
+       << " NumReadAdvanceEntries, NumWriteProcResEntries,"
+       << " NumWriteLatencyEntries}\n";
     OS << "static const llvm::MCSchedClassDesc " << Proc.ModelName
        << "SchedClasses[] = {\n";
 
@@ -1421,11 +1437,12 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
          << ", " << (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';
+         << MCDesc.NumReadAdvanceEntries << ", "
+         << static_cast<unsigned>(MCDesc.NumWriteProcResEntries) << ", "
+         << static_cast<unsigned>(MCDesc.NumWriteLatencyEntries)
+         << "}, // #" << SCIdx << '\n';
     }
     OS << "}; // " << Proc.ModelName << "SchedClasses\n";
   }



More information about the llvm-commits mailing list