Author: Fangrui Song
Date: 2026-09-06T11:26:20-07:00
New Revision: 42012a90b98a62359547f0b72e8215a4037cc711

URL: 
https://github.com/llvm/llvm-project/commit/42012a90b98a62359547f0b72e8215a4037cc711
DIFF: 
https://github.com/llvm/llvm-project/commit/42012a90b98a62359547f0b72e8215a4037cc711.diff

LOG: [OptTable] Store Info strings in the string table (#218845)

Change HelpText, MetaVar, AliasArgs, and Values from `const char *` to
StringTable::offset, making the fields smaller, and removing dynamic
relocations in .data.rel.ro in PIC links.

Store them as StringTable::Offset, like the option names already are,
and return StringRef from getOptionHelpText() and getOptionMetaVar().
The 53 tables in the tree lose all 616 KB of .data.rel.ro, and sizeof(Info)
drops from 88 to 60; clang's table becomes 232 KB of .rodata.

An unset field and one explicitly set to the empty string, such as a
HelpText<"">, have to stay distinguishable, so the latter gets an empty
string of its own rather than offset zero.

Values declared with ValuesCode are only known to the generated code,
which supplies getOptionValuesCode() for OptTable to call; only clang has any.

Aided by Opus 5

Added: 
    

Modified: 
    clang-tools-extra/clangd/CompileCommands.cpp
    clang/lib/Options/DriverOptions.cpp
    llvm/include/llvm/Option/OptTable.h
    llvm/include/llvm/Option/Option.h
    llvm/lib/Option/OptTable.cpp
    llvm/lib/Option/Option.cpp
    llvm/unittests/Option/OptionParsingTest.cpp
    llvm/unittests/Option/Opts.td
    llvm/utils/TableGen/OptionParserEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/CompileCommands.cpp 
b/clang-tools-extra/clangd/CompileCommands.cpp
index b5965d163d7db..2ba9446aa57dc 100644
--- a/clang-tools-extra/clangd/CompileCommands.cpp
+++ b/clang-tools-extra/clangd/CompileCommands.cpp
@@ -495,7 +495,7 @@ llvm::ArrayRef<ArgStripper::Rule> 
ArgStripper::rulesFor(llvm::StringRef Arg) {
     struct {
       DriverID ID;
       DriverID AliasID;
-      const void *AliasArgs;
+      unsigned AliasArgsOffset;
     } AliasTable[] = {
 #define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS,       
\
                FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,       
\
@@ -505,7 +505,7 @@ llvm::ArrayRef<ArgStripper::Rule> 
ArgStripper::rulesFor(llvm::StringRef Arg) {
 #undef OPTION
     };
     for (auto &E : AliasTable)
-      if (E.AliasID != DriverID::OPT_INVALID && E.AliasArgs == nullptr)
+      if (E.AliasID != DriverID::OPT_INVALID && !E.AliasArgsOffset)
         AddAlias(E.ID, E.AliasID);
 
     auto Result = std::make_unique<TableTy>();

diff  --git a/clang/lib/Options/DriverOptions.cpp 
b/clang/lib/Options/DriverOptions.cpp
index c9c826a439123..6f630843f7019 100644
--- a/clang/lib/Options/DriverOptions.cpp
+++ b/clang/lib/Options/DriverOptions.cpp
@@ -41,7 +41,9 @@ class DriverOptTable : public PrecomputedOptTable {
 public:
   DriverOptTable()
       : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
-                            OptionPrefixesUnion) {}
+                            OptionPrefixesUnion) {
+    setValuesCodeFn(getOptionValuesCode);
+  }
 };
 } // anonymous namespace
 

diff  --git a/llvm/include/llvm/Option/OptTable.h 
b/llvm/include/llvm/Option/OptTable.h
index 45083b31c11f4..ceb22c68ccb3b 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -60,23 +60,30 @@ class LLVM_ABI OptTable {
     const char *Usage;
   };
 
