[llvm] [Option] Shrink Info from 24 to 16 bytes (PR #227582)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 23:50:03 PDT 2026
https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/227582
Move Flags, Visibility, and Param (getNumArgs() for `MultiArg`) into
`InfoExtra` and narrow `PrefixesOffset` to 8 bits. Outside clang, Flags
and Visibility take a handful of values per table; clang has 436
distinct InfoExtra rows for 3871 options.
clang's Info and InfoExtra tables shrink from 96,968 to 72,400 bytes.
LLM-aided
>From f6ed8caa49dc7c24f1a42f3f9ddc4350a27807ad Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 29 Sep 2026 23:33:39 -0700
Subject: [PATCH] [Option] Shrink Info from 24 to 16 bytes
Move Flags, Visibility, and Param (getNumArgs() for `MultiArg`) into
`InfoExtra` and narrow `PrefixesOffset` to 8 bits. Outside clang, Flags
and Visibility take a handful of values per table; clang has 436
distinct InfoExtra rows for 3871 options.
clang's Info and InfoExtra tables shrink from 96,968 to 72,400 bytes.
LLM-aided
---
llvm/include/llvm/Option/OptTable.h | 35 ++++++------
llvm/include/llvm/Option/Option.h | 12 ++--
llvm/lib/Option/OptTable.cpp | 29 +++++-----
llvm/utils/TableGen/OptionParserEmitter.cpp | 62 ++++++++++++---------
4 files changed, 77 insertions(+), 61 deletions(-)
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index a8232e400c22d..b61ae77f64a34 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -17,6 +17,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/StringSaver.h"
#include <cassert>
+#include <cstdint>
#include <string>
#include <vector>
@@ -73,18 +74,21 @@ class LLVM_ABI OptTable {
StringTable::Offset HelpTextOffset;
};
- /// Fields few options set. Row 0 is all zero and serves the options that set
- /// none.
+ /// Fields that are rarely set or take few distinct values, shared by the
+ /// options with equal values. Row 0 is all zero.
struct InfoExtra {
StringTable::Offset MetaVarOffset;
StringTable::Offset AliasArgsOffset;
/// The possible values as a comma separated list, empty for an option whose
/// values only getOptionValuesCode() knows.
StringTable::Offset ValuesOffset;
+ unsigned Flags;
+ uint16_t Visibility;
// Offset into OptTable's HelpTextVariantsTable; 0 for none.
- unsigned short HelpTextVariantsOffset;
+ uint16_t HelpTextVariantsOffset;
// Offset into OptTable's SubCommandIDsTable.
- unsigned short SubCommandIDsOffset;
+ uint16_t SubCommandIDsOffset;
+ uint8_t Param;
};
/// Entry for a single option instance in the option data table. An option's
@@ -94,16 +98,13 @@ class LLVM_ABI OptTable {
/// Offset 0 means the .td supplied no HelpText. A HelpText<""> maps to a
/// distinct empty string, marking the option deliberately undocumented.
StringTable::Offset HelpTextOffset;
- unsigned Flags;
- unsigned short Visibility;
- // Offset into OptTable's PrefixesTable.
- unsigned short PrefixesOffset;
- unsigned short GroupID;
- unsigned short AliasID;
+ uint16_t GroupID;
+ uint16_t AliasID;
// Offset into OptTable's InfoExtrasTable.
- unsigned short ExtraOffset;
- unsigned char Kind;
- unsigned char Param;
+ uint16_t ExtraOffset;
+ // Offset into OptTable's PrefixesTable.
+ uint8_t PrefixesOffset;
+ uint8_t Kind;
bool hasNoPrefix() const { return PrefixesOffset == 0; }
@@ -223,10 +224,6 @@ class LLVM_ABI OptTable {
return OptionInfos[id - 1];
}
- const InfoExtra &getExtra(const Info &I) const {
- return InfoExtrasTable[I.ExtraOffset];
- }
-
StringTable::Offset getHelpTextOffset(const Info &I,
Visibility VisibilityMask) const {
for (const HelpTextVariant *V =
@@ -307,6 +304,10 @@ class LLVM_ABI OptTable {
return getInfo(id).getPrefixedName(StrTable);
}
+ const InfoExtra &getExtra(const Info &I) const {
+ return InfoExtrasTable[I.ExtraOffset];
+ }
+
/// Get the kind of the given option.
unsigned getOptionKind(OptSpecifier id) const {
return getInfo(id).Kind;
diff --git a/llvm/include/llvm/Option/Option.h b/llvm/include/llvm/Option/Option.h
index dd91be2e4c527..c1b24efbe5d50 100644
--- a/llvm/include/llvm/Option/Option.h
+++ b/llvm/include/llvm/Option/Option.h
@@ -136,14 +136,14 @@ class Option {
/// Get the meta-variable list for this option.
StringRef getMetaVar() const { return Owner->getOptionMetaVar(getID()); }
- unsigned getNumArgs() const { return Info->Param; }
+ unsigned getNumArgs() const { return Owner->getExtra(*Info).Param; }
- bool hasNoOptAsInput() const { return Info->Flags & RenderAsInput;}
+ bool hasNoOptAsInput() const { return hasFlag(RenderAsInput); }
RenderStyleKind getRenderStyle() const {
- if (Info->Flags & RenderJoined)
+ if (hasFlag(RenderJoined))
return RenderJoinedStyle;
- if (Info->Flags & RenderSeparate)
+ if (hasFlag(RenderSeparate))
return RenderSeparateStyle;
switch (getKind()) {
case GroupClass:
@@ -169,12 +169,12 @@ class Option {
/// Test if this option has the flag \a Val.
bool hasFlag(unsigned Val) const {
- return Info->Flags & Val;
+ return Owner->getExtra(*Info).Flags & Val;
}
/// Test if this option has the visibility flag \a Val.
bool hasVisibilityFlag(unsigned Val) const {
- return Info->Visibility & Val;
+ return Owner->getExtra(*Info).Visibility & Val;
}
/// getUnaliasedOption - Return the final option this option
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index 5725639eeb776..dc17d4a10d45b 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -214,9 +214,10 @@ OptTable::findByPrefix(StringRef Cur, Visibility VisibilityMask,
const Info &In = OptionInfos[I];
if (In.hasNoPrefix() || (!In.hasHelpText() && !In.GroupID))
continue;
- if (!(In.Visibility & VisibilityMask))
+ const InfoExtra &Extra = getExtra(In);
+ if (!(Extra.Visibility & VisibilityMask))
continue;
- if (In.Flags & DisableFlags)
+ if (Extra.Flags & DisableFlags)
continue;
StringRef Name = In.getName(StrTable, PrefixesTable);
@@ -237,8 +238,8 @@ unsigned OptTable::findNearest(StringRef Option, std::string &NearestString,
unsigned MaximumDistance) const {
return internalFindNearest(
Option, NearestString, MinimumLength, MaximumDistance,
- [VisibilityMask](const Info &CandidateInfo) {
- return (CandidateInfo.Visibility & VisibilityMask) == 0;
+ [this, VisibilityMask](const Info &CandidateInfo) {
+ return (getExtra(CandidateInfo).Visibility & VisibilityMask) == 0;
});
}
@@ -248,10 +249,11 @@ unsigned OptTable::findNearest(StringRef Option, std::string &NearestString,
unsigned MaximumDistance) const {
return internalFindNearest(
Option, NearestString, MinimumLength, MaximumDistance,
- [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {
- if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude))
+ [this, FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {
+ unsigned Flags = getExtra(CandidateInfo).Flags;
+ if (FlagsToInclude && !(Flags & FlagsToInclude))
return true;
- if (CandidateInfo.Flags & FlagsToExclude)
+ if (Flags & FlagsToExclude)
return true;
return false;
});
@@ -716,8 +718,8 @@ void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title,
StringRef SubCommand) const {
return internalPrintHelp(
OS, Usage, Title, SubCommand, ShowHidden, ShowAllAliases,
- [VisibilityMask](const Info &CandidateInfo) -> bool {
- return (CandidateInfo.Visibility & VisibilityMask) == 0;
+ [this, VisibilityMask](const Info &CandidateInfo) -> bool {
+ return (getExtra(CandidateInfo).Visibility & VisibilityMask) == 0;
},
VisibilityMask);
}
@@ -729,10 +731,11 @@ void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title,
FlagsToExclude &= ~HelpHidden;
return internalPrintHelp(
OS, Usage, Title, /*SubCommand=*/{}, ShowHidden, ShowAllAliases,
- [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {
- if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude))
+ [this, FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {
+ unsigned Flags = getExtra(CandidateInfo).Flags;
+ if (FlagsToInclude && !(Flags & FlagsToInclude))
return true;
- if (CandidateInfo.Flags & FlagsToExclude)
+ if (Flags & FlagsToExclude)
return true;
return false;
},
@@ -799,7 +802,7 @@ void OptTable::internalPrintHelp(
continue;
const Info &CandidateInfo = getInfo(Id);
- if (!ShowHidden && (CandidateInfo.Flags & opt::HelpHidden))
+ if (!ShowHidden && (getExtra(CandidateInfo).Flags & opt::HelpHidden))
continue;
if (ExcludeOption(CandidateInfo))
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index 2360d55267625..eb33c77e6e9b3 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -21,6 +21,7 @@
#include <array>
#include <cstring>
#include <map>
+#include <set>
using namespace llvm;
@@ -295,18 +296,20 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
}
// Flags and Visibility name enumerators of the including tool. An option
- // inherits its group's.
+ // inherits its group's. Names are sorted so that equal masks share an
+ // InfoExtra row.
auto GetMask = [](const Record &R, StringRef Field) {
- std::string Mask;
- raw_string_ostream MaskOS(Mask);
- ListSeparator Sep(" | ");
+ std::set<StringRef> Names;
for (const Init *I : *R.getValueAsListInit(Field))
- MaskOS << Sep << "static_cast<unsigned>("
- << cast<DefInit>(I)->getDef()->getName() << ")";
+ Names.insert(cast<DefInit>(I)->getDef()->getName());
if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group")))
for (const Init *I : *DI->getDef()->getValueAsListInit(Field))
- MaskOS << Sep << "static_cast<unsigned>("
- << cast<DefInit>(I)->getDef()->getName() << ")";
+ Names.insert(cast<DefInit>(I)->getDef()->getName());
+ std::string Mask;
+ raw_string_ostream MaskOS(Mask);
+ ListSeparator Sep(" | ");
+ for (StringRef Name : Names)
+ MaskOS << Sep << "static_cast<unsigned>(" << Name << ")";
return Mask.empty() ? std::string("0") : Mask;
};
@@ -345,6 +348,8 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
// First emit the number of prefix strings in this list of prefixes.
OS << Sep << " " << Prefix.size() << " /* prefixes */";
PrefixIndex = CurIndex;
+ if (PrefixIndex > 255)
+ PrintFatalError("too many distinct prefix sets");
assert((CurIndex == 0 || !Prefix.empty()) &&
"Only first prefix set should be empty!");
for (const auto &PrefixKey : Prefix)
@@ -427,19 +432,27 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
}
OS << "\n };\n\n";
- // Rarely set fields, in OptTable::InfoExtra order. Options with equal values
- // share a row; row 0 is all zero.
+ // Fields that are rarely set or take few distinct values, in
+ // OptTable::InfoExtra order. Options with equal values share a row; row 0 is
+ // all zero.
OS << " static constexpr llvm::opt::OptTable::InfoExtra "
- "OptionInfoExtrasTable[] = {\n {0, 0, 0, 0, 0},\n";
- std::map<std::array<unsigned, 5>, unsigned> ExtraRows;
- ExtraRows.try_emplace({}, 0);
+ "OptionInfoExtrasTable[] = {\n {0, 0, 0, 0, 0, 0, 0, 0},\n";
+ std::map<std::array<std::string, 8>, unsigned> ExtraRows;
+ ExtraRows.try_emplace({"0", "0", "0", "0", "0", "0", "0", "0"}, 0);
DenseMap<const Record *, unsigned> ExtraOffset;
for (const Record &R : llvm::make_pointee_range(Opts)) {
- std::array<unsigned, 5> Row = {
- *Table.GetStringOffset(getOptionalString(R, "MetaVarName")),
- *Table.GetStringOffset(getAliasArgsBlob(R)),
- *Table.GetStringOffset(getOptionalString(R, "Values")),
- HelpTextVariantsOffset.lookup(&R), GetSubCommandIDsOffset(R)};
+ int64_t NumArgs = R.getValueAsInt("NumArgs");
+ if (NumArgs < 0 || NumArgs > 255)
+ PrintFatalError(R.getLoc(), "NumArgs must be in [0, 255]");
+ std::array<std::string, 8> Row = {
+ utostr(*Table.GetStringOffset(getOptionalString(R, "MetaVarName"))),
+ utostr(*Table.GetStringOffset(getAliasArgsBlob(R))),
+ utostr(*Table.GetStringOffset(getOptionalString(R, "Values"))),
+ GetMask(R, "Flags"),
+ GetMask(R, "Visibility"),
+ utostr(HelpTextVariantsOffset.lookup(&R)),
+ utostr(GetSubCommandIDsOffset(R)),
+ utostr(NumArgs)};
auto [It, Inserted] = ExtraRows.try_emplace(Row, ExtraRows.size());
if (Inserted) {
OS << " {";
@@ -458,8 +471,8 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
/*EmitComment=*/true);
OS << ", ";
writeStrTableOffset(OS, Table, getHelpText(R));
- OS << ", 0, 0, 0, " << GetRefID(R, "Group")
- << ", 0, 0, llvm::opt::Option::GroupClass, 0},\n";
+ OS << ", " << GetRefID(R, "Group")
+ << ", 0, 0, 0, llvm::opt::Option::GroupClass},\n";
}
for (const Record &R : llvm::make_pointee_range(Opts)) {
OS << " {";
@@ -467,13 +480,12 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
/*EmitComment=*/true);
OS << ", ";
writeStrTableOffset(OS, Table, getHelpText(R));
- OS << ", " << GetMask(R, "Flags") << ", " << GetMask(R, "Visibility");
+ OS << ", " << GetRefID(R, "Group") << ", " << GetRefID(R, "Alias");
+ OS << ", " << ExtraOffset.lookup(&R);
std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
OS << ", " << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())];
- OS << ", " << GetRefID(R, "Group") << ", " << GetRefID(R, "Alias");
- OS << ", " << ExtraOffset.lookup(&R) << ", llvm::opt::Option::"
- << R.getValueAsDef("Kind")->getValueAsString("Name") << "Class, "
- << R.getValueAsInt("NumArgs") << "},\n";
+ OS << ", llvm::opt::Option::"
+ << R.getValueAsDef("Kind")->getValueAsString("Name") << "Class},\n";
}
OS << " };\n\n";
More information about the llvm-commits
mailing list