/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ClangOpenCLBuiltinEmitter.cpp - Generate Clang OpenCL Builtin handling |
2 | | // |
3 | | // The LLVM Compiler Infrastructure |
4 | | // |
5 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
6 | | // See https://llvm.org/LICENSE.txt for license information. |
7 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
8 | | // |
9 | | //===----------------------------------------------------------------------===// |
10 | | // |
11 | | // These backends consume the definitions of OpenCL builtin functions in |
12 | | // clang/lib/Sema/OpenCLBuiltins.td and produce builtin handling code for |
13 | | // inclusion in SemaLookup.cpp, or a test file that calls all declared builtins. |
14 | | // |
15 | | //===----------------------------------------------------------------------===// |
16 | | |
17 | | #include "TableGenBackends.h" |
18 | | #include "llvm/ADT/MapVector.h" |
19 | | #include "llvm/ADT/STLExtras.h" |
20 | | #include "llvm/ADT/SmallSet.h" |
21 | | #include "llvm/ADT/SmallString.h" |
22 | | #include "llvm/ADT/StringExtras.h" |
23 | | #include "llvm/ADT/StringMap.h" |
24 | | #include "llvm/ADT/StringRef.h" |
25 | | #include "llvm/ADT/StringSwitch.h" |
26 | | #include "llvm/Support/ErrorHandling.h" |
27 | | #include "llvm/Support/raw_ostream.h" |
28 | | #include "llvm/TableGen/Error.h" |
29 | | #include "llvm/TableGen/Record.h" |
30 | | #include "llvm/TableGen/StringMatcher.h" |
31 | | #include "llvm/TableGen/TableGenBackend.h" |
32 | | |
33 | | using namespace llvm; |
34 | | |
35 | | namespace { |
36 | | |
37 | | // A list of signatures that are shared by one or more builtin functions. |
38 | | struct BuiltinTableEntries { |
39 | | SmallVector<StringRef, 4> Names; |
40 | | std::vector<std::pair<const Record *, unsigned>> Signatures; |
41 | | }; |
42 | | |
43 | | // This tablegen backend emits code for checking whether a function is an |
44 | | // OpenCL builtin function. If so, all overloads of this function are |
45 | | // added to the LookupResult. The generated include file is used by |
46 | | // SemaLookup.cpp |
47 | | // |
48 | | // For a successful lookup of e.g. the "cos" builtin, isOpenCLBuiltin("cos") |
49 | | // returns a pair <Index, Len>. |
50 | | // BuiltinTable[Index] to BuiltinTable[Index + Len] contains the pairs |
51 | | // <SigIndex, SigLen> of the overloads of "cos". |
52 | | // SignatureTable[SigIndex] to SignatureTable[SigIndex + SigLen] contains |
53 | | // one of the signatures of "cos". The SignatureTable entry can be |
54 | | // referenced by other functions, e.g. "sin", to exploit the fact that |
55 | | // many OpenCL builtins share the same signature. |
56 | | // |
57 | | // The file generated by this TableGen emitter contains the following: |
58 | | // |
59 | | // * Structs and enums to represent types and function signatures. |
60 | | // |
61 | | // * const char *FunctionExtensionTable[] |
62 | | // List of space-separated OpenCL extensions. A builtin references an |
63 | | // entry in this table when the builtin requires a particular (set of) |
64 | | // extension(s) to be enabled. |
65 | | // |
66 | | // * OpenCLTypeStruct TypeTable[] |
67 | | // Type information for return types and arguments. |
68 | | // |
69 | | // * unsigned SignatureTable[] |
70 | | // A list of types representing function signatures. Each entry is an index |
71 | | // into the above TypeTable. Multiple entries following each other form a |
72 | | // signature, where the first entry is the return type and subsequent |
73 | | // entries are the argument types. |
74 | | // |
75 | | // * OpenCLBuiltinStruct BuiltinTable[] |
76 | | // Each entry represents one overload of an OpenCL builtin function and |
77 | | // consists of an index into the SignatureTable and the number of arguments. |
78 | | // |
79 | | // * std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef Name) |
80 | | // Find out whether a string matches an existing OpenCL builtin function |
81 | | // name and return an index into BuiltinTable and the number of overloads. |
82 | | // |
83 | | // * void OCL2Qual(Sema&, OpenCLTypeStruct, std::vector<QualType>&) |
84 | | // Convert an OpenCLTypeStruct type to a list of QualType instances. |
85 | | // One OpenCLTypeStruct can represent multiple types, primarily when using |
86 | | // GenTypes. |
87 | | // |
88 | | class BuiltinNameEmitter { |
89 | | public: |
90 | | BuiltinNameEmitter(RecordKeeper &Records, raw_ostream &OS) |
91 | 0 | : Records(Records), OS(OS) {} |
92 | | |
93 | | // Entrypoint to generate the functions and structures for checking |
94 | | // whether a function is an OpenCL builtin function. |
95 | | void Emit(); |
96 | | |
97 | | private: |
98 | | // A list of indices into the builtin function table. |
99 | | using BuiltinIndexListTy = SmallVector<unsigned, 11>; |
100 | | |
101 | | // Contains OpenCL builtin functions and related information, stored as |
102 | | // Record instances. They are coming from the associated TableGen file. |
103 | | RecordKeeper &Records; |
104 | | |
105 | | // The output file. |
106 | | raw_ostream &OS; |
107 | | |
108 | | // Helper function for BuiltinNameEmitter::EmitDeclarations. Generate enum |
109 | | // definitions in the Output string parameter, and save their Record instances |
110 | | // in the List parameter. |
111 | | // \param Types (in) List containing the Types to extract. |
112 | | // \param TypesSeen (inout) List containing the Types already extracted. |
113 | | // \param Output (out) String containing the enums to emit in the output file. |
114 | | // \param List (out) List containing the extracted Types, except the Types in |
115 | | // TypesSeen. |
116 | | void ExtractEnumTypes(std::vector<Record *> &Types, |
117 | | StringMap<bool> &TypesSeen, std::string &Output, |
118 | | std::vector<const Record *> &List); |
119 | | |
120 | | // Emit the enum or struct used in the generated file. |
121 | | // Populate the TypeList at the same time. |
122 | | void EmitDeclarations(); |
123 | | |
124 | | // Parse the Records generated by TableGen to populate the SignaturesList, |
125 | | // FctOverloadMap and TypeMap. |
126 | | void GetOverloads(); |
127 | | |
128 | | // Compare two lists of signatures and check that e.g. the OpenCL version, |
129 | | // function attributes, and extension are equal for each signature. |
130 | | // \param Candidate (in) Entry in the SignatureListMap to check. |
131 | | // \param SignatureList (in) List of signatures of the considered function. |
132 | | // \returns true if the two lists of signatures are identical. |
133 | | bool CanReuseSignature( |
134 | | BuiltinIndexListTy *Candidate, |
135 | | std::vector<std::pair<const Record *, unsigned>> &SignatureList); |
136 | | |
137 | | // Group functions with the same list of signatures by populating the |
138 | | // SignatureListMap. |
139 | | // Some builtin functions have the same list of signatures, for example the |
140 | | // "sin" and "cos" functions. To save space in the BuiltinTable, the |
141 | | // "isOpenCLBuiltin" function will have the same output for these two |
142 | | // function names. |
143 | | void GroupBySignature(); |
144 | | |
145 | | // Emit the FunctionExtensionTable that lists all function extensions. |
146 | | void EmitExtensionTable(); |
147 | | |
148 | | // Emit the TypeTable containing all types used by OpenCL builtins. |
149 | | void EmitTypeTable(); |
150 | | |
151 | | // Emit the SignatureTable. This table contains all the possible signatures. |
152 | | // A signature is stored as a list of indexes of the TypeTable. |
153 | | // The first index references the return type (mandatory), and the followings |
154 | | // reference its arguments. |
155 | | // E.g.: |
156 | | // 15, 2, 15 can represent a function with the signature: |
157 | | // int func(float, int) |
158 | | // The "int" type being at the index 15 in the TypeTable. |
159 | | void EmitSignatureTable(); |
160 | | |
161 | | // Emit the BuiltinTable table. This table contains all the overloads of |
162 | | // each function, and is a struct OpenCLBuiltinDecl. |
163 | | // E.g.: |
164 | | // // 891 convert_float2_rtn |
165 | | // { 58, 2, 3, 100, 0 }, |
166 | | // This means that the signature of this convert_float2_rtn overload has |
167 | | // 1 argument (+1 for the return type), stored at index 58 in |
168 | | // the SignatureTable. This prototype requires extension "3" in the |
169 | | // FunctionExtensionTable. The last two values represent the minimum (1.0) |
170 | | // and maximum (0, meaning no max version) OpenCL version in which this |
171 | | // overload is supported. |
172 | | void EmitBuiltinTable(); |
173 | | |
174 | | // Emit a StringMatcher function to check whether a function name is an |
175 | | // OpenCL builtin function name. |
176 | | void EmitStringMatcher(); |
177 | | |
178 | | // Emit a function returning the clang QualType instance associated with |
179 | | // the TableGen Record Type. |
180 | | void EmitQualTypeFinder(); |
181 | | |
182 | | // Contains a list of the available signatures, without the name of the |
183 | | // function. Each pair consists of a signature and a cumulative index. |
184 | | // E.g.: <<float, float>, 0>, |
185 | | // <<float, int, int, 2>>, |
186 | | // <<float>, 5>, |
187 | | // ... |
188 | | // <<double, double>, 35>. |
189 | | std::vector<std::pair<std::vector<Record *>, unsigned>> SignaturesList; |
190 | | |
191 | | // Map the name of a builtin function to its prototypes (instances of the |
192 | | // TableGen "Builtin" class). |
193 | | // Each prototype is registered as a pair of: |
194 | | // <pointer to the "Builtin" instance, |
195 | | // cumulative index of the associated signature in the SignaturesList> |
196 | | // E.g.: The function cos: (float cos(float), double cos(double), ...) |
197 | | // <"cos", <<ptrToPrototype0, 5>, |
198 | | // <ptrToPrototype1, 35>, |
199 | | // <ptrToPrototype2, 79>> |
200 | | // ptrToPrototype1 has the following signature: <double, double> |
201 | | MapVector<StringRef, std::vector<std::pair<const Record *, unsigned>>> |
202 | | FctOverloadMap; |
203 | | |
204 | | // Contains the map of OpenCL types to their index in the TypeTable. |
205 | | MapVector<const Record *, unsigned> TypeMap; |
206 | | |
207 | | // List of OpenCL function extensions mapping extension strings to |
208 | | // an index into the FunctionExtensionTable. |
209 | | StringMap<unsigned> FunctionExtensionIndex; |
210 | | |
211 | | // List of OpenCL type names in the same order as in enum OpenCLTypeID. |
212 | | // This list does not contain generic types. |
213 | | std::vector<const Record *> TypeList; |
214 | | |
215 | | // Same as TypeList, but for generic types only. |
216 | | std::vector<const Record *> GenTypeList; |
217 | | |
218 | | // Map an ordered vector of signatures to their original Record instances, |
219 | | // and to a list of function names that share these signatures. |
220 | | // |
221 | | // For example, suppose the "cos" and "sin" functions have only three |
222 | | // signatures, and these signatures are at index Ix in the SignatureTable: |
223 | | // cos | sin | Signature | Index |
224 | | // float cos(float) | float sin(float) | Signature1 | I1 |
225 | | // double cos(double) | double sin(double) | Signature2 | I2 |
226 | | // half cos(half) | half sin(half) | Signature3 | I3 |
227 | | // |
228 | | // Then we will create a mapping of the vector of signatures: |
229 | | // SignatureListMap[<I1, I2, I3>] = < |
230 | | // <"cos", "sin">, |
231 | | // <Signature1, Signature2, Signature3>> |
232 | | // The function "tan", having the same signatures, would be mapped to the |
233 | | // same entry (<I1, I2, I3>). |
234 | | MapVector<BuiltinIndexListTy *, BuiltinTableEntries> SignatureListMap; |
235 | | }; |
236 | | |
237 | | /// Base class for emitting a file (e.g. header or test) from OpenCLBuiltins.td |
238 | | class OpenCLBuiltinFileEmitterBase { |
239 | | public: |
240 | | OpenCLBuiltinFileEmitterBase(RecordKeeper &Records, raw_ostream &OS) |
241 | 1 | : Records(Records), OS(OS) {} |
242 | 1 | virtual ~OpenCLBuiltinFileEmitterBase() = default; |
243 | | |
244 | | // Entrypoint to generate the functions for testing all OpenCL builtin |
245 | | // functions. |
246 | | virtual void emit() = 0; |
247 | | |
248 | | protected: |
249 | | struct TypeFlags { |
250 | 24.2k | TypeFlags() : IsConst(false), IsVolatile(false), IsPointer(false) {} |
251 | | bool IsConst : 1; |
252 | | bool IsVolatile : 1; |
253 | | bool IsPointer : 1; |
254 | | StringRef AddrSpace; |
255 | | }; |
256 | | |
257 | | // Return a string representation of the given type, such that it can be |
258 | | // used as a type in OpenCL C code. |
259 | | std::string getTypeString(const Record *Type, TypeFlags Flags, |
260 | | int VectorSize) const; |
261 | | |
262 | | // Return the type(s) and vector size(s) for the given type. For |
263 | | // non-GenericTypes, the resulting vectors will contain 1 element. For |
264 | | // GenericTypes, the resulting vectors typically contain multiple elements. |
265 | | void getTypeLists(Record *Type, TypeFlags &Flags, |
266 | | std::vector<Record *> &TypeList, |
267 | | std::vector<int64_t> &VectorList) const; |
268 | | |
269 | | // Expand the TableGen Records representing a builtin function signature into |
270 | | // one or more function signatures. Return them as a vector of a vector of |
271 | | // strings, with each string containing an OpenCL C type and optional |
272 | | // qualifiers. |
273 | | // |
274 | | // The Records may contain GenericTypes, which expand into multiple |
275 | | // signatures. Repeated occurrences of GenericType in a signature expand to |
276 | | // the same types. For example [char, FGenType, FGenType] expands to: |
277 | | // [char, float, float] |
278 | | // [char, float2, float2] |
279 | | // [char, float3, float3] |
280 | | // ... |
281 | | void |
282 | | expandTypesInSignature(const std::vector<Record *> &Signature, |
283 | | SmallVectorImpl<SmallVector<std::string, 2>> &Types); |
284 | | |
285 | | // Emit extension enabling pragmas. |
286 | | void emitExtensionSetup(); |
287 | | |
288 | | // Emit an #if guard for a Builtin's extension. Return the corresponding |
289 | | // closing #endif, or an empty string if no extension #if guard was emitted. |
290 | | std::string emitExtensionGuard(const Record *Builtin); |
291 | | |
292 | | // Emit an #if guard for a Builtin's language version. Return the |
293 | | // corresponding closing #endif, or an empty string if no version #if guard |
294 | | // was emitted. |
295 | | std::string emitVersionGuard(const Record *Builtin); |
296 | | |
297 | | // Emit an #if guard for all type extensions required for the given type |
298 | | // strings. Return the corresponding closing #endif, or an empty string |
299 | | // if no extension #if guard was emitted. |
300 | | StringRef |
301 | | emitTypeExtensionGuards(const SmallVectorImpl<std::string> &Signature); |
302 | | |
303 | | // Map type strings to type extensions (e.g. "half2" -> "cl_khr_fp16"). |
304 | | StringMap<StringRef> TypeExtMap; |
305 | | |
306 | | // Contains OpenCL builtin functions and related information, stored as |
307 | | // Record instances. They are coming from the associated TableGen file. |
308 | | RecordKeeper &Records; |
309 | | |
310 | | // The output file. |
311 | | raw_ostream &OS; |
312 | | }; |
313 | | |
314 | | // OpenCL builtin test generator. This class processes the same TableGen input |
315 | | // as BuiltinNameEmitter, but generates a .cl file that contains a call to each |
316 | | // builtin function described in the .td input. |
317 | | class OpenCLBuiltinTestEmitter : public OpenCLBuiltinFileEmitterBase { |
318 | | public: |
319 | | OpenCLBuiltinTestEmitter(RecordKeeper &Records, raw_ostream &OS) |
320 | 1 | : OpenCLBuiltinFileEmitterBase(Records, OS) {} |
321 | | |
322 | | // Entrypoint to generate the functions for testing all OpenCL builtin |
323 | | // functions. |
324 | | void emit() override; |
325 | | }; |
326 | | |
327 | | } // namespace |
328 | | |
329 | 0 | void BuiltinNameEmitter::Emit() { |
330 | 0 | emitSourceFileHeader("OpenCL Builtin handling", OS); |
331 | |
|
332 | 0 | OS << "#include \"llvm/ADT/StringRef.h\"\n"; |
333 | 0 | OS << "using namespace clang;\n\n"; |
334 | | |
335 | | // Emit enums and structs. |
336 | 0 | EmitDeclarations(); |
337 | | |
338 | | // Parse the Records to populate the internal lists. |
339 | 0 | GetOverloads(); |
340 | 0 | GroupBySignature(); |
341 | | |
342 | | // Emit tables. |
343 | 0 | EmitExtensionTable(); |
344 | 0 | EmitTypeTable(); |
345 | 0 | EmitSignatureTable(); |
346 | 0 | EmitBuiltinTable(); |
347 | | |
348 | | // Emit functions. |
349 | 0 | EmitStringMatcher(); |
350 | 0 | EmitQualTypeFinder(); |
351 | 0 | } |
352 | | |
353 | | void BuiltinNameEmitter::ExtractEnumTypes(std::vector<Record *> &Types, |
354 | | StringMap<bool> &TypesSeen, |
355 | | std::string &Output, |
356 | 0 | std::vector<const Record *> &List) { |
357 | 0 | raw_string_ostream SS(Output); |
358 | |
|
359 | 0 | for (const auto *T : Types) { |
360 | 0 | if (TypesSeen.find(T->getValueAsString("Name")) == TypesSeen.end()) { |
361 | 0 | SS << " OCLT_" + T->getValueAsString("Name") << ",\n"; |
362 | | // Save the type names in the same order as their enum value. Note that |
363 | | // the Record can be a VectorType or something else, only the name is |
364 | | // important. |
365 | 0 | List.push_back(T); |
366 | 0 | TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true)); |
367 | 0 | } |
368 | 0 | } |
369 | 0 | SS.flush(); |
370 | 0 | } |
371 | | |
372 | 0 | void BuiltinNameEmitter::EmitDeclarations() { |
373 | | // Enum of scalar type names (float, int, ...) and generic type sets. |
374 | 0 | OS << "enum OpenCLTypeID {\n"; |
375 | |
|
376 | 0 | StringMap<bool> TypesSeen; |
377 | 0 | std::string GenTypeEnums; |
378 | 0 | std::string TypeEnums; |
379 | | |
380 | | // Extract generic types and non-generic types separately, to keep |
381 | | // gentypes at the end of the enum which simplifies the special handling |
382 | | // for gentypes in SemaLookup. |
383 | 0 | std::vector<Record *> GenTypes = |
384 | 0 | Records.getAllDerivedDefinitions("GenericType"); |
385 | 0 | ExtractEnumTypes(GenTypes, TypesSeen, GenTypeEnums, GenTypeList); |
386 | |
|
387 | 0 | std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type"); |
388 | 0 | ExtractEnumTypes(Types, TypesSeen, TypeEnums, TypeList); |
389 | |
|
390 | 0 | OS << TypeEnums; |
391 | 0 | OS << GenTypeEnums; |
392 | 0 | OS << "};\n"; |
393 | | |
394 | | // Structure definitions. |
395 | 0 | OS << R"( |
396 | 0 | // Image access qualifier. |
397 | 0 | enum OpenCLAccessQual : unsigned char { |
398 | 0 | OCLAQ_None, |
399 | 0 | OCLAQ_ReadOnly, |
400 | 0 | OCLAQ_WriteOnly, |
401 | 0 | OCLAQ_ReadWrite |
402 | 0 | }; |
403 | 0 |
|
404 | 0 | // Represents a return type or argument type. |
405 | 0 | struct OpenCLTypeStruct { |
406 | 0 | // A type (e.g. float, int, ...). |
407 | 0 | const OpenCLTypeID ID; |
408 | 0 | // Vector size (if applicable; 0 for scalars and generic types). |
409 | 0 | const unsigned VectorWidth; |
410 | 0 | // 0 if the type is not a pointer. |
411 | 0 | const bool IsPointer : 1; |
412 | 0 | // 0 if the type is not const. |
413 | 0 | const bool IsConst : 1; |
414 | 0 | // 0 if the type is not volatile. |
415 | 0 | const bool IsVolatile : 1; |
416 | 0 | // Access qualifier. |
417 | 0 | const OpenCLAccessQual AccessQualifier; |
418 | 0 | // Address space of the pointer (if applicable). |
419 | 0 | const LangAS AS; |
420 | 0 | }; |
421 | 0 |
|
422 | 0 | // One overload of an OpenCL builtin function. |
423 | 0 | struct OpenCLBuiltinStruct { |
424 | 0 | // Index of the signature in the OpenCLTypeStruct table. |
425 | 0 | const unsigned SigTableIndex; |
426 | 0 | // Entries between index SigTableIndex and (SigTableIndex + NumTypes - 1) in |
427 | 0 | // the SignatureTable represent the complete signature. The first type at |
428 | 0 | // index SigTableIndex is the return type. |
429 | 0 | const unsigned NumTypes; |
430 | 0 | // Function attribute __attribute__((pure)) |
431 | 0 | const bool IsPure : 1; |
432 | 0 | // Function attribute __attribute__((const)) |
433 | 0 | const bool IsConst : 1; |
434 | 0 | // Function attribute __attribute__((convergent)) |
435 | 0 | const bool IsConv : 1; |
436 | 0 | // OpenCL extension(s) required for this overload. |
437 | 0 | const unsigned short Extension; |
438 | 0 | // OpenCL versions in which this overload is available. |
439 | 0 | const unsigned short Versions; |
440 | 0 | }; |
441 | 0 |
|
442 | 0 | )"; |
443 | 0 | } |
444 | | |
445 | | // Verify that the combination of GenTypes in a signature is supported. |
446 | | // To simplify the logic for creating overloads in SemaLookup, only allow |
447 | | // a signature to contain different GenTypes if these GenTypes represent |
448 | | // the same number of actual scalar or vector types. |
449 | | // |
450 | | // Exit with a fatal error if an unsupported construct is encountered. |
451 | | static void VerifySignature(const std::vector<Record *> &Signature, |
452 | 0 | const Record *BuiltinRec) { |
453 | 0 | unsigned GenTypeVecSizes = 1; |
454 | 0 | unsigned GenTypeTypes = 1; |
455 | |
|
456 | 0 | for (const auto *T : Signature) { |
457 | | // Check all GenericType arguments in this signature. |
458 | 0 | if (T->isSubClassOf("GenericType")) { |
459 | | // Check number of vector sizes. |
460 | 0 | unsigned NVecSizes = |
461 | 0 | T->getValueAsDef("VectorList")->getValueAsListOfInts("List").size(); |
462 | 0 | if (NVecSizes != GenTypeVecSizes && NVecSizes != 1) { |
463 | 0 | if (GenTypeVecSizes > 1) { |
464 | | // We already saw a gentype with a different number of vector sizes. |
465 | 0 | PrintFatalError(BuiltinRec->getLoc(), |
466 | 0 | "number of vector sizes should be equal or 1 for all gentypes " |
467 | 0 | "in a declaration"); |
468 | 0 | } |
469 | 0 | GenTypeVecSizes = NVecSizes; |
470 | 0 | } |
471 | | |
472 | | // Check number of data types. |
473 | 0 | unsigned NTypes = |
474 | 0 | T->getValueAsDef("TypeList")->getValueAsListOfDefs("List").size(); |
475 | 0 | if (NTypes != GenTypeTypes && NTypes != 1) { |
476 | 0 | if (GenTypeTypes > 1) { |
477 | | // We already saw a gentype with a different number of types. |
478 | 0 | PrintFatalError(BuiltinRec->getLoc(), |
479 | 0 | "number of types should be equal or 1 for all gentypes " |
480 | 0 | "in a declaration"); |
481 | 0 | } |
482 | 0 | GenTypeTypes = NTypes; |
483 | 0 | } |
484 | 0 | } |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | 0 | void BuiltinNameEmitter::GetOverloads() { |
489 | | // Populate the TypeMap. |
490 | 0 | std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type"); |
491 | 0 | unsigned I = 0; |
492 | 0 | for (const auto &T : Types) { |
493 | 0 | TypeMap.insert(std::make_pair(T, I++)); |
494 | 0 | } |
495 | | |
496 | | // Populate the SignaturesList and the FctOverloadMap. |
497 | 0 | unsigned CumulativeSignIndex = 0; |
498 | 0 | std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin"); |
499 | 0 | for (const auto *B : Builtins) { |
500 | 0 | StringRef BName = B->getValueAsString("Name"); |
501 | 0 | if (FctOverloadMap.find(BName) == FctOverloadMap.end()) { |
502 | 0 | FctOverloadMap.insert(std::make_pair( |
503 | 0 | BName, std::vector<std::pair<const Record *, unsigned>>{})); |
504 | 0 | } |
505 | |
|
506 | 0 | auto Signature = B->getValueAsListOfDefs("Signature"); |
507 | | // Reuse signatures to avoid unnecessary duplicates. |
508 | 0 | auto it = |
509 | 0 | llvm::find_if(SignaturesList, |
510 | 0 | [&](const std::pair<std::vector<Record *>, unsigned> &a) { |
511 | 0 | return a.first == Signature; |
512 | 0 | }); |
513 | 0 | unsigned SignIndex; |
514 | 0 | if (it == SignaturesList.end()) { |
515 | 0 | VerifySignature(Signature, B); |
516 | 0 | SignaturesList.push_back(std::make_pair(Signature, CumulativeSignIndex)); |
517 | 0 | SignIndex = CumulativeSignIndex; |
518 | 0 | CumulativeSignIndex += Signature.size(); |
519 | 0 | } else { |
520 | 0 | SignIndex = it->second; |
521 | 0 | } |
522 | 0 | FctOverloadMap[BName].push_back(std::make_pair(B, SignIndex)); |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | 0 | void BuiltinNameEmitter::EmitExtensionTable() { |
527 | 0 | OS << "static const char *FunctionExtensionTable[] = {\n"; |
528 | 0 | unsigned Index = 0; |
529 | 0 | std::vector<Record *> FuncExtensions = |
530 | 0 | Records.getAllDerivedDefinitions("FunctionExtension"); |
531 | |
|
532 | 0 | for (const auto &FE : FuncExtensions) { |
533 | | // Emit OpenCL extension table entry. |
534 | 0 | OS << " // " << Index << ": " << FE->getName() << "\n" |
535 | 0 | << " \"" << FE->getValueAsString("ExtName") << "\",\n"; |
536 | | |
537 | | // Record index of this extension. |
538 | 0 | FunctionExtensionIndex[FE->getName()] = Index++; |
539 | 0 | } |
540 | 0 | OS << "};\n\n"; |
541 | 0 | } |
542 | | |
543 | 0 | void BuiltinNameEmitter::EmitTypeTable() { |
544 | 0 | OS << "static const OpenCLTypeStruct TypeTable[] = {\n"; |
545 | 0 | for (const auto &T : TypeMap) { |
546 | 0 | const char *AccessQual = |
547 | 0 | StringSwitch<const char *>(T.first->getValueAsString("AccessQualifier")) |
548 | 0 | .Case("RO", "OCLAQ_ReadOnly") |
549 | 0 | .Case("WO", "OCLAQ_WriteOnly") |
550 | 0 | .Case("RW", "OCLAQ_ReadWrite") |
551 | 0 | .Default("OCLAQ_None"); |
552 | |
|
553 | 0 | OS << " // " << T.second << "\n" |
554 | 0 | << " {OCLT_" << T.first->getValueAsString("Name") << ", " |
555 | 0 | << T.first->getValueAsInt("VecWidth") << ", " |
556 | 0 | << T.first->getValueAsBit("IsPointer") << ", " |
557 | 0 | << T.first->getValueAsBit("IsConst") << ", " |
558 | 0 | << T.first->getValueAsBit("IsVolatile") << ", " |
559 | 0 | << AccessQual << ", " |
560 | 0 | << T.first->getValueAsString("AddrSpace") << "},\n"; |
561 | 0 | } |
562 | 0 | OS << "};\n\n"; |
563 | 0 | } |
564 | | |
565 | 0 | void BuiltinNameEmitter::EmitSignatureTable() { |
566 | | // Store a type (e.g. int, float, int2, ...). The type is stored as an index |
567 | | // of a struct OpenCLType table. Multiple entries following each other form a |
568 | | // signature. |
569 | 0 | OS << "static const unsigned short SignatureTable[] = {\n"; |
570 | 0 | for (const auto &P : SignaturesList) { |
571 | 0 | OS << " // " << P.second << "\n "; |
572 | 0 | for (const Record *R : P.first) { |
573 | 0 | unsigned Entry = TypeMap.find(R)->second; |
574 | 0 | if (Entry > USHRT_MAX) { |
575 | | // Report an error when seeing an entry that is too large for the |
576 | | // current index type (unsigned short). When hitting this, the type |
577 | | // of SignatureTable will need to be changed. |
578 | 0 | PrintFatalError("Entry in SignatureTable exceeds limit."); |
579 | 0 | } |
580 | 0 | OS << Entry << ", "; |
581 | 0 | } |
582 | 0 | OS << "\n"; |
583 | 0 | } |
584 | 0 | OS << "};\n\n"; |
585 | 0 | } |
586 | | |
587 | | // Encode a range MinVersion..MaxVersion into a single bit mask that can be |
588 | | // checked against LangOpts using isOpenCLVersionContainedInMask(). |
589 | | // This must be kept in sync with OpenCLVersionID in OpenCLOptions.h. |
590 | | // (Including OpenCLOptions.h here would be a layering violation.) |
591 | | static unsigned short EncodeVersions(unsigned int MinVersion, |
592 | 0 | unsigned int MaxVersion) { |
593 | 0 | unsigned short Encoded = 0; |
594 | | |
595 | | // A maximum version of 0 means available in all later versions. |
596 | 0 | if (MaxVersion == 0) { |
597 | 0 | MaxVersion = UINT_MAX; |
598 | 0 | } |
599 | |
|
600 | 0 | unsigned VersionIDs[] = {100, 110, 120, 200, 300}; |
601 | 0 | for (unsigned I = 0; I < sizeof(VersionIDs) / sizeof(VersionIDs[0]); I++) { |
602 | 0 | if (VersionIDs[I] >= MinVersion && VersionIDs[I] < MaxVersion) { |
603 | 0 | Encoded |= 1 << I; |
604 | 0 | } |
605 | 0 | } |
606 | |
|
607 | 0 | return Encoded; |
608 | 0 | } |
609 | | |
610 | 0 | void BuiltinNameEmitter::EmitBuiltinTable() { |
611 | 0 | unsigned Index = 0; |
612 | |
|
613 | 0 | OS << "static const OpenCLBuiltinStruct BuiltinTable[] = {\n"; |
614 | 0 | for (const auto &SLM : SignatureListMap) { |
615 | |
|
616 | 0 | OS << " // " << (Index + 1) << ": "; |
617 | 0 | for (const auto &Name : SLM.second.Names) { |
618 | 0 | OS << Name << ", "; |
619 | 0 | } |
620 | 0 | OS << "\n"; |
621 | |
|
622 | 0 | for (const auto &Overload : SLM.second.Signatures) { |
623 | 0 | StringRef ExtName = Overload.first->getValueAsDef("Extension")->getName(); |
624 | 0 | unsigned int MinVersion = |
625 | 0 | Overload.first->getValueAsDef("MinVersion")->getValueAsInt("ID"); |
626 | 0 | unsigned int MaxVersion = |
627 | 0 | Overload.first->getValueAsDef("MaxVersion")->getValueAsInt("ID"); |
628 | |
|
629 | 0 | OS << " { " << Overload.second << ", " |
630 | 0 | << Overload.first->getValueAsListOfDefs("Signature").size() << ", " |
631 | 0 | << (Overload.first->getValueAsBit("IsPure")) << ", " |
632 | 0 | << (Overload.first->getValueAsBit("IsConst")) << ", " |
633 | 0 | << (Overload.first->getValueAsBit("IsConv")) << ", " |
634 | 0 | << FunctionExtensionIndex[ExtName] << ", " |
635 | 0 | << EncodeVersions(MinVersion, MaxVersion) << " },\n"; |
636 | 0 | Index++; |
637 | 0 | } |
638 | 0 | } |
639 | 0 | OS << "};\n\n"; |
640 | 0 | } |
641 | | |
642 | | bool BuiltinNameEmitter::CanReuseSignature( |
643 | | BuiltinIndexListTy *Candidate, |
644 | 0 | std::vector<std::pair<const Record *, unsigned>> &SignatureList) { |
645 | 0 | assert(Candidate->size() == SignatureList.size() && |
646 | 0 | "signature lists should have the same size"); |
647 | | |
648 | 0 | auto &CandidateSigs = |
649 | 0 | SignatureListMap.find(Candidate)->second.Signatures; |
650 | 0 | for (unsigned Index = 0; Index < Candidate->size(); Index++) { |
651 | 0 | const Record *Rec = SignatureList[Index].first; |
652 | 0 | const Record *Rec2 = CandidateSigs[Index].first; |
653 | 0 | if (Rec->getValueAsBit("IsPure") == Rec2->getValueAsBit("IsPure") && |
654 | 0 | Rec->getValueAsBit("IsConst") == Rec2->getValueAsBit("IsConst") && |
655 | 0 | Rec->getValueAsBit("IsConv") == Rec2->getValueAsBit("IsConv") && |
656 | 0 | Rec->getValueAsDef("MinVersion")->getValueAsInt("ID") == |
657 | 0 | Rec2->getValueAsDef("MinVersion")->getValueAsInt("ID") && |
658 | 0 | Rec->getValueAsDef("MaxVersion")->getValueAsInt("ID") == |
659 | 0 | Rec2->getValueAsDef("MaxVersion")->getValueAsInt("ID") && |
660 | 0 | Rec->getValueAsDef("Extension")->getName() == |
661 | 0 | Rec2->getValueAsDef("Extension")->getName()) { |
662 | 0 | return true; |
663 | 0 | } |
664 | 0 | } |
665 | 0 | return false; |
666 | 0 | } |
667 | | |
668 | 0 | void BuiltinNameEmitter::GroupBySignature() { |
669 | | // List of signatures known to be emitted. |
670 | 0 | std::vector<BuiltinIndexListTy *> KnownSignatures; |
671 | |
|
672 | 0 | for (auto &Fct : FctOverloadMap) { |
673 | 0 | bool FoundReusableSig = false; |
674 | | |
675 | | // Gather all signatures for the current function. |
676 | 0 | auto *CurSignatureList = new BuiltinIndexListTy(); |
677 | 0 | for (const auto &Signature : Fct.second) { |
678 | 0 | CurSignatureList->push_back(Signature.second); |
679 | 0 | } |
680 | | // Sort the list to facilitate future comparisons. |
681 | 0 | llvm::sort(*CurSignatureList); |
682 | | |
683 | | // Check if we have already seen another function with the same list of |
684 | | // signatures. If so, just add the name of the function. |
685 | 0 | for (auto *Candidate : KnownSignatures) { |
686 | 0 | if (Candidate->size() == CurSignatureList->size() && |
687 | 0 | *Candidate == *CurSignatureList) { |
688 | 0 | if (CanReuseSignature(Candidate, Fct.second)) { |
689 | 0 | SignatureListMap.find(Candidate)->second.Names.push_back(Fct.first); |
690 | 0 | FoundReusableSig = true; |
691 | 0 | } |
692 | 0 | } |
693 | 0 | } |
694 | |
|
695 | 0 | if (FoundReusableSig) { |
696 | 0 | delete CurSignatureList; |
697 | 0 | } else { |
698 | | // Add a new entry. |
699 | 0 | SignatureListMap[CurSignatureList] = { |
700 | 0 | SmallVector<StringRef, 4>(1, Fct.first), Fct.second}; |
701 | 0 | KnownSignatures.push_back(CurSignatureList); |
702 | 0 | } |
703 | 0 | } |
704 | |
|
705 | 0 | for (auto *I : KnownSignatures) { |
706 | 0 | delete I; |
707 | 0 | } |
708 | 0 | } |
709 | | |
710 | 0 | void BuiltinNameEmitter::EmitStringMatcher() { |
711 | 0 | std::vector<StringMatcher::StringPair> ValidBuiltins; |
712 | 0 | unsigned CumulativeIndex = 1; |
713 | |
|
714 | 0 | for (const auto &SLM : SignatureListMap) { |
715 | 0 | const auto &Ovl = SLM.second.Signatures; |
716 | | |
717 | | // A single signature list may be used by different builtins. Return the |
718 | | // same <index, length> pair for each of those builtins. |
719 | 0 | for (const auto &FctName : SLM.second.Names) { |
720 | 0 | std::string RetStmt; |
721 | 0 | raw_string_ostream SS(RetStmt); |
722 | 0 | SS << "return std::make_pair(" << CumulativeIndex << ", " << Ovl.size() |
723 | 0 | << ");"; |
724 | 0 | SS.flush(); |
725 | 0 | ValidBuiltins.push_back( |
726 | 0 | StringMatcher::StringPair(std::string(FctName), RetStmt)); |
727 | 0 | } |
728 | 0 | CumulativeIndex += Ovl.size(); |
729 | 0 | } |
730 | |
|
731 | 0 | OS << R"( |
732 | 0 | // Find out whether a string matches an existing OpenCL builtin function name. |
733 | 0 | // Returns: A pair <0, 0> if no name matches. |
734 | 0 | // A pair <Index, Len> indexing the BuiltinTable if the name is |
735 | 0 | // matching an OpenCL builtin function. |
736 | 0 | static std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef Name) { |
737 | 0 |
|
738 | 0 | )"; |
739 | |
|
740 | 0 | StringMatcher("Name", ValidBuiltins, OS).Emit(0, true); |
741 | |
|
742 | 0 | OS << " return std::make_pair(0, 0);\n"; |
743 | 0 | OS << "} // isOpenCLBuiltin\n"; |
744 | 0 | } |
745 | | |
746 | | // Emit an if-statement with an isMacroDefined call for each extension in |
747 | | // the space-separated list of extensions. |
748 | 0 | static void EmitMacroChecks(raw_ostream &OS, StringRef Extensions) { |
749 | 0 | SmallVector<StringRef, 2> ExtVec; |
750 | 0 | Extensions.split(ExtVec, " "); |
751 | 0 | OS << " if ("; |
752 | 0 | for (StringRef Ext : ExtVec) { |
753 | 0 | if (Ext != ExtVec.front()) |
754 | 0 | OS << " && "; |
755 | 0 | OS << "S.getPreprocessor().isMacroDefined(\"" << Ext << "\")"; |
756 | 0 | } |
757 | 0 | OS << ") {\n "; |
758 | 0 | } |
759 | | |
760 | 0 | void BuiltinNameEmitter::EmitQualTypeFinder() { |
761 | 0 | OS << R"( |
762 | 0 |
|
763 | 0 | static QualType getOpenCLEnumType(Sema &S, llvm::StringRef Name); |
764 | 0 | static QualType getOpenCLTypedefType(Sema &S, llvm::StringRef Name); |
765 | 0 |
|
766 | 0 | // Convert an OpenCLTypeStruct type to a list of QualTypes. |
767 | 0 | // Generic types represent multiple types and vector sizes, thus a vector |
768 | 0 | // is returned. The conversion is done in two steps: |
769 | 0 | // Step 1: A switch statement fills a vector with scalar base types for the |
770 | 0 | // Cartesian product of (vector sizes) x (types) for generic types, |
771 | 0 | // or a single scalar type for non generic types. |
772 | 0 | // Step 2: Qualifiers and other type properties such as vector size are |
773 | 0 | // applied. |
774 | 0 | static void OCL2Qual(Sema &S, const OpenCLTypeStruct &Ty, |
775 | 0 | llvm::SmallVectorImpl<QualType> &QT) { |
776 | 0 | ASTContext &Context = S.Context; |
777 | 0 | // Number of scalar types in the GenType. |
778 | 0 | unsigned GenTypeNumTypes; |
779 | 0 | // Pointer to the list of vector sizes for the GenType. |
780 | 0 | llvm::ArrayRef<unsigned> GenVectorSizes; |
781 | 0 | )"; |
782 | | |
783 | | // Generate list of vector sizes for each generic type. |
784 | 0 | for (const auto *VectList : Records.getAllDerivedDefinitions("IntList")) { |
785 | 0 | OS << " constexpr unsigned List" |
786 | 0 | << VectList->getValueAsString("Name") << "[] = {"; |
787 | 0 | for (const auto V : VectList->getValueAsListOfInts("List")) { |
788 | 0 | OS << V << ", "; |
789 | 0 | } |
790 | 0 | OS << "};\n"; |
791 | 0 | } |
792 | | |
793 | | // Step 1. |
794 | | // Start of switch statement over all types. |
795 | 0 | OS << "\n switch (Ty.ID) {\n"; |
796 | | |
797 | | // Switch cases for image types (Image2d, Image3d, ...) |
798 | 0 | std::vector<Record *> ImageTypes = |
799 | 0 | Records.getAllDerivedDefinitions("ImageType"); |
800 | | |
801 | | // Map an image type name to its 3 access-qualified types (RO, WO, RW). |
802 | 0 | StringMap<SmallVector<Record *, 3>> ImageTypesMap; |
803 | 0 | for (auto *IT : ImageTypes) { |
804 | 0 | auto Entry = ImageTypesMap.find(IT->getValueAsString("Name")); |
805 | 0 | if (Entry == ImageTypesMap.end()) { |
806 | 0 | SmallVector<Record *, 3> ImageList; |
807 | 0 | ImageList.push_back(IT); |
808 | 0 | ImageTypesMap.insert( |
809 | 0 | std::make_pair(IT->getValueAsString("Name"), ImageList)); |
810 | 0 | } else { |
811 | 0 | Entry->second.push_back(IT); |
812 | 0 | } |
813 | 0 | } |
814 | | |
815 | | // Emit the cases for the image types. For an image type name, there are 3 |
816 | | // corresponding QualTypes ("RO", "WO", "RW"). The "AccessQualifier" field |
817 | | // tells which one is needed. Emit a switch statement that puts the |
818 | | // corresponding QualType into "QT". |
819 | 0 | for (const auto &ITE : ImageTypesMap) { |
820 | 0 | OS << " case OCLT_" << ITE.getKey() << ":\n" |
821 | 0 | << " switch (Ty.AccessQualifier) {\n" |
822 | 0 | << " case OCLAQ_None:\n" |
823 | 0 | << " llvm_unreachable(\"Image without access qualifier\");\n"; |
824 | 0 | for (const auto &Image : ITE.getValue()) { |
825 | 0 | StringRef Exts = |
826 | 0 | Image->getValueAsDef("Extension")->getValueAsString("ExtName"); |
827 | 0 | OS << StringSwitch<const char *>( |
828 | 0 | Image->getValueAsString("AccessQualifier")) |
829 | 0 | .Case("RO", " case OCLAQ_ReadOnly:\n") |
830 | 0 | .Case("WO", " case OCLAQ_WriteOnly:\n") |
831 | 0 | .Case("RW", " case OCLAQ_ReadWrite:\n"); |
832 | 0 | if (!Exts.empty()) { |
833 | 0 | OS << " "; |
834 | 0 | EmitMacroChecks(OS, Exts); |
835 | 0 | } |
836 | 0 | OS << " QT.push_back(" |
837 | 0 | << Image->getValueAsDef("QTExpr")->getValueAsString("TypeExpr") |
838 | 0 | << ");\n"; |
839 | 0 | if (!Exts.empty()) { |
840 | 0 | OS << " }\n"; |
841 | 0 | } |
842 | 0 | OS << " break;\n"; |
843 | 0 | } |
844 | 0 | OS << " }\n" |
845 | 0 | << " break;\n"; |
846 | 0 | } |
847 | | |
848 | | // Switch cases for generic types. |
849 | 0 | for (const auto *GenType : Records.getAllDerivedDefinitions("GenericType")) { |
850 | 0 | OS << " case OCLT_" << GenType->getValueAsString("Name") << ": {\n"; |
851 | | |
852 | | // Build the Cartesian product of (vector sizes) x (types). Only insert |
853 | | // the plain scalar types for now; other type information such as vector |
854 | | // size and type qualifiers will be added after the switch statement. |
855 | 0 | std::vector<Record *> BaseTypes = |
856 | 0 | GenType->getValueAsDef("TypeList")->getValueAsListOfDefs("List"); |
857 | | |
858 | | // Collect all QualTypes for a single vector size into TypeList. |
859 | 0 | OS << " SmallVector<QualType, " << BaseTypes.size() << "> TypeList;\n"; |
860 | 0 | for (const auto *T : BaseTypes) { |
861 | 0 | StringRef Exts = |
862 | 0 | T->getValueAsDef("Extension")->getValueAsString("ExtName"); |
863 | 0 | if (!Exts.empty()) { |
864 | 0 | EmitMacroChecks(OS, Exts); |
865 | 0 | } |
866 | 0 | OS << " TypeList.push_back(" |
867 | 0 | << T->getValueAsDef("QTExpr")->getValueAsString("TypeExpr") << ");\n"; |
868 | 0 | if (!Exts.empty()) { |
869 | 0 | OS << " }\n"; |
870 | 0 | } |
871 | 0 | } |
872 | 0 | OS << " GenTypeNumTypes = TypeList.size();\n"; |
873 | | |
874 | | // Duplicate the TypeList for every vector size. |
875 | 0 | std::vector<int64_t> VectorList = |
876 | 0 | GenType->getValueAsDef("VectorList")->getValueAsListOfInts("List"); |
877 | 0 | OS << " QT.reserve(" << VectorList.size() * BaseTypes.size() << ");\n" |
878 | 0 | << " for (unsigned I = 0; I < " << VectorList.size() << "; I++) {\n" |
879 | 0 | << " QT.append(TypeList);\n" |
880 | 0 | << " }\n"; |
881 | | |
882 | | // GenVectorSizes is the list of vector sizes for this GenType. |
883 | 0 | OS << " GenVectorSizes = List" |
884 | 0 | << GenType->getValueAsDef("VectorList")->getValueAsString("Name") |
885 | 0 | << ";\n" |
886 | 0 | << " break;\n" |
887 | 0 | << " }\n"; |
888 | 0 | } |
889 | | |
890 | | // Switch cases for non generic, non image types (int, int4, float, ...). |
891 | | // Only insert the plain scalar type; vector information and type qualifiers |
892 | | // are added in step 2. |
893 | 0 | std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type"); |
894 | 0 | StringMap<bool> TypesSeen; |
895 | |
|
896 | 0 | for (const auto *T : Types) { |
897 | | // Check this is not an image type |
898 | 0 | if (ImageTypesMap.find(T->getValueAsString("Name")) != ImageTypesMap.end()) |
899 | 0 | continue; |
900 | | // Check we have not seen this Type |
901 | 0 | if (TypesSeen.find(T->getValueAsString("Name")) != TypesSeen.end()) |
902 | 0 | continue; |
903 | 0 | TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true)); |
904 | | |
905 | | // Check the Type does not have an "abstract" QualType |
906 | 0 | auto QT = T->getValueAsDef("QTExpr"); |
907 | 0 | if (QT->getValueAsBit("IsAbstract") == 1) |
908 | 0 | continue; |
909 | | // Emit the cases for non generic, non image types. |
910 | 0 | OS << " case OCLT_" << T->getValueAsString("Name") << ":\n"; |
911 | |
|
912 | 0 | StringRef Exts = T->getValueAsDef("Extension")->getValueAsString("ExtName"); |
913 | | // If this type depends on an extension, ensure the extension macros are |
914 | | // defined. |
915 | 0 | if (!Exts.empty()) { |
916 | 0 | EmitMacroChecks(OS, Exts); |
917 | 0 | } |
918 | 0 | OS << " QT.push_back(" << QT->getValueAsString("TypeExpr") << ");\n"; |
919 | 0 | if (!Exts.empty()) { |
920 | 0 | OS << " }\n"; |
921 | 0 | } |
922 | 0 | OS << " break;\n"; |
923 | 0 | } |
924 | | |
925 | | // End of switch statement. |
926 | 0 | OS << " } // end of switch (Ty.ID)\n\n"; |
927 | | |
928 | | // Step 2. |
929 | | // Add ExtVector types if this was a generic type, as the switch statement |
930 | | // above only populated the list with scalar types. This completes the |
931 | | // construction of the Cartesian product of (vector sizes) x (types). |
932 | 0 | OS << " // Construct the different vector types for each generic type.\n"; |
933 | 0 | OS << " if (Ty.ID >= " << TypeList.size() << ") {"; |
934 | 0 | OS << R"( |
935 | 0 | for (unsigned I = 0; I < QT.size(); I++) { |
936 | 0 | // For scalars, size is 1. |
937 | 0 | if (GenVectorSizes[I / GenTypeNumTypes] != 1) { |
938 | 0 | QT[I] = Context.getExtVectorType(QT[I], |
939 | 0 | GenVectorSizes[I / GenTypeNumTypes]); |
940 | 0 | } |
941 | 0 | } |
942 | 0 | } |
943 | 0 | )"; |
944 | | |
945 | | // Assign the right attributes to the types (e.g. vector size). |
946 | 0 | OS << R"( |
947 | 0 | // Set vector size for non-generic vector types. |
948 | 0 | if (Ty.VectorWidth > 1) { |
949 | 0 | for (unsigned Index = 0; Index < QT.size(); Index++) { |
950 | 0 | QT[Index] = Context.getExtVectorType(QT[Index], Ty.VectorWidth); |
951 | 0 | } |
952 | 0 | } |
953 | 0 |
|
954 | 0 | if (Ty.IsVolatile != 0) { |
955 | 0 | for (unsigned Index = 0; Index < QT.size(); Index++) { |
956 | 0 | QT[Index] = Context.getVolatileType(QT[Index]); |
957 | 0 | } |
958 | 0 | } |
959 | 0 |
|
960 | 0 | if (Ty.IsConst != 0) { |
961 | 0 | for (unsigned Index = 0; Index < QT.size(); Index++) { |
962 | 0 | QT[Index] = Context.getConstType(QT[Index]); |
963 | 0 | } |
964 | 0 | } |
965 | 0 |
|
966 | 0 | // Transform the type to a pointer as the last step, if necessary. |
967 | 0 | // Builtin functions only have pointers on [const|volatile], no |
968 | 0 | // [const|volatile] pointers, so this is ok to do it as a last step. |
969 | 0 | if (Ty.IsPointer != 0) { |
970 | 0 | for (unsigned Index = 0; Index < QT.size(); Index++) { |
971 | 0 | QT[Index] = Context.getAddrSpaceQualType(QT[Index], Ty.AS); |
972 | 0 | QT[Index] = Context.getPointerType(QT[Index]); |
973 | 0 | } |
974 | 0 | } |
975 | 0 | )"; |
976 | | |
977 | | // End of the "OCL2Qual" function. |
978 | 0 | OS << "\n} // OCL2Qual\n"; |
979 | 0 | } |
980 | | |
981 | | std::string OpenCLBuiltinFileEmitterBase::getTypeString(const Record *Type, |
982 | | TypeFlags Flags, |
983 | 34.9k | int VectorSize) const { |
984 | 34.9k | std::string S; |
985 | 34.9k | if (Type->getValueAsBit("IsConst") || Flags.IsConst34.6k ) { |
986 | 674 | S += "const "; |
987 | 674 | } |
988 | 34.9k | if (Type->getValueAsBit("IsVolatile") || Flags.IsVolatile33.9k ) { |
989 | 1.01k | S += "volatile "; |
990 | 1.01k | } |
991 | | |
992 | 34.9k | auto PrintAddrSpace = [&S](StringRef AddrSpace) { |
993 | 3.29k | S += StringSwitch<const char *>(AddrSpace) |
994 | 3.29k | .Case("clang::LangAS::opencl_private", "__private") |
995 | 3.29k | .Case("clang::LangAS::opencl_global", "__global") |
996 | 3.29k | .Case("clang::LangAS::opencl_constant", "__constant") |
997 | 3.29k | .Case("clang::LangAS::opencl_local", "__local") |
998 | 3.29k | .Case("clang::LangAS::opencl_generic", "__generic") |
999 | 3.29k | .Default("__private"); |
1000 | 3.29k | S += " "; |
1001 | 3.29k | }; |
1002 | 34.9k | if (Flags.IsPointer) { |
1003 | 1.02k | PrintAddrSpace(Flags.AddrSpace); |
1004 | 33.9k | } else if (Type->getValueAsBit("IsPointer")) { |
1005 | 2.27k | PrintAddrSpace(Type->getValueAsString("AddrSpace")); |
1006 | 2.27k | } |
1007 | | |
1008 | 34.9k | StringRef Acc = Type->getValueAsString("AccessQualifier"); |
1009 | 34.9k | if (Acc != "") { |
1010 | 428 | S += StringSwitch<const char *>(Acc) |
1011 | 428 | .Case("RO", "__read_only ") |
1012 | 428 | .Case("WO", "__write_only ") |
1013 | 428 | .Case("RW", "__read_write "); |
1014 | 428 | } |
1015 | | |
1016 | 34.9k | S += Type->getValueAsString("Name").str(); |
1017 | 34.9k | if (VectorSize > 1) { |
1018 | 22.0k | S += std::to_string(VectorSize); |
1019 | 22.0k | } |
1020 | | |
1021 | 34.9k | if (Type->getValueAsBit("IsPointer") || Flags.IsPointer32.7k ) { |
1022 | 3.29k | S += " *"; |
1023 | 3.29k | } |
1024 | | |
1025 | 34.9k | return S; |
1026 | 34.9k | } |
1027 | | |
1028 | | void OpenCLBuiltinFileEmitterBase::getTypeLists( |
1029 | | Record *Type, TypeFlags &Flags, std::vector<Record *> &TypeList, |
1030 | 24.2k | std::vector<int64_t> &VectorList) const { |
1031 | 24.2k | bool isGenType = Type->isSubClassOf("GenericType"); |
1032 | 24.2k | if (isGenType) { |
1033 | 898 | TypeList = Type->getValueAsDef("TypeList")->getValueAsListOfDefs("List"); |
1034 | 898 | VectorList = |
1035 | 898 | Type->getValueAsDef("VectorList")->getValueAsListOfInts("List"); |
1036 | 898 | return; |
1037 | 898 | } |
1038 | | |
1039 | 23.3k | if (Type->isSubClassOf("PointerType") || Type->isSubClassOf("ConstType")21.0k || |
1040 | 23.3k | Type->isSubClassOf("VolatileType")21.0k ) { |
1041 | 2.32k | StringRef SubTypeName = Type->getValueAsString("Name"); |
1042 | 2.32k | Record *PossibleGenType = Records.getDef(SubTypeName); |
1043 | 2.32k | if (PossibleGenType && PossibleGenType->isSubClassOf("GenericType")57 ) { |
1044 | | // When PointerType, ConstType, or VolatileType is applied to a |
1045 | | // GenericType, the flags need to be taken from the subtype, not from the |
1046 | | // GenericType. |
1047 | 57 | Flags.IsPointer = Type->getValueAsBit("IsPointer"); |
1048 | 57 | Flags.IsConst = Type->getValueAsBit("IsConst"); |
1049 | 57 | Flags.IsVolatile = Type->getValueAsBit("IsVolatile"); |
1050 | 57 | Flags.AddrSpace = Type->getValueAsString("AddrSpace"); |
1051 | 57 | getTypeLists(PossibleGenType, Flags, TypeList, VectorList); |
1052 | 57 | return; |
1053 | 57 | } |
1054 | 2.32k | } |
1055 | | |
1056 | | // Not a GenericType, so just insert the single type. |
1057 | 23.3k | TypeList.push_back(Type); |
1058 | 23.3k | VectorList.push_back(Type->getValueAsInt("VecWidth")); |
1059 | 23.3k | } |
1060 | | |
1061 | | void OpenCLBuiltinFileEmitterBase::expandTypesInSignature( |
1062 | | const std::vector<Record *> &Signature, |
1063 | 9.54k | SmallVectorImpl<SmallVector<std::string, 2>> &Types) { |
1064 | | // Find out if there are any GenTypes in this signature, and if so, calculate |
1065 | | // into how many signatures they will expand. |
1066 | 9.54k | unsigned NumSignatures = 1; |
1067 | 9.54k | SmallVector<SmallVector<std::string, 4>, 4> ExpandedGenTypes; |
1068 | 24.2k | for (const auto &Arg : Signature) { |
1069 | 24.2k | SmallVector<std::string, 4> ExpandedArg; |
1070 | 24.2k | std::vector<Record *> TypeList; |
1071 | 24.2k | std::vector<int64_t> VectorList; |
1072 | 24.2k | TypeFlags Flags; |
1073 | | |
1074 | 24.2k | getTypeLists(Arg, Flags, TypeList, VectorList); |
1075 | | |
1076 | | // Insert the Cartesian product of the types and vector sizes. |
1077 | 27.8k | for (const auto &Vector : VectorList) { |
1078 | 34.9k | for (const auto &Type : TypeList) { |
1079 | 34.9k | std::string FullType = getTypeString(Type, Flags, Vector); |
1080 | 34.9k | ExpandedArg.push_back(FullType); |
1081 | | |
1082 | | // If the type requires an extension, add a TypeExtMap entry mapping |
1083 | | // the full type name to the extension. |
1084 | 34.9k | StringRef Ext = |
1085 | 34.9k | Arg->getValueAsDef("Extension")->getValueAsString("ExtName"); |
1086 | 34.9k | if (!Ext.empty() && TypeExtMap.find(FullType) == TypeExtMap.end()3.79k ) { |
1087 | 52 | TypeExtMap.insert({FullType, Ext}); |
1088 | 52 | } |
1089 | 34.9k | } |
1090 | 27.8k | } |
1091 | 24.2k | NumSignatures = std::max<unsigned>(NumSignatures, ExpandedArg.size()); |
1092 | 24.2k | ExpandedGenTypes.push_back(ExpandedArg); |
1093 | 24.2k | } |
1094 | | |
1095 | | // Now the total number of signatures is known. Populate the return list with |
1096 | | // all signatures. |
1097 | 23.3k | for (unsigned I = 0; I < NumSignatures; I++13.8k ) { |
1098 | 13.8k | SmallVector<std::string, 2> Args; |
1099 | | |
1100 | | // Process a single signature. |
1101 | 50.7k | for (unsigned ArgNum = 0; ArgNum < Signature.size(); ArgNum++36.9k ) { |
1102 | | // For differently-sized GenTypes in a parameter list, the smaller |
1103 | | // GenTypes just repeat, so index modulo the number of expanded types. |
1104 | 36.9k | size_t TypeIndex = I % ExpandedGenTypes[ArgNum].size(); |
1105 | 36.9k | Args.push_back(ExpandedGenTypes[ArgNum][TypeIndex]); |
1106 | 36.9k | } |
1107 | 13.8k | Types.push_back(Args); |
1108 | 13.8k | } |
1109 | 9.54k | } |
1110 | | |
1111 | 1 | void OpenCLBuiltinFileEmitterBase::emitExtensionSetup() { |
1112 | 1 | OS << R"( |
1113 | 1 | #pragma OPENCL EXTENSION cl_khr_fp16 : enable |
1114 | 1 | #pragma OPENCL EXTENSION cl_khr_fp64 : enable |
1115 | 1 | #pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable |
1116 | 1 | #pragma OPENCL EXTENSION cl_khr_int64_extended_atomics : enable |
1117 | 1 | #pragma OPENCL EXTENSION cl_khr_gl_msaa_sharing : enable |
1118 | 1 | #pragma OPENCL EXTENSION cl_khr_mipmap_image_writes : enable |
1119 | 1 | #pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable |
1120 | 1 | |
1121 | 1 | )"; |
1122 | 1 | } |
1123 | | |
1124 | | std::string |
1125 | 9.54k | OpenCLBuiltinFileEmitterBase::emitExtensionGuard(const Record *Builtin) { |
1126 | 9.54k | StringRef Extensions = |
1127 | 9.54k | Builtin->getValueAsDef("Extension")->getValueAsString("ExtName"); |
1128 | 9.54k | if (Extensions.empty()) |
1129 | 7.19k | return ""; |
1130 | | |
1131 | 2.35k | OS << "#if"; |
1132 | | |
1133 | 2.35k | SmallVector<StringRef, 2> ExtVec; |
1134 | 2.35k | Extensions.split(ExtVec, " "); |
1135 | 2.35k | bool isFirst = true; |
1136 | 3.36k | for (StringRef Ext : ExtVec) { |
1137 | 3.36k | if (!isFirst) { |
1138 | 1.01k | OS << " &&"; |
1139 | 1.01k | } |
1140 | 3.36k | OS << " defined(" << Ext << ")"; |
1141 | 3.36k | isFirst = false; |
1142 | 3.36k | } |
1143 | 2.35k | OS << "\n"; |
1144 | | |
1145 | 2.35k | return "#endif // Extension\n"; |
1146 | 9.54k | } |
1147 | | |
1148 | | std::string |
1149 | 9.54k | OpenCLBuiltinFileEmitterBase::emitVersionGuard(const Record *Builtin) { |
1150 | 9.54k | std::string OptionalEndif; |
1151 | 9.54k | auto PrintOpenCLVersion = [this](int Version) { |
1152 | 917 | OS << "CL_VERSION_" << (Version / 100) << "_" << ((Version % 100) / 10); |
1153 | 917 | }; |
1154 | 9.54k | int MinVersion = Builtin->getValueAsDef("MinVersion")->getValueAsInt("ID"); |
1155 | 9.54k | if (MinVersion != 100) { |
1156 | | // OpenCL 1.0 is the default minimum version. |
1157 | 917 | OS << "#if __OPENCL_C_VERSION__ >= "; |
1158 | 917 | PrintOpenCLVersion(MinVersion); |
1159 | 917 | OS << "\n"; |
1160 | 917 | OptionalEndif = "#endif // MinVersion\n" + OptionalEndif; |
1161 | 917 | } |
1162 | 9.54k | int MaxVersion = Builtin->getValueAsDef("MaxVersion")->getValueAsInt("ID"); |
1163 | 9.54k | if (MaxVersion) { |
1164 | 0 | OS << "#if __OPENCL_C_VERSION__ < "; |
1165 | 0 | PrintOpenCLVersion(MaxVersion); |
1166 | 0 | OS << "\n"; |
1167 | 0 | OptionalEndif = "#endif // MaxVersion\n" + OptionalEndif; |
1168 | 0 | } |
1169 | 9.54k | return OptionalEndif; |
1170 | 9.54k | } |
1171 | | |
1172 | | StringRef OpenCLBuiltinFileEmitterBase::emitTypeExtensionGuards( |
1173 | 13.8k | const SmallVectorImpl<std::string> &Signature) { |
1174 | 13.8k | SmallSet<StringRef, 2> ExtSet; |
1175 | | |
1176 | | // Iterate over all types to gather the set of required TypeExtensions. |
1177 | 36.9k | for (const auto &Ty : Signature) { |
1178 | 36.9k | StringRef TypeExt = TypeExtMap.lookup(Ty); |
1179 | 36.9k | if (!TypeExt.empty()) { |
1180 | | // The TypeExtensions are space-separated in the .td file. |
1181 | 7.26k | SmallVector<StringRef, 2> ExtVec; |
1182 | 7.26k | TypeExt.split(ExtVec, " "); |
1183 | 7.76k | for (const auto Ext : ExtVec) { |
1184 | 7.76k | ExtSet.insert(Ext); |
1185 | 7.76k | } |
1186 | 7.26k | } |
1187 | 36.9k | } |
1188 | | |
1189 | | // Emit the #if only when at least one extension is required. |
1190 | 13.8k | if (ExtSet.empty()) |
1191 | 9.36k | return ""; |
1192 | | |
1193 | 4.47k | OS << "#if "; |
1194 | 4.47k | bool isFirst = true; |
1195 | 5.26k | for (const auto Ext : ExtSet) { |
1196 | 5.26k | if (!isFirst) |
1197 | 794 | OS << " && "; |
1198 | 5.26k | OS << "defined(" << Ext << ")"; |
1199 | 5.26k | isFirst = false; |
1200 | 5.26k | } |
1201 | 4.47k | OS << "\n"; |
1202 | 4.47k | return "#endif // TypeExtension\n"; |
1203 | 13.8k | } |
1204 | | |
1205 | 1 | void OpenCLBuiltinTestEmitter::emit() { |
1206 | 1 | emitSourceFileHeader("OpenCL Builtin exhaustive testing", OS); |
1207 | | |
1208 | 1 | emitExtensionSetup(); |
1209 | | |
1210 | | // Ensure each test has a unique name by numbering them. |
1211 | 1 | unsigned TestID = 0; |
1212 | | |
1213 | | // Iterate over all builtins. |
1214 | 1 | std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin"); |
1215 | 9.54k | for (const auto *B : Builtins) { |
1216 | 9.54k | StringRef Name = B->getValueAsString("Name"); |
1217 | | |
1218 | 9.54k | SmallVector<SmallVector<std::string, 2>, 4> FTypes; |
1219 | 9.54k | expandTypesInSignature(B->getValueAsListOfDefs("Signature"), FTypes); |
1220 | | |
1221 | 9.54k | OS << "// Test " << Name << "\n"; |
1222 | | |
1223 | 9.54k | std::string OptionalExtensionEndif = emitExtensionGuard(B); |
1224 | 9.54k | std::string OptionalVersionEndif = emitVersionGuard(B); |
1225 | | |
1226 | 13.8k | for (const auto &Signature : FTypes) { |
1227 | 13.8k | StringRef OptionalTypeExtEndif = emitTypeExtensionGuards(Signature); |
1228 | | |
1229 | | // Emit function declaration. |
1230 | 13.8k | OS << Signature[0] << " test" << TestID++ << "_" << Name << "("; |
1231 | 13.8k | if (Signature.size() > 1) { |
1232 | 36.9k | for (unsigned I = 1; I < Signature.size(); I++23.0k ) { |
1233 | 23.0k | if (I != 1) |
1234 | 9.26k | OS << ", "; |
1235 | 23.0k | OS << Signature[I] << " arg" << I; |
1236 | 23.0k | } |
1237 | 13.8k | } |
1238 | 13.8k | OS << ") {\n"; |
1239 | | |
1240 | | // Emit function body. |
1241 | 13.8k | OS << " "; |
1242 | 13.8k | if (Signature[0] != "void") { |
1243 | 12.9k | OS << "return "; |
1244 | 12.9k | } |
1245 | 13.8k | OS << Name << "("; |
1246 | 36.9k | for (unsigned I = 1; I < Signature.size(); I++23.0k ) { |
1247 | 23.0k | if (I != 1) |
1248 | 9.26k | OS << ", "; |
1249 | 23.0k | OS << "arg" << I; |
1250 | 23.0k | } |
1251 | 13.8k | OS << ");\n"; |
1252 | | |
1253 | | // End of function body. |
1254 | 13.8k | OS << "}\n"; |
1255 | 13.8k | OS << OptionalTypeExtEndif; |
1256 | 13.8k | } |
1257 | | |
1258 | 9.54k | OS << OptionalVersionEndif; |
1259 | 9.54k | OS << OptionalExtensionEndif; |
1260 | 9.54k | } |
1261 | 1 | } |
1262 | | |
1263 | 0 | void clang::EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { |
1264 | 0 | BuiltinNameEmitter NameChecker(Records, OS); |
1265 | 0 | NameChecker.Emit(); |
1266 | 0 | } |
1267 | | |
1268 | | void clang::EmitClangOpenCLBuiltinTests(RecordKeeper &Records, |
1269 | 1 | raw_ostream &OS) { |
1270 | 1 | OpenCLBuiltinTestEmitter TestFileGenerator(Records, OS); |
1271 | 1 | TestFileGenerator.emit(); |
1272 | 1 | } |