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