Skip to content

[MC] Use StringTable for MCSchedClassDesc names #137012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jayfoad
Copy link
Contributor

@jayfoad jayfoad commented Apr 23, 2025

Use StringTable for the name strings so that large arrays of
MCSchedClassDesc can live in read-only data. This only affects builds
with assertions enabled, otherwise these strings are compiled out.

This improves startup times for llc on my Ubuntu system where PIE is
enabled by default. In a Release+Asserts build it reduced the check-llvm
time from 159 to 112 seconds.

Use StringTable for the name strings so that large arrays of
MCSchedClassDesc can live in read-only data. This only affects builds
with assertions enabled, otherwise these strings are compiled out.

This improves startup times for llc on my Ubuntu system where PIE is
enabled by default. In a Release+Asserts build it reduced the check-llvm
time from 159 to 112 seconds.
@llvmbot
Copy link
Member

llvmbot commented Apr 23, 2025

@llvm/pr-subscribers-tools-llvm-mca
@llvm/pr-subscribers-tablegen

@llvm/pr-subscribers-mc

Author: Jay Foad (jayfoad)

Changes

Use StringTable for the name strings so that large arrays of
MCSchedClassDesc can live in read-only data. This only affects builds
with assertions enabled, otherwise these strings are compiled out.

This improves startup times for llc on my Ubuntu system where PIE is
enabled by default. In a Release+Asserts build it reduced the check-llvm
time from 159 to 112 seconds.


Full diff: https://github.com/llvm/llvm-project/pull/137012.diff

7 Files Affected:

  • (modified) llvm/include/llvm/MC/MCSchedule.h (+11-1)
  • (modified) llvm/lib/MC/MCSchedule.cpp (+19-18)
  • (modified) llvm/lib/MCA/InstrBuilder.cpp (+3-2)
  • (modified) llvm/test/TableGen/CompressWriteLatencyEntry.td (+4-4)
  • (modified) llvm/test/TableGen/InvalidMCSchedClassDesc.td (+6-6)
  • (modified) llvm/tools/llvm-exegesis/lib/Analysis.cpp (+6-4)
  • (modified) llvm/utils/TableGen/SubtargetEmitter.cpp (+12-5)
diff --git a/llvm/include/llvm/MC/MCSchedule.h b/llvm/include/llvm/MC/MCSchedule.h
index 57c8ebeee02a7..2d6d0d4199ee6 100644
--- a/llvm/include/llvm/MC/MCSchedule.h
+++ b/llvm/include/llvm/MC/MCSchedule.h
@@ -15,6 +15,7 @@
 #define LLVM_MC_MCSCHEDULE_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringTable.h"
 #include "llvm/MC/MCInstrDesc.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
