[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