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 // This tablegen backend emits code for checking whether a function is an 12 // OpenCL builtin function. If so, all overloads of this function are 13 // added to the LookupResult. The generated include file is used by 14 // SemaLookup.cpp 15 // 16 // For a successful lookup of e.g. the "cos" builtin, isOpenCLBuiltin("cos") 17 // returns a pair <Index, Len>. 18 // BuiltinTable[Index] to BuiltinTable[Index + Len] contains the pairs 19 // <SigIndex, SigLen> of the overloads of "cos". 20 // SignatureTable[SigIndex] to SignatureTable[SigIndex + SigLen] contains 21 // one of the signatures of "cos". The SignatureTable entry can be 22 // referenced by other functions, e.g. "sin", to exploit the fact that 23 // many OpenCL builtins share the same signature. 24 // 25 // The file generated by this TableGen emitter contains the following: 26 // 27 // * Structs and enums to represent types and function signatures. 28 // 29 // * OpenCLTypeStruct TypeTable[] 30 // Type information for return types and arguments. 31 // 32 // * unsigned SignatureTable[] 33 // A list of types representing function signatures. Each entry is an index 34 // into the above TypeTable. Multiple entries following each other form a 35 // signature, where the first entry is the return type and subsequent 36 // entries are the argument types. 37 // 38 // * OpenCLBuiltinStruct BuiltinTable[] 39 // Each entry represents one overload of an OpenCL builtin function and 40 // consists of an index into the SignatureTable and the number of arguments. 41 // 42 // * std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef Name) 43 // Find out whether a string matches an existing OpenCL builtin function 44 // name and return an index into BuiltinTable and the number of overloads. 45 // 46 // * void OCL2Qual(ASTContext&, OpenCLTypeStruct, std::vector<QualType>&) 47 // Convert an OpenCLTypeStruct type to a list of QualType instances. 48 // One OpenCLTypeStruct can represent multiple types, primarily when using 49 // GenTypes. 50 // 51 //===----------------------------------------------------------------------===// 52 53 #include "llvm/ADT/MapVector.h" 54 #include "llvm/ADT/STLExtras.h" 55 #include "llvm/ADT/SmallString.h" 56 #include "llvm/ADT/StringExtras.h" 57 #include "llvm/ADT/StringRef.h" 58 #include "llvm/ADT/StringSet.h" 59 #include "llvm/Support/ErrorHandling.h" 60 #include "llvm/Support/raw_ostream.h" 61 #include "llvm/TableGen/Error.h" 62 #include "llvm/TableGen/Record.h" 63 #include "llvm/TableGen/StringMatcher.h" 64 #include "llvm/TableGen/TableGenBackend.h" 65 #include <set> 66 67 using namespace llvm; 68 69 namespace { 70 class BuiltinNameEmitter { 71 public: 72 BuiltinNameEmitter(RecordKeeper &Records, raw_ostream &OS) 73 : Records(Records), OS(OS) {} 74 75 // Entrypoint to generate the functions and structures for checking 76 // whether a function is an OpenCL builtin function. 77 void Emit(); 78 79 private: 80 // Contains OpenCL builtin functions and related information, stored as 81 // Record instances. They are coming from the associated TableGen file. 82 RecordKeeper &Records; 83 84 // The output file. 85 raw_ostream &OS; 86 87 // Helper function for BuiltinNameEmitter::EmitDeclarations. Generate enum 88 // definitions in the Output string parameter, and save their Record instances 89 // in the List parameter. 90 // \param Types (in) List containing the Types to extract. 91 // \param TypesSeen (inout) List containing the Types already extracted. 92 // \param Output (out) String containing the enums to emit in the output file. 93 // \param List (out) List containing the extracted Types, except the Types in 94 // TypesSeen. 95 void ExtractEnumTypes(std::vector<Record *> &Types, 96 StringMap<bool> &TypesSeen, std::string &Output, 97 std::vector<const Record *> &List); 98 99 // Emit the enum or struct used in the generated file. 100 // Populate the TypeList at the same time. 101 void EmitDeclarations(); 102 103 // Parse the Records generated by TableGen to populate the SignaturesList, 104 // FctOverloadMap and TypeMap. 105 void GetOverloads(); 106 107 // Emit the TypeTable containing all types used by OpenCL builtins. 108 void EmitTypeTable(); 109 110 // Emit the SignatureTable. This table contains all the possible signatures. 111 // A signature is stored as a list of indexes of the TypeTable. 112 // The first index references the return type (mandatory), and the followings 113 // reference its arguments. 114 // E.g.: 115 // 15, 2, 15 can represent a function with the signature: 116 // int func(float, int) 117 // The "int" type being at the index 15 in the TypeTable. 118 void EmitSignatureTable(); 119 120 // Emit the BuiltinTable table. This table contains all the overloads of 121 // each function, and is a struct OpenCLBuiltinDecl. 122 // E.g.: 123 // // convert_float2_rtn 124 // { 58, 2 }, 125 // This means that the signature of this convert_float2_rtn overload has 126 // 1 argument (+1 for the return type), stored at index 58 in 127 // the SignatureTable. 128 void EmitBuiltinTable(); 129 130 // Emit a StringMatcher function to check whether a function name is an 131 // OpenCL builtin function name. 132 void EmitStringMatcher(); 133 134 // Emit a function returning the clang QualType instance associated with 135 // the TableGen Record Type. 136 void EmitQualTypeFinder(); 137 138 // Contains a list of the available signatures, without the name of the 139 // function. Each pair consists of a signature and a cumulative index. 140 // E.g.: <<float, float>, 0>, 141 // <<float, int, int, 2>>, 142 // <<float>, 5>, 143 // ... 144 // <<double, double>, 35>. 145 std::vector<std::pair<std::vector<Record *>, unsigned>> SignaturesList; 146 147 // Map the name of a builtin function to its prototypes (instances of the 148 // TableGen "Builtin" class). 149 // Each prototype is registered as a pair of: 150 // <pointer to the "Builtin" instance, 151 // cumulative index of the associated signature in the SignaturesList> 152 // E.g.: The function cos: (float cos(float), double cos(double), ...) 153 // <"cos", <<ptrToPrototype0, 5>, 154 // <ptrToPrototype1, 35>, 155 // <ptrToPrototype2, 79>> 156 // ptrToPrototype1 has the following signature: <double, double> 157 MapVector<StringRef, std::vector<std::pair<const Record *, unsigned>>> 158 FctOverloadMap; 159 160 // Contains the map of OpenCL types to their index in the TypeTable. 161 MapVector<const Record *, unsigned> TypeMap; 162 163 // List of OpenCL type names in the same order as in enum OpenCLTypeID. 164 // This list does not contain generic types. 165 std::vector<const Record *> TypeList; 166 167 // Same as TypeList, but for generic types only. 168 std::vector<const Record *> GenTypeList; 169 }; 170 } // namespace 171 172 void BuiltinNameEmitter::Emit() { 173 emitSourceFileHeader("OpenCL Builtin handling", OS); 174 175 OS << "#include \"llvm/ADT/StringRef.h\"\n"; 176 OS << "using namespace clang;\n\n"; 177 178 // Emit enums and structs. 179 EmitDeclarations(); 180 181 GetOverloads(); 182 183 // Emit tables. 184 EmitTypeTable(); 185 EmitSignatureTable(); 186 EmitBuiltinTable(); 187 188 EmitStringMatcher(); 189 190 EmitQualTypeFinder(); 191 } 192 193 void BuiltinNameEmitter::ExtractEnumTypes(std::vector<Record *> &Types, 194 StringMap<bool> &TypesSeen, 195 std::string &Output, 196 std::vector<const Record *> &List) { 197 raw_string_ostream SS(Output); 198 199 for (const auto *T : Types) { 200 if (TypesSeen.find(T->getValueAsString("Name")) == TypesSeen.end()) { 201 SS << " OCLT_" + T->getValueAsString("Name") << ",\n"; 202 // Save the type names in the same order as their enum value. Note that 203 // the Record can be a VectorType or something else, only the name is 204 // important. 205 List.push_back(T); 206 TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true)); 207 } 208 } 209 SS.flush(); 210 } 211 212 void BuiltinNameEmitter::EmitDeclarations() { 213 // Enum of scalar type names (float, int, ...) and generic type sets. 214 OS << "enum OpenCLTypeID {\n"; 215 216 StringMap<bool> TypesSeen; 217 std::string GenTypeEnums; 218 std::string TypeEnums; 219 220 // Extract generic types and non-generic types separately, to keep 221 // gentypes at the end of the enum which simplifies the special handling 222 // for gentypes in SemaLookup. 223 std::vector<Record *> GenTypes = 224 Records.getAllDerivedDefinitions("GenericType"); 225 ExtractEnumTypes(GenTypes, TypesSeen, GenTypeEnums, GenTypeList); 226 227 std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type"); 228 ExtractEnumTypes(Types, TypesSeen, TypeEnums, TypeList); 229 230 OS << TypeEnums; 231 OS << GenTypeEnums; 232 OS << "};\n"; 233 234 // Structure definitions. 235 OS << R"( 236 237 // Represents a return type or argument type. 238 struct OpenCLTypeStruct { 239 // A type (e.g. float, int, ...). 240 const OpenCLTypeID ID; 241 // Vector size (if applicable; 0 for scalars and generic types). 242 const unsigned VectorWidth; 243 // 0 if the type is not a pointer. 244 const bool IsPointer; 245 // 0 if the type is not const. 246 const bool IsConst; 247 // 0 if the type is not volatile. 248 const bool IsVolatile; 249 // Address space of the pointer (if applicable). 250 const LangAS AS; 251 }; 252 253 // One overload of an OpenCL builtin function. 254 struct OpenCLBuiltinStruct { 255 // Index of the signature in the OpenCLTypeStruct table. 256 const unsigned SigTableIndex; 257 // Entries between index SigTableIndex and (SigTableIndex + NumTypes - 1) in 258 // the SignatureTable represent the complete signature. The first type at 259 // index SigTableIndex is the return type. 260 const unsigned NumTypes; 261 }; 262 263 )"; 264 } 265 266 // Verify that the combination of GenTypes in a signature is supported. 267 // To simplify the logic for creating overloads in SemaLookup, only allow 268 // a signature to contain different GenTypes if these GenTypes represent 269 // the same number of actual scalar or vector types. 270 // 271 // Exit with a fatal error if an unsupported construct is encountered. 272 static void VerifySignature(const std::vector<Record *> &Signature, 273 const Record *BuiltinRec) { 274 unsigned GenTypeVecSizes = 1; 275 unsigned GenTypeTypes = 1; 276 277 for (const auto *T : Signature) { 278 // Check all GenericType arguments in this signature. 279 if (T->isSubClassOf("GenericType")) { 280 // Check number of vector sizes. 281 unsigned NVecSizes = 282 T->getValueAsDef("VectorList")->getValueAsListOfInts("List").size(); 283 if (NVecSizes != GenTypeVecSizes && NVecSizes != 1) { 284 if (GenTypeVecSizes > 1) { 285 // We already saw a gentype with a different number of vector sizes. 286 PrintFatalError(BuiltinRec->getLoc(), 287 "number of vector sizes should be equal or 1 for all gentypes " 288 "in a declaration"); 289 } 290 GenTypeVecSizes = NVecSizes; 291 } 292 293 // Check number of data types. 294 unsigned NTypes = 295 T->getValueAsDef("TypeList")->getValueAsListOfDefs("List").size(); 296 if (NTypes != GenTypeTypes && NTypes != 1) { 297 if (GenTypeTypes > 1) { 298 // We already saw a gentype with a different number of types. 299 PrintFatalError(BuiltinRec->getLoc(), 300 "number of types should be equal or 1 for all gentypes " 301 "in a declaration"); 302 } 303 GenTypeTypes = NTypes; 304 } 305 } 306 } 307 } 308 309 void BuiltinNameEmitter::GetOverloads() { 310 // Populate the TypeMap. 311 std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type"); 312 unsigned I = 0; 313 for (const auto &T : Types) { 314 TypeMap.insert(std::make_pair(T, I++)); 315 } 316 317 // Populate the SignaturesList and the FctOverloadMap. 318 unsigned CumulativeSignIndex = 0; 319 std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin"); 320 for (const auto *B : Builtins) { 321 StringRef BName = B->getValueAsString("Name"); 322 if (FctOverloadMap.find(BName) == FctOverloadMap.end()) { 323 FctOverloadMap.insert(std::make_pair( 324 BName, std::vector<std::pair<const Record *, unsigned>>{})); 325 } 326 327 auto Signature = B->getValueAsListOfDefs("Signature"); 328 // Reuse signatures to avoid unnecessary duplicates. 329 auto it = 330 std::find_if(SignaturesList.begin(), SignaturesList.end(), 331 [&](const std::pair<std::vector<Record *>, unsigned> &a) { 332 return a.first == Signature; 333 }); 334 unsigned SignIndex; 335 if (it == SignaturesList.end()) { 336 VerifySignature(Signature, B); 337 SignaturesList.push_back(std::make_pair(Signature, CumulativeSignIndex)); 338 SignIndex = CumulativeSignIndex; 339 CumulativeSignIndex += Signature.size(); 340 } else { 341 SignIndex = it->second; 342 } 343 FctOverloadMap[BName].push_back(std::make_pair(B, SignIndex)); 344 } 345 } 346 347 void BuiltinNameEmitter::EmitTypeTable() { 348 OS << "static const OpenCLTypeStruct TypeTable[] = {\n"; 349 for (const auto &T : TypeMap) { 350 OS << " // " << T.second << "\n"; 351 OS << " {OCLT_" << T.first->getValueAsString("Name") << ", " 352 << T.first->getValueAsInt("VecWidth") << ", " 353 << T.first->getValueAsBit("IsPointer") << ", " 354 << T.first->getValueAsBit("IsConst") << ", " 355 << T.first->getValueAsBit("IsVolatile") << ", " 356 << T.first->getValueAsString("AddrSpace") << "},\n"; 357 } 358 OS << "};\n\n"; 359 } 360 361 void BuiltinNameEmitter::EmitSignatureTable() { 362 // Store a type (e.g. int, float, int2, ...). The type is stored as an index 363 // of a struct OpenCLType table. Multiple entries following each other form a 364 // signature. 365 OS << "static const unsigned SignatureTable[] = {\n"; 366 for (const auto &P : SignaturesList) { 367 OS << " // " << P.second << "\n "; 368 for (const Record *R : P.first) { 369 OS << TypeMap.find(R)->second << ", "; 370 } 371 OS << "\n"; 372 } 373 OS << "};\n\n"; 374 } 375 376 void BuiltinNameEmitter::EmitBuiltinTable() { 377 unsigned Index = 0; 378 379 OS << "static const OpenCLBuiltinStruct BuiltinTable[] = {\n"; 380 for (const auto &FOM : FctOverloadMap) { 381 382 OS << " // " << (Index + 1) << ": " << FOM.first << "\n"; 383 384 for (const auto &Overload : FOM.second) { 385 OS << " { " 386 << Overload.second << ", " 387 << Overload.first->getValueAsListOfDefs("Signature").size() 388 << " },\n"; 389 Index++; 390 } 391 } 392 OS << "};\n\n"; 393 } 394 395 void BuiltinNameEmitter::EmitStringMatcher() { 396 std::vector<StringMatcher::StringPair> ValidBuiltins; 397 unsigned CumulativeIndex = 1; 398 for (auto &i : FctOverloadMap) { 399 auto &Ov = i.second; 400 std::string RetStmt; 401 raw_string_ostream SS(RetStmt); 402 SS << "return std::make_pair(" << CumulativeIndex << ", " << Ov.size() 403 << ");"; 404 SS.flush(); 405 CumulativeIndex += Ov.size(); 406 407 ValidBuiltins.push_back(StringMatcher::StringPair(i.first, RetStmt)); 408 } 409 410 OS << R"( 411 // Find out whether a string matches an existing OpenCL builtin function name. 412 // Returns: A pair <0, 0> if no name matches. 413 // A pair <Index, Len> indexing the BuiltinTable if the name is 414 // matching an OpenCL builtin function. 415 static std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef Name) { 416 417 )"; 418 419 StringMatcher("Name", ValidBuiltins, OS).Emit(0, true); 420 421 OS << " return std::make_pair(0, 0);\n"; 422 OS << "} // isOpenCLBuiltin\n"; 423 } 424 425 void BuiltinNameEmitter::EmitQualTypeFinder() { 426 OS << R"( 427 428 // Convert an OpenCLTypeStruct type to a list of QualTypes. 429 // Generic types represent multiple types and vector sizes, thus a vector 430 // is returned. The conversion is done in two steps: 431 // Step 1: A switch statement fills a vector with scalar base types for the 432 // Cartesian product of (vector sizes) x (types) for generic types, 433 // or a single scalar type for non generic types. 434 // Step 2: Qualifiers and other type properties such as vector size are 435 // applied. 436 static void OCL2Qual(ASTContext &Context, const OpenCLTypeStruct &Ty, 437 std::vector<QualType> &QT) { 438 // Number of scalar types in the GenType. 439 unsigned GenTypeNumTypes; 440 // Pointer to the list of vector sizes for the GenType. 441 llvm::SmallVector<unsigned, 6> *GenVectorSizes; 442 )"; 443 444 // Generate list of vector sizes for each generic type. 445 for (const auto *VectList : Records.getAllDerivedDefinitions("IntList")) { 446 OS << " llvm::SmallVector<unsigned, 6> List" 447 << VectList->getValueAsString("Name") << "{"; 448 for (const auto V : VectList->getValueAsListOfInts("List")) { 449 OS << V << ", "; 450 } 451 OS << "};\n"; 452 } 453 454 // Step 1. 455 // Start of switch statement over all types. 456 OS << "\n switch (Ty.ID) {\n"; 457 458 // Switch cases for generic types. 459 for (const auto *GenType : Records.getAllDerivedDefinitions("GenericType")) { 460 OS << " case OCLT_" << GenType->getValueAsString("Name") << ":\n"; 461 462 // Build the Cartesian product of (vector sizes) x (types). Only insert 463 // the plain scalar types for now; other type information such as vector 464 // size and type qualifiers will be added after the switch statement. 465 for (unsigned I = 0; I < GenType->getValueAsDef("VectorList") 466 ->getValueAsListOfInts("List") 467 .size(); 468 I++) { 469 for (const auto *T : 470 GenType->getValueAsDef("TypeList")->getValueAsListOfDefs("List")) { 471 OS << " QT.push_back(Context." 472 << T->getValueAsDef("QTName")->getValueAsString("Name") << ");\n"; 473 } 474 } 475 // GenTypeNumTypes is the number of types in the GenType 476 // (e.g. float/double/half). 477 OS << " GenTypeNumTypes = " 478 << GenType->getValueAsDef("TypeList")->getValueAsListOfDefs("List") 479 .size() 480 << ";\n"; 481 // GenVectorSizes is the list of vector sizes for this GenType. 482 // QT contains GenTypeNumTypes * #GenVectorSizes elements. 483 OS << " GenVectorSizes = &List" 484 << GenType->getValueAsDef("VectorList")->getValueAsString("Name") 485 << ";\n"; 486 OS << " break;\n"; 487 } 488 489 // Switch cases for non generic, non image types (int, int4, float, ...). 490 // Only insert the plain scalar type; vector information and type qualifiers 491 // are added in step 2. 492 std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type"); 493 StringMap<bool> TypesSeen; 494 495 for (const auto *T : Types) { 496 // Check we have not seen this Type 497 if (TypesSeen.find(T->getValueAsString("Name")) != TypesSeen.end()) 498 continue; 499 TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true)); 500 501 // Check the Type does not have an "abstract" QualType 502 auto QT = T->getValueAsDef("QTName"); 503 if (QT->getValueAsBit("IsAbstract") == 1) 504 continue; 505 // Emit the cases for non generic, non image types. 506 OS << " case OCLT_" << T->getValueAsString("Name") << ":\n"; 507 OS << " QT.push_back(Context." << QT->getValueAsString("Name") 508 << ");\n"; 509 OS << " break;\n"; 510 } 511 512 // End of switch statement. 513 OS << " } // end of switch (Ty.ID)\n\n"; 514 515 // Step 2. 516 // Add ExtVector types if this was a generic type, as the switch statement 517 // above only populated the list with scalar types. This completes the 518 // construction of the Cartesian product of (vector sizes) x (types). 519 OS << " // Construct the different vector types for each generic type.\n"; 520 OS << " if (Ty.ID >= " << TypeList.size() << ") {"; 521 OS << R"( 522 for (unsigned I = 0; I < QT.size(); I++) { 523 // For scalars, size is 1. 524 if ((*GenVectorSizes)[I / GenTypeNumTypes] != 1) { 525 QT[I] = Context.getExtVectorType(QT[I], 526 (*GenVectorSizes)[I / GenTypeNumTypes]); 527 } 528 } 529 } 530 )"; 531 532 // Assign the right attributes to the types (e.g. vector size). 533 OS << R"( 534 // Set vector size for non-generic vector types. 535 if (Ty.VectorWidth > 1) { 536 for (unsigned Index = 0; Index < QT.size(); Index++) { 537 QT[Index] = Context.getExtVectorType(QT[Index], Ty.VectorWidth); 538 } 539 } 540 541 if (Ty.IsVolatile != 0) { 542 for (unsigned Index = 0; Index < QT.size(); Index++) { 543 QT[Index] = Context.getVolatileType(QT[Index]); 544 } 545 } 546 547 if (Ty.IsConst != 0) { 548 for (unsigned Index = 0; Index < QT.size(); Index++) { 549 QT[Index] = Context.getConstType(QT[Index]); 550 } 551 } 552 553 // Transform the type to a pointer as the last step, if necessary. 554 // Builtin functions only have pointers on [const|volatile], no 555 // [const|volatile] pointers, so this is ok to do it as a last step. 556 if (Ty.IsPointer != 0) { 557 for (unsigned Index = 0; Index < QT.size(); Index++) { 558 QT[Index] = Context.getAddrSpaceQualType(QT[Index], Ty.AS); 559 QT[Index] = Context.getPointerType(QT[Index]); 560 } 561 } 562 )"; 563 564 // End of the "OCL2Qual" function. 565 OS << "\n} // OCL2Qual\n"; 566 } 567 568 namespace clang { 569 570 void EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { 571 BuiltinNameEmitter NameChecker(Records, OS); 572 NameChecker.Emit(); 573 } 574 575 } // end namespace clang 576