@@ -123,7 +124,7 @@ struct MCSchedClassDesc {
   static const unsigned short VariantNumMicroOps = InvalidNumMicroOps - 1;
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-  const char* Name;
+  unsigned NameOffset;
 #endif
   uint16_t NumMicroOps : 13;
   uint16_t BeginGroup : 1;
@@ -321,6 +322,9 @@ struct MCSchedModel {
   unsigned ProcID;
   const MCProcResourceDesc *ProcResourceTable;
   const MCSchedClassDesc *SchedClassTable;
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+  StringTable SchedClassNameTable = "";
+#endif
   unsigned NumProcResourceKinds;
   unsigned NumSchedClasses;
   // Instruction itinerary tables used by InstrItineraryData.
@@ -408,6 +412,12 @@ struct MCSchedModel {
 
   /// Returns the default initialized model.
   static const MCSchedModel Default;
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+  StringRef getSchedClassName(const MCSchedClassDesc *SCDesc) const {
+    return SchedClassNameTable[SCDesc->NameOffset];
+  }
+#endif
 };
 
 // The first three are only template'd arguments so we can get away with leaving
diff --git a/llvm/lib/MC/MCSchedule.cpp b/llvm/lib/MC/MCSchedule.cpp
index 8aea08919f469..b1e1904d654c8 100644
--- a/llvm/lib/MC/MCSchedule.cpp
+++ b/llvm/lib/MC/MCSchedule.cpp
@@ -20,24 +20,25 @@
 
 using namespace llvm;
 
-static_assert(std::is_trivial_v<MCSchedModel>,
-              "MCSchedModel is required to be a trivial type");
-const MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
-                                            DefaultMicroOpBufferSize,
-                                            DefaultLoopMicroOpBufferSize,
-                                            DefaultLoadLatency,
-                                            DefaultHighLatency,
-                                            DefaultMispredictPenalty,
-                                            false,
-                                            true,
-                                            /*EnableIntervals=*/false,
-                                            0,
-                                            nullptr,
-                                            nullptr,
-                                            0,
-                                            0,
-                                            nullptr,
-                                            nullptr};
+constexpr MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
+                                                DefaultMicroOpBufferSize,
+                                                DefaultLoopMicroOpBufferSize,
+                                                DefaultLoadLatency,
+                                                DefaultHighLatency,
+                                                DefaultMispredictPenalty,
+                                                false,
+                                                true,
+                                                /*EnableIntervals=*/false,
+                                                0,
+                                                nullptr,
+                                                nullptr,
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+                                                "",
+#endif
+                                                0,
+                                                0,
+                                                nullptr,
+                                                nullptr};
 
 int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI,
                                       const MCSchedClassDesc &SCDesc) {
diff --git a/llvm/lib/MCA/InstrBuilder.cpp b/llvm/lib/MCA/InstrBuilder.cpp
index 2bac99b6309af..d80eb28115b47 100644
--- a/llvm/lib/MCA/InstrBuilder.cpp
+++ b/llvm/lib/MCA/InstrBuilder.cpp
@@ -75,8 +75,9 @@ static void initializeUsedResources(InstrDesc &ID,
       WithColor::warning()
           << "Ignoring invalid write of zero cycles on processor resource "
           << PR.Name << "\n";
-      WithColor::note() << "found in scheduling class " << SCDesc.Name
-                        << " (write index #" << I << ")\n";
+      WithColor::note() << "found in scheduling class "
+                        << SM.getSchedClassName(&SCDesc) << " (write index #"
+                        << I << ")\n";
 #endif
       continue;
     }
diff --git a/llvm/test/TableGen/CompressWriteLatencyEntry.td b/llvm/test/TableGen/CompressWriteLatencyEntry.td
index 88273e8858448..a6a942882ca7f 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("InvalidSchedClass")  8191, false, false, false, 0, 0,  0, 0,  0, 0},
-// CHECK-NEXT:  {DBGFIELD("Inst_A")             1, false, false, false,  0, 0,  1, 1,  0, 0}, // #1
-// CHECK-NEXT:  {DBGFIELD("Inst_B")             1, false, false, false,  0, 0,  2, 1,  0, 0}, // #2
-// CHECK-NEXT:  {DBGFIELD("Inst_C")             1, false, false, false,  0, 0,  1, 1,  1, 1}, // #3
+// CHECK-NEXT:  {DBGFIELD(1 /* InvalidSchedClass */) 8191, false, false, false, 0, 0,  0, 0,  0, 0},
+// CHECK-NEXT:  {DBGFIELD(19 /* Inst_A */) 1, false, false, false,  0, 0,  1, 1,  0, 0}, // #1
+// CHECK-NEXT:  {DBGFIELD(26 /* Inst_B */) 1, false, false, false,  0, 0,  2, 1,  0, 0}, // #2
+// CHECK-NEXT:  {DBGFIELD(33 /* 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 de5392237a84c..5e12d088088ac 100644
--- a/llvm/test/TableGen/InvalidMCSchedClassDesc.td
+++ b/llvm/test/TableGen/InvalidMCSchedClassDesc.td
@@ -18,8 +18,8 @@ let CompleteModel = 0 in {
 
 // Inst_B didn't have the resoures, and it is invalid.
 // CHECK: SchedModel_ASchedClasses[] = {
-// CHECK: {DBGFIELD("Inst_A")             1
-// CHECK-NEXT: {DBGFIELD("Inst_B")             8191
+// CHECK: {DBGFIELD(19 /* Inst_A */) 1
+// CHECK-NEXT: {DBGFIELD(26 /* Inst_B */) 8191
 let SchedModel = SchedModel_A in {
   def Write_A : SchedWriteRes<[]>;
   def : InstRW<[Write_A], (instrs Inst_A)>;
@@ -27,16 +27,16 @@ let SchedModel = SchedModel_A in {
 
 // Inst_A didn't have the resoures, and it is invalid.
 // CHECK: SchedModel_BSchedClasses[] = {
-// CHECK: {DBGFIELD("Inst_A")             8191
-// CHECK-NEXT: {DBGFIELD("Inst_B")             1 
+// CHECK: {DBGFIELD(19 /* Inst_A */) 8191
+// CHECK-NEXT: {DBGFIELD(26 /* Inst_B */) 1
 let SchedModel = SchedModel_B in {
   def Write_B: SchedWriteRes<[]>; 
   def : InstRW<[Write_B], (instrs Inst_B)>;
 }
 
 // CHECK: SchedModel_CSchedClasses[] = {
-// CHECK: {DBGFIELD("Inst_A")             1
-// CHECK-NEXT: {DBGFIELD("Inst_B")             1
+// CHECK: {DBGFIELD(19 /* Inst_A */) 1
+// CHECK-NEXT: {DBGFIELD(26 /* Inst_B */) 1
 let SchedModel = SchedModel_C in {
   def Write_C: SchedWriteRes<[]>; 
   def : InstRW<[Write_C], (instrs Inst_A, Inst_B)>;
diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.cpp b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
index be10c32cf08d5..496f23b016a7e 100644
--- a/llvm/tools/llvm-exegesis/lib/Analysis.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
@@ -137,9 +137,9 @@ void Analysis::printInstructionRowCsv(const size_t PointId,
   std::tie(SchedClassId, std::ignore) = ResolvedSchedClass::resolveSchedClassId(
       State_.getSubtargetInfo(), State_.getInstrInfo(), MCI);
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-  const MCSchedClassDesc *const SCDesc =
-      State_.getSubtargetInfo().getSchedModel().getSchedClassDesc(SchedClassId);
-  writeEscaped<kEscapeCsv>(OS, SCDesc->Name);
+  const MCSchedModel &SM = State_.getSubtargetInfo().getSchedModel();
+  const MCSchedClassDesc *const SCDesc = SM.getSchedClassDesc(SchedClassId);
+  writeEscaped<kEscapeCsv>(OS, SM.getSchedClassName(SCDesc));
 #else
   OS << SchedClassId;
 #endif
@@ -563,7 +563,9 @@ Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
     OS << "<div class=\"inconsistency\"><p>Sched Class <span "
           "class=\"sched-class-name\">";
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-    writeEscaped<kEscapeHtml>(OS, RSCAndPoints.RSC.SCDesc->Name);
+    const auto &SM = SI.getSchedModel();
+    writeEscaped<kEscapeHtml>(OS,
+                              SM.getSchedClassName(RSCAndPoints.RSC.SCDesc));
 #else
     OS << RSCAndPoints.RSC.SchedClassId;
 #endif
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index 8d2fc99f84670..a454575e609e2 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -29,6 +29,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Error.h"
 #include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringToOffsetTable.h"
 #include "llvm/TableGen/TableGenBackend.h"
 #include "llvm/TargetParser/SubtargetFeature.h"
 #include <algorithm>
@@ -1430,6 +1431,7 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
   OS << "}; // " << Target << "ReadAdvanceTable\n";
 
   // Emit a SchedClass table for each processor.
+  StringToOffsetTable NameTable;
   for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
     if (!Proc.hasInstrSchedModel())
       continue;
@@ -1446,14 +1448,17 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
     // name and position.
     assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" &&
            "invalid class not first");
-    OS << "  {DBGFIELD(\"InvalidSchedClass\")  "
+    unsigned NameOffset = NameTable.GetOrAddStringOffset("InvalidSchedClass");
+    OS << "  {DBGFIELD(" << NameOffset << " /* InvalidSchedClass */)  "
        << 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);
-      OS << "  {DBGFIELD(\"" << SchedClass.Name << "\") ";
+      NameOffset = NameTable.GetOrAddStringOffset(SchedClass.Name);
+      OS << "  {DBGFIELD(" << NameOffset << " /* " << SchedClass.Name
+         << " */) ";
       if (SchedClass.Name.size() < 18)
         OS.indent(18 - SchedClass.Name.size());
       OS << MCDesc.NumMicroOps << ", " << (MCDesc.BeginGroup ? "true" : "false")
@@ -1467,6 +1472,7 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
          << MCDesc.NumReadAdvanceEntries << "}, // #" << SCIdx << '\n';
     }
     OS << "}; // " << Proc.ModelName << "SchedClasses\n";
+    NameTable.EmitStringTableDef(OS, Proc.ModelName + "SchedClassNames");
   }
 }
 
