Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/OptionParserEmitter.cpp
213765 views
//===- OptionParserEmitter.cpp - Table Driven Command Option Line Parsing -===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Common/OptEmitter.h"9#include "llvm/ADT/STLExtras.h"10#include "llvm/ADT/SmallString.h"11#include "llvm/ADT/StringExtras.h"12#include "llvm/ADT/Twine.h"13#include "llvm/Support/InterleavedRange.h"14#include "llvm/Support/raw_ostream.h"15#include "llvm/TableGen/Record.h"16#include "llvm/TableGen/StringToOffsetTable.h"17#include "llvm/TableGen/TableGenBackend.h"18#include <cstring>19#include <map>2021using namespace llvm;2223static std::string getOptionName(const Record &R) {24// Use the record name unless EnumName is defined.25if (isa<UnsetInit>(R.getValueInit("EnumName")))26return R.getName().str();2728return R.getValueAsString("EnumName").str();29}3031static raw_ostream &writeStrTableOffset(raw_ostream &OS,32const StringToOffsetTable &Table,33llvm::StringRef Str) {34OS << Table.GetStringOffset(Str) << " /* ";35OS.write_escaped(Str);36OS << " */";37return OS;38}3940static raw_ostream &writeCstring(raw_ostream &OS, llvm::StringRef Str) {41OS << '"';42OS.write_escaped(Str);43OS << '"';44return OS;45}4647static std::string getOptionPrefixedName(const Record &R) {48std::vector<StringRef> Prefixes = R.getValueAsListOfStrings("Prefixes");49StringRef Name = R.getValueAsString("Name");5051if (Prefixes.empty())52return Name.str();5354return (Prefixes[0] + Twine(Name)).str();55}5657class MarshallingInfo {58public:59static constexpr const char *MacroName = "OPTION_WITH_MARSHALLING";60const Record &R;61bool ShouldAlwaysEmit = false;62StringRef MacroPrefix;63StringRef KeyPath;64StringRef DefaultValue;65StringRef NormalizedValuesScope;66StringRef ImpliedCheck;67StringRef ImpliedValue;68StringRef ShouldParse;69StringRef Normalizer;70StringRef Denormalizer;71StringRef ValueMerger;72StringRef ValueExtractor;73int TableIndex = -1;74std::vector<StringRef> Values;75std::vector<StringRef> NormalizedValues;76std::string ValueTableName;7778static size_t NextTableIndex;7980static constexpr const char *ValueTablePreamble = R"(81struct SimpleEnumValue {82const char *Name;83unsigned Value;84};8586struct SimpleEnumValueTable {87const SimpleEnumValue *Table;88unsigned Size;89};90)";9192static constexpr const char *ValueTablesDecl =93"static const SimpleEnumValueTable SimpleEnumValueTables[] = ";9495MarshallingInfo(const Record &R) : R(R) {}9697std::string getMacroName() const {98return (MacroPrefix + MarshallingInfo::MacroName).str();99}100101void emit(raw_ostream &OS) const {102OS << ShouldParse;103OS << ", ";104OS << ShouldAlwaysEmit;105OS << ", ";106OS << KeyPath;107OS << ", ";108emitScopedNormalizedValue(OS, DefaultValue);109OS << ", ";110OS << ImpliedCheck;111OS << ", ";112emitScopedNormalizedValue(OS, ImpliedValue);113OS << ", ";114OS << Normalizer;115OS << ", ";116OS << Denormalizer;117OS << ", ";118OS << ValueMerger;119OS << ", ";120OS << ValueExtractor;121OS << ", ";122OS << TableIndex;123}124125std::optional<StringRef> emitValueTable(raw_ostream &OS) const {126if (TableIndex == -1)127return {};128OS << "static const SimpleEnumValue " << ValueTableName << "[] = {\n";129for (unsigned I = 0, E = Values.size(); I != E; ++I) {130OS << "{";131writeCstring(OS, Values[I]);132OS << ",";133OS << "static_cast<unsigned>(";134emitScopedNormalizedValue(OS, NormalizedValues[I]);135OS << ")},";136}137OS << "};\n";138return StringRef(ValueTableName);139}140141private:142void emitScopedNormalizedValue(raw_ostream &OS,143StringRef NormalizedValue) const {144if (!NormalizedValuesScope.empty())145OS << NormalizedValuesScope << "::";146OS << NormalizedValue;147}148};149150size_t MarshallingInfo::NextTableIndex = 0;151152static MarshallingInfo createMarshallingInfo(const Record &R) {153assert(!isa<UnsetInit>(R.getValueInit("KeyPath")) &&154!isa<UnsetInit>(R.getValueInit("DefaultValue")) &&155!isa<UnsetInit>(R.getValueInit("ValueMerger")) &&156"MarshallingInfo must have a provide a keypath, default value and a "157"value merger");158159MarshallingInfo Ret(R);160161Ret.ShouldAlwaysEmit = R.getValueAsBit("ShouldAlwaysEmit");162Ret.MacroPrefix = R.getValueAsString("MacroPrefix");163Ret.KeyPath = R.getValueAsString("KeyPath");164Ret.DefaultValue = R.getValueAsString("DefaultValue");165Ret.NormalizedValuesScope = R.getValueAsString("NormalizedValuesScope");166Ret.ImpliedCheck = R.getValueAsString("ImpliedCheck");167Ret.ImpliedValue =168R.getValueAsOptionalString("ImpliedValue").value_or(Ret.DefaultValue);169170Ret.ShouldParse = R.getValueAsString("ShouldParse");171Ret.Normalizer = R.getValueAsString("Normalizer");172Ret.Denormalizer = R.getValueAsString("Denormalizer");173Ret.ValueMerger = R.getValueAsString("ValueMerger");174Ret.ValueExtractor = R.getValueAsString("ValueExtractor");175176if (!isa<UnsetInit>(R.getValueInit("NormalizedValues"))) {177assert(!isa<UnsetInit>(R.getValueInit("Values")) &&178"Cannot provide normalized values for value-less options");179Ret.TableIndex = MarshallingInfo::NextTableIndex++;180Ret.NormalizedValues = R.getValueAsListOfStrings("NormalizedValues");181Ret.Values.reserve(Ret.NormalizedValues.size());182Ret.ValueTableName = getOptionName(R) + "ValueTable";183184StringRef ValuesStr = R.getValueAsString("Values");185for (;;) {186size_t Idx = ValuesStr.find(',');187if (Idx == StringRef::npos)188break;189if (Idx > 0)190Ret.Values.push_back(ValuesStr.slice(0, Idx));191ValuesStr = ValuesStr.substr(Idx + 1);192}193if (!ValuesStr.empty())194Ret.Values.push_back(ValuesStr);195196assert(Ret.Values.size() == Ret.NormalizedValues.size() &&197"The number of normalized values doesn't match the number of "198"values");199}200201return Ret;202}203204static void emitHelpTextsForVariants(205raw_ostream &OS, std::vector<std::pair<std::vector<std::string>, StringRef>>206HelpTextsForVariants) {207// OptTable must be constexpr so it uses std::arrays with these capacities.208const unsigned MaxVisibilityPerHelp = 2;209const unsigned MaxVisibilityHelp = 1;210211assert(HelpTextsForVariants.size() <= MaxVisibilityHelp &&212"Too many help text variants to store in "213"OptTable::HelpTextsForVariants");214215// This function must initialise any unused elements of those arrays.216for (auto [Visibilities, _] : HelpTextsForVariants)217while (Visibilities.size() < MaxVisibilityPerHelp)218Visibilities.push_back("0");219220while (HelpTextsForVariants.size() < MaxVisibilityHelp)221HelpTextsForVariants.push_back(222{std::vector<std::string>(MaxVisibilityPerHelp, "0"), ""});223224OS << ", (std::array<std::pair<std::array<unsigned, " << MaxVisibilityPerHelp225<< ">, const char*>, " << MaxVisibilityHelp << ">{{ ";226227auto VisibilityHelpEnd = HelpTextsForVariants.cend();228for (auto VisibilityHelp = HelpTextsForVariants.cbegin();229VisibilityHelp != VisibilityHelpEnd; ++VisibilityHelp) {230auto [Visibilities, Help] = *VisibilityHelp;231232assert(Visibilities.size() <= MaxVisibilityPerHelp &&233"Too many visibilities to store in an "234"OptTable::HelpTextsForVariants entry");235OS << "{std::array<unsigned, " << MaxVisibilityPerHelp << ">{{"236<< llvm::interleaved(Visibilities) << "}}, ";237238if (Help.size())239writeCstring(OS, Help);240else241OS << "nullptr";242OS << "}";243244if (std::next(VisibilityHelp) != VisibilityHelpEnd)245OS << ", ";246}247OS << " }})";248}249250/// OptionParserEmitter - This tablegen backend takes an input .td file251/// describing a list of options and emits a data structure for parsing and252/// working with those options when given an input command line.253static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {254// Get the option groups and options.255ArrayRef<const Record *> Groups =256Records.getAllDerivedDefinitions("OptionGroup");257std::vector<const Record *> Opts = Records.getAllDerivedDefinitions("Option");258llvm::sort(Opts, IsOptionRecordsLess);259260emitSourceFileHeader("Option Parsing Definitions", OS);261262// Generate prefix groups.263typedef SmallVector<SmallString<2>, 2> PrefixKeyT;264typedef std::map<PrefixKeyT, unsigned> PrefixesT;265PrefixesT Prefixes;266Prefixes.try_emplace(PrefixKeyT(), 0);267for (const Record &R : llvm::make_pointee_range(Opts)) {268std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");269PrefixKeyT PrefixKey(RPrefixes.begin(), RPrefixes.end());270Prefixes.try_emplace(PrefixKey, 0);271}272273DenseSet<StringRef> PrefixesUnionSet;274for (const auto &[Prefix, _] : Prefixes)275PrefixesUnionSet.insert_range(Prefix);276SmallVector<StringRef> PrefixesUnion(PrefixesUnionSet.begin(),277PrefixesUnionSet.end());278array_pod_sort(PrefixesUnion.begin(), PrefixesUnion.end());279280llvm::StringToOffsetTable Table;281// We can add all the prefixes via the union.282for (const auto &Prefix : PrefixesUnion)283Table.GetOrAddStringOffset(Prefix);284for (const Record &R : llvm::make_pointee_range(Groups))285Table.GetOrAddStringOffset(R.getValueAsString("Name"));286for (const Record &R : llvm::make_pointee_range(Opts))287Table.GetOrAddStringOffset(getOptionPrefixedName(R));288289// Dump string table.290OS << "/////////\n";291OS << "// String table\n\n";292OS << "#ifdef OPTTABLE_STR_TABLE_CODE\n";293Table.EmitStringTableDef(OS, "OptionStrTable");294OS << "#endif // OPTTABLE_STR_TABLE_CODE\n\n";295296// Dump prefixes.297OS << "/////////\n";298OS << "// Prefixes\n\n";299OS << "#ifdef OPTTABLE_PREFIXES_TABLE_CODE\n";300OS << "static constexpr llvm::StringTable::Offset OptionPrefixesTable[] = "301"{\n";302{303// Ensure the first prefix set is always empty.304assert(!Prefixes.empty() &&305"We should always emit an empty set of prefixes");306assert(Prefixes.begin()->first.empty() &&307"First prefix set should always be empty");308llvm::ListSeparator Sep(",\n");309unsigned CurIndex = 0;310for (auto &[Prefix, PrefixIndex] : Prefixes) {311// First emit the number of prefix strings in this list of prefixes.312OS << Sep << " " << Prefix.size() << " /* prefixes */";313PrefixIndex = CurIndex;314assert((CurIndex == 0 || !Prefix.empty()) &&315"Only first prefix set should be empty!");316for (const auto &PrefixKey : Prefix)317OS << ", " << *Table.GetStringOffset(PrefixKey) << " /* '" << PrefixKey318<< "' */";319CurIndex += Prefix.size() + 1;320}321}322OS << "\n};\n";323OS << "#endif // OPTTABLE_PREFIXES_TABLE_CODE\n\n";324325// Dump prefixes union.326OS << "/////////\n";327OS << "// Prefix Union\n\n";328OS << "#ifdef OPTTABLE_PREFIXES_UNION_CODE\n";329OS << "static constexpr llvm::StringTable::Offset OptionPrefixesUnion[] = "330"{\n";331{332llvm::ListSeparator Sep(", ");333for (auto Prefix : PrefixesUnion)334OS << Sep << " " << *Table.GetStringOffset(Prefix) << " /* '" << Prefix335<< "' */";336}337OS << "\n};\n";338OS << "#endif // OPTTABLE_PREFIXES_UNION_CODE\n\n";339340// Dump groups.341OS << "/////////\n";342OS << "// ValuesCode\n\n";343OS << "#ifdef OPTTABLE_VALUES_CODE\n";344for (const Record &R : llvm::make_pointee_range(Opts)) {345// The option values, if any;346if (!isa<UnsetInit>(R.getValueInit("ValuesCode"))) {347assert(isa<UnsetInit>(R.getValueInit("Values")) &&348"Cannot choose between Values and ValuesCode");349OS << "#define VALUES_CODE " << getOptionName(R) << "_Values\n";350OS << R.getValueAsString("ValuesCode") << "\n";351OS << "#undef VALUES_CODE\n";352}353}354OS << "#endif\n";355356OS << "/////////\n";357OS << "// Groups\n\n";358OS << "#ifdef OPTION\n";359for (const Record &R : llvm::make_pointee_range(Groups)) {360// Start a single option entry.361OS << "OPTION(";362363// A zero prefix offset corresponds to an empty set of prefixes.364OS << "0 /* no prefixes */";365366// The option string offset.367OS << ", ";368writeStrTableOffset(OS, Table, R.getValueAsString("Name"));369370// The option identifier name.371OS << ", " << getOptionName(R);372373// The option kind.374OS << ", Group";375376// The containing option group (if any).377OS << ", ";378if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group")))379OS << getOptionName(*DI->getDef());380else381OS << "INVALID";382383// The other option arguments (unused for groups).384OS << ", INVALID, nullptr, 0, 0, 0";385386// The option help text.387if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {388OS << ",\n";389OS << " ";390writeCstring(OS, R.getValueAsString("HelpText"));391} else {392OS << ", nullptr";393}394395// Not using Visibility specific text for group help.396emitHelpTextsForVariants(OS, {});397398// The option meta-variable name (unused).399OS << ", nullptr";400401// The option Values (unused for groups).402OS << ", nullptr)\n";403}404OS << "\n";405406OS << "//////////\n";407OS << "// Options\n\n";408409auto WriteOptRecordFields = [&](raw_ostream &OS, const Record &R) {410// The option prefix;411std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");412OS << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())] << ", ";413414// The option prefixed name.415writeStrTableOffset(OS, Table, getOptionPrefixedName(R));416417// The option identifier name.418OS << ", " << getOptionName(R);419420// The option kind.421OS << ", " << R.getValueAsDef("Kind")->getValueAsString("Name");422423// The containing option group (if any).424OS << ", ";425const ListInit *GroupFlags = nullptr;426const ListInit *GroupVis = nullptr;427if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {428GroupFlags = DI->getDef()->getValueAsListInit("Flags");429GroupVis = DI->getDef()->getValueAsListInit("Visibility");430OS << getOptionName(*DI->getDef());431} else {432OS << "INVALID";433}434435// The option alias (if any).436OS << ", ";437if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Alias")))438OS << getOptionName(*DI->getDef());439else440OS << "INVALID";441442// The option alias arguments (if any).443// Emitted as a \0 separated list in a string, e.g. ["foo", "bar"]444// would become "foo\0bar\0". Note that the compiler adds an implicit445// terminating \0 at the end.446OS << ", ";447std::vector<StringRef> AliasArgs = R.getValueAsListOfStrings("AliasArgs");448if (AliasArgs.size() == 0) {449OS << "nullptr";450} else {451OS << "\"";452for (StringRef AliasArg : AliasArgs)453OS << AliasArg << "\\0";454OS << "\"";455}456457// "Flags" for the option, such as HelpHidden and Render*458OS << ", ";459int NumFlags = 0;460const ListInit *LI = R.getValueAsListInit("Flags");461for (const Init *I : *LI)462OS << (NumFlags++ ? " | " : "") << cast<DefInit>(I)->getDef()->getName();463if (GroupFlags) {464for (const Init *I : *GroupFlags)465OS << (NumFlags++ ? " | " : "")466<< cast<DefInit>(I)->getDef()->getName();467}468if (NumFlags == 0)469OS << '0';470471// Option visibility, for sharing options between drivers.472OS << ", ";473int NumVisFlags = 0;474LI = R.getValueAsListInit("Visibility");475for (const Init *I : *LI)476OS << (NumVisFlags++ ? " | " : "")477<< cast<DefInit>(I)->getDef()->getName();478if (GroupVis) {479for (const Init *I : *GroupVis)480OS << (NumVisFlags++ ? " | " : "")481<< cast<DefInit>(I)->getDef()->getName();482}483if (NumVisFlags == 0)484OS << '0';485486// The option parameter field.487OS << ", " << R.getValueAsInt("NumArgs");488489// The option help text.490if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {491OS << ",\n";492OS << " ";493writeCstring(OS, R.getValueAsString("HelpText"));494} else {495OS << ", nullptr";496}497498std::vector<std::pair<std::vector<std::string>, StringRef>>499HelpTextsForVariants;500for (const Record *VisibilityHelp :501R.getValueAsListOfDefs("HelpTextsForVariants")) {502ArrayRef<const Init *> Visibilities =503VisibilityHelp->getValueAsListInit("Visibilities")->getElements();504505std::vector<std::string> VisibilityNames;506for (const Init *Visibility : Visibilities)507VisibilityNames.push_back(Visibility->getAsUnquotedString());508509HelpTextsForVariants.emplace_back(510VisibilityNames, VisibilityHelp->getValueAsString("Text"));511}512emitHelpTextsForVariants(OS, std::move(HelpTextsForVariants));513514// The option meta-variable name.515OS << ", ";516if (!isa<UnsetInit>(R.getValueInit("MetaVarName")))517writeCstring(OS, R.getValueAsString("MetaVarName"));518else519OS << "nullptr";520521// The option Values. Used for shell autocompletion.522OS << ", ";523if (!isa<UnsetInit>(R.getValueInit("Values")))524writeCstring(OS, R.getValueAsString("Values"));525else if (!isa<UnsetInit>(R.getValueInit("ValuesCode")))526OS << getOptionName(R) << "_Values";527else528OS << "nullptr";529};530531auto IsMarshallingOption = [](const Record &R) {532return !isa<UnsetInit>(R.getValueInit("KeyPath")) &&533!R.getValueAsString("KeyPath").empty();534};535536std::vector<const Record *> OptsWithMarshalling;537for (const Record &R : llvm::make_pointee_range(Opts)) {538// Start a single option entry.539OS << "OPTION(";540WriteOptRecordFields(OS, R);541OS << ")\n";542if (IsMarshallingOption(R))543OptsWithMarshalling.push_back(&R);544}545OS << "#endif // OPTION\n";546547auto CmpMarshallingOpts = [](const Record *const *A, const Record *const *B) {548unsigned AID = (*A)->getID();549unsigned BID = (*B)->getID();550551if (AID < BID)552return -1;553if (AID > BID)554return 1;555return 0;556};557// The RecordKeeper stores records (options) in lexicographical order, and we558// have reordered the options again when generating prefix groups. We need to559// restore the original definition order of options with marshalling to honor560// the topology of the dependency graph implied by `DefaultAnyOf`.561array_pod_sort(OptsWithMarshalling.begin(), OptsWithMarshalling.end(),562CmpMarshallingOpts);563564std::vector<MarshallingInfo> MarshallingInfos;565MarshallingInfos.reserve(OptsWithMarshalling.size());566for (const auto *R : OptsWithMarshalling)567MarshallingInfos.push_back(createMarshallingInfo(*R));568569for (const auto &MI : MarshallingInfos) {570OS << "#ifdef " << MI.getMacroName() << "\n";571OS << MI.getMacroName() << "(";572WriteOptRecordFields(OS, MI.R);573OS << ", ";574MI.emit(OS);575OS << ")\n";576OS << "#endif // " << MI.getMacroName() << "\n";577}578579OS << "\n";580OS << "#ifdef SIMPLE_ENUM_VALUE_TABLE";581OS << "\n";582OS << MarshallingInfo::ValueTablePreamble;583std::vector<StringRef> ValueTableNames;584for (const auto &MI : MarshallingInfos)585if (auto MaybeValueTableName = MI.emitValueTable(OS))586ValueTableNames.push_back(*MaybeValueTableName);587588OS << MarshallingInfo::ValueTablesDecl << "{";589for (auto ValueTableName : ValueTableNames)590OS << "{" << ValueTableName << ", std::size(" << ValueTableName << ")},\n";591OS << "};\n";592OS << "static const unsigned SimpleEnumValueTablesSize = "593"std::size(SimpleEnumValueTables);\n";594595OS << "#endif // SIMPLE_ENUM_VALUE_TABLE\n";596OS << "\n";597598OS << "\n";599}600601static TableGen::Emitter::Opt X("gen-opt-parser-defs", emitOptionParser,602"Generate option definitions");603604605