Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/OptParserEmitter.cpp
35258 views
//===- OptParserEmitter.cpp - Table Driven Command 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/Twine.h"12#include "llvm/Support/raw_ostream.h"13#include "llvm/TableGen/Record.h"14#include "llvm/TableGen/TableGenBackend.h"15#include <cstring>16#include <map>17#include <memory>1819using namespace llvm;2021static std::string getOptionName(const Record &R) {22// Use the record name unless EnumName is defined.23if (isa<UnsetInit>(R.getValueInit("EnumName")))24return std::string(R.getName());2526return std::string(R.getValueAsString("EnumName"));27}2829static raw_ostream &write_cstring(raw_ostream &OS, llvm::StringRef Str) {30OS << '"';31OS.write_escaped(Str);32OS << '"';33return OS;34}3536static std::string getOptionPrefixedName(const Record &R) {37std::vector<StringRef> Prefixes = R.getValueAsListOfStrings("Prefixes");38StringRef Name = R.getValueAsString("Name");3940if (Prefixes.empty())41return Name.str();4243return (Prefixes[0] + Twine(Name)).str();44}4546class MarshallingInfo {47public:48static constexpr const char *MacroName = "OPTION_WITH_MARSHALLING";49const Record &R;50bool ShouldAlwaysEmit = false;51StringRef MacroPrefix;52StringRef KeyPath;53StringRef DefaultValue;54StringRef NormalizedValuesScope;55StringRef ImpliedCheck;56StringRef ImpliedValue;57StringRef ShouldParse;58StringRef Normalizer;59StringRef Denormalizer;60StringRef ValueMerger;61StringRef ValueExtractor;62int TableIndex = -1;63std::vector<StringRef> Values;64std::vector<StringRef> NormalizedValues;65std::string ValueTableName;6667static size_t NextTableIndex;6869static constexpr const char *ValueTablePreamble = R"(70struct SimpleEnumValue {71const char *Name;72unsigned Value;73};7475struct SimpleEnumValueTable {76const SimpleEnumValue *Table;77unsigned Size;78};79)";8081static constexpr const char *ValueTablesDecl =82"static const SimpleEnumValueTable SimpleEnumValueTables[] = ";8384MarshallingInfo(const Record &R) : R(R) {}8586std::string getMacroName() const {87return (MacroPrefix + MarshallingInfo::MacroName).str();88}8990void emit(raw_ostream &OS) const {91OS << ShouldParse;92OS << ", ";93OS << ShouldAlwaysEmit;94OS << ", ";95OS << KeyPath;96OS << ", ";97emitScopedNormalizedValue(OS, DefaultValue);98OS << ", ";99OS << ImpliedCheck;100OS << ", ";101emitScopedNormalizedValue(OS, ImpliedValue);102OS << ", ";103OS << Normalizer;104OS << ", ";105OS << Denormalizer;106OS << ", ";107OS << ValueMerger;108OS << ", ";109OS << ValueExtractor;110OS << ", ";111OS << TableIndex;112}113114std::optional<StringRef> emitValueTable(raw_ostream &OS) const {115if (TableIndex == -1)116return {};117OS << "static const SimpleEnumValue " << ValueTableName << "[] = {\n";118for (unsigned I = 0, E = Values.size(); I != E; ++I) {119OS << "{";120write_cstring(OS, Values[I]);121OS << ",";122OS << "static_cast<unsigned>(";123emitScopedNormalizedValue(OS, NormalizedValues[I]);124OS << ")},";125}126OS << "};\n";127return StringRef(ValueTableName);128}129130private:131void emitScopedNormalizedValue(raw_ostream &OS,132StringRef NormalizedValue) const {133if (!NormalizedValuesScope.empty())134OS << NormalizedValuesScope << "::";135OS << NormalizedValue;136}137};138139size_t MarshallingInfo::NextTableIndex = 0;140141static MarshallingInfo createMarshallingInfo(const Record &R) {142assert(!isa<UnsetInit>(R.getValueInit("KeyPath")) &&143!isa<UnsetInit>(R.getValueInit("DefaultValue")) &&144!isa<UnsetInit>(R.getValueInit("ValueMerger")) &&145"MarshallingInfo must have a provide a keypath, default value and a "146"value merger");147148MarshallingInfo Ret(R);149150Ret.ShouldAlwaysEmit = R.getValueAsBit("ShouldAlwaysEmit");151Ret.MacroPrefix = R.getValueAsString("MacroPrefix");152Ret.KeyPath = R.getValueAsString("KeyPath");153Ret.DefaultValue = R.getValueAsString("DefaultValue");154Ret.NormalizedValuesScope = R.getValueAsString("NormalizedValuesScope");155Ret.ImpliedCheck = R.getValueAsString("ImpliedCheck");156Ret.ImpliedValue =157R.getValueAsOptionalString("ImpliedValue").value_or(Ret.DefaultValue);158159Ret.ShouldParse = R.getValueAsString("ShouldParse");160Ret.Normalizer = R.getValueAsString("Normalizer");161Ret.Denormalizer = R.getValueAsString("Denormalizer");162Ret.ValueMerger = R.getValueAsString("ValueMerger");163Ret.ValueExtractor = R.getValueAsString("ValueExtractor");164165if (!isa<UnsetInit>(R.getValueInit("NormalizedValues"))) {166assert(!isa<UnsetInit>(R.getValueInit("Values")) &&167"Cannot provide normalized values for value-less options");168Ret.TableIndex = MarshallingInfo::NextTableIndex++;169Ret.NormalizedValues = R.getValueAsListOfStrings("NormalizedValues");170Ret.Values.reserve(Ret.NormalizedValues.size());171Ret.ValueTableName = getOptionName(R) + "ValueTable";172173StringRef ValuesStr = R.getValueAsString("Values");174for (;;) {175size_t Idx = ValuesStr.find(',');176if (Idx == StringRef::npos)177break;178if (Idx > 0)179Ret.Values.push_back(ValuesStr.slice(0, Idx));180ValuesStr = ValuesStr.slice(Idx + 1, StringRef::npos);181}182if (!ValuesStr.empty())183Ret.Values.push_back(ValuesStr);184185assert(Ret.Values.size() == Ret.NormalizedValues.size() &&186"The number of normalized values doesn't match the number of "187"values");188}189190return Ret;191}192193static void EmitHelpTextsForVariants(194raw_ostream &OS, std::vector<std::pair<std::vector<std::string>, StringRef>>195HelpTextsForVariants) {196// OptTable must be constexpr so it uses std::arrays with these capacities.197const unsigned MaxVisibilityPerHelp = 2;198const unsigned MaxVisibilityHelp = 1;199200assert(HelpTextsForVariants.size() <= MaxVisibilityHelp &&201"Too many help text variants to store in "202"OptTable::HelpTextsForVariants");203204// This function must initialise any unused elements of those arrays.205for (auto [Visibilities, _] : HelpTextsForVariants)206while (Visibilities.size() < MaxVisibilityPerHelp)207Visibilities.push_back("0");208209while (HelpTextsForVariants.size() < MaxVisibilityHelp)210HelpTextsForVariants.push_back(211{std::vector<std::string>(MaxVisibilityPerHelp, "0"), ""});212213OS << ", (std::array<std::pair<std::array<unsigned, " << MaxVisibilityPerHelp214<< ">, const char*>, " << MaxVisibilityHelp << ">{{ ";215216auto VisibilityHelpEnd = HelpTextsForVariants.cend();217for (auto VisibilityHelp = HelpTextsForVariants.cbegin();218VisibilityHelp != VisibilityHelpEnd; ++VisibilityHelp) {219auto [Visibilities, Help] = *VisibilityHelp;220221assert(Visibilities.size() <= MaxVisibilityPerHelp &&222"Too many visibilities to store in an "223"OptTable::HelpTextsForVariants entry");224OS << "std::make_pair(std::array<unsigned, " << MaxVisibilityPerHelp225<< ">{{";226227auto VisibilityEnd = Visibilities.cend();228for (auto Visibility = Visibilities.cbegin(); Visibility != VisibilityEnd;229++Visibility) {230OS << *Visibility;231if (std::next(Visibility) != VisibilityEnd)232OS << ", ";233}234235OS << "}}, ";236237if (Help.size())238write_cstring(OS, Help);239else240OS << "nullptr";241OS << ")";242243if (std::next(VisibilityHelp) != VisibilityHelpEnd)244OS << ", ";245}246OS << " }})";247}248249/// OptParserEmitter - This tablegen backend takes an input .td file250/// describing a list of options and emits a data structure for parsing and251/// working with those options when given an input command line.252static void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {253// Get the option groups and options.254const std::vector<Record *> &Groups =255Records.getAllDerivedDefinitions("OptionGroup");256std::vector<Record *> Opts = Records.getAllDerivedDefinitions("Option");257258emitSourceFileHeader("Option Parsing Definitions", OS);259260array_pod_sort(Opts.begin(), Opts.end(), CompareOptionRecords);261// Generate prefix groups.262typedef SmallVector<SmallString<2>, 2> PrefixKeyT;263typedef std::map<PrefixKeyT, std::string> PrefixesT;264PrefixesT Prefixes;265Prefixes.insert(std::pair(PrefixKeyT(), "prefix_0"));266unsigned CurPrefix = 0;267for (const Record &R : llvm::make_pointee_range(Opts)) {268std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");269PrefixKeyT PrefixKey(RPrefixes.begin(), RPrefixes.end());270unsigned NewPrefix = CurPrefix + 1;271std::string Prefix = (Twine("prefix_") + Twine(NewPrefix)).str();272if (Prefixes.insert(std::pair(PrefixKey, Prefix)).second)273CurPrefix = NewPrefix;274}275276DenseSet<StringRef> PrefixesUnionSet;277for (const auto &Prefix : Prefixes)278PrefixesUnionSet.insert(Prefix.first.begin(), Prefix.first.end());279SmallVector<StringRef> PrefixesUnion(PrefixesUnionSet.begin(),280PrefixesUnionSet.end());281array_pod_sort(PrefixesUnion.begin(), PrefixesUnion.end());282283// Dump prefixes.284OS << "/////////\n";285OS << "// Prefixes\n\n";286OS << "#ifdef PREFIX\n";287OS << "#define COMMA ,\n";288for (const auto &Prefix : Prefixes) {289OS << "PREFIX(";290291// Prefix name.292OS << Prefix.second;293294// Prefix values.295OS << ", {";296for (const auto &PrefixKey : Prefix.first)297OS << "llvm::StringLiteral(\"" << PrefixKey << "\") COMMA ";298// Append an empty element to avoid ending up with an empty array.299OS << "llvm::StringLiteral(\"\")})\n";300}301OS << "#undef COMMA\n";302OS << "#endif // PREFIX\n\n";303304// Dump prefix unions.305OS << "/////////\n";306OS << "// Prefix Union\n\n";307OS << "#ifdef PREFIX_UNION\n";308OS << "#define COMMA ,\n";309OS << "PREFIX_UNION({\n";310for (const auto &Prefix : PrefixesUnion) {311OS << "llvm::StringLiteral(\"" << Prefix << "\") COMMA ";312}313OS << "llvm::StringLiteral(\"\")})\n";314OS << "#undef COMMA\n";315OS << "#endif // PREFIX_UNION\n\n";316317// Dump groups.318OS << "/////////\n";319OS << "// ValuesCode\n\n";320OS << "#ifdef OPTTABLE_VALUES_CODE\n";321for (const Record &R : llvm::make_pointee_range(Opts)) {322// The option values, if any;323if (!isa<UnsetInit>(R.getValueInit("ValuesCode"))) {324assert(isa<UnsetInit>(R.getValueInit("Values")) &&325"Cannot choose between Values and ValuesCode");326OS << "#define VALUES_CODE " << getOptionName(R) << "_Values\n";327OS << R.getValueAsString("ValuesCode") << "\n";328OS << "#undef VALUES_CODE\n";329}330}331OS << "#endif\n";332333OS << "/////////\n";334OS << "// Groups\n\n";335OS << "#ifdef OPTION\n";336for (const Record &R : llvm::make_pointee_range(Groups)) {337// Start a single option entry.338OS << "OPTION(";339340// The option prefix;341OS << "llvm::ArrayRef<llvm::StringLiteral>()";342343// The option string.344OS << ", \"" << R.getValueAsString("Name") << '"';345346// The option identifier name.347OS << ", " << getOptionName(R);348349// The option kind.350OS << ", Group";351352// The containing option group (if any).353OS << ", ";354if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group")))355OS << getOptionName(*DI->getDef());356else357OS << "INVALID";358359// The other option arguments (unused for groups).360OS << ", INVALID, nullptr, 0, 0, 0";361362// The option help text.363if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {364OS << ",\n";365OS << " ";366write_cstring(OS, R.getValueAsString("HelpText"));367} else368OS << ", nullptr";369370// Not using Visibility specific text for group help.371EmitHelpTextsForVariants(OS, {});372373// The option meta-variable name (unused).374OS << ", nullptr";375376// The option Values (unused for groups).377OS << ", nullptr)\n";378}379OS << "\n";380381OS << "//////////\n";382OS << "// Options\n\n";383384auto WriteOptRecordFields = [&](raw_ostream &OS, const Record &R) {385// The option prefix;386std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");387OS << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())] << ", ";388389// The option prefixed name.390write_cstring(OS, getOptionPrefixedName(R));391392// The option identifier name.393OS << ", " << getOptionName(R);394395// The option kind.396OS << ", " << R.getValueAsDef("Kind")->getValueAsString("Name");397398// The containing option group (if any).399OS << ", ";400const ListInit *GroupFlags = nullptr;401const ListInit *GroupVis = nullptr;402if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {403GroupFlags = DI->getDef()->getValueAsListInit("Flags");404GroupVis = DI->getDef()->getValueAsListInit("Visibility");405OS << getOptionName(*DI->getDef());406} else407OS << "INVALID";408409// The option alias (if any).410OS << ", ";411if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Alias")))412OS << getOptionName(*DI->getDef());413else414OS << "INVALID";415416// The option alias arguments (if any).417// Emitted as a \0 separated list in a string, e.g. ["foo", "bar"]418// would become "foo\0bar\0". Note that the compiler adds an implicit419// terminating \0 at the end.420OS << ", ";421std::vector<StringRef> AliasArgs = R.getValueAsListOfStrings("AliasArgs");422if (AliasArgs.size() == 0) {423OS << "nullptr";424} else {425OS << "\"";426for (StringRef AliasArg : AliasArgs)427OS << AliasArg << "\\0";428OS << "\"";429}430431// "Flags" for the option, such as HelpHidden and Render*432OS << ", ";433int NumFlags = 0;434const ListInit *LI = R.getValueAsListInit("Flags");435for (Init *I : *LI)436OS << (NumFlags++ ? " | " : "") << cast<DefInit>(I)->getDef()->getName();437if (GroupFlags) {438for (Init *I : *GroupFlags)439OS << (NumFlags++ ? " | " : "")440<< cast<DefInit>(I)->getDef()->getName();441}442if (NumFlags == 0)443OS << '0';444445// Option visibility, for sharing options between drivers.446OS << ", ";447int NumVisFlags = 0;448LI = R.getValueAsListInit("Visibility");449for (Init *I : *LI)450OS << (NumVisFlags++ ? " | " : "")451<< cast<DefInit>(I)->getDef()->getName();452if (GroupVis) {453for (Init *I : *GroupVis)454OS << (NumVisFlags++ ? " | " : "")455<< cast<DefInit>(I)->getDef()->getName();456}457if (NumVisFlags == 0)458OS << '0';459460// The option parameter field.461OS << ", " << R.getValueAsInt("NumArgs");462463// The option help text.464if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {465OS << ",\n";466OS << " ";467write_cstring(OS, R.getValueAsString("HelpText"));468} else469OS << ", nullptr";470471std::vector<std::pair<std::vector<std::string>, StringRef>>472HelpTextsForVariants;473for (Record *VisibilityHelp :474R.getValueAsListOfDefs("HelpTextsForVariants")) {475ArrayRef<Init *> Visibilities =476VisibilityHelp->getValueAsListInit("Visibilities")->getValues();477478std::vector<std::string> VisibilityNames;479for (Init *Visibility : Visibilities)480VisibilityNames.push_back(Visibility->getAsUnquotedString());481482HelpTextsForVariants.push_back(std::make_pair(483VisibilityNames, VisibilityHelp->getValueAsString("Text")));484}485EmitHelpTextsForVariants(OS, HelpTextsForVariants);486487// The option meta-variable name.488OS << ", ";489if (!isa<UnsetInit>(R.getValueInit("MetaVarName")))490write_cstring(OS, R.getValueAsString("MetaVarName"));491else492OS << "nullptr";493494// The option Values. Used for shell autocompletion.495OS << ", ";496if (!isa<UnsetInit>(R.getValueInit("Values")))497write_cstring(OS, R.getValueAsString("Values"));498else if (!isa<UnsetInit>(R.getValueInit("ValuesCode"))) {499OS << getOptionName(R) << "_Values";500} else501OS << "nullptr";502};503504auto IsMarshallingOption = [](const Record &R) {505return !isa<UnsetInit>(R.getValueInit("KeyPath")) &&506!R.getValueAsString("KeyPath").empty();507};508509std::vector<const Record *> OptsWithMarshalling;510for (const Record &R : llvm::make_pointee_range(Opts)) {511// Start a single option entry.512OS << "OPTION(";513WriteOptRecordFields(OS, R);514OS << ")\n";515if (IsMarshallingOption(R))516OptsWithMarshalling.push_back(&R);517}518OS << "#endif // OPTION\n";519520auto CmpMarshallingOpts = [](const Record *const *A, const Record *const *B) {521unsigned AID = (*A)->getID();522unsigned BID = (*B)->getID();523524if (AID < BID)525return -1;526if (AID > BID)527return 1;528return 0;529};530// The RecordKeeper stores records (options) in lexicographical order, and we531// have reordered the options again when generating prefix groups. We need to532// restore the original definition order of options with marshalling to honor533// the topology of the dependency graph implied by `DefaultAnyOf`.534array_pod_sort(OptsWithMarshalling.begin(), OptsWithMarshalling.end(),535CmpMarshallingOpts);536537std::vector<MarshallingInfo> MarshallingInfos;538MarshallingInfos.reserve(OptsWithMarshalling.size());539for (const auto *R : OptsWithMarshalling)540MarshallingInfos.push_back(createMarshallingInfo(*R));541542for (const auto &MI : MarshallingInfos) {543OS << "#ifdef " << MI.getMacroName() << "\n";544OS << MI.getMacroName() << "(";545WriteOptRecordFields(OS, MI.R);546OS << ", ";547MI.emit(OS);548OS << ")\n";549OS << "#endif // " << MI.getMacroName() << "\n";550}551552OS << "\n";553OS << "#ifdef SIMPLE_ENUM_VALUE_TABLE";554OS << "\n";555OS << MarshallingInfo::ValueTablePreamble;556std::vector<StringRef> ValueTableNames;557for (const auto &MI : MarshallingInfos)558if (auto MaybeValueTableName = MI.emitValueTable(OS))559ValueTableNames.push_back(*MaybeValueTableName);560561OS << MarshallingInfo::ValueTablesDecl << "{";562for (auto ValueTableName : ValueTableNames)563OS << "{" << ValueTableName << ", std::size(" << ValueTableName << ")},\n";564OS << "};\n";565OS << "static const unsigned SimpleEnumValueTablesSize = "566"std::size(SimpleEnumValueTables);\n";567568OS << "#endif // SIMPLE_ENUM_VALUE_TABLE\n";569OS << "\n";570571OS << "\n";572}573574static TableGen::Emitter::Opt X("gen-opt-parser-defs", EmitOptParser,575"Generate option definitions");576577578