svenvh updated this revision to Diff 213904.
svenvh marked 9 inline comments as done.
svenvh added a comment.

Addressing review comments.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D65456/new/

https://reviews.llvm.org/D65456

Files:
  clang/lib/Sema/OpenCLBuiltins.td
  clang/lib/Sema/SemaLookup.cpp
  clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl
  clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp

Index: clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
===================================================================
--- clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
+++ clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
@@ -15,12 +15,39 @@
 //
 // For a successful lookup of e.g. the "cos" builtin, isOpenCLBuiltin("cos")
 // returns a pair <Index, Len>.
-// OpenCLBuiltins[Index] to OpenCLBuiltins[Index + Len] contains the pairs
+// BuiltinTable[Index] to BuiltinTable[Index + Len] contains the pairs
 // <SigIndex, SigLen> of the overloads of "cos".
-// OpenCLSignature[SigIndex] to OpenCLSignature[SigIndex + SigLen] contains
-// one of the signatures of "cos". The OpenCLSignature entry can be
-// referenced by other functions, i.e. "sin", since multiple OpenCL builtins
-// share the same signature.
+// SignatureTable[SigIndex] to SignatureTable[SigIndex + SigLen] contains
+// one of the signatures of "cos". The SignatureTable entry can be
+// referenced by other functions, e.g. "sin", to exploit the fact that
+// many OpenCL builtins share the same signature.
+//
+// The file generated by this TableGen emitter contains the following:
+//
+//  * Structs and enums to represent types and function signatures.
+//
+//  * OpenCLTypeStruct TypeTable[]
+//    Type information for return types and arguments.
+//
+//  * unsigned SignatureTable[]
+//    A list of types representing function signatures.  Each entry is an index
+//    into the above TypeTable.  Multiple entries following each other form a
+//    signature, where the first entry is the return type and subsequent
+//    entries are the argument types.
+//
+//  * OpenCLBuiltinStruct BuiltinTable[]
+//    Each entry represents one overload of an OpenCL builtin function and
+//    consists of an index into the SignatureTable and the number of arguments.
+//
+//  * std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef Name)
+//    Find out whether a string matches an existing OpenCL builtin function
+//    name and return an index into BuiltinTable and the number of overloads.
+//
+//  * void OCL2Qual(ASTContext&, OpenCLTypeStruct, std::vector<QualType>&)
+//    Convert an OpenCLTypeStruct type to a list of QualType instances.
+//    One OpenCLTypeStruct can represent multiple types, primarily when using
+//    GenTypes.
+//
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/MapVector.h"
@@ -57,34 +84,47 @@
   // The output file.
   raw_ostream &OS;
 
-  // Emit the enums and structs.
+  // Helper function for BuiltinNameEmitter::EmitDeclarations.  Generate enum
+  // definitions in the Output string parameter, and save their Record instances
+  // in the List parameter.
+  // \param Types (in) List containing the Types to extract.
+  // \param TypesSeen (inout) List containing the Types already extracted.
+  // \param Output (out) String containing the enums to emit in the output file.
+  // \param List (out) List containing the extracted Types, except the Types in
+  //        TypesSeen.
+  void ExtractEnumTypes(std::vector<Record *> &Types,
+                        StringMap<bool> &TypesSeen, std::string &Output,
+                        std::vector<const Record *> &List);
+
+  // Emit the enum or struct used in the generated file.
+  // Populate the TypeList at the same time.
   void EmitDeclarations();
 
-  // Parse the Records generated by TableGen and populate OverloadInfo and
-  // SignatureSet.
+  // Parse the Records generated by TableGen to populate the SignaturesList,
+  // FctOverloadMap and TypeMap.
   void GetOverloads();
 
-  // Emit the OpenCLSignature table. This table contains all possible
-  // signatures, and is a struct OpenCLType. A signature is composed of a
-  // return type (mandatory), followed by zero or more argument types.
+  // Emit the TypeTable containing all types used by OpenCL builtins.
+  void EmitTypeTable();
+
+  // Emit the SignatureTable. This table contains all the possible signatures.
+  // A signature is stored as a list of indexes of the TypeTable.
+  // The first index references the return type (mandatory), and the followings
+  // reference its arguments.
   // E.g.:
-  // // 12
-  // { OCLT_uchar, 4, clang::LangAS::Default, false },
-  // { OCLT_float, 4, clang::LangAS::Default, false },
-  // This means that index 12 represents a signature
-  //   - returning a uchar vector of 4 elements, and
-  //   - taking as first argument a float vector of 4 elements.
+  // 15, 2, 15 can represent a function with the signature:
+  // int func(float, int)
+  // The "int" type being at the index 15 in the TypeTable.
   void EmitSignatureTable();
 
-  // Emit the OpenCLBuiltins table. This table contains all overloads of
+  // Emit the BuiltinTable table. This table contains all the overloads of
   // each function, and is a struct OpenCLBuiltinDecl.
   // E.g.:
-  // // acos
-  //   { 2, 0, "", 100 },
-  // This means that the signature of this acos overload is defined in OpenCL
-  // version 1.0 (100) and does not belong to any extension ("").  It has a
-  // 1 argument (+1 for the return type), stored at index 0 in the
-  // OpenCLSignature table.
+  // // convert_float2_rtn
+  //   { 58, 2 },
+  // This means that the signature of this convert_float2_rtn overload has
+  // 1 argument (+1 for the return type), stored at index 58 in
+  // the SignatureTable.
   void EmitBuiltinTable();
 
   // Emit a StringMatcher function to check whether a function name is an
@@ -102,20 +142,30 @@
   //        <<float>, 5>,
   //        ...
   //        <<double, double>, 35>.
-  std::vector<std::pair<std::vector<Record *>, unsigned>> SignatureSet;
+  std::vector<std::pair<std::vector<Record *>, unsigned>> SignaturesList;
 
   // Map the name of a builtin function to its prototypes (instances of the
   // TableGen "Builtin" class).
   // Each prototype is registered as a pair of:
   //   <pointer to the "Builtin" instance,