@@ -1516,10 +1522,11 @@ void SubtargetEmitter::emitProcessorModels(raw_ostream &OS) {
     if (PM.hasInstrSchedModel())
       OS << "  " << PM.ModelName << "ProcResources" << ",\n"
          << "  " << PM.ModelName << "SchedClasses" << ",\n"
+         << "  DBGFIELD(" << PM.ModelName << "SchedClassNames" << ")\n"
          << "  " << PM.ProcResourceDefs.size() + 1 << ",\n"
          << "  " << SchedModels.schedClasses().size() << ",\n";
     else
-      OS << "  nullptr, nullptr, 0, 0,"
+      OS << "  nullptr, nullptr, DBGFIELD(\"\") 0, 0,"
          << " // No instruction-level machine model.\n";
     if (PM.hasItineraries())
       OS << "  " << PM.ItinsDef->getName() << ",\n";
@@ -1561,10 +1568,10 @@ void SubtargetEmitter::emitSchedModel(raw_ostream &OS) {
   }
   emitSchedClassTables(SchedTables, OS);
 
-  OS << "\n#undef DBGFIELD\n";
-
   // Emit the processor machine model
   emitProcessorModels(OS);
+
+  OS << "\n#undef DBGFIELD\n";
 }
 
 static void emitPredicateProlog(const RecordKeeper &Records, raw_ostream &OS) {

@llvmbot
Copy link
Member

llvmbot commented Apr 23, 2025

@llvm/pr-subscribers-tools-llvm-exegesis

Author: Jay Foad (jayfoad)

Changes

Use StringTable for the name strings so that large arrays of
MCSchedClassDesc can live in read-only data. This only affects builds
with assertions enabled, otherwise these strings are compiled out.

This improves startup times for llc on my Ubuntu system where PIE is
enabled by default. In a Release+Asserts build it reduced the check-llvm
time from 159 to 112 seconds.


Full diff: https://github.com/llvm/llvm-project/pull/137012.diff

7 Files Affected:

  • (modified) llvm/include/llvm/MC/MCSchedule.h (+11-1)
  • (modified) llvm/lib/MC/MCSchedule.cpp (+19-18)
  • (modified) llvm/lib/MCA/InstrBuilder.cpp (+3-2)
  • (modified) llvm/test/TableGen/CompressWriteLatencyEntry.td (+4-4)
  • (modified) llvm/test/TableGen/InvalidMCSchedClassDesc.td (+6-6)
  • (modified) llvm/tools/llvm-exegesis/lib/Analysis.cpp (+6-4)
  • (modified) llvm/utils/TableGen/SubtargetEmitter.cpp (+12-5)
diff --git a/llvm/include/llvm/MC/MCSchedule.h b/llvm/include/llvm/MC/MCSchedule.h
index 57c8ebeee02a7..2d6d0d4199ee6 100644
--- a/llvm/include/llvm/MC/MCSchedule.h
+++ b/llvm/include/llvm/MC/MCSchedule.h
@@ -15,6 +15,7 @@
 #define LLVM_MC_MCSCHEDULE_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringTable.h"
 #include "llvm/MC/MCInstrDesc.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
@@ -123,7 +124,7 @@ struct MCSchedClassDesc {
   static const unsigned short VariantNumMicroOps = InvalidNumMicroOps - 1;
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-  const char* Name;
+  unsigned NameOffset;
 #endif
   uint16_t NumMicroOps : 13;
   uint16_t BeginGroup : 1;
@@ -321,6 +322,9 @@ struct MCSchedModel {
   unsigned ProcID;
   const MCProcResourceDesc *ProcResourceTable;
   const MCSchedClassDesc *SchedClassTable;
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+  StringTable SchedClassNameTable = "";
+#endif
   unsigned NumProcResourceKinds;
   unsigned NumSchedClasses;
   // Instruction itinerary tables used by InstrItineraryData.
@@ -408,6 +412,12 @@ struct MCSchedModel {
 
   /// Returns the default initialized model.
   static const MCSchedModel Default;
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+  StringRef getSchedClassName(const MCSchedClassDesc *SCDesc) const {
+    return SchedClassNameTable[SCDesc->NameOffset];
+  }
+#endif
 };
 
 // The first three are only template'd arguments so we can get away with leaving
diff --git a/llvm/lib/MC/MCSchedule.cpp b/llvm/lib/MC/MCSchedule.cpp
index 8aea08919f469..b1e1904d654c8 100644
--- a/llvm/lib/MC/MCSchedule.cpp
+++ b/llvm/lib/MC/MCSchedule.cpp
@@ -20,24 +20,25 @@
 
 using namespace llvm;
 
-static_assert(std::is_trivial_v<MCSchedModel>,
-              "MCSchedModel is required to be a trivial type");
-const MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
-                                            DefaultMicroOpBufferSize,
-                                            DefaultLoopMicroOpBufferSize,
-                                            DefaultLoadLatency,
-                                            DefaultHighLatency,
-                                            DefaultMispredictPenalty,
-                                            false,
-                                            true,
-                                            /*EnableIntervals=*/false,
-                                            0,
-                                            nullptr,
-                                            nullptr,
-                                            0,
-                                            0,
-                                            nullptr,
-                                            nullptr};
+constexpr MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
+                                                DefaultMicroOpBufferSize,
+                                                DefaultLoopMicroOpBufferSize,
+                                                DefaultLoadLatency,
+                                                DefaultHighLatency,
+                                                DefaultMispredictPenalty,
+                                                false,
+                                                true,
+                                                /*EnableIntervals=*/false,
+                                                0,
+                                                nullptr,
+                                                nullptr,
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+                                                "",
+#endif
+                                                0,
+                                                0,
+                                                nullptr,
+                                                nullptr};
 
 int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI,
                                       const MCSchedClassDesc &SCDesc) {
diff --git a/llvm/lib/MCA/InstrBuilder.cpp b/llvm/lib/MCA/InstrBuilder.cpp
index 2bac99b6309af..d80eb28115b47 100644
--- a/llvm/lib/MCA/InstrBuilder.cpp
+++ b/llvm/lib/MCA/InstrBuilder.cpp
@@ -75,8 +75,9 @@ static void initializeUsedResources(InstrDesc &ID,
       WithColor::warning()
           << "Ignoring invalid write of zero cycles on processor resource "
           << PR.Name << "\n";
-      WithColor::note() << "found in scheduling class " << SCDesc.Name
-                        << " (write index #" << I << ")\n";
+      WithColor::note() << "found in scheduling class "
+                        << SM.getSchedClassName(&SCDesc) << " (write index #"
+                        << I << ")\n";
 #endif
       continue;
     }
diff --git a/llvm/test/TableGen/CompressWriteLatencyEntry.td b/llvm/test/TableGen/CompressWriteLatencyEntry.td
index 88273e8858448..a6a942882ca7f 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("InvalidSchedClass")  8191, false, false, false, 0, 0,  0, 0,  0, 0},
-// CHECK-NEXT:  {DBGFIELD("Inst_A")             1, false, false, false,  0, 0,  1, 1,  0, 0}, // #1
-// CHECK-NEXT:  {DBGFIELD("Inst_B")             1, false, false, false,  0, 0,  2, 1,  0, 0}, // #2
-// CHECK-NEXT:  {DBGFIELD("Inst_C")             1, false, false, false,  0, 0,  1, 1,  1, 1}, // #3
+// CHECK-NEXT:  {DBGFIELD(1 /* InvalidSchedClass */) 8191, false, false, false, 0, 0,  0, 0,  0, 0},
+// CHECK-NEXT:  {DBGFIELD(19 /* Inst_A */) 1, false, false, false,  0, 0,  1, 1,  0, 0}, // #1
+// CHECK-NEXT:  {DBGFIELD(26 /* Inst_B */) 1, false, false, false,  0, 0,  2, 1,  0, 0}, // #2
+// CHECK-NEXT:  {DBGFIELD(33 /* 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 de5392237a84c..5e12d088088ac 100644
--- a/llvm/test/TableGen/InvalidMCSchedClassDesc.td
+++ b/llvm/test/TableGen/InvalidMCSchedClassDesc.td
@@ -18,8 +18,8 @@ let CompleteModel = 0 in {
 
 // Inst_B didn't have the resoures, and it is invalid.
 // CHECK: SchedModel_ASchedClasses[] = {
-// CHECK: {DBGFIELD("Inst_A")             1
-// CHECK-NEXT: {DBGFIELD("Inst_B")             8191
+// CHECK: {DBGFIELD(19 /* Inst_A */) 1
+// CHECK-NEXT: {DBGFIELD(26 /* Inst_B */) 8191
 let SchedModel = SchedModel_A in {
   def Write_A : SchedWriteRes<[]>;
   def : InstRW<[Write_A], (instrs Inst_A)>;
@@ -27,16 +27,16 @@ let SchedModel = SchedModel_A in {
 
 // Inst_A didn't have the resoures, and it is invalid.
 // CHECK: SchedModel_BSchedClasses[] = {
-// CHECK: {DBGFIELD("Inst_A")             8191
-// CHECK-NEXT: {DBGFIELD("Inst_B")             1 
+// CHECK: {DBGFIELD(19 /* Inst_A */) 8191
+// CHECK-NEXT: {DBGFIELD(26 /* Inst_B */) 1
 let SchedModel = SchedModel_B in {
   def Write_B: SchedWriteRes<[]>; 
   def : InstRW<[Write_B], (instrs Inst_B)>;
 }
 
 // CHECK: SchedModel_CSchedClasses[] = {
-// CHECK: {DBGFIELD("Inst_A")             1
-// CHECK-NEXT: {DBGFIELD("Inst_B")             1
+// CHECK: {DBGFIELD(19 /* Inst_A */) 1
+// CHECK-NEXT: {DBGFIELD(26 /* Inst_B */) 1
 let SchedModel = SchedModel_C in {
   def Write_C: SchedWriteRes<[]>; 
   def : InstRW<[Write_C], (instrs Inst_A, Inst_B)>;
diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.cpp b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
index be10c32cf08d5..496f23b016a7e 100644
--- a/llvm/tools/llvm-exegesis/lib/Analysis.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
@@ -137,9 +137,9 @@ void Analysis::printInstructionRowCsv(const size_t PointId,
   std::tie(SchedClassId, std::ignore) = ResolvedSchedClass::resolveSchedClassId(
       State_.getSubtargetInfo(), State_.getInstrInfo(), MCI);
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-  const MCSchedClassDesc *const SCDesc =
-      State_.getSubtargetInfo().getSchedModel().getSchedClassDesc(SchedClassId);
-  writeEscaped<kEscapeCsv>(OS, SCDesc->Name);
+  const MCSchedModel &SM = State_.getSubtargetInfo().getSchedModel();
+  const MCSchedClassDesc *const SCDesc = SM.getSchedClassDesc(SchedClassId);
+  writeEscaped<kEscapeCsv>(OS, SM.getSchedClassName(SCDesc));
 #else
   OS << SchedClassId;
 #endif
@@ -563,7 +563,9 @@ Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
     OS << "<div class=\"inconsistency\"><p>Sched Class <span "
           "class=\"sched-class-name\">";
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-    writeEscaped<kEscapeHtml>(OS, RSCAndPoints.RSC.SCDesc->Name);
+    const auto &SM = SI.getSchedModel();
+    writeEscaped<kEscapeHtml>(OS,
+                              SM.getSchedClassName(RSCAndPoints.RSC.SCDesc));
 #else
     OS << RSCAndPoints.RSC.SchedClassId;
 #endif
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index 8d2fc99f84670..a454575e609e2 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -29,6 +29,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Error.h"
 #include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringToOffsetTable.h"
 #include "llvm/TableGen/TableGenBackend.h"
 #include "llvm/TargetParser/SubtargetFeature.h"
 #include <algorithm>
@@ -1430,6 +1431,7 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
   OS << "}; // " << Target << "ReadAdvanceTable\n";
 
   // Emit a SchedClass table for each processor.
+  StringToOffsetTable NameTable;
   for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
     if (!Proc.hasInstrSchedModel())
       continue;
@@ -1446,14 +1448,17 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
     // name and position.
     assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" &&
            "invalid class not first");
-    OS << "  {DBGFIELD(\"InvalidSchedClass\")  "
+    unsigned NameOffset = NameTable.GetOrAddStringOffset("InvalidSchedClass");
+    OS << "  {DBGFIELD(" << NameOffset << " /* InvalidSchedClass */)  "
        << 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);
-      OS << "  {DBGFIELD(\"" << SchedClass.Name << "\") ";
+      NameOffset = NameTable.GetOrAddStringOffset(SchedClass.Name);
+      OS << "  {DBGFIELD(" << NameOffset << " /* " << SchedClass.Name
+         << " */) ";
       if (SchedClass.Name.size() < 18)
         OS.indent(18 - SchedClass.Name.size());
       OS << MCDesc.NumMicroOps << ", " << (MCDesc.BeginGroup ? "true" : "false")
@@ -1467,6 +1472,7 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
          << MCDesc.NumReadAdvanceEntries << "}, // #" << SCIdx << '\n';
     }
     OS << "}; // " << Proc.ModelName << "SchedClasses\n";
+    NameTable.EmitStringTableDef(OS, Proc.ModelName + "SchedClassNames");
   }
 }
 
