[llvm-branch-commits] [llvm] TableGen: Use a compact table for CPU aliases (PR #211952)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jul 24 23:26:03 PDT 2026


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/211952

>From 38f619ca4ed84729524ed3993dd1a6c4bc70ff15 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 24 Jul 2026 18:44:04 +0200
Subject: [PATCH 1/2] TableGen: Use a compact table for CPU aliases

Previously each ProcessorAlias was emitted as a full SubtargetSubTypeKV
entry in the processor subtype table, duplicating the canonical
processor's feature masks and scheduling model index. At 104 bytes per
entry: AArch64's 18 aliases added ~1.8KB, and X86 will add more as aliases
are introduced.

Emit aliases into a separate SubtargetSubTypeAliasKV table instead. Each
alias is just a name string offset plus the index of the canonical
processor it resolves to.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
---
 .../llvm/CodeGen/TargetSubtargetInfo.h        |   5 +-
 llvm/include/llvm/MC/MCSubtargetInfo.h        |  35 ++++-
 llvm/lib/CodeGen/TargetSubtargetInfo.cpp      |  12 +-
 llvm/lib/MC/MCSubtargetInfo.cpp               |  30 ++--
 llvm/test/TableGen/ProcessorAlias.td          |  29 ++--
 llvm/unittests/CodeGen/MFCommon.inc           |   6 +-
 llvm/unittests/CodeGen/MachineInstrTest.cpp   |   4 +-
 llvm/unittests/CodeGen/MachineOperandTest.cpp |   2 +-
 .../Target/AArch64/AArch64InstPrinterTest.cpp |   2 +-
 llvm/utils/TableGen/SubtargetEmitter.cpp      | 138 ++++++++++++------
 10 files changed, 174 insertions(+), 89 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
index db73a9675b71d..a50f7f804b03d 100644
--- a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
@@ -72,8 +72,9 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo {
   TargetSubtargetInfo(const Triple &TT, StringRef CPU, StringRef TuneCPU,
                       StringRef FS, StringTable PN,
                       ArrayRef<SubtargetFeatureKV> PF,
-                      ArrayRef<SubtargetSubTypeKV> PD, const MCSchedModel *PSM,
-                      const MCWriteProcResEntry *WPR,
+                      ArrayRef<SubtargetSubTypeKV> PD,
+                      ArrayRef<SubtargetSubTypeAliasKV> PA,
+                      const MCSchedModel *PSM, const MCWriteProcResEntry *WPR,
                       const MCWriteLatencyEntry *WL,
                       const MCReadAdvanceEntry *RA, const InstrStage *IS,
                       const unsigned *OC, const unsigned *FP);
diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h
index e8de24cee5831..ce12169a1a891 100644
--- a/llvm/include/llvm/MC/MCSubtargetInfo.h
+++ b/llvm/include/llvm/MC/MCSubtargetInfo.h
@@ -22,6 +22,7 @@
 #include "llvm/Support/Compiler.h"
 #include "llvm/TargetParser/SubtargetFeature.h"
 #include "llvm/TargetParser/Triple.h"
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <optional>
@@ -104,9 +105,28 @@ struct SubtargetSubTypeKV {
   }
 };
 
-template <size_t NumSubTypes, size_t SubTypeStrTabSize>
+/// Maps a CPU alias name to the index of the processor it resolves to.
+struct SubtargetSubTypeAliasKV {
+  uint16_t KeyStrOff;  ///< Relative offset to the alias name.
+  uint16_t SubTypeIdx; ///< Index into the SubtargetSubTypeKV array.
+
+  constexpr SubtargetSubTypeAliasKV(uint16_t KeyStrOff, uint16_t SubTypeIdx)
+      : KeyStrOff(KeyStrOff), SubTypeIdx(SubTypeIdx) {}
+
+  SubtargetSubTypeAliasKV(const SubtargetSubTypeAliasKV &) = delete;
+  SubtargetSubTypeAliasKV &operator=(const SubtargetSubTypeAliasKV &) = delete;
+
+  const char *key() const {
+    return reinterpret_cast<const char *>(this) + KeyStrOff;
+  }
+
+  bool operator<(StringRef S) const { return StringRef(key()) < S; }
+};
+
+template <size_t NumSubTypes, size_t NumAliases, size_t SubTypeStrTabSize>
 struct SubtargetSubTypeKVStorage {
   SubtargetSubTypeKV SubTypes[NumSubTypes];
+  std::array<SubtargetSubTypeAliasKV, NumAliases> Aliases;
   char Strings[SubTypeStrTabSize];
 };
 
@@ -121,6 +141,7 @@ class LLVM_ABI MCSubtargetInfo {
   StringTable ProcNames; // Processor list, including aliases
   ArrayRef<SubtargetFeatureKV> ProcFeatures;  // Processor feature list
   ArrayRef<SubtargetSubTypeKV> ProcDesc;  // Processor descriptions
+  ArrayRef<SubtargetSubTypeAliasKV> ProcAliases; // CPU alias -> processor map
   const MCSchedModel *ProcSchedModels;    ///< Processor scheduling models.
 
   // Scheduler machine model
@@ -139,7 +160,8 @@ class LLVM_ABI MCSubtargetInfo {
   MCSubtargetInfo(const MCSubtargetInfo &) = default;
   MCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef TuneCPU,
                   StringRef FS, StringTable PN, ArrayRef<SubtargetFeatureKV> PF,
-                  ArrayRef<SubtargetSubTypeKV> PD, const MCSchedModel *PSM,
+                  ArrayRef<SubtargetSubTypeKV> PD,
+                  ArrayRef<SubtargetSubTypeAliasKV> PA, const MCSchedModel *PSM,
                   const MCWriteProcResEntry *WPR, const MCWriteLatencyEntry *WL,
                   const MCReadAdvanceEntry *RA, const InstrStage *IS,
                   const unsigned *OC, const unsigned *FP);
@@ -273,11 +295,12 @@ class LLVM_ABI MCSubtargetInfo {
     return 0;
   }
 
+  /// Look up the processor entry for a CPU name, resolving aliases. Returns
+  /// nullptr if the name matches neither a processor nor an alias.
+  const SubtargetSubTypeKV *resolveCPU(StringRef CPU) const;
+
   /// Check whether the CPU string is valid.
-  bool isCPUStringValid(StringRef CPU) const {
-    auto Found = llvm::lower_bound(ProcDesc, CPU);
-    return Found != ProcDesc.end() && StringRef(Found->key()) == CPU;
-  }
+  bool isCPUStringValid(StringRef CPU) const { return resolveCPU(CPU); }
 
   /// Return processor descriptions.
   ArrayRef<SubtargetSubTypeKV> getAllProcessorDescriptions() const {
diff --git a/llvm/lib/CodeGen/TargetSubtargetInfo.cpp b/llvm/lib/CodeGen/TargetSubtargetInfo.cpp
index 727eff7ddce58..b5d51525710c9 100644
--- a/llvm/lib/CodeGen/TargetSubtargetInfo.cpp
+++ b/llvm/lib/CodeGen/TargetSubtargetInfo.cpp
@@ -18,12 +18,12 @@ using namespace llvm;
 TargetSubtargetInfo::TargetSubtargetInfo(
     const Triple &TT, StringRef CPU, StringRef TuneCPU, StringRef FS,
     StringTable PN, ArrayRef<SubtargetFeatureKV> PF,
-    ArrayRef<SubtargetSubTypeKV> PD, const MCSchedModel *PSM,
-    const MCWriteProcResEntry *WPR, const MCWriteLatencyEntry *WL,
-    const MCReadAdvanceEntry *RA, const InstrStage *IS, const unsigned *OC,
-    const unsigned *FP)
-    : MCSubtargetInfo(TT, CPU, TuneCPU, FS, PN, PF, PD, PSM, WPR, WL, RA, IS,
-                      OC, FP) {}
+    ArrayRef<SubtargetSubTypeKV> PD, ArrayRef<SubtargetSubTypeAliasKV> PA,
+    const MCSchedModel *PSM, const MCWriteProcResEntry *WPR,
+    const MCWriteLatencyEntry *WL, const MCReadAdvanceEntry *RA,
+    const InstrStage *IS, const unsigned *OC, const unsigned *FP)
+    : MCSubtargetInfo(TT, CPU, TuneCPU, FS, PN, PF, PD, PA, PSM, WPR, WL, RA,
+                      IS, OC, FP) {}
 
 TargetSubtargetInfo::~TargetSubtargetInfo() = default;
 
diff --git a/llvm/lib/MC/MCSubtargetInfo.cpp b/llvm/lib/MC/MCSubtargetInfo.cpp
index 7a962052f211c..45059ba8fe01d 100644
--- a/llvm/lib/MC/MCSubtargetInfo.cpp
+++ b/llvm/lib/MC/MCSubtargetInfo.cpp
@@ -197,7 +197,7 @@ static FeatureBitset getFeatures(MCSubtargetInfo &STI, StringRef CPU,
 
   // Find CPU entry if CPU name is specified.
   else if (!CPU.empty()) {
-    const SubtargetSubTypeKV *CPUEntry = Find(CPU, ProcDesc);
+    const SubtargetSubTypeKV *CPUEntry = STI.resolveCPU(CPU);
 
     // If there is a match
     if (CPUEntry) {
@@ -210,7 +210,7 @@ static FeatureBitset getFeatures(MCSubtargetInfo &STI, StringRef CPU,
   }
 
   if (!TuneCPU.empty()) {
-    const SubtargetSubTypeKV *CPUEntry = Find(TuneCPU, ProcDesc);
+    const SubtargetSubTypeKV *CPUEntry = STI.resolveCPU(TuneCPU);
 
     // If there is a match
     if (CPUEntry) {
@@ -258,13 +258,14 @@ void MCSubtargetInfo::setDefaultFeatures(StringRef CPU, StringRef TuneCPU,
 MCSubtargetInfo::MCSubtargetInfo(
     const Triple &TT, StringRef C, StringRef TC, StringRef FS, StringTable PN,
     ArrayRef<SubtargetFeatureKV> PF, ArrayRef<SubtargetSubTypeKV> PD,
-    const MCSchedModel *PSM, const MCWriteProcResEntry *WPR,
-    const MCWriteLatencyEntry *WL, const MCReadAdvanceEntry *RA,
-    const InstrStage *IS, const unsigned *OC, const unsigned *FP)
+    ArrayRef<SubtargetSubTypeAliasKV> PA, const MCSchedModel *PSM,
+    const MCWriteProcResEntry *WPR, const MCWriteLatencyEntry *WL,
+    const MCReadAdvanceEntry *RA, const InstrStage *IS, const unsigned *OC,
+    const unsigned *FP)
     : TargetTriple(TT), CPU(std::string(C)), TuneCPU(std::string(TC)),
-      ProcNames(PN), ProcFeatures(PF), ProcDesc(PD), ProcSchedModels(PSM),
-      WriteProcResTable(WPR), WriteLatencyTable(WL), ReadAdvanceTable(RA),
-      Stages(IS), OperandCycles(OC), ForwardingPaths(FP) {
+      ProcNames(PN), ProcFeatures(PF), ProcDesc(PD), ProcAliases(PA),
+      ProcSchedModels(PSM), WriteProcResTable(WPR), WriteLatencyTable(WL),
+      ReadAdvanceTable(RA), Stages(IS), OperandCycles(OC), ForwardingPaths(FP) {
   InitMCProcessorInfo(CPU, TuneCPU, FS);
 }
 
@@ -434,12 +435,23 @@ bool MCSubtargetInfo::checkFeatureExpression(StringRef FeatureExpr) const {
   return Parser.parse();
 }
 
+const SubtargetSubTypeKV *MCSubtargetInfo::resolveCPU(StringRef CPU) const {
+  if (const SubtargetSubTypeKV *CPUEntry = Find(CPU, ProcDesc))
+    return CPUEntry;
+
+  // Not a canonical processor name; check whether it is a known alias.
+  if (const SubtargetSubTypeAliasKV *Alias = Find(CPU, ProcAliases))
+    return &ProcDesc[Alias->SubTypeIdx];
+
+  return nullptr;
+}
+
 const MCSchedModel &MCSubtargetInfo::getSchedModelForCPU(StringRef CPU) const {
   assert(llvm::is_sorted(ProcDesc) &&
          "Processor machine model table is not sorted");
 
   // Find entry
-  const SubtargetSubTypeKV *CPUEntry = Find(CPU, ProcDesc);
+  const SubtargetSubTypeKV *CPUEntry = resolveCPU(CPU);
 
   if (!CPUEntry) {
     if (CPU != "help") // Don't error if the user asked for help.
diff --git a/llvm/test/TableGen/ProcessorAlias.td b/llvm/test/TableGen/ProcessorAlias.td
index c70c7dd58f8e4..6e3f970f0d425 100644
--- a/llvm/test/TableGen/ProcessorAlias.td
+++ b/llvm/test/TableGen/ProcessorAlias.td
@@ -1,7 +1,7 @@
 // RUN: llvm-tblgen -gen-subtarget -I %p/../../include %s 2>&1 | FileCheck %s
-// Verify that ProcessorAlias entries are emitted into the CPU subtype table
-// alongside real processors, resolving to their canonical processor's
-// features and scheduling model, and that the table stays sorted by key.
+// Verify that ProcessorAlias entries are emitted into a separate, compact alias
+// table (mapping alias name -> canonical processor index) rather than as full
+// SubtargetSubTypeKV entries, and that both tables stay sorted by key.
 
 include "llvm/Target/Target.td"
 
@@ -12,13 +12,20 @@ def FeatureA : SubtargetFeature<"feature-a", "HasA", "true", "">;
 def ProcA : ProcessorModel<"cpu-a", NoSchedModel, [FeatureA]>;
 def ProcB : ProcessorModel<"cpu-b", NoSchedModel, []>;
 
-// An alias resolves to an existing processor and is emitted as its own entry.
+// Two aliases pointing at cpu-a (index 0). One sorts before its canonical name,
+// the other after, so the alias table order is independent of cpu-a's position.
 def : ProcessorAlias<"alias-of-a", "cpu-a">;
+def : ProcessorAlias<"zzz-of-a", "cpu-a">;
 
-// The subtype table has 3 entries (2 processors + 1 alias) sorted by key:
-// alias-of-a, cpu-a, cpu-b. The alias carries cpu-a's feature mask (bit 0 set).
-// CHECK: extern const llvm::SubtargetSubTypeKVStorage< 3,
-// CHECK:      { sizeof(SubtargetSubTypeKV) * 3 + {{[0-9]+}}, { { { 0x1ULL,{{.*}} }, {{.*}}, 0 },
-// CHECK-NEXT: { sizeof(SubtargetSubTypeKV) * 2 + {{[0-9]+}}, { { { 0x1ULL,{{.*}} }, {{.*}}, 0 },
-// CHECK-NEXT: { sizeof(SubtargetSubTypeKV) * 1 + {{[0-9]+}}, { { { 0x0ULL,{{.*}} }, {{.*}}, 0 },
-// CHECK: "\000alias-of-a\000cpu-a\000cpu-b\000"
+// The subtype table holds only the 2 real processors; the storage template
+// records 2 subtypes and 2 aliases.
+// CHECK: static constexpr size_t MyTargetCPUAliasArraySize = sizeof(std::array<SubtargetSubTypeAliasKV, 2>);
+// CHECK: extern const llvm::SubtargetSubTypeKVStorage< 2, 2, {{[0-9]+}}> MyTargetSubTypeKVStorage
+// CHECK:      { sizeof(SubtargetSubTypeKV) * 2 + MyTargetCPUAliasArraySize + {{[0-9]+}}, { { { 0x1ULL,{{.*}} }, {{.*}}, 0 },
+// CHECK-NEXT: { sizeof(SubtargetSubTypeKV) * 1 + MyTargetCPUAliasArraySize + {{[0-9]+}}, { { { 0x0ULL,{{.*}} }, {{.*}}, 0 },
+
+// The alias table is sorted by alias name (alias-of-a, zzz-of-a); both resolve
+// to subtype index 0 (cpu-a).
+// CHECK:      { sizeof(SubtargetSubTypeAliasKV) * 2 + {{[0-9]+}}, 0 },
+// CHECK-NEXT: { sizeof(SubtargetSubTypeAliasKV) * 1 + {{[0-9]+}}, 0 },
+// CHECK: "\000cpu-a\000cpu-b\000alias-of-a\000zzz-of-a\000"
diff --git a/llvm/unittests/CodeGen/MFCommon.inc b/llvm/unittests/CodeGen/MFCommon.inc
index 5a09e62cc99c5..35b5351d6f364 100644
--- a/llvm/unittests/CodeGen/MFCommon.inc
+++ b/llvm/unittests/CodeGen/MFCommon.inc
@@ -92,7 +92,7 @@ public:
 class BogusSubtarget : public TargetSubtargetInfo {
 public:
   BogusSubtarget(TargetMachine &TM)
-      : TargetSubtargetInfo(Triple(""), "", "", "", "", {}, {}, nullptr,
+      : TargetSubtargetInfo(Triple(""), "", "", "", "", {}, {}, {}, nullptr,
                             nullptr, nullptr, nullptr, nullptr, nullptr,
                             nullptr),
         FL(), TL(TM, *this) {}
@@ -148,8 +148,8 @@ public:
     MRI = std::make_unique<MCRegisterInfo>();
     STI = std::make_unique<MCSubtargetInfo>(
         Triple(""), "", "", "", StringTable{""}, ArrayRef<SubtargetFeatureKV>{},
-        ArrayRef<SubtargetSubTypeKV>{}, nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr);
+        ArrayRef<SubtargetSubTypeKV>{}, ArrayRef<SubtargetSubTypeAliasKV>{},
+        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
   }
 
   ~BogusTargetMachine() override = default;
diff --git a/llvm/unittests/CodeGen/MachineInstrTest.cpp b/llvm/unittests/CodeGen/MachineInstrTest.cpp
index 0ec589c45af08..03f4077ea55ac 100644
--- a/llvm/unittests/CodeGen/MachineInstrTest.cpp
+++ b/llvm/unittests/CodeGen/MachineInstrTest.cpp
@@ -43,9 +43,9 @@ std::unique_ptr<MCContext> createMCContext(const MCAsmInfo &AsmInfo) {
   Triple TheTriple(/*ArchStr=*/"", /*VendorStr=*/"", /*OSStr=*/"",
                    /*EnvironmentStr=*/"elf");
   static MCRegisterInfo MRI;
-  static const MCSubtargetInfo STI(TheTriple, "", "", "", "", {}, {}, nullptr,
+  static const MCSubtargetInfo STI(TheTriple, "", "", "", "", {}, {}, {},
                                    nullptr, nullptr, nullptr, nullptr, nullptr,
-                                   nullptr);
+                                   nullptr, nullptr);
   return std::make_unique<MCContext>(TheTriple, AsmInfo, MRI, STI, nullptr,
                                      false);
 }
diff --git a/llvm/unittests/CodeGen/MachineOperandTest.cpp b/llvm/unittests/CodeGen/MachineOperandTest.cpp
index 21254599a227c..f5bcb5332d64e 100644
--- a/llvm/unittests/CodeGen/MachineOperandTest.cpp
+++ b/llvm/unittests/CodeGen/MachineOperandTest.cpp
@@ -353,7 +353,7 @@ TEST(MachineOperandTest, PrintMCSymbol) {
   MCAsmInfo MAI(MCOptions);
   MCRegisterInfo MRI;
   Triple T = Triple("unknown-unknown-unknown");
-  MCSubtargetInfo STI(T, "", "", "", "", {}, {}, nullptr, nullptr, nullptr,
+  MCSubtargetInfo STI(T, "", "", "", "", {}, {}, {}, nullptr, nullptr, nullptr,
                       nullptr, nullptr, nullptr, nullptr);
   MCContext Ctx(T, MAI, MRI, STI);
   MCSymbol *Sym = Ctx.getOrCreateSymbol("foo");
diff --git a/llvm/unittests/Target/AArch64/AArch64InstPrinterTest.cpp b/llvm/unittests/Target/AArch64/AArch64InstPrinterTest.cpp
index f922d7c7b9a15..b336eb80a2a18 100644
--- a/llvm/unittests/Target/AArch64/AArch64InstPrinterTest.cpp
+++ b/llvm/unittests/Target/AArch64/AArch64InstPrinterTest.cpp
@@ -38,7 +38,7 @@ static std::string AArch64InstPrinterTestPrintAlignedLabel(uint64_t value) {
   MCAsmInfo MAI(MCOptions);
   MCInstrInfo MII;
   MCRegisterInfo MRI;
-  MCSubtargetInfo STI(Triple(""), "", "", "", "", {}, {}, nullptr, nullptr,
+  MCSubtargetInfo STI(Triple(""), "", "", "", "", {}, {}, {}, nullptr, nullptr,
                       nullptr, nullptr, nullptr, nullptr, nullptr);
   MCContext Ctx(Triple(""), MAI, MRI, STI);
   MCInst MI;
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index ff7eaa1c55b4b..93b5eb2b3d985 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -22,6 +22,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/MC/MCInstrItineraries.h"
 #include "llvm/MC/MCSchedule.h"
 #include "llvm/Support/Debug.h"
@@ -85,6 +86,7 @@ class SubtargetEmitter : TargetFeaturesEmitter {
     unsigned NumFeatures;
     unsigned FeatureStrTabSize;
     unsigned NumProcs;
+    unsigned NumAliases;
     unsigned SubTypeStrTabSize;
   };
   MCDescInfo emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap);
@@ -92,10 +94,15 @@ class SubtargetEmitter : TargetFeaturesEmitter {
   void emitHeader(raw_ostream &OS);
   void emitCtor(raw_ostream &OS, MCDescInfo DescInfo);
 
+  struct CPUKeyValuesInfo {
+    unsigned NumProcs;
+    unsigned NumAliases;
+    unsigned SubTypeStrTabSize;
+  };
   std::pair<unsigned, unsigned>
   featureKeyValues(raw_ostream &OS, const FeatureMapTy &FeatureMap);
-  std::pair<unsigned, unsigned> cpuKeyValues(raw_ostream &OS,
-                                             const FeatureMapTy &FeatureMap);
+  CPUKeyValuesInfo cpuKeyValues(raw_ostream &OS,
+                                const FeatureMapTy &FeatureMap);
   void formItineraryStageString(const std::string &Names,
                                 const Record *ItinData, std::string &ItinString,
                                 unsigned &NStages);
@@ -283,73 +290,92 @@ static void checkDuplicateCPUFeatures(StringRef CPUName,
 // CPUKeyValues - Emit data of all the subtarget processors.  Used by command
 // line.
 //
-std::pair<unsigned, unsigned>
+SubtargetEmitter::CPUKeyValuesInfo
 SubtargetEmitter::cpuKeyValues(raw_ostream &OS,
                                const FeatureMapTy &FeatureMap) {
+  // Gather and sort the processors. Only real processors go in the subtype
+  // table; aliases are stored in a separate, more compact table.
   std::vector<const Record *> ProcessorList =
       Records.getAllDerivedDefinitions("Processor");
-
-  StringMap<const Record *> ProcessorMap;
-  for (const Record *Processor : ProcessorList)
-    ProcessorMap[Processor->getValueAsString("Name")] = Processor;
-
-  // Maps each emitted CPU name (processor or alias) to the processor record it
-  // resolves to. Keying by name detects duplicates on insertion.
-  StringMap<const Record *> SubTypeEntries;
-  for (const Record *Processor : ProcessorList)
-    SubTypeEntries[Processor->getValueAsString("Name")] = Processor;
-
+  llvm::sort(ProcessorList, LessRecordFieldName());
+
+  // Map from processor name to its index in the sorted subtype table, so that
+  // aliases can point at the canonical processor entry.
+  StringMap<unsigned> ProcessorIndex;
+  for (const auto &[Idx, Processor] : enumerate(ProcessorList))
+    ProcessorIndex[Processor->getValueAsString("Name")] = Idx;
+
+  // Validate and resolve each alias to the index of its canonical processor.
+  struct AliasEntry {
+    StringRef Name;
+    unsigned SubTypeIdx;
+  };
+  std::vector<AliasEntry> AliasEntries;
   std::vector<const Record *> ProcessorAliasList =
       Records.getAllDerivedDefinitionsIfDefined("ProcessorAlias");
+  AliasEntries.reserve(ProcessorAliasList.size());
+
+  StringSet<> AliasNames;
   for (const Record *Rec : ProcessorAliasList) {
     StringRef Name = Rec->getValueAsString("Name");
     StringRef Alias = Rec->getValueAsString("Alias");
-    auto It = ProcessorMap.find(Alias);
-    if (It == ProcessorMap.end())
+    auto It = ProcessorIndex.find(Alias);
+    if (It == ProcessorIndex.end())
       PrintFatalError(Rec, "Alias '" + Name +
                                "' references a non-existent Processor '" +
                                Alias + "'");
-    if (!SubTypeEntries.try_emplace(Name, It->second).second)
-      PrintFatalError(
-          Rec, "Alias '" + Name + "' duplicates an existing " +
-                   (ProcessorMap.contains(Name) ? "Processor" : "alias"));
+    if (ProcessorIndex.contains(Name))
+      PrintFatalError(Rec,
+                      "Alias '" + Name + "' duplicates an existing Processor");
+    if (!AliasNames.insert(Name).second)
+      PrintFatalError(Rec, "Alias '" + Name + "' duplicates an existing alias");
+    AliasEntries.push_back({Name, It->second});
   }
 
-  // The table must stay sorted by key for the binary search in the lookups.
-  std::vector<std::pair<StringRef, const Record *>> SortedEntries;
-  SortedEntries.reserve(SubTypeEntries.size());
-  for (const auto &Entry : SubTypeEntries)
-    SortedEntries.emplace_back(Entry.getKey(), Entry.getValue());
-  llvm::sort(SortedEntries, llvm::less_first());
+  // The alias table must be sorted by key for the binary search in the lookups.
+  llvm::sort(AliasEntries, [](const AliasEntry &LHS, const AliasEntry &RHS) {
+    return LHS.Name < RHS.Name;
+  });
 
   StringToOffsetTable StrTab;
-  for (const auto &[Name, Proc] : SortedEntries)
-    StrTab.GetOrAddStringOffset(Name);
+  for (const Record *Processor : ProcessorList)
+    StrTab.GetOrAddStringOffset(Processor->getValueAsString("Name"));
+  for (const AliasEntry &Entry : AliasEntries)
+    StrTab.GetOrAddStringOffset(Entry.Name);
 
   // Note that unlike `FeatureKeyValues`, here we do not need to check for
   // duplicate processors, since that is already done when the SubtargetEmitter
   // constructor calls `getSchedModels` to build a `CodeGenSchedModels` object,
   // which does the duplicate processor check.
 
-  unsigned Total = SortedEntries.size();
+  unsigned NumProcs = ProcessorList.size();
+  unsigned NumAliases = AliasEntries.size();
+
+  // The alias array's byte size, used to reach the string blob from a subtype
+  // entry.
+  OS << "static constexpr size_t " << Target
+     << "CPUAliasArraySize = sizeof(std::array<SubtargetSubTypeAliasKV, "
+     << NumAliases << ">);\n";
 
   // Begin processor table.
   OS << "// Sorted (by key) array of values for CPU subtype.\n"
-     << "extern const llvm::SubtargetSubTypeKVStorage< " << Total << ", "
-     << (StrTab.size() + 1) << "> " << Target << "SubTypeKVStorage = {\n  {\n";
+     << "extern const llvm::SubtargetSubTypeKVStorage< " << NumProcs << ", "
+     << NumAliases << ", " << (StrTab.size() + 1) << "> " << Target
+     << "SubTypeKVStorage = {\n  {\n";
 
-  for (const auto &[Idx, Entry] : enumerate(SortedEntries)) {
-    const auto &[Name, Processor] = Entry;
+  for (const auto &[Idx, Processor] : enumerate(ProcessorList)) {
+    StringRef Name = Processor->getValueAsString("Name");
     ConstRecVec FeatureList = Processor->getValueAsListOfDefs("Features");
     ConstRecVec TuneFeatureList =
         Processor->getValueAsListOfDefs("TuneFeatures");
 
-    // Aliases share the canonical processor's already-checked feature lists.
-    if (Name == Processor->getValueAsString("Name"))
-      checkDuplicateCPUFeatures(Name, FeatureList, TuneFeatureList);
+    // Warn the user if there are duplicate processor features or tune features.
+    checkDuplicateCPUFeatures(Name, FeatureList, TuneFeatureList);
 
-    OS << "   { sizeof(SubtargetSubTypeKV) * " << (Total - Idx) << " + "
-       << StrTab.GetOrAddStringOffset(Name) << ", ";
+    // The string blob follows the subtype and alias arrays, so skip both.
+    OS << "   { sizeof(SubtargetSubTypeKV) * " << (NumProcs - Idx) << " + "
+       << Target << "CPUAliasArraySize + " << StrTab.GetOrAddStringOffset(Name)
+       << ", ";
 
     printFeatureMask(OS, FeatureList, FeatureMap);
     OS << ", ";
@@ -360,12 +386,23 @@ SubtargetEmitter::cpuKeyValues(raw_ostream &OS,
   }
 
   OS << "  },\n";
+
+  // Begin alias table. The extra brace layer is std::array's wrapped C array.
+  OS << "  { {\n";
+  for (const auto &[Idx, Entry] : enumerate(AliasEntries)) {
+    // The string blob immediately follows the alias array.
+    OS << "   { sizeof(SubtargetSubTypeAliasKV) * " << (NumAliases - Idx)
+       << " + " << StrTab.GetOrAddStringOffset(Entry.Name) << ", "
+       << Entry.SubTypeIdx << " },\n";
+  }
+  OS << "  } },\n";
+
   StrTab.EmitString(OS);
 
   // End processor table.
   OS << "};\n";
 
-  return {Total, StrTab.size() + 1};
+  return {NumProcs, NumAliases, unsigned(StrTab.size() + 1)};
 }
 
 //
@@ -2067,12 +2104,13 @@ void SubtargetEmitter::emitGenMCSubtargetInfo(raw_ostream &OS) {
      << "    StringTable PN,\n"
      << "    ArrayRef<SubtargetFeatureKV> PF,\n"
      << "    ArrayRef<SubtargetSubTypeKV> PD,\n"
+     << "    ArrayRef<SubtargetSubTypeAliasKV> PA,\n"
      << "    const MCSchedModel *PSM,\n"
      << "    const MCWriteProcResEntry *WPR,\n"
      << "    const MCWriteLatencyEntry *WL,\n"
      << "    const MCReadAdvanceEntry *RA, const InstrStage *IS,\n"
      << "    const unsigned *OC, const unsigned *FP) :\n"
-     << "      MCSubtargetInfo(TT, CPU, TuneCPU, FS, PN, PF, PD, PSM,\n"
+     << "      MCSubtargetInfo(TT, CPU, TuneCPU, FS, PN, PF, PD, PA, PSM,\n"
      << "                      WPR, WL, RA, IS, OC, FP) { }\n\n"
      << "  unsigned resolveVariantSchedClass(unsigned SchedClass,\n"
      << "      const MCInst *MI, const MCInstrInfo *MCII,\n"
@@ -2126,8 +2164,9 @@ SubtargetEmitter::emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap) {
   OS << "\n";
   emitSchedModel(OS);
   OS << "\n";
-  auto [NumProcs, SubTypeStrTabSize] = cpuKeyValues(OS, FeatureMap);
+  auto [NumProcs, NumAliases, SubTypeStrTabSize] = cpuKeyValues(OS, FeatureMap);
   Res.NumProcs = NumProcs;
+  Res.NumAliases = NumAliases;
   Res.SubTypeStrTabSize = SubTypeStrTabSize;
   OS << "\n";
 
@@ -2145,9 +2184,11 @@ SubtargetEmitter::emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap) {
   else
     OS << "{}, ";
   if (Res.NumProcs)
-    OS << Target << "SubTypeKVStorage.SubTypes, " << Target << "SchedModels, ";
+    OS << Target << "SubTypeKVStorage.SubTypes, "
+       << "ArrayRef(" << Target << "SubTypeKVStorage.Aliases).take_front("
+       << Res.NumAliases << "), " << Target << "SchedModels, ";
   else
-    OS << "{}, nullptr, ";
+    OS << "{}, {}, nullptr, ";
   OS << '\n';
   OS.indent(22);
   OS << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, "
@@ -2249,8 +2290,8 @@ void SubtargetEmitter::emitCtor(raw_ostream &OS, MCDescInfo DescInfo) {
        << Target << "FeatureKVStorage;\n";
   }
   OS << "extern const llvm::SubtargetSubTypeKVStorage<" << DescInfo.NumProcs
-     << ", " << DescInfo.SubTypeStrTabSize << "> " << Target
-     << "SubTypeKVStorage;\n";
+     << ", " << DescInfo.NumAliases << ", " << DescInfo.SubTypeStrTabSize
+     << "> " << Target << "SubTypeKVStorage;\n";
   OS << "extern const llvm::MCSchedModel " << Target << "SchedModels[];\n";
   OS << "extern const llvm::MCWriteProcResEntry " << Target
      << "WriteProcResTable[];\n";
@@ -2276,10 +2317,11 @@ void SubtargetEmitter::emitCtor(raw_ostream &OS, MCDescInfo DescInfo) {
   else
     OS << "{}, ";
   if (DescInfo.NumProcs) {
-    OS << "ArrayRef(" << Target << "SubTypeKVStorage.SubTypes), " << Target
-       << "SchedModels, ";
+    OS << "ArrayRef(" << Target << "SubTypeKVStorage.SubTypes), "
+       << "ArrayRef(" << Target << "SubTypeKVStorage.Aliases).take_front("
+       << DescInfo.NumAliases << "), " << Target << "SchedModels, ";
   } else {
-    OS << "{}, nullptr, ";
+    OS << "{}, {}, nullptr, ";
   }
   OS << '\n';
   OS.indent(24);

>From a6d9e7e1c861ffb7655e27f0bba9c309597c2431 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Sat, 25 Jul 2026 08:25:26 +0200
Subject: [PATCH 2/2] sort

---
 llvm/test/TableGen/ProcessorAlias.td     |  3 ++-
 llvm/utils/TableGen/SubtargetEmitter.cpp | 13 ++++++++++---
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/llvm/test/TableGen/ProcessorAlias.td b/llvm/test/TableGen/ProcessorAlias.td
index 6e3f970f0d425..bfcff64dfde95 100644
--- a/llvm/test/TableGen/ProcessorAlias.td
+++ b/llvm/test/TableGen/ProcessorAlias.td
@@ -28,4 +28,5 @@ def : ProcessorAlias<"zzz-of-a", "cpu-a">;
 // to subtype index 0 (cpu-a).
 // CHECK:      { sizeof(SubtargetSubTypeAliasKV) * 2 + {{[0-9]+}}, 0 },
 // CHECK-NEXT: { sizeof(SubtargetSubTypeAliasKV) * 1 + {{[0-9]+}}, 0 },
-// CHECK: "\000cpu-a\000cpu-b\000alias-of-a\000zzz-of-a\000"
+// The string blob is sorted, independent of either table's order.
+// CHECK: "\000alias-of-a\000cpu-a\000cpu-b\000zzz-of-a\000"
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index 93b5eb2b3d985..226f8068626da 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -337,11 +337,18 @@ SubtargetEmitter::cpuKeyValues(raw_ostream &OS,
     return LHS.Name < RHS.Name;
   });
 
-  StringToOffsetTable StrTab;
+  // Sort all names together so the emitted string blob is sorted.
+  SmallVector<StringRef> Names;
+  Names.reserve(ProcessorList.size() + AliasEntries.size());
   for (const Record *Processor : ProcessorList)
-    StrTab.GetOrAddStringOffset(Processor->getValueAsString("Name"));
+    Names.push_back(Processor->getValueAsString("Name"));
   for (const AliasEntry &Entry : AliasEntries)
-    StrTab.GetOrAddStringOffset(Entry.Name);
+    Names.push_back(Entry.Name);
+  llvm::sort(Names);
+
+  StringToOffsetTable StrTab;
+  for (StringRef Name : Names)
+    StrTab.GetOrAddStringOffset(Name);
 
   // Note that unlike `FeatureKeyValues`, here we do not need to check for
   // duplicate processors, since that is already done when the SubtargetEmitter



More information about the llvm-branch-commits mailing list