[llvm] 64ada7a - [TableGen] Add backend to generate command guide for tools using libOption.
Jonas Devlieghere via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 22 14:10:25 PST 2019
Author: Jonas Devlieghere
Date: 2019-11-22T14:10:17-08:00
New Revision: 64ada7accbcccf1ef3ac4910d596e890570c64b2
URL: https://github.com/llvm/llvm-project/commit/64ada7accbcccf1ef3ac4910d596e890570c64b2
DIFF: https://github.com/llvm/llvm-project/commit/64ada7accbcccf1ef3ac4910d596e890570c64b2.diff
LOG: [TableGen] Add backend to generate command guide for tools using libOption.
For lldb and dsymutil, the command guide is essentially a copy of its
help output generated by libOption. Making sure the two stay in sync is
tedious and error prone. Given that we already generate the help from a
tablegen file, we might as well generate the RST as well.
This adds a tablegen backend for generating Sphinx/RST command guides
from the tablegen file.
Differential revision: https://reviews.llvm.org/D70610
Added:
llvm/utils/TableGen/OptEmitter.cpp
llvm/utils/TableGen/OptEmitter.h
llvm/utils/TableGen/OptRSTEmitter.cpp
Modified:
llvm/utils/TableGen/CMakeLists.txt
llvm/utils/TableGen/OptParserEmitter.cpp
llvm/utils/TableGen/TableGen.cpp
llvm/utils/TableGen/TableGenBackends.h
Removed:
################################################################################
diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt
index 407e10d8bf2d..dbca62c5c78f 100644
--- a/llvm/utils/TableGen/CMakeLists.txt
+++ b/llvm/utils/TableGen/CMakeLists.txt
@@ -33,7 +33,9 @@ add_tablegen(llvm-tblgen LLVM
InstrInfoEmitter.cpp
InstrDocsEmitter.cpp
IntrinsicEmitter.cpp
+ OptEmitter.cpp
OptParserEmitter.cpp
+ OptRSTEmitter.cpp
PredicateExpander.cpp
PseudoLoweringEmitter.cpp
RISCVCompressInstEmitter.cpp
diff --git a/llvm/utils/TableGen/OptEmitter.cpp b/llvm/utils/TableGen/OptEmitter.cpp
new file mode 100644
index 000000000000..7fcf3074e093
--- /dev/null
+++ b/llvm/utils/TableGen/OptEmitter.cpp
@@ -0,0 +1,84 @@
+//===- OptEmitter.cpp - Helper for emitting options.----------- -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "OptEmitter.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include <cctype>
+#include <cstring>
+
+namespace llvm {
+
+// Ordering on Info. The logic should match with the consumer-side function in
+// llvm/Option/OptTable.h.
+// FIXME: Make this take StringRefs instead of null terminated strings to
+// simplify callers.
+static int StrCmpOptionName(const char *A, const char *B) {
+ const char *X = A, *Y = B;
+ char a = tolower(*A), b = tolower(*B);
+ while (a == b) {
+ if (a == '\0')
+ return strcmp(A, B);
+
+ a = tolower(*++X);
+ b = tolower(*++Y);
+ }
+
+ if (a == '\0') // A is a prefix of B.
+ return 1;
+ if (b == '\0') // B is a prefix of A.
+ return -1;
+
+ // Otherwise lexicographic.
+ return (a < b) ? -1 : 1;
+}
+
+int CompareOptionRecords(Record *const *Av, Record *const *Bv) {
+ const Record *A = *Av;
+ const Record *B = *Bv;
+
+ // Sentinel options precede all others and are only ordered by precedence.
+ bool ASent = A->getValueAsDef("Kind")->getValueAsBit("Sentinel");
+ bool BSent = B->getValueAsDef("Kind")->getValueAsBit("Sentinel");
+ if (ASent != BSent)
+ return ASent ? -1 : 1;
+
+ // Compare options by name, unless they are sentinels.
+ if (!ASent)
+ if (int Cmp = StrCmpOptionName(A->getValueAsString("Name").str().c_str(),
+ B->getValueAsString("Name").str().c_str()))
+ return Cmp;
+
+ if (!ASent) {
+ std::vector<StringRef> APrefixes = A->getValueAsListOfStrings("Prefixes");
+ std::vector<StringRef> BPrefixes = B->getValueAsListOfStrings("Prefixes");
+
+ for (std::vector<StringRef>::const_iterator APre = APrefixes.begin(),
+ AEPre = APrefixes.end(),
+ BPre = BPrefixes.begin(),
+ BEPre = BPrefixes.end();
+ APre != AEPre && BPre != BEPre; ++APre, ++BPre) {
+ if (int Cmp = StrCmpOptionName(APre->str().c_str(), BPre->str().c_str()))
+ return Cmp;
+ }
+ }
+
+ // Then by the kind precedence;
+ int APrec = A->getValueAsDef("Kind")->getValueAsInt("Precedence");
+ int BPrec = B->getValueAsDef("Kind")->getValueAsInt("Precedence");
+ if (APrec == BPrec && A->getValueAsListOfStrings("Prefixes") ==
+ B->getValueAsListOfStrings("Prefixes")) {
+ PrintError(A->getLoc(), Twine("Option is equivalent to"));
+ PrintError(B->getLoc(), Twine("Other defined here"));
+ PrintFatalError("Equivalent Options found.");
+ }
+ return APrec < BPrec ? -1 : 1;
+}
+
+} // namespace llvm
diff --git a/llvm/utils/TableGen/OptEmitter.h b/llvm/utils/TableGen/OptEmitter.h
new file mode 100644
index 000000000000..c8f9246ef1e6
--- /dev/null
+++ b/llvm/utils/TableGen/OptEmitter.h
@@ -0,0 +1,16 @@
+//===- OptEmitter.h - Helper for emitting options. --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UTILS_TABLEGEN_OPTEMITTER_H
+#define LLVM_UTILS_TABLEGEN_OPTEMITTER_H
+
+namespace llvm {
+class Record;
+int CompareOptionRecords(Record *const *Av, Record *const *Bv);
+} // namespace llvm
+#endif
diff --git a/llvm/utils/TableGen/OptParserEmitter.cpp b/llvm/utils/TableGen/OptParserEmitter.cpp
index 51b1cb093b21..5398bc82886e 100644
--- a/llvm/utils/TableGen/OptParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptParserEmitter.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/TableGen/Error.h"
+#include "OptEmitter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
@@ -18,75 +18,6 @@
using namespace llvm;
-// Ordering on Info. The logic should match with the consumer-side function in
-// llvm/Option/OptTable.h.
-// FIXME: Mmake this take StringRefs instead of null terminated strings to
-// simplify callers.
-static int StrCmpOptionName(const char *A, const char *B) {
- const char *X = A, *Y = B;
- char a = tolower(*A), b = tolower(*B);
- while (a == b) {
- if (a == '\0')
- return strcmp(A, B);
-
- a = tolower(*++X);
- b = tolower(*++Y);
- }
-
- if (a == '\0') // A is a prefix of B.
- return 1;
- if (b == '\0') // B is a prefix of A.
- return -1;
-
- // Otherwise lexicographic.
- return (a < b) ? -1 : 1;
-}
-
-static int CompareOptionRecords(Record *const *Av, Record *const *Bv) {
- const Record *A = *Av;
- const Record *B = *Bv;
-
- // Sentinel options precede all others and are only ordered by precedence.
- bool ASent = A->getValueAsDef("Kind")->getValueAsBit("Sentinel");
- bool BSent = B->getValueAsDef("Kind")->getValueAsBit("Sentinel");
- if (ASent != BSent)
- return ASent ? -1 : 1;
-
- // Compare options by name, unless they are sentinels.
- if (!ASent)
- if (int Cmp = StrCmpOptionName(A->getValueAsString("Name").str().c_str(),
- B->getValueAsString("Name").str().c_str()))
- return Cmp;
-
- if (!ASent) {
- std::vector<StringRef> APrefixes = A->getValueAsListOfStrings("Prefixes");
- std::vector<StringRef> BPrefixes = B->getValueAsListOfStrings("Prefixes");
-
- for (std::vector<StringRef>::const_iterator APre = APrefixes.begin(),
- AEPre = APrefixes.end(),
- BPre = BPrefixes.begin(),
- BEPre = BPrefixes.end();
- APre != AEPre &&
- BPre != BEPre;
- ++APre, ++BPre) {
- if (int Cmp = StrCmpOptionName(APre->str().c_str(), BPre->str().c_str()))
- return Cmp;
- }
- }
-
- // Then by the kind precedence;
- int APrec = A->getValueAsDef("Kind")->getValueAsInt("Precedence");
- int BPrec = B->getValueAsDef("Kind")->getValueAsInt("Precedence");
- if (APrec == BPrec &&
- A->getValueAsListOfStrings("Prefixes") ==
- B->getValueAsListOfStrings("Prefixes")) {
- PrintError(A->getLoc(), Twine("Option is equivalent to"));
- PrintError(B->getLoc(), Twine("Other defined here"));
- PrintFatalError("Equivalent Options found.");
- }
- return APrec < BPrec ? -1 : 1;
-}
-
static const std::string getOptionName(const Record &R) {
// Use the record name unless EnumName is defined.
if (isa<UnsetInit>(R.getValueInit("EnumName")))
diff --git a/llvm/utils/TableGen/OptRSTEmitter.cpp b/llvm/utils/TableGen/OptRSTEmitter.cpp
new file mode 100644
index 000000000000..a04817244413
--- /dev/null
+++ b/llvm/utils/TableGen/OptRSTEmitter.cpp
@@ -0,0 +1,88 @@
+//===- OptParserEmitter.cpp - Table Driven Command Line Parsing -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "OptEmitter.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <cctype>
+#include <cstring>
+#include <map>
+
+using namespace llvm;
+
+/// OptParserEmitter - This tablegen backend takes an input .td file
+/// describing a list of options and emits a RST man page.
+namespace llvm {
+void EmitOptRST(RecordKeeper &Records, raw_ostream &OS) {
+ llvm::StringMap<std::vector<Record *>> OptionsByGroup;
+ std::vector<Record *> OptionsWithoutGroup;
+
+ // Get the options.
+ std::vector<Record *> Opts = Records.getAllDerivedDefinitions("Option");
+ array_pod_sort(Opts.begin(), Opts.end(), CompareOptionRecords);
+
+ // Get the option groups.
+ const std::vector<Record *> &Groups =
+ Records.getAllDerivedDefinitions("OptionGroup");
+ for (unsigned i = 0, e = Groups.size(); i != e; ++i) {
+ const Record &R = *Groups[i];
+ OptionsByGroup.try_emplace(R.getValueAsString("Name"));
+ }
+
+ // Map options to their group.
+ for (unsigned i = 0, e = Opts.size(); i != e; ++i) {
+ const Record &R = *Opts[i];
+ const ListInit *GroupFlags = nullptr;
+ if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
+ GroupFlags = DI->getDef()->getValueAsListInit("Flags");
+ OptionsByGroup[DI->getDef()->getValueAsString("Name")].push_back(Opts[i]);
+ } else {
+ OptionsByGroup["options"].push_back(Opts[i]);
+ }
+ }
+
+ // Print options under their group.
+ for (const auto &KV : OptionsByGroup) {
+ std::string GroupName = KV.getKey().upper();
+ OS << GroupName << '\n';
+ OS << std::string(GroupName.size(), '-') << '\n';
+ OS << '\n';
+
+ for (Record *R : KV.getValue()) {
+ OS << ".. option:: ";
+
+ // Print the prefix.
+ std::vector<StringRef> Prefixes = R->getValueAsListOfStrings("Prefixes");
+ if (!Prefixes.empty())
+ OS << Prefixes[0];
+
+ // Print the option name.
+ OS << R->getValueAsString("Name");
+
+ // Print the meta-variable.
+ if (!isa<UnsetInit>(R->getValueInit("MetaVarName"))) {
+ OS << '=';
+ OS.write_escaped(R->getValueAsString("MetaVarName"));
+ }
+
+ OS << "\n\n";
+
+ // The option help text.
+ if (!isa<UnsetInit>(R->getValueInit("HelpText"))) {
+ OS << ' ';
+ OS.write_escaped(R->getValueAsString("HelpText"));
+ OS << "\n\n";
+ }
+ }
+ }
+}
+} // end namespace llvm
diff --git a/llvm/utils/TableGen/TableGen.cpp b/llvm/utils/TableGen/TableGen.cpp
index f730d91160ad..086560e7b265 100644
--- a/llvm/utils/TableGen/TableGen.cpp
+++ b/llvm/utils/TableGen/TableGen.cpp
@@ -45,6 +45,7 @@ enum ActionType {
PrintEnums,
PrintSets,
GenOptParserDefs,
+ GenOptRST,
GenCTags,
GenAttributes,
GenSearchableTables,
@@ -110,6 +111,7 @@ cl::opt<ActionType> Action(
"Print expanded sets for testing DAG exprs"),
clEnumValN(GenOptParserDefs, "gen-opt-parser-defs",
"Generate option definitions"),
+ clEnumValN(GenOptRST, "gen-opt-rst", "Generate option RST"),
clEnumValN(GenCTags, "gen-ctags", "Generate ctags-compatible index"),
clEnumValN(GenAttributes, "gen-attrs", "Generate attributes"),
clEnumValN(GenSearchableTables, "gen-searchable-tables",
@@ -126,8 +128,7 @@ cl::opt<ActionType> Action(
"Generate registers bank descriptions"),
clEnumValN(GenExegesis, "gen-exegesis",
"Generate llvm-exegesis tables"),
- clEnumValN(GenAutomata, "gen-automata",
- "Generate generic automata")));
+ clEnumValN(GenAutomata, "gen-automata", "Generate generic automata")));
cl::OptionCategory PrintEnumsCat("Options for -print-enums");
cl::opt<std::string> Class("class", cl::desc("Print Enum list for this class"),
@@ -204,6 +205,9 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenOptParserDefs:
EmitOptParser(Records, OS);
break;
+ case GenOptRST:
+ EmitOptRST(Records, OS);
+ break;
case PrintEnums:
{
for (Record *Rec : Records.getAllDerivedDefinitions(Class))
diff --git a/llvm/utils/TableGen/TableGenBackends.h b/llvm/utils/TableGen/TableGenBackends.h
index 8c067dd51b3b..a6278e0c5f08 100644
--- a/llvm/utils/TableGen/TableGenBackends.h
+++ b/llvm/utils/TableGen/TableGenBackends.h
@@ -81,6 +81,7 @@ void EmitRegisterInfo(RecordKeeper &RK, raw_ostream &OS);
void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS);
void EmitMapTable(RecordKeeper &RK, raw_ostream &OS);
void EmitOptParser(RecordKeeper &RK, raw_ostream &OS);
+void EmitOptRST(RecordKeeper &RK, raw_ostream &OS);
void EmitCTags(RecordKeeper &RK, raw_ostream &OS);
void EmitAttributes(RecordKeeper &RK, raw_ostream &OS);
void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS);
More information about the llvm-commits
mailing list