| //===-- ClangBuiltinsEmitter.cpp - Generate Clang builtins tables ---------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This tablegen backend emits Clang's builtins tables. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "TableGenBackends.h" | |
| #include "llvm/ADT/MapVector.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/ADT/StringSwitch.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/StringToOffsetTable.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <sstream> | |
| using namespace llvm; | |
| namespace { | |
| enum class BuiltinType { | |
| Builtin, | |
| AtomicBuiltin, | |
| LibBuiltin, | |
| LangBuiltin, | |
| TargetBuiltin, | |
| TargetLibBuiltin, | |
| }; | |
| class HeaderNameParser { | |
| public: | |
| HeaderNameParser(const Record *Builtin) { | |
| for (char c : Builtin->getValueAsString("Header")) { | |
| if (std::islower(c)) | |
| HeaderName += static_cast<char>(std::toupper(c)); | |
| else if (c == '.' || c == '_' || c == '/' || c == '-') | |
| HeaderName += '_'; | |
| else | |
| PrintFatalError(Builtin->getLoc(), "Unexpected header name"); | |
| } | |
| } | |
| void Print(raw_ostream &OS) const { OS << HeaderName; } | |
| private: | |
| std::string HeaderName; | |
| }; | |
| struct Builtin { | |
| BuiltinType BT; | |
| std::string Name; | |
| std::string Type; | |
| std::string Attributes; | |
| const Record *BuiltinRecord; | |
| void EmitEnumerator(llvm::raw_ostream &OS) const { | |
| OS << " BI"; | |
| // If there is a required name prefix, include its spelling in the | |
| // enumerator. | |
| if (auto *PrefixRecord = | |
| BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix")) | |
| OS << PrefixRecord->getValueAsString("Spelling"); | |
| OS << Name << ",\n"; | |
| } | |
| void EmitInfo(llvm::raw_ostream &OS, const StringToOffsetTable &Table) const { | |
| OS << " Builtin::Info{Builtin::Info::StrOffsets{" | |
| << Table.GetStringOffset(Name) << " /* " << Name << " */, " | |
| << Table.GetStringOffset(Type) << " /* " << Type << " */, " | |
| << Table.GetStringOffset(Attributes) << " /* " << Attributes << " */, "; | |
| if (BT == BuiltinType::TargetBuiltin) { | |
| const auto &Features = BuiltinRecord->getValueAsString("Features"); | |
| OS << Table.GetStringOffset(Features) << " /* " << Features << " */"; | |
| } else { | |
| OS << "0"; | |
| } | |
| OS << "}, "; | |
| if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::TargetLibBuiltin) { | |
| OS << "HeaderDesc::"; | |
| HeaderNameParser{BuiltinRecord}.Print(OS); | |
| } else { | |
| OS << "HeaderDesc::NO_HEADER"; | |
| } | |
| OS << ", "; | |
| if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::LangBuiltin || | |
| BT == BuiltinType::TargetLibBuiltin) { | |
| OS << BuiltinRecord->getValueAsString("Languages"); | |
| } else { | |
| OS << "ALL_LANGUAGES"; | |
| } | |
| OS << "},\n"; | |
| } | |
| void EmitXMacro(llvm::raw_ostream &OS) const { | |
| if (BuiltinRecord->getValueAsBit("RequiresUndef")) | |
| OS << "#undef " << Name << '\n'; | |
| switch (BT) { | |
| case BuiltinType::LibBuiltin: | |
| OS << "LIBBUILTIN"; | |
| break; | |
| case BuiltinType::LangBuiltin: | |
| OS << "LANGBUILTIN"; | |
| break; | |
| case BuiltinType::Builtin: | |
| OS << "BUILTIN"; | |
| break; | |
| case BuiltinType::AtomicBuiltin: | |
| OS << "ATOMIC_BUILTIN"; | |
| break; | |
| case BuiltinType::TargetBuiltin: | |
| OS << "TARGET_BUILTIN"; | |
| break; | |
| case BuiltinType::TargetLibBuiltin: | |
| OS << "TARGET_HEADER_BUILTIN"; | |
| break; | |
| } | |
| OS << "(" << Name << ", \"" << Type << "\", \"" << Attributes << "\""; | |
| switch (BT) { | |
| case BuiltinType::LibBuiltin: { | |
| OS << ", "; | |
| HeaderNameParser{BuiltinRecord}.Print(OS); | |
| [[fallthrough]]; | |
| } | |
| case BuiltinType::LangBuiltin: { | |
| OS << ", " << BuiltinRecord->getValueAsString("Languages"); | |
| break; | |
| } | |
| case BuiltinType::TargetLibBuiltin: { | |
| OS << ", "; | |
| HeaderNameParser{BuiltinRecord}.Print(OS); | |
| OS << ", " << BuiltinRecord->getValueAsString("Languages"); | |
| [[fallthrough]]; | |
| } | |
| case BuiltinType::TargetBuiltin: { | |
| OS << ", \"" << BuiltinRecord->getValueAsString("Features") << "\""; | |
| break; | |
| } | |
| case BuiltinType::AtomicBuiltin: | |
| case BuiltinType::Builtin: | |
| break; | |
| } | |
| OS << ")\n"; | |
| } | |
| }; | |
| class PrototypeParser { | |
| public: | |
| PrototypeParser(StringRef Substitution, const Record *Builtin) | |
| : Loc(Builtin->getFieldLoc("Prototype")), Substitution(Substitution), | |
| EnableOpenCLLong(Builtin->getValueAsBit("EnableOpenCLLong")) { | |
| ParsePrototype(Builtin->getValueAsString("Prototype")); | |
| } | |
| std::string takeTypeString() && { return std::move(Type); } | |
| private: | |
| void ParsePrototype(StringRef Prototype) { | |
| Prototype = Prototype.trim(); | |
| // Some builtins don't have an expressible prototype, simply emit an empty | |
| // string for them. | |
| if (Prototype.empty()) { | |
| Type = ""; | |
| return; | |
| } | |
| ParseTypes(Prototype); | |
| } | |
| void ParseTypes(StringRef &Prototype) { | |
| auto ReturnType = Prototype.take_until([](char c) { return c == '('; }); | |
| ParseType(ReturnType); | |
| Prototype = Prototype.drop_front(ReturnType.size() + 1); | |
| if (!Prototype.ends_with(")")) | |
| PrintFatalError(Loc, "Expected closing brace at end of prototype"); | |
| Prototype = Prototype.drop_back(); | |
| // Look through the input parameters. | |
| const size_t end = Prototype.size(); | |
| for (size_t I = 0; I != end;) { | |
| const StringRef Current = Prototype.substr(I, end); | |
| // Skip any leading space or commas | |
| if (Current.starts_with(" ") || Current.starts_with(",")) { | |
| ++I; | |
| continue; | |
| } | |
| // Check if we are in _ExtVector. We do this first because | |
| // extended vectors are written in template form with the syntax | |
| // _ExtVector< ..., ...>, so we need to make sure we are not | |
| // detecting the comma of the template class as a separator for | |
| // the parameters of the prototype. Note: the assumption is that | |
| // we cannot have nested _ExtVector. | |
| if (Current.starts_with("_ExtVector<") || | |
| Current.starts_with("_Vector<")) { | |
| size_t Pos = Current.find('<'); | |
| int Depth = 1; | |
| // There may be a nested address_space<...> modifier on the type. | |
| while (Depth > 0 && ++Pos < Current.size()) { | |
| if (Current[Pos] == '<') | |
| ++Depth; | |
| else if (Current[Pos] == '>') | |
| --Depth; | |
| } | |
| const size_t EndTemplate = Pos; | |
| ParseType(Current.substr(0, EndTemplate + 1)); | |
| // Move the prototype beyond _ExtVector<...> | |
| I += EndTemplate + 1; | |
| continue; | |
| } | |
| // We know that we are past _ExtVector, therefore the first seen | |
| // comma is the boundary of a parameter in the prototype. | |
| if (size_t CommaPos = Current.find(',', 0)) { | |
| if (CommaPos != StringRef::npos) { | |
| StringRef T = Current.substr(0, CommaPos); | |
| ParseType(T); | |
| // Move the prototype beyond the comma. | |
| I += CommaPos + 1; | |
| continue; | |
| } | |
| } | |
| // No more commas, parse final parameter. | |
| ParseType(Current); | |
| I = end; | |
| } | |
| } | |
| void ParseType(StringRef T) { | |
| T = T.trim(); | |
| auto ConsumeAddrSpace = [&]() -> std::optional<unsigned> { | |
| T = T.trim(); | |
| if (!T.consume_back(">")) | |
| return std::nullopt; | |
| auto Open = T.find_last_of('<'); | |
| if (Open == StringRef::npos) | |
| PrintFatalError(Loc, "Mismatched angle-brackets in type"); | |
| StringRef ArgStr = T.substr(Open + 1); | |
| T = T.slice(0, Open); | |
| if (!T.consume_back("address_space")) | |
| PrintFatalError(Loc, | |
| "Only `address_space<N>` supported as a parameterized " | |
| "pointer or reference type qualifier"); | |
| unsigned Number = 0; | |
| if (ArgStr.getAsInteger(10, Number)) | |
| PrintFatalError( | |
| Loc, "Expected an integer argument to the address_space qualifier"); | |
| return Number; | |
| }; | |
| if (T.consume_back("*")) { | |
| // Pointers may have an address space qualifier immediately before them. | |
| std::optional<unsigned> AS = ConsumeAddrSpace(); | |
| // Pointers can apply to already parsed types, like vectors. | |
| if (!T.empty()) | |
| ParseType(T); | |
| Type += "*"; | |
| if (AS) | |
| Type += std::to_string(*AS); | |
| } else if (T.consume_back("const")) { | |
| if (!T.empty()) | |
| ParseType(T); | |
| Type += "C"; | |
| } else if (T.consume_back("volatile")) { | |
| if (!T.empty()) | |
| ParseType(T); | |
| Type += "D"; | |
| } else if (T.consume_back("restrict")) { | |
| if (!T.empty()) | |
| ParseType(T); | |
| Type += "R"; | |
| } else if (T.consume_back("&")) { | |
| // References may have an address space qualifier immediately before them. | |
| std::optional<unsigned> AS = ConsumeAddrSpace(); | |
| ParseType(T); | |
| Type += "&"; | |
| if (AS) | |
| Type += std::to_string(*AS); | |
| } else if (T.consume_back(")")) { | |
| ParseType(T); | |
| Type += "&"; | |
| } else if (EnableOpenCLLong && T.consume_front("long long")) { | |
| Type += "O"; | |
| ParseType(T); | |
| } else if (T.consume_front("long")) { | |
| Type += "L"; | |
| ParseType(T); | |
| } else if (T.consume_front("signed")) { | |
| Type += "S"; | |
| ParseType(T); | |
| } else if (T.consume_front("unsigned")) { | |
| Type += "U"; | |
| ParseType(T); | |
| } else if (T.consume_front("_Complex")) { | |
| Type += "X"; | |
| ParseType(T); | |
| } else if (T.consume_front("_Constant")) { | |
| Type += "I"; | |
| ParseType(T); | |
| } else if (T.consume_front("T")) { | |
| if (Substitution.empty()) | |
| PrintFatalError(Loc, "Not a template"); | |
| ParseType(Substitution); | |
| } else if (auto IsExt = T.consume_front("_ExtVector"); | |
| IsExt || T.consume_front("_Vector")) { | |
| // Clang extended vector types are mangled as follows: | |
| // | |
| // '_ExtVector<' <lanes> ',' <scalar type> '>' | |
| // Before parsing T(=<scalar type>), make sure the syntax of | |
| // `_ExtVector<N, T>` is correct... | |
| if (!T.consume_front("<")) | |
| PrintFatalError(Loc, "Expected '<' after '_ExtVector'"); | |
| unsigned long long Lanes; | |
| if (consumeUnsignedInteger(T, 10, Lanes)) | |
| PrintFatalError(Loc, "Expected number of lanes after '_ExtVector<'"); | |
| Type += (IsExt ? "E" : "V") + std::to_string(Lanes); | |
| if (!T.consume_front(",")) | |
| PrintFatalError(Loc, | |
| "Expected ',' after number of lanes in '_ExtVector<'"); | |
| if (!T.consume_back(">")) | |
| PrintFatalError( | |
| Loc, "Expected '>' after scalar type in '_ExtVector<N, type>'"); | |
| // ...all good, we can check if we have a valid `<scalar type>`. | |
| ParseType(T); | |
| } else { | |
| auto ReturnTypeVal = StringSwitch<std::string>(T) | |
| .Case("__builtin_va_list_ref", "A") | |
| .Case("__builtin_va_list", "a") | |
| .Case("__float128", "LLd") | |
| .Case("__fp16", "h") | |
| .Case("__hlsl_resource_t", "Qr") | |
| .Case("__amdgpu_buffer_rsrc_t", "Qb") | |
| .Case("__amdgpu_feature_predicate_t", "Qc") | |
| .Case("__amdgpu_texture_t", "Qt") | |
| .Case("__int128_t", "LLLi") | |
| .Case("__uint128_t", "ULLLi") | |
| .Case("_Float16", "x") | |
| .Case("__bf16", "y") | |
| .Case("bool", "b") | |
| .Case("char", "c") | |
| .Case("constant_CFString", "F") | |
| .Case("double", "d") | |
| .Case("FILE", "P") | |
| .Case("float", "f") | |
| .Case("id", "G") | |
| .Case("int", "i") | |
| .Case("int8_t", "Bi") | |
| .Case("int16_t", "Ti") | |
| .Case("int32_t", "Zi") | |
| .Case("int64_t", "Wi") | |
| .Case("jmp_buf", "J") | |
| .Case("msint32_t", "Ni") | |
| .Case("msuint32_t", "UNi") | |
| .Case("objc_super", "M") | |
| .Case("pid_t", "p") | |
| .Case("ptrdiff_t", "Y") | |
| .Case("SEL", "H") | |
| .Case("short", "s") | |
| .Case("sigjmp_buf", "SJ") | |
| .Case("size_t", "z") | |
| .Case("ucontext_t", "K") | |
| .Case("uint8_t", "UBi") | |
| .Case("uint16_t", "UTi") | |
| .Case("uint32_t", "UZi") | |
| .Case("uint64_t", "UWi") | |
| .Case("void", "v") | |
| .Case("wchar_t", "w") | |
| .Case("...", ".") | |
| .Default("error"); | |
| if (ReturnTypeVal == "error") | |
| PrintFatalError(Loc, "Unknown Type: " + T); | |
| Type += ReturnTypeVal; | |
| } | |
| } | |
| SMLoc Loc; | |
| StringRef Substitution; | |
| bool EnableOpenCLLong; | |
| std::string Type; | |
| }; | |
| std::string renderAttributes(const Record *Builtin, BuiltinType BT) { | |
| std::string Attributes; | |
| raw_string_ostream OS(Attributes); | |
| if (Builtin->isSubClassOf("LibBuiltin")) { | |
| if (BT == BuiltinType::LibBuiltin) { | |
| OS << 'f'; | |
| } else { | |
| OS << 'F'; | |
| if (Builtin->getValueAsBit("OnlyBuiltinPrefixedAliasIsConstexpr")) | |
| OS << 'E'; | |
| } | |
| } | |
| if (auto NS = Builtin->getValueAsOptionalString("Namespace")) { | |
| if (NS != "std") | |
| PrintFatalError(Builtin->getFieldLoc("Namespace"), "Unknown namespace: "); | |
| OS << "z"; | |
| } | |
| for (const auto *Attr : Builtin->getValueAsListOfDefs("Attributes")) { | |
| OS << Attr->getValueAsString("Mangling"); | |
| if (Attr->isSubClassOf("IndexedAttribute")) { | |
| OS << ':' << Attr->getValueAsInt("Index") << ':'; | |
| } else if (Attr->isSubClassOf("MultiIndexAttribute")) { | |
| OS << '<'; | |
| llvm::ListSeparator Sep(","); | |
| for (int64_t Index : Attr->getValueAsListOfInts("Indices")) | |
| OS << Sep << Index; | |
| OS << '>'; | |
| } | |
| } | |
| return Attributes; | |
| } | |
| Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord, | |
| Twine Spelling, BuiltinType BT) { | |
| Builtin B; | |
| B.BT = BT; | |
| B.Name = Spelling.str(); | |
| B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString(); | |
| B.Attributes = renderAttributes(BuiltinRecord, BT); | |
| B.BuiltinRecord = BuiltinRecord; | |
| return B; | |
| } | |
| struct TemplateInsts { | |
| std::vector<std::string> Substitution; | |
| std::vector<std::string> Affix; | |
| bool IsPrefix; | |
| }; | |
| TemplateInsts getTemplateInsts(const Record *R) { | |
| TemplateInsts temp; | |
| auto Substitutions = R->getValueAsListOfStrings("Substitutions"); | |
| auto Affixes = R->getValueAsListOfStrings("Affixes"); | |
| temp.IsPrefix = R->getValueAsBit("AsPrefix"); | |
| if (Substitutions.size() != Affixes.size()) | |
| PrintFatalError(R->getLoc(), "Substitutions and affixes " | |
| "don't have the same lengths"); | |
| for (auto [Affix, Substitution] : zip(Affixes, Substitutions)) { | |
| temp.Substitution.emplace_back(Substitution); | |
| temp.Affix.emplace_back(Affix); | |
| } | |
| return temp; | |
| } | |
| void collectBuiltins(const Record *BuiltinRecord, | |
| SmallVectorImpl<Builtin> &Builtins) { | |
| TemplateInsts Templates = {}; | |
| if (BuiltinRecord->isSubClassOf("Template")) { | |
| Templates = getTemplateInsts(BuiltinRecord); | |
| } else { | |
| Templates.Affix.emplace_back(); | |
| Templates.Substitution.emplace_back(); | |
| } | |
| for (auto [Substitution, Affix] : | |
| zip(Templates.Substitution, Templates.Affix)) { | |
| for (StringRef Spelling : | |
| BuiltinRecord->getValueAsListOfStrings("Spellings")) { | |
| auto FullSpelling = | |
| (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str(); | |
| BuiltinType BT = BuiltinType::Builtin; | |
| if (BuiltinRecord->isSubClassOf("AtomicBuiltin")) { | |
| BT = BuiltinType::AtomicBuiltin; | |
| } else if (BuiltinRecord->isSubClassOf("LangBuiltin")) { | |
| BT = BuiltinType::LangBuiltin; | |
| } else if (BuiltinRecord->isSubClassOf("TargetLibBuiltin")) { | |
| BT = BuiltinType::TargetLibBuiltin; | |
| } else if (BuiltinRecord->isSubClassOf("TargetBuiltin")) { | |
| BT = BuiltinType::TargetBuiltin; | |
| } else if (BuiltinRecord->isSubClassOf("LibBuiltin")) { | |
| BT = BuiltinType::LibBuiltin; | |
| if (BuiltinRecord->getValueAsBit("AddBuiltinPrefixedAlias")) | |
| Builtins.push_back(buildBuiltin( | |
| Substitution, BuiltinRecord, | |
| std::string("__builtin_") + FullSpelling, BuiltinType::Builtin)); | |
| } | |
| Builtins.push_back( | |
| buildBuiltin(Substitution, BuiltinRecord, FullSpelling, BT)); | |
| } | |
| } | |
| } | |
| } // namespace | |
| void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("List of builtins that Clang recognizes", OS); | |
| SmallVector<Builtin> Builtins; | |
| // AtomicBuiltins are order dependent. Emit them first to make manual checking | |
| // easier and so we can build a special atomic builtin X-macro. | |
| for (const auto *BuiltinRecord : | |
| Records.getAllDerivedDefinitions("AtomicBuiltin")) | |
| collectBuiltins(BuiltinRecord, Builtins); | |
| unsigned NumAtomicBuiltins = Builtins.size(); | |
| for (const auto *BuiltinRecord : | |
| Records.getAllDerivedDefinitions("Builtin")) { | |
| if (BuiltinRecord->isSubClassOf("AtomicBuiltin")) | |
| continue; | |
| // Prefixed builtins are also special and we emit them last so they can have | |
| // their own representation that skips the prefix. | |
| if (BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix")) | |
| continue; | |
| collectBuiltins(BuiltinRecord, Builtins); | |
| } | |
| // Now collect (and count) the prefixed builtins. | |
| unsigned NumPrefixedBuiltins = Builtins.size(); | |
| const Record *FirstPrefix = nullptr; | |
| for (const auto *BuiltinRecord : | |
| Records.getAllDerivedDefinitions("Builtin")) { | |
| auto *Prefix = BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix"); | |
| if (!Prefix) | |
| continue; | |
| if (!FirstPrefix) | |
| FirstPrefix = Prefix; | |
| assert(Prefix == FirstPrefix && | |
| "Multiple distinct prefixes which is not currently supported!"); | |
| assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin") && | |
| "Cannot require a name prefix for an atomic builtin."); | |
| collectBuiltins(BuiltinRecord, Builtins); | |
| } | |
| NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins; | |
| auto AtomicBuiltins = ArrayRef(Builtins).slice(0, NumAtomicBuiltins); | |
| auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(NumPrefixedBuiltins); | |
| auto PrefixedBuiltins = ArrayRef(Builtins).take_back(NumPrefixedBuiltins); | |
| // Collect strings into a table. | |
| StringToOffsetTable Table; | |
| Table.GetOrAddStringOffset(""); | |
| for (const auto &B : Builtins) { | |
| Table.GetOrAddStringOffset(B.Name); | |
| Table.GetOrAddStringOffset(B.Type); | |
| Table.GetOrAddStringOffset(B.Attributes); | |
| if (B.BT == BuiltinType::TargetBuiltin) | |
| Table.GetOrAddStringOffset(B.BuiltinRecord->getValueAsString("Features")); | |
| } | |
| // Emit enumerators. | |
| OS << R"c++( | |
| #ifdef GET_BUILTIN_ENUMERATORS | |
| )c++"; | |
| for (const auto &B : Builtins) | |
| B.EmitEnumerator(OS); | |
| OS << R"c++( | |
| #endif // GET_BUILTIN_ENUMERATORS | |
| )c++"; | |
| // Emit a string table that can be referenced for these builtins. | |
| OS << R"c++( | |
| #ifdef GET_BUILTIN_STR_TABLE | |
| )c++"; | |
| Table.EmitStringTableDef(OS, "BuiltinStrings"); | |
| OS << R"c++( | |
| #endif // GET_BUILTIN_STR_TABLE | |
| )c++"; | |
| // Emit a direct set of `Builtin::Info` initializers, first for the unprefixed | |
| // builtins and then for the prefixed builtins. | |
| OS << R"c++( | |
| #ifdef GET_BUILTIN_INFOS | |
| )c++"; | |
| for (const auto &B : UnprefixedBuiltins) | |
| B.EmitInfo(OS, Table); | |
| OS << R"c++( | |
| #endif // GET_BUILTIN_INFOS | |
| )c++"; | |
| OS << R"c++( | |
| #ifdef GET_BUILTIN_PREFIXED_INFOS | |
| )c++"; | |
| for (const auto &B : PrefixedBuiltins) | |
| B.EmitInfo(OS, Table); | |
| OS << R"c++( | |
| #endif // GET_BUILTIN_PREFIXED_INFOS | |
| )c++"; | |
| // Emit X-macros for the atomic builtins to support various custome patterns | |
| // used exclusively with those builtins. | |
| // | |
| // FIXME: We should eventually move this to a separate file so that users | |
| // don't need to include the full set of builtins. | |
| OS << R"c++( | |
| #ifdef ATOMIC_BUILTIN | |
| )c++"; | |
| for (const auto &Builtin : AtomicBuiltins) { | |
| Builtin.EmitXMacro(OS); | |
| } | |
| OS << R"c++( | |
| #endif // ATOMIC_BUILTIN | |
| #undef ATOMIC_BUILTIN | |
| )c++"; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Builtin documentation emitter | |
| //===----------------------------------------------------------------------===// | |
| /// Holds the data needed to emit documentation for a single builtin. | |
| namespace { | |
| struct BuiltinDocData { | |
| const Record *Documentation = nullptr; | |
| const Record *BuiltinRecord = nullptr; | |
| std::string Heading; | |
| BuiltinDocData(const Record *D, const Record *B) | |
| : Documentation(D), BuiltinRecord(B) { | |
| // Use the Heading field if set, otherwise use the builtin's first | |
| // spelling. | |
| StringRef HeadingStr = D->getValueAsString("Heading"); | |
| if (HeadingStr.empty()) { | |
| std::vector<StringRef> Spellings = | |
| B->getValueAsListOfStrings("Spellings"); | |
| if (!Spellings.empty()) | |
| Heading = Spellings[0].str(); | |
| else | |
| Heading = B->getName().str(); | |
| } else { | |
| Heading = HeadingStr.str(); | |
| } | |
| } | |
| }; | |
| } // namespace | |
| static void writeCategoryHeader(const Record *Category, raw_ostream &OS) { | |
| StringRef CategoryName = Category->getValueAsString("Name"); | |
| OS << "\n" << CategoryName << "\n"; | |
| for (size_t I = 0, E = CategoryName.size(); I < E; ++I) | |
| OS << "="; | |
| OS << "\n\n"; | |
| StringRef CategoryContent = Category->getValueAsString("Content"); | |
| if (!CategoryContent.trim().empty()) | |
| OS << CategoryContent.trim() << "\n\n"; | |
| } | |
| /// Split a parameter list string into individual parameter type strings, | |
| /// respecting nested angle brackets (e.g. address_space<4>, _ExtVector<4, | |
| /// float>). | |
| static SmallVector<StringRef> splitParams(StringRef Params) { | |
| SmallVector<StringRef> Result; | |
| if (Params.empty()) | |
| return Result; | |
| int Depth = 0; | |
| size_t Start = 0; | |
| for (size_t I = 0, E = Params.size(); I < E; ++I) { | |
| if (Params[I] == '<') { | |
| ++Depth; | |
| } else if (Params[I] == '>') { | |
| --Depth; | |
| } else if (Params[I] == ',' && Depth == 0) { | |
| Result.push_back(Params.substr(Start, I - Start).trim()); | |
| Start = I + 1; | |
| } | |
| } | |
| // Add the last parameter. | |
| StringRef Last = Params.substr(Start).trim(); | |
| if (!Last.empty()) | |
| Result.push_back(Last); | |
| return Result; | |
| } | |
| static void writeBuiltinDocumentation(const BuiltinDocData &Doc, | |
| raw_ostream &OS) { | |
| // Write heading with '-' underline (subsection). | |
| std::string HeadingText = "``" + Doc.Heading + "``"; | |
| OS << HeadingText << "\n"; | |
| for (size_t I = 0, E = HeadingText.size(); I < E; ++I) | |
| OS << "-"; | |
| OS << "\n\n"; | |
| // Write prototype as a code block. | |
| StringRef Prototype = Doc.BuiltinRecord->getValueAsString("Prototype"); | |
| if (!Prototype.empty()) { | |
| std::vector<StringRef> Spellings = | |
| Doc.BuiltinRecord->getValueAsListOfStrings("Spellings"); | |
| StringRef Name = | |
| Spellings.empty() ? Doc.BuiltinRecord->getName() : Spellings[0]; | |
| // Split prototype into return type and params at the first '('. | |
| size_t ParenPos = Prototype.find('('); | |
| if (ParenPos != StringRef::npos) { | |
| StringRef RetType = Prototype.substr(0, ParenPos).rtrim(); | |
| StringRef ParamStr = | |
| Prototype.substr(ParenPos + 1, Prototype.size() - ParenPos - 2); | |
| OS << "**Prototype:**\n\n"; | |
| OS << ".. code-block:: c\n\n"; | |
| OS << " " << RetType << " " << Name << "("; | |
| std::vector<StringRef> ArgNames = | |
| Doc.BuiltinRecord->getValueAsListOfStrings("ArgNames"); | |
| if (!ArgNames.empty()) { | |
| SmallVector<StringRef> ParamTypes = splitParams(ParamStr); | |
| bool IsVariadic = !ParamTypes.empty() && ParamTypes.back() == "..."; | |
| size_t NamedParams = ParamTypes.size() - (IsVariadic ? 1 : 0); | |
| if (ArgNames.size() != NamedParams) | |
| PrintFatalError(Doc.BuiltinRecord->getLoc(), | |
| "number of ArgNames (" + Twine(ArgNames.size()) + | |
| ") does not match number of prototype " | |
| "parameters (" + | |
| Twine(NamedParams) + ")"); | |
| for (size_t I = 0, E = ParamTypes.size(); I < E; ++I) { | |
| if (I > 0) | |
| OS << ", "; | |
| if (I < NamedParams) | |
| OS << ParamTypes[I] << " " << ArgNames[I]; | |
| else | |
| OS << ParamTypes[I]; | |
| } | |
| } else { | |
| OS << ParamStr; | |
| } | |
| OS << ")\n\n"; | |
| } | |
| } | |
| // Write target features if this is a TargetBuiltin with features. | |
| if (Doc.BuiltinRecord->isSubClassOf("TargetBuiltin")) { | |
| StringRef Features = Doc.BuiltinRecord->getValueAsString("Features"); | |
| if (!Features.empty()) | |
| OS << "**Target Features:** " << Features << "\n\n"; | |
| } | |
| // Write documentation content. | |
| StringRef Content = Doc.Documentation->getValueAsString("Content"); | |
| OS << Content.trim() << "\n\n\n"; | |
| } | |
| void clang::EmitClangBuiltinDocs(const RecordKeeper &Records, raw_ostream &OS) { | |
| // Get the documentation introduction paragraph. | |
| const Record *Doc = Records.getDef("GlobalDocumentation"); | |
| if (!Doc) { | |
| PrintFatalError("The GlobalDocumentation top-level definition is missing, " | |
| "no documentation will be generated."); | |
| } | |
| OS << Doc->getValueAsString("Intro") << "\n"; | |
| // Gather documentation from each builtin, grouped by category. | |
| llvm::MapVector<const Record *, std::vector<BuiltinDocData>> SplitDocs; | |
| for (const Record *B : Records.getAllDerivedDefinitions("Builtin")) { | |
| for (const Record *D : B->getValueAsListOfDefs("Documentation")) { | |
| const Record *Category = D->getValueAsDef("Category"); | |
| StringRef Cat = Category->getValueAsString("Name"); | |
| // Skip builtins that are explicitly internal-only. | |
| if (Cat == "InternalOnly") | |
| continue; | |
| SplitDocs[Category].emplace_back(D, B); | |
| } | |
| } | |
| // Sort categories alphabetically by name for deterministic output, but | |
| // push the "Undocumented" category to the end so that documented sections | |
| // always appear first. | |
| llvm::sort(SplitDocs, [](const auto &A, const auto &B) { | |
| StringRef NameA = A.first->getValueAsString("Name"); | |
| StringRef NameB = B.first->getValueAsString("Name"); | |
| bool UndocA = (NameA == "Undocumented"); | |
| bool UndocB = (NameB == "Undocumented"); | |
| if (UndocA != UndocB) | |
| return UndocB; | |
| return NameA < NameB; | |
| }); | |
| // Write out each category and its builtins. | |
| for (auto &[Category, Docs] : SplitDocs) { | |
| writeCategoryHeader(Category, OS); | |
| // Sort entries alphabetically by heading. | |
| llvm::sort(Docs, [](const BuiltinDocData &A, const BuiltinDocData &B) { | |
| return A.Heading < B.Heading; | |
| }); | |
| for (const BuiltinDocData &D : Docs) | |
| writeBuiltinDocumentation(D, OS); | |
| } | |
| } |