[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