| 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/MapVector.h" |
| 15 | #include "llvm/ADT/STLExtras.h" |
| 16 | #include "llvm/ADT/StringExtras.h" |
| 17 | #include "llvm/ADT/StringSwitch.h" |
| 18 | #include "llvm/TableGen/Error.h" |
| 19 | #include "llvm/TableGen/Record.h" |
| 20 | #include "llvm/TableGen/StringToOffsetTable.h" |
| 21 | #include "llvm/TableGen/TableGenBackend.h" |
| 22 | #include <sstream> |
| 23 | |
| 24 | using namespace llvm; |
| 25 | |
| 26 | namespace { |
| 27 | enum class BuiltinType { |
| 28 | Builtin, |
| 29 | AtomicBuiltin, |
| 30 | LibBuiltin, |
| 31 | LangBuiltin, |
| 32 | TargetBuiltin, |
| 33 | TargetLibBuiltin, |
| 34 | }; |
| 35 | |
| 36 | class { |
| 37 | public: |
| 38 | (const Record *Builtin) { |
| 39 | for (char c : Builtin->getValueAsString(FieldName: "Header" )) { |
| 40 | if (std::islower(c)) |
| 41 | HeaderName += static_cast<char>(std::toupper(c: c)); |
| 42 | else if (c == '.' || c == '_' || c == '/' || c == '-') |
| 43 | HeaderName += '_'; |
| 44 | else |
| 45 | PrintFatalError(ErrorLoc: Builtin->getLoc(), Msg: "Unexpected header name" ); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | void (raw_ostream &OS) const { OS << HeaderName; } |
| 50 | |
| 51 | private: |
| 52 | std::string ; |
| 53 | }; |
| 54 | |
| 55 | struct Builtin { |
| 56 | BuiltinType BT; |
| 57 | std::string Name; |
| 58 | std::string Type; |
| 59 | std::string Attributes; |
| 60 | |
| 61 | const Record *BuiltinRecord; |
| 62 | |
| 63 | void EmitEnumerator(llvm::raw_ostream &OS) const { |
| 64 | OS << " BI" ; |
| 65 | // If there is a required name prefix, include its spelling in the |
| 66 | // enumerator. |
| 67 | if (auto *PrefixRecord = |
| 68 | BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" )) |
| 69 | OS << PrefixRecord->getValueAsString(FieldName: "Spelling" ); |
| 70 | OS << Name << ",\n" ; |
| 71 | } |
| 72 | |
| 73 | void EmitInfo(llvm::raw_ostream &OS, const StringToOffsetTable &Table) const { |
| 74 | OS << " Builtin::Info{Builtin::Info::StrOffsets{" |
| 75 | << Table.GetStringOffset(Str: Name) << " /* " << Name << " */, " |
| 76 | << Table.GetStringOffset(Str: Type) << " /* " << Type << " */, " |
| 77 | << Table.GetStringOffset(Str: Attributes) << " /* " << Attributes << " */, " ; |
| 78 | if (BT == BuiltinType::TargetBuiltin) { |
| 79 | const auto &Features = BuiltinRecord->getValueAsString(FieldName: "Features" ); |
| 80 | OS << Table.GetStringOffset(Str: Features) << " /* " << Features << " */" ; |
| 81 | } else { |
| 82 | OS << "0" ; |
| 83 | } |
| 84 | OS << "}, " ; |
| 85 | if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::TargetLibBuiltin) { |
| 86 | OS << "HeaderDesc::" ; |
| 87 | HeaderNameParser{BuiltinRecord}.Print(OS); |
| 88 | } else { |
| 89 | OS << "HeaderDesc::NO_HEADER" ; |
| 90 | } |
| 91 | OS << ", " ; |
| 92 | if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::LangBuiltin || |
| 93 | BT == BuiltinType::TargetLibBuiltin) { |
| 94 | OS << BuiltinRecord->getValueAsString(FieldName: "Languages" ); |
| 95 | } else { |
| 96 | OS << "ALL_LANGUAGES" ; |
| 97 | } |
| 98 | OS << "},\n" ; |
| 99 | } |
| 100 | |
| 101 | void EmitXMacro(llvm::raw_ostream &OS) const { |
| 102 | if (BuiltinRecord->getValueAsBit(FieldName: "RequiresUndef" )) |
| 103 | OS << "#undef " << Name << '\n'; |
| 104 | switch (BT) { |
| 105 | case BuiltinType::LibBuiltin: |
| 106 | OS << "LIBBUILTIN" ; |
| 107 | break; |
| 108 | case BuiltinType::LangBuiltin: |
| 109 | OS << "LANGBUILTIN" ; |
| 110 | break; |
| 111 | case BuiltinType::Builtin: |
| 112 | OS << "BUILTIN" ; |
| 113 | break; |
| 114 | case BuiltinType::AtomicBuiltin: |
| 115 | OS << "ATOMIC_BUILTIN" ; |
| 116 | break; |
| 117 | case BuiltinType::TargetBuiltin: |
| 118 | OS << "TARGET_BUILTIN" ; |
| 119 | break; |
| 120 | case BuiltinType::TargetLibBuiltin: |
| 121 | OS << "TARGET_HEADER_BUILTIN" ; |
| 122 | break; |
| 123 | } |
| 124 | |
| 125 | OS << "(" << Name << ", \"" << Type << "\", \"" << Attributes << "\"" ; |
| 126 | |
| 127 | switch (BT) { |
| 128 | case BuiltinType::LibBuiltin: { |
| 129 | OS << ", " ; |
| 130 | HeaderNameParser{BuiltinRecord}.Print(OS); |
| 131 | [[fallthrough]]; |
| 132 | } |
| 133 | case BuiltinType::LangBuiltin: { |
| 134 | OS << ", " << BuiltinRecord->getValueAsString(FieldName: "Languages" ); |
| 135 | break; |
| 136 | } |
| 137 | case BuiltinType::TargetLibBuiltin: { |
| 138 | OS << ", " ; |
| 139 | HeaderNameParser{BuiltinRecord}.Print(OS); |
| 140 | OS << ", " << BuiltinRecord->getValueAsString(FieldName: "Languages" ); |
| 141 | [[fallthrough]]; |
| 142 | } |
| 143 | case BuiltinType::TargetBuiltin: { |
| 144 | OS << ", \"" << BuiltinRecord->getValueAsString(FieldName: "Features" ) << "\"" ; |
| 145 | break; |
| 146 | } |
| 147 | case BuiltinType::AtomicBuiltin: |
| 148 | case BuiltinType::Builtin: |
| 149 | break; |
| 150 | } |
| 151 | OS << ")\n" ; |
| 152 | } |
| 153 | }; |
| 154 | |
| 155 | class PrototypeParser { |
| 156 | public: |
| 157 | PrototypeParser(StringRef Substitution, const Record *Builtin) |
| 158 | : Loc(Builtin->getFieldLoc(FieldName: "Prototype" )), Substitution(Substitution), |
| 159 | EnableOpenCLLong(Builtin->getValueAsBit(FieldName: "EnableOpenCLLong" )) { |
| 160 | ParsePrototype(Prototype: Builtin->getValueAsString(FieldName: "Prototype" )); |
| 161 | } |
| 162 | |
| 163 | std::string takeTypeString() && { return std::move(Type); } |
| 164 | |
| 165 | private: |
| 166 | void ParsePrototype(StringRef Prototype) { |
| 167 | Prototype = Prototype.trim(); |
| 168 | |
| 169 | // Some builtins don't have an expressible prototype, simply emit an empty |
| 170 | // string for them. |
| 171 | if (Prototype.empty()) { |
| 172 | Type = "" ; |
| 173 | return; |
| 174 | } |
| 175 | |
| 176 | ParseTypes(Prototype); |
| 177 | } |
| 178 | |
| 179 | void ParseTypes(StringRef &Prototype) { |
| 180 | auto ReturnType = Prototype.take_until(F: [](char c) { return c == '('; }); |
| 181 | ParseType(T: ReturnType); |
| 182 | Prototype = Prototype.drop_front(N: ReturnType.size() + 1); |
| 183 | if (!Prototype.ends_with(Suffix: ")" )) |
| 184 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected closing brace at end of prototype" ); |
| 185 | Prototype = Prototype.drop_back(); |
| 186 | |
| 187 | // Look through the input parameters. |
| 188 | const size_t end = Prototype.size(); |
| 189 | for (size_t I = 0; I != end;) { |
| 190 | const StringRef Current = Prototype.substr(Start: I, N: end); |
| 191 | // Skip any leading space or commas |
| 192 | if (Current.starts_with(Prefix: " " ) || Current.starts_with(Prefix: "," )) { |
| 193 | ++I; |
| 194 | continue; |
| 195 | } |
| 196 | |
| 197 | // Check if we are in _ExtVector. We do this first because |
| 198 | // extended vectors are written in template form with the syntax |
| 199 | // _ExtVector< ..., ...>, so we need to make sure we are not |
| 200 | // detecting the comma of the template class as a separator for |
| 201 | // the parameters of the prototype. Note: the assumption is that |
| 202 | // we cannot have nested _ExtVector. |
| 203 | if (Current.starts_with(Prefix: "_ExtVector<" ) || |
| 204 | Current.starts_with(Prefix: "_Vector<" )) { |
| 205 | size_t Pos = Current.find(C: '<'); |
| 206 | int Depth = 1; |
| 207 | |
| 208 | // There may be a nested address_space<...> modifier on the type. |
| 209 | while (Depth > 0 && ++Pos < Current.size()) { |
| 210 | if (Current[Pos] == '<') |
| 211 | ++Depth; |
| 212 | else if (Current[Pos] == '>') |
| 213 | --Depth; |
| 214 | } |
| 215 | |
| 216 | const size_t EndTemplate = Pos; |
| 217 | ParseType(T: Current.substr(Start: 0, N: EndTemplate + 1)); |
| 218 | // Move the prototype beyond _ExtVector<...> |
| 219 | I += EndTemplate + 1; |
| 220 | continue; |
| 221 | } |
| 222 | |
| 223 | // We know that we are past _ExtVector, therefore the first seen |
| 224 | // comma is the boundary of a parameter in the prototype. |
| 225 | if (size_t CommaPos = Current.find(C: ',', From: 0)) { |
| 226 | if (CommaPos != StringRef::npos) { |
| 227 | StringRef T = Current.substr(Start: 0, N: CommaPos); |
| 228 | ParseType(T); |
| 229 | // Move the prototype beyond the comma. |
| 230 | I += CommaPos + 1; |
| 231 | continue; |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | // No more commas, parse final parameter. |
| 236 | ParseType(T: Current); |
| 237 | I = end; |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | void ParseType(StringRef T) { |
| 242 | T = T.trim(); |
| 243 | |
| 244 | auto ConsumeAddrSpace = [&]() -> std::optional<unsigned> { |
| 245 | T = T.trim(); |
| 246 | if (!T.consume_back(Suffix: ">" )) |
| 247 | return std::nullopt; |
| 248 | |
| 249 | auto Open = T.find_last_of(C: '<'); |
| 250 | if (Open == StringRef::npos) |
| 251 | PrintFatalError(ErrorLoc: Loc, Msg: "Mismatched angle-brackets in type" ); |
| 252 | |
| 253 | StringRef ArgStr = T.substr(Start: Open + 1); |
| 254 | T = T.slice(Start: 0, End: Open); |
| 255 | if (!T.consume_back(Suffix: "address_space" )) |
| 256 | PrintFatalError(ErrorLoc: Loc, |
| 257 | Msg: "Only `address_space<N>` supported as a parameterized " |
| 258 | "pointer or reference type qualifier" ); |
| 259 | |
| 260 | unsigned Number = 0; |
| 261 | if (ArgStr.getAsInteger(Radix: 10, Result&: Number)) |
| 262 | PrintFatalError( |
| 263 | ErrorLoc: Loc, Msg: "Expected an integer argument to the address_space qualifier" ); |
| 264 | return Number; |
| 265 | }; |
| 266 | |
| 267 | if (T.consume_back(Suffix: "*" )) { |
| 268 | // Pointers may have an address space qualifier immediately before them. |
| 269 | std::optional<unsigned> AS = ConsumeAddrSpace(); |
| 270 | // Pointers can apply to already parsed types, like vectors. |
| 271 | if (!T.empty()) |
| 272 | ParseType(T); |
| 273 | Type += "*" ; |
| 274 | if (AS) |
| 275 | Type += std::to_string(val: *AS); |
| 276 | } else if (T.consume_back(Suffix: "const" )) { |
| 277 | ParseType(T); |
| 278 | Type += "C" ; |
| 279 | } else if (T.consume_back(Suffix: "volatile" )) { |
| 280 | ParseType(T); |
| 281 | Type += "D" ; |
| 282 | } else if (T.consume_back(Suffix: "restrict" )) { |
| 283 | ParseType(T); |
| 284 | Type += "R" ; |
| 285 | } else if (T.consume_back(Suffix: "&" )) { |
| 286 | // References may have an address space qualifier immediately before them. |
| 287 | std::optional<unsigned> AS = ConsumeAddrSpace(); |
| 288 | ParseType(T); |
| 289 | Type += "&" ; |
| 290 | if (AS) |
| 291 | Type += std::to_string(val: *AS); |
| 292 | } else if (T.consume_back(Suffix: ")" )) { |
| 293 | ParseType(T); |
| 294 | Type += "&" ; |
| 295 | } else if (EnableOpenCLLong && T.consume_front(Prefix: "long long" )) { |
| 296 | Type += "O" ; |
| 297 | ParseType(T); |
| 298 | } else if (T.consume_front(Prefix: "long" )) { |
| 299 | Type += "L" ; |
| 300 | ParseType(T); |
| 301 | } else if (T.consume_front(Prefix: "signed" )) { |
| 302 | Type += "S" ; |
| 303 | ParseType(T); |
| 304 | } else if (T.consume_front(Prefix: "unsigned" )) { |
| 305 | Type += "U" ; |
| 306 | ParseType(T); |
| 307 | } else if (T.consume_front(Prefix: "_Complex" )) { |
| 308 | Type += "X" ; |
| 309 | ParseType(T); |
| 310 | } else if (T.consume_front(Prefix: "_Constant" )) { |
| 311 | Type += "I" ; |
| 312 | ParseType(T); |
| 313 | } else if (T.consume_front(Prefix: "T" )) { |
| 314 | if (Substitution.empty()) |
| 315 | PrintFatalError(ErrorLoc: Loc, Msg: "Not a template" ); |
| 316 | ParseType(T: Substitution); |
| 317 | } else if (auto IsExt = T.consume_front(Prefix: "_ExtVector" ); |
| 318 | IsExt || T.consume_front(Prefix: "_Vector" )) { |
| 319 | // Clang extended vector types are mangled as follows: |
| 320 | // |
| 321 | // '_ExtVector<' <lanes> ',' <scalar type> '>' |
| 322 | |
| 323 | // Before parsing T(=<scalar type>), make sure the syntax of |
| 324 | // `_ExtVector<N, T>` is correct... |
| 325 | if (!T.consume_front(Prefix: "<" )) |
| 326 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected '<' after '_ExtVector'" ); |
| 327 | unsigned long long Lanes; |
| 328 | if (consumeUnsignedInteger(Str&: T, Radix: 10, Result&: Lanes)) |
| 329 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected number of lanes after '_ExtVector<'" ); |
| 330 | Type += (IsExt ? "E" : "V" ) + std::to_string(val: Lanes); |
| 331 | if (!T.consume_front(Prefix: "," )) |
| 332 | PrintFatalError(ErrorLoc: Loc, |
| 333 | Msg: "Expected ',' after number of lanes in '_ExtVector<'" ); |
| 334 | if (!T.consume_back(Suffix: ">" )) |
| 335 | PrintFatalError( |
| 336 | ErrorLoc: Loc, Msg: "Expected '>' after scalar type in '_ExtVector<N, type>'" ); |
| 337 | |
| 338 | // ...all good, we can check if we have a valid `<scalar type>`. |
| 339 | ParseType(T); |
| 340 | } else { |
| 341 | auto ReturnTypeVal = StringSwitch<std::string>(T) |
| 342 | .Case(S: "__builtin_va_list_ref" , Value: "A" ) |
| 343 | .Case(S: "__builtin_va_list" , Value: "a" ) |
| 344 | .Case(S: "__float128" , Value: "LLd" ) |
| 345 | .Case(S: "__fp16" , Value: "h" ) |
| 346 | .Case(S: "__hlsl_resource_t" , Value: "Qr" ) |
| 347 | .Case(S: "__amdgpu_buffer_rsrc_t" , Value: "Qb" ) |
| 348 | .Case(S: "__amdgpu_texture_t" , Value: "Qt" ) |
| 349 | .Case(S: "__int128_t" , Value: "LLLi" ) |
| 350 | .Case(S: "_Float16" , Value: "x" ) |
| 351 | .Case(S: "__bf16" , Value: "y" ) |
| 352 | .Case(S: "bool" , Value: "b" ) |
| 353 | .Case(S: "char" , Value: "c" ) |
| 354 | .Case(S: "constant_CFString" , Value: "F" ) |
| 355 | .Case(S: "double" , Value: "d" ) |
| 356 | .Case(S: "FILE" , Value: "P" ) |
| 357 | .Case(S: "float" , Value: "f" ) |
| 358 | .Case(S: "id" , Value: "G" ) |
| 359 | .Case(S: "int" , Value: "i" ) |
| 360 | .Case(S: "int32_t" , Value: "Zi" ) |
| 361 | .Case(S: "int64_t" , Value: "Wi" ) |
| 362 | .Case(S: "jmp_buf" , Value: "J" ) |
| 363 | .Case(S: "msint32_t" , Value: "Ni" ) |
| 364 | .Case(S: "msuint32_t" , Value: "UNi" ) |
| 365 | .Case(S: "objc_super" , Value: "M" ) |
| 366 | .Case(S: "pid_t" , Value: "p" ) |
| 367 | .Case(S: "ptrdiff_t" , Value: "Y" ) |
| 368 | .Case(S: "SEL" , Value: "H" ) |
| 369 | .Case(S: "short" , Value: "s" ) |
| 370 | .Case(S: "sigjmp_buf" , Value: "SJ" ) |
| 371 | .Case(S: "size_t" , Value: "z" ) |
| 372 | .Case(S: "ucontext_t" , Value: "K" ) |
| 373 | .Case(S: "uint32_t" , Value: "UZi" ) |
| 374 | .Case(S: "uint64_t" , Value: "UWi" ) |
| 375 | .Case(S: "void" , Value: "v" ) |
| 376 | .Case(S: "wchar_t" , Value: "w" ) |
| 377 | .Case(S: "..." , Value: "." ) |
| 378 | .Default(Value: "error" ); |
| 379 | if (ReturnTypeVal == "error" ) |
| 380 | PrintFatalError(ErrorLoc: Loc, Msg: "Unknown Type: " + T); |
| 381 | Type += ReturnTypeVal; |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | SMLoc Loc; |
| 386 | StringRef Substitution; |
| 387 | bool EnableOpenCLLong; |
| 388 | std::string Type; |
| 389 | }; |
| 390 | |
| 391 | std::string renderAttributes(const Record *Builtin, BuiltinType BT) { |
| 392 | std::string Attributes; |
| 393 | raw_string_ostream OS(Attributes); |
| 394 | if (Builtin->isSubClassOf(Name: "LibBuiltin" )) { |
| 395 | if (BT == BuiltinType::LibBuiltin) { |
| 396 | OS << 'f'; |
| 397 | } else { |
| 398 | OS << 'F'; |
| 399 | if (Builtin->getValueAsBit(FieldName: "OnlyBuiltinPrefixedAliasIsConstexpr" )) |
| 400 | OS << 'E'; |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | if (auto NS = Builtin->getValueAsOptionalString(FieldName: "Namespace" )) { |
| 405 | if (NS != "std" ) |
| 406 | PrintFatalError(ErrorLoc: Builtin->getFieldLoc(FieldName: "Namespace" ), Msg: "Unknown namespace: " ); |
| 407 | OS << "z" ; |
| 408 | } |
| 409 | |
| 410 | for (const auto *Attr : Builtin->getValueAsListOfDefs(FieldName: "Attributes" )) { |
| 411 | OS << Attr->getValueAsString(FieldName: "Mangling" ); |
| 412 | if (Attr->isSubClassOf(Name: "IndexedAttribute" )) { |
| 413 | OS << ':' << Attr->getValueAsInt(FieldName: "Index" ) << ':'; |
| 414 | } else if (Attr->isSubClassOf(Name: "MultiIndexAttribute" )) { |
| 415 | OS << '<'; |
| 416 | llvm::ListSeparator Sep("," ); |
| 417 | for (int64_t Index : Attr->getValueAsListOfInts(FieldName: "Indices" )) |
| 418 | OS << Sep << Index; |
| 419 | OS << '>'; |
| 420 | } |
| 421 | } |
| 422 | return Attributes; |
| 423 | } |
| 424 | |
| 425 | Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord, |
| 426 | Twine Spelling, BuiltinType BT) { |
| 427 | Builtin B; |
| 428 | B.BT = BT; |
| 429 | B.Name = Spelling.str(); |
| 430 | B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString(); |
| 431 | B.Attributes = renderAttributes(Builtin: BuiltinRecord, BT); |
| 432 | B.BuiltinRecord = BuiltinRecord; |
| 433 | return B; |
| 434 | } |
| 435 | |
| 436 | struct TemplateInsts { |
| 437 | std::vector<std::string> Substitution; |
| 438 | std::vector<std::string> Affix; |
| 439 | bool IsPrefix; |
| 440 | }; |
| 441 | |
| 442 | TemplateInsts getTemplateInsts(const Record *R) { |
| 443 | TemplateInsts temp; |
| 444 | auto Substitutions = R->getValueAsListOfStrings(FieldName: "Substitutions" ); |
| 445 | auto Affixes = R->getValueAsListOfStrings(FieldName: "Affixes" ); |
| 446 | temp.IsPrefix = R->getValueAsBit(FieldName: "AsPrefix" ); |
| 447 | |
| 448 | if (Substitutions.size() != Affixes.size()) |
| 449 | PrintFatalError(ErrorLoc: R->getLoc(), Msg: "Substitutions and affixes " |
| 450 | "don't have the same lengths" ); |
| 451 | |
| 452 | for (auto [Affix, Substitution] : zip(t&: Affixes, u&: Substitutions)) { |
| 453 | temp.Substitution.emplace_back(args&: Substitution); |
| 454 | temp.Affix.emplace_back(args&: Affix); |
| 455 | } |
| 456 | return temp; |
| 457 | } |
| 458 | |
| 459 | void collectBuiltins(const Record *BuiltinRecord, |
| 460 | SmallVectorImpl<Builtin> &Builtins) { |
| 461 | TemplateInsts Templates = {}; |
| 462 | if (BuiltinRecord->isSubClassOf(Name: "Template" )) { |
| 463 | Templates = getTemplateInsts(R: BuiltinRecord); |
| 464 | } else { |
| 465 | Templates.Affix.emplace_back(); |
| 466 | Templates.Substitution.emplace_back(); |
| 467 | } |
| 468 | |
| 469 | for (auto [Substitution, Affix] : |
| 470 | zip(t&: Templates.Substitution, u&: Templates.Affix)) { |
| 471 | for (StringRef Spelling : |
| 472 | BuiltinRecord->getValueAsListOfStrings(FieldName: "Spellings" )) { |
| 473 | auto FullSpelling = |
| 474 | (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str(); |
| 475 | BuiltinType BT = BuiltinType::Builtin; |
| 476 | if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin" )) { |
| 477 | BT = BuiltinType::AtomicBuiltin; |
| 478 | } else if (BuiltinRecord->isSubClassOf(Name: "LangBuiltin" )) { |
| 479 | BT = BuiltinType::LangBuiltin; |
| 480 | } else if (BuiltinRecord->isSubClassOf(Name: "TargetLibBuiltin" )) { |
| 481 | BT = BuiltinType::TargetLibBuiltin; |
| 482 | } else if (BuiltinRecord->isSubClassOf(Name: "TargetBuiltin" )) { |
| 483 | BT = BuiltinType::TargetBuiltin; |
| 484 | } else if (BuiltinRecord->isSubClassOf(Name: "LibBuiltin" )) { |
| 485 | BT = BuiltinType::LibBuiltin; |
| 486 | if (BuiltinRecord->getValueAsBit(FieldName: "AddBuiltinPrefixedAlias" )) |
| 487 | Builtins.push_back(Elt: buildBuiltin( |
| 488 | Substitution, BuiltinRecord, |
| 489 | Spelling: std::string("__builtin_" ) + FullSpelling, BT: BuiltinType::Builtin)); |
| 490 | } |
| 491 | Builtins.push_back( |
| 492 | Elt: buildBuiltin(Substitution, BuiltinRecord, Spelling: FullSpelling, BT)); |
| 493 | } |
| 494 | } |
| 495 | } |
| 496 | } // namespace |
| 497 | |
| 498 | void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) { |
| 499 | emitSourceFileHeader(Desc: "List of builtins that Clang recognizes" , OS); |
| 500 | |
| 501 | SmallVector<Builtin> Builtins; |
| 502 | // AtomicBuiltins are order dependent. Emit them first to make manual checking |
| 503 | // easier and so we can build a special atomic builtin X-macro. |
| 504 | for (const auto *BuiltinRecord : |
| 505 | Records.getAllDerivedDefinitions(ClassName: "AtomicBuiltin" )) |
| 506 | collectBuiltins(BuiltinRecord, Builtins); |
| 507 | unsigned NumAtomicBuiltins = Builtins.size(); |
| 508 | |
| 509 | for (const auto *BuiltinRecord : |
| 510 | Records.getAllDerivedDefinitions(ClassName: "Builtin" )) { |
| 511 | if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin" )) |
| 512 | continue; |
| 513 | // Prefixed builtins are also special and we emit them last so they can have |
| 514 | // their own representation that skips the prefix. |
| 515 | if (BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" )) |
| 516 | continue; |
| 517 | |
| 518 | collectBuiltins(BuiltinRecord, Builtins); |
| 519 | } |
| 520 | |
| 521 | // Now collect (and count) the prefixed builtins. |
| 522 | unsigned NumPrefixedBuiltins = Builtins.size(); |
| 523 | const Record *FirstPrefix = nullptr; |
| 524 | for (const auto *BuiltinRecord : |
| 525 | Records.getAllDerivedDefinitions(ClassName: "Builtin" )) { |
| 526 | auto *Prefix = BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" ); |
| 527 | if (!Prefix) |
| 528 | continue; |
| 529 | |
| 530 | if (!FirstPrefix) |
| 531 | FirstPrefix = Prefix; |
| 532 | assert(Prefix == FirstPrefix && |
| 533 | "Multiple distinct prefixes which is not currently supported!" ); |
| 534 | assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin" ) && |
| 535 | "Cannot require a name prefix for an atomic builtin." ); |
| 536 | collectBuiltins(BuiltinRecord, Builtins); |
| 537 | } |
| 538 | NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins; |
| 539 | |
| 540 | auto AtomicBuiltins = ArrayRef(Builtins).slice(N: 0, M: NumAtomicBuiltins); |
| 541 | auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(N: NumPrefixedBuiltins); |
| 542 | auto PrefixedBuiltins = ArrayRef(Builtins).take_back(N: NumPrefixedBuiltins); |
| 543 | |
| 544 | // Collect strings into a table. |
| 545 | StringToOffsetTable Table; |
| 546 | Table.GetOrAddStringOffset(Str: "" ); |
| 547 | for (const auto &B : Builtins) { |
| 548 | Table.GetOrAddStringOffset(Str: B.Name); |
| 549 | Table.GetOrAddStringOffset(Str: B.Type); |
| 550 | Table.GetOrAddStringOffset(Str: B.Attributes); |
| 551 | if (B.BT == BuiltinType::TargetBuiltin) |
| 552 | Table.GetOrAddStringOffset(Str: B.BuiltinRecord->getValueAsString(FieldName: "Features" )); |
| 553 | } |
| 554 | |
| 555 | // Emit enumerators. |
| 556 | OS << R"c++( |
| 557 | #ifdef GET_BUILTIN_ENUMERATORS |
| 558 | )c++" ; |
| 559 | for (const auto &B : Builtins) |
| 560 | B.EmitEnumerator(OS); |
| 561 | OS << R"c++( |
| 562 | #endif // GET_BUILTIN_ENUMERATORS |
| 563 | )c++" ; |
| 564 | |
| 565 | // Emit a string table that can be referenced for these builtins. |
| 566 | OS << R"c++( |
| 567 | #ifdef GET_BUILTIN_STR_TABLE |
| 568 | )c++" ; |
| 569 | Table.EmitStringTableDef(OS, Name: "BuiltinStrings" ); |
| 570 | OS << R"c++( |
| 571 | #endif // GET_BUILTIN_STR_TABLE |
| 572 | )c++" ; |
| 573 | |
| 574 | // Emit a direct set of `Builtin::Info` initializers, first for the unprefixed |
| 575 | // builtins and then for the prefixed builtins. |
| 576 | OS << R"c++( |
| 577 | #ifdef GET_BUILTIN_INFOS |
| 578 | )c++" ; |
| 579 | for (const auto &B : UnprefixedBuiltins) |
| 580 | B.EmitInfo(OS, Table); |
| 581 | OS << R"c++( |
| 582 | #endif // GET_BUILTIN_INFOS |
| 583 | )c++" ; |
| 584 | |
| 585 | OS << R"c++( |
| 586 | #ifdef GET_BUILTIN_PREFIXED_INFOS |
| 587 | )c++" ; |
| 588 | for (const auto &B : PrefixedBuiltins) |
| 589 | B.EmitInfo(OS, Table); |
| 590 | OS << R"c++( |
| 591 | #endif // GET_BUILTIN_PREFIXED_INFOS |
| 592 | )c++" ; |
| 593 | |
| 594 | // Emit X-macros for the atomic builtins to support various custome patterns |
| 595 | // used exclusively with those builtins. |
| 596 | // |
| 597 | // FIXME: We should eventually move this to a separate file so that users |
| 598 | // don't need to include the full set of builtins. |
| 599 | OS << R"c++( |
| 600 | #ifdef ATOMIC_BUILTIN |
| 601 | )c++" ; |
| 602 | for (const auto &Builtin : AtomicBuiltins) { |
| 603 | Builtin.EmitXMacro(OS); |
| 604 | } |
| 605 | OS << R"c++( |
| 606 | #endif // ATOMIC_BUILTIN |
| 607 | #undef ATOMIC_BUILTIN |
| 608 | )c++" ; |
| 609 | } |
| 610 | |
| 611 | //===----------------------------------------------------------------------===// |
| 612 | // Builtin documentation emitter |
| 613 | //===----------------------------------------------------------------------===// |
| 614 | |
| 615 | /// Holds the data needed to emit documentation for a single builtin. |
| 616 | namespace { |
| 617 | struct BuiltinDocData { |
| 618 | const Record *Documentation = nullptr; |
| 619 | const Record *BuiltinRecord = nullptr; |
| 620 | std::string Heading; |
| 621 | |
| 622 | BuiltinDocData(const Record *D, const Record *B) |
| 623 | : Documentation(D), BuiltinRecord(B) { |
| 624 | // Use the Heading field if set, otherwise use the builtin's first |
| 625 | // spelling. |
| 626 | StringRef HeadingStr = D->getValueAsString(FieldName: "Heading" ); |
| 627 | if (HeadingStr.empty()) { |
| 628 | std::vector<StringRef> Spellings = |
| 629 | B->getValueAsListOfStrings(FieldName: "Spellings" ); |
| 630 | if (!Spellings.empty()) |
| 631 | Heading = Spellings[0].str(); |
| 632 | else |
| 633 | Heading = B->getName().str(); |
| 634 | } else { |
| 635 | Heading = HeadingStr.str(); |
| 636 | } |
| 637 | } |
| 638 | }; |
| 639 | } // namespace |
| 640 | |
| 641 | static void (const Record *Category, raw_ostream &OS) { |
| 642 | StringRef CategoryName = Category->getValueAsString(FieldName: "Name" ); |
| 643 | OS << "\n" << CategoryName << "\n" ; |
| 644 | for (size_t I = 0, E = CategoryName.size(); I < E; ++I) |
| 645 | OS << "=" ; |
| 646 | OS << "\n\n" ; |
| 647 | |
| 648 | StringRef CategoryContent = Category->getValueAsString(FieldName: "Content" ); |
| 649 | if (!CategoryContent.trim().empty()) |
| 650 | OS << CategoryContent.trim() << "\n\n" ; |
| 651 | } |
| 652 | |
| 653 | /// Split a parameter list string into individual parameter type strings, |
| 654 | /// respecting nested angle brackets (e.g. address_space<4>, _ExtVector<4, |
| 655 | /// float>). |
| 656 | static SmallVector<StringRef> splitParams(StringRef Params) { |
| 657 | SmallVector<StringRef> Result; |
| 658 | if (Params.empty()) |
| 659 | return Result; |
| 660 | |
| 661 | int Depth = 0; |
| 662 | size_t Start = 0; |
| 663 | for (size_t I = 0, E = Params.size(); I < E; ++I) { |
| 664 | if (Params[I] == '<') { |
| 665 | ++Depth; |
| 666 | } else if (Params[I] == '>') { |
| 667 | --Depth; |
| 668 | } else if (Params[I] == ',' && Depth == 0) { |
| 669 | Result.push_back(Elt: Params.substr(Start, N: I - Start).trim()); |
| 670 | Start = I + 1; |
| 671 | } |
| 672 | } |
| 673 | // Add the last parameter. |
| 674 | StringRef Last = Params.substr(Start).trim(); |
| 675 | if (!Last.empty()) |
| 676 | Result.push_back(Elt: Last); |
| 677 | return Result; |
| 678 | } |
| 679 | |
| 680 | static void writeBuiltinDocumentation(const BuiltinDocData &Doc, |
| 681 | raw_ostream &OS) { |
| 682 | // Write heading with '-' underline (subsection). |
| 683 | std::string HeadingText = "``" + Doc.Heading + "``" ; |
| 684 | OS << HeadingText << "\n" ; |
| 685 | for (size_t I = 0, E = HeadingText.size(); I < E; ++I) |
| 686 | OS << "-" ; |
| 687 | OS << "\n\n" ; |
| 688 | |
| 689 | // Write prototype as a code block. |
| 690 | StringRef Prototype = Doc.BuiltinRecord->getValueAsString(FieldName: "Prototype" ); |
| 691 | if (!Prototype.empty()) { |
| 692 | std::vector<StringRef> Spellings = |
| 693 | Doc.BuiltinRecord->getValueAsListOfStrings(FieldName: "Spellings" ); |
| 694 | StringRef Name = |
| 695 | Spellings.empty() ? Doc.BuiltinRecord->getName() : Spellings[0]; |
| 696 | |
| 697 | // Split prototype into return type and params at the first '('. |
| 698 | size_t ParenPos = Prototype.find(C: '('); |
| 699 | if (ParenPos != StringRef::npos) { |
| 700 | StringRef RetType = Prototype.substr(Start: 0, N: ParenPos).rtrim(); |
| 701 | StringRef ParamStr = |
| 702 | Prototype.substr(Start: ParenPos + 1, N: Prototype.size() - ParenPos - 2); |
| 703 | |
| 704 | OS << "**Prototype:**\n\n" ; |
| 705 | OS << ".. code-block:: c\n\n" ; |
| 706 | OS << " " << RetType << " " << Name << "(" ; |
| 707 | |
| 708 | std::vector<StringRef> ArgNames = |
| 709 | Doc.BuiltinRecord->getValueAsListOfStrings(FieldName: "ArgNames" ); |
| 710 | if (!ArgNames.empty()) { |
| 711 | SmallVector<StringRef> ParamTypes = splitParams(Params: ParamStr); |
| 712 | bool IsVariadic = !ParamTypes.empty() && ParamTypes.back() == "..." ; |
| 713 | size_t NamedParams = ParamTypes.size() - (IsVariadic ? 1 : 0); |
| 714 | if (ArgNames.size() != NamedParams) |
| 715 | PrintFatalError(ErrorLoc: Doc.BuiltinRecord->getLoc(), |
| 716 | Msg: "number of ArgNames (" + Twine(ArgNames.size()) + |
| 717 | ") does not match number of prototype " |
| 718 | "parameters (" + |
| 719 | Twine(NamedParams) + ")" ); |
| 720 | for (size_t I = 0, E = ParamTypes.size(); I < E; ++I) { |
| 721 | if (I > 0) |
| 722 | OS << ", " ; |
| 723 | if (I < NamedParams) |
| 724 | OS << ParamTypes[I] << " " << ArgNames[I]; |
| 725 | else |
| 726 | OS << ParamTypes[I]; |
| 727 | } |
| 728 | } else { |
| 729 | OS << ParamStr; |
| 730 | } |
| 731 | |
| 732 | OS << ")\n\n" ; |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | // Write target features if this is a TargetBuiltin with features. |
| 737 | if (Doc.BuiltinRecord->isSubClassOf(Name: "TargetBuiltin" )) { |
| 738 | StringRef Features = Doc.BuiltinRecord->getValueAsString(FieldName: "Features" ); |
| 739 | if (!Features.empty()) |
| 740 | OS << "**Target Features:** " << Features << "\n\n" ; |
| 741 | } |
| 742 | |
| 743 | // Write documentation content. |
| 744 | StringRef Content = Doc.Documentation->getValueAsString(FieldName: "Content" ); |
| 745 | OS << Content.trim() << "\n\n\n" ; |
| 746 | } |
| 747 | |
| 748 | void clang::EmitClangBuiltinDocs(const RecordKeeper &Records, raw_ostream &OS) { |
| 749 | // Get the documentation introduction paragraph. |
| 750 | const Record *Doc = Records.getDef(Name: "GlobalDocumentation" ); |
| 751 | if (!Doc) { |
| 752 | PrintFatalError(Msg: "The GlobalDocumentation top-level definition is missing, " |
| 753 | "no documentation will be generated." ); |
| 754 | } |
| 755 | |
| 756 | OS << Doc->getValueAsString(FieldName: "Intro" ) << "\n" ; |
| 757 | |
| 758 | // Gather documentation from each builtin, grouped by category. |
| 759 | llvm::MapVector<const Record *, std::vector<BuiltinDocData>> SplitDocs; |
| 760 | |
| 761 | for (const Record *B : Records.getAllDerivedDefinitions(ClassName: "Builtin" )) { |
| 762 | for (const Record *D : B->getValueAsListOfDefs(FieldName: "Documentation" )) { |
| 763 | const Record *Category = D->getValueAsDef(FieldName: "Category" ); |
| 764 | StringRef Cat = Category->getValueAsString(FieldName: "Name" ); |
| 765 | // Skip builtins that are explicitly internal-only. |
| 766 | if (Cat == "InternalOnly" ) |
| 767 | continue; |
| 768 | SplitDocs[Category].emplace_back(args&: D, args&: B); |
| 769 | } |
| 770 | } |
| 771 | |
| 772 | // Sort categories alphabetically by name for deterministic output. |
| 773 | llvm::sort(C&: SplitDocs, Comp: [](const auto &A, const auto &B) { |
| 774 | return A.first->getValueAsString("Name" ) < |
| 775 | B.first->getValueAsString("Name" ); |
| 776 | }); |
| 777 | |
| 778 | // Write out each category and its builtins. |
| 779 | for (auto &[Category, Docs] : SplitDocs) { |
| 780 | writeCategoryHeader(Category, OS); |
| 781 | |
| 782 | // Sort entries alphabetically by heading. |
| 783 | llvm::sort(C&: Docs, Comp: [](const BuiltinDocData &A, const BuiltinDocData &B) { |
| 784 | return A.Heading < B.Heading; |
| 785 | }); |
| 786 | |
| 787 | for (const BuiltinDocData &D : Docs) |
| 788 | writeBuiltinDocumentation(Doc: D, OS); |
| 789 | } |
| 790 | } |
| 791 | |