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 | |
19 | using namespace llvm; |
20 | |
21 | namespace { |
22 | enum class BuiltinType { |
23 | Builtin, |
24 | AtomicBuiltin, |
25 | LibBuiltin, |
26 | LangBuiltin, |
27 | TargetBuiltin, |
28 | }; |
29 | |
30 | class PrototypeParser { |
31 | public: |
32 | PrototypeParser(StringRef Substitution, const Record *Builtin) |
33 | : Loc(Builtin->getFieldLoc(FieldName: "Prototype" )), Substitution(Substitution) { |
34 | ParsePrototype(Prototype: Builtin->getValueAsString(FieldName: "Prototype" )); |
35 | } |
36 | |
37 | private: |
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 | |
189 | public: |
190 | void Print(llvm::raw_ostream &OS) const { OS << ", \"" << Type << '\"'; } |
191 | |
192 | private: |
193 | SMLoc Loc; |
194 | StringRef Substitution; |
195 | std::string Type; |
196 | }; |
197 | |
198 | class { |
199 | public: |
200 | (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 (llvm::raw_ostream &OS) const { OS << HeaderName; } |
212 | |
213 | private: |
214 | std::string ; |
215 | }; |
216 | |
217 | void 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 | |
244 | void 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 | |
291 | struct TemplateInsts { |
292 | std::vector<std::string> Substitution; |
293 | std::vector<std::string> Affix; |
294 | bool IsPrefix; |
295 | }; |
296 | |
297 | TemplateInsts 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 | |
314 | void 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 | |
348 | void 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 | |