-  //    cumulative index of the associated signature in the SignatureSet>
+  //    cumulative index of the associated signature in the SignaturesList>
   // E.g.:  The function cos: (float cos(float), double cos(double), ...)
   //        <"cos", <<ptrToPrototype0, 5>,
-  //                <ptrToPrototype1, 35>>
-  //                <ptrToPrototype2, 79>>
+  //                 <ptrToPrototype1, 35>,
+  //                 <ptrToPrototype2, 79>>
   // ptrToPrototype1 has the following signature: <double, double>
   MapVector<StringRef, std::vector<std::pair<const Record *, unsigned>>>
-      OverloadInfo;
+      FctOverloadMap;
+
+  // Contains the map of OpenCL types to their index in the TypeTable.
+  MapVector<const Record *, unsigned> TypeMap;
+
+  // List of OpenCL type names in the same order as in enum OpenCLTypeID.
+  // This list does not contain generic types.
+  std::vector<const Record *> TypeList;
+
+  // Same as TypeList, but for generic types only.
+  std::vector<const Record *> GenTypeList;
 };
 } // namespace
 
@@ -125,12 +175,14 @@
   OS << "#include \"llvm/ADT/StringRef.h\"\n";
   OS << "using namespace clang;\n\n";
 
+  // Emit enums and structs.
   EmitDeclarations();
 
   GetOverloads();
 
+  // Emit tables.
+  EmitTypeTable();
   EmitSignatureTable();
-
   EmitBuiltinTable();
 
   EmitStringMatcher();
@@ -138,100 +190,147 @@
   EmitQualTypeFinder();
 }
 
+void BuiltinNameEmitter::ExtractEnumTypes(std::vector<Record *> &Types,
+                                          StringMap<bool> &TypesSeen,
+                                          std::string &Output,
+                                          std::vector<const Record *> &List) {
+  raw_string_ostream SS(Output);
+
+  for (const auto *T : Types) {
+    if (TypesSeen.find(T->getValueAsString("Name")) == TypesSeen.end()) {
+      SS << "  OCLT_" + T->getValueAsString("Name") << ",\n";
+      // Save the type names in the same order as their enum value. Note that
+      // the Record can be a VectorType or something else, only the name is
+      // important.
+      List.push_back(T);
+      TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true));
+    }
+  }
+  SS.flush();
+}
+
 void BuiltinNameEmitter::EmitDeclarations() {
+  // Enum of scalar type names (float, int, ...) and generic type sets.
   OS << "enum OpenCLTypeID {\n";
-  std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type");
+
   StringMap<bool> TypesSeen;
-  for (const auto *T : Types) {
-    if (TypesSeen.find(T->getValueAsString("Name")) == TypesSeen.end())
-      OS << "  OCLT_" + T->getValueAsString("Name") << ",\n";
-    TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true));
-  }
+  std::string GenTypeEnums;
+  std::string TypeEnums;
+
+  // Extract generic types and non-generic types separately, to keep
+  // gentypes at the end of the enum which simplifies the special handling
+  // for gentypes in SemaLookup.
+  std::vector<Record *> GenTypes =
+      Records.getAllDerivedDefinitions("GenericType");
+  ExtractEnumTypes(GenTypes, TypesSeen, GenTypeEnums, GenTypeList);
+
+  std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type");
+  ExtractEnumTypes(Types, TypesSeen, TypeEnums, TypeList);
+
+  OS << TypeEnums;
+  OS << GenTypeEnums;
   OS << "};\n";
 
+  // Structure definitions.
   OS << R"(
 
