[clang] [clang-tools-extra] [lld] [llvm] [llvm] Add subcommand support for OptTable (PR #155026)
Prabhu Rajasekaran via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 23 14:46:46 PDT 2025
https://github.com/Prabhuk updated https://github.com/llvm/llvm-project/pull/155026
>From 3584c6aacac629c51c4d1e8e08258c0c24f3a165 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 19 Aug 2025 15:48:47 -0700
Subject: [PATCH 01/19] [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";
}
>From 8665d9eea63c69de644e582afc3fa2a3bf240295 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 22 Aug 2025 22:04:00 +0000
Subject: [PATCH 02/19] Cleanup internalPrintHelp code
---
llvm/lib/Option/OptTable.cpp | 73 ++++++++++++++++++------------------
1 file changed, 37 insertions(+), 36 deletions(-)
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index 80c36407e3b70..d8fcbb9193a6e 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -779,6 +779,15 @@ void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title,
Visibility(0));
}
+static const OptTable::Command *
+getActiveCommand(ArrayRef<OptTable::Command> Commands, StringRef Subcommand) {
+ for (const auto &C : Commands) {
+ if (Subcommand == C.Name)
+ return &C;
+ }
+ return nullptr;
+}
+
void OptTable::internalPrintHelp(
raw_ostream &OS, const char *Usage, const char *Title, StringRef Subcommand,
bool ShowHidden, bool ShowAllAliases,
@@ -790,35 +799,36 @@ 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;
+ const Command *ActiveCommand = getActiveCommand(Commands, Subcommand);
+ if (ActiveCommand) {
+ if (ActiveCommand->HelpText)
+ OS << ActiveCommand->HelpText << "\n\n";
+ } else {
+ // Assume top level command (toolname) is active.
+ StringRef TopLevelCommandName = "TopLevelCommand";
+ if (Commands.size() > 1) {
+ OS << "SUBCOMMANDS:\n\n";
+ for (const auto &C : Commands) {
+ if (C.Name == TopLevelCommandName)
+ continue;
+ OS << C.Name << " - " << C.HelpText << "\n";
+ }
+ OS << "\n";
}
}
- 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";
- }
+ auto DoesOptionBelongToActiveCommand =
+ [this, &ActiveCommand](const Info &CandidateInfo) {
+ // ActiveCommand won't be set for tools that did not create command
+ // group info table.
+ if (!ActiveCommand)
+ return true;
+ ArrayRef<unsigned> CommandIDs =
+ CandidateInfo.getCommandIDs(CommandIDsTable);
+ unsigned ActiveCommandID = ActiveCommand - Commands.data();
+ return is_contained(CommandIDs, ActiveCommandID);
+ };
for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) {
// FIXME: Split out option groups.
@@ -832,17 +842,8 @@ 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 (!DoesOptionBelongToActiveCommand(CandidateInfo))
+ continue;
// If an alias doesn't have a help text, show a help text for the aliased
// option instead.
>From d485046726b79d202848f2bac049f33684ff01af Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 22 Aug 2025 22:05:59 +0000
Subject: [PATCH 03/19] Fix formatting
---
llvm/include/llvm/Option/OptParser.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index 2bf920be8d946..9d3b660b6312c 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -296,4 +296,4 @@ class ValueExtractor<code extractor> { code ValueExtractor = extractor; }
def INPUT : Option<[], "<input>", KIND_INPUT, [TopLevelCommand]>;
def UNKNOWN : Option<[], "<unknown>", KIND_UNKNOWN, [TopLevelCommand]>;
-#endif // LLVM_OPTION_OPTPARSER_TD
\ No newline at end of file
+#endif // LLVM_OPTION_OPTPARSER_TD
>From b325c781bb68e24e09a534c31efc1a5663b7ef16 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 22 Aug 2025 23:02:50 +0000
Subject: [PATCH 04/19] Add custom usage string to tablegen for each subcommand
---
llvm/examples/OptSubcommand/Opts.td | 9 ++++++---
llvm/include/llvm/Option/OptParser.td | 3 ++-
llvm/include/llvm/Option/OptTable.h | 1 +
llvm/lib/Option/OptTable.cpp | 6 +++---
llvm/utils/TableGen/OptionParserEmitter.cpp | 9 +++++----
5 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/llvm/examples/OptSubcommand/Opts.td b/llvm/examples/OptSubcommand/Opts.td
index 8c4f66b8c3043..a724fb739ec72 100644
--- a/llvm/examples/OptSubcommand/Opts.td
+++ b/llvm/examples/OptSubcommand/Opts.td
@@ -1,10 +1,13 @@
include "llvm/Option/OptParser.td"
-def sc_foo : Subcommand<"foo", "HelpText for Subcommand foo.">;
+def sc_foo : Subcommand<"foo", "HelpText for Subcommand foo.",
+ "OptSubcommand foo <options>">;
-def sc_bar : Subcommand<"bar", "HelpText for Subcommand bar.">;
+def sc_bar : Subcommand<"bar", "HelpText for Subcommand bar.",
+ "OptSubcommand bar <options>">;
-def help : Flag<["--"], "help">, HelpText<"Top Level Help Text for the tool.">;
+def help : Flag<["--"], "help">,
+ HelpText<"OptSubcommand <subcommand> <options>">;
def version : Flag<["-"], "version">,
HelpText<"Toplevel Display the version number">;
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index 9d3b660b6312c..ddcf9de0d9fe1 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -102,8 +102,9 @@ class HelpTextVariant<list<OptionVisibility> visibilities, string text> {
class Command<string name> { string Name = name; }
// Class definition for positional subcommands.
-class Subcommand<string name, string helpText> : Command<name> {
+class Subcommand<string name, string helpText, string usage> : Command<name> {
string HelpText = helpText;
+ string Usage = usage;
}
// Compile time representation for top level command (aka toolname).
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index 3f362521a7e64..911eb4ae93066 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -58,6 +58,7 @@ class LLVM_ABI OptTable {
struct Command {
const char *Name;
const char *HelpText;
+ const char *Usage;
};
/// Entry for a single option instance in the option data table.
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index d8fcbb9193a6e..af3526cd24331 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -794,7 +794,6 @@ void OptTable::internalPrintHelp(
std::function<bool(const Info &)> ExcludeOption,
Visibility VisibilityMask) const {
OS << "OVERVIEW: " << Title << "\n\n";
- OS << "USAGE: " << Usage << "\n\n";
// Render help text into a map of group-name to a list of (option, help)
// pairs.
@@ -802,9 +801,10 @@ void OptTable::internalPrintHelp(
const Command *ActiveCommand = getActiveCommand(Commands, Subcommand);
if (ActiveCommand) {
- if (ActiveCommand->HelpText)
- OS << ActiveCommand->HelpText << "\n\n";
+ OS << ActiveCommand->HelpText << "\n\n";
+ OS << "USAGE: " << ActiveCommand->Usage << "\n\n";
} else {
+ OS << "USAGE: " << Usage << "\n\n";
// Assume top level command (toolname) is active.
StringRef TopLevelCommandName = "TopLevelCommand";
if (Commands.size() > 1) {
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index a89688c9ff799..53683f7ef4d30 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -685,10 +685,11 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
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";
+ if (Command->isSubClassOf("Subcommand")) {
+ OS << "\"" << Command->getValueAsString("HelpText") << "\", ";
+ OS << "\"" << Command->getValueAsString("Usage") << "\" },\n";
+ } else
+ OS << "nullptr, nullptr},\n";
}
OS << "};\n";
OS << "#endif // OPTTABLE_COMMANDS_CODE\n\n";
>From 61334ef88badc3088758a0955738d89a7254f1cd Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 22 Aug 2025 23:05:41 +0000
Subject: [PATCH 05/19] Add todo
---
llvm/include/llvm/Option/OptParser.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index ddcf9de0d9fe1..dba529bd65841 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -104,7 +104,7 @@ class Command<string name> { string Name = name; }
// Class definition for positional subcommands.
class Subcommand<string name, string helpText, string usage> : Command<name> {
string HelpText = helpText;
- string Usage = usage;
+ string Usage = usage; //TODO(prabhuk): This could be part of another subclass of subcommand to make passing usage string optional.
}
// Compile time representation for top level command (aka toolname).
>From 1690f3b5db41e9b375b188eee7179ae5996e9cbd Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 26 Aug 2025 16:27:25 +0000
Subject: [PATCH 06/19] Make subcommand usage text optional
---
llvm/examples/OptSubcommand/Opts.td | 3 +--
llvm/include/llvm/Option/OptParser.td | 3 ++-
llvm/lib/Option/OptTable.cpp | 3 ++-
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/llvm/examples/OptSubcommand/Opts.td b/llvm/examples/OptSubcommand/Opts.td
index a724fb739ec72..21a417ffec12a 100644
--- a/llvm/examples/OptSubcommand/Opts.td
+++ b/llvm/examples/OptSubcommand/Opts.td
@@ -1,7 +1,6 @@
include "llvm/Option/OptParser.td"
-def sc_foo : Subcommand<"foo", "HelpText for Subcommand foo.",
- "OptSubcommand foo <options>">;
+def sc_foo : Subcommand<"foo", "HelpText for Subcommand foo.">;
def sc_bar : Subcommand<"bar", "HelpText for Subcommand bar.",
"OptSubcommand bar <options>">;
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index dba529bd65841..365228d41711a 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -102,7 +102,8 @@ class HelpTextVariant<list<OptionVisibility> visibilities, string text> {
class Command<string name> { string Name = name; }
// Class definition for positional subcommands.
-class Subcommand<string name, string helpText, string usage> : Command<name> {
+class Subcommand<string name, string helpText, string usage = "">
+ : Command<name> {
string HelpText = helpText;
string Usage = usage; //TODO(prabhuk): This could be part of another subclass of subcommand to make passing usage string optional.
}
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index af3526cd24331..e51ae39ffa544 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -802,7 +802,8 @@ void OptTable::internalPrintHelp(
const Command *ActiveCommand = getActiveCommand(Commands, Subcommand);
if (ActiveCommand) {
OS << ActiveCommand->HelpText << "\n\n";
- OS << "USAGE: " << ActiveCommand->Usage << "\n\n";
+ if (!StringRef(ActiveCommand->Usage).empty())
+ OS << "USAGE: " << ActiveCommand->Usage << "\n\n";
} else {
OS << "USAGE: " << Usage << "\n\n";
// Assume top level command (toolname) is active.
>From 9c419d9f064fcf1234d2b863836a6c156c56b77d Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 26 Aug 2025 15:13:29 -0700
Subject: [PATCH 07/19] Make subcommand's position flexible and not just as
first argument.
---
.../examples/OptSubcommand/llvm-hello-sub.cpp | 51 ++++++++++++-------
llvm/include/llvm/Option/ArgList.h | 8 ++-
llvm/include/llvm/Option/OptTable.h | 2 +
llvm/lib/Option/ArgList.cpp | 20 ++++++--
llvm/lib/Option/OptTable.cpp | 6 ++-
5 files changed, 62 insertions(+), 25 deletions(-)
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index aecc981e485c1..c6f3c1e0be754 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -1,6 +1,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/raw_ostream.h"
@@ -54,40 +55,54 @@ int main(int argc, char **argv) {
InputArgList Args = T.ParseArgs(ArrayRef(argv + 1, argc - 1), MissingArgIndex,
MissingArgCount);
- StringRef Subcommand = Args.getSubcommand();
+ auto SubcommandResult = Args.getSubcommand(T.getCommands());
+ if (!SubcommandResult) {
+ llvm::errs() << "error: unknown subcommand '"
+ << toString(SubcommandResult.takeError())
+ << "'. See --help.\n";
+ return 1;
+ }
+ // Valid subcommand found.
+ StringRef Subcommand = *SubcommandResult;
+
+ // Handle help. When help options is found, ignore all other options and exit
+ // after printing help.
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;
+ bool HasUnknownOptions = false;
+ for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
+ HasUnknownOptions = true;
+ llvm::errs() << "Unknown option `" << A->getAsString(Args) << "'\n";
}
-
- if (Subcommand == "foo") {
+ if (HasUnknownOptions) {
+ llvm::errs() << "See `OptSubcommand --help`.\n";
+ return 1;
+ }
+ if (Subcommand.empty()) {
+ if (Args.hasArg(OPT_version)) {
+ llvm::outs() << "LLVM Hello Subcommand Example 1.0\n";
+ }
+ } else 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;
+
+ if (Args.hasArg(OPT_version))
+ llvm::outs() << "LLVM Hello Subcommand foo Example 1.0\n";
+
} 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;
+
+ if (Args.hasArg(OPT_version))
+ llvm::outs() << "LLVM Hello Subcommand bar Example 1.0\n";
}
return 0;
diff --git a/llvm/include/llvm/Option/ArgList.h b/llvm/include/llvm/Option/ArgList.h
index 2394b2e8301b8..98b724412102d 100644
--- a/llvm/include/llvm/Option/ArgList.h
+++ b/llvm/include/llvm/Option/ArgList.h
@@ -20,6 +20,7 @@
#include "llvm/Option/OptSpecifier.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Error.h"
#include <algorithm>
#include <cstddef>
#include <initializer_list>
@@ -280,8 +281,11 @@ class ArgList {
/// list.
virtual unsigned getNumInputArgStrings() const = 0;
- /// getSubcommand - Return the active subcommand, if one exists.
- LLVM_ABI StringRef getSubcommand() const;
+ /// getSubcommand - Return the active subcommand, if one exists. Return empty
+ /// StringRef if no subcommand passed. Return an error if an invalid/unknown
+ /// subcommand is found as the first argument.
+ LLVM_ABI llvm::Expected<StringRef>
+ getSubcommand(const ArrayRef<OptTable::Command> Commands) const;
/// @}
/// @name Argument Lookup Utilities
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index 911eb4ae93066..ac94bdf17d8a8 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -212,6 +212,8 @@ class LLVM_ABI OptTable {
/// Return the string table used for option names.
const StringTable &getStrTable() const { return *StrTable; }
+ const ArrayRef<Command> getCommands() const { return Commands; }
+
/// Return the prefixes table used for option names.
ArrayRef<StringTable::Offset> getPrefixesTable() const {
return PrefixesTable;
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index 7c94e78c41e67..418052bf202f7 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -14,9 +14,11 @@
#include "llvm/Config/llvm-config.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/OptSpecifier.h"
+#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
@@ -203,14 +205,24 @@ void ArgList::print(raw_ostream &O) const {
LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
#endif
-StringRef ArgList::getSubcommand() const {
+llvm::Expected<StringRef>
+ArgList::getSubcommand(const ArrayRef<OptTable::Command> Commands) const {
+ // Identify if a valid subcommand is passed.
for (const Arg *A : *this) {
if (A->getOption().getKind() == Option::InputClass) {
- if (StringRef(A->getValue()).empty())
- return StringRef();
- return A->getValue();
+ assert(!StringRef(A->getValue()).empty());
+ for (auto CMD : Commands) {
+ if (StringRef(CMD.Name) == "TopLevelCommand")
+ continue;
+ if (StringRef(CMD.Name) == A->getValue())
+ return A->getValue(); // Found a valid subcommand.
+ }
+ // Invalid/Unexpected subcommand passed. Let the users handle this case as
+ // they see fit. return
+ // llvm::createStringError(std::errc::invalid_argument, A->getValue());
}
}
+ // No registered subcommand found.
return StringRef();
}
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index e51ae39ffa544..e223bf64715ba 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -810,9 +810,13 @@ void OptTable::internalPrintHelp(
StringRef TopLevelCommandName = "TopLevelCommand";
if (Commands.size() > 1) {
OS << "SUBCOMMANDS:\n\n";
+ // This loop prints subcommands list and sets ActiveCommand to
+ // TopLevelCommand while iterating over all commands.
for (const auto &C : Commands) {
- if (C.Name == TopLevelCommandName)
+ if (C.Name == TopLevelCommandName) {
+ ActiveCommand = &C;
continue;
+ }
OS << C.Name << " - " << C.HelpText << "\n";
}
OS << "\n";
>From 19e643754ca3455f9be1be14a5685540fbf4ea53 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 26 Aug 2025 15:22:44 -0700
Subject: [PATCH 08/19] Handle unknown subcommands in a graceful manner.
---
llvm/lib/Option/ArgList.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index 418052bf202f7..b963d9f791654 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -218,8 +218,9 @@ ArgList::getSubcommand(const ArrayRef<OptTable::Command> Commands) const {
return A->getValue(); // Found a valid subcommand.
}
// Invalid/Unexpected subcommand passed. Let the users handle this case as
- // they see fit. return
- // llvm::createStringError(std::errc::invalid_argument, A->getValue());
+ // they see fit.
+ return llvm::createStringError(std::errc::invalid_argument,
+ A->getValue());
}
}
// No registered subcommand found.
>From 8d5363b2d700fb22750a330a6591ab1c6725850b Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 26 Aug 2025 16:07:23 -0700
Subject: [PATCH 09/19] Handle more than one subcommand passed in commandline.
---
.../examples/OptSubcommand/llvm-hello-sub.cpp | 4 +--
llvm/lib/Option/ArgList.cpp | 29 ++++++++++++++-----
2 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index c6f3c1e0be754..3741e95b177ea 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -57,9 +57,9 @@ int main(int argc, char **argv) {
auto SubcommandResult = Args.getSubcommand(T.getCommands());
if (!SubcommandResult) {
- llvm::errs() << "error: unknown subcommand '"
+ llvm::errs() << "error: more than one subcommand passed ["
<< toString(SubcommandResult.takeError())
- << "'. See --help.\n";
+ << "]. See --help.\n";
return 1;
}
// Valid subcommand found.
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index b963d9f791654..240d05905392c 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -208,23 +208,36 @@ LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
llvm::Expected<StringRef>
ArgList::getSubcommand(const ArrayRef<OptTable::Command> Commands) const {
// Identify if a valid subcommand is passed.
+ StringRef SubCommand = {};
for (const Arg *A : *this) {
if (A->getOption().getKind() == Option::InputClass) {
assert(!StringRef(A->getValue()).empty());
for (auto CMD : Commands) {
if (StringRef(CMD.Name) == "TopLevelCommand")
continue;
- if (StringRef(CMD.Name) == A->getValue())
- return A->getValue(); // Found a valid subcommand.
+ if (StringRef(CMD.Name) == A->getValue()) {
+ // Found a valid subcommand.
+ if (SubCommand.empty()) {
+ SubCommand = A->getValue();
+ } else {
+ SmallString<20> SubCommandsList;
+ SubCommandsList += SubCommand;
+ SubCommandsList += ",";
+ SubCommandsList += A->getValue();
+ // More than one subcommand passed in commandline.
+ return llvm::createStringError(std::errc::invalid_argument,
+ SubCommandsList.c_str());
+ }
+ }
+ }
+ if (SubCommand.empty()) {
+ // TODO:: Return a different error for unknown subcommand
+ return llvm::createStringError(std::errc::invalid_argument,
+ A->getValue());
}
- // Invalid/Unexpected subcommand passed. Let the users handle this case as
- // they see fit.
- return llvm::createStringError(std::errc::invalid_argument,
- A->getValue());
}
}
- // No registered subcommand found.
- return StringRef();
+ return SubCommand;
}
void InputArgList::releaseMemory() {
>From 51ef95ecc6396eab3a3157ac4facab43f4295f54 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 26 Aug 2025 17:53:04 -0700
Subject: [PATCH 10/19] Handle subcommand errors in parsing through lambdas.
---
.../examples/OptSubcommand/llvm-hello-sub.cpp | 36 +++++++++++-----
llvm/include/llvm/Option/ArgList.h | 10 ++---
llvm/lib/Option/ArgList.cpp | 43 ++++++++++---------
3 files changed, 53 insertions(+), 36 deletions(-)
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index 3741e95b177ea..e5d88048240f9 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -1,4 +1,5 @@
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/Error.h"
@@ -52,19 +53,34 @@ int main(int argc, char **argv) {
InitLLVM X(argc, argv);
HelloSubOptTable T;
unsigned MissingArgIndex, MissingArgCount;
+
+ auto HandleMultipleSubcommands = [](const ArrayRef<StringRef> SubCommands) {
+ assert(SubCommands.size() > 1);
+ llvm::errs() << "error: more than one subcommand passed [\n";
+ for (auto SC : SubCommands) {
+ llvm::errs() << " `" << SC << "`\n";
+ }
+ llvm::errs() << "]\n";
+ llvm::errs() << "See --help.\n";
+ exit(1);
+ };
+
+ auto HandleOtherPositionals = [](const ArrayRef<StringRef> Positionals) {
+ assert(!Positionals.empty());
+ llvm::errs() << "error: unknown positional argument(s) [\n";
+ for (auto SC : Positionals) {
+ llvm::errs() << " `" << SC << "`\n";
+ }
+ llvm::errs() << "]\n";
+ llvm::errs() << "See --help.\n";
+ exit(1);
+ };
+
InputArgList Args = T.ParseArgs(ArrayRef(argv + 1, argc - 1), MissingArgIndex,
MissingArgCount);
- auto SubcommandResult = Args.getSubcommand(T.getCommands());
- if (!SubcommandResult) {
- llvm::errs() << "error: more than one subcommand passed ["
- << toString(SubcommandResult.takeError())
- << "]. See --help.\n";
- return 1;
- }
- // Valid subcommand found.
- StringRef Subcommand = *SubcommandResult;
-
+ StringRef Subcommand = Args.getSubcommand(
+ T.getCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
// Handle help. When help options is found, ignore all other options and exit
// after printing help.
if (Args.hasArg(OPT_help)) {
diff --git a/llvm/include/llvm/Option/ArgList.h b/llvm/include/llvm/Option/ArgList.h
index 98b724412102d..0bee87a10759d 100644
--- a/llvm/include/llvm/Option/ArgList.h
+++ b/llvm/include/llvm/Option/ArgList.h
@@ -281,11 +281,11 @@ class ArgList {
/// list.
virtual unsigned getNumInputArgStrings() const = 0;
- /// getSubcommand - Return the active subcommand, if one exists. Return empty
- /// StringRef if no subcommand passed. Return an error if an invalid/unknown
- /// subcommand is found as the first argument.
- LLVM_ABI llvm::Expected<StringRef>
- getSubcommand(const ArrayRef<OptTable::Command> Commands) const;
+ /// getSubcommand - Return the active subcommand, if one exists.
+ LLVM_ABI StringRef getSubcommand(
+ const ArrayRef<OptTable::Command> Commands,
+ std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
+ std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const;
/// @}
/// @name Argument Lookup Utilities
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index 240d05905392c..e1910c449ca99 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -205,38 +205,39 @@ void ArgList::print(raw_ostream &O) const {
LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
#endif
-llvm::Expected<StringRef>
-ArgList::getSubcommand(const ArrayRef<OptTable::Command> Commands) const {
- // Identify if a valid subcommand is passed.
+StringRef ArgList::getSubcommand(
+ const ArrayRef<OptTable::Command> Commands,
+ std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
+ std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const {
+
StringRef SubCommand = {};
+ SmallVector<StringRef, 4> SubCommands;
+ SmallVector<StringRef, 4> OtherPositionals;
for (const Arg *A : *this) {
+ bool IsSubCommand = false;
if (A->getOption().getKind() == Option::InputClass) {
- assert(!StringRef(A->getValue()).empty());
- for (auto CMD : Commands) {
+ for (const OptTable::Command CMD : Commands) {
if (StringRef(CMD.Name) == "TopLevelCommand")
continue;
if (StringRef(CMD.Name) == A->getValue()) {
- // Found a valid subcommand.
- if (SubCommand.empty()) {
- SubCommand = A->getValue();
- } else {
- SmallString<20> SubCommandsList;
- SubCommandsList += SubCommand;
- SubCommandsList += ",";
- SubCommandsList += A->getValue();
- // More than one subcommand passed in commandline.
- return llvm::createStringError(std::errc::invalid_argument,
- SubCommandsList.c_str());
- }
+ SubCommands.push_back(A->getValue());
+ IsSubCommand = true;
}
}
- if (SubCommand.empty()) {
- // TODO:: Return a different error for unknown subcommand
- return llvm::createStringError(std::errc::invalid_argument,
- A->getValue());
+ if (!IsSubCommand) {
+ OtherPositionals.push_back(A->getValue());
+ IsSubCommand = false;
}
}
}
+ if (SubCommands.size() > 1) {
+ HandleMultipleSubcommands(SubCommands);
+ }
+ if (!OtherPositionals.empty()) {
+ HandleOtherPositionals(OtherPositionals);
+ }
+ if (SubCommands.size() == 1)
+ return SubCommands.front();
return SubCommand;
}
>From 043bbc889e2c4950fcec3fa84a4db98c60ee34e5 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 15 Sep 2025 21:19:17 +0000
Subject: [PATCH 11/19] Address review comments.
---
.../examples/OptSubcommand/llvm-hello-sub.cpp | 10 ++---
llvm/include/llvm/Option/ArgList.h | 2 +-
llvm/include/llvm/Option/OptParser.td | 2 +-
llvm/include/llvm/Option/OptTable.h | 37 ++++++------------
llvm/lib/Option/ArgList.cpp | 2 +-
llvm/lib/Option/OptTable.cpp | 19 ++++-----
llvm/utils/TableGen/OptionParserEmitter.cpp | 39 ++++++++-----------
7 files changed, 42 insertions(+), 69 deletions(-)
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index e5d88048240f9..6b5495b53a63a 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -54,23 +54,21 @@ int main(int argc, char **argv) {
HelloSubOptTable T;
unsigned MissingArgIndex, MissingArgCount;
- auto HandleMultipleSubcommands = [](const ArrayRef<StringRef> SubCommands) {
+ auto HandleMultipleSubcommands = [](ArrayRef<StringRef> SubCommands) {
assert(SubCommands.size() > 1);
llvm::errs() << "error: more than one subcommand passed [\n";
- for (auto SC : SubCommands) {
+ for (auto SC : SubCommands)
llvm::errs() << " `" << SC << "`\n";
- }
llvm::errs() << "]\n";
llvm::errs() << "See --help.\n";
exit(1);
};
- auto HandleOtherPositionals = [](const ArrayRef<StringRef> Positionals) {
+ auto HandleOtherPositionals = [](ArrayRef<StringRef> Positionals) {
assert(!Positionals.empty());
llvm::errs() << "error: unknown positional argument(s) [\n";
- for (auto SC : Positionals) {
+ for (auto SC : Positionals)
llvm::errs() << " `" << SC << "`\n";
- }
llvm::errs() << "]\n";
llvm::errs() << "See --help.\n";
exit(1);
diff --git a/llvm/include/llvm/Option/ArgList.h b/llvm/include/llvm/Option/ArgList.h
index 0bee87a10759d..81f3a3bca7120 100644
--- a/llvm/include/llvm/Option/ArgList.h
+++ b/llvm/include/llvm/Option/ArgList.h
@@ -283,7 +283,7 @@ class ArgList {
/// getSubcommand - Return the active subcommand, if one exists.
LLVM_ABI StringRef getSubcommand(
- const ArrayRef<OptTable::Command> Commands,
+ ArrayRef<OptTable::Command> Commands,
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const;
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index 365228d41711a..f3d1a17cb2677 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -105,7 +105,7 @@ class Command<string name> { string Name = name; }
class Subcommand<string name, string helpText, string usage = "">
: Command<name> {
string HelpText = helpText;
- string Usage = usage; //TODO(prabhuk): This could be part of another subclass of subcommand to make passing usage string optional.
+ string Usage = usage;
}
// Compile time representation for top level command (aka toolname).
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index ac94bdf17d8a8..f8f1fc3f8bb3b 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -197,11 +197,9 @@ class LLVM_ABI OptTable {
/// manually call \c buildPrefixChars once they are fully constructed.
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);
+ ArrayRef<Info> OptionInfos, bool IgnoreCase = false,
+ ArrayRef<Command> Commands = {},
+ ArrayRef<unsigned> CommandIDsTable = {});
/// Build (or rebuild) the PrefixChars member.
void buildPrefixChars();
@@ -466,38 +464,25 @@ class GenericOptTable : public OptTable {
protected:
LLVM_ABI GenericOptTable(const StringTable &StrTable,
ArrayRef<StringTable::Offset> PrefixesTable,
- 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);
+ ArrayRef<Info> OptionInfos, bool IgnoreCase = false,
+ ArrayRef<Command> Commands = {},
+ ArrayRef<unsigned> CommandIDsTable = {});
};
class PrecomputedOptTable : public OptTable {
protected:
PrecomputedOptTable(const StringTable &StrTable,
ArrayRef<StringTable::Offset> PrefixesTable,
- ArrayRef<Info> OptionInfos, ArrayRef<Command> Commands,
- ArrayRef<unsigned> CommandIDsTable,
+ ArrayRef<Info> OptionInfos,
ArrayRef<StringTable::Offset> PrefixesUnionOffsets,
- bool IgnoreCase = false)
- : OptTable(StrTable, PrefixesTable, OptionInfos, Commands,
- CommandIDsTable, IgnoreCase) {
+ bool IgnoreCase = false, ArrayRef<Command> Commands = {},
+ ArrayRef<unsigned> CommandIDsTable = {})
+ : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase, Commands,
+ CommandIDsTable) {
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
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index e1910c449ca99..1d215ccf31b8e 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -206,7 +206,7 @@ LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
#endif
StringRef ArgList::getSubcommand(
- const ArrayRef<OptTable::Command> Commands,
+ ArrayRef<OptTable::Command> Commands,
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const {
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index e223bf64715ba..dfdb4509beb02 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -79,13 +79,9 @@ 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)
+ ArrayRef<Info> OptionInfos, bool IgnoreCase,
+ ArrayRef<Command> Commands,
+ ArrayRef<unsigned> CommandIDsTable)
: StrTable(&StrTable), PrefixesTable(PrefixesTable),
OptionInfos(OptionInfos), Commands(Commands),
CommandIDsTable(CommandIDsTable), IgnoreCase(IgnoreCase) {
@@ -877,12 +873,11 @@ void OptTable::internalPrintHelp(
GenericOptTable::GenericOptTable(const StringTable &StrTable,
ArrayRef<StringTable::Offset> PrefixesTable,
- ArrayRef<Info> OptionInfos,
+ ArrayRef<Info> OptionInfos, bool IgnoreCase,
ArrayRef<Command> Commands,
- ArrayRef<unsigned> CommandIDsTable,
- bool IgnoreCase)
- : OptTable(StrTable, PrefixesTable, OptionInfos, Commands, CommandIDsTable,
- IgnoreCase) {
+ ArrayRef<unsigned> CommandIDsTable)
+ : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase, Commands,
+ CommandIDsTable) {
std::set<StringRef> TmpPrefixesUnion;
for (auto const &Info : OptionInfos.drop_front(FirstSearchableIndex))
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index 53683f7ef4d30..5ae954f2c0eb3 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -283,6 +283,21 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
typedef SmallVector<StringRef, 2> CommandKeyT;
typedef std::map<CommandKeyT, unsigned> CommandIDsT;
CommandIDsT CommandIDs;
+
+ auto PrintCommandIdsOffset = [&CommandIDs, &OS](const Record &R) {
+ 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";
+ }
+ };
+
CommandIDs.try_emplace(CommandKeyT(), 0);
for (const Record &R : llvm::make_pointee_range(Opts)) {
std::vector<const Record *> RCommands =
@@ -459,17 +474,7 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
// 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";
- }
+ PrintCommandIdsOffset(R);
OS << ")\n";
}
OS << "\n";
@@ -600,17 +605,7 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
// 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";
- }
+ PrintCommandIdsOffset(R);
};
auto IsMarshallingOption = [](const Record &R) {
>From bc8bd1b2699bee92acd8ad99f800e37fffb764d6 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 15 Sep 2025 21:22:19 +0000
Subject: [PATCH 12/19] Fix initialization order of IgnoreCase.
---
llvm/include/llvm/Option/OptTable.h | 3 ++-
llvm/lib/Option/OptTable.cpp | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index f8f1fc3f8bb3b..44216166ece7c 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -156,13 +156,14 @@ class LLVM_ABI OptTable {
/// The option information table.
ArrayRef<Info> OptionInfos;
+ bool IgnoreCase;
+
/// The command information table.
ArrayRef<Command> Commands;
/// The command IDs table.
ArrayRef<unsigned> CommandIDsTable;
- bool IgnoreCase;
bool GroupedShortOptions = false;
bool DashDashParsing = false;
const char *EnvVar = nullptr;
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index dfdb4509beb02..ae1348a266fee 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -83,8 +83,8 @@ OptTable::OptTable(const StringTable &StrTable,
ArrayRef<Command> Commands,
ArrayRef<unsigned> CommandIDsTable)
: StrTable(&StrTable), PrefixesTable(PrefixesTable),
- OptionInfos(OptionInfos), Commands(Commands),
- CommandIDsTable(CommandIDsTable), IgnoreCase(IgnoreCase) {
+ OptionInfos(OptionInfos), IgnoreCase(IgnoreCase), Commands(Commands),
+ CommandIDsTable(CommandIDsTable) {
// Explicitly zero initialize the error to work around a bug in array
// value-initialization on MinGW with gcc 4.3.5.
>From dcb6779a245e5d187bc8aad9c244ea89be8b1133 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 15 Sep 2025 23:12:36 +0000
Subject: [PATCH 13/19] Address review comments 2/
---
llvm/lib/Option/ArgList.cpp | 3 +--
llvm/lib/Option/OptTable.cpp | 29 ++++++++-------------
llvm/utils/TableGen/OptionParserEmitter.cpp | 6 ++---
3 files changed, 15 insertions(+), 23 deletions(-)
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index 1d215ccf31b8e..17d50baad5cc0 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -210,7 +210,6 @@ StringRef ArgList::getSubcommand(
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const {
- StringRef SubCommand = {};
SmallVector<StringRef, 4> SubCommands;
SmallVector<StringRef, 4> OtherPositionals;
for (const Arg *A : *this) {
@@ -238,7 +237,7 @@ StringRef ArgList::getSubcommand(
}
if (SubCommands.size() == 1)
return SubCommands.front();
- return SubCommand;
+ return {}; // No valid usage of subcommand found.
}
void InputArgList::releaseMemory() {
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index ae1348a266fee..cab85be02e830 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -541,6 +541,14 @@ InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args,
});
}
+static const OptTable::Command *
+getActiveCommand(ArrayRef<OptTable::Command> Commands, StringRef Subcommand) {
+ const OptTable::Command *FoundSC =
+ std::find_if(Commands.begin(), Commands.end(),
+ [&](const auto &C) { return Subcommand == C.Name; });
+ return (FoundSC == Commands.end()) ? nullptr : FoundSC;
+}
+
InputArgList OptTable::internalParseArgs(
ArrayRef<const char *> ArgArr, unsigned &MissingArgIndex,
unsigned &MissingArgCount,
@@ -553,17 +561,11 @@ InputArgList OptTable::internalParseArgs(
unsigned Index = 0, End = ArgArr.size();
const Command *ActiveCommand = nullptr;
- // Look for subcommand which is positional.
+ // Look for subcommand.
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;
- }
- }
- }
+ if (isInput(PrefixesUnion, FirstArg))
+ ActiveCommand = getActiveCommand(Commands, FirstArg);
}
while (Index < End) {
@@ -775,15 +777,6 @@ void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title,
Visibility(0));
}
-static const OptTable::Command *
-getActiveCommand(ArrayRef<OptTable::Command> Commands, StringRef Subcommand) {
- for (const auto &C : Commands) {
- if (Subcommand == C.Name)
- return &C;
- }
- return nullptr;
-}
-
void OptTable::internalPrintHelp(
raw_ostream &OS, const char *Usage, const char *Title, StringRef Subcommand,
bool ShowHidden, bool ShowAllAliases,
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index 5ae954f2c0eb3..69a7176a2ba33 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -380,9 +380,9 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
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;
- });
+ auto It = std::find_if(
+ Commands.begin(), Commands.end(),
+ [&](const Record *R) { return R->getName() == CommandKey; });
assert(It != Commands.end() && "Command not found");
OS << ", " << std::distance(Commands.begin(), It) << " /* '"
<< CommandKey << "' */";
>From e7ea6476a2d9b4057639f583686e57087e154b2e Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 15 Sep 2025 23:30:52 +0000
Subject: [PATCH 14/19] Address review comments 3/
---
.../examples/OptSubcommand/llvm-hello-sub.cpp | 2 +-
llvm/include/llvm/Option/ArgList.h | 14 ++++++++--
llvm/lib/Option/ArgList.cpp | 26 +++++++++----------
3 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index 6b5495b53a63a..6a4fb61623fed 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -44,7 +44,7 @@ static constexpr OptTable::Info InfoTable[] = {
class HelloSubOptTable : public GenericOptTable {
public:
HelloSubOptTable()
- : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+ : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, false,
OptionCommands, OptionCommandIDsTable) {}
};
} // namespace
diff --git a/llvm/include/llvm/Option/ArgList.h b/llvm/include/llvm/Option/ArgList.h
index 81f3a3bca7120..59e574ab327f1 100644
--- a/llvm/include/llvm/Option/ArgList.h
+++ b/llvm/include/llvm/Option/ArgList.h
@@ -281,8 +281,18 @@ class ArgList {
/// list.
virtual unsigned getNumInputArgStrings() const = 0;
- /// getSubcommand - Return the active subcommand, if one exists.
- LLVM_ABI StringRef getSubcommand(
+ /// getSubcommand - Find a subcommand in the arguments.
+ ///
+ /// \param Commands - A list of all valid subcommands.
+ /// \param HandleMultipleSubcommands - A callback for the case where multiple
+ /// subcommands are present in the arguments. It gets a list of all found
+ /// subcommands.
+ /// \param HandleOtherPositionals - A callback for the case where positional
+ /// arguments that are not subcommands are present.
+ /// \return The name of the subcommand found. If no subcommand is found,
+ /// this returns an empty StringRef. If multiple subcommands are found, the
+ /// first one is returned.
+ StringRef getSubcommand(
ArrayRef<OptTable::Command> Commands,
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const;
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index 17d50baad5cc0..eb828fa2554ea 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -213,21 +213,19 @@ StringRef ArgList::getSubcommand(
SmallVector<StringRef, 4> SubCommands;
SmallVector<StringRef, 4> OtherPositionals;
for (const Arg *A : *this) {
- bool IsSubCommand = false;
- if (A->getOption().getKind() == Option::InputClass) {
- for (const OptTable::Command CMD : Commands) {
- if (StringRef(CMD.Name) == "TopLevelCommand")
- continue;
- if (StringRef(CMD.Name) == A->getValue()) {
- SubCommands.push_back(A->getValue());
- IsSubCommand = true;
- }
- }
- if (!IsSubCommand) {
- OtherPositionals.push_back(A->getValue());
- IsSubCommand = false;
- }
+ if (A->getOption().getKind() != Option::InputClass)
+ continue;
+
+ size_t OldSize = SubCommands.size();
+ for (const OptTable::Command &CMD : Commands) {
+ if (StringRef(CMD.Name) == "TopLevelCommand")
+ continue;
+ if (StringRef(CMD.Name) == A->getValue())
+ SubCommands.push_back(A->getValue());
}
+
+ if (SubCommands.size() == OldSize)
+ OtherPositionals.push_back(A->getValue());
}
if (SubCommands.size() > 1) {
HandleMultipleSubcommands(SubCommands);
>From ff8034a3cf2dcf2a1921043163f3e648a8657bd0 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 15 Sep 2025 23:33:45 +0000
Subject: [PATCH 15/19] Update inline comment.
---
llvm/include/llvm/Option/ArgList.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Option/ArgList.h b/llvm/include/llvm/Option/ArgList.h
index 59e574ab327f1..fdbed4d667245 100644
--- a/llvm/include/llvm/Option/ArgList.h
+++ b/llvm/include/llvm/Option/ArgList.h
@@ -281,7 +281,7 @@ class ArgList {
/// list.
virtual unsigned getNumInputArgStrings() const = 0;
- /// getSubcommand - Find a subcommand in the arguments.
+ /// getSubcommand - Find subcommand from the arguments if the usage is valid.
///
/// \param Commands - A list of all valid subcommands.
/// \param HandleMultipleSubcommands - A callback for the case where multiple
>From e99a9a02b427a42664835502ad01386bc1744369 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 15 Sep 2025 23:36:13 +0000
Subject: [PATCH 16/19] Address review comments 4/
---
llvm/lib/Option/OptTable.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index cab85be02e830..e1e2f66bc4588 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -795,14 +795,12 @@ void OptTable::internalPrintHelp(
OS << "USAGE: " << ActiveCommand->Usage << "\n\n";
} else {
OS << "USAGE: " << Usage << "\n\n";
- // Assume top level command (toolname) is active.
- StringRef TopLevelCommandName = "TopLevelCommand";
if (Commands.size() > 1) {
OS << "SUBCOMMANDS:\n\n";
// This loop prints subcommands list and sets ActiveCommand to
// TopLevelCommand while iterating over all commands.
for (const auto &C : Commands) {
- if (C.Name == TopLevelCommandName) {
+ if (StringRef(C.Name) == "TopLevelCommand") {
ActiveCommand = &C;
continue;
}
>From d41c2aead4574408f69daf2e5fd2d3319ae4bf37 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 23 Sep 2025 10:04:20 -0700
Subject: [PATCH 17/19] Address review comments.
---
llvm/examples/OptSubcommand/llvm-hello-sub.cpp | 8 ++++----
llvm/include/llvm/Option/OptTable.h | 2 ++
llvm/lib/Option/ArgList.cpp | 10 +++++-----
llvm/lib/Option/OptTable.cpp | 8 ++++----
llvm/utils/TableGen/OptionParserEmitter.cpp | 3 ++-
5 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index 6a4fb61623fed..cb24ee4ffcf1e 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -44,8 +44,9 @@ static constexpr OptTable::Info InfoTable[] = {
class HelloSubOptTable : public GenericOptTable {
public:
HelloSubOptTable()
- : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, false,
- OptionCommands, OptionCommandIDsTable) {}
+ : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+ /*IgnoreCase=*/false, OptionCommands,
+ OptionCommandIDsTable) {}
};
} // namespace
@@ -97,9 +98,8 @@ int main(int argc, char **argv) {
return 1;
}
if (Subcommand.empty()) {
- if (Args.hasArg(OPT_version)) {
+ if (Args.hasArg(OPT_version))
llvm::outs() << "LLVM Hello Subcommand Example 1.0\n";
- }
} else if (Subcommand == "foo") {
if (Args.hasArg(OPT_uppercase))
llvm::outs() << "FOO\n";
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index 44216166ece7c..e45975eaf7466 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -45,6 +45,8 @@ class Visibility {
operator unsigned() const { return Mask; }
};
+inline constexpr StringRef TopLevelCommandName = "TopLevelCommand";
+
/// Provide access to the Option info table.
///
/// The OptTable class provides a layer of indirection which allows Option
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index eb828fa2554ea..622296958c754 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -218,7 +218,7 @@ StringRef ArgList::getSubcommand(
size_t OldSize = SubCommands.size();
for (const OptTable::Command &CMD : Commands) {
- if (StringRef(CMD.Name) == "TopLevelCommand")
+ if (StringRef(CMD.Name) == opt::TopLevelCommandName)
continue;
if (StringRef(CMD.Name) == A->getValue())
SubCommands.push_back(A->getValue());
@@ -227,12 +227,12 @@ StringRef ArgList::getSubcommand(
if (SubCommands.size() == OldSize)
OtherPositionals.push_back(A->getValue());
}
- if (SubCommands.size() > 1) {
+ // Invoke callbacks if necessary.
+ if (SubCommands.size() > 1)
HandleMultipleSubcommands(SubCommands);
- }
- if (!OtherPositionals.empty()) {
+ if (!OtherPositionals.empty())
HandleOtherPositionals(OtherPositionals);
- }
+
if (SubCommands.size() == 1)
return SubCommands.front();
return {}; // No valid usage of subcommand found.
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index e1e2f66bc4588..f8f7da5bf7953 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -418,7 +418,7 @@ 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, nullptr,
+ return internalParseOneArg(Args, Index, /*ActiveCommand=*/nullptr,
[VisibilityMask](const Option &Opt) {
return !Opt.hasVisibilityFlag(VisibilityMask);
});
@@ -428,7 +428,7 @@ std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index,
unsigned FlagsToInclude,
unsigned FlagsToExclude) const {
return internalParseOneArg(
- Args, Index, nullptr,
+ Args, Index, /*ActiveCommand=*/nullptr,
[FlagsToInclude, FlagsToExclude](const Option &Opt) {
if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude))
return true;
@@ -766,7 +766,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, /*Subcommand=*/{}, ShowHidden, ShowAllAliases,
[FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {
if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude))
return true;
@@ -800,7 +800,7 @@ void OptTable::internalPrintHelp(
// This loop prints subcommands list and sets ActiveCommand to
// TopLevelCommand while iterating over all commands.
for (const auto &C : Commands) {
- if (StringRef(C.Name) == "TopLevelCommand") {
+ if (StringRef(C.Name) == opt::TopLevelCommandName) {
ActiveCommand = &C;
continue;
}
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index 69a7176a2ba33..dbdba9dcdf475 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -12,6 +12,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Option/OptTable.h"
#include "llvm/Support/InterleavedRange.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Record.h"
@@ -263,7 +264,7 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
Records.getAllDerivedDefinitions("Command");
// TopLevelCommand should come first.
std::stable_partition(Commands.begin(), Commands.end(), [](const Record *R) {
- return R->getName() == "TopLevelCommand";
+ return R->getName() == opt::TopLevelCommandName;
});
emitSourceFileHeader("Option Parsing Definitions", OS);
>From c1cddc965bf6ae3de29ab7fc69e3f642e4bdc1ce Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 23 Sep 2025 12:34:19 -0700
Subject: [PATCH 18/19] Remove TopLevelCommand abstraction completely.
---
.../examples/OptSubcommand/llvm-hello-sub.cpp | 1 +
llvm/include/llvm/Option/OptParser.td | 26 ++++------
llvm/lib/Option/ArgList.cpp | 4 +-
llvm/lib/Option/OptTable.cpp | 52 +++++++++++--------
llvm/utils/TableGen/OptionParserEmitter.cpp | 9 ++--
5 files changed, 49 insertions(+), 43 deletions(-)
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index cb24ee4ffcf1e..f85270e17da30 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -82,6 +82,7 @@ int main(int argc, char **argv) {
T.getCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
// Handle help. When help options is found, ignore all other options and exit
// after printing help.
+
if (Args.hasArg(OPT_help)) {
T.printHelp(llvm::outs(), "llvm-hello-sub [subcommand] [options]",
"LLVM Hello Subcommand Example", false, false, Visibility(),
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index f3d1a17cb2677..74ce1f8d31be4 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -108,13 +108,8 @@ class Subcommand<string name, string helpText, string usage = "">
string Usage = usage;
}
-// 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]> {
+ list<Command> commandGroup = []> {
string EnumName = ?; // Uses the def name if undefined.
list<string> Prefixes = prefixes;
string Name = name;
@@ -150,28 +145,27 @@ class Option<list<string> prefixes, string name, OptionKind kind,
// Helpers for defining options.
-class Flag<list<string> prefixes, string name,
- list<Command> commandGroup = [TopLevelCommand]>
+class Flag<list<string> prefixes, string name, list<Command> commandGroup = []>
: Option<prefixes, name, KIND_FLAG, commandGroup>;
class Joined<list<string> prefixes, string name,
- list<Command> commandGroup = [TopLevelCommand]>
+ list<Command> commandGroup = []>
: Option<prefixes, name, KIND_JOINED, commandGroup>;
class Separate<list<string> prefixes, string name,
- list<Command> commandGroup = [TopLevelCommand]>
+ list<Command> commandGroup = []>
: Option<prefixes, name, KIND_SEPARATE, commandGroup>;
class CommaJoined<list<string> prefixes, string name,
- list<Command> commandGroup = [TopLevelCommand]>
+ list<Command> commandGroup = []>
: Option<prefixes, name, KIND_COMMAJOINED, commandGroup>;
class MultiArg<list<string> prefixes, string name, int numargs,
- list<Command> commandGroup = [TopLevelCommand]>
+ list<Command> commandGroup = []>
: Option<prefixes, name, KIND_MULTIARG, commandGroup> {
int NumArgs = numargs;
}
class JoinedOrSeparate<list<string> prefixes, string name,
- list<Command> commandGroup = [TopLevelCommand]>
+ list<Command> commandGroup = []>
: Option<prefixes, name, KIND_JOINED_OR_SEPARATE, commandGroup>;
class JoinedAndSeparate<list<string> prefixes, string name,
- list<Command> commandGroup = [TopLevelCommand]>
+ list<Command> commandGroup = []>
: Option<prefixes, name, KIND_JOINED_AND_SEPARATE, commandGroup>;
// Mix-ins for adding optional attributes.
@@ -295,7 +289,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, [TopLevelCommand]>;
-def UNKNOWN : Option<[], "<unknown>", KIND_UNKNOWN, [TopLevelCommand]>;
+def INPUT : Option<[], "<input>", KIND_INPUT>;
+def UNKNOWN : Option<[], "<unknown>", KIND_UNKNOWN>;
#endif // LLVM_OPTION_OPTPARSER_TD
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index 622296958c754..f7ef7ef7e8e8d 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -218,8 +218,8 @@ StringRef ArgList::getSubcommand(
size_t OldSize = SubCommands.size();
for (const OptTable::Command &CMD : Commands) {
- if (StringRef(CMD.Name) == opt::TopLevelCommandName)
- continue;
+ // if (StringRef(CMD.Name) == opt::TopLevelCommandName)
+ // continue;
if (StringRef(CMD.Name) == A->getValue())
SubCommands.push_back(A->getValue());
}
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index f8f7da5bf7953..88d423484218f 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -789,7 +789,8 @@ void OptTable::internalPrintHelp(
std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp;
const Command *ActiveCommand = getActiveCommand(Commands, Subcommand);
- if (ActiveCommand) {
+ if (!Subcommand.empty()) {
+ assert(ActiveCommand != nullptr && "Not a valid registered subcommand.");
OS << ActiveCommand->HelpText << "\n\n";
if (!StringRef(ActiveCommand->Usage).empty())
OS << "USAGE: " << ActiveCommand->Usage << "\n\n";
@@ -797,30 +798,38 @@ void OptTable::internalPrintHelp(
OS << "USAGE: " << Usage << "\n\n";
if (Commands.size() > 1) {
OS << "SUBCOMMANDS:\n\n";
- // This loop prints subcommands list and sets ActiveCommand to
- // TopLevelCommand while iterating over all commands.
- for (const auto &C : Commands) {
- if (StringRef(C.Name) == opt::TopLevelCommandName) {
- ActiveCommand = &C;
- continue;
- }
+ for (const auto &C : Commands)
OS << C.Name << " - " << C.HelpText << "\n";
- }
OS << "\n";
}
}
- auto DoesOptionBelongToActiveCommand =
- [this, &ActiveCommand](const Info &CandidateInfo) {
- // ActiveCommand won't be set for tools that did not create command
- // group info table.
- if (!ActiveCommand)
- return true;
- ArrayRef<unsigned> CommandIDs =
- CandidateInfo.getCommandIDs(CommandIDsTable);
- unsigned ActiveCommandID = ActiveCommand - Commands.data();
- return is_contained(CommandIDs, ActiveCommandID);
- };
+ auto DoesOptionBelongToSubcommand = [&](const Info &CandidateInfo) {
+ // llvm::outs() << CandidateInfo.
+ ArrayRef<unsigned> CommandIDs =
+ CandidateInfo.getCommandIDs(CommandIDsTable);
+
+ // Options with no commands are global.
+ if (CommandIDs.empty()) {
+ // if Subcommand is not empty then don't print global options.
+ return Subcommand.empty();
+ }
+
+ // If we are not in a subcommand, don't show subcommand-specific options.
+ if (Subcommand.empty())
+ return false;
+
+ // The command ID is its index in the Commands table.
+ unsigned ActiveCommandID = ActiveCommand - &Commands[0];
+
+ // Check if the option belongs to the active subcommand.
+ for (unsigned ID : CommandIDs) {
+ if (ID == ActiveCommandID) {
+ return true;
+ }
+ }
+ return false;
+ };
for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) {
// FIXME: Split out option groups.
@@ -828,13 +837,14 @@ void OptTable::internalPrintHelp(
continue;
const Info &CandidateInfo = getInfo(Id);
+
if (!ShowHidden && (CandidateInfo.Flags & opt::HelpHidden))
continue;
if (ExcludeOption(CandidateInfo))
continue;
- if (!DoesOptionBelongToActiveCommand(CandidateInfo))
+ if (!DoesOptionBelongToSubcommand(CandidateInfo))
continue;
// If an alias doesn't have a help text, show a help text for the aliased
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index dbdba9dcdf475..a24481a75ce6e 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -262,10 +262,11 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
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() == opt::TopLevelCommandName;
- });
+ // // TopLevelCommand should come first.
+ // std::stable_partition(Commands.begin(), Commands.end(), [](const Record *R)
+ // {
+ // return R->getName() == opt::TopLevelCommandName;
+ // });
emitSourceFileHeader("Option Parsing Definitions", OS);
>From e28d2b069b2a141ee2db07291d7367aa50c50efb Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 23 Sep 2025 14:31:11 -0700
Subject: [PATCH 19/19] Update variable names to reflect there is no
Toplevelcommand.
---
llvm/examples/OptSubcommand/Opts.td | 4 +-
.../examples/OptSubcommand/llvm-hello-sub.cpp | 14 +--
llvm/include/llvm/Option/ArgList.h | 2 +-
llvm/include/llvm/Option/OptParser.td | 40 ++++----
llvm/include/llvm/Option/OptTable.h | 43 ++++----
llvm/lib/Option/ArgList.cpp | 6 +-
llvm/lib/Option/OptTable.cpp | 79 +++++----------
llvm/utils/TableGen/OptionParserEmitter.cpp | 98 +++++++++----------
8 files changed, 123 insertions(+), 163 deletions(-)
diff --git a/llvm/examples/OptSubcommand/Opts.td b/llvm/examples/OptSubcommand/Opts.td
index 21a417ffec12a..7c980ee7a0e7f 100644
--- a/llvm/examples/OptSubcommand/Opts.td
+++ b/llvm/examples/OptSubcommand/Opts.td
@@ -1,8 +1,8 @@
include "llvm/Option/OptParser.td"
-def sc_foo : Subcommand<"foo", "HelpText for Subcommand foo.">;
+def sc_foo : SubCommand<"foo", "HelpText for SubCommand foo.">;
-def sc_bar : Subcommand<"bar", "HelpText for Subcommand bar.",
+def sc_bar : SubCommand<"bar", "HelpText for SubCommand bar.",
"OptSubcommand bar <options>">;
def help : Flag<["--"], "help">,
diff --git a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
index f85270e17da30..c81155aea60d7 100644
--- a/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
+++ b/llvm/examples/OptSubcommand/llvm-hello-sub.cpp
@@ -27,13 +27,13 @@ enum ID {
#include "Opts.inc"
#undef OPTTABLE_PREFIXES_TABLE_CODE
-#define OPTTABLE_COMMAND_IDS_TABLE_CODE
+#define OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
#include "Opts.inc"
-#undef OPTTABLE_COMMAND_IDS_TABLE_CODE
+#undef OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
-#define OPTTABLE_COMMANDS_CODE
+#define OPTTABLE_SUBCOMMANDS_CODE
#include "Opts.inc"
-#undef OPTTABLE_COMMANDS_CODE
+#undef OPTTABLE_SUBCOMMANDS_CODE
static constexpr OptTable::Info InfoTable[] = {
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
@@ -45,8 +45,8 @@ class HelloSubOptTable : public GenericOptTable {
public:
HelloSubOptTable()
: GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
- /*IgnoreCase=*/false, OptionCommands,
- OptionCommandIDsTable) {}
+ /*IgnoreCase=*/false, OptionSubCommands,
+ OptionSubCommandIDsTable) {}
};
} // namespace
@@ -79,7 +79,7 @@ int main(int argc, char **argv) {
MissingArgCount);
StringRef Subcommand = Args.getSubcommand(
- T.getCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
// Handle help. When help options is found, ignore all other options and exit
// after printing help.
diff --git a/llvm/include/llvm/Option/ArgList.h b/llvm/include/llvm/Option/ArgList.h
index 72804d71ceb16..4da0ca56f0718 100644
--- a/llvm/include/llvm/Option/ArgList.h
+++ b/llvm/include/llvm/Option/ArgList.h
@@ -293,7 +293,7 @@ class ArgList {
/// this returns an empty StringRef. If multiple subcommands are found, the
/// first one is returned.
StringRef getSubcommand(
- ArrayRef<OptTable::Command> Commands,
+ ArrayRef<OptTable::SubCommand> AllSubCommands,
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const;
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index 74ce1f8d31be4..8f32fb4493511 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -98,18 +98,15 @@ class HelpTextVariant<list<OptionVisibility> visibilities, string text> {
string Text = text;
}
-// Base class for TopLevelCommand and Subcommands.
-class Command<string name> { string Name = name; }
-
// Class definition for positional subcommands.
-class Subcommand<string name, string helpText, string usage = "">
- : Command<name> {
+class SubCommand<string name, string helpText, string usage = ""> {
+ string Name = name;
string HelpText = helpText;
string Usage = usage;
}
class Option<list<string> prefixes, string name, OptionKind kind,
- list<Command> commandGroup = []> {
+ list<SubCommand> subcommands = []> {
string EnumName = ?; // Uses the def name if undefined.
list<string> Prefixes = prefixes;
string Name = name;
@@ -140,33 +137,34 @@ class Option<list<string> prefixes, string name, OptionKind kind,
code ValueMerger = "mergeForwardValue";
code ValueExtractor = "extractForwardValue";
list<code> NormalizedValues = ?;
- list<Command> CommandGroup = commandGroup;
+ list<SubCommand> SubCommands = subcommands;
}
// Helpers for defining options.
-class Flag<list<string> prefixes, string name, list<Command> commandGroup = []>
- : Option<prefixes, name, KIND_FLAG, commandGroup>;
+class Flag<list<string> prefixes, string name,
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_FLAG, subcommands>;
class Joined<list<string> prefixes, string name,
- list<Command> commandGroup = []>
- : Option<prefixes, name, KIND_JOINED, commandGroup>;
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_JOINED, subcommands>;
class Separate<list<string> prefixes, string name,
- list<Command> commandGroup = []>
- : Option<prefixes, name, KIND_SEPARATE, commandGroup>;
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_SEPARATE, subcommands>;
class CommaJoined<list<string> prefixes, string name,
- list<Command> commandGroup = []>
- : Option<prefixes, name, KIND_COMMAJOINED, commandGroup>;
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_COMMAJOINED, subcommands>;
class MultiArg<list<string> prefixes, string name, int numargs,
- list<Command> commandGroup = []>
- : Option<prefixes, name, KIND_MULTIARG, commandGroup> {
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_MULTIARG, subcommands> {
int NumArgs = numargs;
}
class JoinedOrSeparate<list<string> prefixes, string name,
- list<Command> commandGroup = []>
- : Option<prefixes, name, KIND_JOINED_OR_SEPARATE, commandGroup>;
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_JOINED_OR_SEPARATE, subcommands>;
class JoinedAndSeparate<list<string> prefixes, string name,
- list<Command> commandGroup = []>
- : Option<prefixes, name, KIND_JOINED_AND_SEPARATE, commandGroup>;
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_JOINED_AND_SEPARATE, subcommands>;
// Mix-ins for adding optional attributes.
diff --git a/llvm/include/llvm/Option/OptTable.h b/llvm/include/llvm/Option/OptTable.h
index e45975eaf7466..3dd08f39b242e 100644
--- a/llvm/include/llvm/Option/OptTable.h
+++ b/llvm/include/llvm/Option/OptTable.h
@@ -45,8 +45,6 @@ class Visibility {
operator unsigned() const { return Mask; }
};
-inline constexpr StringRef TopLevelCommandName = "TopLevelCommand";
-
/// Provide access to the Option info table.
///
/// The OptTable class provides a layer of indirection which allows Option
@@ -57,7 +55,7 @@ inline constexpr StringRef TopLevelCommandName = "TopLevelCommand";
class LLVM_ABI OptTable {
public:
/// Represents a subcommand and its options in the option table.
- struct Command {
+ struct SubCommand {
const char *Name;
const char *HelpText;
const char *Usage;
@@ -107,16 +105,17 @@ class LLVM_ABI OptTable {
bool hasCommands() const { return CommandIDsOffset != 0; }
- unsigned getNumCommandIDs(ArrayRef<unsigned> CommandIDsTable) const {
+ unsigned getNumCommandIDs(ArrayRef<unsigned> SubCommandIDsTable) const {
// We embed the number of command IDs in the value of the first offset.
- return CommandIDsTable[CommandIDsOffset];
+ return SubCommandIDsTable[CommandIDsOffset];
}
- ArrayRef<unsigned> getCommandIDs(ArrayRef<unsigned> CommandIDsTable) const {
- return hasCommands()
- ? CommandIDsTable.slice(CommandIDsOffset + 1,
- getNumCommandIDs(CommandIDsTable))
- : ArrayRef<unsigned>();
+ ArrayRef<unsigned>
+ getCommandIDs(ArrayRef<unsigned> SubCommandIDsTable) const {
+ return hasCommands() ? SubCommandIDsTable.slice(
+ CommandIDsOffset + 1,
+ getNumCommandIDs(SubCommandIDsTable))
+ : ArrayRef<unsigned>();
}
void appendPrefixes(const StringTable &StrTable,
@@ -161,10 +160,10 @@ class LLVM_ABI OptTable {
bool IgnoreCase;
/// The command information table.
- ArrayRef<Command> Commands;
+ ArrayRef<SubCommand> SubCommands;
/// The command IDs table.
- ArrayRef<unsigned> CommandIDsTable;
+ ArrayRef<unsigned> SubCommandIDsTable;
bool GroupedShortOptions = false;
bool DashDashParsing = false;
@@ -201,8 +200,8 @@ class LLVM_ABI OptTable {
OptTable(const StringTable &StrTable,
ArrayRef<StringTable::Offset> PrefixesTable,
ArrayRef<Info> OptionInfos, bool IgnoreCase = false,
- ArrayRef<Command> Commands = {},
- ArrayRef<unsigned> CommandIDsTable = {});
+ ArrayRef<SubCommand> SubCommands = {},
+ ArrayRef<unsigned> SubCommandIDsTable = {});
/// Build (or rebuild) the PrefixChars member.
void buildPrefixChars();
@@ -213,7 +212,7 @@ class LLVM_ABI OptTable {
/// Return the string table used for option names.
const StringTable &getStrTable() const { return *StrTable; }
- const ArrayRef<Command> getCommands() const { return Commands; }
+ const ArrayRef<SubCommand> getSubCommands() const { return SubCommands; }
/// Return the prefixes table used for option names.
ArrayRef<StringTable::Offset> getPrefixesTable() const {
@@ -386,7 +385,6 @@ 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:
@@ -468,8 +466,8 @@ class GenericOptTable : public OptTable {
LLVM_ABI GenericOptTable(const StringTable &StrTable,
ArrayRef<StringTable::Offset> PrefixesTable,
ArrayRef<Info> OptionInfos, bool IgnoreCase = false,
- ArrayRef<Command> Commands = {},
- ArrayRef<unsigned> CommandIDsTable = {});
+ ArrayRef<SubCommand> Commands = {},
+ ArrayRef<unsigned> SubCommandIDsTable = {});
};
class PrecomputedOptTable : public OptTable {
@@ -478,10 +476,11 @@ class PrecomputedOptTable : public OptTable {
ArrayRef<StringTable::Offset> PrefixesTable,
ArrayRef<Info> OptionInfos,
ArrayRef<StringTable::Offset> PrefixesUnionOffsets,
- bool IgnoreCase = false, ArrayRef<Command> Commands = {},
- ArrayRef<unsigned> CommandIDsTable = {})
- : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase, Commands,
- CommandIDsTable) {
+ bool IgnoreCase = false,
+ ArrayRef<SubCommand> SubCommands = {},
+ ArrayRef<unsigned> SubCommandIDsTable = {})
+ : OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase, SubCommands,
+ SubCommandIDsTable) {
for (auto PrefixOffset : PrefixesUnionOffsets)
PrefixesUnion.push_back(StrTable[PrefixOffset]);
buildPrefixChars();
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
index f7ef7ef7e8e8d..26ada3591eaee 100644
--- a/llvm/lib/Option/ArgList.cpp
+++ b/llvm/lib/Option/ArgList.cpp
@@ -206,7 +206,7 @@ LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
#endif
StringRef ArgList::getSubcommand(
- ArrayRef<OptTable::Command> Commands,
+ ArrayRef<OptTable::SubCommand> AllSubCommands,
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const {
@@ -217,9 +217,7 @@ StringRef ArgList::getSubcommand(
continue;
size_t OldSize = SubCommands.size();
- for (const OptTable::Command &CMD : Commands) {
- // if (StringRef(CMD.Name) == opt::TopLevelCommandName)
- // continue;
+ for (const OptTable::SubCommand &CMD : AllSubCommands) {
if (StringRef(CMD.Name) == A->getValue())
SubCommands.push_back(A->getValue());
}
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index 88d423484218f..de078e03e7ab0 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -80,11 +80,11 @@ OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {}
OptTable::OptTable(const StringTable &StrTable,
ArrayRef<StringTable::Offset> PrefixesTable,
ArrayRef<Info> OptionInfos, bool IgnoreCase,
- ArrayRef<Command> Commands,
- ArrayRef<unsigned> CommandIDsTable)
+ ArrayRef<SubCommand> SubCommands,
+ ArrayRef<unsigned> SubCommandIDsTable)
: StrTable(&StrTable), PrefixesTable(PrefixesTable),
- OptionInfos(OptionInfos), IgnoreCase(IgnoreCase), Commands(Commands),
- CommandIDsTable(CommandIDsTable) {
+ OptionInfos(OptionInfos), IgnoreCase(IgnoreCase),
+ SubCommands(SubCommands), SubCommandIDsTable(SubCommandIDsTable) {
// Explicitly zero initialize the error to work around a bug in array
// value-initialization on MinGW with gcc 4.3.5.
@@ -418,18 +418,16 @@ 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, /*ActiveCommand=*/nullptr,
- [VisibilityMask](const Option &Opt) {
- return !Opt.hasVisibilityFlag(VisibilityMask);
- });
+ return internalParseOneArg(Args, Index, [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, /*ActiveCommand=*/nullptr,
- [FlagsToInclude, FlagsToExclude](const Option &Opt) {
+ Args, Index, [FlagsToInclude, FlagsToExclude](const Option &Opt) {
if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude))
return true;
if (Opt.hasFlag(FlagsToExclude))
@@ -439,7 +437,7 @@ std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index,
}
std::unique_ptr<Arg> OptTable::internalParseOneArg(
- const ArgList &Args, unsigned &Index, const Command *ActiveCommand,
+ const ArgList &Args, unsigned &Index,
std::function<bool(const Option &)> ExcludeOption) const {
unsigned Prev = Index;
StringRef Str = Args.getArgString(Index);
@@ -481,18 +479,6 @@ 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),
@@ -541,14 +527,6 @@ InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args,
});
}
-static const OptTable::Command *
-getActiveCommand(ArrayRef<OptTable::Command> Commands, StringRef Subcommand) {
- const OptTable::Command *FoundSC =
- std::find_if(Commands.begin(), Commands.end(),
- [&](const auto &C) { return Subcommand == C.Name; });
- return (FoundSC == Commands.end()) ? nullptr : FoundSC;
-}
-
InputArgList OptTable::internalParseArgs(
ArrayRef<const char *> ArgArr, unsigned &MissingArgIndex,
unsigned &MissingArgCount,
@@ -559,14 +537,6 @@ InputArgList OptTable::internalParseArgs(
MissingArgIndex = MissingArgCount = 0;
unsigned Index = 0, End = ArgArr.size();
- const Command *ActiveCommand = nullptr;
-
- // Look for subcommand.
- if (!Commands.empty() && Index < End) {
- StringRef FirstArg = Args.getArgString(Index);
- if (isInput(PrefixesUnion, FirstArg))
- ActiveCommand = getActiveCommand(Commands, FirstArg);
- }
while (Index < End) {
// Ingore nullptrs, they are response file's EOL markers
@@ -593,9 +563,8 @@ InputArgList OptTable::internalParseArgs(
unsigned Prev = Index;
std::unique_ptr<Arg> A =
- GroupedShortOptions
- ? parseOneArgGrouped(Args, Index)
- : internalParseOneArg(Args, Index, ActiveCommand, ExcludeOption);
+ GroupedShortOptions ? parseOneArgGrouped(Args, Index)
+ : internalParseOneArg(Args, Index, ExcludeOption);
assert((Index > Prev || GroupedShortOptions) &&
"Parser failed to consume argument.");
@@ -788,17 +757,19 @@ void OptTable::internalPrintHelp(
// pairs.
std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp;
- const Command *ActiveCommand = getActiveCommand(Commands, Subcommand);
+ const SubCommand *ActiveSubCommand =
+ std::find_if(SubCommands.begin(), SubCommands.end(),
+ [&](const auto &C) { return Subcommand == C.Name; });
if (!Subcommand.empty()) {
- assert(ActiveCommand != nullptr && "Not a valid registered subcommand.");
- OS << ActiveCommand->HelpText << "\n\n";
- if (!StringRef(ActiveCommand->Usage).empty())
- OS << "USAGE: " << ActiveCommand->Usage << "\n\n";
+ assert(ActiveSubCommand != nullptr && "Not a valid registered subcommand.");
+ OS << ActiveSubCommand->HelpText << "\n\n";
+ if (!StringRef(ActiveSubCommand->Usage).empty())
+ OS << "USAGE: " << ActiveSubCommand->Usage << "\n\n";
} else {
OS << "USAGE: " << Usage << "\n\n";
- if (Commands.size() > 1) {
+ if (SubCommands.size() > 1) {
OS << "SUBCOMMANDS:\n\n";
- for (const auto &C : Commands)
+ for (const auto &C : SubCommands)
OS << C.Name << " - " << C.HelpText << "\n";
OS << "\n";
}
@@ -807,7 +778,7 @@ void OptTable::internalPrintHelp(
auto DoesOptionBelongToSubcommand = [&](const Info &CandidateInfo) {
// llvm::outs() << CandidateInfo.
ArrayRef<unsigned> CommandIDs =
- CandidateInfo.getCommandIDs(CommandIDsTable);
+ CandidateInfo.getCommandIDs(SubCommandIDsTable);
// Options with no commands are global.
if (CommandIDs.empty()) {
@@ -820,7 +791,7 @@ void OptTable::internalPrintHelp(
return false;
// The command ID is its index in the Commands table.
- unsigned ActiveCommandID = ActiveCommand - &Commands[0];
+ unsigned ActiveCommandID = ActiveSubCommand - &SubCommands[0];
// Check if the option belongs to the active subcommand.
for (unsigned ID : CommandIDs) {
@@ -875,10 +846,10 @@ void OptTable::internalPrintHelp(
GenericOptTable::GenericOptTable(const StringTable &StrTable,
ArrayRef<StringTable::Offset> PrefixesTable,
ArrayRef<Info> OptionInfos, bool IgnoreCase,
- ArrayRef<Command> Commands,
- ArrayRef<unsigned> CommandIDsTable)
+ ArrayRef<SubCommand> Commands,
+ ArrayRef<unsigned> SubCommandIDsTable)
: OptTable(StrTable, PrefixesTable, OptionInfos, IgnoreCase, Commands,
- CommandIDsTable) {
+ SubCommandIDsTable) {
std::set<StringRef> TmpPrefixesUnion;
for (auto const &Info : OptionInfos.drop_front(FirstSearchableIndex))
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index a24481a75ce6e..ec725d67d1f6d 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -260,13 +260,8 @@ 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() == opt::TopLevelCommandName;
- // });
+ std::vector<const Record *> SubCommands =
+ Records.getAllDerivedDefinitions("SubCommand");
emitSourceFileHeader("Option Parsing Definitions", OS);
@@ -281,33 +276,33 @@ 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;
-
- auto PrintCommandIdsOffset = [&CommandIDs, &OS](const Record &R) {
- 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];
+ // Generate sub command groups.
+ typedef SmallVector<StringRef, 2> SubCommandKeyT;
+ typedef std::map<SubCommandKeyT, unsigned> SubCommandIDsT;
+ SubCommandIDsT SubCommandIDs;
+
+ auto PrintSubCommandIdsOffset = [&SubCommandIDs, &OS](const Record &R) {
+ if (R.getValue("SubCommands") != nullptr) {
+ std::vector<const Record *> SubCommands =
+ R.getValueAsListOfDefs("SubCommands");
+ SubCommandKeyT CommandKey;
+ for (const auto &SubCommand : SubCommands)
+ CommandKey.push_back(SubCommand->getName());
+ OS << SubCommandIDs[CommandKey];
} else {
- // The option CommandIDsOffset (for default top level toolname is 0).
+ // The option SubCommandIDsOffset (for default top level toolname is 0).
OS << " 0";
}
};
- CommandIDs.try_emplace(CommandKeyT(), 0);
+ SubCommandIDs.try_emplace(SubCommandKeyT(), 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);
+ std::vector<const Record *> RSubCommands =
+ R.getValueAsListOfDefs("SubCommands");
+ SubCommandKeyT CommandKey;
+ for (const auto &SubCommand : RSubCommands)
+ CommandKey.push_back(SubCommand->getName());
+ SubCommandIDs.try_emplace(CommandKey, 0);
}
DenseSet<StringRef> PrefixesUnionSet;
@@ -365,17 +360,17 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
// Dump command IDs.
OS << "/////////";
OS << "// Command IDs\n\n";
- OS << "#ifdef OPTTABLE_COMMAND_IDS_TABLE_CODE\n";
- OS << "static constexpr unsigned OptionCommandIDsTable[] = {\n";
+ OS << "#ifdef OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE\n";
+ OS << "static constexpr unsigned OptionSubCommandIDsTable[] = {\n";
{
// Ensure the first command set is always empty.
- assert(!CommandIDs.empty() &&
+ assert(!SubCommandIDs.empty() &&
"We should always emit an empty set of commands");
- assert(CommandIDs.begin()->first.empty() &&
+ assert(SubCommandIDs.begin()->first.empty() &&
"First command set should always be empty");
llvm::ListSeparator Sep(",\n");
unsigned CurIndex = 0;
- for (auto &[Command, CommandIndex] : CommandIDs) {
+ for (auto &[Command, CommandIndex] : SubCommandIDs) {
// First emit the number of command strings in this list of commands.
OS << Sep << " " << Command.size() << " /* commands */";
CommandIndex = CurIndex;
@@ -383,17 +378,17 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
"Only first command set should be empty!");
for (const auto &CommandKey : Command) {
auto It = std::find_if(
- Commands.begin(), Commands.end(),
+ SubCommands.begin(), SubCommands.end(),
[&](const Record *R) { return R->getName() == CommandKey; });
- assert(It != Commands.end() && "Command not found");
- OS << ", " << std::distance(Commands.begin(), It) << " /* '"
+ assert(It != SubCommands.end() && "Command not found");
+ OS << ", " << std::distance(SubCommands.begin(), It) << " /* '"
<< CommandKey << "' */";
}
CurIndex += Command.size() + 1;
}
}
OS << "\n};\n";
- OS << "#endif // OPTTABLE_COMMAND_IDS_TABLE_CODE\n\n";
+ OS << "#endif // OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE\n\n";
// Dump prefixes union.
OS << "/////////\n";
@@ -474,9 +469,9 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
// The option Values (unused for groups).
OS << ", nullptr";
- // The option CommandIDsOffset.
+ // The option SubCommandIDsOffset.
OS << ", ";
- PrintCommandIdsOffset(R);
+ PrintSubCommandIdsOffset(R);
OS << ")\n";
}
OS << "\n";
@@ -605,9 +600,9 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
else
OS << "nullptr";
- // The option CommandIDsOffset.
+ // The option SubCommandIDsOffset.
OS << ", ";
- PrintCommandIdsOffset(R);
+ PrintSubCommandIdsOffset(R);
};
auto IsMarshallingOption = [](const Record &R) {
@@ -677,19 +672,18 @@ 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") << "\", ";
- OS << "\"" << Command->getValueAsString("Usage") << "\" },\n";
- } else
- OS << "nullptr, nullptr},\n";
+ OS << "\n// SubCommands\n\n";
+ OS << "#ifdef OPTTABLE_SUBCOMMANDS_CODE\n";
+ OS << "static constexpr llvm::opt::OptTable::SubCommand OptionSubCommands[] "
+ "= "
+ "{\n";
+ for (const Record *SubCommand : SubCommands) {
+ OS << " { \"" << SubCommand->getValueAsString("Name") << "\", ";
+ OS << "\"" << SubCommand->getValueAsString("HelpText") << "\", ";
+ OS << "\"" << SubCommand->getValueAsString("Usage") << "\" },\n";
}
OS << "};\n";
- OS << "#endif // OPTTABLE_COMMANDS_CODE\n\n";
+ OS << "#endif // OPTTABLE_SUBCOMMANDS_CODE\n\n";
OS << "\n";
}
More information about the llvm-commits
mailing list