xref: /freebsd/contrib/llvm-project/clang/utils/TableGen/ClangBuiltinsEmitter.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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:
PrototypeParser(StringRef Substitution,const Record * Builtin)32   PrototypeParser(StringRef Substitution, const Record *Builtin)
33       : Loc(Builtin->getFieldLoc("Prototype")), Substitution(Substitution) {
34     ParsePrototype(Builtin->getValueAsString("Prototype"));
35   }
36 
37 private:
ParsePrototype(StringRef Prototype)38   void ParsePrototype(StringRef Prototype) {
39     Prototype = Prototype.trim();
40     ParseTypes(Prototype);
41   }
42 
ParseTypes(StringRef & Prototype)43   void ParseTypes(StringRef &Prototype) {
44     auto ReturnType = Prototype.take_until([](char c) { return c == '('; });
45     ParseType(ReturnType);
46     Prototype = Prototype.drop_front(ReturnType.size() + 1);
47     if (!Prototype.ends_with(")"))
48       PrintFatalError(Loc, "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(I, end);
55       // Skip any leading space or commas
56       if (Current.starts_with(" ") || Current.starts_with(",")) {
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("_ExtVector<")) {
68         const size_t EndTemplate = Current.find('>', 0);
69         ParseType(Current.substr(0, 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(',', 0)) {
78         if (CommaPos != StringRef::npos) {
79           StringRef T = Current.substr(0, 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(Current);
89       I = end;
90     }
91   }
92 
ParseType(StringRef T)93   void ParseType(StringRef T) {
94     T = T.trim();
95     if (T.consume_back("*")) {
96       ParseType(T);
97       Type += "*";
98     } else if (T.consume_back("const")) {
99       ParseType(T);
100       Type += "C";
101     } else if (T.consume_back("volatile")) {
102       ParseType(T);
103       Type += "D";
104     } else if (T.consume_back("restrict")) {
105       ParseType(T);
106       Type += "R";
107     } else if (T.consume_back("&")) {
108       ParseType(T);
109       Type += "&";
110     } else if (T.consume_front("long")) {
111       Type += "L";
112       ParseType(T);
113     } else if (T.consume_front("unsigned")) {
114       Type += "U";
115       ParseType(T);
116     } else if (T.consume_front("_Complex")) {
117       Type += "X";
118       ParseType(T);
119     } else if (T.consume_front("_Constant")) {
120       Type += "I";
121       ParseType(T);
122     } else if (T.consume_front("T")) {
123       if (Substitution.empty())
124         PrintFatalError(Loc, "Not a template");
125       ParseType(Substitution);
126     } else if (T.consume_front("_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("<"))
134         PrintFatalError(Loc, "Expected '<' after '_ExtVector'");
135       unsigned long long Lanes;
136       if (llvm::consumeUnsignedInteger(T, 10, Lanes))
137         PrintFatalError(Loc, "Expected number of lanes after '_ExtVector<'");
138       Type += "E" + std::to_string(Lanes);
139       if (!T.consume_front(","))
140         PrintFatalError(Loc,
141                         "Expected ',' after number of lanes in '_ExtVector<'");
142       if (!T.consume_back(">"))
143         PrintFatalError(
144             Loc, "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("__builtin_va_list_ref", "A")
151                                .Case("__builtin_va_list", "a")
152                                .Case("__float128", "LLd")
153                                .Case("__fp16", "h")
154                                .Case("__int128_t", "LLLi")
155                                .Case("_Float16", "x")
156                                .Case("bool", "b")
157                                .Case("char", "c")
158                                .Case("constant_CFString", "F")
159                                .Case("double", "d")
160                                .Case("FILE", "P")
161                                .Case("float", "f")
162                                .Case("id", "G")
163                                .Case("int", "i")
164                                .Case("int32_t", "Zi")
165                                .Case("int64_t", "Wi")
166                                .Case("jmp_buf", "J")
167                                .Case("msint32_t", "Ni")
168                                .Case("msuint32_t", "UNi")
169                                .Case("objc_super", "M")
170                                .Case("pid_t", "p")
171                                .Case("ptrdiff_t", "Y")
172                                .Case("SEL", "H")
173                                .Case("short", "s")
174                                .Case("sigjmp_buf", "SJ")
175                                .Case("size_t", "z")
176                                .Case("ucontext_t", "K")
177                                .Case("uint32_t", "UZi")
178                                .Case("uint64_t", "UWi")
179                                .Case("void", "v")
180                                .Case("wchar_t", "w")
181                                .Case("...", ".")
182                                .Default("error");
183       if (ReturnTypeVal == "error")
184         PrintFatalError(Loc, "Unknown Type: " + T);
185       Type += ReturnTypeVal;
186     }
187   }
188 
189 public:
Print(llvm::raw_ostream & OS) const190   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 HeaderNameParser {
199 public:
HeaderNameParser(const Record * Builtin)200   HeaderNameParser(const Record *Builtin) {
201     for (char c : Builtin->getValueAsString("Header")) {
202       if (std::islower(c))
203         HeaderName += static_cast<char>(std::toupper(c));
204       else if (c == '.' || c == '_' || c == '/' || c == '-')
205         HeaderName += '_';
206       else
207         PrintFatalError(Builtin->getLoc(), "Unexpected header name");
208     }
209   }
210 
Print(llvm::raw_ostream & OS) const211   void Print(llvm::raw_ostream &OS) const { OS << HeaderName; }
212 
213 private:
214   std::string HeaderName;
215 };
216 
PrintAttributes(const Record * Builtin,BuiltinType BT,llvm::raw_ostream & OS)217 void PrintAttributes(const Record *Builtin, BuiltinType BT,
218                      llvm::raw_ostream &OS) {
219   OS << '\"';
220   if (Builtin->isSubClassOf("LibBuiltin")) {
221     if (BT == BuiltinType::LibBuiltin) {
222       OS << 'f';
223     } else {
224       OS << 'F';
225       if (Builtin->getValueAsBit("OnlyBuiltinPrefixedAliasIsConstexpr"))
226         OS << 'E';
227     }
228   }
229 
230   if (auto NS = Builtin->getValueAsOptionalString("Namespace")) {
231     if (NS != "std")
232       PrintFatalError(Builtin->getFieldLoc("Namespace"), "Unknown namespace: ");
233     OS << "z";
234   }
235 
236   for (const auto *Attr : Builtin->getValueAsListOfDefs("Attributes")) {
237     OS << Attr->getValueAsString("Mangling");
238     if (Attr->isSubClassOf("IndexedAttribute"))
239       OS << ':' << Attr->getValueAsInt("Index") << ':';
240   }
241   OS << '\"';
242 }
243 
EmitBuiltinDef(llvm::raw_ostream & OS,StringRef Substitution,const Record * Builtin,Twine Spelling,BuiltinType BT)244 void EmitBuiltinDef(llvm::raw_ostream &OS, StringRef Substitution,
245                     const Record *Builtin, Twine Spelling, BuiltinType BT) {
246   if (Builtin->getValueAsBit("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("Languages");
279     break;
280   }
281   case BuiltinType::TargetBuiltin:
282     OS << ", \"" << Builtin->getValueAsString("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 
getTemplateInsts(const Record * R)297 TemplateInsts getTemplateInsts(const Record *R) {
298   TemplateInsts temp;
299   auto Substitutions = R->getValueAsListOfStrings("Substitutions");
300   auto Affixes = R->getValueAsListOfStrings("Affixes");
301   temp.IsPrefix = R->getValueAsBit("AsPrefix");
302 
303   if (Substitutions.size() != Affixes.size())
304     PrintFatalError(R->getLoc(), "Substitutions and affixes "
305                                  "don't have the same lengths");
306 
307   for (auto [Affix, Substitution] : llvm::zip(Affixes, Substitutions)) {
308     temp.Substitution.emplace_back(Substitution);
309     temp.Affix.emplace_back(Affix);
310   }
311   return temp;
312 }
313 
EmitBuiltin(llvm::raw_ostream & OS,const Record * Builtin)314 void EmitBuiltin(llvm::raw_ostream &OS, const Record *Builtin) {
315   TemplateInsts Templates = {};
316   if (Builtin->isSubClassOf("Template")) {
317     Templates = getTemplateInsts(Builtin);
318   } else {
319     Templates.Affix.emplace_back();
320     Templates.Substitution.emplace_back();
321   }
322 
323   for (auto [Substitution, Affix] :
324        llvm::zip(Templates.Substitution, Templates.Affix)) {
325     for (StringRef Spelling : Builtin->getValueAsListOfStrings("Spellings")) {
326       auto FullSpelling =
327           (Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str();
328       BuiltinType BT = BuiltinType::Builtin;
329       if (Builtin->isSubClassOf("AtomicBuiltin")) {
330         BT = BuiltinType::AtomicBuiltin;
331       } else if (Builtin->isSubClassOf("LangBuiltin")) {
332         BT = BuiltinType::LangBuiltin;
333       } else if (Builtin->isSubClassOf("TargetBuiltin")) {
334         BT = BuiltinType::TargetBuiltin;
335       } else if (Builtin->isSubClassOf("LibBuiltin")) {
336         BT = BuiltinType::LibBuiltin;
337         if (Builtin->getValueAsBit("AddBuiltinPrefixedAlias"))
338           EmitBuiltinDef(OS, Substitution, Builtin,
339                          std::string("__builtin_") + FullSpelling,
340                          BuiltinType::Builtin);
341       }
342       EmitBuiltinDef(OS, Substitution, Builtin, FullSpelling, BT);
343     }
344   }
345 }
346 } // namespace
347 
EmitClangBuiltins(llvm::RecordKeeper & Records,llvm::raw_ostream & OS)348 void clang::EmitClangBuiltins(llvm::RecordKeeper &Records,
349                               llvm::raw_ostream &OS) {
350   emitSourceFileHeader("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("AtomicBuiltin"))
375     EmitBuiltin(OS, Builtin);
376 
377   for (const auto *Builtin : Records.getAllDerivedDefinitions("Builtin")) {
378     if (Builtin->isSubClassOf("AtomicBuiltin"))
379       continue;
380     EmitBuiltin(OS, Builtin);
381   }
382 
383   for (const auto *Entry : Records.getAllDerivedDefinitions("CustomEntry")) {
384     OS << Entry->getValueAsString("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