595 lines · cpp
1//===-- ClangBuiltinsEmitter.cpp - Generate Clang builtins tables ---------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This tablegen backend emits Clang's builtins tables.10//11//===----------------------------------------------------------------------===//12 13#include "TableGenBackends.h"14#include "llvm/ADT/StringExtras.h"15#include "llvm/ADT/StringSwitch.h"16#include "llvm/TableGen/Error.h"17#include "llvm/TableGen/Record.h"18#include "llvm/TableGen/StringToOffsetTable.h"19#include "llvm/TableGen/TableGenBackend.h"20#include <sstream>21 22using namespace llvm;23 24namespace {25enum class BuiltinType {26 Builtin,27 AtomicBuiltin,28 LibBuiltin,29 LangBuiltin,30 TargetBuiltin,31 TargetLibBuiltin,32};33 34class HeaderNameParser {35public:36 HeaderNameParser(const Record *Builtin) {37 for (char c : Builtin->getValueAsString("Header")) {38 if (std::islower(c))39 HeaderName += static_cast<char>(std::toupper(c));40 else if (c == '.' || c == '_' || c == '/' || c == '-')41 HeaderName += '_';42 else43 PrintFatalError(Builtin->getLoc(), "Unexpected header name");44 }45 }46 47 void Print(raw_ostream &OS) const { OS << HeaderName; }48 49private:50 std::string HeaderName;51};52 53struct Builtin {54 BuiltinType BT;55 std::string Name;56 std::string Type;57 std::string Attributes;58 59 const Record *BuiltinRecord;60 61 void EmitEnumerator(llvm::raw_ostream &OS) const {62 OS << " BI";63 // If there is a required name prefix, include its spelling in the64 // enumerator.65 if (auto *PrefixRecord =66 BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix"))67 OS << PrefixRecord->getValueAsString("Spelling");68 OS << Name << ",\n";69 }70 71 void EmitInfo(llvm::raw_ostream &OS, const StringToOffsetTable &Table) const {72 OS << " Builtin::Info{Builtin::Info::StrOffsets{"73 << Table.GetStringOffset(Name) << " /* " << Name << " */, "74 << Table.GetStringOffset(Type) << " /* " << Type << " */, "75 << Table.GetStringOffset(Attributes) << " /* " << Attributes << " */, ";76 if (BT == BuiltinType::TargetBuiltin) {77 const auto &Features = BuiltinRecord->getValueAsString("Features");78 OS << Table.GetStringOffset(Features) << " /* " << Features << " */";79 } else {80 OS << "0";81 }82 OS << "}, ";83 if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::TargetLibBuiltin) {84 OS << "HeaderDesc::";85 HeaderNameParser{BuiltinRecord}.Print(OS);86 } else {87 OS << "HeaderDesc::NO_HEADER";88 }89 OS << ", ";90 if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::LangBuiltin ||91 BT == BuiltinType::TargetLibBuiltin) {92 OS << BuiltinRecord->getValueAsString("Languages");93 } else {94 OS << "ALL_LANGUAGES";95 }96 OS << "},\n";97 }98 99 void EmitXMacro(llvm::raw_ostream &OS) const {100 if (BuiltinRecord->getValueAsBit("RequiresUndef"))101 OS << "#undef " << Name << '\n';102 switch (BT) {103 case BuiltinType::LibBuiltin:104 OS << "LIBBUILTIN";105 break;106 case BuiltinType::LangBuiltin:107 OS << "LANGBUILTIN";108 break;109 case BuiltinType::Builtin:110 OS << "BUILTIN";111 break;112 case BuiltinType::AtomicBuiltin:113 OS << "ATOMIC_BUILTIN";114 break;115 case BuiltinType::TargetBuiltin:116 OS << "TARGET_BUILTIN";117 break;118 case BuiltinType::TargetLibBuiltin:119 OS << "TARGET_HEADER_BUILTIN";120 break;121 }122 123 OS << "(" << Name << ", \"" << Type << "\", \"" << Attributes << "\"";124 125 switch (BT) {126 case BuiltinType::LibBuiltin: {127 OS << ", ";128 HeaderNameParser{BuiltinRecord}.Print(OS);129 [[fallthrough]];130 }131 case BuiltinType::LangBuiltin: {132 OS << ", " << BuiltinRecord->getValueAsString("Languages");133 break;134 }135 case BuiltinType::TargetLibBuiltin: {136 OS << ", ";137 HeaderNameParser{BuiltinRecord}.Print(OS);138 OS << ", " << BuiltinRecord->getValueAsString("Languages");139 [[fallthrough]];140 }141 case BuiltinType::TargetBuiltin: {142 OS << ", \"" << BuiltinRecord->getValueAsString("Features") << "\"";143 break;144 }145 case BuiltinType::AtomicBuiltin:146 case BuiltinType::Builtin:147 break;148 }149 OS << ")\n";150 }151};152 153class PrototypeParser {154public:155 PrototypeParser(StringRef Substitution, const Record *Builtin)156 : Loc(Builtin->getFieldLoc("Prototype")), Substitution(Substitution),157 EnableOpenCLLong(Builtin->getValueAsBit("EnableOpenCLLong")) {158 ParsePrototype(Builtin->getValueAsString("Prototype"));159 }160 161 std::string takeTypeString() && { return std::move(Type); }162 163private:164 void ParsePrototype(StringRef Prototype) {165 Prototype = Prototype.trim();166 167 // Some builtins don't have an expressible prototype, simply emit an empty168 // string for them.169 if (Prototype.empty()) {170 Type = "";171 return;172 }173 174 ParseTypes(Prototype);175 }176 177 void ParseTypes(StringRef &Prototype) {178 auto ReturnType = Prototype.take_until([](char c) { return c == '('; });179 ParseType(ReturnType);180 Prototype = Prototype.drop_front(ReturnType.size() + 1);181 if (!Prototype.ends_with(")"))182 PrintFatalError(Loc, "Expected closing brace at end of prototype");183 Prototype = Prototype.drop_back();184 185 // Look through the input parameters.186 const size_t end = Prototype.size();187 for (size_t I = 0; I != end;) {188 const StringRef Current = Prototype.substr(I, end);189 // Skip any leading space or commas190 if (Current.starts_with(" ") || Current.starts_with(",")) {191 ++I;192 continue;193 }194 195 // Check if we are in _ExtVector. We do this first because196 // extended vectors are written in template form with the syntax197 // _ExtVector< ..., ...>, so we need to make sure we are not198 // detecting the comma of the template class as a separator for199 // the parameters of the prototype. Note: the assumption is that200 // we cannot have nested _ExtVector.201 if (Current.starts_with("_ExtVector<") ||202 Current.starts_with("_Vector<")) {203 const size_t EndTemplate = Current.find('>', 0);204 ParseType(Current.substr(0, EndTemplate + 1));205 // Move the prototype beyond _ExtVector<...>206 I += EndTemplate + 1;207 continue;208 }209 210 // We know that we are past _ExtVector, therefore the first seen211 // comma is the boundary of a parameter in the prototype.212 if (size_t CommaPos = Current.find(',', 0)) {213 if (CommaPos != StringRef::npos) {214 StringRef T = Current.substr(0, CommaPos);215 ParseType(T);216 // Move the prototype beyond the comma.217 I += CommaPos + 1;218 continue;219 }220 }221 222 // No more commas, parse final parameter.223 ParseType(Current);224 I = end;225 }226 }227 228 void ParseType(StringRef T) {229 T = T.trim();230 231 auto ConsumeAddrSpace = [&]() -> std::optional<unsigned> {232 T = T.trim();233 if (!T.consume_back(">"))234 return std::nullopt;235 236 auto Open = T.find_last_of('<');237 if (Open == StringRef::npos)238 PrintFatalError(Loc, "Mismatched angle-brackets in type");239 240 StringRef ArgStr = T.substr(Open + 1);241 T = T.slice(0, Open);242 if (!T.consume_back("address_space"))243 PrintFatalError(Loc,244 "Only `address_space<N>` supported as a parameterized "245 "pointer or reference type qualifier");246 247 unsigned Number = 0;248 if (ArgStr.getAsInteger(10, Number))249 PrintFatalError(250 Loc, "Expected an integer argument to the address_space qualifier");251 if (Number == 0)252 PrintFatalError(Loc, "No need for a qualifier for address space `0`");253 return Number;254 };255 256 if (T.consume_back("*")) {257 // Pointers may have an address space qualifier immediately before them.258 std::optional<unsigned> AS = ConsumeAddrSpace();259 ParseType(T);260 Type += "*";261 if (AS)262 Type += std::to_string(*AS);263 } else if (T.consume_back("const")) {264 ParseType(T);265 Type += "C";266 } else if (T.consume_back("volatile")) {267 ParseType(T);268 Type += "D";269 } else if (T.consume_back("restrict")) {270 ParseType(T);271 Type += "R";272 } else if (T.consume_back("&")) {273 // References may have an address space qualifier immediately before them.274 std::optional<unsigned> AS = ConsumeAddrSpace();275 ParseType(T);276 Type += "&";277 if (AS)278 Type += std::to_string(*AS);279 } else if (T.consume_back(")")) {280 ParseType(T);281 Type += "&";282 } else if (EnableOpenCLLong && T.consume_front("long long")) {283 Type += "O";284 ParseType(T);285 } else if (T.consume_front("long")) {286 Type += "L";287 ParseType(T);288 } else if (T.consume_front("signed")) {289 Type += "S";290 ParseType(T);291 } else if (T.consume_front("unsigned")) {292 Type += "U";293 ParseType(T);294 } else if (T.consume_front("_Complex")) {295 Type += "X";296 ParseType(T);297 } else if (T.consume_front("_Constant")) {298 Type += "I";299 ParseType(T);300 } else if (T.consume_front("T")) {301 if (Substitution.empty())302 PrintFatalError(Loc, "Not a template");303 ParseType(Substitution);304 } else if (auto IsExt = T.consume_front("_ExtVector");305 IsExt || T.consume_front("_Vector")) {306 // Clang extended vector types are mangled as follows:307 //308 // '_ExtVector<' <lanes> ',' <scalar type> '>'309 310 // Before parsing T(=<scalar type>), make sure the syntax of311 // `_ExtVector<N, T>` is correct...312 if (!T.consume_front("<"))313 PrintFatalError(Loc, "Expected '<' after '_ExtVector'");314 unsigned long long Lanes;315 if (consumeUnsignedInteger(T, 10, Lanes))316 PrintFatalError(Loc, "Expected number of lanes after '_ExtVector<'");317 Type += (IsExt ? "E" : "V") + std::to_string(Lanes);318 if (!T.consume_front(","))319 PrintFatalError(Loc,320 "Expected ',' after number of lanes in '_ExtVector<'");321 if (!T.consume_back(">"))322 PrintFatalError(323 Loc, "Expected '>' after scalar type in '_ExtVector<N, type>'");324 325 // ...all good, we can check if we have a valid `<scalar type>`.326 ParseType(T);327 } else {328 auto ReturnTypeVal = StringSwitch<std::string>(T)329 .Case("__builtin_va_list_ref", "A")330 .Case("__builtin_va_list", "a")331 .Case("__float128", "LLd")332 .Case("__fp16", "h")333 .Case("__hlsl_resource_t", "Qr")334 .Case("__int128_t", "LLLi")335 .Case("_Float16", "x")336 .Case("__bf16", "y")337 .Case("bool", "b")338 .Case("char", "c")339 .Case("constant_CFString", "F")340 .Case("double", "d")341 .Case("FILE", "P")342 .Case("float", "f")343 .Case("id", "G")344 .Case("int", "i")345 .Case("int32_t", "Zi")346 .Case("int64_t", "Wi")347 .Case("jmp_buf", "J")348 .Case("msint32_t", "Ni")349 .Case("msuint32_t", "UNi")350 .Case("objc_super", "M")351 .Case("pid_t", "p")352 .Case("ptrdiff_t", "Y")353 .Case("SEL", "H")354 .Case("short", "s")355 .Case("sigjmp_buf", "SJ")356 .Case("size_t", "z")357 .Case("ucontext_t", "K")358 .Case("uint32_t", "UZi")359 .Case("uint64_t", "UWi")360 .Case("void", "v")361 .Case("wchar_t", "w")362 .Case("...", ".")363 .Default("error");364 if (ReturnTypeVal == "error")365 PrintFatalError(Loc, "Unknown Type: " + T);366 Type += ReturnTypeVal;367 }368 }369 370 SMLoc Loc;371 StringRef Substitution;372 bool EnableOpenCLLong;373 std::string Type;374};375 376std::string renderAttributes(const Record *Builtin, BuiltinType BT) {377 std::string Attributes;378 raw_string_ostream OS(Attributes);379 if (Builtin->isSubClassOf("LibBuiltin")) {380 if (BT == BuiltinType::LibBuiltin) {381 OS << 'f';382 } else {383 OS << 'F';384 if (Builtin->getValueAsBit("OnlyBuiltinPrefixedAliasIsConstexpr"))385 OS << 'E';386 }387 }388 389 if (auto NS = Builtin->getValueAsOptionalString("Namespace")) {390 if (NS != "std")391 PrintFatalError(Builtin->getFieldLoc("Namespace"), "Unknown namespace: ");392 OS << "z";393 }394 395 for (const auto *Attr : Builtin->getValueAsListOfDefs("Attributes")) {396 OS << Attr->getValueAsString("Mangling");397 if (Attr->isSubClassOf("IndexedAttribute")) {398 OS << ':' << Attr->getValueAsInt("Index") << ':';399 } else if (Attr->isSubClassOf("MultiIndexAttribute")) {400 OS << '<';401 llvm::ListSeparator Sep(",");402 for (int64_t Index : Attr->getValueAsListOfInts("Indices"))403 OS << Sep << Index;404 OS << '>';405 }406 }407 return Attributes;408}409 410Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord,411 Twine Spelling, BuiltinType BT) {412 Builtin B;413 B.BT = BT;414 B.Name = Spelling.str();415 B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString();416 B.Attributes = renderAttributes(BuiltinRecord, BT);417 B.BuiltinRecord = BuiltinRecord;418 return B;419}420 421struct TemplateInsts {422 std::vector<std::string> Substitution;423 std::vector<std::string> Affix;424 bool IsPrefix;425};426 427TemplateInsts getTemplateInsts(const Record *R) {428 TemplateInsts temp;429 auto Substitutions = R->getValueAsListOfStrings("Substitutions");430 auto Affixes = R->getValueAsListOfStrings("Affixes");431 temp.IsPrefix = R->getValueAsBit("AsPrefix");432 433 if (Substitutions.size() != Affixes.size())434 PrintFatalError(R->getLoc(), "Substitutions and affixes "435 "don't have the same lengths");436 437 for (auto [Affix, Substitution] : zip(Affixes, Substitutions)) {438 temp.Substitution.emplace_back(Substitution);439 temp.Affix.emplace_back(Affix);440 }441 return temp;442}443 444void collectBuiltins(const Record *BuiltinRecord,445 SmallVectorImpl<Builtin> &Builtins) {446 TemplateInsts Templates = {};447 if (BuiltinRecord->isSubClassOf("Template")) {448 Templates = getTemplateInsts(BuiltinRecord);449 } else {450 Templates.Affix.emplace_back();451 Templates.Substitution.emplace_back();452 }453 454 for (auto [Substitution, Affix] :455 zip(Templates.Substitution, Templates.Affix)) {456 for (StringRef Spelling :457 BuiltinRecord->getValueAsListOfStrings("Spellings")) {458 auto FullSpelling =459 (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str();460 BuiltinType BT = BuiltinType::Builtin;461 if (BuiltinRecord->isSubClassOf("AtomicBuiltin")) {462 BT = BuiltinType::AtomicBuiltin;463 } else if (BuiltinRecord->isSubClassOf("LangBuiltin")) {464 BT = BuiltinType::LangBuiltin;465 } else if (BuiltinRecord->isSubClassOf("TargetLibBuiltin")) {466 BT = BuiltinType::TargetLibBuiltin;467 } else if (BuiltinRecord->isSubClassOf("TargetBuiltin")) {468 BT = BuiltinType::TargetBuiltin;469 } else if (BuiltinRecord->isSubClassOf("LibBuiltin")) {470 BT = BuiltinType::LibBuiltin;471 if (BuiltinRecord->getValueAsBit("AddBuiltinPrefixedAlias"))472 Builtins.push_back(buildBuiltin(473 Substitution, BuiltinRecord,474 std::string("__builtin_") + FullSpelling, BuiltinType::Builtin));475 }476 Builtins.push_back(477 buildBuiltin(Substitution, BuiltinRecord, FullSpelling, BT));478 }479 }480}481} // namespace482 483void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) {484 emitSourceFileHeader("List of builtins that Clang recognizes", OS);485 486 SmallVector<Builtin> Builtins;487 // AtomicBuiltins are order dependent. Emit them first to make manual checking488 // easier and so we can build a special atomic builtin X-macro.489 for (const auto *BuiltinRecord :490 Records.getAllDerivedDefinitions("AtomicBuiltin"))491 collectBuiltins(BuiltinRecord, Builtins);492 unsigned NumAtomicBuiltins = Builtins.size();493 494 for (const auto *BuiltinRecord :495 Records.getAllDerivedDefinitions("Builtin")) {496 if (BuiltinRecord->isSubClassOf("AtomicBuiltin"))497 continue;498 // Prefixed builtins are also special and we emit them last so they can have499 // their own representation that skips the prefix.500 if (BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix"))501 continue;502 503 collectBuiltins(BuiltinRecord, Builtins);504 }505 506 // Now collect (and count) the prefixed builtins.507 unsigned NumPrefixedBuiltins = Builtins.size();508 const Record *FirstPrefix = nullptr;509 for (const auto *BuiltinRecord :510 Records.getAllDerivedDefinitions("Builtin")) {511 auto *Prefix = BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix");512 if (!Prefix)513 continue;514 515 if (!FirstPrefix)516 FirstPrefix = Prefix;517 assert(Prefix == FirstPrefix &&518 "Multiple distinct prefixes which is not currently supported!");519 assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin") &&520 "Cannot require a name prefix for an atomic builtin.");521 collectBuiltins(BuiltinRecord, Builtins);522 }523 NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins;524 525 auto AtomicBuiltins = ArrayRef(Builtins).slice(0, NumAtomicBuiltins);526 auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(NumPrefixedBuiltins);527 auto PrefixedBuiltins = ArrayRef(Builtins).take_back(NumPrefixedBuiltins);528 529 // Collect strings into a table.530 StringToOffsetTable Table;531 Table.GetOrAddStringOffset("");532 for (const auto &B : Builtins) {533 Table.GetOrAddStringOffset(B.Name);534 Table.GetOrAddStringOffset(B.Type);535 Table.GetOrAddStringOffset(B.Attributes);536 if (B.BT == BuiltinType::TargetBuiltin)537 Table.GetOrAddStringOffset(B.BuiltinRecord->getValueAsString("Features"));538 }539 540 // Emit enumerators.541 OS << R"c++(542#ifdef GET_BUILTIN_ENUMERATORS543)c++";544 for (const auto &B : Builtins)545 B.EmitEnumerator(OS);546 OS << R"c++(547#endif // GET_BUILTIN_ENUMERATORS548)c++";549 550 // Emit a string table that can be referenced for these builtins.551 OS << R"c++(552#ifdef GET_BUILTIN_STR_TABLE553)c++";554 Table.EmitStringTableDef(OS, "BuiltinStrings");555 OS << R"c++(556#endif // GET_BUILTIN_STR_TABLE557)c++";558 559 // Emit a direct set of `Builtin::Info` initializers, first for the unprefixed560 // builtins and then for the prefixed builtins.561 OS << R"c++(562#ifdef GET_BUILTIN_INFOS563)c++";564 for (const auto &B : UnprefixedBuiltins)565 B.EmitInfo(OS, Table);566 OS << R"c++(567#endif // GET_BUILTIN_INFOS568)c++";569 570 OS << R"c++(571#ifdef GET_BUILTIN_PREFIXED_INFOS572)c++";573 for (const auto &B : PrefixedBuiltins)574 B.EmitInfo(OS, Table);575 OS << R"c++(576#endif // GET_BUILTIN_PREFIXED_INFOS577)c++";578 579 // Emit X-macros for the atomic builtins to support various custome patterns580 // used exclusively with those builtins.581 //582 // FIXME: We should eventually move this to a separate file so that users583 // don't need to include the full set of builtins.584 OS << R"c++(585#ifdef ATOMIC_BUILTIN586)c++";587 for (const auto &Builtin : AtomicBuiltins) {588 Builtin.EmitXMacro(OS);589 }590 OS << R"c++(591#endif // ATOMIC_BUILTIN592#undef ATOMIC_BUILTIN593)c++";594}595