| //===- OptionParserEmitter.cpp - Table Driven Command Option 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/SmallVector.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/ADT/StringMap.h" | |
| #include "llvm/ADT/Twine.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/StringToOffsetTable.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <array> | |
| #include <cstring> | |
| #include <map> | |
| #include <set> | |
| using namespace llvm; | |
| static std::string getOptionName(const Record &R) { | |
| // Use the record name unless EnumName is defined. | |
| if (isa<UnsetInit>(R.getValueInit("EnumName"))) | |
| return R.getName().str(); | |
| return R.getValueAsString("EnumName").str(); | |
| } | |
| // Only pass EmitComment for short strings that cannot contain "*/". | |
| static void writeStrTableOffset(raw_ostream &OS, | |
| const StringToOffsetTable &Table, | |
| llvm::StringRef Str, bool EmitComment = false) { | |
| std::optional<unsigned> Offset = Table.GetStringOffset(Str); | |
| if (!Offset) | |
| PrintFatalError("string was not added to the option string table: " + Str); | |
| OS << *Offset; | |
| if (EmitComment) { | |
| OS << " /* "; | |
| OS.write_escaped(Str); | |
| OS << " */"; | |
| } | |
| } | |
| static raw_ostream &writeCstring(raw_ostream &OS, llvm::StringRef Str) { | |
| OS << '"'; | |
| OS.write_escaped(Str); | |
| OS << '"'; | |
| return OS; | |
| } | |
| static StringRef getOptionalString(const Record &R, StringRef Field) { | |
| return R.getValueAsOptionalString(Field).value_or(""); | |
| } | |
| // Offset zero is the empty string and stands for an unset HelpText. A | |
| // HelpText<""> marks an option as deliberately undocumented, so it maps to a | |
| // second empty string that the table does not put at offset zero. | |
| static StringRef getHelpText(const Record &R) { | |
| std::optional<StringRef> S = R.getValueAsOptionalString("HelpText"); | |
| if (!S) | |
| return StringRef(); | |
| return S->empty() ? StringRef("\0", 1) : *S; | |
| } | |
| // The string table appends the empty string that terminates the list. | |
| static std::string getAliasArgsBlob(const Record &R) { | |
| std::string Blob; | |
| for (StringRef AliasArg : R.getValueAsListOfStrings("AliasArgs")) { | |
| if (AliasArg.empty()) | |
| PrintFatalError(R.getLoc(), "AliasArgs entries must not be empty"); | |
| Blob += AliasArg; | |
| Blob += '\0'; | |
| } | |
| return Blob; | |
| } | |
| static std::string getOptionPrefixedName(const Record &R) { | |
| std::vector<StringRef> Prefixes = R.getValueAsListOfStrings("Prefixes"); | |
| StringRef Name = R.getValueAsString("Name"); | |
| if (Prefixes.empty()) | |
| return Name.str(); | |
| return (Prefixes[0] + Twine(Name)).str(); | |
| } | |
| class MarshallingInfo { | |
| public: | |
| static constexpr const char *MacroName = "OPTION_WITH_MARSHALLING"; | |
| const Record &R; | |
| bool ShouldAlwaysEmit = false; | |
| StringRef MacroPrefix; | |
| StringRef KeyPath; | |
| StringRef DefaultValue; | |
| StringRef NormalizedValuesScope; | |
| StringRef ImpliedCheck; | |
| StringRef ImpliedValue; | |
| StringRef ShouldParse; | |
| StringRef Normalizer; | |
| StringRef Denormalizer; | |
| int TableIndex = -1; | |
| std::vector<StringRef> Values; | |
| std::vector<StringRef> NormalizedValues; | |
| std::string ValueTableName; | |
| static size_t NextTableIndex; | |
| static constexpr const char *ValueTablePreamble = R"( | |
| struct SimpleEnumValue { | |
| const char *Name; | |
| unsigned Value; | |
| }; | |
| struct SimpleEnumValueTable { | |
| const SimpleEnumValue *Table; | |
| unsigned Size; | |
| }; | |
| )"; | |
| static constexpr const char *ValueTablesDecl = | |
| "static const SimpleEnumValueTable SimpleEnumValueTables[] = "; | |
| MarshallingInfo(const Record &R) : R(R) {} | |
| std::string getMacroName() const { | |
| return (MacroPrefix + MarshallingInfo::MacroName).str(); | |
| } | |
| void emit(raw_ostream &OS) const { | |
| OS << ShouldParse; | |
| OS << ", "; | |
| OS << ShouldAlwaysEmit; | |
| OS << ", "; | |
| OS << KeyPath; | |
| OS << ", "; | |
| emitScopedNormalizedValue(OS, DefaultValue); | |
| OS << ", "; | |
| OS << ImpliedCheck; | |
| OS << ", "; | |
| emitScopedNormalizedValue(OS, ImpliedValue); | |
| OS << ", "; | |
| OS << Normalizer; | |
| OS << ", "; | |
| OS << Denormalizer; | |
| OS << ", "; | |
| OS << TableIndex; | |
| } | |
| std::optional<StringRef> emitValueTable(raw_ostream &OS) const { | |
| if (TableIndex == -1) | |
| return {}; | |
| OS << "static const SimpleEnumValue " << ValueTableName << "[] = {\n"; | |
| for (unsigned I = 0, E = Values.size(); I != E; ++I) { | |
| OS << "{"; | |
| writeCstring(OS, Values[I]); | |
| OS << ","; | |
| OS << "static_cast<unsigned>("; | |
| emitScopedNormalizedValue(OS, NormalizedValues[I]); | |
| OS << ")},"; | |
| } | |
| OS << "};\n"; | |
| return StringRef(ValueTableName); | |
| } | |
| private: | |
| void emitScopedNormalizedValue(raw_ostream &OS, | |
| StringRef NormalizedValue) const { | |
| if (!NormalizedValuesScope.empty()) | |
| OS << NormalizedValuesScope << "::"; | |
| OS << NormalizedValue; | |
| } | |
| }; | |
| size_t MarshallingInfo::NextTableIndex = 0; | |
| static MarshallingInfo createMarshallingInfo(const Record &R) { | |
| assert(!isa<UnsetInit>(R.getValueInit("KeyPath")) && | |
| !isa<UnsetInit>(R.getValueInit("DefaultValue")) && | |
| "MarshallingInfo must have a provide a keypath, default value and a " | |
| "value merger"); | |
| MarshallingInfo Ret(R); | |
| Ret.ShouldAlwaysEmit = R.getValueAsBit("ShouldAlwaysEmit"); | |
| Ret.MacroPrefix = R.getValueAsString("MacroPrefix"); | |
| Ret.KeyPath = R.getValueAsString("KeyPath"); | |
| Ret.DefaultValue = R.getValueAsString("DefaultValue"); | |
| Ret.NormalizedValuesScope = R.getValueAsString("NormalizedValuesScope"); | |
| Ret.ImpliedCheck = R.getValueAsString("ImpliedCheck"); | |
| Ret.ImpliedValue = | |
| R.getValueAsOptionalString("ImpliedValue").value_or(Ret.DefaultValue); | |
| Ret.ShouldParse = R.getValueAsString("ShouldParse"); | |
| Ret.Normalizer = R.getValueAsString("Normalizer"); | |
| Ret.Denormalizer = R.getValueAsString("Denormalizer"); | |
| if (!isa<UnsetInit>(R.getValueInit("NormalizedValues"))) { | |
| assert(!isa<UnsetInit>(R.getValueInit("Values")) && | |
| "Cannot provide normalized values for value-less options"); | |
| Ret.TableIndex = MarshallingInfo::NextTableIndex++; | |
| Ret.NormalizedValues = R.getValueAsListOfStrings("NormalizedValues"); | |
| Ret.Values.reserve(Ret.NormalizedValues.size()); | |
| Ret.ValueTableName = getOptionName(R) + "ValueTable"; | |
| StringRef ValuesStr = R.getValueAsString("Values"); | |
| for (;;) { | |
| size_t Idx = ValuesStr.find(','); | |
| if (Idx == StringRef::npos) | |
| break; | |
| if (Idx > 0) | |
| Ret.Values.push_back(ValuesStr.slice(0, Idx)); | |
| ValuesStr = ValuesStr.substr(Idx + 1); | |
| } | |
| if (!ValuesStr.empty()) | |
| Ret.Values.push_back(ValuesStr); | |
| assert(Ret.Values.size() == Ret.NormalizedValues.size() && | |
| "The number of normalized values doesn't match the number of " | |
| "values"); | |
| } | |
| return Ret; | |
| } | |
| // -foo-bar becomes foo_bar, or bar if Prefix is "foo-". | |
| static std::string getSpellingIdentifier(const Record &R, | |
| StringRef Prefix = "") { | |
| StringRef Spelling = R.getValueAsString("Name"); | |
| Spelling.consume_front(Prefix); | |
| std::string ID = Spelling.str(); | |
| llvm::replace(ID, '-', '_'); | |
| if (ID.empty() || isDigit(ID[0]) || | |
| !all_of(ID, [](char C) { return isAlnum(C) || C == '_'; })) | |
| PrintFatalError(R.getLoc(), "the spelling is not an identifier; name the " | |
| "defm"); | |
| return ID; | |
| } | |
| // `defm : BoolField<"foo-bar", ...>` declares member foo_bar, and a named defm | |
| // names it. | |
| static std::string getMemberName(const Record &R, StringRef Prefix) { | |
| StringRef Name = R.getValueAsString("FieldName"); | |
| return Name.starts_with("anonymous_") ? getSpellingIdentifier(R, Prefix) | |
| : Name.str(); | |
| } | |
| // The OPT_ name of an option of an OptionsStruct. `defm :` rows are named | |
| // after the spelling: -foo-bar is OPT_foo_bar. | |
| static std::string getStructOptionID(const Record &R) { | |
| if (!R.getName().starts_with("anonymous_")) | |
| return getOptionName(R); | |
| return getSpellingIdentifier(R); | |
| } | |
| // The preprocessor expression of R's Condition, or "" if it has none. | |
| static StringRef getCondition(const Record &R) { | |
| const RecordVal *V = R.getValue("Condition"); | |
| const auto *DI = V ? dyn_cast<DefInit>(V->getValue()) : nullptr; | |
| return DI ? DI->getDef()->getValueAsString("Expr") : ""; | |
| } | |
| namespace { | |
| // Wraps what is emitted during its lifetime in `#if Condition` where R has one. | |
| class ConditionScope { | |
| raw_ostream &OS; | |
| StringRef Condition; | |
| public: | |
| ConditionScope(raw_ostream &OS, const Record &R) | |
| : OS(OS), Condition(getCondition(R)) { | |
| if (!Condition.empty()) | |
| OS << "#if " << Condition << '\n'; | |
| } | |
| ~ConditionScope() { | |
| if (!Condition.empty()) | |
| OS << "#endif\n"; | |
| } | |
| }; | |
| } // namespace | |
| // Emits the OPT_ IDs of an OptionsStruct, which its table rows name. | |
| static void emitStructOptionIDs(ArrayRef<const Record *> Groups, | |
| ArrayRef<const Record *> Opts, | |
| raw_ostream &OS) { | |
| OS << "namespace {\nenum ID : unsigned {\n OPT_INVALID = 0,\n"; | |
| for (const Record *R : Groups) | |
| OS << " OPT_" << getOptionName(*R) << ",\n"; | |
| for (const Record *R : Opts) { | |
| ConditionScope CS(OS, *R); | |
| OS << " OPT_" << getStructOptionID(*R) << ",\n"; | |
| } | |
| OS << "};\n} // namespace\n\n"; | |
| } | |
| // Emits the struct an OptionsStruct def declares: its declaration under | |
| // OPTIONS_STRUCT_DECL, and under OPTIONS_STRUCT_DEFS the global instance, the | |
| // option table, and apply(), which sets the member an argument names. | |
| static void emitOptionsStruct(const Record &Struct, | |
| ArrayRef<const Record *> Groups, | |
| ArrayRef<const Record *> Opts, raw_ostream &OS) { | |
| struct Member { | |
| std::string Name; | |
| StringRef Type, Default, Spelling; | |
| }; | |
| std::vector<const Record *> Fields; | |
| for (const Record *R : Opts) { | |
| StringRef Kind = R->getValueAsDef("Kind")->getValueAsString("Name"); | |
| if (!R->getValue("FieldName")) { | |
| if (Kind != "Input" && Kind != "Unknown" && !R->getValue("AliasTarget")) | |
| PrintFatalError(R->getLoc(), "an option of an OptionsStruct must be " | |
| "declared with BoolField, ValueField or " | |
| "AliasOf"); | |
| continue; | |
| } | |
| if (Kind != "FlagOrEq" && Kind != "SeparateOrEq") | |
| PrintFatalError(R->getLoc(), | |
| "a member is set by a FlagOrEq or SeparateOrEq"); | |
| StringRef Type = R->getValueAsString("FieldType"); | |
| if (Kind == "FlagOrEq" && Type != "bool" && Type != "llvm::BoolOrDefault" && | |
| !R->getValue("BareValue")) | |
| PrintFatalError(R->getLoc(), "a FlagOrEq sets a bool or has a BareValue"); | |
| Fields.push_back(R); | |
| } | |
| // Members in declaration order. | |
| std::vector<const Record *> ByID = Fields; | |
| llvm::sort(ByID, [](const Record *A, const Record *B) { | |
| return A->getID() < B->getID(); | |
| }); | |
| StringRef Prefix = Struct.getValueAsString("MemberPrefix"); | |
| std::vector<Member> Members; | |
| StringMap<StringRef> MemberSpelling; | |
| for (const Record *R : ByID) { | |
| Member M{getMemberName(*R, Prefix), R->getValueAsString("FieldType"), | |
| R->getValueAsString("FieldDefault"), R->getValueAsString("Name")}; | |
| auto [It, Inserted] = MemberSpelling.try_emplace(M.Name, M.Spelling); | |
| if (!Inserted) | |
| PrintFatalError(R->getLoc(), | |
| "member '" + M.Name + "' is also set by -" + It->second); | |
| Members.push_back(M); | |
| } | |
| StringRef Name = Struct.getName(); | |
| OS << "\n#ifdef OPTIONS_STRUCT_DECL\n#undef OPTIONS_STRUCT_DECL\n"; | |
| OS << "#include \"llvm/ADT/ArrayRef.h\"\n"; | |
| OS << "#include \"llvm/ADT/BoolOrDefault.h\"\n"; | |
| OS << "#include \"llvm/ADT/StringRef.h\"\n"; | |
| OS << "#include \"llvm/Support/Allocator.h\"\n"; | |
| bool PerContext = Struct.getValueAsBit("PerContext"); | |
| if (PerContext) | |
| OS << "#include \"llvm/Support/LibraryOptionsContext.h\"\n"; | |
| OS << "\n"; | |
| StringRef Namespace = Struct.getValueAsString("Namespace"); | |
| OS << "namespace llvm {\nnamespace opt {\nclass Arg;\n"; | |
| if (PerContext) | |
| OS << "class LibraryOptionsParser;\n"; | |
| OS << "class OptTable;\n} // namespace opt\n} // namespace llvm\n\n"; | |
| OS << "namespace " << Namespace << " {\n"; | |
| OS << "struct " << Name << " {\n"; | |
| if (Namespace != "llvm") | |
| OS << " using StringRef = llvm::StringRef;\n"; | |
| for (auto [R, M] : llvm::zip_equal(ByID, Members)) { | |
| ConditionScope CS(OS, *R); | |
| OS << " " << M.Type << " " << M.Name << "{" << M.Default << "};\n"; | |
| } | |
| OS << "\n static constexpr bool PerContext = " | |
| << (PerContext ? "true" : "false") << ";\n"; | |
| OS << " static const llvm::opt::OptTable &optTable();\n"; | |
| OS << " /// Sets the member that \\p A names, allocating a list from \\p " | |
| "Alloc.\n /// Returns false if the value is invalid.\n"; | |
| OS << " bool apply(const llvm::opt::Arg &A, llvm::BumpPtrAllocator " | |
| "&Alloc);\n"; | |
| if (PerContext) | |
| OS << "\nprivate:\n friend class llvm::cl::LibraryOptionsContext;\n" | |
| " friend class llvm::opt::LibraryOptionsParser;\n"; | |
| OS << "\n static " << Name << " Global;\n"; | |
| OS << " static const " << Name << " Default;\n"; | |
| OS << "};\n} // namespace " << Namespace << "\n"; | |
| OS << "#endif // OPTIONS_STRUCT_DECL\n"; | |
| std::string Qualified = (Namespace + "::" + Name).str(); | |
| OS << "\n#ifdef OPTIONS_STRUCT_DEFS\n#undef OPTIONS_STRUCT_DEFS\n"; | |
| OS << Qualified << " " << Qualified << "::Global;\n"; | |
| OS << "LLVM_REQUIRE_CONSTANT_INITIALIZATION const " << Qualified << " " | |
| << Qualified << "::Default;\n\n"; | |
| OS << "const llvm::opt::OptTable &" << Qualified << "::optTable() {\n"; | |
| OS << " static const llvm::opt::LibraryOptTable T(optionTables());\n"; | |
| OS << " return T;\n}\n\n"; | |
| OS << "bool " << Qualified | |
| << "::apply(const llvm::opt::Arg &A, llvm::BumpPtrAllocator &Alloc) {\n"; | |
| OS << " switch (A.getOption().getID()) {\n"; | |
| for (const Record *R : Fields) { | |
| ConditionScope CS(OS, *R); | |
| OS << " case OPT_" << getStructOptionID(*R) << ":\n"; | |
| std::string Member = getMemberName(*R, Prefix); | |
| if (R->getValue("BareValue")) | |
| OS << " if (!A.getNumValues()) { " << Member << " = " | |
| << R->getValueAsString("BareValue") << "; return true; }\n"; | |
| if (!isa<UnsetInit>(R->getValueInit("NormalizedValues"))) { | |
| SmallVector<StringRef> Values; | |
| R->getValueAsString("Values").split(Values, ','); | |
| std::vector<StringRef> Enumerators = | |
| R->getValueAsListOfStrings("NormalizedValues"); | |
| if (Values.size() != Enumerators.size()) | |
| PrintFatalError(R->getLoc(), "an EnumField needs one enumerator per " | |
| "value"); | |
| // The generic lambda sets a scalar, a std::optional, or a list element. | |
| OS << " return llvm::opt::parseArgValue(A.getValue(), " << Member | |
| << ", Alloc, [](llvm::StringRef V, auto &X) {\n"; | |
| for (auto [Value, Enumerator] : llvm::zip_equal(Values, Enumerators)) | |
| OS << " if (V == \"" << Value | |
| << "\") {\n X = " << Enumerator | |
| << ";\n return true;\n }\n"; | |
| OS << " return false;\n });\n"; | |
| continue; | |
| } | |
| // A FlagOrEq without a value means =true. | |
| StringRef Value = | |
| R->getValueAsDef("Kind")->getValueAsString("Name") == "FlagOrEq" | |
| ? "A.getNumValues() ? A.getValue() : \"true\"" | |
| : "A.getValue()"; | |
| OS << " return llvm::opt::parseArgValue(" << Value << ", " << Member | |
| << ", Alloc);\n"; | |
| } | |
| OS << " }\n llvm_unreachable(\"option without a member\");\n}\n"; | |
| OS << "#endif // OPTIONS_STRUCT_DEFS\n"; | |
| } | |
| /// OptionParserEmitter - This tablegen backend takes an input .td file | |
| /// describing a list of options and emits a data structure for parsing and | |
| /// working with those options when given an input command line. | |
| static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) { | |
| // Get the option groups and options. | |
| ArrayRef<const Record *> Groups = | |
| Records.getAllDerivedDefinitions("OptionGroup"); | |
| std::vector<const Record *> Opts = Records.getAllDerivedDefinitions("Option"); | |
| llvm::sort(Opts, IsOptionRecordsLess); | |
| std::vector<const Record *> SubCommands = | |
| Records.getAllDerivedDefinitions("SubCommand"); | |
| ArrayRef<const Record *> Structs = | |
| Records.getAllDerivedDefinitionsIfDefined("OptionsStruct"); | |
| if (Structs.size() > 1) | |
| PrintFatalError(Structs[1]->getLoc(), "only one OptionsStruct is allowed"); | |
| StringMap<const Record *> Members; | |
| for (const Record *R : Opts) | |
| if (R->getValue("FieldName")) | |
| Members[R->getValueAsString("Name")] = R; | |
| DenseMap<const Record *, const Record *> AliasTargets; | |
| for (const Record *R : Opts) { | |
| if (!R->getValue("AliasTarget")) | |
| continue; | |
| StringRef Target = R->getValueAsString("AliasTarget"); | |
| const Record *T = Members.lookup(Target); | |
| if (!T) | |
| PrintFatalError(R->getLoc(), | |
| "'-" + Target + | |
| "' is not an option of the OptionsStruct"); | |
| if (!getCondition(*T).empty()) | |
| PrintFatalError(R->getLoc(), "an alias cannot refer to an option with a " | |
| "Condition"); | |
| AliasTargets[R] = T; | |
| } | |
| emitSourceFileHeader("Option Parsing Definitions", OS); | |
| // Generate prefix groups. | |
| using PrefixKeyT = SmallVector<SmallString<2>, 2>; | |
| using PrefixesT = std::map<PrefixKeyT, unsigned>; | |
| PrefixesT Prefixes; | |
| Prefixes.try_emplace(PrefixKeyT(), 0); | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes"); | |
| PrefixKeyT PrefixKey(RPrefixes.begin(), RPrefixes.end()); | |
| Prefixes.try_emplace(PrefixKey, 0); | |
| } | |
| // Generate sub command groups. | |
| using SubCommandKeyT = SmallVector<StringRef, 2>; | |
| using SubCommandIDsT = std::map<SubCommandKeyT, unsigned>; | |
| SubCommandIDsT SubCommandIDs; | |
| auto GetSubCommandIDsOffset = [&SubCommandIDs](const Record &R) { | |
| SubCommandKeyT SubCommandKey; | |
| for (const Record *SubCommand : R.getValueAsListOfDefs("SubCommands")) | |
| SubCommandKey.push_back(SubCommand->getName()); | |
| return SubCommandIDs[SubCommandKey]; | |
| }; | |
| SubCommandIDs.try_emplace(SubCommandKeyT(), 0); | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| std::vector<const Record *> RSubCommands = | |
| R.getValueAsListOfDefs("SubCommands"); | |
| SubCommandKeyT SubCommandKey; | |
| for (const auto &SubCommand : RSubCommands) | |
| SubCommandKey.push_back(SubCommand->getName()); | |
| SubCommandIDs.try_emplace(SubCommandKey, 0); | |
| } | |
| llvm::StringToOffsetTable Table; | |
| for (const auto &[PrefixSet, _] : Prefixes) | |
| for (const auto &Prefix : PrefixSet) | |
| Table.GetOrAddStringOffset(Prefix); | |
| for (const Record &R : llvm::make_pointee_range(Groups)) { | |
| Table.GetOrAddStringOffset(R.getValueAsString("Name")); | |
| Table.GetOrAddStringOffset(getHelpText(R)); | |
| } | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| Table.GetOrAddStringOffset(getOptionPrefixedName(R)); | |
| Table.GetOrAddStringOffset(getHelpText(R)); | |
| Table.GetOrAddStringOffset(getOptionalString(R, "MetaVarName")); | |
| Table.GetOrAddStringOffset(getOptionalString(R, "Values")); | |
| Table.GetOrAddStringOffset(getAliasArgsBlob(R)); | |
| for (const Record *V : R.getValueAsListOfDefs("HelpTextsForVariants")) | |
| Table.GetOrAddStringOffset(V->getValueAsString("Text")); | |
| } | |
| // Flags and Visibility name enumerators of the including tool. An option | |
| // inherits its group's. Names are sorted so that equal masks share an | |
| // InfoExtra row. | |
| auto GetMask = [](const Record &R, StringRef Field) { | |
| std::set<StringRef> Names; | |
| for (const Init *I : *R.getValueAsListInit(Field)) | |
| Names.insert(cast<DefInit>(I)->getDef()->getName()); | |
| if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) | |
| for (const Init *I : *DI->getDef()->getValueAsListInit(Field)) | |
| Names.insert(cast<DefInit>(I)->getDef()->getName()); | |
| std::string Mask; | |
| raw_string_ostream MaskOS(Mask); | |
| ListSeparator Sep(" | "); | |
| for (StringRef Name : Names) | |
| MaskOS << Sep << "static_cast<unsigned>(" << Name << ")"; | |
| return Mask.empty() ? std::string("0") : Mask; | |
| }; | |
| // IDs are 1-based positions in the table, which lists groups first. | |
| DenseMap<const Record *, unsigned> OptionID; | |
| for (const Record &R : llvm::make_pointee_range(Groups)) | |
| OptionID.try_emplace(&R, OptionID.size() + 1); | |
| for (const Record &R : llvm::make_pointee_range(Opts)) | |
| OptionID.try_emplace(&R, OptionID.size() + 1); | |
| // An OptionsStruct's rows name the IDs, since a false Condition omits a row | |
| // and shifts the IDs after it. | |
| auto GetRefID = [&](const Record &R, StringRef Field) -> std::string { | |
| const Record *Ref = Field == "Alias" ? AliasTargets.lookup(&R) : nullptr; | |
| if (const auto *DI = dyn_cast<DefInit>(R.getValueInit(Field))) | |
| Ref = DI->getDef(); | |
| if (!Ref) | |
| return "0"; | |
| if (Structs.empty()) | |
| return utostr(OptionID.lookup(Ref)); | |
| return "OPT_" + getStructOptionID(*Ref); | |
| }; | |
| OS << "/////////\n"; | |
| OS << "// Tables\n\n"; | |
| // An OptionsStruct's definitions use the tables. | |
| OS << (Structs.empty() | |
| ? "#ifdef OPTTABLE_CODE\n" | |
| : "#if defined(OPTTABLE_CODE) || defined(OPTIONS_STRUCT_DEFS)\n"); | |
| if (!Structs.empty()) | |
| emitStructOptionIDs(Groups, Opts, OS); | |
| // A function rather than an object: the object needs dynamic relocations. | |
| OS << "static llvm::opt::OptTable::Tables optionTables() {\n"; | |
| // An OptionsStruct's .cpp has no using-directive for llvm::opt. | |
| if (!Structs.empty()) | |
| OS << " using llvm::opt::DefaultVis, llvm::opt::HelpHidden,\n" | |
| " llvm::opt::RenderAsInput, llvm::opt::RenderJoined,\n" | |
| " llvm::opt::RenderSeparate;\n"; | |
| Table.EmitStringTableDef(OS, "OptionStrTable"); | |
| OS << "\n"; | |
| // Dump prefixes. | |
| OS << " static constexpr llvm::StringTable::Offset OptionPrefixesTable[] = " | |
| "{\n"; | |
| { | |
| // Ensure the first prefix set is always empty. | |
| assert(!Prefixes.empty() && | |
| "We should always emit an empty set of prefixes"); | |
| assert(Prefixes.begin()->first.empty() && | |
| "First prefix set should always be empty"); | |
| llvm::ListSeparator Sep(",\n"); | |
| unsigned CurIndex = 0; | |
| for (auto &[Prefix, PrefixIndex] : Prefixes) { | |
| // First emit the number of prefix strings in this list of prefixes. | |
| OS << Sep << " " << Prefix.size() << " /* prefixes */"; | |
| PrefixIndex = CurIndex; | |
| if (PrefixIndex > 255) | |
| PrintFatalError("too many distinct prefix sets"); | |
| assert((CurIndex == 0 || !Prefix.empty()) && | |
| "Only first prefix set should be empty!"); | |
| for (const auto &PrefixKey : Prefix) | |
| OS << ", " << *Table.GetStringOffset(PrefixKey) << " /* '" << PrefixKey | |
| << "' */"; | |
| CurIndex += Prefix.size() + 1; | |
| } | |
| } | |
| OS << "\n };\n\n"; | |
| // Dump help text variants. Each option's variants form a run ended by a zero | |
| // row; offset 0 is the empty run. | |
| OS << " static constexpr llvm::opt::OptTable::HelpTextVariant " | |
| "OptionHelpTextVariantsTable[] = {\n"; | |
| DenseMap<const Record *, unsigned> HelpTextVariantsOffset; | |
| unsigned NumVariantRows = 1; | |
| OS << " {0, 0},\n"; | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| std::vector<const Record *> Variants = | |
| R.getValueAsListOfDefs("HelpTextsForVariants"); | |
| if (Variants.empty()) | |
| continue; | |
| HelpTextVariantsOffset[&R] = NumVariantRows; | |
| NumVariantRows += Variants.size() + 1; | |
| for (const Record *V : Variants) { | |
| const ListInit *Vis = V->getValueAsListInit("Visibilities"); | |
| if (Vis->empty()) | |
| PrintFatalError(V->getLoc(), "HelpTextVariant needs a visibility"); | |
| OS << " {"; | |
| ListSeparator Sep(" | "); | |
| for (const Init *I : *Vis) | |
| OS << Sep << I->getAsUnquotedString(); | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, V->getValueAsString("Text")); | |
| OS << "},\n"; | |
| } | |
| OS << " {0, 0},\n"; | |
| } | |
| OS << " };\n\n"; | |
| // Dump subcommands. | |
| if (!SubCommands.empty()) { | |
| 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\n"; | |
| } | |
| // Dump subcommand IDs. | |
| OS << " static constexpr unsigned OptionSubCommandIDsTable[] = {\n"; | |
| { | |
| // Ensure the first subcommand set is always empty. | |
| assert(!SubCommandIDs.empty() && | |
| "We should always emit an empty set of subcommands"); | |
| assert(SubCommandIDs.begin()->first.empty() && | |
| "First subcommand set should always be empty"); | |
| llvm::ListSeparator Sep(",\n"); | |
| unsigned CurIndex = 0; | |
| for (auto &[SubCommand, SubCommandIndex] : SubCommandIDs) { | |
| // First emit the number of subcommand strings in this list of | |
| // subcommands. | |
| OS << Sep << " " << SubCommand.size() << " /* subcommands */"; | |
| SubCommandIndex = CurIndex; | |
| assert((CurIndex == 0 || !SubCommand.empty()) && | |
| "Only first subcommand set should be empty!"); | |
| for (const auto &SubCommandKey : SubCommand) { | |
| auto It = llvm::find_if(SubCommands, [&](const Record *R) { | |
| return R->getName() == SubCommandKey; | |
| }); | |
| assert(It != SubCommands.end() && "SubCommand not found"); | |
| OS << ", " << std::distance(SubCommands.begin(), It) << " /* '" | |
| << SubCommandKey << "' */"; | |
| } | |
| CurIndex += SubCommand.size() + 1; | |
| } | |
| } | |
| OS << "\n };\n\n"; | |
| // Fields that are rarely set or take few distinct values, in | |
| // OptTable::InfoExtra order. Options with equal values share a row; row 0 is | |
| // all zero. | |
| OS << " static constexpr llvm::opt::OptTable::InfoExtra " | |
| "OptionInfoExtrasTable[] = {\n {0, 0, 0, 0, 0, 0, 0, 0},\n"; | |
| std::map<std::array<std::string, 8>, unsigned> ExtraRows; | |
| ExtraRows.try_emplace({"0", "0", "0", "0", "0", "0", "0", "0"}, 0); | |
| DenseMap<const Record *, unsigned> ExtraOffset; | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| int64_t NumArgs = R.getValueAsInt("NumArgs"); | |
| if (NumArgs < 0 || NumArgs > 255) | |
| PrintFatalError(R.getLoc(), "NumArgs must be in [0, 255]"); | |
| std::array<std::string, 8> Row = { | |
| utostr(*Table.GetStringOffset(getOptionalString(R, "MetaVarName"))), | |
| utostr(*Table.GetStringOffset(getAliasArgsBlob(R))), | |
| utostr(*Table.GetStringOffset(getOptionalString(R, "Values"))), | |
| GetMask(R, "Flags"), | |
| GetMask(R, "Visibility"), | |
| utostr(HelpTextVariantsOffset.lookup(&R)), | |
| utostr(GetSubCommandIDsOffset(R)), | |
| utostr(NumArgs)}; | |
| auto [It, Inserted] = ExtraRows.try_emplace(Row, ExtraRows.size()); | |
| if (Inserted) { | |
| OS << " {"; | |
| interleaveComma(Row, OS); | |
| OS << "},\n"; | |
| } | |
| ExtraOffset[&R] = It->second; | |
| } | |
| OS << " };\n\n"; | |
| // Dump the option table in OptTable::Info field order. | |
| OS << " static constexpr llvm::opt::OptTable::Info OptionInfoTable[] = {\n"; | |
| for (const Record &R : llvm::make_pointee_range(Groups)) { | |
| OS << " {"; | |
| writeStrTableOffset(OS, Table, R.getValueAsString("Name"), | |
| /*EmitComment=*/true); | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, getHelpText(R)); | |
| OS << ", " << GetRefID(R, "Group") | |
| << ", 0, 0, 0, llvm::opt::Option::GroupClass},\n"; | |
| } | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| ConditionScope CS(OS, R); | |
| OS << " {"; | |
| writeStrTableOffset(OS, Table, getOptionPrefixedName(R), | |
| /*EmitComment=*/true); | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, getHelpText(R)); | |
| OS << ", " << GetRefID(R, "Group") << ", " << GetRefID(R, "Alias"); | |
| OS << ", " << ExtraOffset.lookup(&R); | |
| std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes"); | |
| OS << ", " << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())]; | |
| // An AliasOf without AliasArgs takes its target's kind. | |
| const Record *KindR = AliasTargets.lookup(&R); | |
| if (!KindR || !R.getValueAsListOfStrings("AliasArgs").empty()) | |
| KindR = &R; | |
| OS << ", llvm::opt::Option::" | |
| << KindR->getValueAsDef("Kind")->getValueAsString("Name") << "Class},\n"; | |
| } | |
| OS << " };\n\n"; | |
| OS << " return {OptionStrTable, OptionPrefixesTable,\n"; | |
| OS << " OptionInfoTable, OptionInfoExtrasTable, " | |
| "OptionHelpTextVariantsTable, " | |
| << (SubCommands.empty() ? "{}" : "OptionSubCommands") | |
| << ", OptionSubCommandIDsTable};\n"; | |
| OS << "}\n"; | |
| OS << "#undef OPTTABLE_CODE\n"; | |
| OS << (Structs.empty() | |
| ? "#endif // OPTTABLE_CODE\n\n" | |
| : "#endif // OPTTABLE_CODE || OPTIONS_STRUCT_DEFS\n\n"); | |
| if (!Structs.empty()) | |
| emitOptionsStruct(*Structs[0], Groups, Opts, OS); | |
| // Dump ValuesCode. | |
| OS << "/////////\n"; | |
| OS << "// ValuesCode\n\n"; | |
| OS << "#ifdef OPTTABLE_VALUES_CODE\n"; | |
| std::vector<const Record *> ValuesCodeOpts; | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| // The option values, if any; | |
| if (!isa<UnsetInit>(R.getValueInit("ValuesCode"))) { | |
| if (!isa<UnsetInit>(R.getValueInit("Values"))) | |
| PrintFatalError(R.getLoc(), "cannot set both Values and ValuesCode"); | |
| ValuesCodeOpts.push_back(&R); | |
| OS << "#define VALUES_CODE " << getOptionName(R) << "_Values\n"; | |
| OS << R.getValueAsString("ValuesCode") << "\n"; | |
| OS << "#undef VALUES_CODE\n"; | |
| } | |
| } | |
| // A function keeps these strings out of a relocated table. It names OPT_ IDs, | |
| // so include this block after the option enum; a table that uses a different | |
| // ID prefix cannot use ValuesCode. | |
| OS << "static llvm::StringRef getOptionValuesCode(unsigned ID) {\n"; | |
| OS << " switch (ID) {\n"; | |
| for (const Record *R : ValuesCodeOpts) | |
| OS << " case OPT_" << getOptionName(*R) << ": return " << getOptionName(*R) | |
| << "_Values;\n"; | |
| OS << " }\n return {};\n}\n"; | |
| OS << "#undef OPTTABLE_VALUES_CODE\n"; | |
| OS << "#endif // OPTTABLE_VALUES_CODE\n"; | |
| OS << "/////////\n"; | |
| OS << "// Groups\n\n"; | |
| OS << "#ifdef OPTION\n"; | |
| for (const Record &R : llvm::make_pointee_range(Groups)) { | |
| // Start a single option entry. | |
| OS << "OPTION("; | |
| // A zero prefix offset corresponds to an empty set of prefixes. | |
| OS << "0 /* no prefixes */"; | |
| // The option string offset. | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, R.getValueAsString("Name"), | |
| /*EmitComment=*/true); | |
| // The option identifier name. | |
| OS << ", " << getOptionName(R); | |
| // The option kind. | |
| OS << ", Group"; | |
| // The containing option group (if any). | |
| OS << ", "; | |
| if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) | |
| OS << getOptionName(*DI->getDef()); | |
| else | |
| OS << "INVALID"; | |
| // The other option arguments (unused for groups). | |
| OS << ", INVALID, 0, 0, 0, 0"; | |
| // The option help text. | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, getHelpText(R)); | |
| // Groups have no help text variants. | |
| OS << ", 0"; | |
| // The option meta-variable name (unused). | |
| OS << ", 0"; | |
| // The option Values and SubCommandIDsOffset (unused for groups). | |
| OS << ", 0, 0)\n"; | |
| } | |
| OS << "\n"; | |
| OS << "//////////\n"; | |
| OS << "// Options\n\n"; | |
| auto WriteOptRecordFields = [&](raw_ostream &OS, const Record &R) { | |
| // The option prefix; | |
| std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes"); | |
| OS << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())] << ", "; | |
| // The option prefixed name. | |
| writeStrTableOffset(OS, Table, getOptionPrefixedName(R), | |
| /*EmitComment=*/true); | |
| // The option identifier name. | |
| OS << ", " << getOptionName(R); | |
| // The option kind. | |
| OS << ", " << R.getValueAsDef("Kind")->getValueAsString("Name"); | |
| // The containing option group (if any). | |
| OS << ", "; | |
| if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) | |
| OS << getOptionName(*DI->getDef()); | |
| else | |
| OS << "INVALID"; | |
| // The option alias (if any). | |
| OS << ", "; | |
| if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Alias"))) | |
| OS << getOptionName(*DI->getDef()); | |
| else | |
| OS << "INVALID"; | |
| // The option alias arguments (if any). | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, getAliasArgsBlob(R)); | |
| // "Flags" for the option, such as HelpHidden and Render* | |
| OS << ", " << GetMask(R, "Flags"); | |
| // Option visibility, for sharing options between drivers. | |
| OS << ", " << GetMask(R, "Visibility"); | |
| // The option parameter field. | |
| OS << ", " << R.getValueAsInt("NumArgs"); | |
| // The option help text. | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, getHelpText(R)); | |
| // The option help text variants. | |
| OS << ", " << HelpTextVariantsOffset.lookup(&R); | |
| // The option meta-variable name. | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, getOptionalString(R, "MetaVarName")); | |
| // The option Values. Used for shell autocompletion. | |
| OS << ", "; | |
| writeStrTableOffset(OS, Table, getOptionalString(R, "Values")); | |
| // The option SubCommandIDsOffset. | |
| OS << ", " << GetSubCommandIDsOffset(R); | |
| }; | |
| auto IsMarshallingOption = [](const Record &R) { | |
| return !isa<UnsetInit>(R.getValueInit("KeyPath")) && | |
| !R.getValueAsString("KeyPath").empty(); | |
| }; | |
| std::vector<const Record *> OptsWithMarshalling; | |
| for (const Record &R : llvm::make_pointee_range(Opts)) { | |
| ConditionScope CS(OS, R); | |
| // Start a single option entry. | |
| OS << "OPTION("; | |
| WriteOptRecordFields(OS, R); | |
| OS << ")\n"; | |
| if (IsMarshallingOption(R)) | |
| OptsWithMarshalling.push_back(&R); | |
| } | |
| OS << "#endif // OPTION\n"; | |
| auto CmpMarshallingOpts = [](const Record *const *A, const Record *const *B) { | |
| unsigned AID = (*A)->getID(); | |
| unsigned BID = (*B)->getID(); | |
| if (AID < BID) | |
| return -1; | |
| if (AID > BID) | |
| return 1; | |
| return 0; | |
| }; | |
| // The RecordKeeper stores records (options) in lexicographical order, and we | |
| // have reordered the options again when generating prefix groups. We need to | |
| // restore the original definition order of options with marshalling to honor | |
| // the topology of the dependency graph implied by `DefaultAnyOf`. | |
| array_pod_sort(OptsWithMarshalling.begin(), OptsWithMarshalling.end(), | |
| CmpMarshallingOpts); | |
| std::vector<MarshallingInfo> MarshallingInfos; | |
| MarshallingInfos.reserve(OptsWithMarshalling.size()); | |
| for (const auto *R : OptsWithMarshalling) | |
| MarshallingInfos.push_back(createMarshallingInfo(*R)); | |
| for (const auto &MI : MarshallingInfos) { | |
| OS << "#ifdef " << MI.getMacroName() << "\n"; | |
| OS << MI.getMacroName() << "("; | |
| WriteOptRecordFields(OS, MI.R); | |
| OS << ", "; | |
| MI.emit(OS); | |
| OS << ")\n"; | |
| OS << "#endif // " << MI.getMacroName() << "\n"; | |
| } | |
| OS << "\n"; | |
| OS << "#ifdef SIMPLE_ENUM_VALUE_TABLE"; | |
| OS << "\n"; | |
| OS << MarshallingInfo::ValueTablePreamble; | |
| std::vector<StringRef> ValueTableNames; | |
| for (const auto &MI : MarshallingInfos) | |
| if (auto MaybeValueTableName = MI.emitValueTable(OS)) | |
| ValueTableNames.push_back(*MaybeValueTableName); | |
| OS << MarshallingInfo::ValueTablesDecl << "{"; | |
| for (auto ValueTableName : ValueTableNames) | |
| OS << "{" << ValueTableName << ", std::size(" << ValueTableName << ")},\n"; | |
| OS << "};\n"; | |
| OS << "static const unsigned SimpleEnumValueTablesSize = " | |
| "std::size(SimpleEnumValueTables);\n"; | |
| OS << "#endif // SIMPLE_ENUM_VALUE_TABLE\n"; | |
| } | |
| static TableGen::Emitter::Opt X("gen-opt-parser-defs", emitOptionParser, | |
| "Generate option definitions"); |