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
24using namespace llvm;
25
26namespace {
27enum class BuiltinType {
28 Builtin,
29 AtomicBuiltin,
30 LibBuiltin,
31 LangBuiltin,
32 TargetBuiltin,
33 TargetLibBuiltin,
34};
35
36class HeaderNameParser {
37public:
38 HeaderNameParser(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 Print(raw_ostream &OS) const { OS << HeaderName; }
50
51private:
52 std::string HeaderName;
53};
54
55struct 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
155class PrototypeParser {
156public:
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
165private:
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 if (!T.empty())
278 ParseType(T);
279 Type += "C";
280 } else if (T.consume_back(Suffix: "volatile")) {
281 if (!T.empty())
282 ParseType(T);
283 Type += "D";
284 } else if (T.consume_back(Suffix: "restrict")) {
285 if (!T.empty())
286 ParseType(T);
287 Type += "R";
288 } else if (T.consume_back(Suffix: "&")) {
289 // References may have an address space qualifier immediately before them.
290 std::optional<unsigned> AS = ConsumeAddrSpace();
291 ParseType(T);
292 Type += "&";
293 if (AS)
294 Type += std::to_string(val: *AS);
295 } else if (T.consume_back(Suffix: ")")) {
296 ParseType(T);
297 Type += "&";
298 } else if (EnableOpenCLLong && T.consume_front(Prefix: "long long")) {
299 Type += "O";
300 ParseType(T);
301 } else if (T.consume_front(Prefix: "long")) {
302 Type += "L";
303 ParseType(T);
304 } else if (T.consume_front(Prefix: "signed")) {
305 Type += "S";
306 ParseType(T);
307 } else if (T.consume_front(Prefix: "unsigned")) {
308 Type += "U";
309 ParseType(T);
310 } else if (T.consume_front(Prefix: "_Complex")) {
311 Type += "X";
312 ParseType(T);
313 } else if (T.consume_front(Prefix: "_Constant")) {
314 Type += "I";
315 ParseType(T);
316 } else if (T.consume_front(Prefix: "T")) {
317 if (Substitution.empty())
318 PrintFatalError(ErrorLoc: Loc, Msg: "Not a template");
319 ParseType(T: Substitution);
320 } else if (auto IsExt = T.consume_front(Prefix: "_ExtVector");
321 IsExt || T.consume_front(Prefix: "_Vector")) {
322 // Clang extended vector types are mangled as follows:
323 //
324 // '_ExtVector<' <lanes> ',' <scalar type> '>'
325
326 // Before parsing T(=<scalar type>), make sure the syntax of
327 // `_ExtVector<N, T>` is correct...
328 if (!T.consume_front(Prefix: "<"))
329 PrintFatalError(ErrorLoc: Loc, Msg: "Expected '<' after '_ExtVector'");
330 unsigned long long Lanes;
331 if (consumeUnsignedInteger(Str&: T, Radix: 10, Result&: Lanes))
332 PrintFatalError(ErrorLoc: Loc, Msg: "Expected number of lanes after '_ExtVector<'");
333 Type += (IsExt ? "E" : "V") + std::to_string(val: Lanes);
334 if (!T.consume_front(Prefix: ","))
335 PrintFatalError(ErrorLoc: Loc,
336 Msg: "Expected ',' after number of lanes in '_ExtVector<'");
337 if (!T.consume_back(Suffix: ">"))
338 PrintFatalError(
339 ErrorLoc: Loc, Msg: "Expected '>' after scalar type in '_ExtVector<N, type>'");
340
341 // ...all good, we can check if we have a valid `<scalar type>`.
342 ParseType(T);
343 } else {
344 auto ReturnTypeVal = StringSwitch<std::string>(T)
345 .Case(S: "__builtin_va_list_ref", Value: "A")
346 .Case(S: "__builtin_va_list", Value: "a")
347 .Case(S: "__float128", Value: "LLd")
348 .Case(S: "__fp16", Value: "h")
349 .Case(S: "__hlsl_resource_t", Value: "Qr")
350 .Case(S: "__amdgpu_buffer_rsrc_t", Value: "Qb")
351 .Case(S: "__amdgpu_feature_predicate_t", Value: "Qc")
352 .Case(S: "__amdgpu_texture_t", Value: "Qt")
353 .Case(S: "__int128_t", Value: "LLLi")
354 .Case(S: "__uint128_t", Value: "ULLLi")
355 .Case(S: "_Float16", Value: "x")
356 .Case(S: "__bf16", Value: "y")
357 .Case(S: "bool", Value: "b")
358 .Case(S: "char", Value: "c")
359 .Case(S: "constant_CFString", Value: "F")
360 .Case(S: "double", Value: "d")
361 .Case(S: "FILE", Value: "P")
362 .Case(S: "float", Value: "f")
363 .Case(S: "id", Value: "G")
364 .Case(S: "int", Value: "i")
365 .Case(S: "int8_t", Value: "Bi")
366 .Case(S: "int16_t", Value: "Ti")
367 .Case(S: "int32_t", Value: "Zi")
368 .Case(S: "int64_t", Value: "Wi")
369 .Case(S: "jmp_buf", Value: "J")
370 .Case(S: "msint32_t", Value: "Ni")
371 .Case(S: "msuint32_t", Value: "UNi")
372 .Case(S: "objc_super", Value: "M")
373 .Case(S: "pid_t", Value: "p")
374 .Case(S: "ptrdiff_t", Value: "Y")
375 .Case(S: "SEL", Value: "H")
376 .Case(S: "short", Value: "s")
377 .Case(S: "sigjmp_buf", Value: "SJ")
378 .Case(S: "size_t", Value: "z")
379 .Case(S: "ucontext_t", Value: "K")
380 .Case(S: "uint8_t", Value: "UBi")
381 .Case(S: "uint16_t", Value: "UTi")
382 .Case(S: "uint32_t", Value: "UZi")
383 .Case(S: "uint64_t", Value: "UWi")
384 .Case(S: "uint_least8_t", Value: "lUBi")
385 .Case(S: "uint_least16_t", Value: "lUTi")
386 .Case(S: "uint_least32_t", Value: "lUZi")
387 .Case(S: "uint_least64_t", Value: "lUWi")
388 .Case(S: "int_least8_t", Value: "lBi")
389 .Case(S: "int_least16_t", Value: "lTi")
390 .Case(S: "int_least32_t", Value: "lZi")
391 .Case(S: "int_least64_t", Value: "lWi")
392 .Case(S: "void", Value: "v")
393 .Case(S: "wchar_t", Value: "w")
394 .Case(S: "...", Value: ".")
395 .Default(Value: "error");
396 if (ReturnTypeVal == "error")
397 PrintFatalError(ErrorLoc: Loc, Msg: "Unknown Type: " + T);
398 Type += ReturnTypeVal;
399 }
400 }
401
402 SMLoc Loc;
403 StringRef Substitution;
404 bool EnableOpenCLLong;
405 std::string Type;
406};
407
408std::string renderAttributes(const Record *Builtin, BuiltinType BT) {
409 std::string Attributes;
410 raw_string_ostream OS(Attributes);
411 if (Builtin->isSubClassOf(Name: "LibBuiltin")) {
412 if (BT == BuiltinType::LibBuiltin) {
413 OS << 'f';
414 } else {
415 OS << 'F';
416 if (Builtin->getValueAsBit(FieldName: "OnlyBuiltinPrefixedAliasIsConstexpr"))
417 OS << 'E';
418 }
419 }
420
421 if (auto NS = Builtin->getValueAsOptionalString(FieldName: "Namespace")) {
422 if (NS != "std")
423 PrintFatalError(ErrorLoc: Builtin->getFieldLoc(FieldName: "Namespace"), Msg: "Unknown namespace: ");
424 OS << "z";
425 }
426
427 for (const auto *Attr : Builtin->getValueAsListOfDefs(FieldName: "Attributes")) {
428 OS << Attr->getValueAsString(FieldName: "Mangling");
429 if (Attr->isSubClassOf(Name: "IndexedAttribute")) {
430 OS << ':' << Attr->getValueAsInt(FieldName: "Index") << ':';
431 } else if (Attr->isSubClassOf(Name: "MultiIndexAttribute")) {
432 OS << '<';
433 llvm::ListSeparator Sep(",");
434 for (int64_t Index : Attr->getValueAsListOfInts(FieldName: "Indices"))
435 OS << Sep << Index;
436 OS << '>';
437 }
438 }
439 return Attributes;
440}
441
442Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord,
443 Twine Spelling, BuiltinType BT) {
444 Builtin B;
445 B.BT = BT;
446 B.Name = Spelling.str();
447 B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString();
448 B.Attributes = renderAttributes(Builtin: BuiltinRecord, BT);
449 B.BuiltinRecord = BuiltinRecord;
450 return B;
451}
452
453struct TemplateInsts {
454 std::vector<std::string> Substitution;
455 std::vector<std::string> Affix;
456 bool IsPrefix;
457};
458
459TemplateInsts getTemplateInsts(const Record *R) {
460 TemplateInsts temp;
461 auto Substitutions = R->getValueAsListOfStrings(FieldName: "Substitutions");
462 auto Affixes = R->getValueAsListOfStrings(FieldName: "Affixes");
463 temp.IsPrefix = R->getValueAsBit(FieldName: "AsPrefix");
464
465 if (Substitutions.size() != Affixes.size())
466 PrintFatalError(ErrorLoc: R->getLoc(), Msg: "Substitutions and affixes "
467 "don't have the same lengths");
468
469 for (auto [Affix, Substitution] : zip(t&: Affixes, u&: Substitutions)) {
470 temp.Substitution.emplace_back(args&: Substitution);
471 temp.Affix.emplace_back(args&: Affix);
472 }
473 return temp;
474}
475
476void collectBuiltins(const Record *BuiltinRecord,
477 SmallVectorImpl<Builtin> &Builtins) {
478 TemplateInsts Templates = {};
479 if (BuiltinRecord->isSubClassOf(Name: "Template")) {
480 Templates = getTemplateInsts(R: BuiltinRecord);
481 } else {
482 Templates.Affix.emplace_back();
483 Templates.Substitution.emplace_back();
484 }
485
486 for (auto [Substitution, Affix] :
487 zip(t&: Templates.Substitution, u&: Templates.Affix)) {
488 for (StringRef Spelling :
489 BuiltinRecord->getValueAsListOfStrings(FieldName: "Spellings")) {
490 auto FullSpelling =
491 (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str();
492 BuiltinType BT = BuiltinType::Builtin;
493 if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin")) {
494 BT = BuiltinType::AtomicBuiltin;
495 } else if (BuiltinRecord->isSubClassOf(Name: "LangBuiltin")) {
496 BT = BuiltinType::LangBuiltin;
497 } else if (BuiltinRecord->isSubClassOf(Name: "TargetLibBuiltin")) {
498 BT = BuiltinType::TargetLibBuiltin;
499 } else if (BuiltinRecord->isSubClassOf(Name: "TargetBuiltin")) {
500 BT = BuiltinType::TargetBuiltin;
501 } else if (BuiltinRecord->isSubClassOf(Name: "LibBuiltin")) {
502 BT = BuiltinType::LibBuiltin;
503 if (BuiltinRecord->getValueAsBit(FieldName: "AddBuiltinPrefixedAlias"))
504 Builtins.push_back(Elt: buildBuiltin(
505 Substitution, BuiltinRecord,
506 Spelling: std::string("__builtin_") + FullSpelling, BT: BuiltinType::Builtin));
507 }
508 Builtins.push_back(
509 Elt: buildBuiltin(Substitution, BuiltinRecord, Spelling: FullSpelling, BT));
510 }
511 }
512}
513} // namespace
514
515void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) {
516 emitSourceFileHeader(Desc: "List of builtins that Clang recognizes", OS);
517
518 SmallVector<Builtin> Builtins;
519 // AtomicBuiltins are order dependent. Emit them first to make manual checking
520 // easier and so we can build a special atomic builtin X-macro.
521 for (const auto *BuiltinRecord :
522 Records.getAllDerivedDefinitions(ClassName: "AtomicBuiltin"))
523 collectBuiltins(BuiltinRecord, Builtins);
524 unsigned NumAtomicBuiltins = Builtins.size();
525
526 for (const auto *BuiltinRecord :
527 Records.getAllDerivedDefinitions(ClassName: "Builtin")) {
528 if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin"))
529 continue;
530 // Prefixed builtins are also special and we emit them last so they can have
531 // their own representation that skips the prefix.
532 if (BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix"))
533 continue;
534
535 collectBuiltins(BuiltinRecord, Builtins);
536 }
537
538 // Now collect (and count) the prefixed builtins.
539 unsigned NumPrefixedBuiltins = Builtins.size();
540 const Record *FirstPrefix = nullptr;
541 for (const auto *BuiltinRecord :
542 Records.getAllDerivedDefinitions(ClassName: "Builtin")) {
543 auto *Prefix = BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix");
544 if (!Prefix)
545 continue;
546
547 if (!FirstPrefix)
548 FirstPrefix = Prefix;
549 assert(Prefix == FirstPrefix &&
550 "Multiple distinct prefixes which is not currently supported!");
551 assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin") &&
552 "Cannot require a name prefix for an atomic builtin.");
553 collectBuiltins(BuiltinRecord, Builtins);
554 }
555 NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins;
556
557 auto AtomicBuiltins = ArrayRef(Builtins).slice(N: 0, M: NumAtomicBuiltins);
558 auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(N: NumPrefixedBuiltins);
559 auto PrefixedBuiltins = ArrayRef(Builtins).take_back(N: NumPrefixedBuiltins);
560
561 // Collect strings into a table.
562 StringToOffsetTable Table;
563 Table.GetOrAddStringOffset(Str: "");
564 for (const auto &B : Builtins) {
565 Table.GetOrAddStringOffset(Str: B.Name);
566 Table.GetOrAddStringOffset(Str: B.Type);
567 Table.GetOrAddStringOffset(Str: B.Attributes);
568 if (B.BT == BuiltinType::TargetBuiltin)
569 Table.GetOrAddStringOffset(Str: B.BuiltinRecord->getValueAsString(FieldName: "Features"));
570 }
571
572 // Emit enumerators.
573 OS << R"c++(
574#ifdef GET_BUILTIN_ENUMERATORS
575)c++";
576 for (const auto &B : Builtins)
577 B.EmitEnumerator(OS);
578 OS << R"c++(
579#endif // GET_BUILTIN_ENUMERATORS
580)c++";
581
582 // Emit a string table that can be referenced for these builtins.
583 OS << R"c++(
584#ifdef GET_BUILTIN_STR_TABLE
585)c++";
586 Table.EmitStringTableDef(OS, Name: "BuiltinStrings");
587 OS << R"c++(
588#endif // GET_BUILTIN_STR_TABLE
589)c++";
590
591 // Emit a direct set of `Builtin::Info` initializers, first for the unprefixed
592 // builtins and then for the prefixed builtins.
593 OS << R"c++(
594#ifdef GET_BUILTIN_INFOS
595)c++";
596 for (const auto &B : UnprefixedBuiltins)
597 B.EmitInfo(OS, Table);
598 OS << R"c++(
599#endif // GET_BUILTIN_INFOS
600)c++";
601
602 OS << R"c++(
603#ifdef GET_BUILTIN_PREFIXED_INFOS
604)c++";
605 for (const auto &B : PrefixedBuiltins)
606 B.EmitInfo(OS, Table);
607 OS << R"c++(
608#endif // GET_BUILTIN_PREFIXED_INFOS
609)c++";
610
611 // Emit X-macros for the atomic builtins to support various custome patterns
612 // used exclusively with those builtins.
613 //
614 // FIXME: We should eventually move this to a separate file so that users
615 // don't need to include the full set of builtins.
616 OS << R"c++(
617#ifdef ATOMIC_BUILTIN
618)c++";
619 for (const auto &Builtin : AtomicBuiltins) {
620 Builtin.EmitXMacro(OS);
621 }
622 OS << R"c++(
623#endif // ATOMIC_BUILTIN
624#undef ATOMIC_BUILTIN
625)c++";
626}
627
628//===----------------------------------------------------------------------===//
629// Builtin documentation emitter
630//===----------------------------------------------------------------------===//
631
632/// Holds the data needed to emit documentation for a single builtin.
633namespace {
634struct BuiltinDocData {
635 const Record *Documentation = nullptr;
636 const Record *BuiltinRecord = nullptr;
637 std::string Heading;
638
639 BuiltinDocData(const Record *D, const Record *B)
640 : Documentation(D), BuiltinRecord(B) {
641 // Use the Heading field if set, otherwise use the builtin's first
642 // spelling.
643 StringRef HeadingStr = D->getValueAsString(FieldName: "Heading");
644 if (HeadingStr.empty()) {
645 std::vector<StringRef> Spellings =
646 B->getValueAsListOfStrings(FieldName: "Spellings");
647 if (!Spellings.empty())
648 Heading = Spellings[0].str();
649 else
650 Heading = B->getName().str();
651 } else {
652 Heading = HeadingStr.str();
653 }
654 }
655};
656} // namespace
657
658static void writeCategoryHeader(const Record *Category, raw_ostream &OS) {
659 StringRef CategoryName = Category->getValueAsString(FieldName: "Name");
660 OS << "\n" << CategoryName << "\n";
661 for (size_t I = 0, E = CategoryName.size(); I < E; ++I)
662 OS << "=";
663 OS << "\n\n";
664
665 StringRef CategoryContent = Category->getValueAsString(FieldName: "Content");
666 if (!CategoryContent.trim().empty())
667 OS << CategoryContent.trim() << "\n\n";
668}
669
670/// Split a parameter list string into individual parameter type strings,
671/// respecting nested angle brackets (e.g. address_space<4>, _ExtVector<4,
672/// float>).
673static SmallVector<StringRef> splitParams(StringRef Params) {
674 SmallVector<StringRef> Result;
675 if (Params.empty())
676 return Result;
677
678 int Depth = 0;
679 size_t Start = 0;
680 for (size_t I = 0, E = Params.size(); I < E; ++I) {
681 if (Params[I] == '<') {
682 ++Depth;
683 } else if (Params[I] == '>') {
684 --Depth;
685 } else if (Params[I] == ',' && Depth == 0) {
686 Result.push_back(Elt: Params.substr(Start, N: I - Start).trim());
687 Start = I + 1;
688 }
689 }
690 // Add the last parameter.
691 StringRef Last = Params.substr(Start).trim();
692 if (!Last.empty())
693 Result.push_back(Elt: Last);
694 return Result;
695}
696
697static void writeBuiltinDocumentation(const BuiltinDocData &Doc,
698 raw_ostream &OS) {
699 // Write heading with '-' underline (subsection).
700 std::string HeadingText = "``" + Doc.Heading + "``";
701 OS << HeadingText << "\n";
702 for (size_t I = 0, E = HeadingText.size(); I < E; ++I)
703 OS << "-";
704 OS << "\n\n";
705
706 // Write prototype as a code block.
707 StringRef Prototype = Doc.BuiltinRecord->getValueAsString(FieldName: "Prototype");
708 if (!Prototype.empty()) {
709 std::vector<StringRef> Spellings =
710 Doc.BuiltinRecord->getValueAsListOfStrings(FieldName: "Spellings");
711 StringRef Name =
712 Spellings.empty() ? Doc.BuiltinRecord->getName() : Spellings[0];
713
714 // Split prototype into return type and params at the first '('.
715 size_t ParenPos = Prototype.find(C: '(');
716 if (ParenPos != StringRef::npos) {
717 StringRef RetType = Prototype.substr(Start: 0, N: ParenPos).rtrim();
718 StringRef ParamStr =
719 Prototype.substr(Start: ParenPos + 1, N: Prototype.size() - ParenPos - 2);
720
721 OS << "**Prototype:**\n\n";
722 OS << ".. code-block:: c\n\n";
723 OS << " " << RetType << " " << Name << "(";
724
725 std::vector<StringRef> ArgNames =
726 Doc.BuiltinRecord->getValueAsListOfStrings(FieldName: "ArgNames");
727 if (!ArgNames.empty()) {
728 SmallVector<StringRef> ParamTypes = splitParams(Params: ParamStr);
729 bool IsVariadic = !ParamTypes.empty() && ParamTypes.back() == "...";
730 size_t NamedParams = ParamTypes.size() - (IsVariadic ? 1 : 0);
731 if (ArgNames.size() != NamedParams)
732 PrintFatalError(ErrorLoc: Doc.BuiltinRecord->getLoc(),
733 Msg: "number of ArgNames (" + Twine(ArgNames.size()) +
734 ") does not match number of prototype "
735 "parameters (" +
736 Twine(NamedParams) + ")");
737 for (size_t I = 0, E = ParamTypes.size(); I < E; ++I) {
738 if (I > 0)
739 OS << ", ";
740 if (I < NamedParams)
741 OS << ParamTypes[I] << " " << ArgNames[I];
742 else
743 OS << ParamTypes[I];
744 }
745 } else {
746 OS << ParamStr;
747 }
748
749 OS << ")\n\n";
750 }
751 }
752
753 // Write target features if this is a TargetBuiltin with features.
754 if (Doc.BuiltinRecord->isSubClassOf(Name: "TargetBuiltin")) {
755 StringRef Features = Doc.BuiltinRecord->getValueAsString(FieldName: "Features");
756 if (!Features.empty())
757 OS << "**Target Features:** " << Features << "\n\n";
758 }
759
760 // Write documentation content.
761 StringRef Content = Doc.Documentation->getValueAsString(FieldName: "Content");
762 OS << Content.trim() << "\n\n\n";
763}
764
765void clang::EmitClangBuiltinDocs(const RecordKeeper &Records, raw_ostream &OS) {
766 // Get the documentation introduction paragraph.
767 const Record *Doc = Records.getDef(Name: "GlobalDocumentation");
768 if (!Doc) {
769 PrintFatalError(Msg: "The GlobalDocumentation top-level definition is missing, "
770 "no documentation will be generated.");
771 }
772
773 OS << Doc->getValueAsString(FieldName: "Intro") << "\n";
774
775 // Gather documentation from each builtin, grouped by category.
776 llvm::MapVector<const Record *, std::vector<BuiltinDocData>> SplitDocs;
777
778 for (const Record *B : Records.getAllDerivedDefinitions(ClassName: "Builtin")) {
779 for (const Record *D : B->getValueAsListOfDefs(FieldName: "Documentation")) {
780 const Record *Category = D->getValueAsDef(FieldName: "Category");
781 StringRef Cat = Category->getValueAsString(FieldName: "Name");
782 // Skip builtins that are explicitly internal-only.
783 if (Cat == "InternalOnly")
784 continue;
785 SplitDocs[Category].emplace_back(args&: D, args&: B);
786 }
787 }
788
789 // Sort categories alphabetically by name for deterministic output, but
790 // push the "Undocumented" category to the end so that documented sections
791 // always appear first.
792 llvm::sort(C&: SplitDocs, Comp: [](const auto &A, const auto &B) {
793 StringRef NameA = A.first->getValueAsString("Name");
794 StringRef NameB = B.first->getValueAsString("Name");
795 bool UndocA = (NameA == "Undocumented");
796 bool UndocB = (NameB == "Undocumented");
797 if (UndocA != UndocB)
798 return UndocB;
799 return NameA < NameB;
800 });
801
802 // Write out each category and its builtins.
803 for (auto &[Category, Docs] : SplitDocs) {
804 writeCategoryHeader(Category, OS);
805
806 // Sort entries alphabetically by heading.
807 llvm::sort(C&: Docs, Comp: [](const BuiltinDocData &A, const BuiltinDocData &B) {
808 return A.Heading < B.Heading;
809 });
810
811 for (const BuiltinDocData &D : Docs)
812 writeBuiltinDocumentation(Doc: D, OS);
813 }
814}
815