[Lldb-commits] [clang] [lld] [lldb] [llvm] Rework the `Option` library to reduce dynamic relocations (PR #119198)

Chandler Carruth via lldb-commits lldb-commits at lists.llvm.org
Mon Dec 9 02:56:56 PST 2024


https://github.com/chandlerc created https://github.com/llvm/llvm-project/pull/119198

Apologies for the large change, I looked for ways to break this up and all of the ones I saw added real complexity. This change focuses on the option's prefixed names and the array of prefixes. These are present in every option and the dominant source of dynamic relocations for PIE or PIC users of LLVM and Clang tooling. In some cases, 100s or 1000s of them for the Clang driver which has a huge number of options.

This PR addresses this by building a string table and a prefixes table that can be referenced with indices rather than pointers that require dynamic relocations. This removes almost 7k dynmaic relocations from the `clang` binary, roughly 8% of the remaining dynmaic relocations outside of vtables. For busy-boxing use cases where many different option tables are linked into the same binary, the savings add up a bit more.

The string table is a straightforward mechanism, but the prefixes required some subtlety. They are encoded in a Pascal-string fashion with a size followed by a sequence of offsets. This works relatively well for the small realistic prefixes arrays in use.

Lots of code has to change in order to land this though: both all the option library code has to be updated to use the string table and prefixes table, and all the users of the options library have to be updated to correctly instantiate the objects.

Note, I've successfully built and tested `check-{llvm,clang,lld}` with this change, but had to make edits beyond that. I've ended up needing to modify LLDB to reflect these changes, but have not been able to build and test it fully. The relevant binaries build, but `check-lldb` currently hits unrelated errors for me blocking any progress in checking those changes.

>From fa3fc1aaf2976ebeb3307dffcf36ea33b57462a0 Mon Sep 17 00:00:00 2001
From: Chandler Carruth <chandlerc at gmail.com>
Date: Fri, 6 Dec 2024 09:47:03 +0000
Subject: [PATCH] Rework the `Option` library to reduce dynamic relocations

Apologies for the large change, I looked for ways to break this up and
all of the ones I saw added real complexity. This change focuses on the
option's prefixed names and the array of prefixes. These are present in
every option and the dominant source of dynamic relocations for PIE or
PIC users of LLVM and Clang tooling. In some cases, 100s or 1000s of
them for the Clang driver which has a huge number of options.

This PR addresses this by building a string table and a prefixes table
that can be referenced with indices rather than pointers that require
dynamic relocations. This removes almost 7k dynmaic relocations from the
`clang` binary, roughly 8% of the remaining dynmaic relocations outside
of vtables. For busy-boxing use cases where many different option tables
are linked into the same binary, the savings add up a bit more.

The string table is a straightforward mechanism, but the prefixes
required some subtlety. They are encoded in a Pascal-string fashion with
a size followed by a sequence of offsets. This works relatively well for
the small realistic prefixes arrays in use.

Lots of code has to change in order to land this though: both all the
option library code has to be updated to use the string table and
prefixes table, and all the users of the options library have to be
updated to correctly instantiate the objects.

Note, I've successfully built and tested `check-{llvm,clang,lld}` with
this change, but had to make edits beyond that. I've ended up needing to
modify LLDB to reflect these changes, but have not been able to build
and test it fully. The relevant binaries build, but `check-lldb`
currently hits unrelated errors for me blocking any progress in checking
those changes.
---
 clang/lib/Driver/DriverOptions.cpp            |  23 ++-
 clang/lib/Frontend/CompilerInvocation.cpp     |  63 ++++---
 clang/tools/clang-installapi/Options.cpp      |  42 ++---
 .../ClangLinkerWrapper.cpp                    |  14 +-
 .../ClangNVLinkWrapper.cpp                    |  14 +-
 clang/tools/clang-scan-deps/ClangScanDeps.cpp |  14 +-
 .../clang-sycl-linker/ClangSYCLLinker.cpp     |  14 +-
 lld/COFF/DriverUtils.cpp                      |  14 +-
 lld/ELF/DriverUtils.cpp                       |  14 +-
 lld/MachO/DriverUtils.cpp                     |  14 +-
 lld/MinGW/Driver.cpp                          |  16 +-
 lld/wasm/Driver.cpp                           |  15 +-
 .../Platform/MacOSX/PlatformDarwin.cpp        |   8 +-
 lldb/tools/driver/Driver.cpp                  |  14 +-
 lldb/tools/lldb-dap/lldb-dap.cpp              |  15 +-
 lldb/tools/lldb-server/lldb-gdbserver.cpp     |  14 +-
 llvm/include/llvm/Option/OptTable.h           | 144 +++++++++++-----
 llvm/include/llvm/Option/Option.h             |  14 +-
 .../JITLink/COFFDirectiveParser.cpp           |  24 ++-
 llvm/lib/Option/OptTable.cpp                  | 155 ++++++++++--------
 llvm/lib/Option/Option.cpp                    |   9 +-
 .../llvm-dlltool/DlltoolDriver.cpp            |  15 +-
 llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp   |  15 +-
 llvm/tools/dsymutil/dsymutil.cpp              |  14 +-
 llvm/tools/llvm-cgdata/llvm-cgdata.cpp        |  14 +-
 llvm/tools/llvm-cvtres/llvm-cvtres.cpp        |  15 +-
 llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp      |  14 +-
 .../llvm-debuginfod-find.cpp                  |  14 +-
 .../tools/llvm-debuginfod/llvm-debuginfod.cpp |  14 +-
 llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp  |  14 +-
 llvm/tools/llvm-dwp/llvm-dwp.cpp              |  14 +-
 llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp    |  14 +-
 llvm/tools/llvm-ifs/llvm-ifs.cpp              |  14 +-
 .../llvm-libtool-darwin.cpp                   |  14 +-
 llvm/tools/llvm-lipo/llvm-lipo.cpp            |  15 +-
 llvm/tools/llvm-ml/llvm-ml.cpp                |  15 +-
 llvm/tools/llvm-mt/llvm-mt.cpp                |  15 +-
 llvm/tools/llvm-nm/llvm-nm.cpp                |  14 +-
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    |  59 ++++---
 llvm/tools/llvm-objdump/llvm-objdump.cpp      |  39 +++--
 llvm/tools/llvm-rc/llvm-rc.cpp                |  32 ++--
 llvm/tools/llvm-readobj/llvm-readobj.cpp      |  14 +-
 llvm/tools/llvm-readtapi/llvm-readtapi.cpp    |  14 +-
 llvm/tools/llvm-size/llvm-size.cpp            |  16 +-
 llvm/tools/llvm-strings/llvm-strings.cpp      |  14 +-
 .../tools/llvm-symbolizer/llvm-symbolizer.cpp |  14 +-
 .../llvm-tli-checker/llvm-tli-checker.cpp     |  14 +-
 llvm/tools/sancov/sancov.cpp                  |  14 +-
 .../Option/OptionMarshallingTest.cpp          |  24 ++-
 llvm/unittests/Option/OptionParsingTest.cpp   |  25 ++-
 llvm/utils/TableGen/OptionParserEmitter.cpp   | 115 ++++++++-----
 51 files changed, 767 insertions(+), 525 deletions(-)

diff --git a/clang/lib/Driver/DriverOptions.cpp b/clang/lib/Driver/DriverOptions.cpp
index 053e7f1c6404fe..cde1f8989935b0 100644
--- a/clang/lib/Driver/DriverOptions.cpp
+++ b/clang/lib/Driver/DriverOptions.cpp
@@ -14,24 +14,21 @@ using namespace clang::driver;
 using namespace clang::driver::options;
 using namespace llvm::opt;
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "clang/Driver/Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 #define OPTTABLE_VALUES_CODE
 #include "clang/Driver/Options.inc"
 #undef OPTTABLE_VALUES_CODE
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
-  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "clang/Driver/Options.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
-static constexpr const llvm::StringLiteral PrefixTable_init[] =
-#define PREFIX_UNION(VALUES) VALUES
+#define OPTTABLE_PREFIXES_UNION_CODE
 #include "clang/Driver/Options.inc"
-#undef PREFIX_UNION
-    ;
-static constexpr const llvm::ArrayRef<llvm::StringLiteral>
-    PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1);
+#undef OPTTABLE_PREFIXES_UNION_CODE
 
 static constexpr OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -43,7 +40,9 @@ namespace {
 
 class DriverOptTable : public PrecomputedOptTable {
 public:
-  DriverOptTable() : PrecomputedOptTable(InfoTable, PrefixTable) {}
+  DriverOptTable()
+      : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                            OptionPrefixesUnion) {}
 };
 }
 
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 98136b7a455d9c..23906d5c06d380 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -277,6 +277,14 @@ CowCompilerInvocation::getMutPreprocessorOutputOpts() {
 
 using ArgumentConsumer = CompilerInvocation::ArgumentConsumer;
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "clang/Driver/Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+static llvm::StringRef lookupStrInTable(unsigned Offset) {
+  return &OptionStrTable[Offset];
+}
+
 #define SIMPLE_ENUM_VALUE_TABLE
 #include "clang/Driver/Options.inc"
 #undef SIMPLE_ENUM_VALUE_TABLE
