Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8569,6 +8569,10 @@ "invalid reinterpretation: sizes of %0 and %1 must match">; def err_static_kernel : Error< "kernel functions cannot be declared static">; +def err_method_kernel : Error< + "kernel functions cannot be class members">; +def err_template_kernel : Error< + "kernel functions cannot be used in a template declaration, instantiation or specialization">; def err_opencl_ptrptr_kernel_param : Error< "kernel parameter cannot be declared as a pointer to a pointer">; def err_kernel_arg_address_space : Error< Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -5318,6 +5318,18 @@ return false; } +// In OpenCL we implicitly add C linkage context to kernels if it's not +// explicitly specified, let's skip over it here to return to the original +// context. +static void RestoreOpenCLKernelExplicitEnclosingContext(Sema &S, + NamedDecl *FD) { + if (FD->hasAttr()) + if (const auto *LinkSpec = dyn_cast(S.CurContext)) + if (LinkSpec->getLanguage() == LinkageSpecDecl::lang_c && + LinkSpec->isImplicit()) + S.CurContext = S.CurContext->getParent(); +} + NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, MultiTemplateParamsArg TemplateParamLists) { // TODO: consider using NameInfo for diagnostic. @@ -5528,6 +5540,11 @@ if (isInOpenMPDeclareTargetContext()) checkDeclIsAllowedInOpenMPTarget(nullptr, New); + // If we are done with this function parsing, let's check if it's an OpenCL + // kernel to restore explicit enclosing context in C++ mode. + if (LangOpts.OpenCLCPlusPlus && !D.isFunctionDefinition()) + RestoreOpenCLKernelExplicitEnclosingContext(*this, New); + return New; } @@ -8341,9 +8358,28 @@ DeclContext *OriginalDC = DC; bool IsLocalExternDecl = adjustContextForLocalExternDecl(DC); + auto SemaDC = DC; + if (getLangOpts().OpenCLCPlusPlus && !DC->isRecord() && + !DC->isExternCContext()) { + // In OpenCL all kernel functions are implicitly enclosed into C linkage + // scope to keep their source name non-mangled. + for (auto &attr : D.getDeclSpec().getAttributes()) { + if (attr.getKind() == ParsedAttr::AT_OpenCLKernel) { + auto CLinkage = LinkageSpecDecl::Create(Context, DC, SourceLocation(), + SourceLocation(), + LinkageSpecDecl::lang_c, false); + CLinkage->setImplicit(); + DC->addDecl(CLinkage); + SemaDC = CLinkage; + CurContext = CLinkage; + break; + } + } + } + + FunctionDecl *NewFD = + CreateNewFunctionDecl(*this, D, SemaDC, R, TInfo, SC, isVirtualOkay); - FunctionDecl *NewFD = CreateNewFunctionDecl(*this, D, DC, R, TInfo, SC, - isVirtualOkay); if (!NewFD) return nullptr; if (OriginalLexicalContext && OriginalLexicalContext->isObjCContainer()) @@ -9174,18 +9210,9 @@ MarkUnusedFileScopedDecl(NewFD); - if (getLangOpts().CPlusPlus) { - if (FunctionTemplate) { - if (NewFD->isInvalidDecl()) - FunctionTemplate->setInvalidDecl(); - return FunctionTemplate; - } - if (isMemberSpecialization && !NewFD->isInvalidDecl()) - CompleteMemberSpecialization(NewFD, Previous); - } - if (NewFD->hasAttr()) { + if (getLangOpts().OpenCL && NewFD->hasAttr()) { // OpenCL v1.2 s6.8 static is invalid for kernel functions. if ((getLangOpts().OpenCLVersion >= 120) && (SC == SC_Static)) { @@ -9205,7 +9232,30 @@ llvm::SmallPtrSet ValidTypes; for (auto Param : NewFD->parameters()) checkIsValidOpenCLKernelParameter(*this, D, Param, ValidTypes); + + if (getLangOpts().OpenCLCPlusPlus) { + if (DC->isRecord()) { + Diag(D.getIdentifierLoc(), diag::err_method_kernel); + D.setInvalidType(); + } + if (FunctionTemplate) { + Diag(D.getIdentifierLoc(), diag::err_template_kernel); + D.setInvalidType(); + } + } } + + if (getLangOpts().CPlusPlus) { + if (FunctionTemplate) { + if (NewFD->isInvalidDecl()) + FunctionTemplate->setInvalidDecl(); + return FunctionTemplate; + } + + if (isMemberSpecialization && !NewFD->isInvalidDecl()) + CompleteMemberSpecialization(NewFD, Previous); + } + for (const ParmVarDecl *Param : NewFD->parameters()) { QualType PT = Param->getType(); @@ -13416,6 +13466,9 @@ "Leftover expressions for odr-use checking"); } + if (LangOpts.OpenCLCPlusPlus) + RestoreOpenCLKernelExplicitEnclosingContext(*this, FD); + if (!IsInstantiation) PopDeclContext(); Index: test/CodeGenOpenCLCXX/addrspace-of-this.cl =================================================================== --- test/CodeGenOpenCLCXX/addrspace-of-this.cl +++ test/CodeGenOpenCLCXX/addrspace-of-this.cl @@ -82,7 +82,7 @@ // EXPL-LABEL: @__cxx_global_var_init() // EXPL: call void @_ZNU3AS41CC1Ev(%class.C addrspace(4)* addrspacecast (%class.C addrspace(1)* @c to %class.C addrspace(4)*)) -// COMMON-LABEL: @_Z12test__globalv() +// COMMON-LABEL: @test__global() // Test the address space of 'this' when invoking a method. // COMMON: %call = call i32 @_ZNU3AS41C3getEv(%class.C addrspace(4)* addrspacecast (%class.C addrspace(1)* @c to %class.C addrspace(4)*)) @@ -149,19 +149,19 @@ TEST(__local) -// COMMON-LABEL: _Z11test__localv +// COMMON-LABEL: @test__local // Test that we don't initialize an object in local address space // EXPL-NOT: call void @_ZNU3AS41CC1Ev(%class.C addrspace(4)* addrspacecast (%class.C addrspace(3)* @_ZZ11test__localvE1c to %class.C addrspace(4)*)) // Test the address space of 'this' when invoking a method. -// COMMON: %call = call i32 @_ZNU3AS41C3getEv(%class.C addrspace(4)* addrspacecast (%class.C addrspace(3)* @_ZZ11test__localvE1c to %class.C addrspace(4)*)) +// COMMON: %call = call i32 @_ZNU3AS41C3getEv(%class.C addrspace(4)* addrspacecast (%class.C addrspace(3)* @_ZZ11test__localE1c to %class.C addrspace(4)*)) // Test the address space of 'this' when invoking copy-constructor. // COMMON: [[C1GEN:%[0-9]+]] = addrspacecast %class.C* %c1 to %class.C addrspace(4)* -// EXPL: call void @_ZNU3AS41CC1ERU3AS4KS_(%class.C addrspace(4)* [[C1GEN]], %class.C addrspace(4)* dereferenceable(4) addrspacecast (%class.C addrspace(3)* @_ZZ11test__localvE1c to %class.C addrspace(4)*)) +// EXPL: call void @_ZNU3AS41CC1ERU3AS4KS_(%class.C addrspace(4)* [[C1GEN]], %class.C addrspace(4)* dereferenceable(4) addrspacecast (%class.C addrspace(3)* @_ZZ11test__localE1c to %class.C addrspace(4)*)) // IMPL: [[C1VOID:%[0-9]+]] = bitcast %class.C* %c1 to i8* -// IMPL: call void @llvm.memcpy.p0i8.p4i8.i32(i8* {{.*}}[[C1VOID]], i8 addrspace(4)* {{.*}}addrspacecast (i8 addrspace(3)* bitcast (%class.C addrspace(3)* @_ZZ11test__localvE1c to i8 addrspace(3)*) to i8 addrspace(4)*), i32 4, i1 false) +// IMPL: call void @llvm.memcpy.p0i8.p4i8.i32(i8* {{.*}}[[C1VOID]], i8 addrspace(4)* {{.*}}addrspacecast (i8 addrspace(3)* bitcast (%class.C addrspace(3)* @_ZZ11test__localE1c to i8 addrspace(3)*) to i8 addrspace(4)*), i32 4, i1 false) // Test the address space of 'this' when invoking a constructor. // EXPL: [[C2GEN:%[0-9]+]] = addrspacecast %class.C* %c2 to %class.C addrspace(4)* @@ -177,7 +177,7 @@ TEST(__private) -// CHECK-LABEL: @_Z13test__privatev +// CHECK-LABEL: @test__private // Test the address space of 'this' when invoking a constructor for an object in non-default address space // EXPL: [[CGEN:%[0-9]+]] = addrspacecast %class.C* %c to %class.C addrspace(4)* Index: test/CodeGenOpenCLCXX/local_addrspace_init.cl =================================================================== --- test/CodeGenOpenCLCXX/local_addrspace_init.cl +++ test/CodeGenOpenCLCXX/local_addrspace_init.cl @@ -1,8 +1,8 @@ // RUN: %clang_cc1 %s -triple spir -cl-std=c++ -emit-llvm -O0 -o - | FileCheck %s // Test that we don't initialize local address space objects. -//CHECK: @_ZZ4testvE1i = internal addrspace(3) global i32 undef -//CHECK: @_ZZ4testvE2ii = internal addrspace(3) global %class.C undef +//CHECK: @_ZZ4testE1i = internal addrspace(3) global i32 undef +//CHECK: @_ZZ4testE2ii = internal addrspace(3) global %class.C undef class C { int i; }; Index: test/SemaOpenCLCXX/kernel_invalid.cl =================================================================== --- /dev/null +++ test/SemaOpenCLCXX/kernel_invalid.cl @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 %s -cl-std=c++ -pedantic -verify -fsyntax-only + +struct C { + kernel void bar(); //expected-error{{kernel functions cannot be class members}} +}; + +template +kernel void templ(T par) { //expected-error{{kernel functions cannot be used in a template declaration, instantiation or specialization}} +} + +template +kernel void bar(int par) { //expected-error{{kernel functions cannot be used in a template declaration, instantiation or specialization}} +} + +kernel void foo(int); //expected-note{{previous declaration is here}} + +kernel void foo(float); //expected-error{{conflicting types for 'foo'}}