@@ -1516,10 +1522,11 @@ void SubtargetEmitter::emitProcessorModels(raw_ostream &OS) {
     if (PM.hasInstrSchedModel())
       OS << "  " << PM.ModelName << "ProcResources" << ",\n"
          << "  " << PM.ModelName << "SchedClasses" << ",\n"
+         << "  DBGFIELD(" << PM.ModelName << "SchedClassNames" << ")\n"
          << "  " << PM.ProcResourceDefs.size() + 1 << ",\n"
          << "  " << SchedModels.schedClasses().size() << ",\n";
     else
-      OS << "  nullptr, nullptr, 0, 0,"
+      OS << "  nullptr, nullptr, DBGFIELD(\"\") 0, 0,"
          << " // No instruction-level machine model.\n";
     if (PM.hasItineraries())
       OS << "  " << PM.ItinsDef->getName() << ",\n";
@@ -1561,10 +1568,10 @@ void SubtargetEmitter::emitSchedModel(raw_ostream &OS) {
   }
   emitSchedClassTables(SchedTables, OS);
 
-  OS << "\n#undef DBGFIELD\n";
-
   // Emit the processor machine model
   emitProcessorModels(OS);
+
+  OS << "\n#undef DBGFIELD\n";
 }
 
 static void emitPredicateProlog(const RecordKeeper &Records, raw_ostream &OS) {

Copy link
Member

@mshockwave mshockwave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it'll be a good idea to share a single StringTable for all the sched models under the same target, e.g. A single X86SchedClassNames instead of Znver3SchedClassNames, SkylakeSchedClassNames etc. Given the fact that they basically share the same set of sched class names?

@jayfoad
Copy link
Contributor Author

jayfoad commented Apr 24, 2025

Do you think it'll be a good idea to share a single StringTable for all the sched models under the same target

Yes, good idea, done. It shrinks the llc binary significantly, in my case from ~162 MB to 150 MB.

@jurahul
Copy link
Contributor

jurahul commented Apr 24, 2025

Looks like the build failure is related.

LLVMNVPTXCodeGen.lib(NVPTXSubtarget.cpp.obj) : error LNK2019: unresolved external symbol "class llvm::StringTable const llvm::NVPTXNameTable"

@@ -78,10 +79,10 @@ void StringToOffsetTable::EmitStringTableDef(raw_ostream &OS, const Twine &Name,
#pragma GCC diagnostic pop
#endif

{0}static constexpr llvm::StringTable {1} =
{0}{2} constexpr llvm::StringTable {1} =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we renumber 1 and 2 and swap the args to formatv()? Just that its easier to follow if they are in increasing order to the extent possible,

@@ -93,6 +94,10 @@ class MCSubtargetInfo {
FeatureBitset FeatureBits; // Feature bits for current CPU + FS
std::string FeatureString; // Feature string

// General purpose name table, currently only used to store MCSchedClassDesc
// names when assertions are enabled.
StringTable NameTable;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we guard this def as well with if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)? And in the constructor, assert that the NT passed is empty() in non-assert builds optionally?

NameTable.EmitStringTableDef(OS, Target + "NameTable", /*Indent=*/"",
"extern");
OS << "\n#else\n";
OS << "\nextern constexpr llvm::StringTable " << Target
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extern constexpr? I'm surprised this even compiles.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that could also be the cause of the link failure seen above

OS << "\n#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n";
NameTable.EmitStringTableDef(OS, Target + "NameTable", /*Indent=*/"",
"extern");
OS << "\n#else\n";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++ raw string might make this easier to read

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants