https://github.com/Prabhuk created 
https://github.com/llvm/llvm-project/pull/155026

Implement support for `subcommands` in OptTable to attain feature parity with 
`cl`.

Issue: https://github.com/llvm/llvm-project/issues/108307

>From 3584c6aacac629c51c4d1e8e08258c0c24f3a165 Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Tue, 19 Aug 2025 15:48:47 -0700
Subject: [PATCH] [OptTable] Subcommand support.

TODO: Add tests.
---
 clang-tools-extra/clangd/CompileCommands.cpp  |   2 +-
 clang/lib/Frontend/CompilerInvocation.cpp     |  11 +-
 clang/tools/clang-installapi/Options.h        |   2 +-
 lld/MachO/DriverUtils.cpp                     |   5 +-
 lld/MinGW/Driver.cpp                          |   5 +-
 lld/wasm/Driver.cpp                           |   5 +-
 llvm/examples/CMakeLists.txt                  |   1 +
 llvm/examples/OptSubcommand/CMakeLists.txt    |  19 +++
 llvm/examples/OptSubcommand/Opts.td           |  16 +++
 .../examples/OptSubcommand/llvm-hello-sub.cpp |  94 ++++++++++++++
 llvm/include/llvm/Option/ArgList.h            |   3 +
 llvm/include/llvm/Option/OptParser.td         |  58 ++++++---
 llvm/include/llvm/Option/OptTable.h           |  85 ++++++++++---
 llvm/lib/Option/ArgList.cpp                   |  12 ++
 llvm/lib/Option/OptTable.cpp                  | 116 +++++++++++++++---
 .../Option/OptionMarshallingTest.cpp          |   5 +-
 llvm/utils/TableGen/OptionParserEmitter.cpp   |  99 ++++++++++++++-
 17 files changed, 473 insertions(+), 65 deletions(-)
 create mode 100644 llvm/examples/OptSubcommand/CMakeLists.txt
 create mode 100644 llvm/examples/OptSubcommand/Opts.td
 create mode 100644 llvm/examples/OptSubcommand/llvm-hello-sub.cpp

diff --git a/clang-tools-extra/clangd/CompileCommands.cpp 
b/clang-tools-extra/clangd/CompileCommands.cpp
index 80391fe8cce25..7ab04894a63b5 100644
--- a/clang-tools-extra/clangd/CompileCommands.cpp
+++ b/clang-tools-extra/clangd/CompileCommands.cpp
@@ -465,7 +465,7 @@ llvm::ArrayRef<ArgStripper::Rule> 
ArgStripper::rulesFor(llvm::StringRef Arg) {
     } AliasTable[] = {
 #define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS,       
\
                FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,       
\
-               METAVAR, VALUES)                                                
\
+               METAVAR, VALUES, COMMANDIDS_OFFSET)                             
\
   {DriverID::OPT_##ID, DriverID::OPT_##ALIAS, ALIASARGS},
 #include "clang/Driver/Options.inc"
 #undef OPTION
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index a4d18966be35f..681feda59c623 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -533,9 +533,9 @@ static T extractMaskValue(T KeyPath) {
 #define PARSE_OPTION_WITH_MARSHALLING(                                         
\
     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)                                                               
\
+    METAVAR, VALUES, COMMANDIDS_OFFSET, 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)                                                         
\
@@ -551,8 +551,9 @@ static T extractMaskValue(T KeyPath) {
 #define GENERATE_OPTION_WITH_MARSHALLING(                                      
\
     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)   
\
+    COMMANDIDS_OFFSET, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE,      
\
+    IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, 
\
+    TABLE_INDEX)                                                               
\
   if ((VISIBILITY) & options::CC1Option) {                                     
\
     [&](const auto &Extracted) {                                               
\
       if (ALWAYS_EMIT ||                                                       
\
diff --git a/clang/tools/clang-installapi/Options.h 
b/clang/tools/clang-installapi/Options.h
index d62f2efd3141a..918723a334a09 100644
--- a/clang/tools/clang-installapi/Options.h
+++ b/clang/tools/clang-installapi/Options.h
@@ -208,7 +208,7 @@ enum ID {
   OPT_INVALID = 0, // This is not an option ID.
 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,         
\
                VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR,     
\
-               VALUES)                                                         
\
+               VALUES, COMMANDIDS_OFFSET)                                      
\
   OPT_##ID,
 #include "InstallAPIOpts.inc"
   LastOption
diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp
index a3b722f13daca..da7653d15b7f2 100644
--- a/lld/MachO/DriverUtils.cpp
+++ b/lld/MachO/DriverUtils.cpp
@@ -45,7 +45,7 @@ using namespace lld::macho;
 static constexpr OptTable::Info optInfo[] = {
 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,         
\
                VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR,     
\
-               VALUES)                                                         
\
+               VALUES, COMMANDIDS_OFFSET)                                      
\
   {PREFIX,                                                                     
\
    NAME,                                                                       
\
    HELPTEXT,                                                                   
\
@@ -59,7 +59,8 @@ static constexpr OptTable::Info optInfo[] = {
    OPT_##GROUP,                                                                
\
    OPT_##ALIAS,                                                                
\
    ALIASARGS,                                                                  
\
-   VALUES},
+   VALUES,                                                                     
\
+   COMMANDIDS_OFFSET},
 #include "Options.inc"
 #undef OPTION
 };
diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index 5098dbd77b4fd..306c55135b677 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -69,7 +69,7 @@ enum {
 static constexpr opt::OptTable::Info infoTable[] = {
 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,         
\
                VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR,     
\
-               VALUES)                                                         
\
+               VALUES, COMMANDIDS_OFFSET)                                      
\
   {PREFIX,                                                                     
\
    NAME,                                                                       
\
    HELPTEXT,                                                                   
\
@@ -83,7 +83,8 @@ static constexpr opt::OptTable::Info infoTable[] = {
    OPT_##GROUP,                                                                
\
    OPT_##ALIAS,                                                                
\
    ALIASARGS,                                                                  
\
-   VALUES},
+   VALUES,                                                                     
\
+   COMMANDIDS_OFFSET},
 #include "Options.inc"
 #undef OPTION
 };
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 1c5d21c06f5af..baf61da44fad9 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -157,7 +157,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream 
&stdoutOS,
 static constexpr opt::OptTable::Info optInfo[] = {
 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,         
\
                VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR,     
\
-               VALUES)                                                         
\
+               VALUES, COMMANDIDS_OFFSET)                                      
\
   {PREFIX,                                                                     
\
    NAME,                                                                       
\
    HELPTEXT,                                                                   
\
@@ -171,7 +171,8 @@ static constexpr opt::OptTable::Info optInfo[] = {
    OPT_##GROUP,                                                                
\
    OPT_##ALIAS,                                                                
\
    ALIASARGS,                                                                  
\
-   VALUES},
+   VALUES,                                                                     
\
+   COMMANDIDS_OFFSET},
 #include "Options.inc"
 #undef OPTION
 };
diff --git a/llvm/examples/CMakeLists.txt b/llvm/examples/CMakeLists.txt
index 74613bd1350bd..b10a94c5493b8 100644
--- a/llvm/examples/CMakeLists.txt
+++ b/llvm/examples/CMakeLists.txt
@@ -8,6 +8,7 @@ add_subdirectory(ModuleMaker)
 add_subdirectory(OrcV2Examples)
 add_subdirectory(SpeculativeJIT)
 add_subdirectory(Bye)
+add_subdirectory(OptSubcommand)
 
 if(LLVM_ENABLE_EH AND (NOT WIN32) AND (NOT "${LLVM_NATIVE_ARCH}" STREQUAL 
"ARM"))
     add_subdirectory(ExceptionDemo)
