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