[llvm-branch-commits] [MC] Generate FeatureKV with compact string table (PR #206331)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Jun 28 06:15:30 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-tablegen

Author: Alexis Engelke (aengelke)

<details>
<summary>Changes</summary>

FeatureKV is responsible for a fair amount of .data.rel.ro size and
relocations; in an all-target build, this amounts to ~139 kiB that need
to be touched on every startup. Therefore, store strings adjacent to the
SubtargetFeatureKV in memory and reference the strings via relative
offsets to avoid dynamic relocations.


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


3 Files Affected:

- (modified) llvm/include/llvm/MC/MCSubtargetInfo.h (+18-9) 
- (modified) llvm/test/TableGen/MacroFusion.td (-7) 
- (modified) llvm/utils/TableGen/SubtargetEmitter.cpp (+66-38) 


``````````diff
diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h
index 0393dea60b938..065f0322ad84c 100644
--- a/llvm/include/llvm/MC/MCSubtargetInfo.h
+++ b/llvm/include/llvm/MC/MCSubtargetInfo.h
@@ -34,24 +34,27 @@ class MCInst;
 
 /// Used to provide key value pairs for feature and CPU bit flags.
 struct SubtargetFeatureKV {
-private:
-  const char *Key;                      ///< K-V key string
-  const char *Desc;                     ///< Help descriptor
-
-public:
+  uint16_t KeyStrOff;
+  uint16_t DescStrOff;
   unsigned Value;                       ///< K-V integer value
   FeatureBitArray Implies;              ///< K-V bit mask
 
-  constexpr SubtargetFeatureKV(const char *Key, const char *Desc,
+  constexpr SubtargetFeatureKV(uint16_t KeyStrOff, uint16_t DescStrOff,
                                unsigned Value, FeatureBitArray Implies)
-      : Key(Key), Desc(Desc), Value(Value), Implies(Implies) {}
+      : KeyStrOff(KeyStrOff), DescStrOff(DescStrOff), Value(Value),
+        Implies(Implies) {}
 
   // Because of relative string offsets, this type is not copyable.
   SubtargetFeatureKV(const SubtargetFeatureKV &) = delete;
   SubtargetFeatureKV &operator=(const SubtargetFeatureKV &) = delete;
 
-  const char *key() const { return Key; }
-  const char *desc() const { return Desc; }
+  const char *key() const {
+    return reinterpret_cast<const char *>(this) + KeyStrOff;
+  }
+
+  const char *desc() const {
+    return reinterpret_cast<const char *>(this) + DescStrOff;
+  }
 
   /// Compare routine for std::lower_bound
   bool operator<(StringRef S) const { return StringRef(key()) < S; }
@@ -62,6 +65,12 @@ struct SubtargetFeatureKV {
   }
 };
 
+template <size_t NumFeatures, size_t FeatureStrTabSize>
+struct SubtargetFeatureKVStorage {
+  SubtargetFeatureKV Features[NumFeatures];
+  char Strings[FeatureStrTabSize];
+};
+
 //===----------------------------------------------------------------------===//
 
 /// Used to provide key value pairs for feature and CPU bit flags.
diff --git a/llvm/test/TableGen/MacroFusion.td b/llvm/test/TableGen/MacroFusion.td
index fd6e902c24dad..8fd823184a2b1 100644
--- a/llvm/test/TableGen/MacroFusion.td
+++ b/llvm/test/TableGen/MacroFusion.td
@@ -264,13 +264,6 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-EMPTY:
 // CHECK-PREDICATOR-NEXT:  #endif // GET_Test_MACRO_FUSION_PRED_IMPL
 
-// Check that we have generated target subfeature.
-// CHECK-SUBTARGET: { "fusion-test", "Test Fusion", Test::TestFusion,
-// CHECK-SUBTARGET: { "fusion-test-both-fusion-predicate", "Test BothFusionPredicate", Test::TestBothFusionPredicate,
-// CHECK-SUBTARGET: { "fusion-test-commutable", "Test Commutable Fusion", Test::TestCommutableFusion,
-// CHECK-SUBTARGET: { "fusion-test-first-same-reg", "Test FirstSameReg", Test::TestFirstSameRegFusion,
-// CHECK-SUBTARGET: { "fusion-test-single", "Test SingleFusion", Test::TestSingleFusion,
-
 // Check that we have generated `getMacroFusions()` function.
 // CHECK-SUBTARGET:      std::vector<MacroFusionPredTy> getMacroFusions() const final;
 
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index a915ce0daec2d..1a55e2ad873c0 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -78,14 +78,20 @@ class SubtargetEmitter : TargetFeaturesEmitter {
 
   FeatureMapTy emitEnums(raw_ostream &OS);
   void emitSubtargetInfoMacroCalls(raw_ostream &OS);
-  std::tuple<unsigned, unsigned, unsigned>
-  emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap);
+
+  struct MCDescInfo {
+    unsigned NumFeatures;
+    unsigned FeatureStrTabSize;
+    unsigned NumNames;
+    unsigned NumProcs;
+  };
+  MCDescInfo emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap);
   void emitTargetDesc(raw_ostream &OS);
   void emitHeader(raw_ostream &OS);
-  void emitCtor(raw_ostream &OS, unsigned NumNames, unsigned NumFeatures,
-                unsigned NumProcs);
+  void emitCtor(raw_ostream &OS, MCDescInfo DescInfo);
 
-  unsigned featureKeyValues(raw_ostream &OS, const FeatureMapTy &FeatureMap);
+  std::pair<unsigned, unsigned>
+  featureKeyValues(raw_ostream &OS, const FeatureMapTy &FeatureMap);
   unsigned cpuKeyValues(raw_ostream &OS, const FeatureMapTy &FeatureMap);
   unsigned cpuNames(raw_ostream &OS);
   void formItineraryStageString(const std::string &Names,
@@ -186,8 +192,9 @@ void SubtargetEmitter::emitSubtargetInfoMacroCalls(raw_ostream &OS) {
 // FeatureKeyValues - Emit data of all the subtarget features.  Used by the
 // command line.
 //
-unsigned SubtargetEmitter::featureKeyValues(raw_ostream &OS,
-                                            const FeatureMapTy &FeatureMap) {
+std::pair<unsigned, unsigned>
+SubtargetEmitter::featureKeyValues(raw_ostream &OS,
+                                   const FeatureMapTy &FeatureMap) {
   std::vector<const Record *> FeatureList =
       Records.getAllDerivedDefinitions("SubtargetFeature");
 
@@ -196,39 +203,54 @@ unsigned SubtargetEmitter::featureKeyValues(raw_ostream &OS,
     return Rec->getValueAsString("Name").empty();
   });
   if (FeatureList.empty())
-    return 0;
+    return {0, 0};
 
   // Sort and check duplicate Feature name.
   sortAndReportDuplicates(FeatureList, "Feature");
 
+  StringToOffsetTable StrTab;
+  // Offsets of CommandLineName and Desc in StrTab.
+  SmallVector<std::pair<unsigned, unsigned>> StrOffs;
+  for (const Record *Feature : FeatureList) {
+    unsigned NameOff =
+        StrTab.GetOrAddStringOffset(Feature->getValueAsString("Name"));
+    unsigned DescOff =
+        StrTab.GetOrAddStringOffset(Feature->getValueAsString("Desc"));
+    StrOffs.emplace_back(NameOff, DescOff);
+  }
+
   // Begin feature table.
   OS << "// Sorted (by key) array of values for CPU features.\n"
-     << "extern const llvm::SubtargetFeatureKV " << Target
-     << "FeatureKV[] = {\n";
+     << "extern const llvm::SubtargetFeatureKVStorage< " << FeatureList.size()
+     << ", " << (StrTab.size() + 1) << "> " << Target
+     << "FeatureKVStorage = {\n  {\n";
 
-  for (const Record *Feature : FeatureList) {
+  for (auto [Idx, Feature] : enumerate(FeatureList)) {
     // Next feature
     StringRef Name = Feature->getName();
-    StringRef CommandLineName = Feature->getValueAsString("Name");
-    StringRef Desc = Feature->getValueAsString("Desc");
 
     // Emit as { "feature", "description", { featureEnum }, { i1 , i2 , ... , in
     // } }
-    OS << "  { "
-       << "\"" << CommandLineName << "\", "
-       << "\"" << Desc << "\", " << Target << "::" << Name << ", ";
+    auto StrOff =
+        "sizeof(SubtargetFeatureKV) * " + Twine(FeatureList.size() - Idx);
+    OS << "    { " << StrOff << " + " << StrOffs[Idx].first << ", " << StrOff
+       << " + " << StrOffs[Idx].second << ", " << Target << "::" << Name
+       << ", ";
 
     ConstRecVec ImpliesList = Feature->getValueAsListOfDefs("Implies");
 
     printFeatureMask(OS, ImpliesList, FeatureMap);
 
-    OS << " },\n";
+    OS << "   },\n";
   }
 
+  OS << "  },\n";
+  StrTab.EmitString(OS);
+
   // End feature table.
   OS << "};\n";
 
-  return FeatureList.size();
+  return {FeatureList.size(), StrTab.size() + 1};
 }
 
 unsigned SubtargetEmitter::cpuNames(raw_ostream &OS) {
@@ -2079,20 +2101,23 @@ FeatureMapTy SubtargetEmitter::emitEnums(raw_ostream &OS) {
   return enumeration(OS);
 }
 
-std::tuple<unsigned, unsigned, unsigned>
+SubtargetEmitter::MCDescInfo
 SubtargetEmitter::emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap) {
   IfDefEmitter IfDef(OS, "GET_SUBTARGETINFO_MC_DESC");
   if (Target == "AArch64")
     OS << "#include \"llvm/TargetParser/AArch64TargetParser.h\"\n\n";
   NamespaceEmitter LlvmNS(OS, "llvm");
 
-  unsigned NumFeatures = featureKeyValues(OS, FeatureMap);
+  MCDescInfo Res;
+  auto [NumFeatures, FeatureStrTabSize] = featureKeyValues(OS, FeatureMap);
+  Res.NumFeatures = NumFeatures;
+  Res.FeatureStrTabSize = FeatureStrTabSize;
   OS << "\n";
   emitSchedModel(OS);
   OS << "\n";
-  unsigned NumProcs = cpuKeyValues(OS, FeatureMap);
+  Res.NumProcs = cpuKeyValues(OS, FeatureMap);
   OS << "\n";
-  unsigned NumNames = cpuNames(OS);
+  Res.NumNames = cpuNames(OS);
   OS << "\n";
 
   // MCInstrInfo initialization routine.
@@ -2106,15 +2131,15 @@ SubtargetEmitter::emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap) {
        << "  TuneCPU = AArch64::resolveCPUAlias(TuneCPU);\n";
   OS << "  return new " << Target
      << "GenMCSubtargetInfo(TT, CPU, TuneCPU, FS, ";
-  if (NumNames)
+  if (Res.NumNames)
     OS << Target << "Names, ";
   else
     OS << "{}, ";
-  if (NumFeatures)
-    OS << Target << "FeatureKV, ";
+  if (Res.NumFeatures)
+    OS << Target << "FeatureKVStorage.Features, ";
   else
     OS << "{}, ";
-  if (NumProcs)
+  if (Res.NumProcs)
     OS << Target << "SubTypeKV, ";
   else
     OS << "{}, ";
@@ -2131,7 +2156,7 @@ SubtargetEmitter::emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap) {
     OS << "nullptr, nullptr, nullptr";
   }
   OS << ");\n}\n\n";
-  return {NumNames, NumFeatures, NumProcs};
+  return Res;
 }
 
 void SubtargetEmitter::emitTargetDesc(raw_ostream &OS) {
@@ -2208,14 +2233,17 @@ void SubtargetEmitter::emitHeader(raw_ostream &OS) {
   OS << "};\n";
 }
 
-void SubtargetEmitter::emitCtor(raw_ostream &OS, unsigned NumNames,
-                                unsigned NumFeatures, unsigned NumProcs) {
+void SubtargetEmitter::emitCtor(raw_ostream &OS, MCDescInfo DescInfo) {
   IfDefEmitter IfDef(OS, "GET_SUBTARGETINFO_CTOR");
   OS << "#include \"llvm/CodeGen/TargetSchedule.h\"\n\n";
 
   NamespaceEmitter LLVMNS(OS, "llvm");
   OS << "extern const llvm::StringRef " << Target << "Names[];\n";
-  OS << "extern const llvm::SubtargetFeatureKV " << Target << "FeatureKV[];\n";
+  if (DescInfo.NumFeatures) {
+    OS << "extern const llvm::SubtargetFeatureKVStorage<"
+       << DescInfo.NumFeatures << ", " << DescInfo.FeatureStrTabSize << "> "
+       << Target << "FeatureKVStorage;\n";
+  }
   OS << "extern const llvm::SubtargetSubTypeKV " << Target << "SubTypeKV[];\n";
   OS << "extern const llvm::MCWriteProcResEntry " << Target
      << "WriteProcResTable[];\n";
@@ -2239,16 +2267,16 @@ void SubtargetEmitter::emitCtor(raw_ostream &OS, unsigned NumNames,
        << "                        AArch64::resolveCPUAlias(TuneCPU), FS, ";
   else
     OS << "  : TargetSubtargetInfo(TT, CPU, TuneCPU, FS, ";
-  if (NumNames)
-    OS << "ArrayRef(" << Target << "Names, " << NumNames << "), ";
+  if (DescInfo.NumNames)
+    OS << "ArrayRef(" << Target << "Names, " << DescInfo.NumNames << "), ";
   else
     OS << "{}, ";
-  if (NumFeatures)
-    OS << "ArrayRef(" << Target << "FeatureKV, " << NumFeatures << "), ";
+  if (DescInfo.NumFeatures)
+    OS << "ArrayRef(" << Target << "FeatureKVStorage.Features), ";
   else
     OS << "{}, ";
-  if (NumProcs)
-    OS << "ArrayRef(" << Target << "SubTypeKV, " << NumProcs << "), ";
+  if (DescInfo.NumProcs)
+    OS << "ArrayRef(" << Target << "SubTypeKV, " << DescInfo.NumProcs << "), ";
   else
     OS << "{}, ";
   OS << '\n';
@@ -2280,10 +2308,10 @@ void SubtargetEmitter::run(raw_ostream &OS) {
 
   auto FeatureMap = emitEnums(OS);
   emitSubtargetInfoMacroCalls(OS);
-  auto [NumNames, NumFeatures, NumProcs] = emitMCDesc(OS, FeatureMap);
+  MCDescInfo DescInfo = emitMCDesc(OS, FeatureMap);
   emitTargetDesc(OS);
   emitHeader(OS);
-  emitCtor(OS, NumNames, NumFeatures, NumProcs);
+  emitCtor(OS, DescInfo);
   emitMcInstrAnalysisPredicateFunctions(OS);
 }
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/206331


More information about the llvm-branch-commits mailing list