diff --git a/llvm/examples/OptSubcommand/CMakeLists.txt 
b/llvm/examples/OptSubcommand/CMakeLists.txt
new file mode 100644
index 0000000000000..debc948611866
--- /dev/null
+++ b/llvm/examples/OptSubcommand/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Set the .td file to be processed for this target.
+set(LLVM_TARGET_DEFINITIONS Opts.td)
+
+tablegen(LLVM Opts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(HelloSubTableGen)
+
+set(LLVM_LINK_COMPONENTS  
+  Support
+  Option  
+  )
+
+add_llvm_example(OptSubcommand
+  llvm-hello-sub.cpp  
+  )
+
+target_include_directories(OptSubcommand
+  PRIVATE
+  ${CMAKE_CURRENT_BINARY_DIR}
+  )
diff --git a/llvm/examples/OptSubcommand/Opts.td 
b/llvm/examples/OptSubcommand/Opts.td
new file mode 100644
index 0000000000000..8c4f66b8c3043
--- /dev/null
+++ b/llvm/examples/OptSubcommand/Opts.td
@@ -0,0 +1,16 @@
+include "llvm/Option/OptParser.td"
+
+def sc_foo : Subcommand<"foo", "HelpText for Subcommand foo.">;
+
+def sc_bar : Subcommand<"bar", "HelpText for Subcommand bar.">;
+
+def help : Flag<["--"], "help">, HelpText<"Top Level Help Text for the tool.">;
+
+def version : Flag<["-"], "version">,
+              HelpText<"Toplevel Display the version number">;
+
+def uppercase : Flag<["-"], "uppercase", [sc_foo, sc_bar]>,
+                HelpText<"Print in uppercase">;
+
+def lowercase : Flag<["-"], "lowercase", [sc_foo]>,
+                HelpText<"Print in lowercase">;
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp 
b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
new file mode 100644
index 0000000000000..aecc981e485c1
--- /dev/null
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -0,0 +1,94 @@
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::opt;
+
+namespace {
+enum ID {
+  OPT_INVALID = 0,
+#define OPTION(PREFIXES, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS,       
\
+               VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR,     
\
+               VALUES, COMMANDIDS_OFFSET)                                      
\
+  OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+#define OPTTABLE_STR_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
+
+#define OPTTABLE_COMMAND_IDS_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_COMMAND_IDS_TABLE_CODE
+
+#define OPTTABLE_COMMANDS_CODE
+#include "Opts.inc"
+#undef OPTTABLE_COMMANDS_CODE
+
+static constexpr OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
+
+class HelloSubOptTable : public GenericOptTable {
+public:
+  HelloSubOptTable()
+      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+                        OptionCommands, OptionCommandIDsTable) {}
+};
+} // namespace
+
+int main(int argc, char **argv) {
+  InitLLVM X(argc, argv);
+  HelloSubOptTable T;
+  unsigned MissingArgIndex, MissingArgCount;
+  InputArgList Args = T.ParseArgs(ArrayRef(argv + 1, argc - 1), 
MissingArgIndex,
+                                  MissingArgCount);
+
+  StringRef Subcommand = Args.getSubcommand();
+  if (Args.hasArg(OPT_help)) {
+    T.printHelp(llvm::outs(), "llvm-hello-sub [subcommand] [options]",
+                "LLVM Hello Subcommand Example", false, false, Visibility(),
+                Subcommand);
+    return 0;
+  }
+
+  if (Args.hasArg(OPT_version)) {
+    llvm::outs() << "LLVM Hello Subcommand Example 1.0\n";
+    return 0;
+  }
+
+  if (Subcommand == "foo") {
+    if (Args.hasArg(OPT_uppercase))
+      llvm::outs() << "FOO\n";
+    else if (Args.hasArg(OPT_lowercase))
+      llvm::outs() << "foo\n";
+    else
+      llvm::errs() << "error: unknown option for subcommand '" << Subcommand
+                   << "'. See -help.\n";
+    return 1;
+  } else if (Subcommand == "bar") {
+    if (Args.hasArg(OPT_lowercase))
+      llvm::outs() << "bar\n";
+    else if (Args.hasArg(OPT_uppercase))
+      llvm::outs() << "BAR\n";
+    else
+      llvm::errs() << "error: unknown option for subcommand '" << Subcommand
+                   << "'. See -help.\n";
+  } else {
+    llvm::errs() << "error: unknown subcommand '" << Subcommand
+                 << "'. See --help.\n";
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/llvm/include/llvm/Option/ArgList.h 
b/llvm/include/llvm/Option/ArgList.h
index 313164bc29689..2394b2e8301b8 100644
--- a/llvm/include/llvm/Option/ArgList.h
+++ b/llvm/include/llvm/Option/ArgList.h
@@ -280,6 +280,9 @@ class ArgList {
   /// list.
   virtual unsigned getNumInputArgStrings() const = 0;
 
+  /// getSubcommand - Return the active subcommand, if one exists.
+  LLVM_ABI StringRef getSubcommand() const;
+
   /// @}
   /// @name Argument Lookup Utilities
   /// @{
diff --git a/llvm/include/llvm/Option/OptParser.td 
b/llvm/include/llvm/Option/OptParser.td
index 9fd606b0d6fcb..2bf920be8d946 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -98,7 +98,21 @@ class HelpTextVariant<list<OptionVisibility> visibilities, 
string text> {
   string Text = text;
 }
 
-class Option<list<string> prefixes, string name, OptionKind kind> {
+// Base class for TopLevelCommand and Subcommands.
+class Command<string name> { string Name = name; }
+
+// Class definition for positional subcommands.
+class Subcommand<string name, string helpText> : Command<name> {
+  string HelpText = helpText;
+}
+
+// Compile time representation for top level command (aka toolname).
+// Offers backward compatibility with existing Option class definitions before
+// introduction of commandGroup in Option class to support subcommands.
+def TopLevelCommand : Command<"TopLevelCommand">;
+
+class Option<list<string> prefixes, string name, OptionKind kind,
+             list<Command> commandGroup = [TopLevelCommand]> {
   string EnumName = ?; // Uses the def name if undefined.
   list<string> Prefixes = prefixes;
   string Name = name;
@@ -129,26 +143,34 @@ class Option<list<string> prefixes, string name, 
OptionKind kind> {
   code ValueMerger = "mergeForwardValue";
   code ValueExtractor = "extractForwardValue";
   list<code> NormalizedValues = ?;
+  list<Command> CommandGroup = commandGroup;
 }
 
 // Helpers for defining options.
 
-class Flag<list<string> prefixes, string name>
-  : Option<prefixes, name, KIND_FLAG>;
-class Joined<list<string> prefixes, string name>
-  : Option<prefixes, name, KIND_JOINED>;
-class Separate<list<string> prefixes, string name>
-  : Option<prefixes, name, KIND_SEPARATE>;
-class CommaJoined<list<string> prefixes, string name>
-  : Option<prefixes, name, KIND_COMMAJOINED>;
-class MultiArg<list<string> prefixes, string name, int numargs>
-  : Option<prefixes, name, KIND_MULTIARG> {
+class Flag<list<string> prefixes, string name,
+           list<Command> commandGroup = [TopLevelCommand]>
+    : Option<prefixes, name, KIND_FLAG, commandGroup>;
+class Joined<list<string> prefixes, string name,
+             list<Command> commandGroup = [TopLevelCommand]>
+    : Option<prefixes, name, KIND_JOINED, commandGroup>;
+class Separate<list<string> prefixes, string name,
+               list<Command> commandGroup = [TopLevelCommand]>
+    : Option<prefixes, name, KIND_SEPARATE, commandGroup>;
+class CommaJoined<list<string> prefixes, string name,
+                  list<Command> commandGroup = [TopLevelCommand]>
+    : Option<prefixes, name, KIND_COMMAJOINED, commandGroup>;
+class MultiArg<list<string> prefixes, string name, int numargs,
+               list<Command> commandGroup = [TopLevelCommand]>
+    : Option<prefixes, name, KIND_MULTIARG, commandGroup> {
   int NumArgs = numargs;
 }
-class JoinedOrSeparate<list<string> prefixes, string name>
-  : Option<prefixes, name, KIND_JOINED_OR_SEPARATE>;
-class JoinedAndSeparate<list<string> prefixes, string name>
-  : Option<prefixes, name, KIND_JOINED_AND_SEPARATE>;
+class JoinedOrSeparate<list<string> prefixes, string name,
+                       list<Command> commandGroup = [TopLevelCommand]>
+    : Option<prefixes, name, KIND_JOINED_OR_SEPARATE, commandGroup>;
+class JoinedAndSeparate<list<string> prefixes, string name,
+                        list<Command> commandGroup = [TopLevelCommand]>
+    : Option<prefixes, name, KIND_JOINED_AND_SEPARATE, commandGroup>;
 
 // Mix-ins for adding optional attributes.
 
@@ -271,7 +293,7 @@ class ValueExtractor<code extractor> { code ValueExtractor 
= extractor; }
 
 // FIXME: Have generator validate that these appear in correct position (and
 // aren't duplicated).
-def INPUT : Option<[], "<input>", KIND_INPUT>;
-def UNKNOWN : Option<[], "<unknown>", KIND_UNKNOWN>;
+def INPUT : Option<[], "<input>", KIND_INPUT, [TopLevelCommand]>;
+def UNKNOWN : Option<[], "<unknown>", KIND_UNKNOWN, [TopLevelCommand]>;
 
-#endif // LLVM_OPTION_OPTPARSER_TD
+#endif // LLVM_OPTION_OPTPARSER_TD
\ No newline at end of file
diff --git a/llvm/include/llvm/Option/OptTable.h 
b/llvm/include/llvm/Option/OptTable.h
index df42ee341ee58..3f362521a7e64 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -10,6 +10,7 @@
 #define LLVM_OPTION_OPTTABLE_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringTable.h"
@@ -53,6 +54,12 @@ class Visibility {
 /// parts of the driver still use Option instances where convenient.
 class LLVM_ABI OptTable {
 public:
+  /// Represents a subcommand and its options in the option table.
+  struct Command {
+    const char *Name;
+    const char *HelpText;
+  };
+
   /// Entry for a single option instance in the option data table.
   struct Info {
     unsigned PrefixesOffset;
@@ -79,6 +86,7 @@ class LLVM_ABI OptTable {
     unsigned short AliasID;
     const char *AliasArgs;
     const char *Values;
+    unsigned CommandIDsOffset;
 
     bool hasNoPrefix() const { return PrefixesOffset == 0; }
 
@@ -94,6 +102,20 @@ class LLVM_ABI OptTable {
                                                  
getNumPrefixes(PrefixesTable));
     }
 
+    bool hasCommands() const { return CommandIDsOffset != 0; }
+
+    unsigned getNumCommandIDs(ArrayRef<unsigned> CommandIDsTable) const {
+      // We embed the number of command IDs in the value of the first offset.
+      return CommandIDsTable[CommandIDsOffset];
+    }
+
+    ArrayRef<unsigned> getCommandIDs(ArrayRef<unsigned> CommandIDsTable) const 
{
+      return hasCommands()
+                 ? CommandIDsTable.slice(CommandIDsOffset + 1,
+                                         getNumCommandIDs(CommandIDsTable))
+                 : ArrayRef<unsigned>();
+    }
+
     void appendPrefixes(const StringTable &StrTable,
                         ArrayRef<StringTable::Offset> PrefixesTable,
                         SmallVectorImpl<StringRef> &Prefixes) const {
@@ -133,6 +155,12 @@ class LLVM_ABI OptTable {
   /// The option information table.
   ArrayRef<Info> OptionInfos;
 
+  /// The command information table.
+  ArrayRef<Command> Commands;
+
+  /// The command IDs table.
+  ArrayRef<unsigned> CommandIDsTable;
+
   bool IgnoreCase;
   bool GroupedShortOptions = false;
   bool DashDashParsing = false;
@@ -169,6 +197,10 @@ class LLVM_ABI OptTable {
   OptTable(const StringTable &StrTable,
            ArrayRef<StringTable::Offset> PrefixesTable,
            ArrayRef<Info> OptionInfos, bool IgnoreCase = false);
+  OptTable(const StringTable &StrTable,
+           ArrayRef<StringTable::Offset> PrefixesTable,
+           ArrayRef<Info> OptionInfos, ArrayRef<Command> Commands,
+           ArrayRef<unsigned> CommandIDsTable, bool IgnoreCase = false);
 
   /// Build (or rebuild) the PrefixChars member.
   void buildPrefixChars();
@@ -350,6 +382,7 @@ class LLVM_ABI OptTable {
 private:
   std::unique_ptr<Arg>
   internalParseOneArg(const ArgList &Args, unsigned &Index,
+                      const Command *ActiveCommand,
                       std::function<bool(const Option &)> ExcludeOption) const;
 
 public:
@@ -410,7 +443,8 @@ class LLVM_ABI OptTable {
   ///                         texts.
   void printHelp(raw_ostream &OS, const char *Usage, const char *Title,
                  bool ShowHidden = false, bool ShowAllAliases = false,
-                 Visibility VisibilityMask = Visibility()) const;
+                 Visibility VisibilityMask = Visibility(),
+                 StringRef SubCommand = {}) const;
 
   void printHelp(raw_ostream &OS, const char *Usage, const char *Title,
                  unsigned FlagsToInclude, unsigned FlagsToExclude,
@@ -418,7 +452,8 @@ class LLVM_ABI OptTable {
 
 private:
   void internalPrintHelp(raw_ostream &OS, const char *Usage, const char *Title,
-                         bool ShowHidden, bool ShowAllAliases,
+                         StringRef Subcommand, bool ShowHidden,
+                         bool ShowAllAliases,
                          std::function<bool(const Info &)> ExcludeOption,
                          Visibility VisibilityMask) const;
 };
@@ -428,21 +463,38 @@ class GenericOptTable : public OptTable {
 protected:
   LLVM_ABI GenericOptTable(const StringTable &StrTable,
                            ArrayRef<StringTable::Offset> PrefixesTable,
-                           ArrayRef<Info> OptionInfos, bool IgnoreCase = 
false);
+                           ArrayRef<Info> OptionInfos, bool IgnoreCase = false)
+      : GenericOptTable(StrTable, PrefixesTable, OptionInfos, {}, {},
+                        IgnoreCase) {}
+  LLVM_ABI GenericOptTable(const StringTable &StrTable,
+                           ArrayRef<StringTable::Offset> PrefixesTable,
+                           ArrayRef<Info> OptionInfos,
+                           ArrayRef<Command> Commands,
+                           ArrayRef<unsigned> CommandIDsTable,
+                           bool IgnoreCase = false);
 };
 
 class PrecomputedOptTable : public OptTable {
 protected:
   PrecomputedOptTable(const StringTable &StrTable,
                       ArrayRef<StringTable::Offset> PrefixesTable,
-                      ArrayRef<Info> OptionInfos,
+                      ArrayRef<Info> OptionInfos, ArrayRef<Command> Commands,
+                      ArrayRef<unsigned> CommandIDsTable,
                       ArrayRef<StringTable::Offset> PrefixesUnionOffsets,
                       bool IgnoreCase = false)
-      : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase) {
+      : OptTable(StrTable, PrefixesTable, OptionInfos, Commands,
+                 CommandIDsTable, IgnoreCase) {
     for (auto PrefixOffset : PrefixesUnionOffsets)
       PrefixesUnion.push_back(StrTable[PrefixOffset]);
     buildPrefixChars();
   }
+  PrecomputedOptTable(const StringTable &StrTable,
+                      ArrayRef<StringTable::Offset> PrefixesTable,
+                      ArrayRef<Info> OptionInfos,
+                      ArrayRef<StringTable::Offset> PrefixesUnionOffsets,
+                      bool IgnoreCase = false)
+      : PrecomputedOptTable(StrTable, PrefixesTable, OptionInfos, {}, {},
+                            PrefixesUnionOffsets, IgnoreCase) {}
 };
 
 } // end namespace opt
@@ -452,33 +504,36 @@ class PrecomputedOptTable : public OptTable {
 #define LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(                                       
\
     ID_PREFIX, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS,  
\
     ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,       
\
-    METAVAR, VALUES)                                                           
\
+    METAVAR, VALUES, COMMANDIDS_OFFSET)                                        
\
   ID_PREFIX##ID
 
 #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, VALUES)
+                         HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES,      
\
+                         COMMANDIDS_OFFSET)                                    
\
+  LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(                                             
\
+      OPT_, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS,     
\
+      ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,     
\
+      METAVAR, VALUES, COMMANDIDS_OFFSET)
 
 #define LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(                                
\
     ID_PREFIX, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS,  
\
     ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,       
\
-    METAVAR, VALUES)                                                           
\
+    METAVAR, VALUES, COMMANDIDS_OFFSET)                                        
\
   llvm::opt::OptTable::Info {                                                  
\
     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      
\
+        VISIBILITY, ID_PREFIX##GROUP, ID_PREFIX##ALIAS, ALIASARGS, VALUES,     
\
+        COMMANDIDS_OFFSET                                                      
\
   }
 
 #define LLVM_CONSTRUCT_OPT_INFO(                                               
\
     PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS, ALIASARGS,  
\
-    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES) 
\
+    FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, 
\
+    COMMANDIDS_OFFSET)                                                         
\
   LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(                                      
\
       OPT_, PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS,     
\
       ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS,     
\
-      METAVAR, VALUES)
+      METAVAR, VALUES, COMMANDIDS_OFFSET)
 
 #endif // LLVM_OPTION_OPTTABLE_H
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index c4188b3b12112..7c94e78c41e67 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -20,6 +20,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cassert>
+#include <cstddef>
 #include <memory>
 #include <string>
 #include <utility>
@@ -202,6 +203,17 @@ void ArgList::print(raw_ostream &O) const {
 LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
 #endif
 
+StringRef ArgList::getSubcommand() const {
+  for (const Arg *A : *this) {
+    if (A->getOption().getKind() == Option::InputClass) {
+      if (StringRef(A->getValue()).empty())
+        return StringRef();
+      return A->getValue();
+    }
+  }
+  return StringRef();
+}
+
 void InputArgList::releaseMemory() {
   // An InputArgList always owns its arguments.
   for (Arg *A : *this)
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index 6d10e6154147e..80c36407e3b70 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -80,8 +80,15 @@ OptSpecifier::OptSpecifier(const Option *Opt) : 
ID(Opt->getID()) {}
 OptTable::OptTable(const StringTable &StrTable,
                    ArrayRef<StringTable::Offset> PrefixesTable,
                    ArrayRef<Info> OptionInfos, bool IgnoreCase)
+    : OptTable(StrTable, PrefixesTable, OptionInfos, {}, {}, IgnoreCase) {}
+
+OptTable::OptTable(const StringTable &StrTable,
+                   ArrayRef<StringTable::Offset> PrefixesTable,
+                   ArrayRef<Info> OptionInfos, ArrayRef<Command> Commands,
+                   ArrayRef<unsigned> CommandIDsTable, bool IgnoreCase)
     : StrTable(&StrTable), PrefixesTable(PrefixesTable),
-      OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) {
+      OptionInfos(OptionInfos), Commands(Commands),
+      CommandIDsTable(CommandIDsTable), IgnoreCase(IgnoreCase) {
   // Explicitly zero initialize the error to work around a bug in array
   // value-initialization on MinGW with gcc 4.3.5.
 
@@ -415,16 +422,18 @@ std::unique_ptr<Arg> 
OptTable::parseOneArgGrouped(InputArgList &Args,
 
 std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned 
&Index,
                                            Visibility VisibilityMask) const {
-  return internalParseOneArg(Args, Index, [VisibilityMask](const Option &Opt) {
-    return !Opt.hasVisibilityFlag(VisibilityMask);
-  });
+  return internalParseOneArg(Args, Index, nullptr,
+                             [VisibilityMask](const Option &Opt) {
+                               return !Opt.hasVisibilityFlag(VisibilityMask);
+                             });
 }
 
 std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned 
&Index,
                                            unsigned FlagsToInclude,
                                            unsigned FlagsToExclude) const {
   return internalParseOneArg(
-      Args, Index, [FlagsToInclude, FlagsToExclude](const Option &Opt) {
+      Args, Index, nullptr,
+      [FlagsToInclude, FlagsToExclude](const Option &Opt) {
         if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude))
           return true;
         if (Opt.hasFlag(FlagsToExclude))
@@ -434,7 +443,7 @@ std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList 
&Args, unsigned &Index,
 }
 
 std::unique_ptr<Arg> OptTable::internalParseOneArg(
-    const ArgList &Args, unsigned &Index,
+    const ArgList &Args, unsigned &Index, const Command *ActiveCommand,
     std::function<bool(const Option &)> ExcludeOption) const {
   unsigned Prev = Index;
   StringRef Str = Args.getArgString(Index);
@@ -476,6 +485,18 @@ std::unique_ptr<Arg> OptTable::internalParseOneArg(
     if (ExcludeOption(Opt))
       continue;
 
+    // If a command is active, accept options for that command.
+    if (ActiveCommand) {
+      unsigned ActiveCommandID = ActiveCommand - Commands.data();
+      ArrayRef<unsigned> CommandIDs = Start->getCommandIDs(CommandIDsTable);
+      bool IsInCommand = is_contained(CommandIDs, ActiveCommandID);
+      // Command ID 0 is the top level command.
+      bool IsGlobal = is_contained(CommandIDs, 0);
+      // If not part of the command and not a global option, continue.
+      if (!IsInCommand && !IsGlobal)
+        continue;
+    }
+
     // See if this option matches.
     if (std::unique_ptr<Arg> A =
             Opt.accept(Args, StringRef(Args.getArgString(Index), ArgSize),
@@ -534,6 +555,21 @@ InputArgList OptTable::internalParseArgs(
 
   MissingArgIndex = MissingArgCount = 0;
   unsigned Index = 0, End = ArgArr.size();
+  const Command *ActiveCommand = nullptr;
+
+  // Look for subcommand which is positional.
+  if (!Commands.empty() && Index < End) {
+    StringRef FirstArg = Args.getArgString(Index);
+    if (isInput(PrefixesUnion, FirstArg)) {
+      for (const auto &C : Commands) {
+        if (FirstArg == C.Name) {
+          ActiveCommand = &C;
+          break;
+        }
+      }
+    }
+  }
+
   while (Index < End) {
     // Ingore nullptrs, they are response file's EOL markers
     if (Args.getArgString(Index) == nullptr) {
@@ -558,9 +594,10 @@ InputArgList OptTable::internalParseArgs(
     }
 
     unsigned Prev = Index;
-    std::unique_ptr<Arg> A = GroupedShortOptions
-                 ? parseOneArgGrouped(Args, Index)
-                 : internalParseOneArg(Args, Index, ExcludeOption);
+    std::unique_ptr<Arg> A =
+        GroupedShortOptions
+            ? parseOneArgGrouped(Args, Index)
+            : internalParseOneArg(Args, Index, ActiveCommand, ExcludeOption);
     assert((Index > Prev || GroupedShortOptions) &&
            "Parser failed to consume argument.");
 
@@ -715,9 +752,10 @@ static const char *getOptionHelpGroup(const OptTable 
&Opts, OptSpecifier Id) {
 
 void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title,
                          bool ShowHidden, bool ShowAllAliases,
-                         Visibility VisibilityMask) const {
+                         Visibility VisibilityMask,
+                         StringRef Subcommand) const {
   return internalPrintHelp(
-      OS, Usage, Title, ShowHidden, ShowAllAliases,
+      OS, Usage, Title, Subcommand, ShowHidden, ShowAllAliases,
       [VisibilityMask](const Info &CandidateInfo) -> bool {
         return (CandidateInfo.Visibility & VisibilityMask) == 0;
       },
@@ -730,7 +768,7 @@ void OptTable::printHelp(raw_ostream &OS, const char 
*Usage, const char *Title,
   bool ShowHidden = !(FlagsToExclude & HelpHidden);
   FlagsToExclude &= ~HelpHidden;
   return internalPrintHelp(
-      OS, Usage, Title, ShowHidden, ShowAllAliases,
+      OS, Usage, Title, {}, ShowHidden, ShowAllAliases,
       [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {
         if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude))
           return true;
@@ -742,8 +780,9 @@ void OptTable::printHelp(raw_ostream &OS, const char 
*Usage, const char *Title,
 }
 
 void OptTable::internalPrintHelp(
-    raw_ostream &OS, const char *Usage, const char *Title, bool ShowHidden,
-    bool ShowAllAliases, std::function<bool(const Info &)> ExcludeOption,
+    raw_ostream &OS, const char *Usage, const char *Title, StringRef 
Subcommand,
+    bool ShowHidden, bool ShowAllAliases,
+    std::function<bool(const Info &)> ExcludeOption,
     Visibility VisibilityMask) const {
   OS << "OVERVIEW: " << Title << "\n\n";
   OS << "USAGE: " << Usage << "\n\n";
@@ -751,6 +790,35 @@ void OptTable::internalPrintHelp(
   // Render help text into a map of group-name to a list of (option, help)
   // pairs.
   std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp;
+  StringRef TopLevelCommandName = "TopLevelCommand";
+  if (Subcommand.empty()) {
+    // Assume top level command (toolname) by default.
+    Subcommand = TopLevelCommandName;
+  }
+
+  const Command *ActiveCommand = nullptr;
+  for (const auto &C : Commands) {
+    if (Subcommand == C.Name) {
+      ActiveCommand = &C;
+      if (ActiveCommand->HelpText)
+        OS << ActiveCommand->HelpText << "\n\n";
+      // TODO: Need to sortout how to maintain helptext for toplevel and
+      // subcommands and show them in view. What does existing tool do?
+      break;
+    }
+  }
+
+  if ((!ActiveCommand || ActiveCommand->Name == TopLevelCommandName) &&
+      Commands.size() > 1) {
+    OS << "SUBCOMMANDS:\n\n";
+    for (const auto &C : Commands) {
+      if (C.Name == TopLevelCommandName)
+        continue;
+      // TODO(prabhuk): This should be better aligned in UI using a helper
+      OS << C.Name << " - " << C.HelpText << "\n";
+    }
+    OS << "\n";
+  }
 
   for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) {
     // FIXME: Split out option groups.
@@ -764,6 +832,18 @@ void OptTable::internalPrintHelp(
     if (ExcludeOption(CandidateInfo))
       continue;
 
+    if (ActiveCommand) {
+      // ActiveCommand won't be set for tools that did not create command group
+      // info table.
+      // TODO: Move this to a lambda outside the loop.
+      ArrayRef<unsigned> CommandIDs =
+          CandidateInfo.getCommandIDs(CommandIDsTable);
+      unsigned ActiveCommandID = ActiveCommand - Commands.data();
+      bool IsInCommand = is_contained(CommandIDs, ActiveCommandID);
+      if (!IsInCommand)
+        continue;
+    }
+
     // If an alias doesn't have a help text, show a help text for the aliased
     // option instead.
     const char *HelpText = getOptionHelpText(Id, VisibilityMask);
@@ -791,8 +871,12 @@ void OptTable::internalPrintHelp(
 
 GenericOptTable::GenericOptTable(const StringTable &StrTable,
                                  ArrayRef<StringTable::Offset> PrefixesTable,
-                                 ArrayRef<Info> OptionInfos, bool IgnoreCase)
-    : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase) {
+                                 ArrayRef<Info> OptionInfos,
+                                 ArrayRef<Command> Commands,
+                                 ArrayRef<unsigned> CommandIDsTable,
+                                 bool IgnoreCase)
+    : OptTable(StrTable, PrefixesTable, OptionInfos, Commands, CommandIDsTable,
+               IgnoreCase) {
 
   std::set<StringRef> TmpPrefixesUnion;
   for (auto const &Info : OptionInfos.drop_front(FirstSearchableIndex))
diff --git a/llvm/unittests/Option/OptionMarshallingTest.cpp 
b/llvm/unittests/Option/OptionMarshallingTest.cpp
index 005144b91bf7f..1f17ad217e9ee 100644
--- a/llvm/unittests/Option/OptionMarshallingTest.cpp
+++ b/llvm/unittests/Option/OptionMarshallingTest.cpp
@@ -29,8 +29,9 @@ static const OptionWithMarshallingInfo MarshallingTable[] = {
 #define OPTION_WITH_MARSHALLING(                                               
\
     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)   
\
+    COMMANDIDS_OFFSET, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE,      
\
+    IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, 
\
+    TABLE_INDEX)                                                               
\
   {PREFIXED_NAME_OFFSET, #KEYPATH, #IMPLIED_CHECK, #IMPLIED_VALUE},
 #include "Opts.inc"
 #undef OPTION_WITH_MARSHALLING
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp 
b/llvm/utils/TableGen/OptionParserEmitter.cpp
index a470fbbcadd58..a89688c9ff799 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -9,6 +9,7 @@
 #include "Common/OptEmitter.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/InterleavedRange.h"
@@ -258,6 +259,13 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
   std::vector<const Record *> Opts = 
Records.getAllDerivedDefinitions("Option");
   llvm::sort(Opts, IsOptionRecordsLess);
 
+  std::vector<const Record *> Commands =
+      Records.getAllDerivedDefinitions("Command");
+  // TopLevelCommand should come first.
+  std::stable_partition(Commands.begin(), Commands.end(), [](const Record *R) {
+    return R->getName() == "TopLevelCommand";
+  });
+
   emitSourceFileHeader("Option Parsing Definitions", OS);
 
   // Generate prefix groups.
@@ -271,6 +279,20 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
     Prefixes.try_emplace(PrefixKey, 0);
   }
 
+  // Generate command groups.
+  typedef SmallVector<StringRef, 2> CommandKeyT;
+  typedef std::map<CommandKeyT, unsigned> CommandIDsT;
+  CommandIDsT CommandIDs;
+  CommandIDs.try_emplace(CommandKeyT(), 0);
+  for (const Record &R : llvm::make_pointee_range(Opts)) {
+    std::vector<const Record *> RCommands =
+        R.getValueAsListOfDefs("CommandGroup");
+    CommandKeyT CommandKey;
+    for (const auto &Command : RCommands)
+      CommandKey.push_back(Command->getName());
+    CommandIDs.try_emplace(CommandKey, 0);
+  }
+
   DenseSet<StringRef> PrefixesUnionSet;
   for (const auto &[Prefix, _] : Prefixes)
     PrefixesUnionSet.insert_range(Prefix);
@@ -323,6 +345,39 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
   OS << "\n};\n";
   OS << "#endif // OPTTABLE_PREFIXES_TABLE_CODE\n\n";
 
+  // Dump command IDs.
+  OS << "/////////";
+  OS << "// Command IDs\n\n";
+  OS << "#ifdef OPTTABLE_COMMAND_IDS_TABLE_CODE\n";
+  OS << "static constexpr unsigned OptionCommandIDsTable[] = {\n";
+  {
+    // Ensure the first command set is always empty.
+    assert(!CommandIDs.empty() &&
+           "We should always emit an empty set of commands");
+    assert(CommandIDs.begin()->first.empty() &&
+           "First command set should always be empty");
+    llvm::ListSeparator Sep(",\n");
+    unsigned CurIndex = 0;
+    for (auto &[Command, CommandIndex] : CommandIDs) {
+      // First emit the number of command strings in this list of commands.
+      OS << Sep << "  " << Command.size() << " /* commands */";
+      CommandIndex = CurIndex;
+      assert((CurIndex == 0 || !Command.empty()) &&
+             "Only first command set should be empty!");
+      for (const auto &CommandKey : Command) {
+        auto It = llvm::find_if(Commands, [&](const Record *R) {
+          return R->getName() == CommandKey;
+        });
+        assert(It != Commands.end() && "Command not found");
+        OS << ", " << std::distance(Commands.begin(), It) << " /* '"
+           << CommandKey << "' */";
+      }
+      CurIndex += Command.size() + 1;
+    }
+  }
+  OS << "\n};\n";
+  OS << "#endif // OPTTABLE_COMMAND_IDS_TABLE_CODE\n\n";
+
   // Dump prefixes union.
   OS << "/////////\n";
   OS << "// Prefix Union\n\n";
@@ -400,7 +455,22 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
     OS << ", nullptr";
 
     // The option Values (unused for groups).
-    OS << ", nullptr)\n";
+    OS << ", nullptr";
+
+    // The option CommandIDsOffset.
+    OS << ", ";
+    if (R.getValue("CommandGroup") != nullptr) {
+      std::vector<const Record *> CommandGroup =
+          R.getValueAsListOfDefs("CommandGroup");
+      CommandKeyT CommandKey;
+      for (const auto &Command : CommandGroup)
+        CommandKey.push_back(Command->getName());
+      OS << CommandIDs[CommandKey];
+    } else {
+      // The option CommandIDsOffset (for default top level toolname is 0).
+      OS << " 0";
+    }
+    OS << ")\n";
   }
   OS << "\n";
 
@@ -527,6 +597,20 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
       OS << getOptionName(R) << "_Values";
     else
       OS << "nullptr";
+
+    // The option CommandIDsOffset.
+    OS << ", ";
+    if (R.getValue("CommandGroup") != nullptr) {
+      std::vector<const Record *> CommandGroup =
+          R.getValueAsListOfDefs("CommandGroup");
+      CommandKeyT CommandKey;
+      for (const auto &Command : CommandGroup)
+        CommandKey.push_back(Command->getName());
+      OS << CommandIDs[CommandKey];
+    } else {
+      // The option CommandIDsOffset (for default top level toolname is 0).
+      OS << " 0";
+    }
   };
 
   auto IsMarshallingOption = [](const Record &R) {
@@ -595,6 +679,19 @@ static void emitOptionParser(const RecordKeeper &Records, 
raw_ostream &OS) {
 
   OS << "#endif // SIMPLE_ENUM_VALUE_TABLE\n";
   OS << "\n";
+  OS << "/////////\n";
+  OS << "\n// Commands\n\n";
+  OS << "#ifdef OPTTABLE_COMMANDS_CODE\n";
+  OS << "static constexpr llvm::opt::OptTable::Command OptionCommands[] = {\n";
+  for (const Record *Command : Commands) {
+    OS << "  { \"" << Command->getValueAsString("Name") << "\", ";
+    if (Command->isSubClassOf("Subcommand"))
+      OS << "\"" << Command->getValueAsString("HelpText") << "\" },\n";
+    else
+      OS << "nullptr },\n";
+  }
+  OS << "};\n";
+  OS << "#endif // OPTTABLE_COMMANDS_CODE\n\n";
 
   OS << "\n";
 }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to