| 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-exception |
| 6 | // |
| 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 | |
| 22 | using namespace llvm; |
| 23 | |
| 24 | namespace { |
| 25 | enum class BuiltinType { |
| 26 | Builtin, |
| 27 | AtomicBuiltin, |
| 28 | LibBuiltin, |
| 29 | LangBuiltin, |
| 30 | TargetBuiltin, |
| 31 | TargetLibBuiltin, |
| 32 | }; |
| 33 | |
| 34 | class { |
| 35 | public: |
| 36 | (const Record *Builtin) { |
| 37 | for (char c : Builtin->getValueAsString(FieldName: "Header" )) { |
| 38 | if (std::islower(c)) |
| 39 | HeaderName += static_cast<char>(std::toupper(c: c)); |
| 40 | else if (c == '.' || c == '_' || c == '/' || c == '-') |
| 41 | HeaderName += '_'; |
| 42 | else |
| 43 | PrintFatalError(ErrorLoc: Builtin->getLoc(), Msg: "Unexpected header name" ); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | void (raw_ostream &OS) const { OS << HeaderName; } |
| 48 | |
| 49 | private: |
| 50 | std::string ; |
| 51 | }; |
| 52 | |
| 53 | struct 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 the |
| 64 | // enumerator. |
| 65 | if (auto *PrefixRecord = |
| 66 | BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" )) |
| 67 | OS << PrefixRecord->getValueAsString(FieldName: "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(Str: Name) << " /* " << Name << " */, " |
| 74 | << Table.GetStringOffset(Str: Type) << " /* " << Type << " */, " |
| 75 | << Table.GetStringOffset(Str: Attributes) << " /* " << Attributes << " */, " ; |
| 76 | if (BT == BuiltinType::TargetBuiltin) { |
| 77 | const auto &Features = BuiltinRecord->getValueAsString(FieldName: "Features" ); |
| 78 | OS << Table.GetStringOffset(Str: 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(FieldName: "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(FieldName: "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(FieldName: "Languages" ); |
| 133 | break; |
| 134 | } |
| 135 | case BuiltinType::TargetLibBuiltin: { |
| 136 | OS << ", " ; |
| 137 | HeaderNameParser{BuiltinRecord}.Print(OS); |
| 138 | OS << ", " << BuiltinRecord->getValueAsString(FieldName: "Languages" ); |
| 139 | [[fallthrough]]; |
| 140 | } |
| 141 | case BuiltinType::TargetBuiltin: { |
| 142 | OS << ", \"" << BuiltinRecord->getValueAsString(FieldName: "Features" ) << "\"" ; |
| 143 | break; |
| 144 | } |
| 145 | case BuiltinType::AtomicBuiltin: |
| 146 | case BuiltinType::Builtin: |
| 147 | break; |
| 148 | } |
| 149 | OS << ")\n" ; |
| 150 | } |
| 151 | }; |
| 152 | |
| 153 | class PrototypeParser { |
| 154 | public: |
| 155 | PrototypeParser(StringRef Substitution, const Record *Builtin) |
| 156 | : Loc(Builtin->getFieldLoc(FieldName: "Prototype" )), Substitution(Substitution), |
| 157 | EnableOpenCLLong(Builtin->getValueAsBit(FieldName: "EnableOpenCLLong" )) { |
| 158 | ParsePrototype(Prototype: Builtin->getValueAsString(FieldName: "Prototype" )); |
| 159 | } |
| 160 | |
| 161 | std::string takeTypeString() && { return std::move(Type); } |
| 162 | |
| 163 | private: |
| 164 | void ParsePrototype(StringRef Prototype) { |
| 165 | Prototype = Prototype.trim(); |
| 166 | |
| 167 | // Some builtins don't have an expressible prototype, simply emit an empty |
| 168 | // 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(F: [](char c) { return c == '('; }); |
| 179 | ParseType(T: ReturnType); |
| 180 | Prototype = Prototype.drop_front(N: ReturnType.size() + 1); |
| 181 | if (!Prototype.ends_with(Suffix: ")" )) |
| 182 | PrintFatalError(ErrorLoc: Loc, Msg: "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(Start: I, N: end); |
| 189 | // Skip any leading space or commas |
| 190 | if (Current.starts_with(Prefix: " " ) || Current.starts_with(Prefix: "," )) { |
| 191 | ++I; |
| 192 | continue; |
| 193 | } |
| 194 | |
| 195 | // Check if we are in _ExtVector. We do this first because |
| 196 | // extended vectors are written in template form with the syntax |
| 197 | // _ExtVector< ..., ...>, so we need to make sure we are not |
| 198 | // detecting the comma of the template class as a separator for |
| 199 | // the parameters of the prototype. Note: the assumption is that |
| 200 | // we cannot have nested _ExtVector. |
| 201 | if (Current.starts_with(Prefix: "_ExtVector<" ) || |
| 202 | Current.starts_with(Prefix: "_Vector<" )) { |
| 203 | const size_t EndTemplate = Current.find(C: '>', From: 0); |
| 204 | ParseType(T: Current.substr(Start: 0, N: 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 seen |
| 211 | // comma is the boundary of a parameter in the prototype. |
| 212 | if (size_t CommaPos = Current.find(C: ',', From: 0)) { |
| 213 | if (CommaPos != StringRef::npos) { |
| 214 | StringRef T = Current.substr(Start: 0, N: 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(T: 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(Suffix: ">" )) |
| 234 | return std::nullopt; |
| 235 | |
| 236 | auto Open = T.find_last_of(C: '<'); |
| 237 | if (Open == StringRef::npos) |
| 238 | PrintFatalError(ErrorLoc: Loc, Msg: "Mismatched angle-brackets in type" ); |
| 239 | |
| 240 | StringRef ArgStr = T.substr(Start: Open + 1); |
| 241 | T = T.slice(Start: 0, End: Open); |
| 242 | if (!T.consume_back(Suffix: "address_space" )) |
| 243 | PrintFatalError(ErrorLoc: Loc, |
| 244 | Msg: "Only `address_space<N>` supported as a parameterized " |
| 245 | "pointer or reference type qualifier" ); |
| 246 | |
| 247 | unsigned Number = 0; |
| 248 | if (ArgStr.getAsInteger(Radix: 10, Result&: Number)) |
| 249 | PrintFatalError( |
| 250 | ErrorLoc: Loc, Msg: "Expected an integer argument to the address_space qualifier" ); |
| 251 | if (Number == 0) |
| 252 | PrintFatalError(ErrorLoc: Loc, Msg: "No need for a qualifier for address space `0`" ); |
| 253 | return Number; |
| 254 | }; |
| 255 | |
| 256 | if (T.consume_back(Suffix: "*" )) { |
| 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(val: *AS); |
| 263 | } else if (T.consume_back(Suffix: "const" )) { |
| 264 | ParseType(T); |
| 265 | Type += "C" ; |
| 266 | } else if (T.consume_back(Suffix: "volatile" )) { |
| 267 | ParseType(T); |
| 268 | Type += "D" ; |
| 269 | } else if (T.consume_back(Suffix: "restrict" )) { |
| 270 | ParseType(T); |
| 271 | Type += "R" ; |
| 272 | } else if (T.consume_back(Suffix: "&" )) { |
| 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(val: *AS); |
| 279 | } else if (T.consume_back(Suffix: ")" )) { |
| 280 | ParseType(T); |
| 281 | Type += "&" ; |
| 282 | } else if (EnableOpenCLLong && T.consume_front(Prefix: "long long" )) { |
| 283 | Type += "O" ; |
| 284 | ParseType(T); |
| 285 | } else if (T.consume_front(Prefix: "long" )) { |
| 286 | Type += "L" ; |
| 287 | ParseType(T); |
| 288 | } else if (T.consume_front(Prefix: "signed" )) { |
| 289 | Type += "S" ; |
| 290 | ParseType(T); |
| 291 | } else if (T.consume_front(Prefix: "unsigned" )) { |
| 292 | Type += "U" ; |
| 293 | ParseType(T); |
| 294 | } else if (T.consume_front(Prefix: "_Complex" )) { |
| 295 | Type += "X" ; |
| 296 | ParseType(T); |
| 297 | } else if (T.consume_front(Prefix: "_Constant" )) { |
| 298 | Type += "I" ; |
| 299 | ParseType(T); |
| 300 | } else if (T.consume_front(Prefix: "T" )) { |
| 301 | if (Substitution.empty()) |
| 302 | PrintFatalError(ErrorLoc: Loc, Msg: "Not a template" ); |
| 303 | ParseType(T: Substitution); |
| 304 | } else if (auto IsExt = T.consume_front(Prefix: "_ExtVector" ); |
| 305 | IsExt || T.consume_front(Prefix: "_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 of |
| 311 | // `_ExtVector<N, T>` is correct... |
| 312 | if (!T.consume_front(Prefix: "<" )) |
| 313 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected '<' after '_ExtVector'" ); |
| 314 | unsigned long long Lanes; |
| 315 | if (consumeUnsignedInteger(Str&: T, Radix: 10, Result&: Lanes)) |
| 316 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected number of lanes after '_ExtVector<'" ); |
| 317 | Type += (IsExt ? "E" : "V" ) + std::to_string(val: Lanes); |
| 318 | if (!T.consume_front(Prefix: "," )) |
| 319 | PrintFatalError(ErrorLoc: Loc, |
| 320 | Msg: "Expected ',' after number of lanes in '_ExtVector<'" ); |
| 321 | if (!T.consume_back(Suffix: ">" )) |
| 322 | PrintFatalError( |
| 323 | ErrorLoc: Loc, Msg: "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(S: "__builtin_va_list_ref" , Value: "A" ) |
| 330 | .Case(S: "__builtin_va_list" , Value: "a" ) |
| 331 | .Case(S: "__float128" , Value: "LLd" ) |
| 332 | .Case(S: "__fp16" , Value: "h" ) |
| 333 | .Case(S: "__int128_t" , Value: "LLLi" ) |
| 334 | .Case(S: "_Float16" , Value: "x" ) |
| 335 | .Case(S: "__bf16" , Value: "y" ) |
| 336 | .Case(S: "bool" , Value: "b" ) |
| 337 | .Case(S: "char" , Value: "c" ) |
| 338 | .Case(S: "constant_CFString" , Value: "F" ) |
| 339 | .Case(S: "double" , Value: "d" ) |
| 340 | .Case(S: "FILE" , Value: "P" ) |
| 341 | .Case(S: "float" , Value: "f" ) |
| 342 | .Case(S: "id" , Value: "G" ) |
| 343 | .Case(S: "int" , Value: "i" ) |
| 344 | .Case(S: "int32_t" , Value: "Zi" ) |
| 345 | .Case(S: "int64_t" , Value: "Wi" ) |
| 346 | .Case(S: "jmp_buf" , Value: "J" ) |
| 347 | .Case(S: "msint32_t" , Value: "Ni" ) |
| 348 | .Case(S: "msuint32_t" , Value: "UNi" ) |
| 349 | .Case(S: "objc_super" , Value: "M" ) |
| 350 | .Case(S: "pid_t" , Value: "p" ) |
| 351 | .Case(S: "ptrdiff_t" , Value: "Y" ) |
| 352 | .Case(S: "SEL" , Value: "H" ) |
| 353 | .Case(S: "short" , Value: "s" ) |
| 354 | .Case(S: "sigjmp_buf" , Value: "SJ" ) |
| 355 | .Case(S: "size_t" , Value: "z" ) |
| 356 | .Case(S: "ucontext_t" , Value: "K" ) |
| 357 | .Case(S: "uint32_t" , Value: "UZi" ) |
| 358 | .Case(S: "uint64_t" , Value: "UWi" ) |
| 359 | .Case(S: "void" , Value: "v" ) |
| 360 | .Case(S: "wchar_t" , Value: "w" ) |
| 361 | .Case(S: "..." , Value: "." ) |
| 362 | .Default(Value: "error" ); |
| 363 | if (ReturnTypeVal == "error" ) |
| 364 | PrintFatalError(ErrorLoc: Loc, Msg: "Unknown Type: " + T); |
| 365 | Type += ReturnTypeVal; |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | SMLoc Loc; |
| 370 | StringRef Substitution; |
| 371 | bool EnableOpenCLLong; |
| 372 | std::string Type; |
| 373 | }; |
| 374 | |
| 375 | std::string renderAttributes(const Record *Builtin, BuiltinType BT) { |
| 376 | std::string Attributes; |
| 377 | raw_string_ostream OS(Attributes); |
| 378 | if (Builtin->isSubClassOf(Name: "LibBuiltin" )) { |
| 379 | if (BT == BuiltinType::LibBuiltin) { |
| 380 | OS << 'f'; |
| 381 | } else { |
| 382 | OS << 'F'; |
| 383 | if (Builtin->getValueAsBit(FieldName: "OnlyBuiltinPrefixedAliasIsConstexpr" )) |
| 384 | OS << 'E'; |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | if (auto NS = Builtin->getValueAsOptionalString(FieldName: "Namespace" )) { |
| 389 | if (NS != "std" ) |
| 390 | PrintFatalError(ErrorLoc: Builtin->getFieldLoc(FieldName: "Namespace" ), Msg: "Unknown namespace: " ); |
| 391 | OS << "z" ; |
| 392 | } |
| 393 | |
| 394 | for (const auto *Attr : Builtin->getValueAsListOfDefs(FieldName: "Attributes" )) { |
| 395 | OS << Attr->getValueAsString(FieldName: "Mangling" ); |
| 396 | if (Attr->isSubClassOf(Name: "IndexedAttribute" )) { |
| 397 | OS << ':' << Attr->getValueAsInt(FieldName: "Index" ) << ':'; |
| 398 | } else if (Attr->isSubClassOf(Name: "MultiIndexAttribute" )) { |
| 399 | OS << '<'; |
| 400 | llvm::ListSeparator Sep("," ); |
| 401 | for (int64_t Index : Attr->getValueAsListOfInts(FieldName: "Indices" )) |
| 402 | OS << Sep << Index; |
| 403 | OS << '>'; |
| 404 | } |
| 405 | } |
| 406 | return Attributes; |
| 407 | } |
| 408 | |
| 409 | Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord, |
| 410 | Twine Spelling, BuiltinType BT) { |
| 411 | Builtin B; |
| 412 | B.BT = BT; |
| 413 | B.Name = Spelling.str(); |
| 414 | B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString(); |
| 415 | B.Attributes = renderAttributes(Builtin: BuiltinRecord, BT); |
| 416 | B.BuiltinRecord = BuiltinRecord; |
| 417 | return B; |
| 418 | } |
| 419 | |
| 420 | struct TemplateInsts { |
| 421 | std::vector<std::string> Substitution; |
| 422 | std::vector<std::string> Affix; |
| 423 | bool IsPrefix; |
| 424 | }; |
| 425 | |
| 426 | TemplateInsts getTemplateInsts(const Record *R) { |
| 427 | TemplateInsts temp; |
| 428 | auto Substitutions = R->getValueAsListOfStrings(FieldName: "Substitutions" ); |
| 429 | auto Affixes = R->getValueAsListOfStrings(FieldName: "Affixes" ); |
| 430 | temp.IsPrefix = R->getValueAsBit(FieldName: "AsPrefix" ); |
| 431 | |
| 432 | if (Substitutions.size() != Affixes.size()) |
| 433 | PrintFatalError(ErrorLoc: R->getLoc(), Msg: "Substitutions and affixes " |
| 434 | "don't have the same lengths" ); |
| 435 | |
| 436 | for (auto [Affix, Substitution] : zip(t&: Affixes, u&: Substitutions)) { |
| 437 | temp.Substitution.emplace_back(args&: Substitution); |
| 438 | temp.Affix.emplace_back(args&: Affix); |
| 439 | } |
| 440 | return temp; |
| 441 | } |
| 442 | |
| 443 | void collectBuiltins(const Record *BuiltinRecord, |
| 444 | SmallVectorImpl<Builtin> &Builtins) { |
| 445 | TemplateInsts Templates = {}; |
| 446 | if (BuiltinRecord->isSubClassOf(Name: "Template" )) { |
| 447 | Templates = getTemplateInsts(R: BuiltinRecord); |
| 448 | } else { |
| 449 | Templates.Affix.emplace_back(); |
| 450 | Templates.Substitution.emplace_back(); |
| 451 | } |
| 452 | |
| 453 | for (auto [Substitution, Affix] : |
| 454 | zip(t&: Templates.Substitution, u&: Templates.Affix)) { |
| 455 | for (StringRef Spelling : |
| 456 | BuiltinRecord->getValueAsListOfStrings(FieldName: "Spellings" )) { |
| 457 | auto FullSpelling = |
| 458 | (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str(); |
| 459 | BuiltinType BT = BuiltinType::Builtin; |
| 460 | if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin" )) { |
| 461 | BT = BuiltinType::AtomicBuiltin; |
| 462 | } else if (BuiltinRecord->isSubClassOf(Name: "LangBuiltin" )) { |
| 463 | BT = BuiltinType::LangBuiltin; |
| 464 | } else if (BuiltinRecord->isSubClassOf(Name: "TargetLibBuiltin" )) { |
| 465 | BT = BuiltinType::TargetLibBuiltin; |
| 466 | } else if (BuiltinRecord->isSubClassOf(Name: "TargetBuiltin" )) { |
| 467 | BT = BuiltinType::TargetBuiltin; |
| 468 | } else if (BuiltinRecord->isSubClassOf(Name: "LibBuiltin" )) { |
| 469 | BT = BuiltinType::LibBuiltin; |
| 470 | if (BuiltinRecord->getValueAsBit(FieldName: "AddBuiltinPrefixedAlias" )) |
| 471 | Builtins.push_back(Elt: buildBuiltin( |
| 472 | Substitution, BuiltinRecord, |
| 473 | Spelling: std::string("__builtin_" ) + FullSpelling, BT: BuiltinType::Builtin)); |
| 474 | } |
| 475 | Builtins.push_back( |
| 476 | Elt: buildBuiltin(Substitution, BuiltinRecord, Spelling: FullSpelling, BT)); |
| 477 | } |
| 478 | } |
| 479 | } |
| 480 | } // namespace |
| 481 | |
| 482 | void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) { |
| 483 | emitSourceFileHeader(Desc: "List of builtins that Clang recognizes" , OS); |
| 484 | |
| 485 | SmallVector<Builtin> Builtins; |
| 486 | // AtomicBuiltins are order dependent. Emit them first to make manual checking |
| 487 | // easier and so we can build a special atomic builtin X-macro. |
| 488 | for (const auto *BuiltinRecord : |
| 489 | Records.getAllDerivedDefinitions(ClassName: "AtomicBuiltin" )) |
| 490 | collectBuiltins(BuiltinRecord, Builtins); |
| 491 | unsigned NumAtomicBuiltins = Builtins.size(); |
| 492 | |
| 493 | for (const auto *BuiltinRecord : |
| 494 | Records.getAllDerivedDefinitions(ClassName: "Builtin" )) { |
| 495 | if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin" )) |
| 496 | continue; |
| 497 | // Prefixed builtins are also special and we emit them last so they can have |
| 498 | // their own representation that skips the prefix. |
| 499 | if (BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" )) |
| 500 | continue; |
| 501 | |
| 502 | collectBuiltins(BuiltinRecord, Builtins); |
| 503 | } |
| 504 | |
| 505 | // Now collect (and count) the prefixed builtins. |
| 506 | unsigned NumPrefixedBuiltins = Builtins.size(); |
| 507 | const Record *FirstPrefix = nullptr; |
| 508 | for (const auto *BuiltinRecord : |
| 509 | Records.getAllDerivedDefinitions(ClassName: "Builtin" )) { |
| 510 | auto *Prefix = BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" ); |
| 511 | if (!Prefix) |
| 512 | continue; |
| 513 | |
| 514 | if (!FirstPrefix) |
| 515 | FirstPrefix = Prefix; |
| 516 | assert(Prefix == FirstPrefix && |
| 517 | "Multiple distinct prefixes which is not currently supported!" ); |
| 518 | assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin" ) && |
| 519 | "Cannot require a name prefix for an atomic builtin." ); |
| 520 | collectBuiltins(BuiltinRecord, Builtins); |
| 521 | } |
| 522 | NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins; |
| 523 | |
| 524 | auto AtomicBuiltins = ArrayRef(Builtins).slice(N: 0, M: NumAtomicBuiltins); |
| 525 | auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(N: NumPrefixedBuiltins); |
| 526 | auto PrefixedBuiltins = ArrayRef(Builtins).take_back(N: NumPrefixedBuiltins); |
| 527 | |
| 528 | // Collect strings into a table. |
| 529 | StringToOffsetTable Table; |
| 530 | Table.GetOrAddStringOffset(Str: "" ); |
| 531 | for (const auto &B : Builtins) { |
| 532 | Table.GetOrAddStringOffset(Str: B.Name); |
| 533 | Table.GetOrAddStringOffset(Str: B.Type); |
| 534 | Table.GetOrAddStringOffset(Str: B.Attributes); |
| 535 | if (B.BT == BuiltinType::TargetBuiltin) |
| 536 | Table.GetOrAddStringOffset(Str: B.BuiltinRecord->getValueAsString(FieldName: "Features" )); |
| 537 | } |
| 538 | |
| 539 | // Emit enumerators. |
| 540 | OS << R"c++( |
| 541 | #ifdef GET_BUILTIN_ENUMERATORS |
| 542 | )c++" ; |
| 543 | for (const auto &B : Builtins) |
| 544 | B.EmitEnumerator(OS); |
| 545 | OS << R"c++( |
| 546 | #endif // GET_BUILTIN_ENUMERATORS |
| 547 | )c++" ; |
| 548 | |
| 549 | // Emit a string table that can be referenced for these builtins. |
| 550 | OS << R"c++( |
| 551 | #ifdef GET_BUILTIN_STR_TABLE |
| 552 | )c++" ; |
| 553 | Table.EmitStringTableDef(OS, Name: "BuiltinStrings" ); |
| 554 | OS << R"c++( |
| 555 | #endif // GET_BUILTIN_STR_TABLE |
| 556 | )c++" ; |
| 557 | |
| 558 | // Emit a direct set of `Builtin::Info` initializers, first for the unprefixed |
| 559 | // builtins and then for the prefixed builtins. |
| 560 | OS << R"c++( |
| 561 | #ifdef GET_BUILTIN_INFOS |
| 562 | )c++" ; |
| 563 | for (const auto &B : UnprefixedBuiltins) |
| 564 | B.EmitInfo(OS, Table); |
| 565 | OS << R"c++( |
| 566 | #endif // GET_BUILTIN_INFOS |
| 567 | )c++" ; |
| 568 | |
| 569 | OS << R"c++( |
| 570 | #ifdef GET_BUILTIN_PREFIXED_INFOS |
| 571 | )c++" ; |
| 572 | for (const auto &B : PrefixedBuiltins) |
| 573 | B.EmitInfo(OS, Table); |
| 574 | OS << R"c++( |
| 575 | #endif // GET_BUILTIN_PREFIXED_INFOS |
| 576 | )c++" ; |
| 577 | |
| 578 | // Emit X-macros for the atomic builtins to support various custome patterns |
| 579 | // used exclusively with those builtins. |
| 580 | // |
| 581 | // FIXME: We should eventually move this to a separate file so that users |
| 582 | // don't need to include the full set of builtins. |
| 583 | OS << R"c++( |
| 584 | #ifdef ATOMIC_BUILTIN |
| 585 | )c++" ; |
| 586 | for (const auto &Builtin : AtomicBuiltins) { |
| 587 | Builtin.EmitXMacro(OS); |
| 588 | } |
| 589 | OS << R"c++( |
| 590 | #endif // ATOMIC_BUILTIN |
| 591 | #undef ATOMIC_BUILTIN |
| 592 | )c++" ; |
| 593 | } |
| 594 | |