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 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
391std::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
425Builtin 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
436struct TemplateInsts {
437 std::vector<std::string> Substitution;
438 std::vector<std::string> Affix;
439 bool IsPrefix;
440};
441
442TemplateInsts 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
459void 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
498void 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.
616namespace {
617struct 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
641static void writeCategoryHeader(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>).
656static 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
680static 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
748void 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