@@ -303,6 +311,11 @@ static std::optional<bool> normalizeSimpleNegativeFlag(OptSpecifier Opt,
 /// denormalizeSimpleFlags never looks at it. Avoid bloating compile-time with
 /// unnecessary template instantiations and just ignore it with a variadic
 /// argument.
+static void denormalizeSimpleFlag(ArgumentConsumer Consumer,
+                                  unsigned SpellingOffset, Option::OptionClass,
+                                  unsigned, /*T*/...) {
+  Consumer(lookupStrInTable(SpellingOffset));
+}
 static void denormalizeSimpleFlag(ArgumentConsumer Consumer,
                                   const Twine &Spelling, Option::OptionClass,
                                   unsigned, /*T*/...) {
@@ -343,10 +356,10 @@ static auto makeBooleanOptionNormalizer(bool Value, bool OtherValue,
 }
 
 static auto makeBooleanOptionDenormalizer(bool Value) {
-  return [Value](ArgumentConsumer Consumer, const Twine &Spelling,
+  return [Value](ArgumentConsumer Consumer, unsigned SpellingOffset,
                  Option::OptionClass, unsigned, bool KeyPath) {
     if (KeyPath == Value)
-      Consumer(Spelling);
+      Consumer(lookupStrInTable(SpellingOffset));
   };
 }
 
@@ -371,6 +384,14 @@ static void denormalizeStringImpl(ArgumentConsumer Consumer,
   }
 }
 
+template <typename T>
+static void
+denormalizeString(ArgumentConsumer Consumer, unsigned SpellingOffset,
+                  Option::OptionClass OptClass, unsigned TableIndex, T Value) {
+  denormalizeStringImpl(Consumer, lookupStrInTable(SpellingOffset), OptClass,
+                        TableIndex, Twine(Value));
+}
+
 template <typename T>
 static void denormalizeString(ArgumentConsumer Consumer, const Twine &Spelling,
                               Option::OptionClass OptClass, unsigned TableIndex,
@@ -417,14 +438,14 @@ static std::optional<unsigned> normalizeSimpleEnum(OptSpecifier Opt,
 }
 
 static void denormalizeSimpleEnumImpl(ArgumentConsumer Consumer,
-                                      const Twine &Spelling,
+                                      unsigned SpellingOffset,
                                       Option::OptionClass OptClass,
                                       unsigned TableIndex, unsigned Value) {
   assert(TableIndex < SimpleEnumValueTablesSize);
   const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex];
   if (auto MaybeEnumVal = findValueTableByValue(Table, Value)) {
-    denormalizeString(Consumer, Spelling, OptClass, TableIndex,
-                      MaybeEnumVal->Name);
+    denormalizeString(Consumer, lookupStrInTable(SpellingOffset), OptClass,
+                      TableIndex, MaybeEnumVal->Name);
   } else {
     llvm_unreachable("The simple enum value was not correctly defined in "
                      "the tablegen option description");
@@ -433,11 +454,11 @@ static void denormalizeSimpleEnumImpl(ArgumentConsumer Consumer,
 
 template <typename T>
 static void denormalizeSimpleEnum(ArgumentConsumer Consumer,
-                                  const Twine &Spelling,
+                                  unsigned SpellingOffset,
                                   Option::OptionClass OptClass,
                                   unsigned TableIndex, T Value) {
-  return denormalizeSimpleEnumImpl(Consumer, Spelling, OptClass, TableIndex,
-                                   static_cast<unsigned>(Value));
+  return denormalizeSimpleEnumImpl(Consumer, SpellingOffset, OptClass,
+                                   TableIndex, static_cast<unsigned>(Value));
 }
 
 static std::optional<std::string> normalizeString(OptSpecifier Opt,
@@ -473,7 +494,7 @@ normalizeStringVector(OptSpecifier Opt, int, const ArgList &Args,
 }
 
 static void denormalizeStringVector(ArgumentConsumer Consumer,
-                                    const Twine &Spelling,
+                                    unsigned SpellingOffset,
                                     Option::OptionClass OptClass,
                                     unsigned TableIndex,
                                     const std::vector<std::string> &Values) {
@@ -487,15 +508,16 @@ static void denormalizeStringVector(ArgumentConsumer Consumer,
         CommaJoinedValue.append(Value);
       }
     }
-    denormalizeString(Consumer, Spelling, Option::OptionClass::JoinedClass,
-                      TableIndex, CommaJoinedValue);
+    denormalizeString(Consumer, SpellingOffset,
+                      Option::OptionClass::JoinedClass, TableIndex,
+                      CommaJoinedValue);
     break;
   }
   case Option::JoinedClass:
   case Option::SeparateClass:
   case Option::JoinedOrSeparateClass:
     for (const std::string &Value : Values)
-      denormalizeString(Consumer, Spelling, OptClass, TableIndex, Value);
+      denormalizeString(Consumer, SpellingOffset, OptClass, TableIndex, Value);
     break;
   default:
     llvm_unreachable("Cannot denormalize an option with option class "
@@ -532,10 +554,11 @@ static T extractMaskValue(T KeyPath) {
 }
 
 #define PARSE_OPTION_WITH_MARSHALLING(                                         \
-    ARGS, DIAGS, PREFIX_TYPE, SPELLING, ID, KIND, GROUP, ALIAS, ALIASARGS,     \
-    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
-    SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK,          \
-    IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX)   \
+    ARGS, DIAGS, PREFIX_TYPE, SPELLING_OFFSET, ID, KIND, GROUP, ALIAS,         \
+    ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,       \
+    METAVAR, VALUES, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE,        \
+    IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, \
+    TABLE_INDEX)                                                               \
   if ((VISIBILITY) & options::CC1Option) {                                     \
     KEYPATH = MERGER(KEYPATH, DEFAULT_VALUE);                                  \
     if (IMPLIED_CHECK)                                                         \
@@ -549,8 +572,8 @@ static T extractMaskValue(T KeyPath) {
 // Capture the extracted value as a lambda argument to avoid potential issues
 // with lifetime extension of the reference.
 #define GENERATE_OPTION_WITH_MARSHALLING(                                      \
-    CONSUMER, PREFIX_TYPE, SPELLING, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
-    VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES,        \
+    CONSUMER, PREFIX_TYPE, SPELLING_OFFSET, ID, KIND, GROUP, ALIAS, ALIASARGS, \
+    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
     SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK,          \
     IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX)   \
   if ((VISIBILITY) & options::CC1Option) {                                     \
@@ -559,8 +582,8 @@ static T extractMaskValue(T KeyPath) {
           (Extracted !=                                                        \
            static_cast<decltype(KEYPATH)>((IMPLIED_CHECK) ? (IMPLIED_VALUE)    \
                                                           : (DEFAULT_VALUE)))) \
-        DENORMALIZER(CONSUMER, SPELLING, Option::KIND##Class, TABLE_INDEX,     \
-                     Extracted);                                               \
+        DENORMALIZER(CONSUMER, SPELLING_OFFSET, Option::KIND##Class,           \
+                     TABLE_INDEX, Extracted);                                  \
     }(EXTRACTOR(KEYPATH));                                                     \
   }
 
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index 3fa79636de5d75..8a2c3463189fa9 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -31,41 +31,21 @@ namespace drv = clang::driver::options;
 namespace clang {
 namespace installapi {
 
-/// Create prefix string literals used in InstallAPIOpts.td.
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
-  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "InstallAPIOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
 
-static constexpr const llvm::StringLiteral PrefixTable_init[] =
-#define PREFIX_UNION(VALUES) VALUES
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "InstallAPIOpts.inc"
-#undef PREFIX_UNION
-    ;
-static constexpr const ArrayRef<StringLiteral>
-    PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1);
+#undef OPTTABLE_PREFIXES_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_UNION_CODE
+#include "InstallAPIOpts.inc"
+#undef OPTTABLE_PREFIXES_UNION_CODE
 
 /// Create table mapping all options defined in InstallAPIOpts.td.
 static constexpr OptTable::Info InfoTable[] = {
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,         \
-               VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR,     \
-               VALUES)                                                         \
-  {PREFIX,                                                                     \
-   NAME,                                                                       \
-   HELPTEXT,                                                                   \
-   HELPTEXTSFORVARIANTS,                                                       \
-   METAVAR,                                                                    \
-   OPT_##ID,                                                                   \
-   Option::KIND##Class,                                                        \
-   PARAM,                                                                      \
-   FLAGS,                                                                      \
-   VISIBILITY,                                                                 \
-   OPT_##GROUP,                                                                \
-   OPT_##ALIAS,                                                                \
-   ALIASARGS,                                                                  \
-   VALUES},
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
 #include "InstallAPIOpts.inc"
 #undef OPTION
 };
@@ -75,7 +55,9 @@ namespace {
 /// \brief Create OptTable class for parsing actual command line arguments.
 class DriverOptTable : public opt::PrecomputedOptTable {
 public:
-  DriverOptTable() : PrecomputedOptTable(InfoTable, PrefixTable) {}
+  DriverOptTable()
+      : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                            OptionPrefixesUnion) {}
 };
 
 } // end anonymous namespace.
diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index ebafd7eb7774ec..fae32a3503c185 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -174,12 +174,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "LinkerWrapperOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "LinkerWrapperOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -189,7 +190,8 @@ static constexpr OptTable::Info InfoTable[] = {
 
 class WrapperOptTable : public opt::GenericOptTable {
 public:
-  WrapperOptTable() : opt::GenericOptTable(InfoTable) {}
+  WrapperOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 
 const OptTable &getOptTable() {
diff --git a/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp b/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp
index bc191afdca739d..faf73a7c2f1938 100644
--- a/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp
+++ b/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp
@@ -109,12 +109,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "NVLinkOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "NVLinkOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -124,7 +125,8 @@ static constexpr OptTable::Info InfoTable[] = {
 
 class WrapperOptTable : public opt::GenericOptTable {
 public:
-  WrapperOptTable() : opt::GenericOptTable(InfoTable) {}
+  WrapperOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 
 const OptTable &getOptTable() {
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 58b56dcfd3bece..bd36181fca3f31 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -50,12 +50,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  constexpr llvm::StringLiteral NAME##_init[] = VALUE;                         \
-  constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                          \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 const llvm::opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -65,7 +66,8 @@ const llvm::opt::OptTable::Info InfoTable[] = {
 
 class ScanDepsOptTable : public llvm::opt::GenericOptTable {
 public:
-  ScanDepsOptTable() : GenericOptTable(InfoTable) {
+  ScanDepsOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
index 076458a275d986..2bcb3757d49d08 100644
--- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
+++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
@@ -88,12 +88,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "SYCLLinkOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "SYCLLinkOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -103,7 +104,8 @@ static constexpr OptTable::Info InfoTable[] = {
 
 class LinkerOptTable : public opt::GenericOptTable {
 public:
-  LinkerOptTable() : opt::GenericOptTable(InfoTable) {}
+  LinkerOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 
 const OptTable &getOptTable() {
diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index c1b3272a1f49ea..1148be09fb10cc 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -845,13 +845,14 @@ MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
 
 // Create OptTable
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 // Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
-  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 // Create table mapping all options defined in Options.td
 static constexpr llvm::opt::OptTable::Info infoTable[] = {
@@ -860,7 +861,8 @@ static constexpr llvm::opt::OptTable::Info infoTable[] = {
 #undef OPTION
 };
 
-COFFOptTable::COFFOptTable() : GenericOptTable(infoTable, true) {}
+COFFOptTable::COFFOptTable()
+    : GenericOptTable(OptionStrTable, OptionPrefixesTable, infoTable, true) {}
 
 // Set color diagnostics according to --color-diagnostics={auto,always,never}
 // or --no-color-diagnostics flags.
diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp
index 4c88723f090d08..6d027c529c19e9 100644
--- a/lld/ELF/DriverUtils.cpp
+++ b/lld/ELF/DriverUtils.cpp
@@ -33,13 +33,14 @@ using namespace lld::elf;
 
 // Create OptTable
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 // Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 // Create table mapping all options defined in Options.td
 static constexpr opt::OptTable::Info optInfo[] = {
@@ -48,7 +49,8 @@ static constexpr opt::OptTable::Info optInfo[] = {
 #undef OPTION
 };
 
-ELFOptTable::ELFOptTable() : GenericOptTable(optInfo) {}
+ELFOptTable::ELFOptTable()
+    : GenericOptTable(OptionStrTable, OptionPrefixesTable, optInfo) {}
 
 // Set color diagnostics according to --color-diagnostics={auto,always,never}
 // or --no-color-diagnostics flags.
diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp
index 308c5eaf8c3178..69d023c23b3c79 100644
--- a/lld/MachO/DriverUtils.cpp
+++ b/lld/MachO/DriverUtils.cpp
@@ -34,13 +34,14 @@ using namespace llvm::sys;
 using namespace lld;
 using namespace lld::macho;
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 // Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 // Create table mapping all options defined in Options.td
 static constexpr OptTable::Info optInfo[] = {
@@ -65,7 +66,8 @@ static constexpr OptTable::Info optInfo[] = {
 #undef OPTION
 };
 
-MachOOptTable::MachOOptTable() : GenericOptTable(optInfo) {}
+MachOOptTable::MachOOptTable()
+    : GenericOptTable(OptionStrTable, OptionPrefixesTable, optInfo) {}
 
 // Set color diagnostics according to --color-diagnostics={auto,always,never}
 // or --no-color-diagnostics flags.
diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index b4371b5b2240d2..a77d86b443a6c3 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -58,13 +58,13 @@ enum {
 #undef OPTION
 };
 
-// Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
-  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 // Create table mapping all options defined in Options.td
 static constexpr opt::OptTable::Info infoTable[] = {
@@ -92,7 +92,9 @@ static constexpr opt::OptTable::Info infoTable[] = {
 namespace {
 class MinGWOptTable : public opt::GenericOptTable {
 public:
-  MinGWOptTable() : opt::GenericOptTable(infoTable, false) {}
+  MinGWOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, infoTable,
+                             false) {}
   opt::InputArgList parse(ArrayRef<const char *> argv);
 };
 } // namespace
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 37a0156c728f6f..00b5c82d9c7777 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -139,13 +139,13 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
   return errorCount() == 0;
 }
 
-// Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 // Create table mapping all options defined in Options.td
 static constexpr opt::OptTable::Info optInfo[] = {
@@ -173,7 +173,8 @@ static constexpr opt::OptTable::Info optInfo[] = {
 namespace {
 class WasmOptTable : public opt::GenericOptTable {
 public:
-  WasmOptTable() : opt::GenericOptTable(optInfo) {}
+  WasmOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, optInfo) {}
   opt::InputArgList parse(ArrayRef<const char *> argv);
 };
 } // namespace
diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index 8baf3a8d60c373..2a36f95c94d0ce 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -54,6 +54,10 @@
 using namespace lldb;
 using namespace lldb_private;
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "clang/Driver/Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 static Status ExceptionMaskValidator(const char *string, void *unused) {
   Status error;
   llvm::StringRef str_ref(string);
@@ -1078,8 +1082,8 @@ void PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(
   // clang has no version-min clang flag for XROS.
   if (!version.empty() && sdk_type != XcodeSDK::Type::Linux &&
       sdk_type != XcodeSDK::Type::XROS) {
-#define OPTION(PREFIX, NAME, VAR, ...)                                         \
-  llvm::StringRef opt_##VAR = NAME;                                            \
+#define OPTION(PREFIX_OFFSET, NAME_OFFSET, VAR, ...)                           \
+  llvm::StringRef opt_##VAR = &OptionStrTable[NAME_OFFSET];                    \
   (void)opt_##VAR;
 #include "clang/Driver/Options.inc"
 #undef OPTION
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index afb1a1ff95c3a1..98c3643f75c97b 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -61,12 +61,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -76,7 +77,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class LLDBOptTable : public opt::GenericOptTable {
 public:
-  LLDBOptTable() : opt::GenericOptTable(InfoTable) {}
+  LLDBOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // namespace
 
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 3bfc578806021e..7e8f7b5f6df679 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -92,12 +92,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
-  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr llvm::opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -106,7 +107,9 @@ static constexpr llvm::opt::OptTable::Info InfoTable[] = {
 };
 class LLDBDAPOptTable : public llvm::opt::GenericOptTable {
 public:
-  LLDBDAPOptTable() : llvm::opt::GenericOptTable(InfoTable, true) {}
+  LLDBDAPOptTable()
+      : llvm::opt::GenericOptTable(OptionStrTable, OptionPrefixesTable,
+                                   InfoTable, true) {}
 };
 
 typedef void (*RequestCallback)(const llvm::json::Object &command);
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index ed10c161b6b2f6..fec868b1fa9a18 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -291,12 +291,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  constexpr llvm::StringLiteral NAME##_init[] = VALUE;                         \
-  constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                          \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "LLGSOptions.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "LLGSOptions.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -306,7 +307,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class LLGSOptTable : public opt::GenericOptTable {
 public:
-  LLGSOptTable() : opt::GenericOptTable(InfoTable) {}
+  LLGSOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 
   void PrintHelp(llvm::StringRef Name) {
     std::string Usage =
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index 8fabc78d81aedf..22ae8f04528d4d 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -53,10 +53,8 @@ class OptTable {
 public:
   /// Entry for a single option instance in the option data table.
   struct Info {
-    /// A null terminated array of prefix strings to apply to name while
-    /// matching.
-    ArrayRef<StringLiteral> Prefixes;
-    StringLiteral PrefixedName;
+    unsigned PrefixesOffset;
+    unsigned PrefixedNameOffset;
     const char *HelpText;
     // Help text for specific visibilities. A list of pairs, where each pair
     // is a list of visibilities and a specific help string for those
@@ -80,15 +78,56 @@ class OptTable {
     const char *AliasArgs;
     const char *Values;
 
-    StringRef getName() const {
-      unsigned PrefixLength = Prefixes.empty() ? 0 : Prefixes[0].size();
-      return PrefixedName.drop_front(PrefixLength);
+    bool hasNoPrefix() const { return PrefixesOffset == 0; }
+
+    unsigned getNumPrefixes(ArrayRef<unsigned> PrefixesTable) const {
+      return PrefixesTable[PrefixesOffset];
+    }
+
+    ArrayRef<unsigned>
+    getPrefixOffsets(ArrayRef<unsigned> PrefixesTable) const {
+      return hasNoPrefix() ? ArrayRef<unsigned>()
+                           : PrefixesTable.slice(PrefixesOffset + 1,
+                                                 getNumPrefixes(PrefixesTable));
+    }
+
+    void appendPrefixes(const char *StrTable, ArrayRef<unsigned> PrefixesTable,
+                        SmallVectorImpl<StringRef> &Prefixes) const {
+      for (unsigned PrefixOffset : getPrefixOffsets(PrefixesTable))
+        Prefixes.push_back(&StrTable[PrefixOffset]);
+    }
+
+    StringRef getPrefix(const char *StrTable, ArrayRef<unsigned> PrefixesTable,
+                        unsigned PrefixIndex) const {
+      return &StrTable[getPrefixOffsets(PrefixesTable)[PrefixIndex]];
+    }
+
+    StringRef getPrefixedName(const char *StrTable) const {
+      return &StrTable[PrefixedNameOffset];
+    }
+
+    StringRef getName(const char *StrTable,
+                      ArrayRef<unsigned> PrefixesTable) const {
+      unsigned PrefixLength =
+          hasNoPrefix() ? 0 : getPrefix(StrTable, PrefixesTable, 0).size();
+      return getPrefixedName(StrTable).drop_front(PrefixLength);
     }
   };
 
 private:
+  // A unified string table for these options. Individual strings are stored as
+  // null terminated C-strings at offsets within this table.
+  const char *StrTable;
+
+  // A table of different sets of prefixes. Each set starts with the number of
+  // prefixes in that set followed by that many offsets into the string table
+  // for each of the prefix strings. This is essentially a Pascal-string style
+  // encoding.
+  ArrayRef<unsigned> PrefixesTable;
+
   /// The option information table.
   ArrayRef<Info> OptionInfos;
+
   bool IgnoreCase;
   bool GroupedShortOptions = false;
   bool DashDashParsing = false;
@@ -102,12 +141,12 @@ class OptTable {
   /// special option like 'input' or 'unknown', and is not an option group).
   unsigned FirstSearchableIndex = 0;
 
-  /// The union of the first element of all option prefixes.
-  SmallString<8> PrefixChars;
-
   /// The union of all option prefixes. If an argument does not begin with
   /// one of these, it is an input.
-  virtual ArrayRef<StringLiteral> getPrefixesUnion() const = 0;
+  SmallVector<StringRef> PrefixesUnion;
+
+  /// The union of the first element of all option prefixes.
+  SmallString<8> PrefixChars;
 
 private:
   const Info &getInfo(OptSpecifier Opt) const {
@@ -122,7 +161,8 @@ class OptTable {
 protected:
   /// Initialize OptTable using Tablegen'ed OptionInfos. Child class must
   /// manually call \c buildPrefixChars once they are fully constructed.
-  OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase = false);
+  OptTable(const char *StrTable, ArrayRef<unsigned> PrefixesTable,
+           ArrayRef<Info> OptionInfos, bool IgnoreCase = false);
 
   /// Build (or rebuild) the PrefixChars member.
   void buildPrefixChars();
@@ -130,6 +170,12 @@ class OptTable {
 public:
   virtual ~OptTable();
 
+  /// Return the string table used for option names.
+  const char *getStrTable() const { return StrTable; }
+
+  /// Return the prefixes table used for option names.
+  ArrayRef<unsigned> getPrefixesTable() const { return PrefixesTable; }
+
   /// Return the total number of option classes.
   unsigned getNumOptions() const { return OptionInfos.size(); }
 
@@ -141,7 +187,19 @@ class OptTable {
 
   /// Lookup the name of the given option.
   StringRef getOptionName(OptSpecifier id) const {
-    return getInfo(id).getName();
+    return getInfo(id).getName(StrTable, PrefixesTable);
+  }
+
+  /// Lookup the prefix of the given option.
+  StringRef getOptionPrefix(OptSpecifier id) const {
+    const Info &I = getInfo(id);
+    return I.hasNoPrefix() ? StringRef()
+                           : I.getPrefix(StrTable, PrefixesTable, 0);
+  }
+
+  /// Lookup the prefixed name of the given option.
+  StringRef getOptionPrefixedName(OptSpecifier id) const {
+    return getInfo(id).getPrefixedName(StrTable);
   }
 
   /// Get the kind of the given option.
@@ -353,28 +411,22 @@ class OptTable {
 
 /// Specialization of OptTable
 class GenericOptTable : public OptTable {
-  SmallVector<StringLiteral> PrefixesUnionBuffer;
-
 protected:
-  GenericOptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase = false);
-  ArrayRef<StringLiteral> getPrefixesUnion() const final {
-    return PrefixesUnionBuffer;
-  }
+  GenericOptTable(const char *StrTable, ArrayRef<unsigned> PrefixesTable,
+                  ArrayRef<Info> OptionInfos, bool IgnoreCase = false);
 };
 
 class PrecomputedOptTable : public OptTable {
-  ArrayRef<StringLiteral> PrefixesUnion;
-
 protected:
-  PrecomputedOptTable(ArrayRef<Info> OptionInfos,
-                      ArrayRef<StringLiteral> PrefixesTable,
+  PrecomputedOptTable(const char *StrTable, ArrayRef<unsigned> PrefixesTable,
+                      ArrayRef<Info> OptionInfos,
+                      ArrayRef<unsigned> PrefixesUnionOffsets,
                       bool IgnoreCase = false)
-      : OptTable(OptionInfos, IgnoreCase), PrefixesUnion(PrefixesTable) {
+      : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase) {
+    for (unsigned PrefixOffset : PrefixesUnionOffsets)
+      PrefixesUnion.push_back(&StrTable[PrefixOffset]);
     buildPrefixChars();
   }
-  ArrayRef<StringLiteral> getPrefixesUnion() const final {
-    return PrefixesUnion;
-  }
 };
 
 } // end namespace opt
@@ -382,31 +434,35 @@ class PrecomputedOptTable : public OptTable {
 } // end namespace llvm
 
 #define LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(                                       \
-    ID_PREFIX, PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS,       \
-    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES) \
+    ID_PREFIX, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS,  \
+    ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,       \
+    METAVAR, VALUES)                                                           \
   ID_PREFIX##ID
 
-#define LLVM_MAKE_OPT_ID(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS,        \
-                         ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT,        \
-                         HELPTEXTSFORVARIANTS, METAVAR, VALUES)                \
-  LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(                                             \
-      OPT_, PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,   \
-      VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUE)
+#define LLVM_MAKE_OPT_ID(PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND,      \
+                         GROUP, ALIAS, ALIASARGS, FLAGS, VISIBILITY, PARAM,    \
+                         HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES)      \
+  LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(OPT_, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, \
+                                  ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,    \
+                                  VISIBILITY, PARAM, HELPTEXT,                 \
+                                  HELPTEXTSFORVARIANTS, METAVAR, VALUE)
 
 #define LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(                                \
-    ID_PREFIX, PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS,       \
-    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES) \
+    ID_PREFIX, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS,  \
+    ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,       \
+    METAVAR, VALUES)                                                           \
   llvm::opt::OptTable::Info {                                                  \
-    PREFIX, PREFIXED_NAME, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR,            \
-        ID_PREFIX##ID, llvm::opt::Option::KIND##Class, PARAM, FLAGS,           \
+    PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, HELPTEXT, HELPTEXTSFORVARIANTS,     \
+        METAVAR, ID_PREFIX##ID, llvm::opt::Option::KIND##Class, PARAM, FLAGS,  \
         VISIBILITY, ID_PREFIX##GROUP, ID_PREFIX##ALIAS, ALIASARGS, VALUES      \
   }
 
-#define LLVM_CONSTRUCT_OPT_INFO(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, \
-                                ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, \
-                                HELPTEXTSFORVARIANTS, METAVAR, VALUES)         \
+#define LLVM_CONSTRUCT_OPT_INFO(                                               \
+    PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS, ALIASARGS,  \
+    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES) \
   LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(                                      \
-      OPT_, PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,   \
-      VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES)
+      OPT_, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS,     \
+      ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,     \
+      METAVAR, VALUES)
 
 #endif // LLVM_OPTION_OPTTABLE_H
diff --git a/llvm/include/llvm/Option/Option.h b/llvm/include/llvm/Option/Option.h
index 5d16fbdb6b77c8..a0563da15c8edf 100644
--- a/llvm/include/llvm/Option/Option.h
+++ b/llvm/include/llvm/Option/Option.h
@@ -100,7 +100,8 @@ class Option {
   /// Get the name of this option without any prefix.
   StringRef getName() const {
     assert(Info && "Must have a valid info!");
-    return Info->getName();
+    assert(Owner && "Must have a valid owner!");
+    return Owner->getOptionName(Info->ID);
   }
 
   const Option getGroup() const {
@@ -127,15 +128,16 @@ class Option {
 
   /// Get the default prefix for this option.
   StringRef getPrefix() const {
-    return Info->Prefixes.empty()
-               ? StringRef()
-               : static_cast<const StringRef &>(Info->Prefixes[0]);
+    assert(Info && "Must have a valid info!");
+    assert(Owner && "Must have a valid owner!");
+    return Owner->getOptionPrefix(Info->ID);
   }
 
   /// Get the name of this option with the default prefix.
-  StringLiteral getPrefixedName() const {
+  StringRef getPrefixedName() const {
     assert(Info && "Must have a valid info!");
-    return Info->PrefixedName;
+    assert(Owner && "Must have a valid owner!");
+    return Owner->getOptionPrefixedName(Info->ID);
   }
 
   /// Get the help text for this option.
diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.cpp b/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.cpp
index f23f3ed9406bdc..ecf5c0e519caec 100644
--- a/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.cpp
@@ -19,21 +19,17 @@ using namespace jitlink;
 
 #define DEBUG_TYPE "jitlink"
 
-// Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "COFFOptions.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
 
-static constexpr const StringLiteral PrefixTable_init[] =
-#define PREFIX_UNION(VALUES) VALUES
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "COFFOptions.inc"
-#undef PREFIX_UNION
-    ;
-static constexpr const ArrayRef<StringLiteral>
-    PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1);
+#undef OPTTABLE_PREFIXES_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_UNION_CODE
+#include "COFFOptions.inc"
+#undef OPTTABLE_PREFIXES_UNION_CODE
 
 // Create table mapping all options defined in COFFOptions.td
 using namespace llvm::opt;
@@ -46,7 +42,9 @@ static constexpr opt::OptTable::Info infoTable[] = {
 
 class COFFOptTable : public opt::PrecomputedOptTable {
 public:
-  COFFOptTable() : PrecomputedOptTable(infoTable, PrefixTable, true) {}
+  COFFOptTable()
+      : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, infoTable,
+                            OptionPrefixesUnion, true) {}
 };
 
 static COFFOptTable optTable;
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index 9fdafed39b8b6c..87e6f1f12364c2 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -31,46 +31,55 @@
 using namespace llvm;
 using namespace llvm::opt;
 
-namespace llvm::opt {
+namespace {
+struct OptNameLess {
+  const char *StrTable;
+  ArrayRef<unsigned> PrefixesTable;
+
+  explicit OptNameLess(const char *StrTable, ArrayRef<unsigned> PrefixesTable)
+      : StrTable(StrTable), PrefixesTable(PrefixesTable) {}
+
 #ifndef NDEBUG
-static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) {
-  if (&A == &B)
-    return false;
-
-  if (int Cmp = StrCmpOptionName(A.getName(), B.getName()))
-    return Cmp < 0;
-
-  // Note: we are converting ArrayRef<StringLiteral> to ArrayRef<StringRef>.
-  // In general, ArrayRef<SubClass> cannot be safely viewed as ArrayRef<Base>
-  // since sizeof(SubClass) may not be same as sizeof(Base). However in this
-  // case, sizeof(StringLiteral) is same as sizeof(StringRef), so this
-  // conversion is safe.
-  static_assert(sizeof(StringRef) == sizeof(StringLiteral));
-  ArrayRef<StringRef> APrefixes(A.Prefixes.data(), A.Prefixes.size());
-  ArrayRef<StringRef> BPrefixes(B.Prefixes.data(), B.Prefixes.size());
-
-  if (int Cmp = StrCmpOptionPrefixes(APrefixes, BPrefixes))
-    return Cmp < 0;
-
-  // Names are the same, check that classes are in order; exactly one
-  // should be joined, and it should succeed the other.
-  assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) &&
-         "Unexpected classes for options with same name.");
-  return B.Kind == Option::JoinedClass;
-}
+  inline bool operator()(const OptTable::Info &A,
+                         const OptTable::Info &B) const {
+    if (&A == &B)
+      return false;
+
+    if (int Cmp = StrCmpOptionName(A.getName(StrTable, PrefixesTable),
+                                   B.getName(StrTable, PrefixesTable)))
+      return Cmp < 0;
+
+    SmallVector<StringRef, 8> APrefixes, BPrefixes;
+    A.appendPrefixes(StrTable, PrefixesTable, APrefixes);
+    B.appendPrefixes(StrTable, PrefixesTable, BPrefixes);
+
+    if (int Cmp = StrCmpOptionPrefixes(APrefixes, BPrefixes))
+      return Cmp < 0;
+
+    // Names are the same, check that classes are in order; exactly one
+    // should be joined, and it should succeed the other.
+    assert(
+        ((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) &&
+        "Unexpected classes for options with same name.");
+    return B.Kind == Option::JoinedClass;
+  }
 #endif
 
-// Support lower_bound between info and an option name.
-static inline bool operator<(const OptTable::Info &I, StringRef Name) {
-  // Do not fallback to case sensitive comparison.
-  return StrCmpOptionName(I.getName(), Name, false) < 0;
-}
-} // namespace llvm::opt
+  // Support lower_bound between info and an option name.
+  inline bool operator()(const OptTable::Info &I, StringRef Name) const {
+    // Do not fallback to case sensitive comparison.
+    return StrCmpOptionName(I.getName(StrTable, PrefixesTable), Name, false) <
+           0;
+  }
+};
+} // namespace
 
 OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {}
 
-OptTable::OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase)
-    : OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) {
+OptTable::OptTable(const char *StrTable, ArrayRef<unsigned> PrefixesTable,
+                   ArrayRef<Info> OptionInfos, bool IgnoreCase)
+    : StrTable(StrTable), PrefixesTable(PrefixesTable),
+      OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) {
   // Explicitly zero initialize the error to work around a bug in array
   // value-initialization on MinGW with gcc 4.3.5.
 
@@ -102,7 +111,7 @@ OptTable::OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase)
 
   // Check that options are in order.
   for (unsigned i = FirstSearchableIndex + 1, e = getNumOptions(); i != e; ++i){
-    if (!(getInfo(i) < getInfo(i + 1))) {
+    if (!(OptNameLess(StrTable, PrefixesTable)(getInfo(i), getInfo(i + 1)))) {
       getOption(i).dump();
       getOption(i + 1).dump();
       llvm_unreachable("Options are not in order!");
@@ -115,7 +124,7 @@ void OptTable::buildPrefixChars() {
   assert(PrefixChars.empty() && "rebuilding a non-empty prefix char");
 
   // Build prefix chars.
-  for (const StringLiteral &Prefix : getPrefixesUnion()) {
+  for (StringRef Prefix : PrefixesUnion) {
     for (char C : Prefix)
       if (!is_contained(PrefixChars, C))
         PrefixChars.push_back(C);
@@ -132,7 +141,7 @@ const Option OptTable::getOption(OptSpecifier Opt) const {
   return Option(&getInfo(id), this);
 }
 
-static bool isInput(const ArrayRef<StringLiteral> &Prefixes, StringRef Arg) {
+static bool isInput(const ArrayRef<StringRef> &Prefixes, StringRef Arg) {
   if (Arg == "-")
     return true;
   for (const StringRef &Prefix : Prefixes)
@@ -142,25 +151,32 @@ static bool isInput(const ArrayRef<StringLiteral> &Prefixes, StringRef Arg) {
 }
 
 /// \returns Matched size. 0 means no match.
-static unsigned matchOption(const OptTable::Info *I, StringRef Str,
+static unsigned matchOption(const char *StrTable,
+                            ArrayRef<unsigned> PrefixesTable,
+                            const OptTable::Info *I, StringRef Str,
                             bool IgnoreCase) {
-  for (auto Prefix : I->Prefixes) {
+  StringRef Name = I->getName(StrTable, PrefixesTable);
+  for (unsigned PrefixOffset : I->getPrefixOffsets(PrefixesTable)) {
+    StringRef Prefix = &StrTable[PrefixOffset];
     if (Str.starts_with(Prefix)) {
       StringRef Rest = Str.substr(Prefix.size());
-      bool Matched = IgnoreCase ? Rest.starts_with_insensitive(I->getName())
-                                : Rest.starts_with(I->getName());
+      bool Matched = IgnoreCase ? Rest.starts_with_insensitive(Name)
+                                : Rest.starts_with(Name);
       if (Matched)
-        return Prefix.size() + StringRef(I->getName()).size();
+        return Prefix.size() + Name.size();
     }
   }
   return 0;
 }
 
 // Returns true if one of the Prefixes + In.Names matches Option
-static bool optionMatches(const OptTable::Info &In, StringRef Option) {
-  for (auto Prefix : In.Prefixes)
-    if (Option.ends_with(In.getName()))
-      if (Option.slice(0, Option.size() - In.getName().size()) == Prefix)
+static bool optionMatches(const char *StrTable,
+                          ArrayRef<unsigned> PrefixesTable,
+                          const OptTable::Info &In, StringRef Option) {
+  StringRef Name = In.getName(StrTable, PrefixesTable);
+  if (Option.consume_back(Name))
+    for (unsigned PrefixOffset : In.getPrefixOffsets(PrefixesTable))
+      if (Option == &StrTable[PrefixOffset])
         return true;
   return false;
 }
@@ -173,7 +189,7 @@ 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(In, Option))
+    if (!In.Values || !optionMatches(StrTable, PrefixesTable, In, Option))
       continue;
 
     SmallVector<StringRef, 8> Candidates;
@@ -194,15 +210,17 @@ 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.Prefixes.empty() || (!In.HelpText && !In.GroupID))
+    if (In.hasNoPrefix() || (!In.HelpText && !In.GroupID))
       continue;
     if (!(In.Visibility & VisibilityMask))
       continue;
     if (In.Flags & DisableFlags)
       continue;
 
-    for (auto Prefix : In.Prefixes) {
-      std::string S = (Prefix + In.getName() + "\t").str();
+    StringRef Name = In.getName(StrTable, PrefixesTable);
+    for (unsigned PrefixOffset : In.getPrefixOffsets(PrefixesTable)) {
+      StringRef Prefix = &StrTable[PrefixOffset];
+      std::string S = (Twine(Prefix) + Name + "\t").str();
       if (In.HelpText)
         S += In.HelpText;
       if (StringRef(S).starts_with(Cur) && S != std::string(Cur) + "\t")
@@ -253,7 +271,7 @@ unsigned OptTable::internalFindNearest(
 
   for (const Info &CandidateInfo :
        ArrayRef<Info>(OptionInfos).drop_front(FirstSearchableIndex)) {
-    StringRef CandidateName = CandidateInfo.getName();
+    StringRef CandidateName = CandidateInfo.getName(StrTable, PrefixesTable);
 
     // We can eliminate some option prefix/name pairs as candidates right away:
     // * Ignore option candidates with empty names, such as "--", or names
@@ -267,7 +285,7 @@ unsigned OptTable::internalFindNearest(
 
     // * Ignore positional argument option candidates (which do not
     //   have prefixes).
-    if (CandidateInfo.Prefixes.empty())
+    if (CandidateInfo.hasNoPrefix())
       continue;
 
     // Now check if the candidate ends with a character commonly used when
@@ -286,7 +304,9 @@ unsigned OptTable::internalFindNearest(
     // Consider each possible prefix for each candidate to find the most
     // appropriate one. For example, if a user asks for "--helm", suggest
     // "--help" over "-help".
-    for (auto CandidatePrefix : CandidateInfo.Prefixes) {
+    for (unsigned CandidatePrefixOffset :
+         CandidateInfo.getPrefixOffsets(PrefixesTable)) {
+      StringRef CandidatePrefix = &StrTable[CandidatePrefixOffset];
       // If Candidate and NormalizedName have more than 'BestDistance'
       // characters of difference, no need to compute the edit distance, it's
       // going to be greater than BestDistance. Don't bother computing Candidate
@@ -332,19 +352,21 @@ std::unique_ptr<Arg> OptTable::parseOneArgGrouped(InputArgList &Args,
   // itself.
   const char *CStr = Args.getArgString(Index);
   StringRef Str(CStr);
-  if (isInput(getPrefixesUnion(), Str))
+  if (isInput(PrefixesUnion, Str))
     return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, CStr);
 
   const Info *End = OptionInfos.data() + OptionInfos.size();
   StringRef Name = Str.ltrim(PrefixChars);
   const Info *Start =
-      std::lower_bound(OptionInfos.data() + FirstSearchableIndex, End, Name);
+      std::lower_bound(OptionInfos.data() + FirstSearchableIndex, End, Name,
+                       OptNameLess(StrTable, PrefixesTable));
   const Info *Fallback = nullptr;
   unsigned Prev = Index;
 
   // Search for the option which matches Str.
   for (; Start != End; ++Start) {
-    unsigned ArgSize = matchOption(Start, Str, IgnoreCase);
+    unsigned ArgSize =
+        matchOption(StrTable, PrefixesTable, Start, Str, IgnoreCase);
     if (!ArgSize)
       continue;
 
@@ -417,7 +439,7 @@ std::unique_ptr<Arg> OptTable::internalParseOneArg(
 
   // Anything that doesn't start with PrefixesUnion is an input, as is '-'
   // itself.
-  if (isInput(getPrefixesUnion(), Str))
+  if (isInput(PrefixesUnion, Str))
     return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++,
                                  Str.data());
 
@@ -426,7 +448,8 @@ std::unique_ptr<Arg> OptTable::internalParseOneArg(
   StringRef Name = Str.ltrim(PrefixChars);
 
   // Search for the first next option which could be a prefix.
-  Start = std::lower_bound(Start, End, Name);
+  Start =
+      std::lower_bound(Start, End, Name, OptNameLess(StrTable, PrefixesTable));
 
   // Options are stored in sorted order, with '\0' at the end of the
   // alphabet. Since the only options which can accept a string must
@@ -440,7 +463,8 @@ std::unique_ptr<Arg> OptTable::internalParseOneArg(
     unsigned ArgSize = 0;
     // Scan for first option which is a proper prefix.
     for (; Start != End; ++Start)
-      if ((ArgSize = matchOption(Start, Str, IgnoreCase)))
+      if ((ArgSize =
+               matchOption(StrTable, PrefixesTable, Start, Str, IgnoreCase)))
         break;
     if (Start == End)
       break;
@@ -763,12 +787,15 @@ void OptTable::internalPrintHelp(
   OS.flush();
 }
 
-GenericOptTable::GenericOptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase)
-    : OptTable(OptionInfos, IgnoreCase) {
+GenericOptTable::GenericOptTable(const char *StrTable,
+                                 ArrayRef<unsigned> PrefixesTable,
+                                 ArrayRef<Info> OptionInfos, bool IgnoreCase)
+    : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase) {
 
-  std::set<StringLiteral> TmpPrefixesUnion;
+  std::set<StringRef> TmpPrefixesUnion;
   for (auto const &Info : OptionInfos.drop_front(FirstSearchableIndex))
-    TmpPrefixesUnion.insert(Info.Prefixes.begin(), Info.Prefixes.end());
-  PrefixesUnionBuffer.append(TmpPrefixesUnion.begin(), TmpPrefixesUnion.end());
+    for (unsigned PrefixOffset : Info.getPrefixOffsets(PrefixesTable))
+      TmpPrefixesUnion.insert(StringRef(&StrTable[PrefixOffset]));
+  PrefixesUnion.append(TmpPrefixesUnion.begin(), TmpPrefixesUnion.end());
   buildPrefixChars();
 }
diff --git a/llvm/lib/Option/Option.cpp b/llvm/lib/Option/Option.cpp
index ecb3e84b1da8bd..738f75bb41e68c 100644
--- a/llvm/lib/Option/Option.cpp
+++ b/llvm/lib/Option/Option.cpp
@@ -57,10 +57,13 @@ void Option::print(raw_ostream &O, bool AddNewLine) const {
 #undef P
   }
 
-  if (!Info->Prefixes.empty()) {
+  if (!Info->hasNoPrefix()) {
     O << " Prefixes:[";
-    for (size_t I = 0, N = Info->Prefixes.size(); I != N; ++I)
-      O << '"' << Info->Prefixes[I] << (I == N - 1 ? "\"" : "\", ");
+    for (size_t I = 0, N = Info->getNumPrefixes(Owner->getPrefixesTable());
+         I != N; ++I)
+      O << '"'
+        << Info->getPrefix(Owner->getStrTable(), Owner->getPrefixesTable(), I)
+        << (I == N - 1 ? "\"" : "\", ");
     O << ']';
   }
 
diff --git a/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp b/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp
index b3dcc0f9866842..58ff720516f384 100644
--- a/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp
+++ b/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp
@@ -31,6 +31,10 @@ using namespace llvm::COFF;
 
 namespace {
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 enum {
   OPT_INVALID = 0,
 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
@@ -38,12 +42,9 @@ enum {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -54,7 +55,9 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class DllOptTable : public opt::GenericOptTable {
 public:
-  DllOptTable() : opt::GenericOptTable(InfoTable, false) {}
+  DllOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                             false) {}
 };
 
 // Opens a file. Path has to be resolved already.
diff --git a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
index 2e0841ba02b543..319aebffdbbba2 100644
--- a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
+++ b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
@@ -37,6 +37,10 @@ using namespace llvm::object;
 
 namespace {
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 enum {
   OPT_INVALID = 0,
 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
@@ -44,12 +48,9 @@ enum {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -60,7 +61,9 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class LibOptTable : public opt::GenericOptTable {
 public:
-  LibOptTable() : opt::GenericOptTable(InfoTable, true) {}
+  LibOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                             true) {}
 };
 } // namespace
 
diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp
index 594b52326871dd..913077eb0b06d5 100644
--- a/llvm/tools/dsymutil/dsymutil.cpp
+++ b/llvm/tools/dsymutil/dsymutil.cpp
@@ -64,12 +64,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -80,7 +81,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class DsymutilOptTable : public opt::GenericOptTable {
 public:
-  DsymutilOptTable() : opt::GenericOptTable(InfoTable) {}
+  DsymutilOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // namespace
 
diff --git a/llvm/tools/llvm-cgdata/llvm-cgdata.cpp b/llvm/tools/llvm-cgdata/llvm-cgdata.cpp
index d33459b194c9a3..9e3800f5bfbbc6 100644
--- a/llvm/tools/llvm-cgdata/llvm-cgdata.cpp
+++ b/llvm/tools/llvm-cgdata/llvm-cgdata.cpp
@@ -51,12 +51,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -67,7 +68,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class CGDataOptTable : public opt::GenericOptTable {
 public:
-  CGDataOptTable() : GenericOptTable(InfoTable) {}
+  CGDataOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // end anonymous namespace
 
diff --git a/llvm/tools/llvm-cvtres/llvm-cvtres.cpp b/llvm/tools/llvm-cvtres/llvm-cvtres.cpp
index 0c10769a9488ea..8ef8d6e239cfe3 100644
--- a/llvm/tools/llvm-cvtres/llvm-cvtres.cpp
+++ b/llvm/tools/llvm-cvtres/llvm-cvtres.cpp
@@ -42,12 +42,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -58,7 +59,9 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class CvtResOptTable : public opt::GenericOptTable {
 public:
-  CvtResOptTable() : opt::GenericOptTable(InfoTable, true) {}
+  CvtResOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                             true) {}
 };
 }
 
diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
index 41b379e8fd396b..1467093e78c0b4 100644
--- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
+++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
@@ -31,12 +31,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
-  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -47,7 +48,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class CxxfiltOptTable : public opt::GenericOptTable {
 public:
-  CxxfiltOptTable() : opt::GenericOptTable(InfoTable) {
+  CxxfiltOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp b/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp
index 77862737bccd2f..934833bf6fe426 100644
--- a/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp
+++ b/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp
@@ -37,12 +37,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -53,7 +54,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class DebuginfodFindOptTable : public opt::GenericOptTable {
 public:
-  DebuginfodFindOptTable() : GenericOptTable(InfoTable) {}
+  DebuginfodFindOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 
 } // end anonymous namespace
diff --git a/llvm/tools/llvm-debuginfod/llvm-debuginfod.cpp b/llvm/tools/llvm-debuginfod/llvm-debuginfod.cpp
index 44d656148a4e2c..2859a36c80b0b3 100644
--- a/llvm/tools/llvm-debuginfod/llvm-debuginfod.cpp
+++ b/llvm/tools/llvm-debuginfod/llvm-debuginfod.cpp
@@ -36,12 +36,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -52,7 +53,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class DebuginfodOptTable : public opt::GenericOptTable {
 public:
-  DebuginfodOptTable() : GenericOptTable(InfoTable) {}
+  DebuginfodOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // end anonymous namespace
 
diff --git a/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
index 7b777b1845f8a4..0180abb834f9d3 100644
--- a/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
+++ b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
@@ -38,12 +38,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Options.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Options.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -54,7 +55,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class DwarfutilOptTable : public opt::GenericOptTable {
 public:
-  DwarfutilOptTable() : opt::GenericOptTable(InfoTable) {}
+  DwarfutilOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // namespace
 
diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp
index 60a89cb13c57a0..e34fcadfde5fc9 100644
--- a/llvm/tools/llvm-dwp/llvm-dwp.cpp
+++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp
@@ -47,12 +47,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -63,7 +64,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class DwpOptTable : public opt::GenericOptTable {
 public:
-  DwpOptTable() : GenericOptTable(InfoTable) {}
+  DwpOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // end anonymous namespace
 
diff --git a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
index 4d441465c47fbf..42900159ce9667 100644
--- a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
+++ b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
@@ -64,12 +64,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  constexpr llvm::StringLiteral NAME##_init[] = VALUE;                         \
-  constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                          \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 const opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -79,7 +80,8 @@ const opt::OptTable::Info InfoTable[] = {
 
 class GSYMUtilOptTable : public llvm::opt::GenericOptTable {
 public:
-  GSYMUtilOptTable() : GenericOptTable(InfoTable) {
+  GSYMUtilOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-ifs/llvm-ifs.cpp b/llvm/tools/llvm-ifs/llvm-ifs.cpp
index b76ea8dec0c98c..e12016c51e906c 100644
--- a/llvm/tools/llvm-ifs/llvm-ifs.cpp
+++ b/llvm/tools/llvm-ifs/llvm-ifs.cpp
@@ -59,12 +59,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -74,7 +75,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class IFSOptTable : public opt::GenericOptTable {
 public:
-  IFSOptTable() : opt::GenericOptTable(InfoTable) {
+  IFSOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
index 3d3f3f0af4b7a7..94247118dc4eb2 100644
--- a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
+++ b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
@@ -48,12 +48,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -63,7 +64,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class LibtoolDarwinOptTable : public opt::GenericOptTable {
 public:
-  LibtoolDarwinOptTable() : GenericOptTable(InfoTable) {}
+  LibtoolDarwinOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // end anonymous namespace
 
diff --git a/llvm/tools/llvm-lipo/llvm-lipo.cpp b/llvm/tools/llvm-lipo/llvm-lipo.cpp
index 711a9185e155f6..3c0197e8b7bac7 100644
--- a/llvm/tools/llvm-lipo/llvm-lipo.cpp
+++ b/llvm/tools/llvm-lipo/llvm-lipo.cpp
@@ -72,12 +72,13 @@ enum LipoID {
 };
 
 namespace lipo {
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
-  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
-      NAME##_init, std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "LipoOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "LipoOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info LipoInfoTable[] = {
@@ -89,7 +90,9 @@ static constexpr opt::OptTable::Info LipoInfoTable[] = {
 
 class LipoOptTable : public opt::GenericOptTable {
 public:
-  LipoOptTable() : opt::GenericOptTable(lipo::LipoInfoTable) {}
+  LipoOptTable()
+      : opt::GenericOptTable(lipo::OptionStrTable, lipo::OptionPrefixesTable,
+                             lipo::LipoInfoTable) {}
 };
 
 enum class LipoAction {
diff --git a/llvm/tools/llvm-ml/llvm-ml.cpp b/llvm/tools/llvm-ml/llvm-ml.cpp
index db69109e2d1fab..1aa41096002eec 100644
--- a/llvm/tools/llvm-ml/llvm-ml.cpp
+++ b/llvm/tools/llvm-ml/llvm-ml.cpp
@@ -58,12 +58,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -73,7 +74,9 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class MLOptTable : public opt::GenericOptTable {
 public:
-  MLOptTable() : opt::GenericOptTable(InfoTable, /*IgnoreCase=*/false) {}
+  MLOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                             /*IgnoreCase=*/false) {}
 };
 } // namespace
 
diff --git a/llvm/tools/llvm-mt/llvm-mt.cpp b/llvm/tools/llvm-mt/llvm-mt.cpp
index 8b793b877642cb..3bd1bc786f86d3 100644
--- a/llvm/tools/llvm-mt/llvm-mt.cpp
+++ b/llvm/tools/llvm-mt/llvm-mt.cpp
@@ -40,12 +40,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -56,7 +57,9 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class CvtResOptTable : public opt::GenericOptTable {
 public:
-  CvtResOptTable() : opt::GenericOptTable(InfoTable, true) {}
+  CvtResOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                             true) {}
 };
 } // namespace
 
diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index d3e8d4c5ed987c..e7c3e36dd38d2f 100644
--- a/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -65,12 +65,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -80,7 +81,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class NmOptTable : public opt::GenericOptTable {
 public:
-  NmOptTable() : opt::GenericOptTable(InfoTable) {
+  NmOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index 104d802b1e1eeb..0925fc55317f7d 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -39,12 +39,13 @@ enum ObjcopyID {
 };
 
 namespace objcopy_opt {
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "ObjcopyOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "ObjcopyOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info ObjcopyInfoTable[] = {
 #define OPTION(...)                                                            \
@@ -56,7 +57,10 @@ static constexpr opt::OptTable::Info ObjcopyInfoTable[] = {
 
 class ObjcopyOptTable : public opt::GenericOptTable {
 public:
-  ObjcopyOptTable() : opt::GenericOptTable(objcopy_opt::ObjcopyInfoTable) {
+  ObjcopyOptTable()
+      : opt::GenericOptTable(objcopy_opt::OptionStrTable,
+                             objcopy_opt::OptionPrefixesTable,
+                             objcopy_opt::ObjcopyInfoTable) {
     setGroupedShortOptions(true);
     setDashDashParsing(true);
   }
@@ -71,13 +75,13 @@ enum InstallNameToolID {
 };
 
 namespace install_name_tool {
+#define OPTTABLE_STR_TABLE_CODE
+#include "InstallNameToolOpts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "InstallNameToolOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InstallNameToolInfoTable[] = {
 #define OPTION(...)                                                            \
@@ -90,7 +94,9 @@ static constexpr opt::OptTable::Info InstallNameToolInfoTable[] = {
 class InstallNameToolOptTable : public opt::GenericOptTable {
 public:
   InstallNameToolOptTable()
-      : GenericOptTable(install_name_tool::InstallNameToolInfoTable) {}
+      : GenericOptTable(install_name_tool::OptionStrTable,
+                        install_name_tool::OptionPrefixesTable,
+                        install_name_tool::InstallNameToolInfoTable) {}
 };
 
 enum BitcodeStripID {
@@ -102,13 +108,13 @@ enum BitcodeStripID {
 };
 
 namespace bitcode_strip {
+#define OPTTABLE_STR_TABLE_CODE
+#include "BitcodeStripOpts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "BitcodeStripOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info BitcodeStripInfoTable[] = {
 #define OPTION(...)                                                            \
@@ -121,7 +127,9 @@ static constexpr opt::OptTable::Info BitcodeStripInfoTable[] = {
 class BitcodeStripOptTable : public opt::GenericOptTable {
 public:
   BitcodeStripOptTable()
-      : opt::GenericOptTable(bitcode_strip::BitcodeStripInfoTable) {}
+      : opt::GenericOptTable(bitcode_strip::OptionStrTable,
+                             bitcode_strip::OptionPrefixesTable,
+                             bitcode_strip::BitcodeStripInfoTable) {}
 };
 
 enum StripID {
@@ -132,12 +140,13 @@ enum StripID {
 };
 
 namespace strip {
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
+#include "StripOpts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "StripOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info StripInfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(STRIP_, __VA_ARGS__),
@@ -148,7 +157,9 @@ static constexpr opt::OptTable::Info StripInfoTable[] = {
 
 class StripOptTable : public opt::GenericOptTable {
 public:
-  StripOptTable() : GenericOptTable(strip::StripInfoTable) {
+  StripOptTable()
+      : GenericOptTable(strip::OptionStrTable, strip::OptionPrefixesTable,
+                        strip::StripInfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 246d5cfa05818a..1e74cb80b21187 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -99,10 +99,11 @@ namespace {
 
 class CommonOptTable : public opt::GenericOptTable {
 public:
-  CommonOptTable(ArrayRef<Info> OptionInfos, const char *Usage,
+  CommonOptTable(const char *StrTable, ArrayRef<unsigned> PrefixesTable,
+                 ArrayRef<Info> OptionInfos, const char *Usage,
                  const char *Description)
-      : opt::GenericOptTable(OptionInfos), Usage(Usage),
-        Description(Description) {
+      : opt::GenericOptTable(StrTable, PrefixesTable, OptionInfos),
+        Usage(Usage), Description(Description) {
     setGroupedShortOptions(true);
   }
 
@@ -121,12 +122,13 @@ class CommonOptTable : public opt::GenericOptTable {
 
 // ObjdumpOptID is in ObjdumpOptID.h
 namespace objdump_opt {
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "ObjdumpOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "ObjdumpOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info ObjdumpInfoTable[] = {
 #define OPTION(...)                                                            \
@@ -139,9 +141,10 @@ static constexpr opt::OptTable::Info ObjdumpInfoTable[] = {
 class ObjdumpOptTable : public CommonOptTable {
 public:
   ObjdumpOptTable()
-      : CommonOptTable(objdump_opt::ObjdumpInfoTable,
-                       " [options] <input object files>",
-                       "llvm object file dumper") {}
+      : CommonOptTable(
+            objdump_opt::OptionStrTable, objdump_opt::OptionPrefixesTable,
+            objdump_opt::ObjdumpInfoTable, " [options] <input object files>",
+            "llvm object file dumper") {}
 };
 
 enum OtoolOptID {
@@ -152,12 +155,13 @@ enum OtoolOptID {
 };
 
 namespace otool {
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
+#include "OtoolOpts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "OtoolOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info OtoolInfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(OTOOL_, __VA_ARGS__),
@@ -169,7 +173,8 @@ static constexpr opt::OptTable::Info OtoolInfoTable[] = {
 class OtoolOptTable : public CommonOptTable {
 public:
   OtoolOptTable()
-      : CommonOptTable(otool::OtoolInfoTable, " [option...] [file...]",
+      : CommonOptTable(otool::OptionStrTable, otool::OptionPrefixesTable,
+                       otool::OtoolInfoTable, " [option...] [file...]",
                        "Mach-O object file displaying tool") {}
 };
 
diff --git a/llvm/tools/llvm-rc/llvm-rc.cpp b/llvm/tools/llvm-rc/llvm-rc.cpp
index 4bc9d90095575b..a77188c462afe0 100644
--- a/llvm/tools/llvm-rc/llvm-rc.cpp
+++ b/llvm/tools/llvm-rc/llvm-rc.cpp
@@ -57,12 +57,13 @@ enum ID {
 };
 
 namespace rc_opt {
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -73,7 +74,10 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class RcOptTable : public opt::GenericOptTable {
 public:
-  RcOptTable() : GenericOptTable(rc_opt::InfoTable, /* IgnoreCase = */ true) {}
+  RcOptTable()
+      : GenericOptTable(rc_opt::OptionStrTable, rc_opt::OptionPrefixesTable,
+                        rc_opt::InfoTable,
+                        /* IgnoreCase = */ true) {}
 };
 
 enum Windres_ID {
@@ -84,12 +88,13 @@ enum Windres_ID {
 };
 
 namespace windres_opt {
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
+#include "WindresOpts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "WindresOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...)                                                            \
@@ -102,7 +107,10 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 class WindresOptTable : public opt::GenericOptTable {
 public:
   WindresOptTable()
-      : GenericOptTable(windres_opt::InfoTable, /* IgnoreCase = */ false) {}
+      : GenericOptTable(windres_opt::OptionStrTable,
+                        windres_opt::OptionPrefixesTable,
+                        windres_opt::InfoTable,
+                        /* IgnoreCase = */ false) {}
 };
 
 static ExitOnError ExitOnErr;
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 3e76cda2dd4335..2f77e5d350553d 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -59,12 +59,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -74,7 +75,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class ReadobjOptTable : public opt::GenericOptTable {
 public:
-  ReadobjOptTable() : opt::GenericOptTable(InfoTable) {
+  ReadobjOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
index 04282d3e4877c1..b5574ea41e332b 100644
--- a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
+++ b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
@@ -45,12 +45,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "TapiOpts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "TapiOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -60,7 +61,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class TAPIOptTable : public opt::GenericOptTable {
 public:
-  TAPIOptTable() : opt::GenericOptTable(InfoTable) {
+  TAPIOptTable()
+      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp
index 4a1b0e879036cc..0d7bf248326702 100644
--- a/llvm/tools/llvm-size/llvm-size.cpp
+++ b/llvm/tools/llvm-size/llvm-size.cpp
@@ -45,12 +45,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -60,7 +61,10 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class SizeOptTable : public opt::GenericOptTable {
 public:
-  SizeOptTable() : GenericOptTable(InfoTable) { setGroupedShortOptions(true); }
+  SizeOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
+    setGroupedShortOptions(true);
+  }
 };
 
 enum OutputFormatTy { berkeley, sysv, darwin };
diff --git a/llvm/tools/llvm-strings/llvm-strings.cpp b/llvm/tools/llvm-strings/llvm-strings.cpp
index d4305096b60a0b..9979b93de84270 100644
--- a/llvm/tools/llvm-strings/llvm-strings.cpp
+++ b/llvm/tools/llvm-strings/llvm-strings.cpp
@@ -38,12 +38,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -54,7 +55,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class StringsOptTable : public opt::GenericOptTable {
 public:
-  StringsOptTable() : GenericOptTable(InfoTable) {
+  StringsOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
     setDashDashParsing(true);
   }
diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 3e41a85d646956..3ba7f59d5b8475 100644
--- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -56,12 +56,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -72,7 +73,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class SymbolizerOptTable : public opt::GenericOptTable {
 public:
-  SymbolizerOptTable() : GenericOptTable(InfoTable) {
+  SymbolizerOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
     setGroupedShortOptions(true);
   }
 };
diff --git a/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp b/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp
index a091e37ff4026d..ca0b4247221968 100644
--- a/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp
+++ b/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp
@@ -33,12 +33,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 using namespace llvm::opt;
 static constexpr opt::OptTable::Info InfoTable[] = {
@@ -49,7 +50,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class TLICheckerOptTable : public opt::GenericOptTable {
 public:
-  TLICheckerOptTable() : GenericOptTable(InfoTable) {}
+  TLICheckerOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // end anonymous namespace
 
diff --git a/llvm/tools/sancov/sancov.cpp b/llvm/tools/sancov/sancov.cpp
index 39feff62391fe1..727b94b8477cc5 100644
--- a/llvm/tools/sancov/sancov.cpp
+++ b/llvm/tools/sancov/sancov.cpp
@@ -67,12 +67,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_STR_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
 static constexpr opt::OptTable::Info InfoTable[] = {
 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -82,7 +83,8 @@ static constexpr opt::OptTable::Info InfoTable[] = {
 
 class SancovOptTable : public opt::GenericOptTable {
 public:
-  SancovOptTable() : GenericOptTable(InfoTable) {}
+  SancovOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
 };
 } // namespace
 
diff --git a/llvm/unittests/Option/OptionMarshallingTest.cpp b/llvm/unittests/Option/OptionMarshallingTest.cpp
index 2ec422f1a09843..08c3b019689f8c 100644
--- a/llvm/unittests/Option/OptionMarshallingTest.cpp
+++ b/llvm/unittests/Option/OptionMarshallingTest.cpp
@@ -9,29 +9,37 @@
 #include "llvm/ADT/StringRef.h"
 #include "gtest/gtest.h"
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 struct OptionWithMarshallingInfo {
-  llvm::StringLiteral PrefixedName;
+  int PrefixedNameOffset;
   const char *KeyPath;
   const char *ImpliedCheck;
   const char *ImpliedValue;
+
+  llvm::StringRef getPrefixedName() const {
+    return &OptionStrTable[PrefixedNameOffset];
+  }
 };
 
 static const OptionWithMarshallingInfo MarshallingTable[] = {
 #define OPTION_WITH_MARSHALLING(                                               \
-    PREFIX_TYPE, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,      \
-    VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES,        \
+    PREFIX_TYPE, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS, ALIASARGS,      \
+    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
     SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK,          \
     IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX)   \
-  {PREFIXED_NAME, #KEYPATH, #IMPLIED_CHECK, #IMPLIED_VALUE},
+  {PREFIXED_NAME_OFFSET, #KEYPATH, #IMPLIED_CHECK, #IMPLIED_VALUE},
 #include "Opts.inc"
 #undef OPTION_WITH_MARSHALLING
 };
 
 TEST(OptionMarshalling, EmittedOrderSameAsDefinitionOrder) {
-  ASSERT_EQ(MarshallingTable[0].PrefixedName, "-marshalled-flag-d");
-  ASSERT_EQ(MarshallingTable[1].PrefixedName, "-marshalled-flag-c");
-  ASSERT_EQ(MarshallingTable[2].PrefixedName, "-marshalled-flag-b");
-  ASSERT_EQ(MarshallingTable[3].PrefixedName, "-marshalled-flag-a");
+  ASSERT_EQ(MarshallingTable[0].getPrefixedName(), "-marshalled-flag-d");
+  ASSERT_EQ(MarshallingTable[1].getPrefixedName(), "-marshalled-flag-c");
+  ASSERT_EQ(MarshallingTable[2].getPrefixedName(), "-marshalled-flag-b");
+  ASSERT_EQ(MarshallingTable[3].getPrefixedName(), "-marshalled-flag-a");
 }
 
 TEST(OptionMarshalling, EmittedSpecifiedKeyPath) {
diff --git a/llvm/unittests/Option/OptionParsingTest.cpp b/llvm/unittests/Option/OptionParsingTest.cpp
index cd8743e49d4fde..3da015e343eb97 100644
--- a/llvm/unittests/Option/OptionParsingTest.cpp
+++ b/llvm/unittests/Option/OptionParsingTest.cpp
@@ -20,6 +20,10 @@ using namespace llvm::opt;
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #endif
 
+#define OPTTABLE_STR_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
 enum ID {
   OPT_INVALID = 0, // This is not an option ID.
 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
@@ -28,20 +32,13 @@ enum ID {
 #undef OPTION
 };
 
-#define PREFIX(NAME, VALUE)                                                    \
-  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
-  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
-                                                std::size(NAME##_init) - 1);
+#define OPTTABLE_PREFIXES_TABLE_CODE
 #include "Opts.inc"
-#undef PREFIX
+#undef OPTTABLE_PREFIXES_TABLE_CODE
 
-static constexpr const StringLiteral PrefixTable_init[] =
-#define PREFIX_UNION(VALUES) VALUES
+#define OPTTABLE_PREFIXES_UNION_CODE
 #include "Opts.inc"
-#undef PREFIX_UNION
-    ;
-static constexpr const ArrayRef<StringLiteral>
-    PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1);
+#undef OPTTABLE_PREFIXES_UNION_CODE
 
 enum OptionFlags {
   OptFlag1 = (1 << 4),
@@ -64,13 +61,15 @@ namespace {
 class TestOptTable : public GenericOptTable {
 public:
   TestOptTable(bool IgnoreCase = false)
-      : GenericOptTable(InfoTable, IgnoreCase) {}
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                        IgnoreCase) {}
 };
 
 class TestPrecomputedOptTable : public PrecomputedOptTable {
 public:
   TestPrecomputedOptTable(bool IgnoreCase = false)
-      : PrecomputedOptTable(InfoTable, PrefixTable, IgnoreCase) {}
+      : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                            OptionPrefixesUnion, IgnoreCase) {}
 };
 }
 
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index 48f5818938ae86..eca828cad5f4d2 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -9,9 +9,11 @@
 #include "Common/OptEmitter.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringToOffsetTable.h"
 #include "llvm/TableGen/TableGenBackend.h"
 #include <cstring>
 #include <map>
@@ -26,6 +28,15 @@ static std::string getOptionName(const Record &R) {
   return std::string(R.getValueAsString("EnumName"));
 }
 
+static raw_ostream &writeStrTableOffset(raw_ostream &OS,
+                                        const StringToOffsetTable &Table,
+                                        llvm::StringRef Str) {
+  OS << Table.GetStringOffset(Str) << " /* ";
+  OS.write_escaped(Str);
+  OS << " */";
+  return OS;
+}
+
 static raw_ostream &writeCstring(raw_ostream &OS, llvm::StringRef Str) {
   OS << '"';
   OS.write_escaped(Str);
@@ -260,59 +271,84 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
 
   // Generate prefix groups.
   typedef SmallVector<SmallString<2>, 2> PrefixKeyT;
-  typedef std::map<PrefixKeyT, std::string> PrefixesT;
+  typedef std::map<PrefixKeyT, unsigned> PrefixesT;
   PrefixesT Prefixes;
-  Prefixes.insert(std::pair(PrefixKeyT(), "prefix_0"));
-  unsigned CurPrefix = 0;
+  Prefixes.insert({PrefixKeyT(), 0});
   for (const Record &R : llvm::make_pointee_range(Opts)) {
     std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
     PrefixKeyT PrefixKey(RPrefixes.begin(), RPrefixes.end());
-    unsigned NewPrefix = CurPrefix + 1;
-    std::string Prefix = (Twine("prefix_") + Twine(NewPrefix)).str();
-    if (Prefixes.insert(std::pair(PrefixKey, Prefix)).second)
-      CurPrefix = NewPrefix;
+    Prefixes.insert({PrefixKey, 0});
   }
 
   DenseSet<StringRef> PrefixesUnionSet;
-  for (const auto &Prefix : Prefixes)
-    PrefixesUnionSet.insert(Prefix.first.begin(), Prefix.first.end());
+  for (const auto &[Prefix, _] : Prefixes)
+    PrefixesUnionSet.insert(Prefix.begin(), Prefix.end());
   SmallVector<StringRef> PrefixesUnion(PrefixesUnionSet.begin(),
                                        PrefixesUnionSet.end());
   array_pod_sort(PrefixesUnion.begin(), PrefixesUnion.end());
 
+  llvm::StringToOffsetTable Table;
+  // Make sure the empty string is the zero-th one in the table. This both makes
+  // it easy to check for empty strings (zero offset == empty) and makes
+  // initialization cheaper for empty strings.
+  Table.GetOrAddStringOffset("");
+  // 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))
+    Table.GetOrAddStringOffset(R.getValueAsString("Name"));
+  for (const Record &R : llvm::make_pointee_range(Opts))
+    Table.GetOrAddStringOffset(getOptionPrefixedName(R));
+
+  // Dump string table.
+  OS << "/////////\n";
+  OS << "// String table\n\n";
+  OS << "#ifdef OPTTABLE_STR_TABLE_CODE\n";
+  Table.EmitStringLiteralDef(OS, "static constexpr char OptionStrTable[]",
+                             /*Indent=*/"");
+  OS << "#endif // OPTTABLE_STR_TABLE_CODE\n\n";
+
   // Dump prefixes.
   OS << "/////////\n";
   OS << "// Prefixes\n\n";
-  OS << "#ifdef PREFIX\n";
-  OS << "#define COMMA ,\n";
-  for (const auto &Prefix : Prefixes) {
-    OS << "PREFIX(";
-
-    // Prefix name.
-    OS << Prefix.second;
-
-    // Prefix values.
-    OS << ", {";
-    for (const auto &PrefixKey : Prefix.first)
-      OS << "llvm::StringLiteral(\"" << PrefixKey << "\") COMMA ";
-    // Append an empty element to avoid ending up with an empty array.
-    OS << "llvm::StringLiteral(\"\")})\n";
+  OS << "#ifdef OPTTABLE_PREFIXES_TABLE_CODE\n";
+  OS << "static constexpr unsigned OptionPrefixesTable[] = {\n";
+  {
+    // Ensure the first prefix set is always empty.
+    assert(!Prefixes.empty() &&
+           "We should always emit an empty set of prefixes");
+    assert(Prefixes.begin()->first.empty() &&
+           "First prefix set should always be empty");
+    llvm::ListSeparator Sep(",\n");
+    unsigned CurIndex = 0;
+    for (auto &[Prefix, PrefixIndex] : Prefixes) {
+      // First emit the number of prefix strings in this list of prefixes.
+      OS << Sep << "  " << Prefix.size() << " /* prefixes */";
+      PrefixIndex = CurIndex;
+      assert((CurIndex == 0 || !Prefix.empty()) &&
+             "Only first prefix set should be empty!");
+      for (const auto &PrefixKey : Prefix)
+        OS << ", " << *Table.GetStringOffset(PrefixKey) << " /* '" << PrefixKey
+           << "' */";
+      CurIndex += Prefix.size() + 1;
+    }
   }
-  OS << "#undef COMMA\n";
-  OS << "#endif // PREFIX\n\n";
+  OS << "\n};\n";
+  OS << "#endif // OPTTABLE_PREFIXES_TABLE_CODE\n\n";
 
-  // Dump prefix unions.
+  // Dump prefixes union.
   OS << "/////////\n";
   OS << "// Prefix Union\n\n";
-  OS << "#ifdef PREFIX_UNION\n";
-  OS << "#define COMMA ,\n";
-  OS << "PREFIX_UNION({\n";
-  for (const auto &Prefix : PrefixesUnion) {
-    OS << "llvm::StringLiteral(\"" << Prefix << "\") COMMA ";
+  OS << "#ifdef OPTTABLE_PREFIXES_UNION_CODE\n";
+  OS << "static constexpr unsigned OptionPrefixesUnion[] = {\n";
+  {
+    llvm::ListSeparator Sep(", ");
+    for (auto Prefix : PrefixesUnion)
+      OS << Sep << "  " << *Table.GetStringOffset(Prefix) << " /* '" << Prefix
+         << "' */";
   }
-  OS << "llvm::StringLiteral(\"\")})\n";
-  OS << "#undef COMMA\n";
-  OS << "#endif // PREFIX_UNION\n\n";
+  OS << "\n};\n";
+  OS << "#endif // OPTTABLE_PREFIXES_UNION_CODE\n\n";
 
   // Dump groups.
   OS << "/////////\n";
@@ -337,11 +373,12 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
     // Start a single option entry.
     OS << "OPTION(";
 
-    // The option prefix;
-    OS << "llvm::ArrayRef<llvm::StringLiteral>()";
+    // A zero prefix offset corresponds to an empty set of prefixes.
+    OS << "0 /* no prefixes */";
 
-    // The option string.
-    OS << ", \"" << R.getValueAsString("Name") << '"';
+    // The option string offset.
+    OS << ", ";
+    writeStrTableOffset(OS, Table, R.getValueAsString("Name"));
 
     // The option identifier name.
     OS << ", " << getOptionName(R);
@@ -387,7 +424,7 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
     OS << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())] << ", ";
 
     // The option prefixed name.
-    writeCstring(OS, getOptionPrefixedName(R));
+    writeStrTableOffset(OS, Table, getOptionPrefixedName(R));
 
     // The option identifier name.
     OS << ", " << getOptionName(R);



More information about the lldb-commits mailing list