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 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
375std::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
409Builtin 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
420struct TemplateInsts {
421 std::vector<std::string> Substitution;
422 std::vector<std::string> Affix;
423 bool IsPrefix;
424};
425
426TemplateInsts 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
443void 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
482void 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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/utils/TableGen/ClangBuiltinsEmitter.cpp