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