xref: /freebsd/contrib/llvm-project/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===- ClangOpenCLBuiltinEmitter.cpp - Generate Clang OpenCL Builtin handling
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric //                     The LLVM Compiler Infrastructure
4*0b57cec5SDimitry Andric //
5*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
7*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
10*0b57cec5SDimitry Andric //
11*0b57cec5SDimitry Andric // This tablegen backend emits code for checking whether a function is an
12*0b57cec5SDimitry Andric // OpenCL builtin function. If so, all overloads of this function are
13*0b57cec5SDimitry Andric // added to the LookupResult. The generated include file is used by
14*0b57cec5SDimitry Andric // SemaLookup.cpp
15*0b57cec5SDimitry Andric //
16*0b57cec5SDimitry Andric // For a successful lookup of e.g. the "cos" builtin, isOpenCLBuiltin("cos")
17*0b57cec5SDimitry Andric // returns a pair <Index, Len>.
18*0b57cec5SDimitry Andric // OpenCLBuiltins[Index] to OpenCLBuiltins[Index + Len] contains the pairs
19*0b57cec5SDimitry Andric // <SigIndex, SigLen> of the overloads of "cos".
20*0b57cec5SDimitry Andric // OpenCLSignature[SigIndex] to OpenCLSignature[SigIndex + SigLen] contains
21*0b57cec5SDimitry Andric // one of the signatures of "cos". The OpenCLSignature entry can be
22*0b57cec5SDimitry Andric // referenced by other functions, i.e. "sin", since multiple OpenCL builtins
23*0b57cec5SDimitry Andric // share the same signature.
24*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
25*0b57cec5SDimitry Andric 
26*0b57cec5SDimitry Andric #include "llvm/ADT/MapVector.h"
27*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
28*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
29*0b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h"
30*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
31*0b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
32*0b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
33*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
34*0b57cec5SDimitry Andric #include "llvm/TableGen/Error.h"
35*0b57cec5SDimitry Andric #include "llvm/TableGen/Record.h"
36*0b57cec5SDimitry Andric #include "llvm/TableGen/StringMatcher.h"
37*0b57cec5SDimitry Andric #include "llvm/TableGen/TableGenBackend.h"
38*0b57cec5SDimitry Andric #include <set>
39*0b57cec5SDimitry Andric 
40*0b57cec5SDimitry Andric using namespace llvm;
41*0b57cec5SDimitry Andric 
42*0b57cec5SDimitry Andric namespace {
43*0b57cec5SDimitry Andric class BuiltinNameEmitter {
44*0b57cec5SDimitry Andric public:
45*0b57cec5SDimitry Andric   BuiltinNameEmitter(RecordKeeper &Records, raw_ostream &OS)
46*0b57cec5SDimitry Andric       : Records(Records), OS(OS) {}
47*0b57cec5SDimitry Andric 
48*0b57cec5SDimitry Andric   // Entrypoint to generate the functions and structures for checking
49*0b57cec5SDimitry Andric   // whether a function is an OpenCL builtin function.
50*0b57cec5SDimitry Andric   void Emit();
51*0b57cec5SDimitry Andric 
52*0b57cec5SDimitry Andric private:
53*0b57cec5SDimitry Andric   // Contains OpenCL builtin functions and related information, stored as
54*0b57cec5SDimitry Andric   // Record instances. They are coming from the associated TableGen file.
55*0b57cec5SDimitry Andric   RecordKeeper &Records;
56*0b57cec5SDimitry Andric 
57*0b57cec5SDimitry Andric   // The output file.
58*0b57cec5SDimitry Andric   raw_ostream &OS;
59*0b57cec5SDimitry Andric 
60*0b57cec5SDimitry Andric   // Emit the enums and structs.
61*0b57cec5SDimitry Andric   void EmitDeclarations();
62*0b57cec5SDimitry Andric 
63*0b57cec5SDimitry Andric   // Parse the Records generated by TableGen and populate OverloadInfo and
64*0b57cec5SDimitry Andric   // SignatureSet.
65*0b57cec5SDimitry Andric   void GetOverloads();
66*0b57cec5SDimitry Andric 
67*0b57cec5SDimitry Andric   // Emit the OpenCLSignature table. This table contains all possible
68*0b57cec5SDimitry Andric   // signatures, and is a struct OpenCLType. A signature is composed of a
69*0b57cec5SDimitry Andric   // return type (mandatory), followed by zero or more argument types.
70*0b57cec5SDimitry Andric   // E.g.:
71*0b57cec5SDimitry Andric   // // 12
72*0b57cec5SDimitry Andric   // { OCLT_uchar, 4, clang::LangAS::Default, false },
73*0b57cec5SDimitry Andric   // { OCLT_float, 4, clang::LangAS::Default, false },
74*0b57cec5SDimitry Andric   // This means that index 12 represents a signature
75*0b57cec5SDimitry Andric   //   - returning a uchar vector of 4 elements, and
76*0b57cec5SDimitry Andric   //   - taking as first argument a float vector of 4 elements.
77*0b57cec5SDimitry Andric   void EmitSignatureTable();
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric   // Emit the OpenCLBuiltins table. This table contains all overloads of
80*0b57cec5SDimitry Andric   // each function, and is a struct OpenCLBuiltinDecl.
81*0b57cec5SDimitry Andric   // E.g.:
82*0b57cec5SDimitry Andric   // // acos
83*0b57cec5SDimitry Andric   //   { 2, 0, "", 100 },
84*0b57cec5SDimitry Andric   // This means that the signature of this acos overload is defined in OpenCL
85*0b57cec5SDimitry Andric   // version 1.0 (100) and does not belong to any extension ("").  It has a
86*0b57cec5SDimitry Andric   // 1 argument (+1 for the return type), stored at index 0 in the
87*0b57cec5SDimitry Andric   // OpenCLSignature table.
88*0b57cec5SDimitry Andric   void EmitBuiltinTable();
89*0b57cec5SDimitry Andric 
90*0b57cec5SDimitry Andric   // Emit a StringMatcher function to check whether a function name is an
91*0b57cec5SDimitry Andric   // OpenCL builtin function name.
92*0b57cec5SDimitry Andric   void EmitStringMatcher();
93*0b57cec5SDimitry Andric 
94*0b57cec5SDimitry Andric   // Emit a function returning the clang QualType instance associated with
95*0b57cec5SDimitry Andric   // the TableGen Record Type.
96*0b57cec5SDimitry Andric   void EmitQualTypeFinder();
97*0b57cec5SDimitry Andric 
98*0b57cec5SDimitry Andric   // Contains a list of the available signatures, without the name of the
99*0b57cec5SDimitry Andric   // function. Each pair consists of a signature and a cumulative index.
100*0b57cec5SDimitry Andric   // E.g.:  <<float, float>, 0>,
101*0b57cec5SDimitry Andric   //        <<float, int, int, 2>>,
102*0b57cec5SDimitry Andric   //        <<float>, 5>,
103*0b57cec5SDimitry Andric   //        ...
104*0b57cec5SDimitry Andric   //        <<double, double>, 35>.
105*0b57cec5SDimitry Andric   std::vector<std::pair<std::vector<Record *>, unsigned>> SignatureSet;
106*0b57cec5SDimitry Andric 
107*0b57cec5SDimitry Andric   // Map the name of a builtin function to its prototypes (instances of the
108*0b57cec5SDimitry Andric   // TableGen "Builtin" class).
109*0b57cec5SDimitry Andric   // Each prototype is registered as a pair of:
110*0b57cec5SDimitry Andric   //   <pointer to the "Builtin" instance,
111*0b57cec5SDimitry Andric   //    cumulative index of the associated signature in the SignatureSet>
112*0b57cec5SDimitry Andric   // E.g.:  The function cos: (float cos(float), double cos(double), ...)
113*0b57cec5SDimitry Andric   //        <"cos", <<ptrToPrototype0, 5>,
114*0b57cec5SDimitry Andric   //                <ptrToPrototype1, 35>>
115*0b57cec5SDimitry Andric   //                <ptrToPrototype2, 79>>
116*0b57cec5SDimitry Andric   // ptrToPrototype1 has the following signature: <double, double>
117*0b57cec5SDimitry Andric   MapVector<StringRef, std::vector<std::pair<const Record *, unsigned>>>
118*0b57cec5SDimitry Andric       OverloadInfo;
119*0b57cec5SDimitry Andric };
120*0b57cec5SDimitry Andric } // namespace
121*0b57cec5SDimitry Andric 
122*0b57cec5SDimitry Andric void BuiltinNameEmitter::Emit() {
123*0b57cec5SDimitry Andric   emitSourceFileHeader("OpenCL Builtin handling", OS);
124*0b57cec5SDimitry Andric 
125*0b57cec5SDimitry Andric   OS << "#include \"llvm/ADT/StringRef.h\"\n";
126*0b57cec5SDimitry Andric   OS << "using namespace clang;\n\n";
127*0b57cec5SDimitry Andric 
128*0b57cec5SDimitry Andric   EmitDeclarations();
129*0b57cec5SDimitry Andric 
130*0b57cec5SDimitry Andric   GetOverloads();
131*0b57cec5SDimitry Andric 
132*0b57cec5SDimitry Andric   EmitSignatureTable();
133*0b57cec5SDimitry Andric 
134*0b57cec5SDimitry Andric   EmitBuiltinTable();
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric   EmitStringMatcher();
137*0b57cec5SDimitry Andric 
138*0b57cec5SDimitry Andric   EmitQualTypeFinder();
139*0b57cec5SDimitry Andric }
140*0b57cec5SDimitry Andric 
141*0b57cec5SDimitry Andric void BuiltinNameEmitter::EmitDeclarations() {
142*0b57cec5SDimitry Andric   OS << "enum OpenCLTypeID {\n";
143*0b57cec5SDimitry Andric   std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type");
144*0b57cec5SDimitry Andric   StringMap<bool> TypesSeen;
145*0b57cec5SDimitry Andric   for (const auto *T : Types) {
146*0b57cec5SDimitry Andric     if (TypesSeen.find(T->getValueAsString("Name")) == TypesSeen.end())
147*0b57cec5SDimitry Andric       OS << "  OCLT_" + T->getValueAsString("Name") << ",\n";
148*0b57cec5SDimitry Andric     TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true));
149*0b57cec5SDimitry Andric   }
150*0b57cec5SDimitry Andric   OS << "};\n";
151*0b57cec5SDimitry Andric 
152*0b57cec5SDimitry Andric   OS << R"(
153*0b57cec5SDimitry Andric 
154*0b57cec5SDimitry Andric // Type used in a prototype of an OpenCL builtin function.
155*0b57cec5SDimitry Andric struct OpenCLType {
156*0b57cec5SDimitry Andric   // A type (e.g.: float, int, ...)
157*0b57cec5SDimitry Andric   OpenCLTypeID ID;
158*0b57cec5SDimitry Andric   // Size of vector (if applicable)
159*0b57cec5SDimitry Andric   unsigned VectorWidth;
160*0b57cec5SDimitry Andric   // Address space of the pointer (if applicable)
161*0b57cec5SDimitry Andric   LangAS AS;
162*0b57cec5SDimitry Andric   // Whether the type is a pointer
163*0b57cec5SDimitry Andric   bool isPointer;
164*0b57cec5SDimitry Andric };
165*0b57cec5SDimitry Andric 
166*0b57cec5SDimitry Andric // One overload of an OpenCL builtin function.
167*0b57cec5SDimitry Andric struct OpenCLBuiltinDecl {
168*0b57cec5SDimitry Andric   // Number of arguments for the signature
169*0b57cec5SDimitry Andric   unsigned NumArgs;
170*0b57cec5SDimitry Andric   // Index in the OpenCLSignature table to get the required types
171*0b57cec5SDimitry Andric   unsigned ArgTableIndex;
172*0b57cec5SDimitry Andric   // Extension to which it belongs (e.g. cl_khr_subgroups)
173*0b57cec5SDimitry Andric   const char *Extension;
174*0b57cec5SDimitry Andric   // Version in which it was introduced (e.g. CL20)
175*0b57cec5SDimitry Andric   unsigned Version;
176*0b57cec5SDimitry Andric };
177*0b57cec5SDimitry Andric 
178*0b57cec5SDimitry Andric )";
179*0b57cec5SDimitry Andric }
180*0b57cec5SDimitry Andric 
181*0b57cec5SDimitry Andric void BuiltinNameEmitter::GetOverloads() {
182*0b57cec5SDimitry Andric   unsigned CumulativeSignIndex = 0;
183*0b57cec5SDimitry Andric   std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin");
184*0b57cec5SDimitry Andric   for (const auto *B : Builtins) {
185*0b57cec5SDimitry Andric     StringRef BName = B->getValueAsString("Name");
186*0b57cec5SDimitry Andric     if (OverloadInfo.find(BName) == OverloadInfo.end()) {
187*0b57cec5SDimitry Andric       OverloadInfo.insert(std::make_pair(
188*0b57cec5SDimitry Andric           BName, std::vector<std::pair<const Record *, unsigned>>{}));
189*0b57cec5SDimitry Andric     }
190*0b57cec5SDimitry Andric 
191*0b57cec5SDimitry Andric     auto Signature = B->getValueAsListOfDefs("Signature");
192*0b57cec5SDimitry Andric     auto it =
193*0b57cec5SDimitry Andric         std::find_if(SignatureSet.begin(), SignatureSet.end(),
194*0b57cec5SDimitry Andric                      [&](const std::pair<std::vector<Record *>, unsigned> &a) {
195*0b57cec5SDimitry Andric                        return a.first == Signature;
196*0b57cec5SDimitry Andric                      });
197*0b57cec5SDimitry Andric     unsigned SignIndex;
198*0b57cec5SDimitry Andric     if (it == SignatureSet.end()) {
199*0b57cec5SDimitry Andric       SignatureSet.push_back(std::make_pair(Signature, CumulativeSignIndex));
200*0b57cec5SDimitry Andric       SignIndex = CumulativeSignIndex;
201*0b57cec5SDimitry Andric       CumulativeSignIndex += Signature.size();
202*0b57cec5SDimitry Andric     } else {
203*0b57cec5SDimitry Andric       SignIndex = it->second;
204*0b57cec5SDimitry Andric     }
205*0b57cec5SDimitry Andric     OverloadInfo[BName].push_back(std::make_pair(B, SignIndex));
206*0b57cec5SDimitry Andric   }
207*0b57cec5SDimitry Andric }
208*0b57cec5SDimitry Andric 
209*0b57cec5SDimitry Andric void BuiltinNameEmitter::EmitSignatureTable() {
210*0b57cec5SDimitry Andric   OS << "static const OpenCLType OpenCLSignature[] = {\n";
211*0b57cec5SDimitry Andric   for (auto &P : SignatureSet) {
212*0b57cec5SDimitry Andric     OS << "// " << P.second << "\n";
213*0b57cec5SDimitry Andric     for (Record *R : P.first) {
214*0b57cec5SDimitry Andric       OS << "{ OCLT_" << R->getValueAsString("Name") << ", "
215*0b57cec5SDimitry Andric          << R->getValueAsInt("VecWidth") << ", "
216*0b57cec5SDimitry Andric          << R->getValueAsString("AddrSpace") << ", "
217*0b57cec5SDimitry Andric          << R->getValueAsBit("IsPointer") << "},";
218*0b57cec5SDimitry Andric       OS << "\n";
219*0b57cec5SDimitry Andric     }
220*0b57cec5SDimitry Andric   }
221*0b57cec5SDimitry Andric   OS << "};\n\n";
222*0b57cec5SDimitry Andric }
223*0b57cec5SDimitry Andric 
224*0b57cec5SDimitry Andric void BuiltinNameEmitter::EmitBuiltinTable() {
225*0b57cec5SDimitry Andric   OS << "static const OpenCLBuiltinDecl OpenCLBuiltins[] = {\n";
226*0b57cec5SDimitry Andric   for (auto &i : OverloadInfo) {
227*0b57cec5SDimitry Andric     StringRef Name = i.first;
228*0b57cec5SDimitry Andric     OS << "// " << Name << "\n";
229*0b57cec5SDimitry Andric     for (auto &Overload : i.second) {
230*0b57cec5SDimitry Andric       OS << "  { " << Overload.first->getValueAsListOfDefs("Signature").size()
231*0b57cec5SDimitry Andric          << ", " << Overload.second << ", " << '"'
232*0b57cec5SDimitry Andric          << Overload.first->getValueAsString("Extension") << "\", "
233*0b57cec5SDimitry Andric          << Overload.first->getValueAsDef("Version")->getValueAsInt("Version")
234*0b57cec5SDimitry Andric          << " },\n";
235*0b57cec5SDimitry Andric     }
236*0b57cec5SDimitry Andric   }
237*0b57cec5SDimitry Andric   OS << "};\n\n";
238*0b57cec5SDimitry Andric }
239*0b57cec5SDimitry Andric 
240*0b57cec5SDimitry Andric void BuiltinNameEmitter::EmitStringMatcher() {
241*0b57cec5SDimitry Andric   std::vector<StringMatcher::StringPair> ValidBuiltins;
242*0b57cec5SDimitry Andric   unsigned CumulativeIndex = 1;
243*0b57cec5SDimitry Andric   for (auto &i : OverloadInfo) {
244*0b57cec5SDimitry Andric     auto &Ov = i.second;
245*0b57cec5SDimitry Andric     std::string RetStmt;
246*0b57cec5SDimitry Andric     raw_string_ostream SS(RetStmt);
247*0b57cec5SDimitry Andric     SS << "return std::make_pair(" << CumulativeIndex << ", " << Ov.size()
248*0b57cec5SDimitry Andric        << ");";
249*0b57cec5SDimitry Andric     SS.flush();
250*0b57cec5SDimitry Andric     CumulativeIndex += Ov.size();
251*0b57cec5SDimitry Andric 
252*0b57cec5SDimitry Andric     ValidBuiltins.push_back(StringMatcher::StringPair(i.first, RetStmt));
253*0b57cec5SDimitry Andric   }
254*0b57cec5SDimitry Andric 
255*0b57cec5SDimitry Andric   OS << R"(
256*0b57cec5SDimitry Andric // Return 0 if name is not a recognized OpenCL builtin, or an index
257*0b57cec5SDimitry Andric // into a table of declarations if it is an OpenCL builtin.
258*0b57cec5SDimitry Andric static std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef name) {
259*0b57cec5SDimitry Andric 
260*0b57cec5SDimitry Andric )";
261*0b57cec5SDimitry Andric 
262*0b57cec5SDimitry Andric   StringMatcher("name", ValidBuiltins, OS).Emit(0, true);
263*0b57cec5SDimitry Andric 
264*0b57cec5SDimitry Andric   OS << "  return std::make_pair(0, 0);\n";
265*0b57cec5SDimitry Andric   OS << "}\n";
266*0b57cec5SDimitry Andric }
267*0b57cec5SDimitry Andric 
268*0b57cec5SDimitry Andric void BuiltinNameEmitter::EmitQualTypeFinder() {
269*0b57cec5SDimitry Andric   OS << R"(
270*0b57cec5SDimitry Andric 
271*0b57cec5SDimitry Andric static QualType OCL2Qual(ASTContext &Context, OpenCLType Ty) {
272*0b57cec5SDimitry Andric   QualType RT = Context.VoidTy;
273*0b57cec5SDimitry Andric   switch (Ty.ID) {
274*0b57cec5SDimitry Andric )";
275*0b57cec5SDimitry Andric 
276*0b57cec5SDimitry Andric   std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type");
277*0b57cec5SDimitry Andric   StringMap<bool> TypesSeen;
278*0b57cec5SDimitry Andric 
279*0b57cec5SDimitry Andric   for (const auto *T : Types) {
280*0b57cec5SDimitry Andric     // Check we have not seen this Type
281*0b57cec5SDimitry Andric     if (TypesSeen.find(T->getValueAsString("Name")) != TypesSeen.end())
282*0b57cec5SDimitry Andric       continue;
283*0b57cec5SDimitry Andric     TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true));
284*0b57cec5SDimitry Andric 
285*0b57cec5SDimitry Andric     // Check the Type does not have an "abstract" QualType
286*0b57cec5SDimitry Andric     auto QT = T->getValueAsDef("QTName");
287*0b57cec5SDimitry Andric     if (QT->getValueAsString("Name") == "null")
288*0b57cec5SDimitry Andric       continue;
289*0b57cec5SDimitry Andric 
290*0b57cec5SDimitry Andric     OS << "  case OCLT_" << T->getValueAsString("Name") << ":\n";
291*0b57cec5SDimitry Andric     OS << "    RT = Context." << QT->getValueAsString("Name") << ";\n";
292*0b57cec5SDimitry Andric     OS << "    break;\n";
293*0b57cec5SDimitry Andric   }
294*0b57cec5SDimitry Andric   OS << "  }\n";
295*0b57cec5SDimitry Andric 
296*0b57cec5SDimitry Andric   // Special cases
297*0b57cec5SDimitry Andric   OS << R"(
298*0b57cec5SDimitry Andric   if (Ty.VectorWidth > 0)
299*0b57cec5SDimitry Andric     RT = Context.getExtVectorType(RT, Ty.VectorWidth);
300*0b57cec5SDimitry Andric 
301*0b57cec5SDimitry Andric   if (Ty.isPointer) {
302*0b57cec5SDimitry Andric     RT = Context.getAddrSpaceQualType(RT, Ty.AS);
303*0b57cec5SDimitry Andric     RT = Context.getPointerType(RT);
304*0b57cec5SDimitry Andric   }
305*0b57cec5SDimitry Andric 
306*0b57cec5SDimitry Andric   return RT;
307*0b57cec5SDimitry Andric }
308*0b57cec5SDimitry Andric )";
309*0b57cec5SDimitry Andric }
310*0b57cec5SDimitry Andric 
311*0b57cec5SDimitry Andric namespace clang {
312*0b57cec5SDimitry Andric 
313*0b57cec5SDimitry Andric void EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) {
314*0b57cec5SDimitry Andric   BuiltinNameEmitter NameChecker(Records, OS);
315*0b57cec5SDimitry Andric   NameChecker.Emit();
316*0b57cec5SDimitry Andric }
317*0b57cec5SDimitry Andric 
318*0b57cec5SDimitry Andric } // end namespace clang
319