1 | //===-- ClangBuiltinsEmitter.cpp - Generate Clang builtins tables ---------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This tablegen backend emits Clang's builtins tables. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "TableGenBackends.h" |
14 | #include "llvm/ADT/StringExtras.h" |
15 | #include "llvm/ADT/StringSwitch.h" |
16 | #include "llvm/TableGen/Error.h" |
17 | #include "llvm/TableGen/Record.h" |
18 | #include "llvm/TableGen/StringToOffsetTable.h" |
19 | #include "llvm/TableGen/TableGenBackend.h" |
20 | #include <sstream> |
21 | |
22 | using namespace llvm; |
23 | |
24 | namespace { |
25 | enum class BuiltinType { |
26 | Builtin, |
27 | AtomicBuiltin, |
28 | LibBuiltin, |
29 | LangBuiltin, |
30 | TargetBuiltin, |
31 | TargetLibBuiltin, |
32 | }; |
33 | |
34 | class { |
35 | public: |
36 | (const Record *Builtin) { |
37 | for (char c : Builtin->getValueAsString(FieldName: "Header" )) { |
38 | if (std::islower(c)) |
39 | HeaderName += static_cast<char>(std::toupper(c: c)); |
40 | else if (c == '.' || c == '_' || c == '/' || c == '-') |
41 | HeaderName += '_'; |
42 | else |
43 | PrintFatalError(ErrorLoc: Builtin->getLoc(), Msg: "Unexpected header name" ); |
44 | } |
45 | } |
46 | |
47 | void (raw_ostream &OS) const { OS << HeaderName; } |
48 | |
49 | private: |
50 | std::string ; |
51 | }; |
52 | |
53 | struct Builtin { |
54 | BuiltinType BT; |
55 | std::string Name; |
56 | std::string Type; |
57 | std::string Attributes; |
58 | |
59 | const Record *BuiltinRecord; |
60 | |
61 | void EmitEnumerator(llvm::raw_ostream &OS) const { |
62 | OS << " BI" ; |
63 | // If there is a required name prefix, include its spelling in the |
64 | // enumerator. |
65 | if (auto *PrefixRecord = |
66 | BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" )) |
67 | OS << PrefixRecord->getValueAsString(FieldName: "Spelling" ); |
68 | OS << Name << ",\n" ; |
69 | } |
70 | |
71 | void EmitInfo(llvm::raw_ostream &OS, const StringToOffsetTable &Table) const { |
72 | OS << " Builtin::Info{Builtin::Info::StrOffsets{" |
73 | << Table.GetStringOffset(Str: Name) << " /* " << Name << " */, " |
74 | << Table.GetStringOffset(Str: Type) << " /* " << Type << " */, " |
75 | << Table.GetStringOffset(Str: Attributes) << " /* " << Attributes << " */, " ; |
76 | if (BT == BuiltinType::TargetBuiltin) { |
77 | const auto &Features = BuiltinRecord->getValueAsString(FieldName: "Features" ); |
78 | OS << Table.GetStringOffset(Str: Features) << " /* " << Features << " */" ; |
79 | } else { |
80 | OS << "0" ; |
81 | } |
82 | OS << "}, " ; |
83 | if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::TargetLibBuiltin) { |
84 | OS << "HeaderDesc::" ; |
85 | HeaderNameParser{BuiltinRecord}.Print(OS); |
86 | } else { |
87 | OS << "HeaderDesc::NO_HEADER" ; |
88 | } |
89 | OS << ", " ; |
90 | if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::LangBuiltin || |
91 | BT == BuiltinType::TargetLibBuiltin) { |
92 | OS << BuiltinRecord->getValueAsString(FieldName: "Languages" ); |
93 | } else { |
94 | OS << "ALL_LANGUAGES" ; |
95 | } |
96 | OS << "},\n" ; |
97 | } |
98 | |
99 | void EmitXMacro(llvm::raw_ostream &OS) const { |
100 | if (BuiltinRecord->getValueAsBit(FieldName: "RequiresUndef" )) |
101 | OS << "#undef " << Name << '\n'; |
102 | switch (BT) { |
103 | case BuiltinType::LibBuiltin: |
104 | OS << "LIBBUILTIN" ; |
105 | break; |
106 | case BuiltinType::LangBuiltin: |
107 | OS << "LANGBUILTIN" ; |
108 | break; |
109 | case BuiltinType::Builtin: |
110 | OS << "BUILTIN" ; |
111 | break; |
112 | case BuiltinType::AtomicBuiltin: |
113 | OS << "ATOMIC_BUILTIN" ; |
114 | break; |
115 | case BuiltinType::TargetBuiltin: |
116 | OS << "TARGET_BUILTIN" ; |
117 | break; |
118 | case BuiltinType::TargetLibBuiltin: |
119 | OS << "TARGET_HEADER_BUILTIN" ; |
120 | break; |
121 | } |
122 | |
123 | OS << "(" << Name << ", \"" << Type << "\", \"" << Attributes << "\"" ; |
124 | |
125 | switch (BT) { |
126 | case BuiltinType::LibBuiltin: { |
127 | OS << ", " ; |
128 | HeaderNameParser{BuiltinRecord}.Print(OS); |
129 | [[fallthrough]]; |
130 | } |
131 | case BuiltinType::LangBuiltin: { |
132 | OS << ", " << BuiltinRecord->getValueAsString(FieldName: "Languages" ); |
133 | break; |
134 | } |
135 | case BuiltinType::TargetLibBuiltin: { |
136 | OS << ", " ; |
137 | HeaderNameParser{BuiltinRecord}.Print(OS); |
138 | OS << ", " << BuiltinRecord->getValueAsString(FieldName: "Languages" ); |
139 | [[fallthrough]]; |
140 | } |
141 | case BuiltinType::TargetBuiltin: { |
142 | OS << ", \"" << BuiltinRecord->getValueAsString(FieldName: "Features" ) << "\"" ; |
143 | break; |
144 | } |
145 | case BuiltinType::AtomicBuiltin: |
146 | case BuiltinType::Builtin: |
147 | break; |
148 | } |
149 | OS << ")\n" ; |
150 | } |
151 | }; |
152 | |
153 | class PrototypeParser { |
154 | public: |
155 | PrototypeParser(StringRef Substitution, const Record *Builtin) |
156 | : Loc(Builtin->getFieldLoc(FieldName: "Prototype" )), Substitution(Substitution), |
157 | EnableOpenCLLong(Builtin->getValueAsBit(FieldName: "EnableOpenCLLong" )) { |
158 | ParsePrototype(Prototype: Builtin->getValueAsString(FieldName: "Prototype" )); |
159 | } |
160 | |
161 | std::string takeTypeString() && { return std::move(Type); } |
162 | |
163 | private: |
164 | void ParsePrototype(StringRef Prototype) { |
165 | Prototype = Prototype.trim(); |
166 | |
167 | // Some builtins don't have an expressible prototype, simply emit an empty |
168 | // string for them. |
169 | if (Prototype.empty()) { |
170 | Type = "" ; |
171 | return; |
172 | } |
173 | |
174 | ParseTypes(Prototype); |
175 | } |
176 | |
177 | void ParseTypes(StringRef &Prototype) { |
178 | auto ReturnType = Prototype.take_until(F: [](char c) { return c == '('; }); |
179 | ParseType(T: ReturnType); |
180 | Prototype = Prototype.drop_front(N: ReturnType.size() + 1); |
181 | if (!Prototype.ends_with(Suffix: ")" )) |
182 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected closing brace at end of prototype" ); |
183 | Prototype = Prototype.drop_back(); |
184 | |
185 | // Look through the input parameters. |
186 | const size_t end = Prototype.size(); |
187 | for (size_t I = 0; I != end;) { |
188 | const StringRef Current = Prototype.substr(Start: I, N: end); |
189 | // Skip any leading space or commas |
190 | if (Current.starts_with(Prefix: " " ) || Current.starts_with(Prefix: "," )) { |
191 | ++I; |
192 | continue; |
193 | } |
194 | |
195 | // Check if we are in _ExtVector. We do this first because |
196 | // extended vectors are written in template form with the syntax |
197 | // _ExtVector< ..., ...>, so we need to make sure we are not |
198 | // detecting the comma of the template class as a separator for |
199 | // the parameters of the prototype. Note: the assumption is that |
200 | // we cannot have nested _ExtVector. |
201 | if (Current.starts_with(Prefix: "_ExtVector<" ) || |
202 | Current.starts_with(Prefix: "_Vector<" )) { |
203 | const size_t EndTemplate = Current.find(C: '>', From: 0); |
204 | ParseType(T: Current.substr(Start: 0, N: EndTemplate + 1)); |
205 | // Move the prototype beyond _ExtVector<...> |
206 | I += EndTemplate + 1; |
207 | continue; |
208 | } |
209 | |
210 | // We know that we are past _ExtVector, therefore the first seen |
211 | // comma is the boundary of a parameter in the prototype. |
212 | if (size_t CommaPos = Current.find(C: ',', From: 0)) { |
213 | if (CommaPos != StringRef::npos) { |
214 | StringRef T = Current.substr(Start: 0, N: CommaPos); |
215 | ParseType(T); |
216 | // Move the prototype beyond the comma. |
217 | I += CommaPos + 1; |
218 | continue; |
219 | } |
220 | } |
221 | |
222 | // No more commas, parse final parameter. |
223 | ParseType(T: Current); |
224 | I = end; |
225 | } |
226 | } |
227 | |
228 | void ParseType(StringRef T) { |
229 | T = T.trim(); |
230 | |
231 | auto ConsumeAddrSpace = [&]() -> std::optional<unsigned> { |
232 | T = T.trim(); |
233 | if (!T.consume_back(Suffix: ">" )) |
234 | return std::nullopt; |
235 | |
236 | auto Open = T.find_last_of(C: '<'); |
237 | if (Open == StringRef::npos) |
238 | PrintFatalError(ErrorLoc: Loc, Msg: "Mismatched angle-brackets in type" ); |
239 | |
240 | StringRef ArgStr = T.substr(Start: Open + 1); |
241 | T = T.slice(Start: 0, End: Open); |
242 | if (!T.consume_back(Suffix: "address_space" )) |
243 | PrintFatalError(ErrorLoc: Loc, |
244 | Msg: "Only `address_space<N>` supported as a parameterized " |
245 | "pointer or reference type qualifier" ); |
246 | |
247 | unsigned Number = 0; |
248 | if (ArgStr.getAsInteger(Radix: 10, Result&: Number)) |
249 | PrintFatalError( |
250 | ErrorLoc: Loc, Msg: "Expected an integer argument to the address_space qualifier" ); |
251 | if (Number == 0) |
252 | PrintFatalError(ErrorLoc: Loc, Msg: "No need for a qualifier for address space `0`" ); |
253 | return Number; |
254 | }; |
255 | |
256 | if (T.consume_back(Suffix: "*" )) { |
257 | // Pointers may have an address space qualifier immediately before them. |
258 | std::optional<unsigned> AS = ConsumeAddrSpace(); |
259 | ParseType(T); |
260 | Type += "*" ; |
261 | if (AS) |
262 | Type += std::to_string(val: *AS); |
263 | } else if (T.consume_back(Suffix: "const" )) { |
264 | ParseType(T); |
265 | Type += "C" ; |
266 | } else if (T.consume_back(Suffix: "volatile" )) { |
267 | ParseType(T); |
268 | Type += "D" ; |
269 | } else if (T.consume_back(Suffix: "restrict" )) { |
270 | ParseType(T); |
271 | Type += "R" ; |
272 | } else if (T.consume_back(Suffix: "&" )) { |
273 | // References may have an address space qualifier immediately before them. |
274 | std::optional<unsigned> AS = ConsumeAddrSpace(); |
275 | ParseType(T); |
276 | Type += "&" ; |
277 | if (AS) |
278 | Type += std::to_string(val: *AS); |
279 | } else if (T.consume_back(Suffix: ")" )) { |
280 | ParseType(T); |
281 | Type += "&" ; |
282 | } else if (EnableOpenCLLong && T.consume_front(Prefix: "long long" )) { |
283 | Type += "O" ; |
284 | ParseType(T); |
285 | } else if (T.consume_front(Prefix: "long" )) { |
286 | Type += "L" ; |
287 | ParseType(T); |
288 | } else if (T.consume_front(Prefix: "signed" )) { |
289 | Type += "S" ; |
290 | ParseType(T); |
291 | } else if (T.consume_front(Prefix: "unsigned" )) { |
292 | Type += "U" ; |
293 | ParseType(T); |
294 | } else if (T.consume_front(Prefix: "_Complex" )) { |
295 | Type += "X" ; |
296 | ParseType(T); |
297 | } else if (T.consume_front(Prefix: "_Constant" )) { |
298 | Type += "I" ; |
299 | ParseType(T); |
300 | } else if (T.consume_front(Prefix: "T" )) { |
301 | if (Substitution.empty()) |
302 | PrintFatalError(ErrorLoc: Loc, Msg: "Not a template" ); |
303 | ParseType(T: Substitution); |
304 | } else if (auto IsExt = T.consume_front(Prefix: "_ExtVector" ); |
305 | IsExt || T.consume_front(Prefix: "_Vector" )) { |
306 | // Clang extended vector types are mangled as follows: |
307 | // |
308 | // '_ExtVector<' <lanes> ',' <scalar type> '>' |
309 | |
310 | // Before parsing T(=<scalar type>), make sure the syntax of |
311 | // `_ExtVector<N, T>` is correct... |
312 | if (!T.consume_front(Prefix: "<" )) |
313 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected '<' after '_ExtVector'" ); |
314 | unsigned long long Lanes; |
315 | if (consumeUnsignedInteger(Str&: T, Radix: 10, Result&: Lanes)) |
316 | PrintFatalError(ErrorLoc: Loc, Msg: "Expected number of lanes after '_ExtVector<'" ); |
317 | Type += (IsExt ? "E" : "V" ) + std::to_string(val: Lanes); |
318 | if (!T.consume_front(Prefix: "," )) |
319 | PrintFatalError(ErrorLoc: Loc, |
320 | Msg: "Expected ',' after number of lanes in '_ExtVector<'" ); |
321 | if (!T.consume_back(Suffix: ">" )) |
322 | PrintFatalError( |
323 | ErrorLoc: Loc, Msg: "Expected '>' after scalar type in '_ExtVector<N, type>'" ); |
324 | |
325 | // ...all good, we can check if we have a valid `<scalar type>`. |
326 | ParseType(T); |
327 | } else { |
328 | auto ReturnTypeVal = StringSwitch<std::string>(T) |
329 | .Case(S: "__builtin_va_list_ref" , Value: "A" ) |
330 | .Case(S: "__builtin_va_list" , Value: "a" ) |
331 | .Case(S: "__float128" , Value: "LLd" ) |
332 | .Case(S: "__fp16" , Value: "h" ) |
333 | .Case(S: "__int128_t" , Value: "LLLi" ) |
334 | .Case(S: "_Float16" , Value: "x" ) |
335 | .Case(S: "__bf16" , Value: "y" ) |
336 | .Case(S: "bool" , Value: "b" ) |
337 | .Case(S: "char" , Value: "c" ) |
338 | .Case(S: "constant_CFString" , Value: "F" ) |
339 | .Case(S: "double" , Value: "d" ) |
340 | .Case(S: "FILE" , Value: "P" ) |
341 | .Case(S: "float" , Value: "f" ) |
342 | .Case(S: "id" , Value: "G" ) |
343 | .Case(S: "int" , Value: "i" ) |
344 | .Case(S: "int32_t" , Value: "Zi" ) |
345 | .Case(S: "int64_t" , Value: "Wi" ) |
346 | .Case(S: "jmp_buf" , Value: "J" ) |
347 | .Case(S: "msint32_t" , Value: "Ni" ) |
348 | .Case(S: "msuint32_t" , Value: "UNi" ) |
349 | .Case(S: "objc_super" , Value: "M" ) |
350 | .Case(S: "pid_t" , Value: "p" ) |
351 | .Case(S: "ptrdiff_t" , Value: "Y" ) |
352 | .Case(S: "SEL" , Value: "H" ) |
353 | .Case(S: "short" , Value: "s" ) |
354 | .Case(S: "sigjmp_buf" , Value: "SJ" ) |
355 | .Case(S: "size_t" , Value: "z" ) |
356 | .Case(S: "ucontext_t" , Value: "K" ) |
357 | .Case(S: "uint32_t" , Value: "UZi" ) |
358 | .Case(S: "uint64_t" , Value: "UWi" ) |
359 | .Case(S: "void" , Value: "v" ) |
360 | .Case(S: "wchar_t" , Value: "w" ) |
361 | .Case(S: "..." , Value: "." ) |
362 | .Default(Value: "error" ); |
363 | if (ReturnTypeVal == "error" ) |
364 | PrintFatalError(ErrorLoc: Loc, Msg: "Unknown Type: " + T); |
365 | Type += ReturnTypeVal; |
366 | } |
367 | } |
368 | |
369 | SMLoc Loc; |
370 | StringRef Substitution; |
371 | bool EnableOpenCLLong; |
372 | std::string Type; |
373 | }; |
374 | |
375 | std::string renderAttributes(const Record *Builtin, BuiltinType BT) { |
376 | std::string Attributes; |
377 | raw_string_ostream OS(Attributes); |
378 | if (Builtin->isSubClassOf(Name: "LibBuiltin" )) { |
379 | if (BT == BuiltinType::LibBuiltin) { |
380 | OS << 'f'; |
381 | } else { |
382 | OS << 'F'; |
383 | if (Builtin->getValueAsBit(FieldName: "OnlyBuiltinPrefixedAliasIsConstexpr" )) |
384 | OS << 'E'; |
385 | } |
386 | } |
387 | |
388 | if (auto NS = Builtin->getValueAsOptionalString(FieldName: "Namespace" )) { |
389 | if (NS != "std" ) |
390 | PrintFatalError(ErrorLoc: Builtin->getFieldLoc(FieldName: "Namespace" ), Msg: "Unknown namespace: " ); |
391 | OS << "z" ; |
392 | } |
393 | |
394 | for (const auto *Attr : Builtin->getValueAsListOfDefs(FieldName: "Attributes" )) { |
395 | OS << Attr->getValueAsString(FieldName: "Mangling" ); |
396 | if (Attr->isSubClassOf(Name: "IndexedAttribute" )) { |
397 | OS << ':' << Attr->getValueAsInt(FieldName: "Index" ) << ':'; |
398 | } else if (Attr->isSubClassOf(Name: "MultiIndexAttribute" )) { |
399 | OS << '<'; |
400 | llvm::ListSeparator Sep("," ); |
401 | for (int64_t Index : Attr->getValueAsListOfInts(FieldName: "Indices" )) |
402 | OS << Sep << Index; |
403 | OS << '>'; |
404 | } |
405 | } |
406 | return Attributes; |
407 | } |
408 | |
409 | Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord, |
410 | Twine Spelling, BuiltinType BT) { |
411 | Builtin B; |
412 | B.BT = BT; |
413 | B.Name = Spelling.str(); |
414 | B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString(); |
415 | B.Attributes = renderAttributes(Builtin: BuiltinRecord, BT); |
416 | B.BuiltinRecord = BuiltinRecord; |
417 | return B; |
418 | } |
419 | |
420 | struct TemplateInsts { |
421 | std::vector<std::string> Substitution; |
422 | std::vector<std::string> Affix; |
423 | bool IsPrefix; |
424 | }; |
425 | |
426 | TemplateInsts getTemplateInsts(const Record *R) { |
427 | TemplateInsts temp; |
428 | auto Substitutions = R->getValueAsListOfStrings(FieldName: "Substitutions" ); |
429 | auto Affixes = R->getValueAsListOfStrings(FieldName: "Affixes" ); |
430 | temp.IsPrefix = R->getValueAsBit(FieldName: "AsPrefix" ); |
431 | |
432 | if (Substitutions.size() != Affixes.size()) |
433 | PrintFatalError(ErrorLoc: R->getLoc(), Msg: "Substitutions and affixes " |
434 | "don't have the same lengths" ); |
435 | |
436 | for (auto [Affix, Substitution] : zip(t&: Affixes, u&: Substitutions)) { |
437 | temp.Substitution.emplace_back(args&: Substitution); |
438 | temp.Affix.emplace_back(args&: Affix); |
439 | } |
440 | return temp; |
441 | } |
442 | |
443 | void collectBuiltins(const Record *BuiltinRecord, |
444 | SmallVectorImpl<Builtin> &Builtins) { |
445 | TemplateInsts Templates = {}; |
446 | if (BuiltinRecord->isSubClassOf(Name: "Template" )) { |
447 | Templates = getTemplateInsts(R: BuiltinRecord); |
448 | } else { |
449 | Templates.Affix.emplace_back(); |
450 | Templates.Substitution.emplace_back(); |
451 | } |
452 | |
453 | for (auto [Substitution, Affix] : |
454 | zip(t&: Templates.Substitution, u&: Templates.Affix)) { |
455 | for (StringRef Spelling : |
456 | BuiltinRecord->getValueAsListOfStrings(FieldName: "Spellings" )) { |
457 | auto FullSpelling = |
458 | (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str(); |
459 | BuiltinType BT = BuiltinType::Builtin; |
460 | if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin" )) { |
461 | BT = BuiltinType::AtomicBuiltin; |
462 | } else if (BuiltinRecord->isSubClassOf(Name: "LangBuiltin" )) { |
463 | BT = BuiltinType::LangBuiltin; |
464 | } else if (BuiltinRecord->isSubClassOf(Name: "TargetLibBuiltin" )) { |
465 | BT = BuiltinType::TargetLibBuiltin; |
466 | } else if (BuiltinRecord->isSubClassOf(Name: "TargetBuiltin" )) { |
467 | BT = BuiltinType::TargetBuiltin; |
468 | } else if (BuiltinRecord->isSubClassOf(Name: "LibBuiltin" )) { |
469 | BT = BuiltinType::LibBuiltin; |
470 | if (BuiltinRecord->getValueAsBit(FieldName: "AddBuiltinPrefixedAlias" )) |
471 | Builtins.push_back(Elt: buildBuiltin( |
472 | Substitution, BuiltinRecord, |
473 | Spelling: std::string("__builtin_" ) + FullSpelling, BT: BuiltinType::Builtin)); |
474 | } |
475 | Builtins.push_back( |
476 | Elt: buildBuiltin(Substitution, BuiltinRecord, Spelling: FullSpelling, BT)); |
477 | } |
478 | } |
479 | } |
480 | } // namespace |
481 | |
482 | void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) { |
483 | emitSourceFileHeader(Desc: "List of builtins that Clang recognizes" , OS); |
484 | |
485 | SmallVector<Builtin> Builtins; |
486 | // AtomicBuiltins are order dependent. Emit them first to make manual checking |
487 | // easier and so we can build a special atomic builtin X-macro. |
488 | for (const auto *BuiltinRecord : |
489 | Records.getAllDerivedDefinitions(ClassName: "AtomicBuiltin" )) |
490 | collectBuiltins(BuiltinRecord, Builtins); |
491 | unsigned NumAtomicBuiltins = Builtins.size(); |
492 | |
493 | for (const auto *BuiltinRecord : |
494 | Records.getAllDerivedDefinitions(ClassName: "Builtin" )) { |
495 | if (BuiltinRecord->isSubClassOf(Name: "AtomicBuiltin" )) |
496 | continue; |
497 | // Prefixed builtins are also special and we emit them last so they can have |
498 | // their own representation that skips the prefix. |
499 | if (BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" )) |
500 | continue; |
501 | |
502 | collectBuiltins(BuiltinRecord, Builtins); |
503 | } |
504 | |
505 | // Now collect (and count) the prefixed builtins. |
506 | unsigned NumPrefixedBuiltins = Builtins.size(); |
507 | const Record *FirstPrefix = nullptr; |
508 | for (const auto *BuiltinRecord : |
509 | Records.getAllDerivedDefinitions(ClassName: "Builtin" )) { |
510 | auto *Prefix = BuiltinRecord->getValueAsOptionalDef(FieldName: "RequiredNamePrefix" ); |
511 | if (!Prefix) |
512 | continue; |
513 | |
514 | if (!FirstPrefix) |
515 | FirstPrefix = Prefix; |
516 | assert(Prefix == FirstPrefix && |
517 | "Multiple distinct prefixes which is not currently supported!" ); |
518 | assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin" ) && |
519 | "Cannot require a name prefix for an atomic builtin." ); |
520 | collectBuiltins(BuiltinRecord, Builtins); |
521 | } |
522 | NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins; |
523 | |
524 | auto AtomicBuiltins = ArrayRef(Builtins).slice(N: 0, M: NumAtomicBuiltins); |
525 | auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(N: NumPrefixedBuiltins); |
526 | auto PrefixedBuiltins = ArrayRef(Builtins).take_back(N: NumPrefixedBuiltins); |
527 | |
528 | // Collect strings into a table. |
529 | StringToOffsetTable Table; |
530 | Table.GetOrAddStringOffset(Str: "" ); |
531 | for (const auto &B : Builtins) { |
532 | Table.GetOrAddStringOffset(Str: B.Name); |
533 | Table.GetOrAddStringOffset(Str: B.Type); |
534 | Table.GetOrAddStringOffset(Str: B.Attributes); |
535 | if (B.BT == BuiltinType::TargetBuiltin) |
536 | Table.GetOrAddStringOffset(Str: B.BuiltinRecord->getValueAsString(FieldName: "Features" )); |
537 | } |
538 | |
539 | // Emit enumerators. |
540 | OS << R"c++( |
541 | #ifdef GET_BUILTIN_ENUMERATORS |
542 | )c++" ; |
543 | for (const auto &B : Builtins) |
544 | B.EmitEnumerator(OS); |
545 | OS << R"c++( |
546 | #endif // GET_BUILTIN_ENUMERATORS |
547 | )c++" ; |
548 | |
549 | // Emit a string table that can be referenced for these builtins. |
550 | OS << R"c++( |
551 | #ifdef GET_BUILTIN_STR_TABLE |
552 | )c++" ; |
553 | Table.EmitStringTableDef(OS, Name: "BuiltinStrings" ); |
554 | OS << R"c++( |
555 | #endif // GET_BUILTIN_STR_TABLE |
556 | )c++" ; |
557 | |
558 | // Emit a direct set of `Builtin::Info` initializers, first for the unprefixed |
559 | // builtins and then for the prefixed builtins. |
560 | OS << R"c++( |
561 | #ifdef GET_BUILTIN_INFOS |
562 | )c++" ; |
563 | for (const auto &B : UnprefixedBuiltins) |
564 | B.EmitInfo(OS, Table); |
565 | OS << R"c++( |
566 | #endif // GET_BUILTIN_INFOS |
567 | )c++" ; |
568 | |
569 | OS << R"c++( |
570 | #ifdef GET_BUILTIN_PREFIXED_INFOS |
571 | )c++" ; |
572 | for (const auto &B : PrefixedBuiltins) |
573 | B.EmitInfo(OS, Table); |
574 | OS << R"c++( |
575 | #endif // GET_BUILTIN_PREFIXED_INFOS |
576 | )c++" ; |
577 | |
578 | // Emit X-macros for the atomic builtins to support various custome patterns |
579 | // used exclusively with those builtins. |
580 | // |
581 | // FIXME: We should eventually move this to a separate file so that users |
582 | // don't need to include the full set of builtins. |
583 | OS << R"c++( |
584 | #ifdef ATOMIC_BUILTIN |
585 | )c++" ; |
586 | for (const auto &Builtin : AtomicBuiltins) { |
587 | Builtin.EmitXMacro(OS); |
588 | } |
589 | OS << R"c++( |
590 | #endif // ATOMIC_BUILTIN |
591 | #undef ATOMIC_BUILTIN |
592 | )c++" ; |
593 | } |
594 | |