+  /// Values of options declared with TableGen `ValuesCode`: only the generated
+  /// code knows them, so they cannot go in the string table. The generated
+  /// table supplies getOptionValuesCode() for this.
+  using ValuesCodeFnTy = StringRef (*)(unsigned);
+
   /// Entry for a single option instance in the option data table.
   struct Info {
     unsigned PrefixesOffset;
     StringTable::Offset PrefixedNameOffset;
-    const char *HelpText;
+    /// Offset 0 means the .td supplied no HelpText. A HelpText<""> maps to a
+    /// distinct empty string, marking the option deliberately undocumented.
+    StringTable::Offset HelpTextOffset;
     // Help text for specific visibilities. A list of pairs, where each pair
     // is a list of visibilities and a specific help string for those
     // visibilities. If no help text is found in this list for the visibility 
of
-    // the program, HelpText is used instead. This cannot use std::vector
+    // the program, HelpTextOffset is used instead. This cannot use std::vector
     // because OptTable is used in constexpr contexts. Increase the array sizes
     // here if you need more entries and adjust the constants in
     // OptionParserEmitter::EmitHelpTextsForVariants.
     std::array<std::pair<std::array<unsigned int, 2 /*MaxVisibilityPerHelp*/>,
-                         const char *>,
+                         StringTable::Offset>,
                1 /*MaxVisibilityHelp*/>
         HelpTextsForVariants;
-    const char *MetaVar;
+    StringTable::Offset MetaVarOffset;
     unsigned ID;
     unsigned char Kind;
     unsigned char Param;
@@ -84,8 +91,10 @@ class LLVM_ABI OptTable {
     unsigned int Visibility;
     unsigned short GroupID;
     unsigned short AliasID;
-    const char *AliasArgs;
-    const char *Values;
+    StringTable::Offset AliasArgsOffset;
+    /// The possible values as a comma separated list, empty for an option 
whose
+    /// values only getOptionValuesCode() knows.
+    StringTable::Offset ValuesOffset;
     // Offset into OptTable's SubCommandIDsTable.
     unsigned SubCommandIDsOffset;
 
@@ -103,6 +112,9 @@ class LLVM_ABI OptTable {
                                                  
getNumPrefixes(PrefixesTable));
     }
 
+    bool hasHelpText() const { return HelpTextOffset.value() != 0; }
+    bool hasAliasArgs() const { return AliasArgsOffset.value() != 0; }
+
     bool hasSubCommands() const { return SubCommandIDsOffset != 0; }
 
     unsigned getNumSubCommandIDs(ArrayRef<unsigned> SubCommandIDsTable) const {
@@ -179,6 +191,8 @@ class LLVM_ABI OptTable {
   /// The subcommand IDs table.
   ArrayRef<unsigned> SubCommandIDsTable;
 
+  ValuesCodeFnTy ValuesCodeFn = nullptr;
+
   bool GroupedShortOptions = false;
   bool DashDashParsing = false;
   const char *EnvVar = nullptr;
@@ -205,6 +219,22 @@ class LLVM_ABI OptTable {
     return OptionInfos[id - 1];
   }
 
+  StringTable::Offset getHelpTextOffset(const Info &I,
+                                        Visibility VisibilityMask) const {
+    for (const auto &[Visibilities, TextOffset] : I.HelpTextsForVariants)
+      for (auto Vis : Visibilities)
+        if (VisibilityMask & Vis)
+          return TextOffset;
+    return I.HelpTextOffset;
+  }
+
+  StringRef getOptionValues(const Info &I) const {
+    StringRef Values = (*StrTable)[I.ValuesOffset];
+    if (Values.empty() && ValuesCodeFn)
+      Values = ValuesCodeFn(I.ID);
+    return Values;
+  }
+
   std::unique_ptr<Arg> parseOneArgGrouped(InputArgList &Args,
                                           unsigned &Index) const;
 
@@ -217,6 +247,8 @@ class LLVM_ABI OptTable {
            ArrayRef<SubCommand> SubCommands = {},
            ArrayRef<unsigned> SubCommandIDsTable = {});
 
+  void setValuesCodeFn(ValuesCodeFnTy Fn) { ValuesCodeFn = Fn; }
+
   /// Build (or rebuild) the PrefixChars member.
   void buildPrefixChars();
 
@@ -276,27 +308,22 @@ class LLVM_ABI OptTable {
   }
 
   /// Get the help text to use to describe this option.
-  const char *getOptionHelpText(OptSpecifier id) const {
+  StringRef getOptionHelpText(OptSpecifier id) const {
     return getOptionHelpText(id, Visibility(0));
   }
 
   // Get the help text to use to describe this option.
   // If it has visibility specific help text and that visibility is in the
   // visibility mask, use that text instead of the generic text.
-  const char *getOptionHelpText(OptSpecifier id,
-                                Visibility VisibilityMask) const {
-    auto Info = getInfo(id);
-    for (auto [Visibilities, Text] : Info.HelpTextsForVariants)
-      for (auto Visibility : Visibilities)
-        if (VisibilityMask & Visibility)
-          return Text;
-    return Info.HelpText;
+  StringRef getOptionHelpText(OptSpecifier id,
+                              Visibility VisibilityMask) const {
+    return (*StrTable)[getHelpTextOffset(getInfo(id), VisibilityMask)];
   }
 
   /// Get the meta-variable name to use when describing
   /// this options values in the help text.
-  const char *getOptionMetaVar(OptSpecifier id) const {
-    return getInfo(id).MetaVar;
+  StringRef getOptionMetaVar(OptSpecifier id) const {
+    return (*StrTable)[getInfo(id).MetaVarOffset];
   }
 
   /// Specify the environment variable where initial options should be read.

diff  --git a/llvm/include/llvm/Option/Option.h 
b/llvm/include/llvm/Option/Option.h
index eac964159dc42..68945c046b642 100644
--- a/llvm/include/llvm/Option/Option.h
+++ b/llvm/include/llvm/Option/Option.h
@@ -121,10 +121,13 @@ class Option {
   /// E.g. ["foo", "bar"] would be returned as "foo\0bar\0".
   const char *getAliasArgs() const {
     assert(Info && "Must have a valid info!");
-    assert((!Info->AliasArgs || Info->AliasArgs[0] != 0) &&
-           "AliasArgs should be either 0 or non-empty.");
+    assert(Owner && "Must have a valid owner!");
+    return Owner->getStrTable().getCString(Info->AliasArgsOffset);
+  }
 
-    return Info->AliasArgs;
+  bool hasAliasArgs() const {
+    assert(Info && "Must have a valid info!");
+    return Info->hasAliasArgs();
   }
 
   /// Get the default prefix for this option.
@@ -144,13 +147,15 @@ class Option {
   /// Get the help text for this option.
   StringRef getHelpText() const {
     assert(Info && "Must have a valid info!");
-    return Info->HelpText;
+    assert(Owner && "Must have a valid owner!");
+    return Owner->getOptionHelpText(Info->ID);
   }
 
   /// Get the meta-variable list for this option.
   StringRef getMetaVar() const {
     assert(Info && "Must have a valid info!");
-    return Info->MetaVar;
+    assert(Owner && "Must have a valid owner!");
+    return Owner->getOptionMetaVar(Info->ID);
   }
 
   unsigned getNumArgs() const { return Info->Param; }

diff  --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index 4a49889a05377..ef3a4c694b6c2 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -20,7 +20,6 @@
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cassert>
-#include <cstring>
 #include <map>
 #include <set>
 #include <string>
@@ -192,11 +191,14 @@ OptTable::suggestValueCompletions(StringRef Option, 
StringRef Arg) const {
   // Search all options and return possible values.
   for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) {
     const Info &In = OptionInfos[I];
-    if (!In.Values || !optionMatches(*StrTable, PrefixesTable, In, Option))
+    if (!optionMatches(*StrTable, PrefixesTable, In, Option))
+      continue;
+    StringRef Values = getOptionValues(In);
+    if (Values.empty())
       continue;
 
     SmallVector<StringRef, 8> Candidates;
-    StringRef(In.Values).split(Candidates, ",", -1, false);
+    Values.split(Candidates, ",", -1, false);
 
     std::vector<std::string> Result;
     for (StringRef Val : Candidates)
@@ -213,7 +215,7 @@ OptTable::findByPrefix(StringRef Cur, Visibility 
VisibilityMask,
   std::vector<std::string> Ret;
   for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) {
     const Info &In = OptionInfos[I];
-    if (In.hasNoPrefix() || (!In.HelpText && !In.GroupID))
+    if (In.hasNoPrefix() || (!In.hasHelpText() && !In.GroupID))
       continue;
     if (!(In.Visibility & VisibilityMask))
       continue;
@@ -224,8 +226,7 @@ OptTable::findByPrefix(StringRef Cur, Visibility 
VisibilityMask,
     for (auto PrefixOffset : In.getPrefixOffsets(PrefixesTable)) {
       StringRef Prefix = (*StrTable)[PrefixOffset];
       std::string S = (Twine(Prefix) + Name + "\t").str();
-      if (In.HelpText)
-        S += In.HelpText;
+      S += (*StrTable)[In.HelpTextOffset];
       if (StringRef(S).starts_with(Cur) && S != std::string(Cur) + "\t")
         Ret.push_back(S);
     }
@@ -615,12 +616,12 @@ static std::string getOptionHelpName(const OptTable 
&Opts, OptSpecifier Id) {
     llvm_unreachable("Invalid option with help text.");
 
   case Option::MultiArgClass:
-    if (const char *MetaVarName = Opts.getOptionMetaVar(Id)) {
+    if (StringRef MetaVarName = Opts.getOptionMetaVar(Id);
+        !MetaVarName.empty()) {
       // For MultiArgs, metavar is full list of all argument names.
       Name += ' ';
       Name += MetaVarName;
-    }
-    else {
+    } else {
       // For MultiArgs<N>, if metavar not supplied, print <value> N times.
       for (unsigned i=0, e=O.getNumArgs(); i< e; ++i) {
         Name += " <value>";
@@ -640,7 +641,7 @@ static std::string getOptionHelpName(const OptTable &Opts, 
OptSpecifier Id) {
     [[fallthrough]];
   case Option::JoinedClass: case Option::CommaJoinedClass:
   case Option::JoinedAndSeparateClass:
-    if (const char *MetaVarName = Opts.getOptionMetaVar(Id))
+    if (StringRef MetaVarName = Opts.getOptionMetaVar(Id); 
!MetaVarName.empty())
       Name += MetaVarName;
     else
       Name += "<value>";
@@ -694,7 +695,7 @@ static void PrintHelpOptionList(raw_ostream &OS, StringRef 
Title,
   }
 }
 
-static const char *getOptionHelpGroup(const OptTable &Opts, OptSpecifier Id) {
+static StringRef getOptionHelpGroup(const OptTable &Opts, OptSpecifier Id) {
   unsigned GroupID = Opts.getOptionGroupID(Id);
 
   // If not in a group, return the default help group.
@@ -705,7 +706,7 @@ static const char *getOptionHelpGroup(const OptTable &Opts, 
OptSpecifier Id) {
   // name.
   //
   // FIXME: Split out option groups.
-  if (const char *GroupHelp = Opts.getOptionHelpText(GroupID))
+  if (StringRef GroupHelp = Opts.getOptionHelpText(GroupID); 
!GroupHelp.empty())
     return GroupHelp;
 
   // Otherwise keep looking.
@@ -750,7 +751,7 @@ void OptTable::internalPrintHelp(
 
   // Render help text into a map of group-name to a list of (option, help)
   // pairs.
-  std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp;
+  std::map<StringRef, std::vector<OptionInfo>> GroupedOptionHelp;
 
   auto ActiveSubCommand = llvm::find_if(
       SubCommands, [&](const auto &C) { return SubCommand == C.Name; });
@@ -813,15 +814,17 @@ void OptTable::internalPrintHelp(
 
     // If an alias doesn't have a help text, show a help text for the aliased
     // option instead.
-    const char *HelpText = getOptionHelpText(Id, VisibilityMask);
-    if (!HelpText && ShowAllAliases) {
+    StringTable::Offset HelpTextOffset =
+        getHelpTextOffset(CandidateInfo, VisibilityMask);
+    if (!HelpTextOffset.value() && ShowAllAliases) {
       const Option Alias = getOption(Id).getAlias();
       if (Alias.isValid())
-        HelpText = getOptionHelpText(Alias.getID(), VisibilityMask);
+        HelpTextOffset =
+            getHelpTextOffset(getInfo(Alias.getID()), VisibilityMask);
     }
 
-    if (HelpText && (strlen(HelpText) != 0)) {
-      const char *HelpGroup = getOptionHelpGroup(*this, Id);
+    if (StringRef HelpText = (*StrTable)[HelpTextOffset]; !HelpText.empty()) {
+      StringRef HelpGroup = getOptionHelpGroup(*this, Id);
       const std::string &OptName = getOptionHelpName(*this, Id);
       GroupedOptionHelp[HelpGroup].push_back({OptName, HelpText});
     }

diff  --git a/llvm/lib/Option/Option.cpp b/llvm/lib/Option/Option.cpp
index 838dd344b18a9..8e3638485b667 100644
--- a/llvm/lib/Option/Option.cpp
+++ b/llvm/lib/Option/Option.cpp
@@ -18,6 +18,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cassert>
+#include <cstring>
 
 using namespace llvm;
 using namespace llvm::opt;
@@ -29,7 +30,7 @@ Option::Option(const OptTable::Info *Info, const OptTable 
*Owner)
   assert((!Info || !getAlias().isValid() || !getAlias().getAlias().isValid()) 
&&
          "Multi-level aliases are not supported.");
 
-  if (Info && getAliasArgs()) {
+  if (Info && hasAliasArgs()) {
     assert(getAlias().isValid() && "Only alias options can have alias args.");
     assert(getKind() == FlagClass && "Only Flag aliases can have alias args.");
     assert(getAlias().getKind() != FlagClass &&
@@ -281,15 +282,9 @@ std::unique_ptr<Arg> Option::accept(const ArgList &Args, 
StringRef CurArg,
   }
 
   // FlagClass aliases can have AliasArgs<>; add those to the unaliased arg.
-  if (const char *Val = getAliasArgs()) {
-    while (*Val != '\0') {
-      UnaliasedA->getValues().push_back(Val);
-
-      // Move past the '\0' to the next argument.
-      Val += strlen(Val) + 1;
-    }
-  }
-  if (UnaliasedOption.getKind() == JoinedClass && !getAliasArgs())
+  for (const char *Val = getAliasArgs(); *Val; Val += strlen(Val) + 1)
+    UnaliasedA->getValues().push_back(Val);
+  if (UnaliasedOption.getKind() == JoinedClass && !hasAliasArgs())
     // A Flag alias for a Joined option must provide an argument.
     UnaliasedA->getValues().push_back("");
   return UnaliasedA;

diff  --git a/llvm/unittests/Option/OptionParsingTest.cpp 
b/llvm/unittests/Option/OptionParsingTest.cpp
index 3da015e343eb9..f494ada47b57f 100644
--- a/llvm/unittests/Option/OptionParsingTest.cpp
+++ b/llvm/unittests/Option/OptionParsingTest.cpp
@@ -32,6 +32,10 @@ enum ID {
 #undef OPTION
 };
 
+#define OPTTABLE_VALUES_CODE
+#include "Opts.inc"
+#undef OPTTABLE_VALUES_CODE
+
 #define OPTTABLE_PREFIXES_TABLE_CODE
 #include "Opts.inc"
 #undef OPTTABLE_PREFIXES_TABLE_CODE
@@ -62,14 +66,18 @@ class TestOptTable : public GenericOptTable {
 public:
   TestOptTable(bool IgnoreCase = false)
       : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
-                        IgnoreCase) {}
+                        IgnoreCase) {
+    setValuesCodeFn(getOptionValuesCode);
+  }
 };
 
 class TestPrecomputedOptTable : public PrecomputedOptTable {
 public:
   TestPrecomputedOptTable(bool IgnoreCase = false)
       : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
-                            OptionPrefixesUnion, IgnoreCase) {}
+                            OptionPrefixesUnion, IgnoreCase) {
+    setValuesCodeFn(getOptionValuesCode);
+  }
 };
 }
 
@@ -229,6 +237,27 @@ TYPED_TEST(OptTableTest, AliasArgs) {
   EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]);
 }
 
+TYPED_TEST(OptTableTest, AliasArgsMultiple) {
+  TypeParam T;
+  unsigned MAI, MAC;
+
+  const char *MyArgs[] = {"-Jmulti"};
+  InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
+  EXPECT_TRUE(AL.hasArg(OPT_D));
+  EXPECT_EQ((std::vector<std::string>{"foo", "bar"}),
+            AL.getAllArgValues(OPT_D));
+}
+
+TYPED_TEST(OptTableTest, SuggestValueCompletions) {
+  TypeParam T;
+
+  EXPECT_EQ((std::vector<std::string>{"inline1", "inline2"}),
+            T.suggestValueCompletions("-values-inline=", ""));
+  // Values computed by ValuesCode live outside the string table.
+  EXPECT_EQ((std::vector<std::string>{"code1", "code2"}),
+            T.suggestValueCompletions("-values-from-code=", ""));
+}
+
 TYPED_TEST(OptTableTest, IgnoreCase) {
   TypeParam T(true);
   unsigned MAI, MAC;

diff  --git a/llvm/unittests/Option/Opts.td b/llvm/unittests/Option/Opts.td
index 5be67c9decdbc..89c20359bd2a0 100644
--- a/llvm/unittests/Option/Opts.td
+++ b/llvm/unittests/Option/Opts.td
@@ -26,9 +26,15 @@ def I : Flag<["-"], "I">, Alias<H>, Group<my_group>;
 
 def J : Flag<["-"], "J">, Alias<B>, AliasArgs<["foo"]>;
 def Joo : Flag<["-"], "Joo">, Alias<B>, AliasArgs<["bar"]>;
+def Jmulti : Flag<["-"], "Jmulti">, Alias<D>, AliasArgs<["foo", "bar"]>;
 
 def K : Flag<["-"], "K">, Alias<B>;
 
+def ValuesInline : Joined<["-"], "values-inline=">, Values<"inline1,inline2">;
+def ValuesFromCode : Joined<["-"], "values-from-code=">, ValuesCode<[{
+  static constexpr const char VALUES_CODE [] = "code1,code2";
+}]>;
+
 def Slurp : Option<["-"], "slurp", KIND_REMAINING_ARGS>;
 
 def SlurpJoined : Option<["-"], "slurpjoined", KIND_REMAINING_ARGS_JOINED>;

diff  --git a/llvm/utils/TableGen/OptionParserEmitter.cpp 
b/llvm/utils/TableGen/OptionParserEmitter.cpp
index 829c202b495e4..ebc5a1a8aa6cc 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -15,6 +15,7 @@
 #include "llvm/Option/OptTable.h"
 #include "llvm/Support/InterleavedRange.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/TableGen/Error.h"
 #include "llvm/TableGen/Record.h"
 #include "llvm/TableGen/StringToOffsetTable.h"
 #include "llvm/TableGen/TableGenBackend.h"
@@ -31,13 +32,19 @@ static std::string getOptionName(const Record &R) {
   return R.getValueAsString("EnumName").str();
 }
 
-static raw_ostream &writeStrTableOffset(raw_ostream &OS,
-                                        const StringToOffsetTable &Table,
-                                        llvm::StringRef Str) {
-  OS << Table.GetStringOffset(Str) << " /* ";
-  OS.write_escaped(Str);
-  OS << " */";
-  return OS;
+// Only pass EmitComment for short strings that cannot contain "*/".
+static void writeStrTableOffset(raw_ostream &OS,
+                                const StringToOffsetTable &Table,
+                                llvm::StringRef Str, bool EmitComment = false) 
{
+  std::optional<unsigned> Offset = Table.GetStringOffset(Str);
+  if (!Offset)
+    PrintFatalError("string was not added to the option string table: " + Str);
+  OS << *Offset;
+  if (EmitComment) {
+    OS << " /* ";
+    OS.write_escaped(Str);
+    OS << " */";
+  }
 }
 
 static raw_ostream &writeCstring(raw_ostream &OS, llvm::StringRef Str) {
@@ -47,6 +54,32 @@ static raw_ostream &writeCstring(raw_ostream &OS, 
llvm::StringRef Str) {
   return OS;
 }
 
+static StringRef getOptionalString(const Record &R, StringRef Field) {
+  return R.getValueAsOptionalString(Field).value_or("");
+}
+
+// Offset zero is the empty string and stands for an unset HelpText. A
+// HelpText<""> marks an option as deliberately undocumented, so it maps to a
+// second empty string that the table does not put at offset zero.
+static StringRef getHelpText(const Record &R) {
+  std::optional<StringRef> S = R.getValueAsOptionalString("HelpText");
+  if (!S)
+    return StringRef();
+  return S->empty() ? StringRef("\0", 1) : *S;
+}
+
+// The string table appends the empty string that terminates the list.
+static std::string getAliasArgsBlob(const Record &R) {
+  std::string Blob;
+  for (StringRef AliasArg : R.getValueAsListOfStrings("AliasArgs")) {
+    if (AliasArg.empty())
+      PrintFatalError(R.getLoc(), "AliasArgs entries must not be empty");
+    Blob += AliasArg;
+    Blob += '\0';
+  }
+  return Blob;
+}
+
 static std::string getOptionPrefixedName(const Record &R) {
   std::vector<StringRef> Prefixes = R.getValueAsListOfStrings("Prefixes");
   StringRef Name = R.getValueAsString("Name");
@@ -196,8 +229,9 @@ static MarshallingInfo createMarshallingInfo(const Record 
&R) {
 }
 
 static void emitHelpTextsForVariants(
-    raw_ostream &OS, std::vector<std::pair<std::vector<std::string>, 
StringRef>>
-                         HelpTextsForVariants) {
+    raw_ostream &OS, const StringToOffsetTable &Table,
+    ArrayRef<std::pair<std::vector<std::string>, StringRef>>
+        HelpTextsForVariants) {
   // OptTable must be constexpr so it uses std::arrays with these capacities.
   const unsigned MaxVisibilityPerHelp = 2;
   const unsigned MaxVisibilityHelp = 1;
@@ -206,38 +240,22 @@ static void emitHelpTextsForVariants(
          "Too many help text variants to store in "
          "OptTable::HelpTextsForVariants");
 
-  // This function must initialise any unused elements of those arrays.
-  for (auto [Visibilities, _] : HelpTextsForVariants)
-    while (Visibilities.size() < MaxVisibilityPerHelp)
-      Visibilities.push_back("0");
-
-  while (HelpTextsForVariants.size() < MaxVisibilityHelp)
-    HelpTextsForVariants.push_back(
-        {std::vector<std::string>(MaxVisibilityPerHelp, "0"), ""});
-
   OS << ", (std::array<std::pair<std::array<unsigned, " << MaxVisibilityPerHelp
-     << ">, const char*>, " << MaxVisibilityHelp << ">{{ ";
-
-  auto VisibilityHelpEnd = HelpTextsForVariants.cend();
-  for (auto VisibilityHelp = HelpTextsForVariants.cbegin();
-       VisibilityHelp != VisibilityHelpEnd; ++VisibilityHelp) {
-    auto [Visibilities, Help] = *VisibilityHelp;
+     << ">, llvm::StringTable::Offset>, " << MaxVisibilityHelp << ">{{ ";
 
+  ListSeparator Sep;
+  for (const auto &[Visibilities, Help] : HelpTextsForVariants) {
     assert(Visibilities.size() <= MaxVisibilityPerHelp &&
            "Too many visibilities to store in an "
            "OptTable::HelpTextsForVariants entry");
-    OS << "{std::array<unsigned, " << MaxVisibilityPerHelp << ">{{"
+    OS << Sep << "{std::array<unsigned, " << MaxVisibilityPerHelp << ">{{"
        << llvm::interleaved(Visibilities) << "}}, ";
-
-    if (Help.size())
-      writeCstring(OS, Help);
-    else
-      OS << "nullptr";
+    writeStrTableOffset(OS, Table, Help);
     OS << "}";
-
-    if (std::next(VisibilityHelp) != VisibilityHelpEnd)
-      OS << ", ";
   }
+  // Unused entries are value-initialized.
+  for (size_t I = HelpTextsForVariants.size(); I < MaxVisibilityHelp; ++I)
+    OS << Sep << "{}";
   OS << " }})";
 }
 
@@ -307,10 +325,20 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
   // We can add all the prefixes via the union.
   for (const auto &Prefix : PrefixesUnion)
     Table.GetOrAddStringOffset(Prefix);
-  for (const Record &R : llvm::make_pointee_range(Groups))
+  for (const Record &R : llvm::make_pointee_range(Groups)) {
     Table.GetOrAddStringOffset(R.getValueAsString("Name"));
-  for (const Record &R : llvm::make_pointee_range(Opts))
+    Table.GetOrAddStringOffset(getHelpText(R));
+  }
+  for (const Record &R : llvm::make_pointee_range(Opts)) {
     Table.GetOrAddStringOffset(getOptionPrefixedName(R));
+    Table.GetOrAddStringOffset(getHelpText(R));
+    Table.GetOrAddStringOffset(getOptionalString(R, "MetaVarName"));
+    Table.GetOrAddStringOffset(getOptionalString(R, "Values"));
+    Table.GetOrAddStringOffset(getAliasArgsBlob(R));
+    for (const Record *VisibilityHelp :
+         R.getValueAsListOfDefs("HelpTextsForVariants"))
+      Table.GetOrAddStringOffset(VisibilityHelp->getValueAsString("Text"));
+  }
 
   // Dump string table.
   OS << "/////////\n";
@@ -401,16 +429,27 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
   OS << "/////////\n";
   OS << "// ValuesCode\n\n";
   OS << "#ifdef OPTTABLE_VALUES_CODE\n";
+  std::vector<const Record *> ValuesCodeOpts;
   for (const Record &R : llvm::make_pointee_range(Opts)) {
     // The option values, if any;
     if (!isa<UnsetInit>(R.getValueInit("ValuesCode"))) {
-      assert(isa<UnsetInit>(R.getValueInit("Values")) &&
-             "Cannot choose between Values and ValuesCode");
+      if (!isa<UnsetInit>(R.getValueInit("Values")))
+        PrintFatalError(R.getLoc(), "cannot set both Values and ValuesCode");
+      ValuesCodeOpts.push_back(&R);
       OS << "#define VALUES_CODE " << getOptionName(R) << "_Values\n";
       OS << R.getValueAsString("ValuesCode") << "\n";
       OS << "#undef VALUES_CODE\n";
     }
   }
+  // A function keeps these strings out of a relocated table. It names OPT_ 
IDs,
+  // so include this block after the option enum; a table that uses a 
diff erent
+  // ID prefix cannot use ValuesCode.
+  OS << "static llvm::StringRef getOptionValuesCode(unsigned ID) {\n";
+  OS << "  switch (ID) {\n";
+  for (const Record *R : ValuesCodeOpts)
+    OS << "  case OPT_" << getOptionName(*R) << ": return " << 
getOptionName(*R)
+       << "_Values;\n";
+  OS << "  }\n  return {};\n}\n";
   OS << "#endif\n";
 
   OS << "/////////\n";
@@ -425,7 +464,8 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
 
     // The option string offset.
     OS << ", ";
-    writeStrTableOffset(OS, Table, R.getValueAsString("Name"));
+    writeStrTableOffset(OS, Table, R.getValueAsString("Name"),
+                        /*EmitComment=*/true);
 
     // The option identifier name.
     OS << ", " << getOptionName(R);
@@ -441,25 +481,20 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
       OS << "INVALID";
 
     // The other option arguments (unused for groups).
-    OS << ", INVALID, nullptr, 0, 0, 0";
+    OS << ", INVALID, 0, 0, 0, 0";
 
     // The option help text.
-    if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
-      OS << ",\n";
-      OS << "       ";
-      writeCstring(OS, R.getValueAsString("HelpText"));
-    } else {
-      OS << ", nullptr";
-    }
+    OS << ", ";
+    writeStrTableOffset(OS, Table, getHelpText(R));
 
     // Not using Visibility specific text for group help.
-    emitHelpTextsForVariants(OS, {});
+    emitHelpTextsForVariants(OS, Table, {});
 
     // The option meta-variable name (unused).
-    OS << ", nullptr";
+    OS << ", 0";
 
     // The option Values (unused for groups).
-    OS << ", nullptr";
+    OS << ", 0";
 
     // The option SubCommandIDsOffset.
     OS << ", ";
@@ -477,7 +512,8 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
     OS << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())] << ", ";
 
     // The option prefixed name.
-    writeStrTableOffset(OS, Table, getOptionPrefixedName(R));
+    writeStrTableOffset(OS, Table, getOptionPrefixedName(R),
+                        /*EmitComment=*/true);
 
     // The option identifier name.
     OS << ", " << getOptionName(R);
@@ -505,19 +541,8 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
       OS << "INVALID";
 
     // The option alias arguments (if any).
-    // Emitted as a \0 separated list in a string, e.g. ["foo", "bar"]
-    // would become "foo\0bar\0". Note that the compiler adds an implicit
-    // terminating \0 at the end.
     OS << ", ";
-    std::vector<StringRef> AliasArgs = R.getValueAsListOfStrings("AliasArgs");
-    if (AliasArgs.size() == 0) {
-      OS << "nullptr";
-    } else {
-      OS << "\"";
-      for (StringRef AliasArg : AliasArgs)
-        OS << AliasArg << "\\0";
-      OS << "\"";
-    }
+    writeStrTableOffset(OS, Table, getAliasArgsBlob(R));
 
     // "Flags" for the option, such as HelpHidden and Render*
     OS << ", ";
@@ -552,13 +577,8 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
     OS << ", " << R.getValueAsInt("NumArgs");
 
     // The option help text.
-    if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
-      OS << ",\n";
-      OS << "       ";
-      writeCstring(OS, R.getValueAsString("HelpText"));
-    } else {
-      OS << ", nullptr";
-    }
+    OS << ", ";
+    writeStrTableOffset(OS, Table, getHelpText(R));
 
     std::vector<std::pair<std::vector<std::string>, StringRef>>
         HelpTextsForVariants;
@@ -574,23 +594,15 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
       HelpTextsForVariants.emplace_back(
           VisibilityNames, VisibilityHelp->getValueAsString("Text"));
     }
-    emitHelpTextsForVariants(OS, std::move(HelpTextsForVariants));
+    emitHelpTextsForVariants(OS, Table, HelpTextsForVariants);
 
     // The option meta-variable name.
     OS << ", ";
-    if (!isa<UnsetInit>(R.getValueInit("MetaVarName")))
-      writeCstring(OS, R.getValueAsString("MetaVarName"));
-    else
-      OS << "nullptr";
+    writeStrTableOffset(OS, Table, getOptionalString(R, "MetaVarName"));
 
     // The option Values. Used for shell autocompletion.
     OS << ", ";
-    if (!isa<UnsetInit>(R.getValueInit("Values")))
-      writeCstring(OS, R.getValueAsString("Values"));
-    else if (!isa<UnsetInit>(R.getValueInit("ValuesCode")))
-      OS << getOptionName(R) << "_Values";
-    else
-      OS << "nullptr";
+    writeStrTableOffset(OS, Table, getOptionalString(R, "Values"));
 
     // The option SubCommandIDsOffset.
     OS << ", ";


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to