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