mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
[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
This commit is contained in:
parent
3ba73f04c0
commit
70be4d27b2
@ -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
|
||||
|
84
utils/TableGen/OptEmitter.cpp
Normal file
84
utils/TableGen/OptEmitter.cpp
Normal file
@ -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
|
16
utils/TableGen/OptEmitter.h
Normal file
16
utils/TableGen/OptEmitter.h
Normal file
@ -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
|
@ -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")))
|
||||
|
88
utils/TableGen/OptRSTEmitter.cpp
Normal file
88
utils/TableGen/OptRSTEmitter.cpp
Normal file
@ -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
|
@ -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))
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user