| //===-- 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/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<")) { |
| const size_t EndTemplate = Current.find('>', 0); |
| 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"); |
| if (Number == 0) |
| PrintFatalError(Loc, "No need for a qualifier for address space `0`"); |
| return Number; |
| }; |
| |
| if (T.consume_back("*")) { |
| // Pointers 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("const")) { |
| ParseType(T); |
| Type += "C"; |
| } else if (T.consume_back("volatile")) { |
| ParseType(T); |
| Type += "D"; |
| } else if (T.consume_back("restrict")) { |
| 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("__int128_t", "LLLi") |
| .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("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("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++"; |
| } |