Index: include/clang/Basic/CMakeLists.txt =================================================================== --- include/clang/Basic/CMakeLists.txt +++ include/clang/Basic/CMakeLists.txt @@ -41,6 +41,12 @@ TARGET ClangAttrHasAttributeImpl ) +clang_tablegen(OpenCLBuiltins.inc + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ -gen-clang-opencl-builtins + SOURCE OpenCLBuiltins.td + TARGET ClangOpenCLBuiltinsImpl + ) + # ARM NEON clang_tablegen(arm_neon.inc -gen-arm-neon-sema SOURCE arm_neon.td Index: include/clang/Basic/OpenCLBuiltins.td =================================================================== --- /dev/null +++ include/clang/Basic/OpenCLBuiltins.td @@ -0,0 +1,169 @@ +//==--- OpenCLBuiltins.td - OpenCL builtin definitions --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +class Type { + string name = Name; + int vecWidth = 0; + int isPointer = 0; + string as = "clang::LangAS::Default"; + int has_rounding = 0; + int is_integer = 0; +} + +class VectorType : Type { + int vecWidth = VecWidth; +} + +class AddressSpace { + string as = _as; +} + +def Global: AddressSpace<"clang::LangAS::opencl_global">; +def Constant: AddressSpace<"clang::LangAS::opencl_constant">; +def Generic: AddressSpace<"clang::LangAS::opencl_generic">; + +class PointerType : Type { + int isPointer = 1; + string as = AS.as; +} + +class Version { + int version = v; +} + +def CL10: Version<100>; +def CL11: Version<110>; +def CL12: Version<120>; +def CL20: Version<200>; + +def void_t: Type<"void_t">; + +def size_t_t: Type<"size_t">; + +let is_integer = 1 in { + def char_t : Type<"char">; + def uchar_t : Type<"uchar">; + def short_t : Type<"short">; + def ushort_t : Type<"ushort">; + def int_t : Type<"int">; + def uint_t : Type<"uint">; + def long_t : Type<"long">; + def ulong_t : Type<"ulong">; +} +def int2 : VectorType; + +def half_t : Type<"half">; +let has_rounding = 1 in { + def float_t : Type<"float">; + def double_t : Type<"double">; +} + +def float4_t : VectorType; + +def image2d_ro_t : Type<"image2d_ro_t">; +def image2d_wo_t : Type<"image2d_wo_t">; + +class Builtin Args> { + string name = Name; + string extension; + list args = !listconcat([ReturnType], Args); + string extension = ""; + Version version = CL10; +} + +// Creates builtins for one argument BIFs, taking and returning the same type. +multiclass bi_vec { + def: Builtin; + foreach v = [2, 3, 4, 8, 16] in { + def : Builtin, [VectorType]>; + } +} + +// Creates builtins for two argument BIFs, taking and returning the same type. +multiclass bi_vec2 { + def: Builtin; + foreach v = [2, 3, 4, 8, 16] in { + def : Builtin, [VectorType, VectorType]>; + } +} + +// Creates builtins for two argument BIFs, taking a vector and a scale and returning the vector type. +multiclass bi_vec3 { + foreach v = [2, 3, 4, 8, 16] in { + def : Builtin, [VectorType, ReturnType]>; + } +} + +// 6.12.2 +foreach name = ["acos", "acosh", "acospi", "asin", "asinh", "asinpi", "atan"] in { + foreach type = [float_t, double_t] in { + defm name#type : bi_vec; + } +} + +foreach name = ["atan2"] in { + foreach type = [float_t, double_t] in { + defm name#type : bi_vec2; + } +} + +foreach name = ["fmax", "fmin"] in { + foreach type = [float_t, double_t] in { + defm: bi_vec2; + defm: bi_vec3; + } +} + +// example 'foo', to show using 'version' +def: Builtin<"foo_version", int_t, [PointerType]>; +let version = CL20 in { + def: Builtin<"foo_version", int_t, [PointerType]>; +} + +// Helper classes for the convert_ BIFs. +class SatModes { + list modes = !if(ty.is_integer, ["", "_sat"], [""]); +} + +class RoundingModes { + list modes = !if(!or(ty.has_rounding, ty2.has_rounding), ["", "_rte", "_rtz", "_rtp", "_rtn"], [""]); +} + +// Generate the convert_ builtins. +foreach type = [float_t, double_t, char_t, uchar_t, short_t, ushort_t, + int_t, uint_t, long_t, ulong_t] in { + foreach type2 = [float_t, double_t, char_t, uchar_t, short_t, ushort_t, + int_t, uint_t, long_t, ulong_t] in { + foreach sat = SatModes.modes in { + foreach rte = RoundingModes.modes in { + def: Builtin<"convert_" # type.name # sat # rte, type, [type2]>; + foreach v = [2, 3, 4, 8, 16] in { + def: Builtin<"convert_" # type.name # v # sat # rte, VectorType, [VectorType]>; + } + } + } + } +} + +// Example showing 'extension' +let extension = "cl_khr_subgroups" in { + def : Builtin<"get_sub_group_size", uint_t, []>; +} + +// samplerless read image +def : Builtin<"read_imagef", float4_t, [image2d_ro_t, VectorType]>; +def : Builtin<"write_imagef", void_t, [image2d_wo_t, VectorType, VectorType]>; + +// 6.11.1 +def get_work_dim : Builtin<"get_work_dim", uint_t, []>; +foreach name = ["get_global_size", "get_global_id", "get_local_size", + "get_local_id", "get_num_groups", "get_group_id", + "get_global_offset"] in { + def name : Builtin; +} Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -47,6 +47,8 @@ #include #include +#include "clang/Basic/OpenCLBuiltins.inc" + using namespace clang; using namespace sema; @@ -667,6 +669,131 @@ D->dump(); } +// TODO: Auto-generate this from tablegen +static QualType OCL2Qual(ASTContext &Context, OpenCLType Ty) { + QualType RT = Context.VoidTy; + + switch (Ty.ID) { + case OCLT_size_t: + RT = Context.getSizeType(); + break; + case OCLT_int: + RT = Context.IntTy; + break; + case OCLT_uint: + RT = Context.UnsignedIntTy; + break; + case OCLT_char: + RT = Context.CharTy; + break; + case OCLT_uchar: + RT = Context.UnsignedCharTy; + break; + case OCLT_short: + RT = Context.ShortTy; + break; + case OCLT_ushort: + RT = Context.UnsignedShortTy; + break; + case OCLT_long: + RT = Context.LongTy; + break; + case OCLT_ulong: + RT = Context.UnsignedLongTy; + break; + case OCLT_float: + RT = Context.FloatTy; + break; + case OCLT_double: + RT = Context.DoubleTy; + break; + case OCLT_image2d_ro_t: + RT = Context.OCLImage2dROTy; + break; + case OCLT_image2d_wo_t: + RT = Context.OCLImage2dWOTy; + break; + case OCLT_void_t: + RT = Context.VoidTy; + break; + default: + assert(0 && "unexpected type!"); + break; + } + + if (Ty.VectorWidth > 0) + RT = Context.getExtVectorType(RT, Ty.VectorWidth); + + if (Ty.isPointer != 0) { + RT = Context.getAddrSpaceQualType(RT, Ty.AS); + RT = Context.getPointerType(RT); + } + return RT; +} + +static void InsertBuiltinDeclarations(Sema &S, LookupResult &LR, + IdentifierInfo *II, + unsigned Index, unsigned Len) { + for (unsigned i = 0; i < Len; ++i) { + OpenCLBuiltinDecl &Decl = OpenCLBuiltins[Index - 1 + i]; + ASTContext &Context = S.Context; + + // Ignore this BIF if the the version is incorrect. + if (Context.getLangOpts().OpenCLVersion < Decl.Version) + continue; + + FunctionProtoType::ExtProtoInfo PI; + PI.Variadic = false; + + QualType RT = OCL2Qual(Context, OpenCLArgTypes[Decl.ArgTableIndex]); + + SmallVector ArgTypes; + for (unsigned i = 1; i < Decl.NumArgs; i++) { + QualType Ty = OCL2Qual(Context, OpenCLArgTypes[Decl.ArgTableIndex + i]); + ArgTypes.push_back(Ty); + } + + QualType R = Context.getFunctionType(RT, ArgTypes, PI); + SourceLocation Loc = LR.getNameLoc(); + + // TODO: This part is taken from Sema::LazilyCreateBuiltin, maybe refactor it. + 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); + + LR.addDecl(New); + } + + // If we added overloads, need to resolve the lookup result. + if (Len > 1) + LR.resolveKind(); +} + /// Lookup a builtin function, when name lookup would otherwise /// fail. static bool LookupBuiltin(Sema &S, LookupResult &R) { @@ -689,6 +816,15 @@ } } + // Check if this is an OpenCL Builtin, and if so, insert the declarations. + if (S.getLangOpts().OpenCL) { + auto Index = isOpenCLBuiltin(II->getName()); + if (Index.first) { + InsertBuiltinDeclarations(S, R, II, Index.first, Index.second); + return true; + } + } + // If this is a builtin on this (or all) targets, create the decl. if (unsigned BuiltinID = II->getBuiltinID()) { // In C++ and OpenCL (spec v1.2 s6.9.f), we don't have any predefined Index: test/SemaOpenCL/builtin-new.cl =================================================================== --- /dev/null +++ test/SemaOpenCL/builtin-new.cl @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL1.2 -DCL12 +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL2.0 -DCL20 + +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 unsigned int uint; + +kernel void test(global float4* buf, global int4* res) +{ + res[0] = convert_int4(buf[0]); +} + +kernel void test2(global int* bar) { + bar[0] = foo_version(bar); +} + +kernel void test3(constant int* bar, global int* baz) { + baz[0] = foo_version(bar); +#ifdef CL12 +// expected-error@-2{{passing '__constant int *' to parameter of type '__global int *' changes address space of pointer}} +#endif +} + +kernel void test4(image2d_t img, int2 coord, global float4* out) { + out[0] = read_imagef(img, coord); +} + +kernel void test5(write_only image2d_t img, int2 coord, float4 colour) { + write_imagef(img, coord, colour); +} + +#ifdef CL20 +kernel void test6(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(); +} +#endif Index: utils/TableGen/CMakeLists.txt =================================================================== --- utils/TableGen/CMakeLists.txt +++ utils/TableGen/CMakeLists.txt @@ -8,6 +8,7 @@ ClangCommentHTMLTagsEmitter.cpp ClangDataCollectorsEmitter.cpp ClangDiagnosticsEmitter.cpp + ClangOpenCLBuiltinEmitter.cpp ClangOptionDocEmitter.cpp ClangSACheckersEmitter.cpp NeonEmitter.cpp Index: utils/TableGen/ClangOpenCLBuiltinEmitter.cpp =================================================================== --- /dev/null +++ utils/TableGen/ClangOpenCLBuiltinEmitter.cpp @@ -0,0 +1,189 @@ +//===- ClangOpenCLBuiltinEmitter.cpp - Generate Clang OpenCL Builtin handling +//=-*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits Clang OpenCL Builtin checking code. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" + +#include + +using namespace llvm; + +namespace { +class BuiltinNameEmitter { +public: + BuiltinNameEmitter(RecordKeeper &Records, raw_ostream &OS) + : Records(Records), OS(OS) {} + + void Emit(); + +private: + RecordKeeper &Records; + raw_ostream &OS; + + void EmitDeclarations(); + void EmitTable(); + void GetOverloads(); + + MapVector>> + OverloadInfo; + std::vector, unsigned>> ArgTypesSet; +}; +} // namespace + +void BuiltinNameEmitter::GetOverloads() { + unsigned CumulativeArgIndex = 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( + BName, std::vector>{})); + } + + auto Args = B->getValueAsListOfDefs("args"); + auto it = + std::find_if(ArgTypesSet.begin(), ArgTypesSet.end(), + [&](const std::pair, unsigned> &a) { + return a.first == Args; + }); + unsigned ArgIndex; + if (it == ArgTypesSet.end()) { + ArgTypesSet.push_back(std::make_pair(Args, CumulativeArgIndex)); + ArgIndex = CumulativeArgIndex; + CumulativeArgIndex += Args.size(); + } else { + ArgIndex = it->second; + } + OverloadInfo[BName].push_back(std::make_pair(B, ArgIndex)); + } +} + +void BuiltinNameEmitter::EmitDeclarations() { + 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)); + } + OS << "};\n"; + + OS << R"( +struct OpenCLType { + OpenCLTypeID ID; + unsigned VectorWidth; + unsigned isPointer; + clang::LangAS AS; +}; + +struct OpenCLBuiltinDecl { + unsigned NumArgs; + unsigned ArgTableIndex; + const char* Extension; + unsigned Version; +}; + +)"; +} + +void BuiltinNameEmitter::EmitTable() { + OS << "OpenCLBuiltinDecl OpenCLBuiltins[] = {\n"; + for (auto &i : OverloadInfo) { + StringRef Name = i.first; + OS << "// " << Name << "\n"; + for (auto &Overload : i.second) { + OS << " { " + << Overload.first->getValueAsListOfDefs("args").size() << ", " + << Overload.second << ", " << '"' + << Overload.first->getValueAsString("extension") << "\", " + << Overload.first->getValueAsDef("version")->getValueAsInt("version") + << " },\n"; + } + } + OS << "};\n\n"; +} + +void BuiltinNameEmitter::Emit() { + emitSourceFileHeader("OpenCL Builtin handling", OS); + + OS << "#include \"llvm/ADT/StringRef.h\"\n\n"; + + EmitDeclarations(); + + GetOverloads(); + + std::vector> ArgTypes; + + OS << "OpenCLType OpenCLArgTypes[] = {\n"; + for (auto &P : ArgTypesSet) { + OS << "// " << P.second << "\n"; + for (Record *R : P.first) { + OS << "{ OCLT_" << R->getValueAsString("name") << ", " + << R->getValueAsInt("vecWidth") << ", " + << R->getValueAsInt("isPointer") << ", " << R->getValueAsString("as") + << ", " + << "}, "; + OS << "\n"; + } + } + OS << "};\n\n"; + + EmitTable(); + + // Construct a StringMatcher. + std::vector validBuiltins; + unsigned CumulativeIndex = 1; + for (auto &i : OverloadInfo) { + auto &Ov = i.second; + std::string RetStmt; + raw_string_ostream SS(RetStmt); + SS << "return std::make_pair(" << CumulativeIndex << ", " << Ov.size() + << ");"; + SS.flush(); + CumulativeIndex += Ov.size(); + + validBuiltins.push_back(StringMatcher::StringPair(i.first, RetStmt)); + } + + OS << "// Return 0 if name is not a recognized OpenCL builtin, or an index\n" + "// into a table of declarations if it is an OpenCL builtin.\n" + "std::pair isOpenCLBuiltin(llvm::StringRef name) " + "{\n"; + + StringMatcher("name", validBuiltins, OS).Emit(0, true); + + OS << " return std::make_pair(0, 0);\n"; + OS << "}\n"; +} + +namespace clang { + +void EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { + BuiltinNameEmitter NameChecker(Records, OS); + NameChecker.Emit(); +} + +} // end namespace clang Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -61,7 +61,8 @@ GenDiagDocs, GenOptDocs, GenDataCollectors, - GenTestPragmaAttributeSupportedAttributes + GenTestPragmaAttributeSupportedAttributes, + GenClangOpenCLBuiltins, }; namespace { @@ -161,7 +162,9 @@ clEnumValN(GenTestPragmaAttributeSupportedAttributes, "gen-clang-test-pragma-attribute-supported-attributes", "Generate a list of attributes supported by #pragma clang " - "attribute for testing purposes"))); + "attribute for testing purposes"), + clEnumValN(GenClangOpenCLBuiltins, "gen-clang-opencl-builtins", + "Generate OpenCL builtin handlers"))); cl::opt ClangComponent("clang-component", @@ -288,6 +291,9 @@ case GenTestPragmaAttributeSupportedAttributes: EmitTestPragmaAttributeSupportedAttributes(Records, OS); break; + case GenClangOpenCLBuiltins: + EmitClangOpenCLBuiltins(Records, OS); + break; } return false; Index: utils/TableGen/TableGenBackends.h =================================================================== --- utils/TableGen/TableGenBackends.h +++ utils/TableGen/TableGenBackends.h @@ -78,6 +78,7 @@ void EmitTestPragmaAttributeSupportedAttributes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangOpenCLBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); } // end namespace clang #endif