1//=- ClangBuiltinsEmitter.cpp - Generate Clang builtins tables -*- C++ -*-====//
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/StringSwitch.h"
15#include "llvm/TableGen/Error.h"
16#include "llvm/TableGen/Record.h"
17#include "llvm/TableGen/TableGenBackend.h"
18
19using namespace llvm;
20
21namespace {
22enum class BuiltinType {
23 Builtin,
24 AtomicBuiltin,
25 LibBuiltin,
26 LangBuiltin,
27 TargetBuiltin,
28};
29
30class PrototypeParser {
31public:
32 PrototypeParser(StringRef Substitution, const Record *Builtin)
33 : Loc(Builtin->getFieldLoc(FieldName: "Prototype")), Substitution(Substitution) {
34 ParsePrototype(Prototype: Builtin->getValueAsString(FieldName: "Prototype"));
35 }
36
37private:
38 void ParsePrototype(StringRef Prototype) {
39 Prototype = Prototype.trim();
40 ParseTypes(Prototype);
41 }
42
43 void ParseTypes(StringRef &Prototype) {
44 auto ReturnType = Prototype.take_until(F: [](char c) { return c == '('; });
45 ParseType(T: ReturnType);
46 Prototype = Prototype.drop_front(N: ReturnType.size() + 1);
47 if (!Prototype.ends_with(Suffix: ")"))
48 PrintFatalError(ErrorLoc: Loc, Msg: "Expected closing brace at end of prototype");
49 Prototype = Prototype.drop_back();
50
51 // Look through the input parameters.
52 const size_t end = Prototype.size();
53 for (size_t I = 0; I != end;) {
54 const StringRef Current = Prototype.substr(Start: I, N: end);
55 // Skip any leading space or commas
56 if (Current.starts_with(Prefix: " ") || Current.starts_with(Prefix: ",")) {
57 ++I;
58 continue;
59 }
60
61 // Check if we are in _ExtVector. We do this first because
62 // extended vectors are written in template form with the syntax
63 // _ExtVector< ..., ...>, so we need to make sure we are not
64 // detecting the comma of the template class as a separator for
65 // the parameters of the prototype. Note: the assumption is that
66 // we cannot have nested _ExtVector.
67 if (Current.starts_with(Prefix: "_ExtVector<")) {
68 const size_t EndTemplate = Current.find(C: '>', From: 0);
69 ParseType(T: Current.substr(Start: 0, N: EndTemplate + 1));
70 // Move the prototype beyond _ExtVector<...>
71 I += EndTemplate + 1;
72 continue;
73 }
74
75 // We know that we are past _ExtVector, therefore the first seen
76 // comma is the boundary of a parameter in the prototype.
77 if (size_t CommaPos = Current.find(C: ',', From: 0)) {
78 if (CommaPos != StringRef::npos) {
79 StringRef T = Current.substr(Start: 0, N: CommaPos);
80 ParseType(T);
81 // Move the prototype beyond the comma.
82 I += CommaPos + 1;
83 continue;
84 }
85 }
86
87 // No more commas, parse final parameter.
88 ParseType(T: Current);
89 I = end;
90 }
91 }
92
93 void ParseType(StringRef T) {
94 T = T.trim();
95 if (T.consume_back(Suffix: "*")) {
96 ParseType(T);
97 Type += "*";
98 } else if (T.consume_back(Suffix: "const")) {
99 ParseType(T);
100 Type += "C";
101 } else if (T.consume_back(Suffix: "volatile")) {
102 ParseType(T);
103 Type += "D";
104 } else if (T.consume_back(Suffix: "restrict")) {
105 ParseType(T);
106 Type += "R";
107 } else if (T.consume_back(Suffix: "&")) {
108 ParseType(T);
109 Type += "&";
110 } else if (T.consume_front(Prefix: "long")) {
111 Type += "L";
112 ParseType(T);
113 } else if (T.consume_front(Prefix: "unsigned")) {
114 Type += "U";
115 ParseType(T);
116 } else if (T.consume_front(Prefix: "_Complex")) {
117 Type += "X";
118 ParseType(T);
119 } else if (T.consume_front(Prefix: "_Constant")) {
120 Type += "I";
121 ParseType(T);
122 } else if (T.consume_front(Prefix: "T")) {
123 if (Substitution.empty())
124 PrintFatalError(ErrorLoc: Loc, Msg: "Not a template");
125 ParseType(T: Substitution);
126 } else if (T.consume_front(Prefix: "_ExtVector")) {
127 // Clang extended vector types are mangled as follows:
128 //
129 // '_ExtVector<' <lanes> ',' <scalar type> '>'
130
131 // Before parsing T(=<scalar type>), make sure the syntax of
132 // `_ExtVector<N, T>` is correct...
133 if (!T.consume_front(Prefix: "<"))
134 PrintFatalError(ErrorLoc: Loc, Msg: "Expected '<' after '_ExtVector'");
135 unsigned long long Lanes;
136 if (llvm::consumeUnsignedInteger(Str&: T, Radix: 10, Result&: Lanes))
137 PrintFatalError(ErrorLoc: Loc, Msg: "Expected number of lanes after '_ExtVector<'");
138 Type += "E" + std::to_string(val: Lanes);
139 if (!T.consume_front(Prefix: ","))
140 PrintFatalError(ErrorLoc: Loc,
141 Msg: "Expected ',' after number of lanes in '_ExtVector<'");
142 if (!T.consume_back(Suffix: ">"))
143 PrintFatalError(
144 ErrorLoc: Loc, Msg: "Expected '>' after scalar type in '_ExtVector<N, type>'");
145
146 // ...all good, we can check if we have a valid `<scalar type>`.
147 ParseType(T);
148 } else {
149 auto ReturnTypeVal = StringSwitch<std::string>(T)
150 .Case(S: "__builtin_va_list_ref", Value: "A")
151 .Case(S: "__builtin_va_list", Value: "a")
152 .Case(S: "__float128", Value: "LLd")
153 .Case(S: "__fp16", Value: "h")
154 .Case(S: "__int128_t", Value: "LLLi")
155 .Case(S: "_Float16", Value: "x")
156 .Case(S: "bool", Value: "b")
157 .Case(S: "char", Value: "c")
158 .Case(S: "constant_CFString", Value: "F")
159 .Case(S: "double", Value: "d")
160 .Case(S: "FILE", Value: "P")
161 .Case(S: "float", Value: "f")
162 .Case(S: "id", Value: "G")
163 .Case(S: "int", Value: "i")
164 .Case(S: "int32_t", Value: "Zi")
165 .Case(S: "int64_t", Value: "Wi")
166 .Case(S: "jmp_buf", Value: "J")
167 .Case(S: "msint32_t", Value: "Ni")
168 .Case(S: "msuint32_t", Value: "UNi")
169 .Case(S: "objc_super", Value: "M")
170 .Case(S: "pid_t", Value: "p")
171 .Case(S: "ptrdiff_t", Value: "Y")
172 .Case(S: "SEL", Value: "H")
173 .Case(S: "short", Value: "s")
174 .Case(S: "sigjmp_buf", Value: "SJ")
175 .Case(S: "size_t", Value: "z")
176 .Case(S: "ucontext_t", Value: "K")
177 .Case(S: "uint32_t", Value: "UZi")
178 .Case(S: "uint64_t", Value: "UWi")
179 .Case(S: "void", Value: "v")
180 .Case(S: "wchar_t", Value: "w")
181 .Case(S: "...", Value: ".")
182 .Default(Value: "error");
183 if (ReturnTypeVal == "error")
184 PrintFatalError(ErrorLoc: Loc, Msg: "Unknown Type: " + T);
185 Type += ReturnTypeVal;
186 }
187 }
188
189public:
190 void Print(llvm::raw_ostream &OS) const { OS << ", \"" << Type << '\"'; }
191
192private:
193 SMLoc Loc;
194 StringRef Substitution;
195 std::string Type;
196};
197
198class HeaderNameParser {
199public:
200 HeaderNameParser(const Record *Builtin) {
201 for (char c : Builtin->getValueAsString(FieldName: "Header")) {
202 if (std::islower(c))
203 HeaderName += static_cast<char>(std::toupper(c: c));
204 else if (c == '.' || c == '_' || c == '/' || c == '-')
205 HeaderName += '_';
206 else
207 PrintFatalError(ErrorLoc: Builtin->getLoc(), Msg: "Unexpected header name");
208 }
209 }
210
211 void Print(llvm::raw_ostream &OS) const { OS << HeaderName; }
212
213private:
214 std::string HeaderName;
215};
216
217void PrintAttributes(const Record *Builtin, BuiltinType BT,
218 llvm::raw_ostream &OS) {
219 OS << '\"';
220 if (Builtin->isSubClassOf(Name: "LibBuiltin")) {
221 if (BT == BuiltinType::LibBuiltin) {
222 OS << 'f';
223 } else {
224 OS << 'F';
225 if (Builtin->getValueAsBit(FieldName: "OnlyBuiltinPrefixedAliasIsConstexpr"))
226 OS << 'E';
227 }
228 }
229
230 if (auto NS = Builtin->getValueAsOptionalString(FieldName: "Namespace")) {
231 if (NS != "std")
232 PrintFatalError(ErrorLoc: Builtin->getFieldLoc(FieldName: "Namespace"), Msg: "Unknown namespace: ");
233 OS << "z";
234 }
235
236 for (const auto *Attr : Builtin->getValueAsListOfDefs(FieldName: "Attributes")) {
237 OS << Attr->getValueAsString(FieldName: "Mangling");
238 if (Attr->isSubClassOf(Name: "IndexedAttribute"))
239 OS << ':' << Attr->getValueAsInt(FieldName: "Index") << ':';
240 }
241 OS << '\"';
242}
243
244void EmitBuiltinDef(llvm::raw_ostream &OS, StringRef Substitution,
245 const Record *Builtin, Twine Spelling, BuiltinType BT) {
246 if (Builtin->getValueAsBit(FieldName: "RequiresUndef"))
247 OS << "#undef " << Spelling << '\n';
248 switch (BT) {
249 case BuiltinType::LibBuiltin:
250 OS << "LIBBUILTIN";
251 break;
252 case BuiltinType::LangBuiltin:
253 OS << "LANGBUILTIN";
254 break;
255 case BuiltinType::Builtin:
256 OS << "BUILTIN";
257 break;
258 case BuiltinType::AtomicBuiltin:
259 OS << "ATOMIC_BUILTIN";
260 break;
261 case BuiltinType::TargetBuiltin:
262 OS << "TARGET_BUILTIN";
263 break;
264 }
265
266 OS << "(" << Spelling;
267 PrototypeParser{Substitution, Builtin}.Print(OS);
268 OS << ", ";
269 PrintAttributes(Builtin, BT, OS);
270
271 switch (BT) {
272 case BuiltinType::LibBuiltin: {
273 OS << ", ";
274 HeaderNameParser{Builtin}.Print(OS);
275 [[fallthrough]];
276 }
277 case BuiltinType::LangBuiltin: {
278 OS << ", " << Builtin->getValueAsString(FieldName: "Languages");
279 break;
280 }
281 case BuiltinType::TargetBuiltin:
282 OS << ", \"" << Builtin->getValueAsString(FieldName: "Features") << "\"";
283 break;
284 case BuiltinType::AtomicBuiltin:
285 case BuiltinType::Builtin:
286 break;
287 }
288 OS << ")\n";
289}
290
291struct TemplateInsts {
292 std::vector<std::string> Substitution;
293 std::vector<std::string> Affix;
294 bool IsPrefix;
295};
296
297TemplateInsts getTemplateInsts(const Record *R) {
298 TemplateInsts temp;
299 auto Substitutions = R->getValueAsListOfStrings(FieldName: "Substitutions");
300 auto Affixes = R->getValueAsListOfStrings(FieldName: "Affixes");
301 temp.IsPrefix = R->getValueAsBit(FieldName: "AsPrefix");
302
303 if (Substitutions.size() != Affixes.size())
304 PrintFatalError(ErrorLoc: R->getLoc(), Msg: "Substitutions and affixes "
305 "don't have the same lengths");
306
307 for (auto [Affix, Substitution] : llvm::zip(t&: Affixes, u&: Substitutions)) {
308 temp.Substitution.emplace_back(args&: Substitution);
309 temp.Affix.emplace_back(args&: Affix);
310 }
311 return temp;
312}
313
314void EmitBuiltin(llvm::raw_ostream &OS, const Record *Builtin) {
315 TemplateInsts Templates = {};
316 if (Builtin->isSubClassOf(Name: "Template")) {
317 Templates = getTemplateInsts(R: Builtin);
318 } else {
319 Templates.Affix.emplace_back();
320 Templates.Substitution.emplace_back();
321 }
322
323 for (auto [Substitution, Affix] :
324 llvm::zip(t&: Templates.Substitution, u&: Templates.Affix)) {
325 for (StringRef Spelling : Builtin->getValueAsListOfStrings(FieldName: "Spellings")) {
326 auto FullSpelling =
327 (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str();
328 BuiltinType BT = BuiltinType::Builtin;
329 if (Builtin->isSubClassOf(Name: "AtomicBuiltin")) {
330 BT = BuiltinType::AtomicBuiltin;
331 } else if (Builtin->isSubClassOf(Name: "LangBuiltin")) {
332 BT = BuiltinType::LangBuiltin;
333 } else if (Builtin->isSubClassOf(Name: "TargetBuiltin")) {
334 BT = BuiltinType::TargetBuiltin;
335 } else if (Builtin->isSubClassOf(Name: "LibBuiltin")) {
336 BT = BuiltinType::LibBuiltin;
337 if (Builtin->getValueAsBit(FieldName: "AddBuiltinPrefixedAlias"))
338 EmitBuiltinDef(OS, Substitution, Builtin,
339 Spelling: std::string("__builtin_") + FullSpelling,
340 BT: BuiltinType::Builtin);
341 }
342 EmitBuiltinDef(OS, Substitution, Builtin, Spelling: FullSpelling, BT);
343 }
344 }
345}
346} // namespace
347
348void clang::EmitClangBuiltins(llvm::RecordKeeper &Records,
349 llvm::raw_ostream &OS) {
350 emitSourceFileHeader(Desc: "List of builtins that Clang recognizes", OS);
351
352 OS << R"c++(
353#if defined(BUILTIN) && !defined(LIBBUILTIN)
354# define LIBBUILTIN(ID, TYPE, ATTRS, HEADER, BUILTIN_LANG) BUILTIN(ID, TYPE, ATTRS)
355#endif
356
357#if defined(BUILTIN) && !defined(LANGBUILTIN)
358# define LANGBUILTIN(ID, TYPE, ATTRS, BUILTIN_LANG) BUILTIN(ID, TYPE, ATTRS)
359#endif
360
361// Some of our atomics builtins are handled by AtomicExpr rather than
362// as normal builtin CallExprs. This macro is used for such builtins.
363#ifndef ATOMIC_BUILTIN
364# define ATOMIC_BUILTIN(ID, TYPE, ATTRS) BUILTIN(ID, TYPE, ATTRS)
365#endif
366
367#if defined(BUILTIN) && !defined(TARGET_BUILTIN)
368# define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) BUILTIN(ID, TYPE, ATTRS)
369#endif
370)c++";
371
372 // AtomicBuiltins are order dependent
373 // emit them first to make manual checking easier
374 for (const auto *Builtin : Records.getAllDerivedDefinitions(ClassName: "AtomicBuiltin"))
375 EmitBuiltin(OS, Builtin);
376
377 for (const auto *Builtin : Records.getAllDerivedDefinitions(ClassName: "Builtin")) {
378 if (Builtin->isSubClassOf(Name: "AtomicBuiltin"))
379 continue;
380 EmitBuiltin(OS, Builtin);
381 }
382
383 for (const auto *Entry : Records.getAllDerivedDefinitions(ClassName: "CustomEntry")) {
384 OS << Entry->getValueAsString(FieldName: "Entry") << '\n';
385 }
386
387 OS << R"c++(
388#undef ATOMIC_BUILTIN
389#undef BUILTIN
390#undef LIBBUILTIN
391#undef LANGBUILTIN
392#undef TARGET_BUILTIN
393)c++";
394}
395