Index: clang/lib/Sema/OpenCLBuiltins.td =================================================================== --- clang/lib/Sema/OpenCLBuiltins.td +++ clang/lib/Sema/OpenCLBuiltins.td @@ -40,37 +40,45 @@ 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, ...). -class Qualifier { - 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 +86,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 +99,31 @@ 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 TypeListField = and VectorList = <1, 2, 4>, then +// then it represents . +// _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 : + 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 +140,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 _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 +163,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 { @@ -224,6 +201,38 @@ 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. +def FGenTypeN : GenericType<"FGenTypeN", TLFloat, VecAndScalar>; +// Generate basic GenTypes. 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 +270,23 @@ 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.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: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -673,76 +673,154 @@ 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. +/// 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 &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; + } + + // Check that that lengths of the gentypes are compatibles. + 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 Context 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 &FunctionList, + std::vector &RetTypes, + SmallVector, 5> &ArgTypes) { + FunctionProtoType::ExtProtoInfo PI; + PI.Variadic = false; + + for (unsigned IGenType = 0; IGenType < GenTypeMaxCnt; IGenType++) { + SmallVector 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 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 The Sema instance. +/// \param LR The LookupResult instance. +/// \param II The identifier being resolved. +/// \param FctIndex Starting index in the BuiltinTable. +/// \param Len The list of signatures 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; + std::vector RetTypes; + SmallVector, 5> ArgTypes; - FunctionProtoType::ExtProtoInfo PI; - PI.Variadic = false; + GetQualTypesForOpenCLBuiltin(Context, OpenCLBuiltin, GenTypeMaxCnt, + RetTypes, ArgTypes); - // Defined in "OpenCLBuiltins.inc" - QualType RT = OCL2Qual(Context, OpenCLSignature[Decl.ArgTableIndex]); - - SmallVector 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 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); + 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(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); } - New->setParams(Params); + NewOpenCLBuiltin->addAttr(OverloadableAttr::CreateImplicit(Context)); + 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 +850,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/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/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 . -// 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,52 @@ // 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 instance + // in the List parameter. + // \param Types (in) List containing the Types to extract. + // \param TypesSeen (out) List containing the Types already extracted. + // \param Output (out) String containing the enums to emit in the output file. + // \param List (out) List to fill with the extracted types. + 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. This table contains all the possible types. A type can + // have several attributes (e.g. vector size, whether it is a pointer type, + // constness, ...). + // For example + // {OCLT_Double, 2}, + // is describing the type "Double", being a vector of 2 elements. See the + // struct OpenCLTypeStruct for more information. + 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 +147,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 +180,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 +195,144 @@ 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; + + // Generic types are defined at the end of the enum. + 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; }; )"; } 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"); 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()) { + 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) { + // 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 (Record *R : P.first) { - OS << "{ OCLT_" << R->getValueAsString("Name") << ", " - << R->getValueAsInt("VecWidth") << ", " - << R->getValueAsString("AddrSpace") << ", " - << R->getValueAsBit("IsPointer") << "},"; - OS << "\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 << " " << FOM.first << "\n"; + + for (const auto &Overload : FOM.second) { + OS << " { " + << Overload.second << ", " + << Overload.first->getValueAsListOfDefs("Signature").size() << " },\n"; + Index++; } } OS << "};\n\n"; @@ -240,7 +341,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 +354,82 @@ } 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); 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. +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"; + } + + // 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; the ExtVector type will be added after + // the switch statement such that it is only added for the case matching + // the input to the OCL2Qual function. + 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, ...). std::vector Types = Records.getAllDerivedDefinitions("Type"); StringMap TypesSeen; @@ -284,28 +441,46 @@ // 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); + // 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 {