Index: cfe/trunk/lib/Sema/OpenCLBuiltins.td =================================================================== --- cfe/trunk/lib/Sema/OpenCLBuiltins.td +++ cfe/trunk/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 { +// Qualified Type. These map to ASTContext::QualType. +class QualType { // 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 _List> { + string Name = _Name; + list 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 { - // 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 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.Name, _Ty.QTName> { bit IsPointer = 1; @@ -91,6 +105,50 @@ let AccessQualifier = _AccessQualifier; } +// List of Types. +class TypeList _Type> { + string Name = _Name; + list 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 TypeList = and VectorList = <1, 2, 4>, then it +// represents . +// _Ty : Name of the GenType. +// _TypeList : List of basic data Types. +// _VectorList : Sizes of the vector for each type of the _TypeList, 1 being a +// scalar. +// +// Some rules apply when using multiple GenericType arguments in a declaration: +// 1. The number of vector sizes must be equal or 1 for all gentypes in a +// declaration. +// 2. The number of Types must be equal or 1 for all gentypes in a +// declaration. +// 3. Generic types are combined by iterating over all generic types at once. +// For example, for the following GenericTypes +// GenT1 = GenericType and +// GenT2 = GenericType +// A declaration f(GenT1, GenT2) results in the combinations +// f(half, float), f(half2, float2), f(half, int), f(half2, int2) . +// 4. "sgentype" from the OpenCL specification is supported by specifying +// a single vector size. +// For example, for the following GenericTypes +// GenT = GenericType and +// SGenT = GenericType +// A declaration f(GenT, SGenT) results in the combinations +// f(half, half), f(half2, half), f(int, int), f(int2, int) . +class GenericType : + Type<_Ty, QualType<"null", 1>> { + // Possible element types of the generic type. + TypeList TypeList = _TypeList; + // Possible vector sizes of the types in the TypeList. + IntList VectorList = _VectorList; + // The VecWidth field is ignored for GenericTypes. Use VectorList instead. + let VecWidth = 0; +} + //===----------------------------------------------------------------------===// // OpenCL C class for builtin functions //===----------------------------------------------------------------------===// @@ -108,54 +166,10 @@ } //===----------------------------------------------------------------------===// -// 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 _Signature, list _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 _Signature, list _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 _Signature, list _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 _Signature, list _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 +188,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; - def uchar#v#_t : VectorType; - def short#v#_t : VectorType; - def ushort#v#_t : VectorType; - def "int"#v#_t : VectorType; - def uint#v#_t : VectorType; - def long#v#_t : VectorType; - def ulong#v#_t : VectorType; - def float#v#_t : VectorType; - def double#v#_t : VectorType; - def half#v#_t : VectorType; -} +// 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 { @@ -225,6 +227,49 @@ 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]>; +def Vec1 : IntList<"Vec1", [1]>; + +// Type lists. +def TLFloat : TypeList<"TLFloat", [Float, Double, Half]>; + +def TLAllInts : TypeList<"TLAllInts", [Char, UChar, Short, UShort, Int, UInt, Long, ULong]>; + +// GenType definitions for multiple base types (e.g. all floating point types, +// or all integer types). +// All integer +def AIGenType1 : GenericType<"AIGenType1", TLAllInts, Vec1>; +def AIGenTypeN : GenericType<"AIGenTypeN", TLAllInts, VecAndScalar>; +def AIGenTypeNNoScalar : GenericType<"AIGenTypeNNoScalar", TLAllInts, VecNoScalar>; +// Float +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 VecSizes = [VecAndScalar, VecNoScalar] in { + def "GenType" # Type # VecSizes : + GenericType<"GenType" # Type # VecSizes, + TypeList<"GL" # Type.Name, [Type]>, + VecSizes>; + } +} + + +//===----------------------------------------------------------------------===// // Definitions of OpenCL builtin functions //===----------------------------------------------------------------------===// //-------------------------------------------------------------------- @@ -261,27 +306,29 @@ foreach name = ["acos", "acosh", "acospi", "asin", "asinh", "asinpi", "atan", "atanh", "atanpi"] in { - foreach type = [Float, Double, Half] in { - defm : Bif1; - } + def : Builtin; } foreach name = ["atan2", "atan2pi"] in { - foreach type = [Float, Double, Half] in { - defm : Bif2; - } + def : Builtin; } foreach name = ["fmax", "fmin"] in { - foreach type = [Float, Double, Half] in { - defm : Bif2; - defm : Bif2; - } + def : Builtin; + def : Builtin; + def : Builtin; + def : Builtin; +} + +// OpenCL v1.1 s6.11.3, v1.2 s6.12.3, v2.0 s6.13.3 - Integer Functions +foreach name = ["max", "min"] in { + def : Builtin; + def : Builtin; } // OpenCL v1.2 s6.12.14: Built-in Image Read Functions def read_imagef : Builtin<"read_imagef", - [float4_t, image2d_RO_t, VectorType]>; + [VectorType, image2d_RO_t, VectorType]>; def write_imagef : Builtin<"write_imagef", [Void, image2d_WO_t, Index: cfe/trunk/lib/Sema/SemaLookup.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaLookup.cpp +++ cfe/trunk/lib/Sema/SemaLookup.cpp @@ -673,76 +673,148 @@ D->dump(); } -/// When trying to resolve a function name, if the isOpenCLBuiltin function -/// defined in "OpenCLBuiltins.inc" returns a non-null , then the -/// identifier is referencing an OpenCL builtin function. Thus, all its -/// prototypes are added 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]; - ASTContext &Context = S.Context; +/// Get the QualType instances of the return type and arguments for an OpenCL +/// builtin function signature. +/// \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 argument types. For each +/// argument, ArgTypes contains QualTypes for the Cartesian product +/// of (vector sizes) x (types) . +static void GetQualTypesForOpenCLBuiltin( + ASTContext &Context, const OpenCLBuiltinStruct &OpenCLBuiltin, + unsigned &GenTypeMaxCnt, std::vector &RetTypes, + SmallVector, 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 Ty; + OCL2Qual(Context, + TypeTable[SignatureTable[OpenCLBuiltin.SigTableIndex + Index]], Ty); + ArgTypes.push_back(Ty); + GenTypeMaxCnt = (Ty.size() > GenTypeMaxCnt) ? Ty.size() : GenTypeMaxCnt; + } +} - // Ignore this BIF if the version is incorrect. - if (Context.getLangOpts().OpenCLVersion < Decl.Version) - continue; +/// Create a list of the candidate function overloads for an OpenCL builtin +/// function. +/// \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 FunctionTypes. +/// \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 &FunctionList, + std::vector &RetTypes, + SmallVector, 5> &ArgTypes) { + FunctionProtoType::ExtProtoInfo PI; + PI.Variadic = false; + + // Create FunctionTypes for each (gen)type. + for (unsigned IGenType = 0; IGenType < GenTypeMaxCnt; IGenType++) { + SmallVector ArgList; + + for (unsigned A = 0; A < ArgTypes.size(); A++) { + // Builtins such as "max" have an "sgentype" argument that represents + // the corresponding scalar type of a gentype. The number of gentypes + // must be a multiple of the number of sgentypes. + assert(GenTypeMaxCnt % ArgTypes[A].size() == 0 && + "argument type count not compatible with gentype type count"); + unsigned Idx = IGenType % ArgTypes[A].size(); + ArgList.push_back(ArgTypes[A][Idx]); + } + + FunctionList.push_back(Context.getFunctionType( + RetTypes[(RetTypes.size() != 1) ? IGenType : 0], ArgList, PI)); + } +} - FunctionProtoType::ExtProtoInfo PI; - PI.Variadic = false; +/// When trying to resolve a function name, if isOpenCLBuiltin() returns a +/// non-null pair, then the name is referencing an OpenCL +/// builtin function. Add all candidate signatures to the LookUpResult. +/// +/// \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; - // Defined in "OpenCLBuiltins.inc" - QualType RT = OCL2Qual(Context, OpenCLSignature[Decl.ArgTableIndex]); + std::vector RetTypes; + SmallVector, 5> ArgTypes; - SmallVector ArgTypes; - for (unsigned I = 1; I < Decl.NumArgs; I++) { - QualType Ty = OCL2Qual(Context, OpenCLSignature[Decl.ArgTableIndex + I]); - ArgTypes.push_back(Ty); + // Obtain QualType lists for the function signature. + GetQualTypesForOpenCLBuiltin(Context, OpenCLBuiltin, GenTypeMaxCnt, + RetTypes, ArgTypes); + 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 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(R)) { - SmallVector 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); - } - New->setParams(Params); - } - - New->addAttr(OverloadableAttr::CreateImplicit(Context)); - - if (strlen(Decl.Extension)) - S.setOpenCLExtensionForDecl(New, Decl.Extension); + FunctionDecl *NewOpenCLBuiltin; - LR.addDecl(New); + 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(FunctionList[Index])) { + SmallVector 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)); + } + LR.addDecl(NewOpenCLBuiltin); + } } // If we added overloads, need to resolve the lookup result. - if (Len > 1) + if (Len > 1 || HasGenType) LR.resolveKind(); } @@ -772,7 +844,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: cfe/trunk/test/SemaOpenCL/fdeclare-opencl-builtins.cl =================================================================== --- cfe/trunk/test/SemaOpenCL/fdeclare-opencl-builtins.cl +++ cfe/trunk/test/SemaOpenCL/fdeclare-opencl-builtins.cl @@ -1,28 +1,41 @@ // 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 char char4 __attribute__((ext_vector_type(4))); 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; + + f = convert_float(d); + d = convert_double_sat_rtp(f); + l2 = convert_long2_rtz(c2); + i4 = convert_int4_sat(f4); } -kernel void basic_readonly_image_type(__read_only image2d_t img, int2 coord, global float4 *out) { - out[0] = read_imagef(img, coord); +char4 test_int(char c, char4 c4) { + char m = max(c, c); + char4 m4 = max(c4, c4); + return max(c4, c); } 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: cfe/trunk/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp =================================================================== --- cfe/trunk/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp +++ cfe/trunk/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp @@ -15,12 +15,39 @@ // // For a successful lookup of e.g. the "cos" builtin, isOpenCLBuiltin("cos") // returns a pair . -// OpenCLBuiltins[Index] to OpenCLBuiltins[Index + Len] contains the pairs +// BuiltinTable[Index] to BuiltinTable[Index + Len] contains the pairs // 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 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&) +// 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 &Types, + StringMap &TypesSeen, std::string &Output, + std::vector &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 @@ // <, 5>, // ... // <, 35>. - std::vector, unsigned>> SignatureSet; + std::vector, 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: // + // cumulative index of the associated signature in the SignaturesList> // E.g.: The function cos: (float cos(float), double cos(double), ...) // <"cos", <, - // > - // > + // , + // > // ptrToPrototype1 has the following signature: MapVector>> - OverloadInfo; + FctOverloadMap; + + // Contains the map of OpenCL types to their index in the TypeTable. + MapVector TypeMap; + + // List of OpenCL type names in the same order as in enum OpenCLTypeID. + // This list does not contain generic types. + std::vector TypeList; + + // Same as TypeList, but for generic types only. + std::vector 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,191 @@ EmitQualTypeFinder(); } +void BuiltinNameEmitter::ExtractEnumTypes(std::vector &Types, + StringMap &TypesSeen, + std::string &Output, + std::vector &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 Types = Records.getAllDerivedDefinitions("Type"); + StringMap 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 GenTypes = + Records.getAllDerivedDefinitions("GenericType"); + ExtractEnumTypes(GenTypes, TypesSeen, GenTypeEnums, GenTypeList); + + std::vector 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; }; )"; } +// Verify that the combination of GenTypes in a signature is supported. +// To simplify the logic for creating overloads in SemaLookup, only allow +// a signature to contain different GenTypes if these GenTypes represent +// the same number of actual scalar or vector types. +// +// Exit with a fatal error if an unsupported construct is encountered. +static void VerifySignature(const std::vector &Signature, + const Record *BuiltinRec) { + unsigned GenTypeVecSizes = 1; + unsigned GenTypeTypes = 1; + + for (const auto *T : Signature) { + // Check all GenericType arguments in this signature. + if (T->isSubClassOf("GenericType")) { + // Check number of vector sizes. + unsigned NVecSizes = + T->getValueAsDef("VectorList")->getValueAsListOfInts("List").size(); + if (NVecSizes != GenTypeVecSizes && NVecSizes != 1) { + if (GenTypeVecSizes > 1) { + // We already saw a gentype with a different number of vector sizes. + PrintFatalError(BuiltinRec->getLoc(), + "number of vector sizes should be equal or 1 for all gentypes " + "in a declaration"); + } + GenTypeVecSizes = NVecSizes; + } + + // Check number of data types. + unsigned NTypes = + T->getValueAsDef("TypeList")->getValueAsListOfDefs("List").size(); + if (NTypes != GenTypeTypes && NTypes != 1) { + if (GenTypeTypes > 1) { + // We already saw a gentype with a different number of types. + PrintFatalError(BuiltinRec->getLoc(), + "number of types should be equal or 1 for all gentypes " + "in a declaration"); + } + GenTypeTypes = NTypes; + } + } + } +} + void BuiltinNameEmitter::GetOverloads() { + // Populate the TypeMap. + std::vector 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 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>{})); } 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, unsigned> &a) { return a.first == Signature; }); unsigned SignIndex; - if (it == SignatureSet.end()) { - SignatureSet.push_back(std::make_pair(Signature, CumulativeSignIndex)); + if (it == SignaturesList.end()) { + VerifySignature(Signature, B); + 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 +383,7 @@ void BuiltinNameEmitter::EmitStringMatcher() { std::vector 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 +396,87 @@ } 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 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 indexing the BuiltinTable if the name is +// matching an OpenCL builtin function. +static std::pair 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 &QT) { + // Number of scalar types in the GenType. + unsigned GenTypeNumTypes; + // Pointer to the list of vector sizes for the GenType. + llvm::SmallVector *GenVectorSizes; )"; + // Generate list of vector sizes for each generic type. + for (const auto *VectList : Records.getAllDerivedDefinitions("IntList")) { + OS << " llvm::SmallVector 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("TypeList")->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("TypeList")->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 Types = Records.getAllDerivedDefinitions("Type"); StringMap TypesSeen; @@ -284,28 +488,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 {