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
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(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 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 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
153class PrototypeParser {
154public:
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
163private:
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 size_t Pos = Current.find(C: '<');
204 int Depth = 1;
205
206 // There may be a nested address_space<...> modifier on the type.
207 while (Depth > 0 && ++Pos < Current.size()) {
208 if (Current[Pos] == '<')
209 ++Depth;
210 else if (Current[Pos] == '>')
211 --Depth;
212 }
213
214 const size_t EndTemplate = Pos;
215 ParseType(T: Current.substr(Start: 0, N: EndTemplate + 1));
216 // Move the prototype beyond _ExtVector<...>
217 I += EndTemplate + 1;
218 continue;
219 }
220
221 // We know that we are past _ExtVector, therefore the first seen
222 // comma is the boundary of a parameter in the prototype.
223 if (size_t CommaPos = Current.find(C: ',', From: 0)) {
224 if (CommaPos != StringRef::npos) {
225 StringRef T = Current.substr(Start: 0, N: CommaPos);
226 ParseType(T);
227 // Move the prototype beyond the comma.
228 I += CommaPos + 1;
229 continue;
230 }
231 }
232
233 // No more commas, parse final parameter.
234 ParseType(T: Current);
235 I = end;
236 }
237 }
238
239 void ParseType(StringRef T) {
240 T = T.trim();
241
242 auto ConsumeAddrSpace = [&]() -> std::optional<unsigned> {
243 T = T.trim();
244 if (!T.consume_back(Suffix: ">"))
245 return std::nullopt;
246
247 auto Open = T.find_last_of(C: '<');
248 if (Open == StringRef::npos)
249 PrintFatalError(ErrorLoc: Loc, Msg: "Mismatched angle-brackets in type");
250
251 StringRef ArgStr = T.substr(Start: Open + 1);
252 T = T.slice(Start: 0, End: Open);
253 if (!T.consume_back(Suffix: "address_space"))
254 PrintFatalError(ErrorLoc: Loc,
255 Msg: "Only `address_space<N>` supported as a parameterized "
256 "pointer or reference type qualifier");
257
258 unsigned Number = 0;
259 if (ArgStr.getAsInteger(Radix: 10, Result&: Number))
260 PrintFatalError(
261 ErrorLoc: Loc, Msg: "Expected an integer argument to the address_space qualifier");
262 return Number;
263 };
264
265 if (T.consume_back(Suffix: "*")) {
266 // Pointers may have an address space qualifier immediately before them.
267 std::optional<unsigned> AS = ConsumeAddrSpace();
268 ParseType(T);
269 Type += "*";
270 if (AS)
271 Type += std::to_string(val: *AS);
272 } else if (T.consume_back(Suffix: "const")) {
273 ParseType(T);
274 Type += "C";
275 } else if (T.consume_back(Suffix: "volatile")) {
276 ParseType(T);
277 Type += "D";
278 } else if (T.consume_back(Suffix: "restrict")) {
279 ParseType(T);
280 Type += "R";
281 } else if (T.consume_back(Suffix: "&")) {
282 // References may have an address space qualifier immediately before them.
283 std::optional<unsigned> AS = ConsumeAddrSpace();
284 ParseType(T);
285 Type += "&";
286 if (AS)
287 Type += std::to_string(val: *AS);
288 } else if (T.consume_back(Suffix: ")")) {
289 ParseType(T);
290 Type += "&";
291 } else if (EnableOpenCLLong && T.consume_front(Prefix: "long long")) {
292 Type += "O";
293 ParseType(T);
294 } else if (T.consume_front(Prefix: "long")) {
295 Type += "L";
296 ParseType(T);
297 } else if (T.consume_front(Prefix: "signed")) {
298 Type += "S";
299 ParseType(T);
300 } else if (T.consume_front(Prefix: "unsigned")) {
301 Type += "U";
302 ParseType(T);
303 } else if (T.consume_front(Prefix: "_Complex")) {
304 Type += "X";
305 ParseType(T);
306 } else if (T.consume_front(Prefix: "_Constant")) {
307 Type += "I";
308 ParseType(T);
309 } else if (T.consume_front(Prefix: "T")) {
310 if (Substitution.empty())
311 PrintFatalError(ErrorLoc: Loc, Msg: "Not a template");
312 ParseType(T: Substitution);
313 } else if (auto IsExt = T.consume_front(Prefix: "_ExtVector");
314 IsExt || T.consume_front(Prefix: "_Vector")) {
315 // Clang extended vector types are mangled as follows:
316 //
317 // '_ExtVector<' <lanes> ',' <scalar type> '>'
318
319 // Before parsing T(=<scalar type>), make sure the syntax of
320 // `_ExtVector<N, T>` is correct...
321 if (!T.consume_front(Prefix: "<"))
322 PrintFatalError(ErrorLoc: Loc, Msg: "Expected '<' after '_ExtVector'");
323 unsigned long long Lanes;
324 if (consumeUnsignedInteger(Str&: T, Radix: 10, Result&: Lanes))
325 PrintFatalError(ErrorLoc: Loc, Msg: "Expected number of lanes after '_ExtVector<'");
326 Type += (IsExt ? "E" : "V") + std::to_string(val: Lanes);
327 if (!T.consume_front(Prefix: ","))
328 PrintFatalError(ErrorLoc: Loc,
329 Msg: "Expected ',' after number of lanes in '_ExtVector<'");
330 if (!T.consume_back(Suffix: ">"))
331 PrintFatalError(
332 ErrorLoc: Loc, Msg: "Expected '>' after scalar type in '_ExtVector<N, type>'");
333
334 // ...all good, we can check if we have a valid `<scalar type>`.
335 ParseType(T);
336 } else {
337 auto ReturnTypeVal = StringSwitch<std::string>(T)
338 .Case(S: "__builtin_va_list_ref", Value: "A")
339 .Case(S: "__builtin_va_list", Value: "a")
340 .Case(S: "__float128", Value: "LLd")
341 .Case(S: "__fp16", Value: "h")
342 .Case(S: "__hlsl_resource_t", Value: "Qr")
343 .Case(S: "__amdgpu_buffer_rsrc_t", Value: "Qb")
344 .Case(S: "__amdgpu_texture_t", Value: "Qt")
345 .Case(S: "__int128_t", Value: "LLLi")
346 .Case(S: "_Float16", Value: "x")
347 .Case(S: "__bf16", Value: "y")
348 .Case(S: "bool", Value: "b")
349 .Case(S: "char", Value: "c")
350 .Case(S: "constant_CFString", Value: "F")
351 .Case(S: "double", Value: "d")
352 .Case(S: "FILE", Value: "P")
353 .Case(S: "float", Value: "f")
354 .Case(S: "id", Value: "G")
355 .Case(S: "int", Value: "i")
356 .Case(S: "int32_t", Value: "Zi")
357 .Case(S: "int64_t", Value: "Wi")
358 .Case(S: "jmp_buf", Value: "J")
359 .Case(S: "msint32_t", Value: "Ni")
360 .Case(S: "msuint32_t", Value: "UNi")
361 .Case(S: "objc_super", Value: "M")
362 .Case(S: "pid_t", Value: "p")
363 .Case(S: "ptrdiff_t", Value: "Y")
364 .Case(S: "SEL", Value: "H")
365 .Case(S: "short", Value: "s")
366 .Case(S: "sigjmp_buf", Value: "SJ")
367 .Case(S: "size_t", Value: "z")
368 .Case(S: "ucontext_t", Value: "K")
369 .Case(S: "uint32_t", Value: "UZi")
370 .Case(S: "uint64_t", Value: "UWi")
371 .Case(S: "void", Value: "v")
372 .Case(S: "wchar_t", Value: "w")
373 .Case(S: "...", Value: ".")
374 .Default(Value: "error");
375 if (ReturnTypeVal == "error")
376 PrintFatalError(ErrorLoc: Loc, Msg: "Unknown Type: " + T);
377 Type += ReturnTypeVal;
378 }
379 }
380
381 SMLoc Loc;
382 StringRef Substitution;
383 bool EnableOpenCLLong;
384 std::string Type;
385};
386
387std::string renderAttributes(const Record *Builtin, BuiltinType BT) {
388 std::string Attributes;
389 raw_string_ostream OS(Attributes);
390 if (Builtin->isSubClassOf(Name: "LibBuiltin")) {
391 if (BT == BuiltinType::LibBuiltin) {
392 OS << 'f';
393 } else {
394 OS << 'F';
395 if (Builtin->getValueAsBit(FieldName: "OnlyBuiltinPrefixedAliasIsConstexpr"))
396 OS << 'E';
397 }
398 }
399
400 if (auto NS = Builtin->getValueAsOptionalString(FieldName: "Namespace")) {
401 if (NS != "std")
402 PrintFatalError(ErrorLoc: Builtin->getFieldLoc(FieldName: "Namespace"), Msg: "Unknown namespace: ");
403 OS << "z";
404 }
405
406 for (const auto *Attr : Builtin->getValueAsListOfDefs(FieldName: "Attributes")) {
407 OS << Attr->getValueAsString(FieldName: "Mangling");
408 if (Attr->isSubClassOf(Name: "IndexedAttribute")) {
409 OS << ':' << Attr->getValueAsInt(FieldName: "Index") << ':';
410 } else if (Attr->isSubClassOf(Name: "MultiIndexAttribute")) {
411 OS << '<';
412 llvm::ListSeparator Sep(",");
413 for (int64_t Index : Attr->getValueAsListOfInts(FieldName: "Indices"))
414 OS << Sep << Index;
415 OS << '>';
416 }
417 }
418 return Attributes;
419}
420
421Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord,
422 Twine Spelling, BuiltinType BT) {
423 Builtin B;
424 B.BT = BT;
425 B.Name = Spelling.str();
426 B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString();
427 B.Attributes = renderAttributes(Builtin: BuiltinRecord, BT);
428 B.BuiltinRecord = BuiltinRecord;
429 return B;
430}
431
432struct TemplateInsts {
433 std::vector<std::string> Substitution;
434 std::vector<std::string> Affix;
435 bool IsPrefix;
436};
437
438TemplateInsts getTemplateInsts(const Record *R) {
439 TemplateInsts temp;
440 auto Substitutions = R->getValueAsListOfStrings(FieldName: "Substitutions");
441 auto Affixes = R->getValueAsListOfStrings(FieldName: "Affixes");
442 temp.IsPrefix = R->getValueAsBit(FieldName: "AsPrefix");
443
444 if (Substitutions.size() != Affixes.size())
445 PrintFatalError(ErrorLoc: R->getLoc(), Msg: "Substitutions and affixes "
446 "don't have the same lengths");
447
448 for (auto [Affix, Substitution] : zip(t&: Affixes, u&: Substitutions)) {
449 temp.Substitution.emplace_back(args&: Substitution);
450 temp.Affix.emplace_back(args&: Affix);
451 }
452 return temp;
453}
454
455void collectBuiltins(const Record *BuiltinRecord,
456 SmallVectorImpl<Builtin> &Builtins) {
457 TemplateInsts Templates = {};
458 if (BuiltinRecord->isSubClassOf(Name: "Template")) {
459 Templates = getTemplateInsts(R: BuiltinRecord);
460 } else {
461 Templates.Affix.emplace_back();
462 Templates.Substitution.emplace_back();
463 }
464
465 for (auto [Substitution, Affix] :
466 zip(t&: Templates.Substitution, u&: Templates.Affix)) {
467 for (StringRef Spelling :
468 BuiltinRecord->getValueAsListOfStrings(FieldName: "Spellings")) {
469 auto FullSpelling =
470 (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str();
471 BuiltinType BT = BuiltinType::Builtin;
472 if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin")) {
473 BT = BuiltinType::AtomicBuiltin;
474 } else if (BuiltinRecord->isSubClassOf(Name: "LangBuiltin")) {
475 BT = BuiltinType::LangBuiltin;
476 } else if (BuiltinRecord->isSubClassOf(Name: "TargetLibBuiltin")) {
477 BT = BuiltinType::TargetLibBuiltin;
478 } else if (BuiltinRecord->isSubClassOf(Name: "TargetBuiltin")) {
479 BT = BuiltinType::TargetBuiltin;
480 } else if (BuiltinRecord->isSubClassOf(Name: "LibBuiltin")) {
481 BT = BuiltinType::LibBuiltin;
482 if (BuiltinRecord->getValueAsBit(FieldName: "AddBuiltinPrefixedAlias"))
483 Builtins.push_back(Elt: buildBuiltin(
484 Substitution, BuiltinRecord,
485 Spelling: std::string("__builtin_") + FullSpelling, BT: BuiltinType::Builtin));
486 }
487 Builtins.push_back(
488 Elt: buildBuiltin(Substitution, BuiltinRecord, Spelling: FullSpelling, BT));
489 }
490 }
491}
492} // namespace
493
494void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) {
495 emitSourceFileHeader(Desc: "List of builtins that Clang recognizes", OS);
496
497 SmallVector<Builtin> Builtins;
498 // AtomicBuiltins are order dependent. Emit them first to make manual checking
499 // easier and so we can build a special atomic builtin X-macro.
500 for (const auto *BuiltinRecord :
501 Records.getAllDerivedDefinitions(ClassName: "AtomicBuiltin"))
502 collectBuiltins(BuiltinRecord, Builtins);
503 unsigned NumAtomicBuiltins = Builtins.size();
504
505 for (const auto *BuiltinRecord :
506 Records.getAllDerivedDefinitions(ClassName: "Builtin")) {
507 if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin"))
508 continue;
509 // Prefixed builtins are also special and we emit them last so they can have
510 // their own representation that skips the prefix.
511 if (BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix"))
512 continue;
513
514 collectBuiltins(BuiltinRecord, Builtins);
515 }
516
517 // Now collect (and count) the prefixed builtins.
518 unsigned NumPrefixedBuiltins = Builtins.size();
519 const Record *FirstPrefix = nullptr;
520 for (const auto *BuiltinRecord :
521 Records.getAllDerivedDefinitions(ClassName: "Builtin")) {
522 auto *Prefix = BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix");
523 if (!Prefix)
524 continue;
525
526 if (!FirstPrefix)
527 FirstPrefix = Prefix;
528 assert(Prefix == FirstPrefix &&
529 "Multiple distinct prefixes which is not currently supported!");
530 assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin") &&
531 "Cannot require a name prefix for an atomic builtin.");
532 collectBuiltins(BuiltinRecord, Builtins);
533 }
534 NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins;
535
536 auto AtomicBuiltins = ArrayRef(Builtins).slice(N: 0, M: NumAtomicBuiltins);
537 auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(N: NumPrefixedBuiltins);
538 auto PrefixedBuiltins = ArrayRef(Builtins).take_back(N: NumPrefixedBuiltins);
539
540 // Collect strings into a table.
541 StringToOffsetTable Table;
542 Table.GetOrAddStringOffset(Str: "");
543 for (const auto &B : Builtins) {
544 Table.GetOrAddStringOffset(Str: B.Name);
545 Table.GetOrAddStringOffset(Str: B.Type);
546 Table.GetOrAddStringOffset(Str: B.Attributes);
547 if (B.BT == BuiltinType::TargetBuiltin)
548 Table.GetOrAddStringOffset(Str: B.BuiltinRecord->getValueAsString(FieldName: "Features"));
549 }
550
551 // Emit enumerators.
552 OS << R"c++(
553#ifdef GET_BUILTIN_ENUMERATORS
554)c++";
555 for (const auto &B : Builtins)
556 B.EmitEnumerator(OS);
557 OS << R"c++(
558#endif // GET_BUILTIN_ENUMERATORS
559)c++";
560
561 // Emit a string table that can be referenced for these builtins.
562 OS << R"c++(
563#ifdef GET_BUILTIN_STR_TABLE
564)c++";
565 Table.EmitStringTableDef(OS, Name: "BuiltinStrings");
566 OS << R"c++(
567#endif // GET_BUILTIN_STR_TABLE
568)c++";
569
570 // Emit a direct set of `Builtin::Info` initializers, first for the unprefixed
571 // builtins and then for the prefixed builtins.
572 OS << R"c++(
573#ifdef GET_BUILTIN_INFOS
574)c++";
575 for (const auto &B : UnprefixedBuiltins)
576 B.EmitInfo(OS, Table);
577 OS << R"c++(
578#endif // GET_BUILTIN_INFOS
579)c++";
580
581 OS << R"c++(
582#ifdef GET_BUILTIN_PREFIXED_INFOS
583)c++";
584 for (const auto &B : PrefixedBuiltins)
585 B.EmitInfo(OS, Table);
586 OS << R"c++(
587#endif // GET_BUILTIN_PREFIXED_INFOS
588)c++";
589
590 // Emit X-macros for the atomic builtins to support various custome patterns
591 // used exclusively with those builtins.
592 //
593 // FIXME: We should eventually move this to a separate file so that users
594 // don't need to include the full set of builtins.
595 OS << R"c++(
596#ifdef ATOMIC_BUILTIN
597)c++";
598 for (const auto &Builtin : AtomicBuiltins) {
599 Builtin.EmitXMacro(OS);
600 }
601 OS << R"c++(
602#endif // ATOMIC_BUILTIN
603#undef ATOMIC_BUILTIN
604)c++";
605}
606