[llvm] [TableGen] Pool duplicate scheduling class descriptors (PR #202659)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 10 10:54:25 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: David Zbarsky (dzbarsky)
<details>
<summary>Changes</summary>
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)
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.
Work towards #<!-- -->202616
AI tool disclosure: Co-authored with OpenAI Codex.
---
Full diff: https://github.com/llvm/llvm-project/pull/202659.diff
4 Files Affected:
- (modified) llvm/include/llvm/MC/MCSchedule.h (+9)
- (modified) llvm/lib/MC/MCSchedule.cpp (+1)
- (added) llvm/test/TableGen/SchedClassTablePooling.td (+74)
- (modified) llvm/utils/TableGen/SubtargetEmitter.cpp (+140-7)
``````````diff
diff --git a/llvm/include/llvm/MC/MCSchedule.h b/llvm/include/llvm/MC/MCSchedule.h
index 8c21dbd0ae385..2b623128ef547 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 c3a9d69d6ec3e..a222286dc824d 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);
@@ -1332,6 +1351,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) {
@@ -1389,6 +1455,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())
@@ -1430,10 +1499,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.
@@ -1477,12 +1589,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";
@@ -1493,7 +1617,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";
}
}
@@ -1526,10 +1658,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";
``````````
</details>
https://github.com/llvm/llvm-project/pull/202659
More information about the llvm-commits
mailing list