-// Type used in a prototype of an OpenCL builtin function.
-struct OpenCLType {
-  // A type (e.g.: float, int, ...)
-  OpenCLTypeID ID;
-  // Size of vector (if applicable)
-  unsigned VectorWidth;
-  // Address space of the pointer (if applicable)
-  LangAS AS;
-  // Whether the type is a pointer
-  bool isPointer;
+// Represents a return type or argument type.
+struct OpenCLTypeStruct {
+  // A type (e.g. float, int, ...)
+  const OpenCLTypeID ID;
+  // Vector size (if applicable; 0 for scalars and generic types).
+  const unsigned VectorWidth;
 };
 
 // One overload of an OpenCL builtin function.
-struct OpenCLBuiltinDecl {
-  // Number of arguments for the signature
-  unsigned NumArgs;
-  // Index in the OpenCLSignature table to get the required types
-  unsigned ArgTableIndex;
-  // Extension to which it belongs (e.g. cl_khr_subgroups)
-  const char *Extension;
-  // Version in which it was introduced (e.g. CL20)
-  unsigned Version;
+struct OpenCLBuiltinStruct {
+  // Index of the signature in the OpenCLTypeStruct table.
+  const unsigned SigTableIndex;
+  // Entries between index SigTableIndex and (SigTableIndex + NumTypes - 1) in
+  // the SignatureTable represent the complete signature.  The first type at
+  // index SigTableIndex is the return type.
+  const unsigned NumTypes;
 };
 
 )";
 }
 
 void BuiltinNameEmitter::GetOverloads() {
+  // Populate the TypeMap.
+  std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type");
+  unsigned I = 0;
+  for (const auto &T : Types) {
+    TypeMap.insert(std::make_pair(T, I++));
+  }
+
+  // Populate the SignaturesList and the FctOverloadMap.
   unsigned CumulativeSignIndex = 0;
   std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin");
   for (const auto *B : Builtins) {
     StringRef BName = B->getValueAsString("Name");
-    if (OverloadInfo.find(BName) == OverloadInfo.end()) {
-      OverloadInfo.insert(std::make_pair(
+    if (FctOverloadMap.find(BName) == FctOverloadMap.end()) {
+      FctOverloadMap.insert(std::make_pair(
           BName, std::vector<std::pair<const Record *, unsigned>>{}));
     }
 
     auto Signature = B->getValueAsListOfDefs("Signature");
+    // Reuse signatures to avoid unnecessary duplicates.
     auto it =
-        std::find_if(SignatureSet.begin(), SignatureSet.end(),
+        std::find_if(SignaturesList.begin(), SignaturesList.end(),
                      [&](const std::pair<std::vector<Record *>, unsigned> &a) {
                        return a.first == Signature;
                      });
     unsigned SignIndex;
-    if (it == SignatureSet.end()) {
-      SignatureSet.push_back(std::make_pair(Signature, CumulativeSignIndex));
+    if (it == SignaturesList.end()) {
+      SignaturesList.push_back(std::make_pair(Signature, CumulativeSignIndex));
       SignIndex = CumulativeSignIndex;
       CumulativeSignIndex += Signature.size();
     } else {
       SignIndex = it->second;
     }
-    OverloadInfo[BName].push_back(std::make_pair(B, SignIndex));
+    FctOverloadMap[BName].push_back(std::make_pair(B, SignIndex));
+  }
+}
+
+void BuiltinNameEmitter::EmitTypeTable() {
+  OS << "static const OpenCLTypeStruct TypeTable[] = {\n";
+  for (const auto &T : TypeMap) {
+    OS << "  // " << T.second << "\n";
+    OS << "  {OCLT_" << T.first->getValueAsString("Name") << ", "
+       << T.first->getValueAsInt("VecWidth") << "},\n";
   }
+  OS << "};\n\n";
 }
 
 void BuiltinNameEmitter::EmitSignatureTable() {
-  OS << "static const OpenCLType OpenCLSignature[] = {\n";
-  for (auto &P : SignatureSet) {
-    OS << "// " << P.second << "\n";
-    for (Record *R : P.first) {
-      OS << "{ OCLT_" << R->getValueAsString("Name") << ", "
-         << R->getValueAsInt("VecWidth") << ", "
-         << R->getValueAsString("AddrSpace") << ", "
-         << R->getValueAsBit("IsPointer") << "},";
-      OS << "\n";
+  // Store a type (e.g. int, float, int2, ...). The type is stored as an index
+  // of a struct OpenCLType table. Multiple entries following each other form a
+  // signature.
+  OS << "static const unsigned SignatureTable[] = {\n";
+  for (const auto &P : SignaturesList) {
+    OS << "  // " << P.second << "\n  ";
+    for (const Record *R : P.first) {
+      OS << TypeMap.find(R)->second << ", ";
     }
+    OS << "\n";
   }
   OS << "};\n\n";
 }
 
 void BuiltinNameEmitter::EmitBuiltinTable() {
-  OS << "static const OpenCLBuiltinDecl OpenCLBuiltins[] = {\n";
-  for (auto &i : OverloadInfo) {
-    StringRef Name = i.first;
-    OS << "// " << Name << "\n";
-    for (auto &Overload : i.second) {
-      OS << "  { " << Overload.first->getValueAsListOfDefs("Signature").size()
-         << ", " << Overload.second << ", " << '"'
-         << Overload.first->getValueAsString("Extension") << "\", "
-         << Overload.first->getValueAsDef("Version")->getValueAsInt("Version")
+  unsigned Index = 0;
+
+  OS << "static const OpenCLBuiltinStruct BuiltinTable[] = {\n";
+  for (const auto &FOM : FctOverloadMap) {
+
+    OS << "  // " << (Index + 1) << ": " << FOM.first << "\n";
+
+    for (const auto &Overload : FOM.second) {
+      OS << "  { "
+         << Overload.second << ", "
+         << Overload.first->getValueAsListOfDefs("Signature").size()
          << " },\n";
+         Index++;
     }
   }
   OS << "};\n\n";
@@ -240,7 +339,7 @@
 void BuiltinNameEmitter::EmitStringMatcher() {
   std::vector<StringMatcher::StringPair> ValidBuiltins;
   unsigned CumulativeIndex = 1;
-  for (auto &i : OverloadInfo) {
+  for (auto &i : FctOverloadMap) {
     auto &Ov = i.second;
     std::string RetStmt;
     raw_string_ostream SS(RetStmt);
@@ -253,26 +352,89 @@
   }
 
   OS << R"(
-// Return 0 if name is not a recognized OpenCL builtin, or an index
-// into a table of declarations if it is an OpenCL builtin.
-static std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef name) {
+// Find out whether a string matches an existing OpenCL builtin function name.
+// Returns: A pair <0, 0> if no name matches.
+//          A pair <Index, Len> indexing the BuiltinTable if the name is
+//          matching an OpenCL builtin function.
+static std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef Name) {
 
 )";
 
-  StringMatcher("name", ValidBuiltins, OS).Emit(0, true);
+  StringMatcher("Name", ValidBuiltins, OS).Emit(0, true);
 
   OS << "  return std::make_pair(0, 0);\n";
-  OS << "}\n";
+  OS << "} // isOpenCLBuiltin\n";
 }
 
 void BuiltinNameEmitter::EmitQualTypeFinder() {
   OS << R"(
 
-static QualType OCL2Qual(ASTContext &Context, OpenCLType Ty) {
-  QualType RT = Context.VoidTy;
-  switch (Ty.ID) {
+// Convert an OpenCLTypeStruct type to a list of QualTypes.
+// Generic types represent multiple types and vector sizes, thus a vector
+// is returned. The conversion is done in two steps:
+// Step 1: A switch statement fills a vector with scalar base types for the
+//         Cartesian product of (vector sizes) x (types) for generic types,
+//         or a single scalar type for non generic types.
+// Step 2: Qualifiers and other type properties such as vector size are
+//         applied.
+static void OCL2Qual(ASTContext &Context, const OpenCLTypeStruct &Ty,
+                     std::vector<QualType> &QT) {
+  // Number of scalar types in the GenType.
+  unsigned GenTypeNumTypes;
+  // Pointer to the list of vector sizes for the GenType.
+  llvm::SmallVector<unsigned, 6> *GenVectorSizes;
 )";
 
+  // Generate list of vector sizes for each generic type.
+  for (const auto *VectList : Records.getAllDerivedDefinitions("IntList")) {
+    OS << "  llvm::SmallVector<unsigned, 6> List"
+       << VectList->getValueAsString("Name") << "{";
+    for (const auto V : VectList->getValueAsListOfInts("List")) {
+      OS << V << ", ";
+    }
+    OS << "};\n";
+  }
+
+  // Step 1.
+  // Start of switch statement over all types.
+  OS << "\n  switch (Ty.ID) {\n";
+
+  // Switch cases for generic types.
+  for (const auto *GenType : Records.getAllDerivedDefinitions("GenericType")) {
+    OS << "    case OCLT_" << GenType->getValueAsString("Name") << ":\n";
+
+    // Build the Cartesian product of (vector sizes) x (types).  Only insert
+    // the plain scalar types for now; other type information such as vector
+    // size and type qualifiers will be added after the switch statement.
+    for (unsigned I = 0; I < GenType->getValueAsDef("VectorList")
+                             ->getValueAsListOfInts("List")
+                             .size();
+         I++) {
+      for (const auto *T : GenType->getValueAsDef("TypeListField")
+                               ->getValueAsListOfDefs("List")) {
+        OS << "      QT.push_back(Context."
+           << T->getValueAsDef("QTName")->getValueAsString("Name")
+           << ");\n";
+      }
+    }
+    // GenTypeNumTypes is the number of types in the GenType
+    // (e.g. float/double/half).
+    OS << "      GenTypeNumTypes = "
+       << GenType->getValueAsDef("TypeListField")
+              ->getValueAsListOfDefs("List")
+              .size()
+       << ";\n";
+    // GenVectorSizes is the list of vector sizes for this GenType.
+    // QT contains GenTypeNumTypes * #GenVectorSizes elements.
+    OS << "      GenVectorSizes = &List"
+       << GenType->getValueAsDef("VectorList")->getValueAsString("Name")
+       << ";\n";
+    OS << "      break;\n";
+  }
+
+  // Switch cases for non generic, non image types (int, int4, float, ...).
+  // Only insert the plain scalar type; vector information and type qualifiers
+  // are added in step 2.
   std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type");
   StringMap<bool> TypesSeen;
 
@@ -284,28 +446,47 @@
 
     // Check the Type does not have an "abstract" QualType
     auto QT = T->getValueAsDef("QTName");
-    if (QT->getValueAsString("Name") == "null")
+    if (QT->getValueAsBit("IsAbstract") == 1)
       continue;
-
-    OS << "  case OCLT_" << T->getValueAsString("Name") << ":\n";
-    OS << "    RT = Context." << QT->getValueAsString("Name") << ";\n";
-    OS << "    break;\n";
+    // Emit the cases for non generic, non image types.
+    OS << "    case OCLT_" << T->getValueAsString("Name") << ":\n";
+    OS << "      QT.push_back(Context." << QT->getValueAsString("Name")
+       << ");\n";
+    OS << "      break;\n";
   }
-  OS << "  }\n";
 
-  // Special cases
-  OS << R"(
-  if (Ty.VectorWidth > 0)
-    RT = Context.getExtVectorType(RT, Ty.VectorWidth);
+  // End of switch statement.
+  OS << "  } // end of switch (Ty.ID)\n\n";
 
-  if (Ty.isPointer) {
-    RT = Context.getAddrSpaceQualType(RT, Ty.AS);
-    RT = Context.getPointerType(RT);
+  // Step 2.
+  // Add ExtVector types if this was a generic type, as the switch statement
+  // above only populated the list with scalar types.  This completes the
+  // construction of the Cartesian product of (vector sizes) x (types).
+  OS << "  // Construct the different vector types for each generic type.\n";
+  OS << "  if (Ty.ID >= " << TypeList.size() << ") {";
+  OS << R"(
+    for (unsigned I = 0; I < QT.size(); I++) {
+      // For scalars, size is 1.
+      if ((*GenVectorSizes)[I / GenTypeNumTypes] != 1) {
+        QT[I] = Context.getExtVectorType(QT[I],
+                          (*GenVectorSizes)[I / GenTypeNumTypes]);
+      }
+    }
   }
+)";
 
-  return RT;
-}
+  // Assign the right attributes to the types (e.g. vector size).
+  OS << R"(
+  // Set vector size for non-generic vector types.
+  if (Ty.VectorWidth > 1) {
+    for (unsigned Index = 0; Index < QT.size(); Index++) {
+      QT[Index] = Context.getExtVectorType(QT[Index], Ty.VectorWidth);
+    }
+  }
 )";
+
+  // End of the "OCL2Qual" function.
+  OS << "\n} // OCL2Qual\n";
 }
 
 namespace clang {
Index: clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl
===================================================================
--- clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl
+++ clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl
@@ -1,28 +1,34 @@
 // RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -DNO_HEADER
 // RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header
+// expected-no-diagnostics
 
 // Test the -fdeclare-opencl-builtins option.
 
 // Provide typedefs when invoking clang without -finclude-default-header.
 #ifdef NO_HEADER
+typedef char char2 __attribute__((ext_vector_type(2)));
 typedef float float4 __attribute__((ext_vector_type(4)));
-typedef int int4 __attribute__((ext_vector_type(4)));
 typedef int int2 __attribute__((ext_vector_type(2)));
+typedef int int4 __attribute__((ext_vector_type(4)));
+typedef long long2 __attribute__((ext_vector_type(2)));
 typedef unsigned int uint;
 typedef __SIZE_TYPE__ size_t;
 #endif
 
-kernel void basic_conversion(global float4 *buf, global int4 *res) {
-  res[0] = convert_int4(buf[0]);
-}
+kernel void basic_conversion() {
+  double d;
+  float f;
+  char2 c2;
+  long2 l2;
+  float4 f4;
+  int4 i4;
 
-kernel void basic_readonly_image_type(__read_only image2d_t img, int2 coord, global float4 *out) {
-  out[0] = read_imagef(img, coord);
+  f = convert_float(d);
+  d = convert_double_sat_rtp(f);
+  l2 = convert_long2_rtz(c2);
+  i4 = convert_int4_sat(f4);
 }
 
 kernel void basic_subgroup(global uint *out) {
   out[0] = get_sub_group_size();
-// expected-error@-1{{use of declaration 'get_sub_group_size' requires cl_khr_subgroups extension to be enabled}}
-#pragma OPENCL EXTENSION cl_khr_subgroups : enable
-  out[1] = get_sub_group_size();
 }
Index: clang/lib/Sema/SemaLookup.cpp
===================================================================
--- clang/lib/Sema/SemaLookup.cpp
+++ clang/lib/Sema/SemaLookup.cpp
@@ -673,76 +673,158 @@
     D->dump();
 }
 
-/// When trying to resolve a function name, if the isOpenCLBuiltin function
-/// defined in "OpenCLBuiltins.inc" returns a non-null <Index, Len>, then the
-/// identifier is referencing an OpenCL builtin function. Thus, all its
-/// prototypes are added to the LookUpResult.
+/// Helper function to get the QualType instances of the return type and
+/// arguments for OpenCL library functions
+/// \param Context (in) The Context instance.
+/// \param OpenCLBuiltin (in) The signature currently handled.
+/// \param GenTypeMaxCnt (out) Maximum number of types contained in a generic
+///        type used as return type or as argument.
+///        Only meaningful for generic types, otherwise equals 1.
+/// \param RetTypes (out) List of the possible return types.
+/// \param ArgTypes (out) List of the possible types for the arguments.
+static void GetQualTypesForOpenCLBuiltin(
+    ASTContext &Context, const OpenCLBuiltinStruct &OpenCLBuiltin,
+    unsigned &GenTypeMaxCnt, std::vector<QualType> &RetTypes,
+    SmallVector<std::vector<QualType>, 5> &ArgTypes) {
+  // Get the QualType instances of the return types.
+  unsigned Sig = SignatureTable[OpenCLBuiltin.SigTableIndex];
+  OCL2Qual(Context, TypeTable[Sig], RetTypes);
+  GenTypeMaxCnt = RetTypes.size();
+
+  // Get the QualType instances of the arguments.
+  // First type is the return type, skip it.
+  for (unsigned Index = 1; Index < OpenCLBuiltin.NumTypes; Index++) {
+    std::vector<QualType> Ty;
+    OCL2Qual(Context,
+        TypeTable[SignatureTable[OpenCLBuiltin.SigTableIndex + Index]], Ty);
+    ArgTypes.push_back(Ty);
+    GenTypeMaxCnt = (Ty.size() > GenTypeMaxCnt) ? Ty.size() : GenTypeMaxCnt;
+  }
+
+  // Check that that lengths of the gentypes are compatible.
+  // The number of types in the gentype must be a multiple of the number
+  // of types in the return type.
+  if ((GenTypeMaxCnt % RetTypes.size()) != 0) {
+    goto error_handler;
+  }
+  for (unsigned Index = 0; Index < ArgTypes.size(); Index++) {
+    unsigned TypeCntAtIndex = ArgTypes[Index].size();
+    if ((GenTypeMaxCnt % TypeCntAtIndex) != 0) {
+      goto error_handler;
+    }
+  }
+
+  return;
+
+error_handler:
+  llvm_unreachable("Lengths of genTypes used are incompatible in the "
+                   "definition of the builtin function");
+}
+
+/// Helper function to create a list of the possible signatures for the
+/// OpenCL library functions.
+/// \param Context (in) The ASTContext instance.
+/// \param GenTypeMaxCnt (in) Maximum number of types contained in a generic
+///        type used as return type or as argument.
+///        Only meaningful for generic types, otherwise equals 1.
+/// \param FunctionList (out) List of the function declarations.
+/// \param RetTypes (in) List of the possible return types.
+/// \param ArgTypes (in) List of the possible types for the arguments.
+static void
+GetOpenCLBuiltinFctOverloads(ASTContext &Context, unsigned GenTypeMaxCnt,
+                             std::vector<QualType> &FunctionList,
+                             std::vector<QualType> &RetTypes,
+                             SmallVector<std::vector<QualType>, 5> &ArgTypes) {
+  FunctionProtoType::ExtProtoInfo PI;
+  PI.Variadic = false;
+
+  for (unsigned IGenType = 0; IGenType < GenTypeMaxCnt; IGenType++) {
+    SmallVector<QualType, 5> ArgList;
+
+    for (unsigned I = 0; I < ArgTypes.size(); I++) {
+      ArgList.push_back(ArgTypes[I][IGenType % ArgTypes[I].size()]);
+    }
+
+    FunctionList.push_back(Context.getFunctionType(
+        RetTypes[(RetTypes.size() != 1) ? IGenType : 0], ArgList, PI));
+  }
+}
+
+/// When trying to resolve a function name, if isOpenCLBuiltin() returns a
+/// non-null <Index, Len> pair, then the name is referencing an OpenCL
+/// builtin function.  Add all candidate signatures to the LookUpResult.
 ///
-/// \param S The Sema instance
-/// \param LR  The LookupResult instance
-/// \param II  The identifier being resolved
-/// \param Index  The list of prototypes starts at Index in OpenCLBuiltins[]
-/// \param Len  The list of prototypes has Len elements
-static void InsertOCLBuiltinDeclarations(Sema &S, LookupResult &LR,
-                                         IdentifierInfo *II, unsigned Index,
-                                         unsigned Len) {
-
-  for (unsigned i = 0; i < Len; ++i) {
-    const OpenCLBuiltinDecl &Decl = OpenCLBuiltins[Index - 1 + i];
+/// \param S (in) The Sema instance.
+/// \param LR (inout) The LookupResult instance.
+/// \param II (in) The identifier being resolved.
+/// \param FctIndex (in) Starting index in the BuiltinTable.
+/// \param Len (in) The signature list has Len elements.
+static void InsertOCLBuiltinDeclarationsFromTable(Sema &S, LookupResult &LR,
+                                                  IdentifierInfo *II,
+                                                  const unsigned FctIndex,
+                                                  const unsigned Len) {
+  // The builtin function declaration uses generic types (gentype).
+  bool HasGenType = false;
+
+  // Maximum number of types contained in a generic type used as return type or
+  // as argument.  Only meaningful for generic types, otherwise equals 1.
+  unsigned GenTypeMaxCnt;
+
+  for (unsigned SignatureIndex = 0; SignatureIndex < Len; SignatureIndex++) {
+    const OpenCLBuiltinStruct &OpenCLBuiltin =
+        BuiltinTable[FctIndex + SignatureIndex];
     ASTContext &Context = S.Context;
 
-    // Ignore this BIF if the version is incorrect.
-    if (Context.getLangOpts().OpenCLVersion < Decl.Version)
-      continue;
-
-    FunctionProtoType::ExtProtoInfo PI;
-    PI.Variadic = false;
+    std::vector<QualType> RetTypes;
+    SmallVector<std::vector<QualType>, 5> ArgTypes;
 
-    // Defined in "OpenCLBuiltins.inc"
-    QualType RT = OCL2Qual(Context, OpenCLSignature[Decl.ArgTableIndex]);
+    GetQualTypesForOpenCLBuiltin(Context, OpenCLBuiltin, GenTypeMaxCnt,
+                                 RetTypes, ArgTypes);
 
-    SmallVector<QualType, 5> ArgTypes;
-    for (unsigned I = 1; I < Decl.NumArgs; I++) {
-      QualType Ty = OCL2Qual(Context, OpenCLSignature[Decl.ArgTableIndex + I]);
-      ArgTypes.push_back(Ty);
+    if (GenTypeMaxCnt > 1) {
+      HasGenType = true;
     }
 
-    QualType R = Context.getFunctionType(RT, ArgTypes, PI);
-    SourceLocation Loc = LR.getNameLoc();
+    // Create function overload for each type combination.
+    std::vector<QualType> FunctionList;
+    GetOpenCLBuiltinFctOverloads(Context, GenTypeMaxCnt, FunctionList, RetTypes,
+                                 ArgTypes);
 
-    // TODO: This part is taken from Sema::LazilyCreateBuiltin,
-    // maybe refactor it.
+    SourceLocation Loc = LR.getNameLoc();
     DeclContext *Parent = Context.getTranslationUnitDecl();
-    FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, R,
-                                             /*TInfo=*/nullptr, SC_Extern,
-                                             false, R->isFunctionProtoType());
-    New->setImplicit();
-
-    // Create Decl objects for each parameter, adding them to the
-    // FunctionDecl.
-    if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(R)) {
-      SmallVector<ParmVarDecl *, 16> Params;
-      for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) {
-        ParmVarDecl *Parm =
-            ParmVarDecl::Create(Context, New, SourceLocation(),
-                                SourceLocation(), nullptr, FT->getParamType(i),
-                                /*TInfo=*/nullptr, SC_None, nullptr);
-        Parm->setScopeInfo(0, i);
-        Params.push_back(Parm);
+    FunctionDecl *NewOpenCLBuiltin;
+
+    for (unsigned Index = 0; Index < GenTypeMaxCnt; Index++) {
+      NewOpenCLBuiltin = FunctionDecl::Create(
+          Context, Parent, Loc, Loc, II, FunctionList[Index],
+          /*TInfo=*/nullptr, SC_Extern, false,
+          FunctionList[Index]->isFunctionProtoType());
+      NewOpenCLBuiltin->setImplicit();
+
+      // Create Decl objects for each parameter, adding them to the
+      // FunctionDecl.
+      if (const FunctionProtoType *FP =
+              dyn_cast<FunctionProtoType>(FunctionList[Index])) {
+        SmallVector<ParmVarDecl *, 16> ParmList;
+        for (unsigned IParm = 0, e = FP->getNumParams(); IParm != e; ++IParm) {
+          ParmVarDecl *Parm = ParmVarDecl::Create(
+              Context, NewOpenCLBuiltin, SourceLocation(), SourceLocation(),
+              nullptr, FP->getParamType(IParm),
+              /*TInfo=*/nullptr, SC_None, nullptr);
+          Parm->setScopeInfo(0, IParm);
+          ParmList.push_back(Parm);
+        }
+        NewOpenCLBuiltin->setParams(ParmList);
+      }
+      if (!S.getLangOpts().OpenCLCPlusPlus) {
+        NewOpenCLBuiltin->addAttr(OverloadableAttr::CreateImplicit(Context));
       }
-      New->setParams(Params);
+      LR.addDecl(NewOpenCLBuiltin);
     }
-
-    New->addAttr(OverloadableAttr::CreateImplicit(Context));
-
-    if (strlen(Decl.Extension))
-      S.setOpenCLExtensionForDecl(New, Decl.Extension);
-
-    LR.addDecl(New);
   }
 
   // If we added overloads, need to resolve the lookup result.
-  if (Len > 1)
+  if (Len > 1 || HasGenType)
     LR.resolveKind();
 }
 
@@ -772,7 +854,8 @@
       if (S.getLangOpts().OpenCL && S.getLangOpts().DeclareOpenCLBuiltins) {
         auto Index = isOpenCLBuiltin(II->getName());
         if (Index.first) {
-          InsertOCLBuiltinDeclarations(S, R, II, Index.first, Index.second);
+          InsertOCLBuiltinDeclarationsFromTable(S, R, II, Index.first - 1,
+                                                Index.second);
           return true;
         }
       }
Index: clang/lib/Sema/OpenCLBuiltins.td
===================================================================
--- clang/lib/Sema/OpenCLBuiltins.td
+++ clang/lib/Sema/OpenCLBuiltins.td
@@ -40,11 +40,15 @@
 def GenericAS    : AddressSpace<"clang::LangAS::opencl_generic">;
 
 
-// Qualified Type. Allow to retrieve one ASTContext QualType.
-class QualType<string _Name> {
+// Qualified Type.  These map to ASTContext::QualType.
+class QualType<string _Name, bit _IsAbstract=0> {
   // Name of the field or function in a clang::ASTContext
   // E.g. Name="IntTy" for the int type, and "getIntPtrType()" for an intptr_t
   string Name = _Name;
+  // Some QualTypes in this file represent an abstract type for which there is
+  // no corresponding AST QualType, e.g. a GenType or an `image2d_t` type
+  // without access qualifiers.
+  bit IsAbstract = _IsAbstract;
 }
 
 // Helper class to store type access qualifiers (volatile, const, ...).
@@ -52,25 +56,35 @@
   string QualName = _QualName;
 }
 
+// List of integers.
+class IntList<string _Name, list<int> _List> {
+  string Name = _Name;
+  list<int> List = _List;
+}
+
 //===----------------------------------------------------------------------===//
 //                      OpenCL C classes for types
 //===----------------------------------------------------------------------===//
-// OpenCL types (int, float, ...)
+// OpenCL C basic data types (int, float, image2d_t, ...).
+// Its Child classes can represent concrete types (e.g.: VectorType) or
+// custom types (e.g.: GenType).
+// Instances of these child classes should be used in Builtin function
+// arguments.  See the definition of the "read_imagef" function as example.
 class Type<string _Name, QualType _QTName> {
-  // Name of the Type
+  // Name of the Type.
   string Name = _Name;
-  // QualType associated with this type
+  // QualType associated with this type.
   QualType QTName = _QTName;
-  // Size of the vector (if applicable)
-  int VecWidth = 0;
-  // Is pointer
+  // Size of the vector (if applicable).
+  int VecWidth = 1;
+  // Is a pointer.
   bit IsPointer = 0;
   // List of qualifiers associated with the type (volatile, ...)
   list<Qualifier> QualList = [];
-  // Address space
-  string AddrSpace = "clang::LangAS::Default";
   // Access qualifier. Must be one of ("RO", "WO", "RW").
   string AccessQualifier = "";
+  // Address space.
+  string AddrSpace = "clang::LangAS::Default";
 }
 
 // OpenCL vector types (e.g. int2, int3, int16, float8, ...)
@@ -78,7 +92,7 @@
   int VecWidth = _VecWidth;
 }
 
-// OpenCL pointer types (e.g. int*, float*, ...)
+// OpenCL pointer types (e.g. int*, float*, ...).
 class PointerType<Type _Ty, AddressSpace _AS = GlobalAS> :
                                       Type<_Ty.Name, _Ty.QTName> {
   bit IsPointer = 1;
@@ -91,6 +105,31 @@
   let AccessQualifier = _AccessQualifier;
 }
 
+// List of Types.
+class TypeList<string _Name, list<Type> _Type> {
+  string Name = _Name;
+  list<Type> List = _Type;
+}
+
+// A GenericType is an abstract type that defines a set of types as a
+// combination of Types and vector sizes.
+//
+// E.g.: If TypeListField = <int, float> and VectorList = <1, 2, 4>, then
+//       then it represents <int, int2, int4, float, float2, float4>.
+// _Ty          : Name of the GenType.
+// _TypeList    : Types this GenericType can be.
+// _VectorList  : Sizes of the vector for each type of the _TypeList. 0 being a
+//                scalar.
+class GenericType<string _Ty, TypeList _TypeList, IntList _VectorList> :
+                                            Type<_Ty, QualType<"null", 1>> {
+  // Possible element types of the generic type.
+  TypeList TypeListField = _TypeList;
+  // Possible vector sizes of the types in the TypeListField.
+  IntList VectorList = _VectorList;
+  // The VecWidth field is ignored for GenericTypes. Use VectorList instead.
+  let VecWidth = 0;
+}
+
 //===----------------------------------------------------------------------===//
 //                      OpenCL C class for builtin functions
 //===----------------------------------------------------------------------===//
@@ -107,55 +146,11 @@
   Version Version = CL10;
 }
 
-//===----------------------------------------------------------------------===//
-//                           Multiclass definitions
-//===----------------------------------------------------------------------===//
-// multiclass BifN: Creates Builtin class instances for OpenCL builtin
-//                  functions with N arguments.
-// _Name      : Name of the function
-// _Signature : Signature of the function (list of the Type used by the
-//              function, the first one being the return type).
-// _IsVector  : List of bit indicating if the type in the _Signature at the
-//              same index is to be a vector in the multiple overloads. The
-//              list must have at least one non-zero value.
-multiclass Bif0<string _Name, list<Type> _Signature, list<bit> _IsVector> {
-  def : Builtin<_Name, _Signature>;
-  foreach v = [2, 3, 4, 8, 16] in {
-    def : Builtin<_Name,
-                  [!if(_IsVector[0], VectorType<_Signature[0], v>, _Signature[0])]>;
-  }
-}
-multiclass Bif1<string _Name, list<Type> _Signature, list<bit> _IsVector> {
-  def : Builtin<_Name, _Signature>;
-  foreach v = [2, 3, 4, 8, 16] in {
-    def : Builtin<_Name,
-                  [!if(_IsVector[0], VectorType<_Signature[0], v>, _Signature[0]),
-                  !if(_IsVector[1], VectorType<_Signature[1], v>, _Signature[1])]>;
-  }
-}
-multiclass Bif2<string _Name, list<Type> _Signature, list<bit> _IsVector> {
-  def : Builtin<_Name, _Signature>;
-  foreach v = [2, 3, 4, 8, 16] in {
-    def : Builtin<_Name,
-                  [!if(_IsVector[0], VectorType<_Signature[0], v>, _Signature[0]),
-                  !if(_IsVector[1], VectorType<_Signature[1], v>, _Signature[1]),
-                  !if(_IsVector[2], VectorType<_Signature[2], v>, _Signature[2])]>;
-  }
-}
-multiclass Bif3<string _Name, list<Type> _Signature, list<bit> _IsVector> {
-  def : Builtin<_Name, _Signature>;
-  foreach v = [2, 3, 4, 8, 16] in {
-    def : Builtin<_Name,
-                  [!if(_IsVector[0], VectorType<_Signature[0], v>, _Signature[0]),
-                  !if(_IsVector[1], VectorType<_Signature[1], v>, _Signature[1]),
-                  !if(_IsVector[2], VectorType<_Signature[2], v>, _Signature[2]),
-                  !if(_IsVector[3], VectorType<_Signature[3], v>, _Signature[3])]>;
-  }
-}
 //===----------------------------------------------------------------------===//
 //                 Definitions of OpenCL C types
 //===----------------------------------------------------------------------===//
-// OpenCL v1.2 s6.1.1: Built-in Scalar Data Types
+
+// OpenCL v1.0/1.2/2.0 s6.1.1: Built-in Scalar Data Types.
 def Bool      : Type<"bool",      QualType<"BoolTy">>;
 def Char      : Type<"char",      QualType<"CharTy">>;
 def UChar     : Type<"uchar",     QualType<"UnsignedCharTy">>;
@@ -174,30 +169,18 @@
 def UIntPtr   : Type<"uintPtr_t", QualType<"getUIntPtrType()">>;
 def Void      : Type<"void_t",    QualType<"VoidTy">>;
 
-// OpenCL v1.2 s6.1.2: Built-in Vector Data Types
-foreach v = [2, 3, 4, 8, 16] in {
-  def char#v#_t    : VectorType<Char, v>;
-  def uchar#v#_t   : VectorType<UChar, v>;
-  def short#v#_t   : VectorType<Short, v>;
-  def ushort#v#_t  : VectorType<UShort, v>;
-  def "int"#v#_t   : VectorType<Int, v>;
-  def uint#v#_t    : VectorType<UInt, v>;
-  def long#v#_t    : VectorType<Long, v>;
-  def ulong#v#_t   : VectorType<ULong, v>;
-  def float#v#_t   : VectorType<Float, v>;
-  def double#v#_t  : VectorType<Double, v>;
-  def half#v#_t    : VectorType<Half, v>;
-}
+// OpenCL v1.0/1.2/2.0 s6.1.2: Built-in Vector Data Types.
+// Built-in vector data types are created by TableGen's OpenCLBuiltinEmitter.
 
 // OpenCL v1.2 s6.1.3: Other Built-in Data Types
 // These definitions with a "null" name are "abstract". They should not
 // be used in definitions of Builtin functions.
-def image2d_t         : Type<"image2d_t", QualType<"null">>;
-def image3d_t         : Type<"image3d_t", QualType<"null">>;
-def image2d_array_t   : Type<"image2d_array_t", QualType<"null">>;
-def image1d_t         : Type<"image1d_t", QualType<"null">>;
-def image1d_buffer_t  : Type<"image1d_buffer_t", QualType<"null">>;
-def image1d_array_t   : Type<"image1d_array_t", QualType<"null">>;
+def image2d_t         : Type<"image2d_t", QualType<"null", 1>>;
+def image3d_t         : Type<"image3d_t", QualType<"null", 1>>;
+def image2d_array_t   : Type<"image2d_array_t", QualType<"null", 1>>;
+def image1d_t         : Type<"image1d_t", QualType<"null", 1>>;
+def image1d_buffer_t  : Type<"image1d_buffer_t", QualType<"null", 1>>;
+def image1d_array_t   : Type<"image1d_array_t", QualType<"null", 1>>;
 // Unlike the few functions above, the following definitions can be used
 // in definitions of Builtin functions (they have a QualType with a name).
 foreach v = ["RO", "WO", "RW"] in {
@@ -224,6 +207,41 @@
 def Sampler           : Type<"Sampler", QualType<"OCLSamplerTy">>;
 def Event             : Type<"Event", QualType<"OCLEventTy">>;
 
+//===----------------------------------------------------------------------===//
+//                 Definitions of OpenCL gentype variants
+//===----------------------------------------------------------------------===//
+// The OpenCL specification often uses "gentype" in builtin function
+// declarations to indicate that a builtin function is available with various
+// argument and return types.  The types represented by "gentype" vary between
+// different parts of the specification.  The following definitions capture
+// the different type lists for gentypes in different parts of the
+// specification.
+
+// Vector width lists.
+def VecAndScalar: IntList<"VecAndScalar", [1, 2, 3, 4, 8, 16]>;
+def VecNoScalar : IntList<"VecNoScalar", [2, 3, 4, 8, 16]>;
+
+// Type lists.
+def TLFloat : TypeList<"TLFloat", [Float, Double, Half]>;
+
+// GenType definitions for multiple base types (e.g. all floating point types,
+// or all integer types).
+def FGenTypeN   : GenericType<"FGenTypeN", TLFloat, VecAndScalar>;
+
+// GenType definitions for every single base type (e.g. fp32 only).
+// Names are like: GenTypeFloatVecAndScalar.
+foreach Type = [Char, UChar, Short, UShort,
+                Int, UInt, Long, ULong,
+                Float, Double, Half] in {
+  foreach List = [VecAndScalar, VecNoScalar] in {
+    def "GenType"#Type#List :
+              GenericType<"GenType" # Type # List,
+                          TypeList<"GL" # Type.Name, [Type]>,
+                          List>;
+  }
+}
+
+
 //===----------------------------------------------------------------------===//
 //                 Definitions of OpenCL builtin functions
 //===----------------------------------------------------------------------===//
@@ -261,27 +279,23 @@
 foreach name = ["acos", "acosh", "acospi",
                 "asin", "asinh", "asinpi",
                 "atan", "atanh", "atanpi"] in {
-  foreach type = [Float, Double, Half] in {
-    defm : Bif1<name, [type, type], [1, 1]>;
-  }
+  def : Builtin<name, [FGenTypeN, FGenTypeN]>;
 }
 
 foreach name = ["atan2", "atan2pi"] in {
-  foreach type = [Float, Double, Half] in {
-    defm : Bif2<name, [type, type, type], [1, 1, 1]>;
-  }
+  def : Builtin<name, [FGenTypeN, FGenTypeN, FGenTypeN]>;
 }
 
 foreach name = ["fmax", "fmin"] in {
-  foreach type = [Float, Double, Half] in {
-    defm : Bif2<name, [type, type, type], [1, 1, 1]>;
-    defm : Bif2<name, [type, type, type], [1, 1, 0]>;
-  }
+  def : Builtin<name, [FGenTypeN, FGenTypeN, FGenTypeN]>;
+  def : Builtin<name, [GenTypeFloatVecNoScalar, GenTypeFloatVecNoScalar, Float]>;
+  def : Builtin<name, [GenTypeDoubleVecNoScalar, GenTypeDoubleVecNoScalar, Double]>;
+  def : Builtin<name, [GenTypeHalfVecNoScalar, GenTypeHalfVecNoScalar, Half]>;
 }
 
 // OpenCL v1.2 s6.12.14: Built-in Image Read Functions
 def read_imagef : Builtin<"read_imagef",
-                          [float4_t, image2d_RO_t, VectorType<Int, 2>]>;
+                          [VectorType<Float, 4>, image2d_RO_t, VectorType<Int, 2>]>;
 def write_imagef : Builtin<"write_imagef",
                            [Void,
                             image2d_WO_t,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to